use crate::{
error::{Error, Result},
preflight::Preflight,
scanner::Scanner,
selector::Variant,
};
#[allow(clippy::wildcard_imports)]
use crate::plugins::*;
use phf::{phf_map as map, phf_ordered_map};
use serde::{Deserialize, Serialize};
use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
fmt, fs, iter,
path::Path,
};
pub const BUILTIN_COLORS: phf::Map<&str, &'static str> = map! {
"red-50" => "oklch(97.1% .013 17.38)",
"red-100" => "oklch(93.6% .032 17.717)",
"red-200" => "oklch(88.5% .062 18.334)",
"red-300" => "oklch(80.8% .114 19.571)",
"red-400" => "oklch(70.4% .191 22.216)",
"red-500" => "oklch(63.7% .237 25.331)",
"red-600" => "oklch(57.7% .245 27.325)",
"red-700" => "oklch(50.5% .213 27.518)",
"red-800" => "oklch(44.4% .177 26.899)",
"red-900" => "oklch(39.6% .141 25.723)",
"red-950" => "oklch(25.8% .092 26.042)",
"orange-50" => "oklch(98% .016 73.684)",
"orange-100" => "oklch(95.4% .038 75.164)",
"orange-200" => "oklch(90.1% .076 70.697)",
"orange-300" => "oklch(83.7% .128 66.29)",
"orange-400" => "oklch(75% .183 55.934)",
"orange-500" => "oklch(70.5% .213 47.604)",
"orange-600" => "oklch(64.6% .222 41.116)",
"orange-700" => "oklch(55.3% .195 38.402)",
"orange-800" => "oklch(47% .157 37.304)",
"orange-900" => "oklch(40.8% .123 38.172)",
"orange-950" => "oklch(26.6% .079 36.259)",
"amber-50" => "oklch(98.7% .022 95.277)",
"amber-100" => "oklch(96.2% .059 95.617)",
"amber-200" => "oklch(92.4% .12 95.746)",
"amber-300" => "oklch(87.9% .169 91.605)",
"amber-400" => "oklch(82.8% .189 84.429)",
"amber-500" => "oklch(76.9% .188 70.08)",
"amber-600" => "oklch(66.6% .179 58.318)",
"amber-700" => "oklch(55.5% .163 48.998)",
"amber-800" => "oklch(47.3% .137 46.201)",
"amber-900" => "oklch(41.4% .112 45.904)",
"amber-950" => "oklch(27.9% .077 45.635)",
"yellow-50" => "oklch(98.7% .026 102.212)",
"yellow-100" => "oklch(97.3% .071 103.193)",
"yellow-200" => "oklch(94.5% .129 101.54)",
"yellow-300" => "oklch(90.5% .182 98.111)",
"yellow-400" => "oklch(85.2% .199 91.936)",
"yellow-500" => "oklch(79.5% .184 86.047)",
"yellow-600" => "oklch(68.1% .162 75.834)",
"yellow-700" => "oklch(55.4% .135 66.442)",
"yellow-800" => "oklch(47.6% .114 61.907)",
"yellow-900" => "oklch(42.1% .095 57.708)",
"yellow-950" => "oklch(28.6% .066 53.813)",
"lime-50" => "oklch(98.6% .031 120.757)",
"lime-100" => "oklch(96.7% .067 122.328)",
"lime-200" => "oklch(93.8% .127 124.321)",
"lime-300" => "oklch(89.7% .196 126.665)",
"lime-400" => "oklch(84.1% .238 128.85)",
"lime-500" => "oklch(76.8% .233 130.85)",
"lime-600" => "oklch(64.8% .2 131.684)",
"lime-700" => "oklch(53.2% .157 131.589)",
"lime-800" => "oklch(45.3% .124 130.933)",
"lime-900" => "oklch(40.5% .101 131.063)",
"lime-950" => "oklch(27.4% .072 132.109)",
"green-50" => "oklch(98.2% .018 155.826)",
"green-100" => "oklch(96.2% .044 156.743)",
"green-200" => "oklch(92.5% .084 155.995)",
"green-300" => "oklch(87.1% .15 154.449)",
"green-400" => "oklch(79.2% .209 151.711)",
"green-500" => "oklch(72.3% .219 149.579)",
"green-600" => "oklch(62.7% .194 149.214)",
"green-700" => "oklch(52.7% .154 150.069)",
"green-800" => "oklch(44.8% .119 151.328)",
"green-900" => "oklch(39.3% .095 152.535)",
"green-950" => "oklch(26.6% .065 152.934)",
"emerald-50" => "oklch(97.9% .021 166.113)",
"emerald-100" => "oklch(95% .052 163.051)",
"emerald-200" => "oklch(90.5% .093 164.15)",
"emerald-300" => "oklch(84.5% .143 164.978)",
"emerald-400" => "oklch(76.5% .177 163.223)",
"emerald-500" => "oklch(69.6% .17 162.48)",
"emerald-600" => "oklch(59.6% .145 163.225)",
"emerald-700" => "oklch(50.8% .118 165.612)",
"emerald-800" => "oklch(43.2% .095 166.913)",
"emerald-900" => "oklch(37.8% .077 168.94)",
"emerald-950" => "oklch(26.2% .051 172.552)",
"teal-50" => "oklch(98.4% .014 180.72)",
"teal-100" => "oklch(95.3% .051 180.801)",
"teal-200" => "oklch(91% .096 180.426)",
"teal-300" => "oklch(85.5% .138 181.071)",
"teal-400" => "oklch(77.7% .152 181.912)",
"teal-500" => "oklch(70.4% .14 182.503)",
"teal-600" => "oklch(60% .118 184.704)",
"teal-700" => "oklch(51.1% .096 186.391)",
"teal-800" => "oklch(43.7% .078 188.216)",
"teal-900" => "oklch(38.6% .063 188.416)",
"teal-950" => "oklch(27.7% .046 192.524)",
"cyan-50" => "oklch(98.4% .019 200.873)",
"cyan-100" => "oklch(95.6% .045 203.388)",
"cyan-200" => "oklch(91.7% .08 205.041)",
"cyan-300" => "oklch(86.5% .127 207.078)",
"cyan-400" => "oklch(78.9% .154 211.53)",
"cyan-500" => "oklch(71.5% .143 215.221)",
"cyan-600" => "oklch(60.9% .126 221.723)",
"cyan-700" => "oklch(52% .105 223.128)",
"cyan-800" => "oklch(45% .085 224.283)",
"cyan-900" => "oklch(39.8% .07 227.392)",
"cyan-950" => "oklch(30.2% .056 229.695)",
"sky-50" => "oklch(97.7% .013 236.62)",
"sky-100" => "oklch(95.1% .026 236.824)",
"sky-200" => "oklch(90.1% .058 230.902)",
"sky-300" => "oklch(82.8% .111 230.318)",
"sky-400" => "oklch(74.6% .16 232.661)",
"sky-500" => "oklch(68.5% .169 237.323)",
"sky-600" => "oklch(58.8% .158 241.966)",
"sky-700" => "oklch(50% .134 242.749)",
"sky-800" => "oklch(44.3% .11 240.79)",
"sky-900" => "oklch(39.1% .09 240.876)",
"sky-950" => "oklch(29.3% .066 243.157)",
"blue-50" => "oklch(97% .014 254.604)",
"blue-100" => "oklch(93.2% .032 255.585)",
"blue-200" => "oklch(88.2% .059 254.128)",
"blue-300" => "oklch(80.9% .105 251.813)",
"blue-400" => "oklch(70.7% .165 254.624)",
"blue-500" => "oklch(62.3% .214 259.815)",
"blue-600" => "oklch(54.6% .245 262.881)",
"blue-700" => "oklch(48.8% .243 264.376)",
"blue-800" => "oklch(42.4% .199 265.638)",
"blue-900" => "oklch(37.9% .146 265.522)",
"blue-950" => "oklch(28.2% .091 267.935)",
"indigo-50" => "oklch(96.2% .018 272.314)",
"indigo-100" => "oklch(93% .034 272.788)",
"indigo-200" => "oklch(87% .065 274.039)",
"indigo-300" => "oklch(78.5% .115 274.713)",
"indigo-400" => "oklch(67.3% .182 276.935)",
"indigo-500" => "oklch(58.5% .233 277.117)",
"indigo-600" => "oklch(51.1% .262 276.966)",
"indigo-700" => "oklch(45.7% .24 277.023)",
"indigo-800" => "oklch(39.8% .195 277.366)",
"indigo-900" => "oklch(35.9% .144 278.697)",
"indigo-950" => "oklch(25.7% .09 281.288)",
"violet-50" => "oklch(96.9% .016 293.756)",
"violet-100" => "oklch(94.3% .029 294.588)",
"violet-200" => "oklch(89.4% .057 293.283)",
"violet-300" => "oklch(81.1% .111 293.571)",
"violet-400" => "oklch(70.2% .183 293.541)",
"violet-500" => "oklch(60.6% .25 292.717)",
"violet-600" => "oklch(54.1% .281 293.009)",
"violet-700" => "oklch(49.1% .27 292.581)",
"violet-800" => "oklch(43.2% .232 292.759)",
"violet-900" => "oklch(38% .189 293.745)",
"violet-950" => "oklch(28.3% .141 291.089)",
"purple-50" => "oklch(97.7% .014 308.299)",
"purple-100" => "oklch(94.6% .033 307.174)",
"purple-200" => "oklch(90.2% .063 306.703)",
"purple-300" => "oklch(82.7% .119 306.383)",
"purple-400" => "oklch(71.4% .203 305.504)",
"purple-500" => "oklch(62.7% .265 303.9)",
"purple-600" => "oklch(55.8% .288 302.321)",
"purple-700" => "oklch(49.6% .265 301.924)",
"purple-800" => "oklch(43.8% .218 303.724)",
"purple-900" => "oklch(38.1% .176 304.987)",
"purple-950" => "oklch(29.1% .149 302.717)",
"fuchsia-50" => "oklch(97.7% .017 320.058)",
"fuchsia-100" => "oklch(95.2% .037 318.852)",
"fuchsia-200" => "oklch(90.3% .076 319.62)",
"fuchsia-300" => "oklch(83.3% .145 321.434)",
"fuchsia-400" => "oklch(74% .238 322.16)",
"fuchsia-500" => "oklch(66.7% .295 322.15)",
"fuchsia-600" => "oklch(59.1% .293 322.896)",
"fuchsia-700" => "oklch(51.8% .253 323.949)",
"fuchsia-800" => "oklch(45.2% .211 324.591)",
"fuchsia-900" => "oklch(40.1% .17 325.612)",
"fuchsia-950" => "oklch(29.3% .136 325.661)",
"pink-50" => "oklch(97.1% .014 343.198)",
"pink-100" => "oklch(94.8% .028 342.258)",
"pink-200" => "oklch(89.9% .061 343.231)",
"pink-300" => "oklch(82.3% .12 346.018)",
"pink-400" => "oklch(71.8% .202 349.761)",
"pink-500" => "oklch(65.6% .241 354.308)",
"pink-600" => "oklch(59.2% .249 .584)",
"pink-700" => "oklch(52.5% .223 3.958)",
"pink-800" => "oklch(45.9% .187 3.815)",
"pink-900" => "oklch(40.8% .153 2.432)",
"pink-950" => "oklch(28.4% .109 3.907)",
"rose-50" => "oklch(96.9% .015 12.422)",
"rose-100" => "oklch(94.1% .03 12.58)",
"rose-200" => "oklch(89.2% .058 10.001)",
"rose-300" => "oklch(81% .117 11.638)",
"rose-400" => "oklch(71.2% .194 13.428)",
"rose-500" => "oklch(64.5% .246 16.439)",
"rose-600" => "oklch(58.6% .253 17.585)",
"rose-700" => "oklch(51.4% .222 16.935)",
"rose-800" => "oklch(45.5% .188 13.697)",
"rose-900" => "oklch(41% .159 10.272)",
"rose-950" => "oklch(27.1% .105 12.094)",
"slate-50" => "oklch(98.4% .003 247.858)",
"slate-100" => "oklch(96.8% .007 247.896)",
"slate-200" => "oklch(92.9% .013 255.508)",
"slate-300" => "oklch(86.9% .022 252.894)",
"slate-400" => "oklch(70.4% .04 256.788)",
"slate-500" => "oklch(55.4% .046 257.417)",
"slate-600" => "oklch(44.6% .043 257.281)",
"slate-700" => "oklch(37.2% .044 257.287)",
"slate-800" => "oklch(27.9% .041 260.031)",
"slate-900" => "oklch(20.8% .042 265.755)",
"slate-950" => "oklch(12.9% .042 264.695)",
"gray-50" => "oklch(98.5% .002 247.839)",
"gray-100" => "oklch(96.7% .003 264.542)",
"gray-200" => "oklch(92.8% .006 264.531)",
"gray-300" => "oklch(87.2% .01 258.338)",
"gray-400" => "oklch(70.7% .022 261.325)",
"gray-500" => "oklch(55.1% .027 264.364)",
"gray-600" => "oklch(44.6% .03 256.802)",
"gray-700" => "oklch(37.3% .034 259.733)",
"gray-800" => "oklch(27.8% .033 256.848)",
"gray-900" => "oklch(21% .034 264.665)",
"gray-950" => "oklch(13% .028 261.692)",
"zinc-50" => "oklch(98.5% 0 0)",
"zinc-100" => "oklch(96.7% .001 286.375)",
"zinc-200" => "oklch(92% .004 286.32)",
"zinc-300" => "oklch(87.1% .006 286.286)",
"zinc-400" => "oklch(70.5% .015 286.067)",
"zinc-500" => "oklch(55.2% .016 285.938)",
"zinc-600" => "oklch(44.2% .017 285.786)",
"zinc-700" => "oklch(37% .013 285.805)",
"zinc-800" => "oklch(27.4% .006 286.033)",
"zinc-900" => "oklch(21% .006 285.885)",
"zinc-950" => "oklch(14.1% .005 285.823)",
"neutral-50" => "oklch(98.5% 0 0)",
"neutral-100" => "oklch(97% 0 0)",
"neutral-200" => "oklch(92.2% 0 0)",
"neutral-300" => "oklch(87% 0 0)",
"neutral-400" => "oklch(70.8% 0 0)",
"neutral-500" => "oklch(55.6% 0 0)",
"neutral-600" => "oklch(43.9% 0 0)",
"neutral-700" => "oklch(37.1% 0 0)",
"neutral-800" => "oklch(26.9% 0 0)",
"neutral-900" => "oklch(20.5% 0 0)",
"neutral-950" => "oklch(14.5% 0 0)",
"stone-50" => "oklch(98.5% .001 106.423)",
"stone-100" => "oklch(97% .001 106.424)",
"stone-200" => "oklch(92.3% .003 48.717)",
"stone-300" => "oklch(86.9% .005 56.366)",
"stone-400" => "oklch(70.9% .01 56.259)",
"stone-500" => "oklch(55.3% .013 58.071)",
"stone-600" => "oklch(44.4% .011 73.639)",
"stone-700" => "oklch(37.4% .01 67.558)",
"stone-800" => "oklch(26.8% .007 34.298)",
"stone-900" => "oklch(21.6% .006 56.043)",
"stone-950" => "oklch(14.7% .004 49.25)",
"black" => "#000",
"white" => "#fff",
};
pub const BUILTIN_SCREENS: &[(&str, &str)] = &[
("sm", "40rem"),
("md", "48rem"),
("lg", "64rem"),
("xl", "80rem"),
("2xl", "96rem"),
];
pub const BUILTIN_CONTAINERS: &[(&str, &str)] = &[
("3xs", "16rem"),
("2xs", "18rem"),
("xs", "20rem"),
("sm", "24rem"),
("md", "28rem"),
("lg", "32rem"),
("xl", "36rem"),
("2xl", "42rem"),
("3xl", "48rem"),
("4xl", "56rem"),
("5xl", "64rem"),
("6xl", "72rem"),
("7xl", "80rem"),
];
#[rustfmt::skip]
pub const BUILTIN_VARIANTS: phf::OrderedMap<&'static str, Variant> = {
let mut counter = 0;
phf_ordered_map! {
"not" => Variant::new_const(&mut counter, "&:not({})").with_prefixed(),
"first-letter" => Variant::new_const(&mut counter, "&::first-letter"),
"first-line" => Variant::new_const(&mut counter, "&::first-line"),
"marker" => Variant::new_const(&mut counter, "& *::marker, &::marker"),
"selection" => Variant::new_const(&mut counter, "& *::selection, &::selection"),
"file" => Variant::new_const(&mut counter, "&::file-selector-button, &::-webkit-file-upload-button"),
"placeholder" => Variant::new_const(&mut counter, "&::placeholder"),
"backdrop" => Variant::new_const(&mut counter, "&::backdrop"),
"details-content" => Variant::new_const(&mut counter, "&::details-content"),
"before" => Variant::new_const(&mut counter, "&::before"),
"after" => Variant::new_const(&mut counter, "&::after"),
"all" => Variant::new_const(&mut counter, "& *"),
"**" => Variant::new_const(&mut counter, "& *"),
"children" => Variant::new_const(&mut counter, "& > *"),
"*" => Variant::new_const(&mut counter, "& > *"),
"siblings" => Variant::new_const(&mut counter, "& ~ *"),
"sibling" => Variant::new_const(&mut counter, "& + *"),
"first" => Variant::new_const(&mut counter, "&:first-child"),
"not-first" => Variant::new_const(&mut counter, "&:not(:first-child)"),
"last" => Variant::new_const(&mut counter, "&:last-child"),
"not-last" => Variant::new_const(&mut counter, "&:not(:last-child)"),
"only" => Variant::new_const(&mut counter, "&:only-child"),
"not-only" => Variant::new_const(&mut counter, "&:not(:only-child)"),
"odd" => Variant::new_const(&mut counter, "&:nth-child(odd)"),
"even" => Variant::new_const(&mut counter, "&:nth-child(even)"),
"first-of-type" => Variant::new_const(&mut counter, "&:first-of-type"),
"last-of-type" => Variant::new_const(&mut counter, "&:last-of-type"),
"only-of-type" => Variant::new_const(&mut counter, "&:only-of-type"),
"not-first-of-type" => Variant::new_const(&mut counter, "&:not(:first-of-type)"),
"not-last-of-type" => Variant::new_const(&mut counter, "&:not(:last-of-type)"),
"not-only-of-type" => Variant::new_const(&mut counter, "&:not(:only-of-type)"),
"visited" => Variant::new_const(&mut counter, "&:visited"),
"target" => Variant::new_const(&mut counter, "&:target"),
"open" => Variant::new_const(&mut counter, "&[open]"),
"default" => Variant::new_const(&mut counter, "&:default"),
"checked" => Variant::new_const(&mut counter, "&:checked"),
"not-checked" => Variant::new_const(&mut counter, "&:not(:checked)"),
"indeterminate" => Variant::new_const(&mut counter, "&:indeterminate"),
"placeholder-shown" => Variant::new_const(&mut counter, "&:placeholder-shown"),
"autofill" => Variant::new_const(&mut counter, "&:autofill"),
"optional" => Variant::new_const(&mut counter, "&:optional"),
"required" => Variant::new_const(&mut counter, "&:required"),
"valid" => Variant::new_const(&mut counter, "&:valid"),
"invalid" => Variant::new_const(&mut counter, "&:invalid"),
"in-range" => Variant::new_const(&mut counter, "&:in-range"),
"out-of-range" => Variant::new_const(&mut counter, "&:out-of-range"),
"read-only" => Variant::new_const(&mut counter, "&:read-only"),
"read-write" => Variant::new_const(&mut counter, "&:read-write"),
"empty" => Variant::new_const(&mut counter, "&:empty"),
"focus-within" => Variant::new_const(&mut counter, "&:focus-within"),
"hover" => Variant::new_const(&mut counter, "&:hover"),
"focus" => Variant::new_const(&mut counter, "&:focus"),
"focus-visible" => Variant::new_const(&mut counter, "&:focus-visible"),
"active" => Variant::new_const(&mut counter, "&:active"),
"enabled" => Variant::new_const(&mut counter, "&:enabled"),
"disabled" => Variant::new_const(&mut counter, "&:disabled"),
"inert" => Variant::new_const(&mut counter, "&:is([inert], [inert] *)"),
"in" => Variant::new_const(&mut counter, ":where({}) &").with_prefixed(),
"has" => Variant::new_const(&mut counter, "&:has({})").with_prefixed(),
"aria-busy" => Variant::new_const(&mut counter, "&[aria-busy=\"true\"]"),
"aria-checked" => Variant::new_const(&mut counter, "&[aria-checked=\"true\"]"),
"aria-disabled" => Variant::new_const(&mut counter, "&[aria-disabled=\"true\"]"),
"aria-expanded" => Variant::new_const(&mut counter, "&[aria-expanded=\"true\"]"),
"aria-hidden" => Variant::new_const(&mut counter, "&[aria-hidden=\"true\"]"),
"aria-pressed" => Variant::new_const(&mut counter, "&[aria-pressed=\"true\"]"),
"aria-readonly" => Variant::new_const(&mut counter, "&[aria-readonly=\"true\"]"),
"aria-required" => Variant::new_const(&mut counter, "&[aria-required=\"true\"]"),
"aria-selected" => Variant::new_const(&mut counter, "&[aria-selected=\"true\"]"),
"aria" => Variant::new_const(&mut counter, "&[aria-{}]").with_prefixed(),
"data" => Variant::new_const(&mut counter, "&[data-{}]").with_prefixed(),
"nth" => Variant::new_const(&mut counter, "&:nth-child({})").with_prefixed(),
"nth-last" => Variant::new_const(&mut counter, "&:nth-last-child({})").with_prefixed(),
"nth-of-type" => Variant::new_const(&mut counter, "&:nth-of-type({})").with_prefixed(),
"nth-last-of-type" => Variant::new_const(&mut counter, "&:nth-last-of-type({})").with_prefixed(),
"supports" => Variant::new_const(&mut counter, "@supports ({})").with_prefixed(),
"motion-safe" => Variant::new_const(&mut counter, "@media (prefers-reduced-motion: no-preference)"),
"motion-reduce" => Variant::new_const(&mut counter, "@media (prefers-reduced-motion: reduce)"),
"contrast-more" => Variant::new_const(&mut counter, "@media (prefers-contrast: more)"),
"contrast-less" => Variant::new_const(&mut counter, "@media (prefers-contrast: less)"),
"max" => Variant::new_const(&mut counter, "@media (width < {})").with_prefixed(),
"min" => Variant::new_const(&mut counter, "@media (width >= {})").with_prefixed(),
"@max" => Variant::new_const(&mut counter, "@container (width < {})").with_prefixed(),
"@min" => Variant::new_const(&mut counter, "@container (width >= {})").with_prefixed(),
"portrait" => Variant::new_const(&mut counter, "@media (orientation: portrait)"),
"landscape" => Variant::new_const(&mut counter, "@media (orientation: landscape)"),
"ltr" => Variant::new_const(&mut counter, "[dir=\"ltr\"] &"),
"rtl" => Variant::new_const(&mut counter, "[dir=\"rtl\"] &"),
"starting" => Variant::new_const(&mut counter, "@starting-style"),
"print" => Variant::new_const(&mut counter, "@media print"),
"forced-colors" => Variant::new_const(&mut counter, "@media (forced-colors: active)"),
"inverted-colors" => Variant::new_const(&mut counter, "@media (inverted-colors: inverted)"),
"pointer-none" => Variant::new_const(&mut counter, "@media (pointer: none)"),
"pointer-coarse" => Variant::new_const(&mut counter, "@media (pointer: coarse)"),
"pointer-fine" => Variant::new_const(&mut counter, "@media (pointer: fine)"),
"any-pointer-none" => Variant::new_const(&mut counter, "@media (any-pointer: none)"),
"any-pointer-coarse" => Variant::new_const(&mut counter, "@media (any-pointer: coarse)"),
"any-pointer-fine" => Variant::new_const(&mut counter, "@media (any-pointer: fine)"),
"noscript" => Variant::new_const(&mut counter, "@media (scripting: none)"),
}
};
pub const BUILTIN_PLUGINS: &[&StaticPlugin] = &[
&layout::container::PLUGIN,
&accessibility::screen_reader::PLUGIN,
&interactivity::pointer_events::PLUGIN,
&layout::visibility::PLUGIN,
&layout::position::PLUGIN,
&layout::placement::PLUGIN.0,
&layout::placement::PLUGIN.1,
&layout::placement::PLUGIN_X.0,
&layout::placement::PLUGIN_X.1,
&layout::placement::PLUGIN_Y.0,
&layout::placement::PLUGIN_Y.1,
&layout::placement::PLUGIN_START.0,
&layout::placement::PLUGIN_START.1,
&layout::placement::PLUGIN_END.0,
&layout::placement::PLUGIN_END.1,
&layout::placement::PLUGIN_TOP.0,
&layout::placement::PLUGIN_TOP.1,
&layout::placement::PLUGIN_RIGHT.0,
&layout::placement::PLUGIN_RIGHT.1,
&layout::placement::PLUGIN_BOTTOM.0,
&layout::placement::PLUGIN_BOTTOM.1,
&layout::placement::PLUGIN_LEFT.0,
&layout::placement::PLUGIN_LEFT.1,
&layout::isolation::PLUGIN,
&layout::z_index::PLUGIN,
&flexbox::order::PLUGIN_LIST,
&flexbox::order::PLUGIN_NUM,
&grid::grid_column::PLUGIN,
&grid::grid_column::PLUGIN_ARBITRARY,
&grid::grid_column::PLUGIN_SPAN_1,
&grid::grid_column::PLUGIN_SPAN_2,
&grid::grid_column::PLUGIN_START_1,
&grid::grid_column::PLUGIN_START_2,
&grid::grid_column::PLUGIN_END_1,
&grid::grid_column::PLUGIN_END_2,
&grid::grid_row::PLUGIN,
&grid::grid_row::PLUGIN_ARBITRARY,
&grid::grid_row::PLUGIN_SPAN_1,
&grid::grid_row::PLUGIN_SPAN_2,
&grid::grid_row::PLUGIN_START_1,
&grid::grid_row::PLUGIN_START_2,
&grid::grid_row::PLUGIN_END_1,
&grid::grid_row::PLUGIN_END_2,
&layout::floats::PLUGIN,
&layout::clear::PLUGIN,
&spacing::margin::PLUGIN.0,
&spacing::margin::PLUGIN.1,
&spacing::margin::PLUGIN_X.0,
&spacing::margin::PLUGIN_X.1,
&spacing::margin::PLUGIN_Y.0,
&spacing::margin::PLUGIN_Y.1,
&spacing::margin::PLUGIN_START.0,
&spacing::margin::PLUGIN_START.1,
&spacing::margin::PLUGIN_END.0,
&spacing::margin::PLUGIN_END.1,
&spacing::margin::PLUGIN_TOP.0,
&spacing::margin::PLUGIN_TOP.1,
&spacing::margin::PLUGIN_RIGHT.0,
&spacing::margin::PLUGIN_RIGHT.1,
&spacing::margin::PLUGIN_BOTTOM.0,
&spacing::margin::PLUGIN_BOTTOM.1,
&spacing::margin::PLUGIN_LEFT.0,
&spacing::margin::PLUGIN_LEFT.1,
&layout::box_sizing::PLUGIN,
&layout::display::PLUGIN,
&layout::aspect_ratio::PLUGIN,
&layout::aspect_ratio::PLUGIN_ARBITRARY,
&sizing::height::PLUGIN_SPACING,
&sizing::height::PLUGIN_LIST,
&sizing::height::PLUGIN_ARBITRARY,
&sizing::max_height::PLUGIN_SPACING,
&sizing::max_height::PLUGIN_LIST,
&sizing::max_height::PLUGIN_ARBITRARY,
&sizing::min_height::PLUGIN_SPACING,
&sizing::min_height::PLUGIN_LIST,
&sizing::min_height::PLUGIN_ARBITRARY,
&sizing::width::PLUGIN_SPACING,
&sizing::width::PLUGIN_LIST,
&sizing::width::PLUGIN_ARBITRARY,
&sizing::min_width::PLUGIN_SPACING,
&sizing::min_width::PLUGIN_LIST,
&sizing::min_width::PLUGIN_ARBITRARY,
&sizing::max_width::PLUGIN_SPACING,
&sizing::max_width::PLUGIN_LIST_1,
&sizing::max_width::PLUGIN_LIST_2,
&sizing::max_width::PLUGIN_ARBITRARY,
&flexbox::flex::PLUGIN,
&flexbox::flex::PLUGIN_ARBITRARY,
&flexbox::flex_shrink::PLUGIN,
&flexbox::flex_grow::PLUGIN,
&flexbox::flex_basis::PLUGIN,
&flexbox::flex_basis::PLUGIN_ARBITRARY,
&table::table_layout::PLUGIN,
&table::caption_side::PLUGIN,
&table::border_collapse::PLUGIN,
&table::border_spacing::PLUGIN.0,
&table::border_spacing::PLUGIN.1,
&table::border_spacing::PLUGIN_X.0,
&table::border_spacing::PLUGIN_X.1,
&table::border_spacing::PLUGIN_Y.0,
&table::border_spacing::PLUGIN_Y.1,
&transform::transform_origin::PLUGIN,
&transform::transform_origin::PLUGIN_ARBITRARY,
&transform::perspective_origin::PLUGIN,
&transform::perspective_origin::PLUGIN_ARBITRARY,
&transform::perspective::PLUGIN,
&transform::perspective::PLUGIN_ARBITRARY,
&transform::translate::PLUGIN_X.0,
&transform::translate::PLUGIN_X.1,
&transform::translate::PLUGIN_Y.0,
&transform::translate::PLUGIN_Y.1,
&transform::translate::PLUGIN_Z.0,
&transform::translate::PLUGIN_Z.1,
&transform::rotate::PLUGIN.0,
&transform::rotate::PLUGIN.1,
&transform::rotate::PLUGIN_X.0,
&transform::rotate::PLUGIN_X.1,
&transform::rotate::PLUGIN_Y.0,
&transform::rotate::PLUGIN_Y.1,
&transform::rotate::PLUGIN_Z.0,
&transform::rotate::PLUGIN_Z.1,
&transform::skew::PLUGIN_X.0,
&transform::skew::PLUGIN_X.1,
&transform::skew::PLUGIN_Y.0,
&transform::skew::PLUGIN_Y.1,
&transform::scale::PLUGIN.0,
&transform::scale::PLUGIN.1,
&transform::scale::PLUGIN_X.0,
&transform::scale::PLUGIN_X.1,
&transform::scale::PLUGIN_Y.0,
&transform::scale::PLUGIN_Y.1,
&transform::scale::PLUGIN_Z.0,
&transform::scale::PLUGIN_Z.1,
&transform::transform_type::PLUGIN,
&transition::animation::PLUGIN,
&transition::animation::PLUGIN_ARBITRARY,
&interactivity::cursor::PLUGIN,
&interactivity::cursor::PLUGIN_ARBITRARY,
&interactivity::touch_action::PLUGIN,
&interactivity::user_select::PLUGIN,
&interactivity::resize::PLUGIN,
&interactivity::scroll_snap_type::PLUGIN,
&interactivity::scroll_snap_align::PLUGIN,
&interactivity::scroll_snap_stop::PLUGIN,
&interactivity::scroll_margin::PLUGIN.0,
&interactivity::scroll_margin::PLUGIN.1,
&interactivity::scroll_margin::PLUGIN_X.0,
&interactivity::scroll_margin::PLUGIN_X.1,
&interactivity::scroll_margin::PLUGIN_Y.0,
&interactivity::scroll_margin::PLUGIN_Y.1,
&interactivity::scroll_margin::PLUGIN_START.0,
&interactivity::scroll_margin::PLUGIN_START.1,
&interactivity::scroll_margin::PLUGIN_END.0,
&interactivity::scroll_margin::PLUGIN_END.1,
&interactivity::scroll_margin::PLUGIN_TOP.0,
&interactivity::scroll_margin::PLUGIN_TOP.1,
&interactivity::scroll_margin::PLUGIN_RIGHT.0,
&interactivity::scroll_margin::PLUGIN_RIGHT.1,
&interactivity::scroll_margin::PLUGIN_BOTTOM.0,
&interactivity::scroll_margin::PLUGIN_BOTTOM.1,
&interactivity::scroll_margin::PLUGIN_LEFT.0,
&interactivity::scroll_margin::PLUGIN_LEFT.1,
&interactivity::scroll_padding::PLUGIN.0,
&interactivity::scroll_padding::PLUGIN.1,
&interactivity::scroll_padding::PLUGIN_X.0,
&interactivity::scroll_padding::PLUGIN_X.1,
&interactivity::scroll_padding::PLUGIN_Y.0,
&interactivity::scroll_padding::PLUGIN_Y.1,
&interactivity::scroll_padding::PLUGIN_START.0,
&interactivity::scroll_padding::PLUGIN_START.1,
&interactivity::scroll_padding::PLUGIN_END.0,
&interactivity::scroll_padding::PLUGIN_END.1,
&interactivity::scroll_padding::PLUGIN_TOP.0,
&interactivity::scroll_padding::PLUGIN_TOP.1,
&interactivity::scroll_padding::PLUGIN_RIGHT.0,
&interactivity::scroll_padding::PLUGIN_RIGHT.1,
&interactivity::scroll_padding::PLUGIN_BOTTOM.0,
&interactivity::scroll_padding::PLUGIN_BOTTOM.1,
&interactivity::scroll_padding::PLUGIN_LEFT.0,
&interactivity::scroll_padding::PLUGIN_LEFT.1,
&typography::list_style_position::PLUGIN,
&typography::list_style_type::PLUGIN,
&typography::list_style_type::PLUGIN_ARBITRARY,
&interactivity::appearance::PLUGIN,
&layout::columns::PLUGIN,
&layout::break_before::PLUGIN,
&layout::break_inside::PLUGIN,
&layout::break_after::PLUGIN,
&grid::grid_auto_columns::PLUGIN,
&grid::grid_auto_columns::PLUGIN_ARBITRARY,
&grid::grid_auto_flow::PLUGIN,
&grid::grid_auto_rows::PLUGIN,
&grid::grid_auto_rows::PLUGIN_ARBITRARY,
&grid::grid_template_columns::PLUGIN_NUMBER,
&grid::grid_template_columns::PLUGIN_LIST,
&grid::grid_template_columns::PLUGIN_ARBITRARY,
&grid::grid_template_rows::PLUGIN_NUMBER,
&grid::grid_template_rows::PLUGIN_LIST,
&grid::grid_template_rows::PLUGIN_ARBITRARY,
&flexbox::flex_direction::PLUGIN,
&flexbox::flex_wrap::PLUGIN,
&flexbox::place_content::PLUGIN,
&flexbox::place_items::PLUGIN,
&flexbox::align_content::PLUGIN,
&flexbox::align_items::PLUGIN,
&flexbox::justify_content::PLUGIN,
&flexbox::justify_items::PLUGIN,
&grid::gap::PLUGIN.0,
&grid::gap::PLUGIN.1,
&grid::gap::PLUGIN_X.0,
&grid::gap::PLUGIN_X.1,
&grid::gap::PLUGIN_Y.0,
&grid::gap::PLUGIN_Y.1,
&spacing::space_between::PLUGIN_X_1,
&spacing::space_between::PLUGIN_X_2,
&spacing::space_between::PLUGIN_X_3,
&spacing::space_between::PLUGIN_Y_1,
&spacing::space_between::PLUGIN_Y_2,
&spacing::space_between::PLUGIN_Y_3,
&border::divide_width::PLUGIN_X_1,
&border::divide_width::PLUGIN_X_2,
&border::divide_width::PLUGIN_X_3,
&border::divide_width::PLUGIN_Y_1,
&border::divide_width::PLUGIN_Y_2,
&border::divide_width::PLUGIN_Y_3,
&border::divide_style::PLUGIN,
&border::divide_color::PLUGIN,
&border::divide_color::PLUGIN_ARBITRARY,
&flexbox::place_self::PLUGIN,
&flexbox::align_self::PLUGIN,
&flexbox::justify_self::PLUGIN,
&layout::overflow::PLUGIN,
&layout::overscroll_behavior::PLUGIN,
&interactivity::scroll_behavior::PLUGIN,
&typography::text_overflow::PLUGIN,
&typography::whitespace::PLUGIN,
&typography::text_wrap::PLUGIN,
&typography::word_break::PLUGIN,
&border::border_radius::PLUGIN.0,
&border::border_radius::PLUGIN.1,
&border::border_radius::PLUGIN_START.0,
&border::border_radius::PLUGIN_START.1,
&border::border_radius::PLUGIN_END.0,
&border::border_radius::PLUGIN_END.1,
&border::border_radius::PLUGIN_TOP.0,
&border::border_radius::PLUGIN_TOP.1,
&border::border_radius::PLUGIN_RIGHT.0,
&border::border_radius::PLUGIN_RIGHT.1,
&border::border_radius::PLUGIN_BOTTOM.0,
&border::border_radius::PLUGIN_BOTTOM.1,
&border::border_radius::PLUGIN_LEFT.0,
&border::border_radius::PLUGIN_LEFT.1,
&border::border_radius::PLUGIN_START_START.0,
&border::border_radius::PLUGIN_START_START.1,
&border::border_radius::PLUGIN_START_END.0,
&border::border_radius::PLUGIN_START_END.1,
&border::border_radius::PLUGIN_END_END.0,
&border::border_radius::PLUGIN_END_END.1,
&border::border_radius::PLUGIN_END_START.0,
&border::border_radius::PLUGIN_END_START.1,
&border::border_radius::PLUGIN_TOP_RIGHT.0,
&border::border_radius::PLUGIN_TOP_RIGHT.1,
&border::border_radius::PLUGIN_TOP_LEFT.0,
&border::border_radius::PLUGIN_TOP_LEFT.1,
&border::border_radius::PLUGIN_BOTTOM_RIGHT.0,
&border::border_radius::PLUGIN_BOTTOM_RIGHT.1,
&border::border_radius::PLUGIN_BOTTOM_LEFT.0,
&border::border_radius::PLUGIN_BOTTOM_LEFT.1,
&border::border_width::PLUGIN.0,
&border::border_width::PLUGIN.1,
&border::border_width::PLUGIN_X.0,
&border::border_width::PLUGIN_X.1,
&border::border_width::PLUGIN_Y.0,
&border::border_width::PLUGIN_Y.1,
&border::border_width::PLUGIN_START.0,
&border::border_width::PLUGIN_START.1,
&border::border_width::PLUGIN_END.0,
&border::border_width::PLUGIN_END.1,
&border::border_width::PLUGIN_TOP.0,
&border::border_width::PLUGIN_TOP.1,
&border::border_width::PLUGIN_RIGHT.0,
&border::border_width::PLUGIN_RIGHT.1,
&border::border_width::PLUGIN_BOTTOM.0,
&border::border_width::PLUGIN_BOTTOM.1,
&border::border_width::PLUGIN_LEFT.0,
&border::border_width::PLUGIN_LEFT.1,
&border::border_style::PLUGIN,
&border::border_style::PLUGIN_ARBITRARY,
&border::border_color::PLUGIN.0,
&border::border_color::PLUGIN.1,
&border::border_color::PLUGIN_X.0,
&border::border_color::PLUGIN_X.1,
&border::border_color::PLUGIN_Y.0,
&border::border_color::PLUGIN_Y.1,
&border::border_color::PLUGIN_START.0,
&border::border_color::PLUGIN_START.1,
&border::border_color::PLUGIN_END.0,
&border::border_color::PLUGIN_END.1,
&border::border_color::PLUGIN_TOP.0,
&border::border_color::PLUGIN_TOP.1,
&border::border_color::PLUGIN_RIGHT.0,
&border::border_color::PLUGIN_RIGHT.1,
&border::border_color::PLUGIN_BOTTOM.0,
&border::border_color::PLUGIN_BOTTOM.1,
&border::border_color::PLUGIN_LEFT.0,
&border::border_color::PLUGIN_LEFT.1,
&background::background_color::PLUGIN,
&background::background_color::PLUGIN_ARBITRARY,
&background::background_image::PLUGIN,
&background::background_image::PLUGIN_ARBITRARY,
&background::background_image::PLUGIN_LINEAR_1,
&background::background_image::PLUGIN_LINEAR_2,
&background::background_image::PLUGIN_LINEAR_3,
&background::background_image::PLUGIN_RADIAL_1,
&background::background_image::PLUGIN_RADIAL_2,
&background::background_image::PLUGIN_CONIC_1,
&background::background_image::PLUGIN_CONIC_2,
&background::background_image::PLUGIN_CONIC_3,
&background::gradient_color_stops::PLUGIN_FROM_1,
&background::gradient_color_stops::PLUGIN_FROM_2,
&background::gradient_color_stops::PLUGIN_VIA_1,
&background::gradient_color_stops::PLUGIN_VIA_2,
&background::gradient_color_stops::PLUGIN_TO_1,
&background::gradient_color_stops::PLUGIN_TO_2,
&layout::box_decoration_break::PLUGIN,
&background::background_size::PLUGIN,
&background::background_size::PLUGIN_ARBITRARY,
&background::background_attachment::PLUGIN,
&background::background_clip::PLUGIN,
&background::background_position::PLUGIN,
&background::background_position::PLUGIN_ARBITRARY,
&background::background_repeat::PLUGIN,
&background::background_origin::PLUGIN,
&svg::fill::PLUGIN,
&svg::fill::PLUGIN_ARBITRARY,
&svg::stroke::PLUGIN,
&svg::stroke::PLUGIN_ARBITRARY,
&svg::stroke_width::PLUGIN,
&svg::stroke_width::PLUGIN_ARBITRARY,
&layout::object_fit::PLUGIN,
&layout::object_position::PLUGIN,
&layout::object_position::PLUGIN_ARBITRARY,
&spacing::padding::PLUGIN.0,
&spacing::padding::PLUGIN.1,
&spacing::padding::PLUGIN_X.0,
&spacing::padding::PLUGIN_X.1,
&spacing::padding::PLUGIN_Y.0,
&spacing::padding::PLUGIN_Y.1,
&spacing::padding::PLUGIN_START.0,
&spacing::padding::PLUGIN_START.1,
&spacing::padding::PLUGIN_END.0,
&spacing::padding::PLUGIN_END.1,
&spacing::padding::PLUGIN_TOP.0,
&spacing::padding::PLUGIN_TOP.1,
&spacing::padding::PLUGIN_RIGHT.0,
&spacing::padding::PLUGIN_RIGHT.1,
&spacing::padding::PLUGIN_BOTTOM.0,
&spacing::padding::PLUGIN_BOTTOM.1,
&spacing::padding::PLUGIN_LEFT.0,
&spacing::padding::PLUGIN_LEFT.1,
&typography::text_align::PLUGIN,
&typography::text_indent::PLUGIN,
&typography::text_indent::PLUGIN_ARBITRARY,
&typography::vertical_align::PLUGIN,
&typography::font_family::PLUGIN,
&typography::font_family::PLUGIN_ARBITRARY,
&typography::font_size::PLUGIN,
&typography::font_size::PLUGIN_ARBITRARY,
&typography::font_weight::PLUGIN,
&typography::font_weight::PLUGIN_ARBITRARY,
&typography::text_transform::PLUGIN,
&typography::font_style::PLUGIN,
&typography::font_variant_numeric::PLUGIN,
&typography::letter_spacing::PLUGIN,
&typography::letter_spacing::PLUGIN_ARBITRARY,
&typography::line_height::PLUGIN_LIST,
&typography::line_height::PLUGIN_SPACING,
&typography::line_height::PLUGIN_ARBITRARY,
&typography::text_color::PLUGIN,
&typography::text_color::PLUGIN_ARBITRARY,
&typography::text_decoration::PLUGIN,
&typography::text_decoration_color::PLUGIN,
&typography::text_decoration_color::PLUGIN_ARBITRARY,
&typography::text_decoration_style::PLUGIN,
&typography::text_decoration_thickness::PLUGIN_LIST,
&typography::text_decoration_thickness::PLUGIN_NUMBER,
&typography::text_decoration_thickness::PLUGIN_ARBITRARY,
&typography::text_underline_offset::PLUGIN_LIST,
&typography::text_underline_offset::PLUGIN_NUMBER,
&typography::text_underline_offset::PLUGIN_ARBITRARY,
&typography::font_smoothing::PLUGIN,
&interactivity::caret_color::PLUGIN,
&interactivity::caret_color::PLUGIN_ARBITRARY,
&interactivity::accent_color::PLUGIN,
&interactivity::accent_color::PLUGIN_ARBITRARY,
&effect::opacity::PLUGIN,
&effect::background_blend_mode::PLUGIN,
&effect::mix_blend_mode::PLUGIN,
&effect::text_shadow::PLUGIN,
&effect::text_shadow::PLUGIN_ARBITRARY,
&effect::text_shadow_color::PLUGIN,
&effect::text_shadow_color::PLUGIN_ARBITRARY,
&effect::box_shadow::PLUGIN,
&effect::box_shadow::PLUGIN_ARBITRARY,
&effect::box_shadow_color::PLUGIN,
&effect::box_shadow_color::PLUGIN_ARBITRARY,
&effect::box_shadow::PLUGIN_INSET_1,
&effect::box_shadow::PLUGIN_INSET_2,
&effect::box_shadow_color::PLUGIN_INSET_1,
&effect::box_shadow_color::PLUGIN_INSET_2,
&border::outline_style::PLUGIN_LIST_1,
&border::outline_style::PLUGIN_LIST_2,
&border::outline_width::PLUGIN,
&border::outline_width::PLUGIN_ARBITRARY,
&border::outline_offset::PLUGIN,
&border::outline_offset::PLUGIN_ARBITRARY,
&border::outline_color::PLUGIN,
&border::outline_color::PLUGIN_ARBITRARY,
&border::ring_width::PLUGIN,
&border::ring_width::PLUGIN_ARBITRARY,
&border::ring_color::PLUGIN,
&border::ring_color::PLUGIN_ARBITRARY,
&border::ring_width::PLUGIN_INSET_1,
&border::ring_width::PLUGIN_INSET_2,
&border::ring_color::PLUGIN_INSET_1,
&border::ring_color::PLUGIN_INSET_2,
&border::ring_offset_width::PLUGIN,
&border::ring_offset_width::PLUGIN_ARBITRARY,
&border::ring_offset_color::PLUGIN,
&border::ring_offset_color::PLUGIN_ARBITRARY,
&filter::blur::PLUGIN,
&filter::blur::PLUGIN_ARBITRARY,
&filter::brightness::PLUGIN,
&filter::contrast::PLUGIN,
&filter::drop_shadow::PLUGIN,
&filter::drop_shadow::PLUGIN_ARBITRARY,
&filter::grayscale::PLUGIN,
&filter::hue_rotate::PLUGIN,
&filter::hue_rotate::PLUGIN_ARBITRARY,
&filter::invert::PLUGIN,
&filter::saturate::PLUGIN,
&filter::sepia::PLUGIN,
&filter::filter_type::PLUGIN,
&filter::backdrop_blur::PLUGIN,
&filter::backdrop_blur::PLUGIN_ARBITRARY,
&filter::backdrop_brightness::PLUGIN,
&filter::backdrop_contrast::PLUGIN,
&filter::backdrop_grayscale::PLUGIN,
&filter::backdrop_hue_rotate::PLUGIN,
&filter::backdrop_hue_rotate::PLUGIN_ARBITRARY,
&filter::backdrop_invert::PLUGIN,
&filter::backdrop_saturate::PLUGIN,
&filter::backdrop_sepia::PLUGIN,
&filter::backdrop_filter::PLUGIN,
&transition::transition_property::PLUGIN,
&transition::transition_property::PLUGIN_ARBITRARY,
&transition::transition_delay::PLUGIN,
&transition::transition_delay::PLUGIN_ARBITRARY,
&transition::transition_duration::PLUGIN,
&transition::transition_duration::PLUGIN_ARBITRARY,
&transition::transition_timing_function::PLUGIN,
&transition::transition_timing_function::PLUGIN_ARBITRARY,
&interactivity::will_change::PLUGIN,
&interactivity::will_change::PLUGIN_ARBITRARY,
&typography::content::PLUGIN,
&typography::content::PLUGIN_ARBITRARY,
&typography::line_clamp::PLUGIN_NUMBER,
&typography::line_clamp::PLUGIN_LIST,
&layout::at_container::PLUGIN,
&layout::at_container::PLUGIN_ARBITRARY,
];
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Default)]
#[serde(rename_all = "lowercase")]
pub enum DarkMode {
Class(Cow<'static, str>),
#[default]
Media,
}
impl DarkMode {
pub fn new_class<T: Into<Cow<'static, str>>>(class: T) -> Self {
Self::Class(class.into())
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Aria(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Aria {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
&mut self,
key: T1,
val: T2,
) {
self.0.insert(key.into(), val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
self.0.iter()
}
pub(crate) fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Screens(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Screens {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
&mut self,
key: T1,
val: T2,
) {
self.0.insert(key.into(), val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
self.0.iter()
}
pub(crate) fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Containers(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Containers {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
&mut self,
key: T1,
val: T2,
) {
self.0.insert(key.into(), val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
self.0.iter()
}
pub(crate) fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Colors(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Colors {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
&mut self,
key: T1,
val: T2,
) {
self.0.insert(key.into(), val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
self.0.get(&key.into())
}
#[inline]
pub(crate) fn contains<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> bool {
self.0.contains_key(&key.into())
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Shortcuts(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Shortcuts {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
&mut self,
key: T1,
val: T2,
) {
self.0.insert(key.into(), val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
self.0.get(&key.into())
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Layers(BTreeMap<Cow<'static, str>, i8>);
impl Layers {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>>(&mut self, key: T1, val: i8) {
self.0.insert(key.into(), val);
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&'a self, key: T) -> Option<&'a i8> {
self.0.get(&key.into())
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub struct MaxShortcutDepth(usize);
impl MaxShortcutDepth {
pub fn new(v: usize) -> Self {
Self(v)
}
pub fn get(&self) -> usize {
self.0
}
}
impl From<usize> for MaxShortcutDepth {
fn from(v: usize) -> Self {
Self(v)
}
}
impl From<MaxShortcutDepth> for usize {
fn from(val: MaxShortcutDepth) -> Self {
val.0
}
}
impl Default for MaxShortcutDepth {
fn default() -> Self {
Self(5)
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Safelist(BTreeSet<Cow<'static, str>>);
impl Safelist {
#[inline]
pub fn add<T: Into<Cow<'static, str>>>(&mut self, val: T) {
self.0.insert(val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, val: T) {
self.0.remove(&val.into());
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = &Cow<'static, str>> {
self.0.iter()
}
}
#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
pub struct Extra(BTreeMap<Cow<'static, str>, toml::Value>);
impl Extra {
#[inline]
pub fn add<T1: Into<Cow<'static, str>>, T2: Into<toml::Value>>(&mut self, key: T1, val: T2) {
self.0.insert(key.into(), val.into());
}
#[inline]
pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
self.0.remove(&key.into());
}
#[inline]
pub fn get<'a, T: Into<Cow<'a, str>>>(&'a self, key: T) -> Option<&'a toml::Value> {
self.0.get(&key.into())
}
}
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
pub struct Theme {
#[serde(default)]
pub dark_mode: DarkMode,
#[serde(default)]
pub screens: Screens,
#[serde(default)]
pub containers: Containers,
#[serde(default)]
pub colors: Colors,
#[serde(default)]
pub aria: Aria,
}
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct Config {
#[serde(default)]
pub safelist: Safelist,
#[serde(default)]
pub theme: Theme,
#[serde(default)]
pub preflight: Preflight,
#[serde(default)]
pub shortcuts: Shortcuts,
#[serde(default)]
pub layers: Layers,
#[serde(default)]
pub max_shortcut_depth: MaxShortcutDepth,
#[serde(default)]
pub extra: Extra,
#[serde(default)]
pub(crate) custom_plugins: Vec<CustomPlugin>,
#[serde(skip)]
pub scanner: Scanner,
#[serde(skip)]
pub(crate) custom_variants: Vec<(Cow<'static, str>, Variant<'static>)>,
}
impl Config {
#[allow(clippy::too_many_lines)]
pub(crate) fn get_derived_variants(&self) -> Vec<(Cow<'static, str>, Variant<'static>)> {
self.theme
.screens
.iter()
.map(|screen| {
(
screen.0.clone(),
Variant {
order: BUILTIN_VARIANTS.len(),
prefixed: false,
template: Cow::Owned(format!("@media (width >= {})", screen.1)),
},
)
})
.chain(BUILTIN_SCREENS.iter().map(|screen| {
(
Cow::from(screen.0),
Variant {
order: BUILTIN_VARIANTS.len() + self.theme.screens.0.len(),
prefixed: false,
template: Cow::Owned(format!("@media (width >= {})", screen.1)),
},
)
}))
.chain(self.theme.screens.iter().map(|screen| {
(
Cow::from(format!("max-{}", screen.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ self.theme.screens.len()
+ BUILTIN_SCREENS.len(),
prefixed: false,
template: Cow::Owned(format!("@media (width < {})", screen.1)),
},
)
}))
.chain(BUILTIN_SCREENS.iter().map(|screen| {
(
Cow::from(format!("max-{}", screen.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ BUILTIN_SCREENS.len(),
prefixed: false,
template: Cow::Owned(format!("@media (width < {})", screen.1)),
},
)
}))
.chain(self.theme.containers.iter().map(|container| {
(
Cow::from(format!("@{}", container.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len(),
prefixed: false,
template: Cow::Owned(format!("@container (width >= {})", container.1)),
},
)
}))
.chain(BUILTIN_CONTAINERS.iter().map(|container| {
(
Cow::from(format!("@{}", container.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ self.theme.containers.len(),
prefixed: false,
template: Cow::Owned(format!("@container (width >= {})", container.1)),
},
)
}))
.chain(self.theme.containers.iter().map(|container| {
(
Cow::from(format!("@max-{}", container.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ self.theme.containers.len()
+ BUILTIN_CONTAINERS.len(),
prefixed: false,
template: Cow::Owned(format!("@container (width < {})", container.1)),
},
)
}))
.chain(BUILTIN_CONTAINERS.iter().map(|container| {
(
Cow::from(format!("@max-{}", container.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ 2 * self.theme.containers.len()
+ BUILTIN_CONTAINERS.len(),
prefixed: false,
template: Cow::Owned(format!("@container (width < {})", container.1)),
},
)
}))
.chain(self.theme.aria.iter().map(|aria| {
(
Cow::from(format!("aria-{}", aria.0)),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ 2 * self.theme.containers.len()
+ 2 * BUILTIN_CONTAINERS.len(),
prefixed: false,
template: Cow::Owned(format!("&[aria-{}]", aria.1)),
},
)
}))
.chain(iter::once(match &self.theme.dark_mode {
DarkMode::Media => (
Cow::from("dark"),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ 2 * self.theme.containers.len()
+ 2 * BUILTIN_CONTAINERS.len()
+ self.theme.aria.0.len(),
prefixed: false,
template: Cow::Borrowed("@media (prefers-color-scheme: dark)"),
},
),
DarkMode::Class(name) => (
Cow::from("dark"),
Variant {
order: BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ 2 * self.theme.containers.len()
+ 2 * BUILTIN_CONTAINERS.len()
+ self.theme.aria.len(),
prefixed: false,
template: Cow::Owned(format!("{name} &")),
},
),
}))
.collect()
}
pub fn last_variant_order(&self) -> usize {
BUILTIN_VARIANTS.len()
+ 2 * self.theme.screens.len()
+ 2 * BUILTIN_SCREENS.len()
+ 2 * self.theme.containers.len()
+ 2 * BUILTIN_CONTAINERS.len()
+ self.theme.aria.len()
+ 1
+ self.custom_variants.len()
}
pub fn register_plugin(&mut self, plugin: &'static StaticPlugin) {
self.custom_plugins.push(CustomPlugin::Static(plugin));
}
pub fn register_dynamic_plugin(&mut self, plugin: DynamicPlugin) {
self.custom_plugins.push(CustomPlugin::Dynamic(plugin));
}
pub fn register_variant<T: Into<Cow<'static, str>>>(
&mut self,
variant_name: T,
variant: Variant<'static>,
) {
self.custom_variants.push((variant_name.into(), variant));
}
pub fn from_file<T: AsRef<Path>>(path: T) -> Result<Self> {
Ok(toml::from_str(&fs::read_to_string(&path).map_err(
|e| Error::ConfigFileNotFound(path.as_ref().to_path_buf(), e),
)?)?)
}
}
impl PartialEq for Config {
fn eq(&self, other: &Self) -> bool {
self.safelist == other.safelist
&& self.theme == other.theme
&& self.preflight == other.preflight
&& self.shortcuts == other.shortcuts
&& self.max_shortcut_depth == other.max_shortcut_depth
&& self.extra == other.extra
&& self.custom_variants == other.custom_variants
}
}
impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Config")
.field("safelist", &self.safelist)
.field("theme", &self.theme)
.field("preflight", &self.preflight)
.field("shortcuts", &self.shortcuts)
.field("max_shortcut_depth", &self.max_shortcut_depth)
.field("extra", &self.extra)
.field("custom_plugins", &self.custom_plugins)
.field("custom_variants", &self.custom_variants)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
use crate::{generate, utils::testing::base_config};
use pretty_assertions::assert_eq;
#[test]
fn gen_css_with_custom_config() {
let mut config = base_config();
config.theme.colors.add("rosa-500", "#e5186a");
config.theme.screens.add("3xl", "1600px");
let generated = generate(["3xl:text-rosa-500"], &config);
assert_eq!(
generated,
String::from(
r"@media (width >= 1600px) {
.\33xl\:text-rosa-500 {
color: #e5186a;
}
}"
)
);
}
#[test]
fn gen_css_with_shortcuts() {
let mut config = base_config();
config
.shortcuts
.add("btn", "bg-red-500 border-1 rounded-xl");
config.shortcuts.add("bg", "bg-blue-100");
let generated = generate(["btn", "bg-yellow-500"], &config);
assert_eq!(
generated,
String::from(
".btn {
border-radius: 0.75rem;
}
.btn {
border-width: 1px;
}
.bg-yellow-500 {
background-color: oklch(79.5% .184 86.047);
}
.btn {
background-color: oklch(63.7% .237 25.331);
}"
)
);
}
#[test]
fn gen_css_with_nested_shortcuts() {
let mut config = base_config();
config
.shortcuts
.add("btn", "bg-red-500 border-1 rounded-xl");
config.shortcuts.add("btn-primary", "btn bg-blue-500");
let generated = generate(["btn-primary"], &config);
assert_eq!(
generated,
String::from(
".btn-primary {
border-radius: 0.75rem;
}
.btn-primary {
border-width: 1px;
}
.btn-primary {
background-color: oklch(62.3% .214 259.815);
}
.btn-primary {
background-color: oklch(63.7% .237 25.331);
}"
)
);
}
#[test]
fn gen_css_with_shortcut_cycle() {
let mut config = base_config();
config
.shortcuts
.add("btn", "bg-red-500 border-1 rounded-xl btn-primary");
config.shortcuts.add("btn-primary", "btn bg-blue-500");
let generated = generate(["btn-primary"], &config);
assert_eq!(
generated,
String::from(
".btn-primary {
border-radius: 0.75rem;
}
.btn-primary {
border-width: 1px;
}
.btn-primary {
background-color: oklch(62.3% .214 259.815);
}
.btn-primary {
background-color: oklch(63.7% .237 25.331);
}"
)
);
}
#[test]
fn gen_css_with_safelist() {
let mut config = base_config();
config.safelist.add("text-red-500");
config.safelist.add("btn");
config.shortcuts.add("btn", "text-red-400");
let generated = generate(["bg-red-300"], &config);
assert_eq!(
generated,
String::from(
".bg-red-300 {
background-color: oklch(80.8% .114 19.571);
}
.btn {
color: oklch(70.4% .191 22.216);
}
.text-red-500 {
color: oklch(63.7% .237 25.331);
}"
)
);
}
#[test]
fn gen_css_with_custom_plugin() {
use crate::prelude::build_plugin::*;
const PLUGIN: StaticPlugin = Plugin::ListValues(ListValues {
prop: SingleProp("content"),
values: map! {
"emoji-tada" => "\"\u{1f389}\"",
"emoji-rocket" => "\"\u{1f680}\"",
},
..ListValues::default()
});
let mut config = base_config();
config.register_plugin(&PLUGIN);
let generated = generate(["emoji-tada"], &config);
assert_eq!(
generated,
String::from(
".emoji-tada {
content: \"\u{1f389}\";
}"
)
);
}
#[test]
fn gen_css_with_dynamic_plugin() {
use crate::prelude::build_plugin::*;
let mut config = Config::default();
config.preflight = Preflight::None;
config.register_dynamic_plugin(Plugin::ListValues(ListValues {
prop: DynamicPropertyName::SingleProp(String::from("content")),
values: HashMap::from([
(String::from("emoji-tada"), String::from("\"\u{1f389}\"")),
(String::from("emoji-rocket"), String::from("\"\u{1f680}\"")),
]),
..ListValues::default_dynamic()
}));
let generated = generate(["emoji-tada"], &config);
assert_eq!(
generated,
String::from(
".emoji-tada {
content: \"\u{1f389}\";
}"
)
);
}
#[test]
fn gen_css_with_custom_parsed_plugin() {
let config = match Config::from_file("tests/fixtures/custom-plugin-config.toml") {
Ok(c) => c,
Err(e) => panic!("{e}"),
};
let generated = generate(["emoji-tada"], &config);
assert_eq!(
generated,
String::from(
".emoji-tada {
content: \"\u{1f389}\";
}"
)
);
}
#[test]
fn gen_css_with_custom_parsed_plugin_and_multiple_props() {
let config = match Config::from_file("tests/fixtures/custom-plugin-config-multiple.toml") {
Ok(c) => c,
Err(e) => panic!("{e}"),
};
let generated = generate(["shape-roof-1"], &config);
assert_eq!(
generated,
String::from(
".shape-roof-1 {
border-inline: 1px;
border-top: 1px;
}"
)
);
}
#[test]
fn gen_css_with_custom_parsed_plugin_stroke() {
let config = match Config::from_file("tests/fixtures/custom-plugin-stroke.toml") {
Ok(c) => c,
Err(e) => panic!("{e}"),
};
let generated = generate(["custom-stroke-1", "custom-stroke-[12px]", "custom-stroke-[12%,1px]", "custom-stroke-[length:12px]"], &config);
assert_eq!(
generated,
String::from(
r".custom-stroke-1 {
stroke-width: 1px;
}
.custom-stroke-\[12\%\,1px\] {
stroke-width: 12%,1px;
}
.custom-stroke-\[12px\] {
stroke-width: 12px;
}
.custom-stroke-\[length\:12px\] {
stroke-width: 12px;
}"
)
);
}
#[test]
fn config_is_extended_and_overridden() {
let config = Config::from_file("tests/fixtures/custom-config.toml").unwrap();
let generated = generate(
[
"bg-rosa-500",
"bg-yellow-400",
"bg-yellow-100",
"3xl:underline",
"lg:text-rosa-500",
],
&config,
);
assert_eq!(
generated,
String::from(
r".bg-rosa-500 {
background-color: #e5186a;
}
.bg-yellow-100 {
background-color: oklch(97.3% .071 103.193);
}
.bg-yellow-400 {
background-color: #ffef0e;
}
@media (width >= 2000px) {
.lg\:text-rosa-500 {
color: #e5186a;
}
}
@media (width >= 1600px) {
.\33xl\:underline {
-webkit-text-decoration-line: underline;
text-decoration-line: underline;
}
}"
)
);
}
#[test]
fn deserialize_config() {
let mut config = base_config();
config.theme.colors.add("rosa-500", "#e5186a");
config.theme.colors.add("yellow-400", "#ffef0e");
config.theme.aria.add("current", "current=\"page\"");
config.theme.screens.add("lg", "2000px");
config.theme.screens.add("3xl", "1600px");
config.theme.dark_mode = DarkMode::new_class(".dark");
assert_eq!(
Config::from_file("tests/fixtures/custom-config.toml").unwrap(),
config
);
}
#[test]
fn serialize_config() {
let mut config = Config {
preflight: Preflight::None,
..Default::default()
};
config.theme.dark_mode = DarkMode::new_class(".dark");
config.theme.screens.add("3xl", "1600px");
config.theme.screens.add("lg", "2000px");
config.theme.colors.add("rosa-500", "#e5186a");
config.theme.colors.add("yellow-400", "#ffef0e");
config.theme.aria.add("current", "current=\"page\"");
let result = toml::to_string(&config).unwrap();
let expected_config = fs::read_to_string("tests/fixtures/custom-config.toml").unwrap();
assert_eq!(expected_config, result);
}
#[test]
fn toml_doc_tests() {
{
let toml_for_shortcuts = r#"
[shortcuts]
btn = "border-1 rounded-xl bg-red-500"
"#;
let config: Config = toml::from_str(toml_for_shortcuts).unwrap();
let generated = generate([r#"<button class="btn">Click me</button>"#], &config);
assert!(generated.ends_with(
r#".btn {
border-radius: 0.75rem;
}
.btn {
border-width: 1px;
}
.btn {
background-color: oklch(63.7% .237 25.331);
}"#
));
}
{
let toml_for_safelist = r#"safelist = ["text-blue-400", "text-gray-400"]"#;
let config: Config = toml::from_str(toml_for_safelist).unwrap();
let generated = generate([r#"<button class="bg-red-500">Click me</button>"#], &config);
assert!(generated.ends_with(
r#".bg-red-500 {
background-color: oklch(63.7% .237 25.331);
}
.text-blue-400 {
color: oklch(70.7% .165 254.624);
}
.text-gray-400 {
color: oklch(70.7% .022 261.325);
}"#
));
}
{
let toml_for_preflight = r#"preflight = "none""#;
let config: Config = toml::from_str(toml_for_preflight).unwrap();
assert!(generate([], &config).is_empty());
let toml_for_preflight = r#"preflight = { custom = "html, body { width: 100vw; height: 100vh; margin: 0; }" }"#;
let config: Config = toml::from_str(toml_for_preflight).unwrap();
assert_eq!(
generate([], &config),
"html, body { width: 100vw; height: 100vh; margin: 0; }"
);
let toml_for_preflight =
r#"preflight = { full = { font_family_mono = "'Fira Code'" } }"#;
let config: Config = toml::from_str(toml_for_preflight).unwrap();
assert!(generate([], &config).contains(
"code, kbd, samp, pre {
font-family: 'Fira Code';"
));
}
{
let toml_for_theme = r##"
[theme]
dark_mode = { class = "body.dark" }
[theme.colors]
primary = "#d3198c"
[theme.screens]
tablet = "640px"
[theme.containers]
medium = "640px"
[theme.aria]
current = 'current="page"'
"##;
let config: Config = toml::from_str(toml_for_theme).unwrap();
let generated = generate(
[
r#"<div class="bg-primary tablet:block aria-current:text-primary"><button class="bg-white @medium:flex">Click me</button>"#,
],
&config,
);
assert!(generated.ends_with(
r#"
.bg-primary {
background-color: #d3198c;
}
.bg-white {
background-color: #fff;
}
@media (width >= 640px) {
.tablet\:block {
display: block;
}
}
@container (width >= 640px) {
.\@medium\:flex {
display: flex;
}
}
.aria-current\:text-primary[aria-current="page"] {
color: #d3198c;
}"#
));
}
}
}