Skip to main content

encre_css/
config.rs

1//! Define the [`Config`] structure used to configure the [`generate`] function using a
2//! [Tailwind-like configuration](https://tailwindcss.com/docs/configuration).
3//!
4//! # Example
5//!
6//! ```
7//! use encre_css::{Config, config::DarkMode};
8//!
9//! let mut config = Config::default();
10//!
11//! // Toggles the dark mode using the class `.dark`
12//! config.theme.dark_mode = DarkMode::new_class(".dark");
13//!
14//! // Defines some custom colors, they will be usable in the `text`, `bg`,
15//! // `border`, etc utilities.
16//! config.theme.colors.add("primary", "#d3198c");
17//! config.theme.colors.add("secondary", "#fff");
18//!
19//! // Defines some custom screen breakpoints
20//! config.theme.screens.add("tablet", "640px");
21//! config.theme.screens.add("laptop", "1024px");
22//! config.theme.screens.add("desktop", "1280px");
23//!
24//! let generated = encre_css::generate(
25//!     ["tablet:dark:bg-primary"],
26//!     &config,
27//! );
28//!
29//! assert!(generated.ends_with(r#"@media (width >= 640px) {
30//!   .dark .tablet\:dark\:bg-primary {
31//!     background-color: #d3198c;
32//!   }
33//! }"#));
34//! ```
35//!
36//! The previous example is equivalent to the following TOML configuration file:
37//!
38//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[theme]</span>
39//! dark_mode = { class = <span class="string">".dark"</span> }
40//! colors = { primary = <span class="string">"#d3198c"</span>, secondary = <span class="string">"#fff"</span> }
41//! screens = { tablet = <span class="string">"640px"</span>, laptop = <span class="string">"1024px"</span>, desktop = <span class="string">"1280px"</span> }
42//! </code></pre></div>
43//!
44//! [`generate`]: crate::generate
45use crate::{
46    error::{Error, Result},
47    preflight::Preflight,
48    scanner::Scanner,
49    selector::Variant,
50};
51
52#[allow(clippy::wildcard_imports)]
53use crate::plugins::*;
54
55use phf::{phf_map as map, phf_ordered_map};
56use serde::{Deserialize, Serialize};
57use std::{
58    borrow::Cow,
59    collections::{BTreeMap, BTreeSet},
60    fmt, fs, iter,
61    path::Path,
62};
63
64/// The list of all default colors.
65///
66/// <table style="display: table;">
67///     <thead>
68///         <tr>
69///             <th style="text-align: center;">Name</th>
70///             <th style="text-align: center;">RGB Value</th>
71///             <th style="text-align: center;">Color</th>
72///         </tr>
73///     </thead>
74///     <tbody>
75///         <tr><td>red-50</td><td>oklch(97.1% .013 17.38)</td><td style="background-color: oklch(97.1% .013 17.38);"></td></tr>
76///         <tr><td>red-100</td><td>oklch(93.6% .032 17.717)</td><td style="background-color: oklch(93.6% .032 17.717);"></td></tr>
77///         <tr><td>red-200</td><td>oklch(88.5% .062 18.334)</td><td style="background-color: oklch(88.5% .062 18.334);"></td></tr>
78///         <tr><td>red-300</td><td>oklch(80.8% .114 19.571)</td><td style="background-color: oklch(80.8% .114 19.571);"></td></tr>
79///         <tr><td>red-400</td><td>oklch(70.4% .191 22.216)</td><td style="background-color: oklch(70.4% .191 22.216);"></td></tr>
80///         <tr><td>red-500</td><td>oklch(63.7% .237 25.331)</td><td style="background-color: oklch(63.7% .237 25.331);"></td></tr>
81///         <tr><td>red-600</td><td>oklch(57.7% .245 27.325)</td><td style="background-color: oklch(57.7% .245 27.325);"></td></tr>
82///         <tr><td>red-700</td><td>oklch(50.5% .213 27.518)</td><td style="background-color: oklch(50.5% .213 27.518);"></td></tr>
83///         <tr><td>red-800</td><td>oklch(44.4% .177 26.899)</td><td style="background-color: oklch(44.4% .177 26.899);"></td></tr>
84///         <tr><td>red-900</td><td>oklch(39.6% .141 25.723)</td><td style="background-color: oklch(39.6% .141 25.723);"></td></tr>
85///         <tr><td>red-950</td><td>oklch(25.8% .092 26.042)</td><td style="background-color: oklch(25.8% .092 26.042);"></td></tr>
86///         <tr><td>orange-50</td><td>oklch(98% .016 73.684)</td><td style="background-color: oklch(98% .016 73.684);"></td></tr>
87///         <tr><td>orange-100</td><td>oklch(95.4% .038 75.164)</td><td style="background-color: oklch(95.4% .038 75.164);"></td></tr>
88///         <tr><td>orange-200</td><td>oklch(90.1% .076 70.697)</td><td style="background-color: oklch(90.1% .076 70.697);"></td></tr>
89///         <tr><td>orange-300</td><td>oklch(83.7% .128 66.29)</td><td style="background-color: oklch(83.7% .128 66.29);"></td></tr>
90///         <tr><td>orange-400</td><td>oklch(75% .183 55.934)</td><td style="background-color: oklch(75% .183 55.934);"></td></tr>
91///         <tr><td>orange-500</td><td>oklch(70.5% .213 47.604)</td><td style="background-color: oklch(70.5% .213 47.604);"></td></tr>
92///         <tr><td>orange-600</td><td>oklch(64.6% .222 41.116)</td><td style="background-color: oklch(64.6% .222 41.116);"></td></tr>
93///         <tr><td>orange-700</td><td>oklch(55.3% .195 38.402)</td><td style="background-color: oklch(55.3% .195 38.402);"></td></tr>
94///         <tr><td>orange-800</td><td>oklch(47% .157 37.304)</td><td style="background-color: oklch(47% .157 37.304);"></td></tr>
95///         <tr><td>orange-900</td><td>oklch(40.8% .123 38.172)</td><td style="background-color: oklch(40.8% .123 38.172);"></td></tr>
96///         <tr><td>orange-950</td><td>oklch(26.6% .079 36.259)</td><td style="background-color: oklch(26.6% .079 36.259);"></td></tr>
97///         <tr><td>amber-50</td><td>oklch(98.7% .022 95.277)</td><td style="background-color: oklch(98.7% .022 95.277);"></td></tr>
98///         <tr><td>amber-100</td><td>oklch(96.2% .059 95.617)</td><td style="background-color: oklch(96.2% .059 95.617);"></td></tr>
99///         <tr><td>amber-200</td><td>oklch(92.4% .12 95.746)</td><td style="background-color: oklch(92.4% .12 95.746);"></td></tr>
100///         <tr><td>amber-300</td><td>oklch(87.9% .169 91.605)</td><td style="background-color: oklch(87.9% .169 91.605);"></td></tr>
101///         <tr><td>amber-400</td><td>oklch(82.8% .189 84.429)</td><td style="background-color: oklch(82.8% .189 84.429);"></td></tr>
102///         <tr><td>amber-500</td><td>oklch(76.9% .188 70.08)</td><td style="background-color: oklch(76.9% .188 70.08);"></td></tr>
103///         <tr><td>amber-600</td><td>oklch(66.6% .179 58.318)</td><td style="background-color: oklch(66.6% .179 58.318);"></td></tr>
104///         <tr><td>amber-700</td><td>oklch(55.5% .163 48.998)</td><td style="background-color: oklch(55.5% .163 48.998);"></td></tr>
105///         <tr><td>amber-800</td><td>oklch(47.3% .137 46.201)</td><td style="background-color: oklch(47.3% .137 46.201);"></td></tr>
106///         <tr><td>amber-900</td><td>oklch(41.4% .112 45.904)</td><td style="background-color: oklch(41.4% .112 45.904);"></td></tr>
107///         <tr><td>amber-950</td><td>oklch(27.9% .077 45.635)</td><td style="background-color: oklch(27.9% .077 45.635);"></td></tr>
108///         <tr><td>yellow-50</td><td>oklch(98.7% .026 102.212)</td><td style="background-color: oklch(98.7% .026 102.212);"></td></tr>
109///         <tr><td>yellow-100</td><td>oklch(97.3% .071 103.193)</td><td style="background-color: oklch(97.3% .071 103.193);"></td></tr>
110///         <tr><td>yellow-200</td><td>oklch(94.5% .129 101.54)</td><td style="background-color: oklch(94.5% .129 101.54);"></td></tr>
111///         <tr><td>yellow-300</td><td>oklch(90.5% .182 98.111)</td><td style="background-color: oklch(90.5% .182 98.111);"></td></tr>
112///         <tr><td>yellow-400</td><td>oklch(85.2% .199 91.936)</td><td style="background-color: oklch(85.2% .199 91.936);"></td></tr>
113///         <tr><td>yellow-500</td><td>oklch(79.5% .184 86.047)</td><td style="background-color: oklch(79.5% .184 86.047);"></td></tr>
114///         <tr><td>yellow-600</td><td>oklch(68.1% .162 75.834)</td><td style="background-color: oklch(68.1% .162 75.834);"></td></tr>
115///         <tr><td>yellow-700</td><td>oklch(55.4% .135 66.442)</td><td style="background-color: oklch(55.4% .135 66.442);"></td></tr>
116///         <tr><td>yellow-800</td><td>oklch(47.6% .114 61.907)</td><td style="background-color: oklch(47.6% .114 61.907);"></td></tr>
117///         <tr><td>yellow-900</td><td>oklch(42.1% .095 57.708)</td><td style="background-color: oklch(42.1% .095 57.708);"></td></tr>
118///         <tr><td>yellow-950</td><td>oklch(28.6% .066 53.813)</td><td style="background-color: oklch(28.6% .066 53.813);"></td></tr>
119///         <tr><td>lime-50</td><td>oklch(98.6% .031 120.757)</td><td style="background-color: oklch(98.6% .031 120.757);"></td></tr>
120///         <tr><td>lime-100</td><td>oklch(96.7% .067 122.328)</td><td style="background-color: oklch(96.7% .067 122.328);"></td></tr>
121///         <tr><td>lime-200</td><td>oklch(93.8% .127 124.321)</td><td style="background-color: oklch(93.8% .127 124.321);"></td></tr>
122///         <tr><td>lime-300</td><td>oklch(89.7% .196 126.665)</td><td style="background-color: oklch(89.7% .196 126.665);"></td></tr>
123///         <tr><td>lime-400</td><td>oklch(84.1% .238 128.85)</td><td style="background-color: oklch(84.1% .238 128.85);"></td></tr>
124///         <tr><td>lime-500</td><td>oklch(76.8% .233 130.85)</td><td style="background-color: oklch(76.8% .233 130.85);"></td></tr>
125///         <tr><td>lime-600</td><td>oklch(64.8% .2 131.684)</td><td style="background-color: oklch(64.8% .2 131.684);"></td></tr>
126///         <tr><td>lime-700</td><td>oklch(53.2% .157 131.589)</td><td style="background-color: oklch(53.2% .157 131.589);"></td></tr>
127///         <tr><td>lime-800</td><td>oklch(45.3% .124 130.933)</td><td style="background-color: oklch(45.3% .124 130.933);"></td></tr>
128///         <tr><td>lime-900</td><td>oklch(40.5% .101 131.063)</td><td style="background-color: oklch(40.5% .101 131.063);"></td></tr>
129///         <tr><td>lime-950</td><td>oklch(27.4% .072 132.109)</td><td style="background-color: oklch(27.4% .072 132.109);"></td></tr>
130///         <tr><td>green-50</td><td>oklch(98.2% .018 155.826)</td><td style="background-color: oklch(98.2% .018 155.826);"></td></tr>
131///         <tr><td>green-100</td><td>oklch(96.2% .044 156.743)</td><td style="background-color: oklch(96.2% .044 156.743);"></td></tr>
132///         <tr><td>green-200</td><td>oklch(92.5% .084 155.995)</td><td style="background-color: oklch(92.5% .084 155.995);"></td></tr>
133///         <tr><td>green-300</td><td>oklch(87.1% .15 154.449)</td><td style="background-color: oklch(87.1% .15 154.449);"></td></tr>
134///         <tr><td>green-400</td><td>oklch(79.2% .209 151.711)</td><td style="background-color: oklch(79.2% .209 151.711);"></td></tr>
135///         <tr><td>green-500</td><td>oklch(72.3% .219 149.579)</td><td style="background-color: oklch(72.3% .219 149.579);"></td></tr>
136///         <tr><td>green-600</td><td>oklch(62.7% .194 149.214)</td><td style="background-color: oklch(62.7% .194 149.214);"></td></tr>
137///         <tr><td>green-700</td><td>oklch(52.7% .154 150.069)</td><td style="background-color: oklch(52.7% .154 150.069);"></td></tr>
138///         <tr><td>green-800</td><td>oklch(44.8% .119 151.328)</td><td style="background-color: oklch(44.8% .119 151.328);"></td></tr>
139///         <tr><td>green-900</td><td>oklch(39.3% .095 152.535)</td><td style="background-color: oklch(39.3% .095 152.535);"></td></tr>
140///         <tr><td>green-950</td><td>oklch(26.6% .065 152.934)</td><td style="background-color: oklch(26.6% .065 152.934);"></td></tr>
141///         <tr><td>emerald-50</td><td>oklch(97.9% .021 166.113)</td><td style="background-color: oklch(97.9% .021 166.113);"></td></tr>
142///         <tr><td>emerald-100</td><td>oklch(95% .052 163.051)</td><td style="background-color: oklch(95% .052 163.051);"></td></tr>
143///         <tr><td>emerald-200</td><td>oklch(90.5% .093 164.15)</td><td style="background-color: oklch(90.5% .093 164.15);"></td></tr>
144///         <tr><td>emerald-300</td><td>oklch(84.5% .143 164.978)</td><td style="background-color: oklch(84.5% .143 164.978);"></td></tr>
145///         <tr><td>emerald-400</td><td>oklch(76.5% .177 163.223)</td><td style="background-color: oklch(76.5% .177 163.223);"></td></tr>
146///         <tr><td>emerald-500</td><td>oklch(69.6% .17 162.48)</td><td style="background-color: oklch(69.6% .17 162.48);"></td></tr>
147///         <tr><td>emerald-600</td><td>oklch(59.6% .145 163.225)</td><td style="background-color: oklch(59.6% .145 163.225);"></td></tr>
148///         <tr><td>emerald-700</td><td>oklch(50.8% .118 165.612)</td><td style="background-color: oklch(50.8% .118 165.612);"></td></tr>
149///         <tr><td>emerald-800</td><td>oklch(43.2% .095 166.913)</td><td style="background-color: oklch(43.2% .095 166.913);"></td></tr>
150///         <tr><td>emerald-900</td><td>oklch(37.8% .077 168.94)</td><td style="background-color: oklch(37.8% .077 168.94);"></td></tr>
151///         <tr><td>emerald-950</td><td>oklch(26.2% .051 172.552)</td><td style="background-color: oklch(26.2% .051 172.552);"></td></tr>
152///         <tr><td>teal-50</td><td>oklch(98.4% .014 180.72)</td><td style="background-color: oklch(98.4% .014 180.72);"></td></tr>
153///         <tr><td>teal-100</td><td>oklch(95.3% .051 180.801)</td><td style="background-color: oklch(95.3% .051 180.801);"></td></tr>
154///         <tr><td>teal-200</td><td>oklch(91% .096 180.426)</td><td style="background-color: oklch(91% .096 180.426);"></td></tr>
155///         <tr><td>teal-300</td><td>oklch(85.5% .138 181.071)</td><td style="background-color: oklch(85.5% .138 181.071);"></td></tr>
156///         <tr><td>teal-400</td><td>oklch(77.7% .152 181.912)</td><td style="background-color: oklch(77.7% .152 181.912);"></td></tr>
157///         <tr><td>teal-500</td><td>oklch(70.4% .14 182.503)</td><td style="background-color: oklch(70.4% .14 182.503);"></td></tr>
158///         <tr><td>teal-600</td><td>oklch(60% .118 184.704)</td><td style="background-color: oklch(60% .118 184.704);"></td></tr>
159///         <tr><td>teal-700</td><td>oklch(51.1% .096 186.391)</td><td style="background-color: oklch(51.1% .096 186.391);"></td></tr>
160///         <tr><td>teal-800</td><td>oklch(43.7% .078 188.216)</td><td style="background-color: oklch(43.7% .078 188.216);"></td></tr>
161///         <tr><td>teal-900</td><td>oklch(38.6% .063 188.416)</td><td style="background-color: oklch(38.6% .063 188.416);"></td></tr>
162///         <tr><td>teal-950</td><td>oklch(27.7% .046 192.524)</td><td style="background-color: oklch(27.7% .046 192.524);"></td></tr>
163///         <tr><td>cyan-50</td><td>oklch(98.4% .019 200.873)</td><td style="background-color: oklch(98.4% .019 200.873);"></td></tr>
164///         <tr><td>cyan-100</td><td>oklch(95.6% .045 203.388)</td><td style="background-color: oklch(95.6% .045 203.388);"></td></tr>
165///         <tr><td>cyan-200</td><td>oklch(91.7% .08 205.041)</td><td style="background-color: oklch(91.7% .08 205.041);"></td></tr>
166///         <tr><td>cyan-300</td><td>oklch(86.5% .127 207.078)</td><td style="background-color: oklch(86.5% .127 207.078);"></td></tr>
167///         <tr><td>cyan-400</td><td>oklch(78.9% .154 211.53)</td><td style="background-color: oklch(78.9% .154 211.53);"></td></tr>
168///         <tr><td>cyan-500</td><td>oklch(71.5% .143 215.221)</td><td style="background-color: oklch(71.5% .143 215.221);"></td></tr>
169///         <tr><td>cyan-600</td><td>oklch(60.9% .126 221.723)</td><td style="background-color: oklch(60.9% .126 221.723);"></td></tr>
170///         <tr><td>cyan-700</td><td>oklch(52% .105 223.128)</td><td style="background-color: oklch(52% .105 223.128);"></td></tr>
171///         <tr><td>cyan-800</td><td>oklch(45% .085 224.283)</td><td style="background-color: oklch(45% .085 224.283);"></td></tr>
172///         <tr><td>cyan-900</td><td>oklch(39.8% .07 227.392)</td><td style="background-color: oklch(39.8% .07 227.392);"></td></tr>
173///         <tr><td>cyan-950</td><td>oklch(30.2% .056 229.695)</td><td style="background-color: oklch(30.2% .056 229.695);"></td></tr>
174///         <tr><td>sky-50</td><td>oklch(97.7% .013 236.62)</td><td style="background-color: oklch(97.7% .013 236.62);"></td></tr>
175///         <tr><td>sky-100</td><td>oklch(95.1% .026 236.824)</td><td style="background-color: oklch(95.1% .026 236.824);"></td></tr>
176///         <tr><td>sky-200</td><td>oklch(90.1% .058 230.902)</td><td style="background-color: oklch(90.1% .058 230.902);"></td></tr>
177///         <tr><td>sky-300</td><td>oklch(82.8% .111 230.318)</td><td style="background-color: oklch(82.8% .111 230.318);"></td></tr>
178///         <tr><td>sky-400</td><td>oklch(74.6% .16 232.661)</td><td style="background-color: oklch(74.6% .16 232.661);"></td></tr>
179///         <tr><td>sky-500</td><td>oklch(68.5% .169 237.323)</td><td style="background-color: oklch(68.5% .169 237.323);"></td></tr>
180///         <tr><td>sky-600</td><td>oklch(58.8% .158 241.966)</td><td style="background-color: oklch(58.8% .158 241.966);"></td></tr>
181///         <tr><td>sky-700</td><td>oklch(50% .134 242.749)</td><td style="background-color: oklch(50% .134 242.749);"></td></tr>
182///         <tr><td>sky-800</td><td>oklch(44.3% .11 240.79)</td><td style="background-color: oklch(44.3% .11 240.79);"></td></tr>
183///         <tr><td>sky-900</td><td>oklch(39.1% .09 240.876)</td><td style="background-color: oklch(39.1% .09 240.876);"></td></tr>
184///         <tr><td>sky-950</td><td>oklch(29.3% .066 243.157)</td><td style="background-color: oklch(29.3% .066 243.157);"></td></tr>
185///         <tr><td>blue-50</td><td>oklch(97% .014 254.604)</td><td style="background-color: oklch(97% .014 254.604);"></td></tr>
186///         <tr><td>blue-100</td><td>oklch(93.2% .032 255.585)</td><td style="background-color: oklch(93.2% .032 255.585);"></td></tr>
187///         <tr><td>blue-200</td><td>oklch(88.2% .059 254.128)</td><td style="background-color: oklch(88.2% .059 254.128);"></td></tr>
188///         <tr><td>blue-300</td><td>oklch(80.9% .105 251.813)</td><td style="background-color: oklch(80.9% .105 251.813);"></td></tr>
189///         <tr><td>blue-400</td><td>oklch(70.7% .165 254.624)</td><td style="background-color: oklch(70.7% .165 254.624);"></td></tr>
190///         <tr><td>blue-500</td><td>oklch(62.3% .214 259.815)</td><td style="background-color: oklch(62.3% .214 259.815);"></td></tr>
191///         <tr><td>blue-600</td><td>oklch(54.6% .245 262.881)</td><td style="background-color: oklch(54.6% .245 262.881);"></td></tr>
192///         <tr><td>blue-700</td><td>oklch(48.8% .243 264.376)</td><td style="background-color: oklch(48.8% .243 264.376);"></td></tr>
193///         <tr><td>blue-800</td><td>oklch(42.4% .199 265.638)</td><td style="background-color: oklch(42.4% .199 265.638);"></td></tr>
194///         <tr><td>blue-900</td><td>oklch(37.9% .146 265.522)</td><td style="background-color: oklch(37.9% .146 265.522);"></td></tr>
195///         <tr><td>blue-950</td><td>oklch(28.2% .091 267.935)</td><td style="background-color: oklch(28.2% .091 267.935);"></td></tr>
196///         <tr><td>indigo-50</td><td>oklch(96.2% .018 272.314)</td><td style="background-color: oklch(96.2% .018 272.314);"></td></tr>
197///         <tr><td>indigo-100</td><td>oklch(93% .034 272.788)</td><td style="background-color: oklch(93% .034 272.788);"></td></tr>
198///         <tr><td>indigo-200</td><td>oklch(87% .065 274.039)</td><td style="background-color: oklch(87% .065 274.039);"></td></tr>
199///         <tr><td>indigo-300</td><td>oklch(78.5% .115 274.713)</td><td style="background-color: oklch(78.5% .115 274.713);"></td></tr>
200///         <tr><td>indigo-400</td><td>oklch(67.3% .182 276.935)</td><td style="background-color: oklch(67.3% .182 276.935);"></td></tr>
201///         <tr><td>indigo-500</td><td>oklch(58.5% .233 277.117)</td><td style="background-color: oklch(58.5% .233 277.117);"></td></tr>
202///         <tr><td>indigo-600</td><td>oklch(51.1% .262 276.966)</td><td style="background-color: oklch(51.1% .262 276.966);"></td></tr>
203///         <tr><td>indigo-700</td><td>oklch(45.7% .24 277.023)</td><td style="background-color: oklch(45.7% .24 277.023);"></td></tr>
204///         <tr><td>indigo-800</td><td>oklch(39.8% .195 277.366)</td><td style="background-color: oklch(39.8% .195 277.366);"></td></tr>
205///         <tr><td>indigo-900</td><td>oklch(35.9% .144 278.697)</td><td style="background-color: oklch(35.9% .144 278.697);"></td></tr>
206///         <tr><td>indigo-950</td><td>oklch(25.7% .09 281.288)</td><td style="background-color: oklch(25.7% .09 281.288);"></td></tr>
207///         <tr><td>violet-50</td><td>oklch(96.9% .016 293.756)</td><td style="background-color: oklch(96.9% .016 293.756);"></td></tr>
208///         <tr><td>violet-100</td><td>oklch(94.3% .029 294.588)</td><td style="background-color: oklch(94.3% .029 294.588);"></td></tr>
209///         <tr><td>violet-200</td><td>oklch(89.4% .057 293.283)</td><td style="background-color: oklch(89.4% .057 293.283);"></td></tr>
210///         <tr><td>violet-300</td><td>oklch(81.1% .111 293.571)</td><td style="background-color: oklch(81.1% .111 293.571);"></td></tr>
211///         <tr><td>violet-400</td><td>oklch(70.2% .183 293.541)</td><td style="background-color: oklch(70.2% .183 293.541);"></td></tr>
212///         <tr><td>violet-500</td><td>oklch(60.6% .25 292.717)</td><td style="background-color: oklch(60.6% .25 292.717);"></td></tr>
213///         <tr><td>violet-600</td><td>oklch(54.1% .281 293.009)</td><td style="background-color: oklch(54.1% .281 293.009);"></td></tr>
214///         <tr><td>violet-700</td><td>oklch(49.1% .27 292.581)</td><td style="background-color: oklch(49.1% .27 292.581);"></td></tr>
215///         <tr><td>violet-800</td><td>oklch(43.2% .232 292.759)</td><td style="background-color: oklch(43.2% .232 292.759);"></td></tr>
216///         <tr><td>violet-900</td><td>oklch(38% .189 293.745)</td><td style="background-color: oklch(38% .189 293.745);"></td></tr>
217///         <tr><td>violet-950</td><td>oklch(28.3% .141 291.089)</td><td style="background-color: oklch(28.3% .141 291.089);"></td></tr>
218///         <tr><td>purple-50</td><td>oklch(97.7% .014 308.299)</td><td style="background-color: oklch(97.7% .014 308.299);"></td></tr>
219///         <tr><td>purple-100</td><td>oklch(94.6% .033 307.174)</td><td style="background-color: oklch(94.6% .033 307.174);"></td></tr>
220///         <tr><td>purple-200</td><td>oklch(90.2% .063 306.703)</td><td style="background-color: oklch(90.2% .063 306.703);"></td></tr>
221///         <tr><td>purple-300</td><td>oklch(82.7% .119 306.383)</td><td style="background-color: oklch(82.7% .119 306.383);"></td></tr>
222///         <tr><td>purple-400</td><td>oklch(71.4% .203 305.504)</td><td style="background-color: oklch(71.4% .203 305.504);"></td></tr>
223///         <tr><td>purple-500</td><td>oklch(62.7% .265 303.9)</td><td style="background-color: oklch(62.7% .265 303.9);"></td></tr>
224///         <tr><td>purple-600</td><td>oklch(55.8% .288 302.321)</td><td style="background-color: oklch(55.8% .288 302.321);"></td></tr>
225///         <tr><td>purple-700</td><td>oklch(49.6% .265 301.924)</td><td style="background-color: oklch(49.6% .265 301.924);"></td></tr>
226///         <tr><td>purple-800</td><td>oklch(43.8% .218 303.724)</td><td style="background-color: oklch(43.8% .218 303.724);"></td></tr>
227///         <tr><td>purple-900</td><td>oklch(38.1% .176 304.987)</td><td style="background-color: oklch(38.1% .176 304.987);"></td></tr>
228///         <tr><td>purple-950</td><td>oklch(29.1% .149 302.717)</td><td style="background-color: oklch(29.1% .149 302.717);"></td></tr>
229///         <tr><td>fuchsia-50</td><td>oklch(97.7% .017 320.058)</td><td style="background-color: oklch(97.7% .017 320.058);"></td></tr>
230///         <tr><td>fuchsia-100</td><td>oklch(95.2% .037 318.852)</td><td style="background-color: oklch(95.2% .037 318.852);"></td></tr>
231///         <tr><td>fuchsia-200</td><td>oklch(90.3% .076 319.62)</td><td style="background-color: oklch(90.3% .076 319.62);"></td></tr>
232///         <tr><td>fuchsia-300</td><td>oklch(83.3% .145 321.434)</td><td style="background-color: oklch(83.3% .145 321.434);"></td></tr>
233///         <tr><td>fuchsia-400</td><td>oklch(74% .238 322.16)</td><td style="background-color: oklch(74% .238 322.16);"></td></tr>
234///         <tr><td>fuchsia-500</td><td>oklch(66.7% .295 322.15)</td><td style="background-color: oklch(66.7% .295 322.15);"></td></tr>
235///         <tr><td>fuchsia-600</td><td>oklch(59.1% .293 322.896)</td><td style="background-color: oklch(59.1% .293 322.896);"></td></tr>
236///         <tr><td>fuchsia-700</td><td>oklch(51.8% .253 323.949)</td><td style="background-color: oklch(51.8% .253 323.949);"></td></tr>
237///         <tr><td>fuchsia-800</td><td>oklch(45.2% .211 324.591)</td><td style="background-color: oklch(45.2% .211 324.591);"></td></tr>
238///         <tr><td>fuchsia-900</td><td>oklch(40.1% .17 325.612)</td><td style="background-color: oklch(40.1% .17 325.612);"></td></tr>
239///         <tr><td>fuchsia-950</td><td>oklch(29.3% .136 325.661)</td><td style="background-color: oklch(29.3% .136 325.661);"></td></tr>
240///         <tr><td>pink-50</td><td>oklch(97.1% .014 343.198)</td><td style="background-color: oklch(97.1% .014 343.198);"></td></tr>
241///         <tr><td>pink-100</td><td>oklch(94.8% .028 342.258)</td><td style="background-color: oklch(94.8% .028 342.258);"></td></tr>
242///         <tr><td>pink-200</td><td>oklch(89.9% .061 343.231)</td><td style="background-color: oklch(89.9% .061 343.231);"></td></tr>
243///         <tr><td>pink-300</td><td>oklch(82.3% .12 346.018)</td><td style="background-color: oklch(82.3% .12 346.018);"></td></tr>
244///         <tr><td>pink-400</td><td>oklch(71.8% .202 349.761)</td><td style="background-color: oklch(71.8% .202 349.761);"></td></tr>
245///         <tr><td>pink-500</td><td>oklch(65.6% .241 354.308)</td><td style="background-color: oklch(65.6% .241 354.308);"></td></tr>
246///         <tr><td>pink-600</td><td>oklch(59.2% .249 .584)</td><td style="background-color: oklch(59.2% .249 .584);"></td></tr>
247///         <tr><td>pink-700</td><td>oklch(52.5% .223 3.958)</td><td style="background-color: oklch(52.5% .223 3.958);"></td></tr>
248///         <tr><td>pink-800</td><td>oklch(45.9% .187 3.815)</td><td style="background-color: oklch(45.9% .187 3.815);"></td></tr>
249///         <tr><td>pink-900</td><td>oklch(40.8% .153 2.432)</td><td style="background-color: oklch(40.8% .153 2.432);"></td></tr>
250///         <tr><td>pink-950</td><td>oklch(28.4% .109 3.907)</td><td style="background-color: oklch(28.4% .109 3.907);"></td></tr>
251///         <tr><td>rose-50</td><td>oklch(96.9% .015 12.422)</td><td style="background-color: oklch(96.9% .015 12.422);"></td></tr>
252///         <tr><td>rose-100</td><td>oklch(94.1% .03 12.58)</td><td style="background-color: oklch(94.1% .03 12.58);"></td></tr>
253///         <tr><td>rose-200</td><td>oklch(89.2% .058 10.001)</td><td style="background-color: oklch(89.2% .058 10.001);"></td></tr>
254///         <tr><td>rose-300</td><td>oklch(81% .117 11.638)</td><td style="background-color: oklch(81% .117 11.638);"></td></tr>
255///         <tr><td>rose-400</td><td>oklch(71.2% .194 13.428)</td><td style="background-color: oklch(71.2% .194 13.428);"></td></tr>
256///         <tr><td>rose-500</td><td>oklch(64.5% .246 16.439)</td><td style="background-color: oklch(64.5% .246 16.439);"></td></tr>
257///         <tr><td>rose-600</td><td>oklch(58.6% .253 17.585)</td><td style="background-color: oklch(58.6% .253 17.585);"></td></tr>
258///         <tr><td>rose-700</td><td>oklch(51.4% .222 16.935)</td><td style="background-color: oklch(51.4% .222 16.935);"></td></tr>
259///         <tr><td>rose-800</td><td>oklch(45.5% .188 13.697)</td><td style="background-color: oklch(45.5% .188 13.697);"></td></tr>
260///         <tr><td>rose-900</td><td>oklch(41% .159 10.272)</td><td style="background-color: oklch(41% .159 10.272);"></td></tr>
261///         <tr><td>rose-950</td><td>oklch(27.1% .105 12.094)</td><td style="background-color: oklch(27.1% .105 12.094);"></td></tr>
262///         <tr><td>slate-50</td><td>oklch(98.4% .003 247.858)</td><td style="background-color: oklch(98.4% .003 247.858);"></td></tr>
263///         <tr><td>slate-100</td><td>oklch(96.8% .007 247.896)</td><td style="background-color: oklch(96.8% .007 247.896);"></td></tr>
264///         <tr><td>slate-200</td><td>oklch(92.9% .013 255.508)</td><td style="background-color: oklch(92.9% .013 255.508);"></td></tr>
265///         <tr><td>slate-300</td><td>oklch(86.9% .022 252.894)</td><td style="background-color: oklch(86.9% .022 252.894);"></td></tr>
266///         <tr><td>slate-400</td><td>oklch(70.4% .04 256.788)</td><td style="background-color: oklch(70.4% .04 256.788);"></td></tr>
267///         <tr><td>slate-500</td><td>oklch(55.4% .046 257.417)</td><td style="background-color: oklch(55.4% .046 257.417);"></td></tr>
268///         <tr><td>slate-600</td><td>oklch(44.6% .043 257.281)</td><td style="background-color: oklch(44.6% .043 257.281);"></td></tr>
269///         <tr><td>slate-700</td><td>oklch(37.2% .044 257.287)</td><td style="background-color: oklch(37.2% .044 257.287);"></td></tr>
270///         <tr><td>slate-800</td><td>oklch(27.9% .041 260.031)</td><td style="background-color: oklch(27.9% .041 260.031);"></td></tr>
271///         <tr><td>slate-900</td><td>oklch(20.8% .042 265.755)</td><td style="background-color: oklch(20.8% .042 265.755);"></td></tr>
272///         <tr><td>slate-950</td><td>oklch(12.9% .042 264.695)</td><td style="background-color: oklch(12.9% .042 264.695);"></td></tr>
273///         <tr><td>gray-50</td><td>oklch(98.5% .002 247.839)</td><td style="background-color: oklch(98.5% .002 247.839);"></td></tr>
274///         <tr><td>gray-100</td><td>oklch(96.7% .003 264.542)</td><td style="background-color: oklch(96.7% .003 264.542);"></td></tr>
275///         <tr><td>gray-200</td><td>oklch(92.8% .006 264.531)</td><td style="background-color: oklch(92.8% .006 264.531);"></td></tr>
276///         <tr><td>gray-300</td><td>oklch(87.2% .01 258.338)</td><td style="background-color: oklch(87.2% .01 258.338);"></td></tr>
277///         <tr><td>gray-400</td><td>oklch(70.7% .022 261.325)</td><td style="background-color: oklch(70.7% .022 261.325);"></td></tr>
278///         <tr><td>gray-500</td><td>oklch(55.1% .027 264.364)</td><td style="background-color: oklch(55.1% .027 264.364);"></td></tr>
279///         <tr><td>gray-600</td><td>oklch(44.6% .03 256.802)</td><td style="background-color: oklch(44.6% .03 256.802);"></td></tr>
280///         <tr><td>gray-700</td><td>oklch(37.3% .034 259.733)</td><td style="background-color: oklch(37.3% .034 259.733);"></td></tr>
281///         <tr><td>gray-800</td><td>oklch(27.8% .033 256.848)</td><td style="background-color: oklch(27.8% .033 256.848);"></td></tr>
282///         <tr><td>gray-900</td><td>oklch(21% .034 264.665)</td><td style="background-color: oklch(21% .034 264.665);"></td></tr>
283///         <tr><td>gray-950</td><td>oklch(13% .028 261.692)</td><td style="background-color: oklch(13% .028 261.692);"></td></tr>
284///         <tr><td>zinc-50</td><td>oklch(98.5% 0 0)</td><td style="background-color: oklch(98.5% 0 0);"></td></tr>
285///         <tr><td>zinc-100</td><td>oklch(96.7% .001 286.375)</td><td style="background-color: oklch(96.7% .001 286.375);"></td></tr>
286///         <tr><td>zinc-200</td><td>oklch(92% .004 286.32)</td><td style="background-color: oklch(92% .004 286.32);"></td></tr>
287///         <tr><td>zinc-300</td><td>oklch(87.1% .006 286.286)</td><td style="background-color: oklch(87.1% .006 286.286);"></td></tr>
288///         <tr><td>zinc-400</td><td>oklch(70.5% .015 286.067)</td><td style="background-color: oklch(70.5% .015 286.067);"></td></tr>
289///         <tr><td>zinc-500</td><td>oklch(55.2% .016 285.938)</td><td style="background-color: oklch(55.2% .016 285.938);"></td></tr>
290///         <tr><td>zinc-600</td><td>oklch(44.2% .017 285.786)</td><td style="background-color: oklch(44.2% .017 285.786);"></td></tr>
291///         <tr><td>zinc-700</td><td>oklch(37% .013 285.805)</td><td style="background-color: oklch(37% .013 285.805);"></td></tr>
292///         <tr><td>zinc-800</td><td>oklch(27.4% .006 286.033)</td><td style="background-color: oklch(27.4% .006 286.033);"></td></tr>
293///         <tr><td>zinc-900</td><td>oklch(21% .006 285.885)</td><td style="background-color: oklch(21% .006 285.885);"></td></tr>
294///         <tr><td>zinc-950</td><td>oklch(14.1% .005 285.823)</td><td style="background-color: oklch(14.1% .005 285.823);"></td></tr>
295///         <tr><td>neutral-50</td><td>oklch(98.5% 0 0)</td><td style="background-color: oklch(98.5% 0 0);"></td></tr>
296///         <tr><td>neutral-100</td><td>oklch(97% 0 0)</td><td style="background-color: oklch(97% 0 0);"></td></tr>
297///         <tr><td>neutral-200</td><td>oklch(92.2% 0 0)</td><td style="background-color: oklch(92.2% 0 0);"></td></tr>
298///         <tr><td>neutral-300</td><td>oklch(87% 0 0)</td><td style="background-color: oklch(87% 0 0);"></td></tr>
299///         <tr><td>neutral-400</td><td>oklch(70.8% 0 0)</td><td style="background-color: oklch(70.8% 0 0);"></td></tr>
300///         <tr><td>neutral-500</td><td>oklch(55.6% 0 0)</td><td style="background-color: oklch(55.6% 0 0);"></td></tr>
301///         <tr><td>neutral-600</td><td>oklch(43.9% 0 0)</td><td style="background-color: oklch(43.9% 0 0);"></td></tr>
302///         <tr><td>neutral-700</td><td>oklch(37.1% 0 0)</td><td style="background-color: oklch(37.1% 0 0);"></td></tr>
303///         <tr><td>neutral-800</td><td>oklch(26.9% 0 0)</td><td style="background-color: oklch(26.9% 0 0);"></td></tr>
304///         <tr><td>neutral-900</td><td>oklch(20.5% 0 0)</td><td style="background-color: oklch(20.5% 0 0);"></td></tr>
305///         <tr><td>neutral-950</td><td>oklch(14.5% 0 0)</td><td style="background-color: oklch(14.5% 0 0);"></td></tr>
306///         <tr><td>stone-50</td><td>oklch(98.5% .001 106.423)</td><td style="background-color: oklch(98.5% .001 106.423);"></td></tr>
307///         <tr><td>stone-100</td><td>oklch(97% .001 106.424)</td><td style="background-color: oklch(97% .001 106.424);"></td></tr>
308///         <tr><td>stone-200</td><td>oklch(92.3% .003 48.717)</td><td style="background-color: oklch(92.3% .003 48.717);"></td></tr>
309///         <tr><td>stone-300</td><td>oklch(86.9% .005 56.366)</td><td style="background-color: oklch(86.9% .005 56.366);"></td></tr>
310///         <tr><td>stone-400</td><td>oklch(70.9% .01 56.259)</td><td style="background-color: oklch(70.9% .01 56.259);"></td></tr>
311///         <tr><td>stone-500</td><td>oklch(55.3% .013 58.071)</td><td style="background-color: oklch(55.3% .013 58.071);"></td></tr>
312///         <tr><td>stone-600</td><td>oklch(44.4% .011 73.639)</td><td style="background-color: oklch(44.4% .011 73.639);"></td></tr>
313///         <tr><td>stone-700</td><td>oklch(37.4% .01 67.558)</td><td style="background-color: oklch(37.4% .01 67.558);"></td></tr>
314///         <tr><td>stone-800</td><td>oklch(26.8% .007 34.298)</td><td style="background-color: oklch(26.8% .007 34.298);"></td></tr>
315///         <tr><td>stone-900</td><td>oklch(21.6% .006 56.043)</td><td style="background-color: oklch(21.6% .006 56.043);"></td></tr>
316///         <tr><td>stone-950</td><td>oklch(14.7% .004 49.25)</td><td style="background-color: oklch(14.7% .004 49.25);"></td></tr>
317///         <tr><td>black</td><td>#000</td><td style="background-color: #000;"></td></tr>
318///         <tr><td>white</td><td>#fff</td><td style="background-color: #fff;"></td></tr>
319///     </tbody>
320/// </table>
321///
322/// Based on [Tailwind's default color palette](https://tailwindcss.com/docs/customizing-colors).
323pub const BUILTIN_COLORS: phf::Map<&str, &'static str> = map! {
324    "red-50" => "oklch(97.1% .013 17.38)",
325    "red-100" => "oklch(93.6% .032 17.717)",
326    "red-200" => "oklch(88.5% .062 18.334)",
327    "red-300" => "oklch(80.8% .114 19.571)",
328    "red-400" => "oklch(70.4% .191 22.216)",
329    "red-500" => "oklch(63.7% .237 25.331)",
330    "red-600" => "oklch(57.7% .245 27.325)",
331    "red-700" => "oklch(50.5% .213 27.518)",
332    "red-800" => "oklch(44.4% .177 26.899)",
333    "red-900" => "oklch(39.6% .141 25.723)",
334    "red-950" => "oklch(25.8% .092 26.042)",
335    "orange-50" => "oklch(98% .016 73.684)",
336    "orange-100" => "oklch(95.4% .038 75.164)",
337    "orange-200" => "oklch(90.1% .076 70.697)",
338    "orange-300" => "oklch(83.7% .128 66.29)",
339    "orange-400" => "oklch(75% .183 55.934)",
340    "orange-500" => "oklch(70.5% .213 47.604)",
341    "orange-600" => "oklch(64.6% .222 41.116)",
342    "orange-700" => "oklch(55.3% .195 38.402)",
343    "orange-800" => "oklch(47% .157 37.304)",
344    "orange-900" => "oklch(40.8% .123 38.172)",
345    "orange-950" => "oklch(26.6% .079 36.259)",
346    "amber-50" => "oklch(98.7% .022 95.277)",
347    "amber-100" => "oklch(96.2% .059 95.617)",
348    "amber-200" => "oklch(92.4% .12 95.746)",
349    "amber-300" => "oklch(87.9% .169 91.605)",
350    "amber-400" => "oklch(82.8% .189 84.429)",
351    "amber-500" => "oklch(76.9% .188 70.08)",
352    "amber-600" => "oklch(66.6% .179 58.318)",
353    "amber-700" => "oklch(55.5% .163 48.998)",
354    "amber-800" => "oklch(47.3% .137 46.201)",
355    "amber-900" => "oklch(41.4% .112 45.904)",
356    "amber-950" => "oklch(27.9% .077 45.635)",
357    "yellow-50" => "oklch(98.7% .026 102.212)",
358    "yellow-100" => "oklch(97.3% .071 103.193)",
359    "yellow-200" => "oklch(94.5% .129 101.54)",
360    "yellow-300" => "oklch(90.5% .182 98.111)",
361    "yellow-400" => "oklch(85.2% .199 91.936)",
362    "yellow-500" => "oklch(79.5% .184 86.047)",
363    "yellow-600" => "oklch(68.1% .162 75.834)",
364    "yellow-700" => "oklch(55.4% .135 66.442)",
365    "yellow-800" => "oklch(47.6% .114 61.907)",
366    "yellow-900" => "oklch(42.1% .095 57.708)",
367    "yellow-950" => "oklch(28.6% .066 53.813)",
368    "lime-50" => "oklch(98.6% .031 120.757)",
369    "lime-100" => "oklch(96.7% .067 122.328)",
370    "lime-200" => "oklch(93.8% .127 124.321)",
371    "lime-300" => "oklch(89.7% .196 126.665)",
372    "lime-400" => "oklch(84.1% .238 128.85)",
373    "lime-500" => "oklch(76.8% .233 130.85)",
374    "lime-600" => "oklch(64.8% .2 131.684)",
375    "lime-700" => "oklch(53.2% .157 131.589)",
376    "lime-800" => "oklch(45.3% .124 130.933)",
377    "lime-900" => "oklch(40.5% .101 131.063)",
378    "lime-950" => "oklch(27.4% .072 132.109)",
379    "green-50" => "oklch(98.2% .018 155.826)",
380    "green-100" => "oklch(96.2% .044 156.743)",
381    "green-200" => "oklch(92.5% .084 155.995)",
382    "green-300" => "oklch(87.1% .15 154.449)",
383    "green-400" => "oklch(79.2% .209 151.711)",
384    "green-500" => "oklch(72.3% .219 149.579)",
385    "green-600" => "oklch(62.7% .194 149.214)",
386    "green-700" => "oklch(52.7% .154 150.069)",
387    "green-800" => "oklch(44.8% .119 151.328)",
388    "green-900" => "oklch(39.3% .095 152.535)",
389    "green-950" => "oklch(26.6% .065 152.934)",
390    "emerald-50" => "oklch(97.9% .021 166.113)",
391    "emerald-100" => "oklch(95% .052 163.051)",
392    "emerald-200" => "oklch(90.5% .093 164.15)",
393    "emerald-300" => "oklch(84.5% .143 164.978)",
394    "emerald-400" => "oklch(76.5% .177 163.223)",
395    "emerald-500" => "oklch(69.6% .17 162.48)",
396    "emerald-600" => "oklch(59.6% .145 163.225)",
397    "emerald-700" => "oklch(50.8% .118 165.612)",
398    "emerald-800" => "oklch(43.2% .095 166.913)",
399    "emerald-900" => "oklch(37.8% .077 168.94)",
400    "emerald-950" => "oklch(26.2% .051 172.552)",
401    "teal-50" => "oklch(98.4% .014 180.72)",
402    "teal-100" => "oklch(95.3% .051 180.801)",
403    "teal-200" => "oklch(91% .096 180.426)",
404    "teal-300" => "oklch(85.5% .138 181.071)",
405    "teal-400" => "oklch(77.7% .152 181.912)",
406    "teal-500" => "oklch(70.4% .14 182.503)",
407    "teal-600" => "oklch(60% .118 184.704)",
408    "teal-700" => "oklch(51.1% .096 186.391)",
409    "teal-800" => "oklch(43.7% .078 188.216)",
410    "teal-900" => "oklch(38.6% .063 188.416)",
411    "teal-950" => "oklch(27.7% .046 192.524)",
412    "cyan-50" => "oklch(98.4% .019 200.873)",
413    "cyan-100" => "oklch(95.6% .045 203.388)",
414    "cyan-200" => "oklch(91.7% .08 205.041)",
415    "cyan-300" => "oklch(86.5% .127 207.078)",
416    "cyan-400" => "oklch(78.9% .154 211.53)",
417    "cyan-500" => "oklch(71.5% .143 215.221)",
418    "cyan-600" => "oklch(60.9% .126 221.723)",
419    "cyan-700" => "oklch(52% .105 223.128)",
420    "cyan-800" => "oklch(45% .085 224.283)",
421    "cyan-900" => "oklch(39.8% .07 227.392)",
422    "cyan-950" => "oklch(30.2% .056 229.695)",
423    "sky-50" => "oklch(97.7% .013 236.62)",
424    "sky-100" => "oklch(95.1% .026 236.824)",
425    "sky-200" => "oklch(90.1% .058 230.902)",
426    "sky-300" => "oklch(82.8% .111 230.318)",
427    "sky-400" => "oklch(74.6% .16 232.661)",
428    "sky-500" => "oklch(68.5% .169 237.323)",
429    "sky-600" => "oklch(58.8% .158 241.966)",
430    "sky-700" => "oklch(50% .134 242.749)",
431    "sky-800" => "oklch(44.3% .11 240.79)",
432    "sky-900" => "oklch(39.1% .09 240.876)",
433    "sky-950" => "oklch(29.3% .066 243.157)",
434    "blue-50" => "oklch(97% .014 254.604)",
435    "blue-100" => "oklch(93.2% .032 255.585)",
436    "blue-200" => "oklch(88.2% .059 254.128)",
437    "blue-300" => "oklch(80.9% .105 251.813)",
438    "blue-400" => "oklch(70.7% .165 254.624)",
439    "blue-500" => "oklch(62.3% .214 259.815)",
440    "blue-600" => "oklch(54.6% .245 262.881)",
441    "blue-700" => "oklch(48.8% .243 264.376)",
442    "blue-800" => "oklch(42.4% .199 265.638)",
443    "blue-900" => "oklch(37.9% .146 265.522)",
444    "blue-950" => "oklch(28.2% .091 267.935)",
445    "indigo-50" => "oklch(96.2% .018 272.314)",
446    "indigo-100" => "oklch(93% .034 272.788)",
447    "indigo-200" => "oklch(87% .065 274.039)",
448    "indigo-300" => "oklch(78.5% .115 274.713)",
449    "indigo-400" => "oklch(67.3% .182 276.935)",
450    "indigo-500" => "oklch(58.5% .233 277.117)",
451    "indigo-600" => "oklch(51.1% .262 276.966)",
452    "indigo-700" => "oklch(45.7% .24 277.023)",
453    "indigo-800" => "oklch(39.8% .195 277.366)",
454    "indigo-900" => "oklch(35.9% .144 278.697)",
455    "indigo-950" => "oklch(25.7% .09 281.288)",
456    "violet-50" => "oklch(96.9% .016 293.756)",
457    "violet-100" => "oklch(94.3% .029 294.588)",
458    "violet-200" => "oklch(89.4% .057 293.283)",
459    "violet-300" => "oklch(81.1% .111 293.571)",
460    "violet-400" => "oklch(70.2% .183 293.541)",
461    "violet-500" => "oklch(60.6% .25 292.717)",
462    "violet-600" => "oklch(54.1% .281 293.009)",
463    "violet-700" => "oklch(49.1% .27 292.581)",
464    "violet-800" => "oklch(43.2% .232 292.759)",
465    "violet-900" => "oklch(38% .189 293.745)",
466    "violet-950" => "oklch(28.3% .141 291.089)",
467    "purple-50" => "oklch(97.7% .014 308.299)",
468    "purple-100" => "oklch(94.6% .033 307.174)",
469    "purple-200" => "oklch(90.2% .063 306.703)",
470    "purple-300" => "oklch(82.7% .119 306.383)",
471    "purple-400" => "oklch(71.4% .203 305.504)",
472    "purple-500" => "oklch(62.7% .265 303.9)",
473    "purple-600" => "oklch(55.8% .288 302.321)",
474    "purple-700" => "oklch(49.6% .265 301.924)",
475    "purple-800" => "oklch(43.8% .218 303.724)",
476    "purple-900" => "oklch(38.1% .176 304.987)",
477    "purple-950" => "oklch(29.1% .149 302.717)",
478    "fuchsia-50" => "oklch(97.7% .017 320.058)",
479    "fuchsia-100" => "oklch(95.2% .037 318.852)",
480    "fuchsia-200" => "oklch(90.3% .076 319.62)",
481    "fuchsia-300" => "oklch(83.3% .145 321.434)",
482    "fuchsia-400" => "oklch(74% .238 322.16)",
483    "fuchsia-500" => "oklch(66.7% .295 322.15)",
484    "fuchsia-600" => "oklch(59.1% .293 322.896)",
485    "fuchsia-700" => "oklch(51.8% .253 323.949)",
486    "fuchsia-800" => "oklch(45.2% .211 324.591)",
487    "fuchsia-900" => "oklch(40.1% .17 325.612)",
488    "fuchsia-950" => "oklch(29.3% .136 325.661)",
489    "pink-50" => "oklch(97.1% .014 343.198)",
490    "pink-100" => "oklch(94.8% .028 342.258)",
491    "pink-200" => "oklch(89.9% .061 343.231)",
492    "pink-300" => "oklch(82.3% .12 346.018)",
493    "pink-400" => "oklch(71.8% .202 349.761)",
494    "pink-500" => "oklch(65.6% .241 354.308)",
495    "pink-600" => "oklch(59.2% .249 .584)",
496    "pink-700" => "oklch(52.5% .223 3.958)",
497    "pink-800" => "oklch(45.9% .187 3.815)",
498    "pink-900" => "oklch(40.8% .153 2.432)",
499    "pink-950" => "oklch(28.4% .109 3.907)",
500    "rose-50" => "oklch(96.9% .015 12.422)",
501    "rose-100" => "oklch(94.1% .03 12.58)",
502    "rose-200" => "oklch(89.2% .058 10.001)",
503    "rose-300" => "oklch(81% .117 11.638)",
504    "rose-400" => "oklch(71.2% .194 13.428)",
505    "rose-500" => "oklch(64.5% .246 16.439)",
506    "rose-600" => "oklch(58.6% .253 17.585)",
507    "rose-700" => "oklch(51.4% .222 16.935)",
508    "rose-800" => "oklch(45.5% .188 13.697)",
509    "rose-900" => "oklch(41% .159 10.272)",
510    "rose-950" => "oklch(27.1% .105 12.094)",
511    "slate-50" => "oklch(98.4% .003 247.858)",
512    "slate-100" => "oklch(96.8% .007 247.896)",
513    "slate-200" => "oklch(92.9% .013 255.508)",
514    "slate-300" => "oklch(86.9% .022 252.894)",
515    "slate-400" => "oklch(70.4% .04 256.788)",
516    "slate-500" => "oklch(55.4% .046 257.417)",
517    "slate-600" => "oklch(44.6% .043 257.281)",
518    "slate-700" => "oklch(37.2% .044 257.287)",
519    "slate-800" => "oklch(27.9% .041 260.031)",
520    "slate-900" => "oklch(20.8% .042 265.755)",
521    "slate-950" => "oklch(12.9% .042 264.695)",
522    "gray-50" => "oklch(98.5% .002 247.839)",
523    "gray-100" => "oklch(96.7% .003 264.542)",
524    "gray-200" => "oklch(92.8% .006 264.531)",
525    "gray-300" => "oklch(87.2% .01 258.338)",
526    "gray-400" => "oklch(70.7% .022 261.325)",
527    "gray-500" => "oklch(55.1% .027 264.364)",
528    "gray-600" => "oklch(44.6% .03 256.802)",
529    "gray-700" => "oklch(37.3% .034 259.733)",
530    "gray-800" => "oklch(27.8% .033 256.848)",
531    "gray-900" => "oklch(21% .034 264.665)",
532    "gray-950" => "oklch(13% .028 261.692)",
533    "zinc-50" => "oklch(98.5% 0 0)",
534    "zinc-100" => "oklch(96.7% .001 286.375)",
535    "zinc-200" => "oklch(92% .004 286.32)",
536    "zinc-300" => "oklch(87.1% .006 286.286)",
537    "zinc-400" => "oklch(70.5% .015 286.067)",
538    "zinc-500" => "oklch(55.2% .016 285.938)",
539    "zinc-600" => "oklch(44.2% .017 285.786)",
540    "zinc-700" => "oklch(37% .013 285.805)",
541    "zinc-800" => "oklch(27.4% .006 286.033)",
542    "zinc-900" => "oklch(21% .006 285.885)",
543    "zinc-950" => "oklch(14.1% .005 285.823)",
544    "neutral-50" => "oklch(98.5% 0 0)",
545    "neutral-100" => "oklch(97% 0 0)",
546    "neutral-200" => "oklch(92.2% 0 0)",
547    "neutral-300" => "oklch(87% 0 0)",
548    "neutral-400" => "oklch(70.8% 0 0)",
549    "neutral-500" => "oklch(55.6% 0 0)",
550    "neutral-600" => "oklch(43.9% 0 0)",
551    "neutral-700" => "oklch(37.1% 0 0)",
552    "neutral-800" => "oklch(26.9% 0 0)",
553    "neutral-900" => "oklch(20.5% 0 0)",
554    "neutral-950" => "oklch(14.5% 0 0)",
555    "stone-50" => "oklch(98.5% .001 106.423)",
556    "stone-100" => "oklch(97% .001 106.424)",
557    "stone-200" => "oklch(92.3% .003 48.717)",
558    "stone-300" => "oklch(86.9% .005 56.366)",
559    "stone-400" => "oklch(70.9% .01 56.259)",
560    "stone-500" => "oklch(55.3% .013 58.071)",
561    "stone-600" => "oklch(44.4% .011 73.639)",
562    "stone-700" => "oklch(37.4% .01 67.558)",
563    "stone-800" => "oklch(26.8% .007 34.298)",
564    "stone-900" => "oklch(21.6% .006 56.043)",
565    "stone-950" => "oklch(14.7% .004 49.25)",
566    "black" => "#000",
567    "white" => "#fff",
568};
569
570/// The list of all default screen breakpoints.
571///
572/// Based on [Tailwind's default screen breakpoints](https://tailwindcss.com/docs/hover-focus-and-other-states#quick-reference).
573pub const BUILTIN_SCREENS: &[(&str, &str)] = &[
574    ("sm", "40rem"),
575    ("md", "48rem"),
576    ("lg", "64rem"),
577    ("xl", "80rem"),
578    ("2xl", "96rem"),
579];
580
581/// The list of all default container size breakpoints.
582///
583/// Based on [Tailwind's default container size breakpoints](https://tailwindcss.com/docs/hover-focus-and-other-states#quick-reference).
584pub const BUILTIN_CONTAINERS: &[(&str, &str)] = &[
585    ("3xs", "16rem"),
586    ("2xs", "18rem"),
587    ("xs", "20rem"),
588    ("sm", "24rem"),
589    ("md", "28rem"),
590    ("lg", "32rem"),
591    ("xl", "36rem"),
592    ("2xl", "42rem"),
593    ("3xl", "48rem"),
594    ("4xl", "56rem"),
595    ("5xl", "64rem"),
596    ("6xl", "72rem"),
597    ("7xl", "80rem"),
598];
599
600/// The list of all default variants (sorted following their importance in the CSS file).
601///
602/// - `not-[{}]`: applies [`:not()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:not) with the value specified between the square brackets
603/// - `group-[{}]`: applies a selector to the group then selects all its child nodes
604/// - `peer-[{}]`: applies a selector to a peer element then selects all its peer nodes
605/// - `first-letter`: applies the [`first-letter`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter) pseudo element
606/// - `first-line`: applies the [`first-line`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-line) pseudo element
607/// - `marker`: applies the [`marker`](https://developer.mozilla.org/en-US/docs/Web/CSS/::marker) pseudo element to the element **and all of its nested children**
608/// - `selection`: applies the [`selection`](https://developer.mozilla.org/en-US/docs/Web/CSS/::selection) pseudo element to the element **and all of its nested children**
609/// - `file`: applies the [`file-selector-button`](https://developer.mozilla.org/en-US/docs/Web/CSS/::file-selector-button) pseudo element
610/// - `placeholder`: applies the [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/CSS/::placeholder) pseudo element
611/// - `backdrop`: applies the [`backdrop`](https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop) pseudo element
612/// - `details-content`: applies the [`details-content`](https://developer.mozilla.org/en-US/docs/Web/CSS/::details-content) pseudo element
613/// - `before`: applies the [`before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) pseudo element
614/// - `after`: applies the [`before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) pseudo element
615/// - `all` or `**`: selects all nested children instead of the element itself
616/// - `children` or `*`: selects all direct children (only one level deep) instead of the element itself
617/// - `siblings`: selects all siblings of the element using the [general sibling combinator `~`](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator)
618/// - `sibling`: select the sibling next to the element using the [adjacent sibling combinator `+`](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator)
619/// - `first`: applies the [`first-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child) pseudo class
620/// - `not-first`: opposite of `first`, applies `:not(:first-child)`
621/// - `last`: applies the [`last-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child) pseudo class
622/// - `not-last`: opposite of `last`, applies `:not(:last-child)`
623/// - `only`: applies the [`only-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child) pseudo class
624/// - `not-only`: opposite of `only`, applies `:not(:only-child)`
625/// - `odd`: applies the [`nth-child(odd)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) pseudo class
626/// - `even`: opposite of `odd`, applies the [`nth-child(even)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) pseudo class
627/// - `first-of-type`: applies the [`first-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type) pseudo class
628/// - `last-of-type`: applies the [`last-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type) pseudo class
629/// - `only-of-type`: applies the [`only-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type) pseudo class
630/// - `not-first-of-type`: opposite of `first-of-type`, applies `:not(:first-of-type)`
631/// - `not-last-of-type`: opposite of `last-of-type`, applies `:not(:last-of-type)`
632/// - `not-only-of-type`: opposite of `only-of-type`, applies `:not(:only-of-type)`
633/// - `visited`: applies the [`visited`](https://developer.mozilla.org/en-US/docs/Web/CSS/:visited) pseudo class
634/// - `target`: applies the [`target`](https://developer.mozilla.org/en-US/docs/Web/CSS/:target) pseudo class
635/// - `open`: selects all elements having the `open` attributes, useful for [`<dialog>` elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
636/// - `default`: applies the [`default`](https://developer.mozilla.org/en-US/docs/Web/CSS/:default) pseudo class
637/// - `checked`: applies the [`checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/:checked) pseudo class
638/// - `not-checked`: opposite of `checked`, applies `:not(:checked)`
639/// - `indeterminate`: applies the [`indeterminate`](https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate) pseudo class
640/// - `placeholder-shown`: applies the [`placeholder-shown`](https://developer.mozilla.org/en-US/docs/Web/CSS/:placeholder-shown) pseudo class
641/// - `autofill`: applies the [`autofill`](https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill) pseudo class
642/// - `optional`: applies the [`optional`](https://developer.mozilla.org/en-US/docs/Web/CSS/:optional) pseudo class
643/// - `required`: applies the [`required`](https://developer.mozilla.org/en-US/docs/Web/CSS/:required) pseudo class
644/// - `valid`: applies the [`valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:valid) pseudo class
645/// - `invalid`: applies the [`invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid) pseudo class
646/// - `in-range`: applies the [`in-range`](https://developer.mozilla.org/en-US/docs/Web/CSS/:in-range) pseudo class
647/// - `out-of-range`: applies the [`out-of-range`](https://developer.mozilla.org/en-US/docs/Web/CSS/:out-of-range) pseudo class
648/// - `read-only`: applies the [`read-only`](https://developer.mozilla.org/en-US/docs/Web/CSS/:read-only) pseudo class
649/// - `read-write`: applies the [`read-write`](https://developer.mozilla.org/en-US/docs/Web/CSS/:read-write) pseudo class
650/// - `empty`: applies the [`empty`](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty) pseudo class
651/// - `focus-within`: applies the [`focus-within`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within) pseudo class
652/// - `hover`: applies the [`hover`](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover) pseudo class
653/// - `focus`: applies the [`focus`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus) pseudo class
654/// - `focus-visible`: applies the [`focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) pseudo class
655/// - `active`: applies the [`active`](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) pseudo class
656/// - `enabled`: applies the [`enabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:enabled) pseudo class
657/// - `disabled`: applies the [`disabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled) pseudo class
658/// - `inert`: selects all the elements (and their child nodes) having the [`inert` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/inert)
659/// - `in-[{}]`: selects the element based on the state of the parent elements
660/// - `has-[{}]`: selects elements using [`:has()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)
661/// - `aria-busy`: selects all elements having the [`aria-busy="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-busy)
662/// - `aria-checked`: selects all elements having the [`aria-checked="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked)
663/// - `aria-disabled`: selects all elements having the [`aria-disabled="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled)
664/// - `aria-expanded`: selects all elements having the [`aria-expanded="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-expanded)
665/// - `aria-hidden`: selects all elements having the [`aria-hidden="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden)
666/// - `aria-pressed`: selects all elements having the [`aria-pressed="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
667/// - `aria-readonly`: selects all elements having the [`aria-readonly="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-readonly)
668/// - `aria-required`: selects all elements having the [`aria-required="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-required)
669/// - `aria-selected`: selects all elements having the [`aria-selected="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-selected)
670/// - `aria-[{}]`: selects all elements having a custom aria attribute
671/// - `data-[{}]`: selects all elements having the [`data-{}` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/data-*)
672/// - `nth-[{}]`: selects all elements having a specific position in a list of children using the [`:nth-child({})` pseudo class](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child)
673/// - `nth-last-[{}]`: selects all elements having a specific position in a list of children using the [`:nth-last-child({})` pseudo class](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-child)
674/// - `nth-of-type-[{}]`: selects all elements having a specific type using the [`:nth-of-type-({})` pseudo class](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type)
675/// - `nth-last-of-type-[{}]`: selects all elements having a specific type using the [`:nth-last-of-type-({})` pseudo class](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type)
676/// - `supports-[{}]`: applies the [`@supports`](https://developer.mozilla.org/en-US/docs/Web/CSS/@supports) at-rule
677/// - `motion-safe`: applies the [`@media (prefers-reduced-motion: no-preference)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) at-rule
678/// - `motion-reduce`: applies the [`@media (prefers-reduced-motion: reduce)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) at-rule
679/// - `contrast-more`: applies the [`@media (prefers-contrast: more)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) at-rule
680/// - `contrast-less`: applies the [`@media (prefers-contrast: less)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) at-rule
681/// - `max`: applies the [`@media (width < {})`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/width) at-rule
682/// - `min`: applies the [`@media (width >= {})`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/width) at-rule
683/// - `@max`: applies the [`@container (width < {})`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/width) at-rule
684/// - `@min`: applies the [`@container (width >= {})`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/width) at-rule
685/// - `portrait`: applies the [`@media (orientation: portrait)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation) at-rule
686/// - `landscape`: applies the [`@media (orientation: landscape)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation) at-rule
687/// - `ltr`: selects all elements contained in an element having the [`dir="ltr"` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir), useful when styling based on writing direction
688/// - `rtl`: selects all elements contained in an element having the [`dir="rtl"` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir), useful when styling based on writing direction
689/// - `starting`: applies the [`@starting-style`](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) at-rule
690/// - `print`: applies the [`@media print`](https://developer.mozilla.org/en-US/docs/Web/Guide/Printing#using_media_queries_to_improve_layout) at-rule
691/// - `forced-colors`: applies the [`@media (forced-colors: active)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors) at-rule
692/// - `inverted-colors`: applies the [`@media (inverted-colors: inverted)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/inverted-colors) at-rule
693/// - `pointer-none`: applies the [`@media (pointer: none)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer) at-rule
694/// - `pointer-coarse`: applies the [`@media (pointer: coarse)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer) at-rule
695/// - `pointer-fine`: applies the [`@media (pointer: fine)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer) at-rule
696/// - `any-pointer-none`: applies the [`@media (any-pointer: none)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer) at-rule
697/// - `any-pointer-coarse`: applies the [`@media (any-pointer: coarse)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer) at-rule
698/// - `any-pointer-fine`: applies the [`@media (any-pointer: fine)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer) at-rule
699/// - `noscript`: applies the [`@media (scripting: none)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/scripting) at-rule
700///
701/// Besides the default variants, some others are auto-generated from the configuration like
702/// breakpoints (see [`BUILTIN_SCREENS`]), container sizes (see [`BUILTIN_CONTAINERS`]) and the dark mode (`dark:` variant).
703///
704/// You can prefix all variants by `group-`, `peer-` or `peer-not-` so that they apply to the group
705/// or peer element, e.g `group-hover:bg-blue-100` or `peer-has-[#test]:hidden`.
706///
707/// Based on [Tailwind's default variants](https://tailwindcss.com/docs/hover-focus-and-other-states).
708#[rustfmt::skip]
709pub const BUILTIN_VARIANTS: phf::OrderedMap<&'static str, Variant> = {
710    let mut counter = 0;
711
712    phf_ordered_map! {
713        "not" => Variant::new_const(&mut counter, "&:not({})").with_prefixed(),
714
715        "first-letter" => Variant::new_const(&mut counter, "&::first-letter"),
716        "first-line" => Variant::new_const(&mut counter, "&::first-line"),
717        "marker" => Variant::new_const(&mut counter, "& *::marker, &::marker"),
718        "selection" => Variant::new_const(&mut counter, "& *::selection, &::selection"),
719        "file" => Variant::new_const(&mut counter, "&::file-selector-button, &::-webkit-file-upload-button"),
720        "placeholder" => Variant::new_const(&mut counter, "&::placeholder"),
721        "backdrop" => Variant::new_const(&mut counter, "&::backdrop"),
722        "details-content" => Variant::new_const(&mut counter, "&::details-content"),
723        "before" => Variant::new_const(&mut counter, "&::before"),
724        "after" => Variant::new_const(&mut counter, "&::after"),
725        "all" => Variant::new_const(&mut counter, "& *"),
726        "**" => Variant::new_const(&mut counter, "& *"),
727        "children" => Variant::new_const(&mut counter, "& > *"),
728        "*" => Variant::new_const(&mut counter, "& > *"),
729        "siblings" => Variant::new_const(&mut counter, "& ~ *"),
730        "sibling" => Variant::new_const(&mut counter, "& + *"),
731
732        "first" => Variant::new_const(&mut counter, "&:first-child"),
733        "not-first" => Variant::new_const(&mut counter, "&:not(:first-child)"),
734        "last" => Variant::new_const(&mut counter, "&:last-child"),
735        "not-last" => Variant::new_const(&mut counter, "&:not(:last-child)"),
736        "only" => Variant::new_const(&mut counter, "&:only-child"),
737        "not-only" => Variant::new_const(&mut counter, "&:not(:only-child)"),
738        "odd" => Variant::new_const(&mut counter, "&:nth-child(odd)"),
739        "even" => Variant::new_const(&mut counter, "&:nth-child(even)"),
740        "first-of-type" => Variant::new_const(&mut counter, "&:first-of-type"),
741        "last-of-type" => Variant::new_const(&mut counter, "&:last-of-type"),
742        "only-of-type" => Variant::new_const(&mut counter, "&:only-of-type"),
743        "not-first-of-type" => Variant::new_const(&mut counter, "&:not(:first-of-type)"),
744        "not-last-of-type" => Variant::new_const(&mut counter, "&:not(:last-of-type)"),
745        "not-only-of-type" => Variant::new_const(&mut counter, "&:not(:only-of-type)"),
746        "visited" => Variant::new_const(&mut counter, "&:visited"),
747        "target" => Variant::new_const(&mut counter, "&:target"),
748        "open" => Variant::new_const(&mut counter, "&[open]"),
749        "default" => Variant::new_const(&mut counter, "&:default"),
750        "checked" => Variant::new_const(&mut counter, "&:checked"),
751        "not-checked" => Variant::new_const(&mut counter, "&:not(:checked)"),
752        "indeterminate" => Variant::new_const(&mut counter, "&:indeterminate"),
753        "placeholder-shown" => Variant::new_const(&mut counter, "&:placeholder-shown"),
754        "autofill" => Variant::new_const(&mut counter, "&:autofill"),
755        "optional" => Variant::new_const(&mut counter, "&:optional"),
756        "required" => Variant::new_const(&mut counter, "&:required"),
757        "valid" => Variant::new_const(&mut counter, "&:valid"),
758        "invalid" => Variant::new_const(&mut counter, "&:invalid"),
759        "in-range" => Variant::new_const(&mut counter, "&:in-range"),
760        "out-of-range" => Variant::new_const(&mut counter, "&:out-of-range"),
761        "read-only" => Variant::new_const(&mut counter, "&:read-only"),
762        "read-write" => Variant::new_const(&mut counter, "&:read-write"),
763        "empty" => Variant::new_const(&mut counter, "&:empty"),
764        "focus-within" => Variant::new_const(&mut counter, "&:focus-within"),
765        "hover" => Variant::new_const(&mut counter, "&:hover"),
766        "focus" => Variant::new_const(&mut counter, "&:focus"),
767        "focus-visible" => Variant::new_const(&mut counter, "&:focus-visible"),
768        "active" => Variant::new_const(&mut counter, "&:active"),
769        "enabled" => Variant::new_const(&mut counter, "&:enabled"),
770        "disabled" => Variant::new_const(&mut counter, "&:disabled"),
771        "inert" => Variant::new_const(&mut counter, "&:is([inert], [inert] *)"),
772        "in" => Variant::new_const(&mut counter, ":where({}) &").with_prefixed(),
773        "has" => Variant::new_const(&mut counter, "&:has({})").with_prefixed(),
774
775        "aria-busy" => Variant::new_const(&mut counter, "&[aria-busy=\"true\"]"),
776        "aria-checked" => Variant::new_const(&mut counter, "&[aria-checked=\"true\"]"),
777        "aria-disabled" => Variant::new_const(&mut counter, "&[aria-disabled=\"true\"]"),
778        "aria-expanded" => Variant::new_const(&mut counter, "&[aria-expanded=\"true\"]"),
779        "aria-hidden" => Variant::new_const(&mut counter, "&[aria-hidden=\"true\"]"),
780        "aria-pressed" => Variant::new_const(&mut counter, "&[aria-pressed=\"true\"]"),
781        "aria-readonly" => Variant::new_const(&mut counter, "&[aria-readonly=\"true\"]"),
782        "aria-required" => Variant::new_const(&mut counter, "&[aria-required=\"true\"]"),
783        "aria-selected" => Variant::new_const(&mut counter, "&[aria-selected=\"true\"]"),
784        "aria" => Variant::new_const(&mut counter, "&[aria-{}]").with_prefixed(),
785
786        "data" => Variant::new_const(&mut counter, "&[data-{}]").with_prefixed(),
787        "nth" => Variant::new_const(&mut counter, "&:nth-child({})").with_prefixed(),
788        "nth-last" => Variant::new_const(&mut counter, "&:nth-last-child({})").with_prefixed(),
789        "nth-of-type" => Variant::new_const(&mut counter, "&:nth-of-type({})").with_prefixed(),
790        "nth-last-of-type" => Variant::new_const(&mut counter, "&:nth-last-of-type({})").with_prefixed(),
791        "supports" => Variant::new_const(&mut counter, "@supports ({})").with_prefixed(),
792
793        "motion-safe" => Variant::new_const(&mut counter, "@media (prefers-reduced-motion: no-preference)"),
794        "motion-reduce" => Variant::new_const(&mut counter, "@media (prefers-reduced-motion: reduce)"),
795        "contrast-more" => Variant::new_const(&mut counter, "@media (prefers-contrast: more)"),
796        "contrast-less" => Variant::new_const(&mut counter, "@media (prefers-contrast: less)"),
797
798        "max" => Variant::new_const(&mut counter, "@media (width < {})").with_prefixed(),
799        "min" => Variant::new_const(&mut counter, "@media (width >= {})").with_prefixed(),
800        "@max" => Variant::new_const(&mut counter, "@container (width < {})").with_prefixed(),
801        "@min" => Variant::new_const(&mut counter, "@container (width >= {})").with_prefixed(),
802
803        "portrait" => Variant::new_const(&mut counter, "@media (orientation: portrait)"),
804        "landscape" => Variant::new_const(&mut counter, "@media (orientation: landscape)"),
805        "ltr" => Variant::new_const(&mut counter, "[dir=\"ltr\"] &"),
806        "rtl" => Variant::new_const(&mut counter, "[dir=\"rtl\"] &"),
807        "starting" => Variant::new_const(&mut counter, "@starting-style"),
808        "print" => Variant::new_const(&mut counter, "@media print"),
809        "forced-colors" => Variant::new_const(&mut counter, "@media (forced-colors: active)"),
810        "inverted-colors" => Variant::new_const(&mut counter, "@media (inverted-colors: inverted)"),
811        "pointer-none" => Variant::new_const(&mut counter, "@media (pointer: none)"),
812        "pointer-coarse" => Variant::new_const(&mut counter, "@media (pointer: coarse)"),
813        "pointer-fine" => Variant::new_const(&mut counter, "@media (pointer: fine)"),
814        "any-pointer-none" => Variant::new_const(&mut counter, "@media (any-pointer: none)"),
815        "any-pointer-coarse" => Variant::new_const(&mut counter, "@media (any-pointer: coarse)"),
816        "any-pointer-fine" => Variant::new_const(&mut counter, "@media (any-pointer: fine)"),
817        "noscript" => Variant::new_const(&mut counter, "@media (scripting: none)"),
818    }
819};
820
821/// The list of all default plugins.
822///
823/// Sorted following [Tailwind's order](https://github.com/tailwindlabs/tailwindcss/blob/master/src/corePlugins.js).
824pub const BUILTIN_PLUGINS: &[&StaticPlugin] = &[
825    &layout::container::PLUGIN,
826    &accessibility::screen_reader::PLUGIN,
827    &interactivity::pointer_events::PLUGIN,
828    &layout::visibility::PLUGIN,
829    &layout::position::PLUGIN,
830    &layout::placement::PLUGIN.0,
831    &layout::placement::PLUGIN.1,
832    &layout::placement::PLUGIN_X.0,
833    &layout::placement::PLUGIN_X.1,
834    &layout::placement::PLUGIN_Y.0,
835    &layout::placement::PLUGIN_Y.1,
836    &layout::placement::PLUGIN_START.0,
837    &layout::placement::PLUGIN_START.1,
838    &layout::placement::PLUGIN_END.0,
839    &layout::placement::PLUGIN_END.1,
840    &layout::placement::PLUGIN_TOP.0,
841    &layout::placement::PLUGIN_TOP.1,
842    &layout::placement::PLUGIN_RIGHT.0,
843    &layout::placement::PLUGIN_RIGHT.1,
844    &layout::placement::PLUGIN_BOTTOM.0,
845    &layout::placement::PLUGIN_BOTTOM.1,
846    &layout::placement::PLUGIN_LEFT.0,
847    &layout::placement::PLUGIN_LEFT.1,
848    &layout::isolation::PLUGIN,
849    &layout::z_index::PLUGIN,
850    &flexbox::order::PLUGIN_LIST,
851    &flexbox::order::PLUGIN_NUM,
852    &grid::grid_column::PLUGIN,
853    &grid::grid_column::PLUGIN_ARBITRARY,
854    &grid::grid_column::PLUGIN_SPAN_1,
855    &grid::grid_column::PLUGIN_SPAN_2,
856    &grid::grid_column::PLUGIN_START_1,
857    &grid::grid_column::PLUGIN_START_2,
858    &grid::grid_column::PLUGIN_END_1,
859    &grid::grid_column::PLUGIN_END_2,
860    &grid::grid_row::PLUGIN,
861    &grid::grid_row::PLUGIN_ARBITRARY,
862    &grid::grid_row::PLUGIN_SPAN_1,
863    &grid::grid_row::PLUGIN_SPAN_2,
864    &grid::grid_row::PLUGIN_START_1,
865    &grid::grid_row::PLUGIN_START_2,
866    &grid::grid_row::PLUGIN_END_1,
867    &grid::grid_row::PLUGIN_END_2,
868    &layout::floats::PLUGIN,
869    &layout::clear::PLUGIN,
870    &spacing::margin::PLUGIN.0,
871    &spacing::margin::PLUGIN.1,
872    &spacing::margin::PLUGIN_X.0,
873    &spacing::margin::PLUGIN_X.1,
874    &spacing::margin::PLUGIN_Y.0,
875    &spacing::margin::PLUGIN_Y.1,
876    &spacing::margin::PLUGIN_START.0,
877    &spacing::margin::PLUGIN_START.1,
878    &spacing::margin::PLUGIN_END.0,
879    &spacing::margin::PLUGIN_END.1,
880    &spacing::margin::PLUGIN_TOP.0,
881    &spacing::margin::PLUGIN_TOP.1,
882    &spacing::margin::PLUGIN_RIGHT.0,
883    &spacing::margin::PLUGIN_RIGHT.1,
884    &spacing::margin::PLUGIN_BOTTOM.0,
885    &spacing::margin::PLUGIN_BOTTOM.1,
886    &spacing::margin::PLUGIN_LEFT.0,
887    &spacing::margin::PLUGIN_LEFT.1,
888    &layout::box_sizing::PLUGIN,
889    &layout::display::PLUGIN,
890    &layout::aspect_ratio::PLUGIN,
891    &layout::aspect_ratio::PLUGIN_ARBITRARY,
892    &sizing::height::PLUGIN_SPACING,
893    &sizing::height::PLUGIN_LIST,
894    &sizing::height::PLUGIN_ARBITRARY,
895    &sizing::max_height::PLUGIN_SPACING,
896    &sizing::max_height::PLUGIN_LIST,
897    &sizing::max_height::PLUGIN_ARBITRARY,
898    &sizing::min_height::PLUGIN_SPACING,
899    &sizing::min_height::PLUGIN_LIST,
900    &sizing::min_height::PLUGIN_ARBITRARY,
901    &sizing::width::PLUGIN_SPACING,
902    &sizing::width::PLUGIN_LIST,
903    &sizing::width::PLUGIN_ARBITRARY,
904    &sizing::min_width::PLUGIN_SPACING,
905    &sizing::min_width::PLUGIN_LIST,
906    &sizing::min_width::PLUGIN_ARBITRARY,
907    &sizing::max_width::PLUGIN_SPACING,
908    &sizing::max_width::PLUGIN_LIST_1,
909    &sizing::max_width::PLUGIN_LIST_2,
910    &sizing::max_width::PLUGIN_ARBITRARY,
911    &flexbox::flex::PLUGIN,
912    &flexbox::flex::PLUGIN_ARBITRARY,
913    &flexbox::flex_shrink::PLUGIN,
914    &flexbox::flex_grow::PLUGIN,
915    &flexbox::flex_basis::PLUGIN,
916    &flexbox::flex_basis::PLUGIN_ARBITRARY,
917    &table::table_layout::PLUGIN,
918    &table::caption_side::PLUGIN,
919    &table::border_collapse::PLUGIN,
920    &table::border_spacing::PLUGIN.0,
921    &table::border_spacing::PLUGIN.1,
922    &table::border_spacing::PLUGIN_X.0,
923    &table::border_spacing::PLUGIN_X.1,
924    &table::border_spacing::PLUGIN_Y.0,
925    &table::border_spacing::PLUGIN_Y.1,
926    &transform::transform_origin::PLUGIN,
927    &transform::transform_origin::PLUGIN_ARBITRARY,
928    &transform::perspective_origin::PLUGIN,
929    &transform::perspective_origin::PLUGIN_ARBITRARY,
930    &transform::perspective::PLUGIN,
931    &transform::perspective::PLUGIN_ARBITRARY,
932    &transform::translate::PLUGIN_X.0,
933    &transform::translate::PLUGIN_X.1,
934    &transform::translate::PLUGIN_Y.0,
935    &transform::translate::PLUGIN_Y.1,
936    &transform::translate::PLUGIN_Z.0,
937    &transform::translate::PLUGIN_Z.1,
938    &transform::rotate::PLUGIN.0,
939    &transform::rotate::PLUGIN.1,
940    &transform::rotate::PLUGIN_X.0,
941    &transform::rotate::PLUGIN_X.1,
942    &transform::rotate::PLUGIN_Y.0,
943    &transform::rotate::PLUGIN_Y.1,
944    &transform::rotate::PLUGIN_Z.0,
945    &transform::rotate::PLUGIN_Z.1,
946    &transform::skew::PLUGIN_X.0,
947    &transform::skew::PLUGIN_X.1,
948    &transform::skew::PLUGIN_Y.0,
949    &transform::skew::PLUGIN_Y.1,
950    &transform::scale::PLUGIN.0,
951    &transform::scale::PLUGIN.1,
952    &transform::scale::PLUGIN_X.0,
953    &transform::scale::PLUGIN_X.1,
954    &transform::scale::PLUGIN_Y.0,
955    &transform::scale::PLUGIN_Y.1,
956    &transform::scale::PLUGIN_Z.0,
957    &transform::scale::PLUGIN_Z.1,
958    &transform::transform_type::PLUGIN,
959    &transition::animation::PLUGIN,
960    &transition::animation::PLUGIN_ARBITRARY,
961    &interactivity::cursor::PLUGIN,
962    &interactivity::cursor::PLUGIN_ARBITRARY,
963    &interactivity::touch_action::PLUGIN,
964    &interactivity::user_select::PLUGIN,
965    &interactivity::resize::PLUGIN,
966    &interactivity::scroll_snap_type::PLUGIN,
967    &interactivity::scroll_snap_align::PLUGIN,
968    &interactivity::scroll_snap_stop::PLUGIN,
969    &interactivity::scroll_margin::PLUGIN.0,
970    &interactivity::scroll_margin::PLUGIN.1,
971    &interactivity::scroll_margin::PLUGIN_X.0,
972    &interactivity::scroll_margin::PLUGIN_X.1,
973    &interactivity::scroll_margin::PLUGIN_Y.0,
974    &interactivity::scroll_margin::PLUGIN_Y.1,
975    &interactivity::scroll_margin::PLUGIN_START.0,
976    &interactivity::scroll_margin::PLUGIN_START.1,
977    &interactivity::scroll_margin::PLUGIN_END.0,
978    &interactivity::scroll_margin::PLUGIN_END.1,
979    &interactivity::scroll_margin::PLUGIN_TOP.0,
980    &interactivity::scroll_margin::PLUGIN_TOP.1,
981    &interactivity::scroll_margin::PLUGIN_RIGHT.0,
982    &interactivity::scroll_margin::PLUGIN_RIGHT.1,
983    &interactivity::scroll_margin::PLUGIN_BOTTOM.0,
984    &interactivity::scroll_margin::PLUGIN_BOTTOM.1,
985    &interactivity::scroll_margin::PLUGIN_LEFT.0,
986    &interactivity::scroll_margin::PLUGIN_LEFT.1,
987    &interactivity::scroll_padding::PLUGIN.0,
988    &interactivity::scroll_padding::PLUGIN.1,
989    &interactivity::scroll_padding::PLUGIN_X.0,
990    &interactivity::scroll_padding::PLUGIN_X.1,
991    &interactivity::scroll_padding::PLUGIN_Y.0,
992    &interactivity::scroll_padding::PLUGIN_Y.1,
993    &interactivity::scroll_padding::PLUGIN_START.0,
994    &interactivity::scroll_padding::PLUGIN_START.1,
995    &interactivity::scroll_padding::PLUGIN_END.0,
996    &interactivity::scroll_padding::PLUGIN_END.1,
997    &interactivity::scroll_padding::PLUGIN_TOP.0,
998    &interactivity::scroll_padding::PLUGIN_TOP.1,
999    &interactivity::scroll_padding::PLUGIN_RIGHT.0,
1000    &interactivity::scroll_padding::PLUGIN_RIGHT.1,
1001    &interactivity::scroll_padding::PLUGIN_BOTTOM.0,
1002    &interactivity::scroll_padding::PLUGIN_BOTTOM.1,
1003    &interactivity::scroll_padding::PLUGIN_LEFT.0,
1004    &interactivity::scroll_padding::PLUGIN_LEFT.1,
1005    &typography::list_style_position::PLUGIN,
1006    &typography::list_style_type::PLUGIN,
1007    &typography::list_style_type::PLUGIN_ARBITRARY,
1008    &interactivity::appearance::PLUGIN,
1009    &layout::columns::PLUGIN,
1010    &layout::break_before::PLUGIN,
1011    &layout::break_inside::PLUGIN,
1012    &layout::break_after::PLUGIN,
1013    &grid::grid_auto_columns::PLUGIN,
1014    &grid::grid_auto_columns::PLUGIN_ARBITRARY,
1015    &grid::grid_auto_flow::PLUGIN,
1016    &grid::grid_auto_rows::PLUGIN,
1017    &grid::grid_auto_rows::PLUGIN_ARBITRARY,
1018    &grid::grid_template_columns::PLUGIN_NUMBER,
1019    &grid::grid_template_columns::PLUGIN_LIST,
1020    &grid::grid_template_columns::PLUGIN_ARBITRARY,
1021    &grid::grid_template_rows::PLUGIN_NUMBER,
1022    &grid::grid_template_rows::PLUGIN_LIST,
1023    &grid::grid_template_rows::PLUGIN_ARBITRARY,
1024    &flexbox::flex_direction::PLUGIN,
1025    &flexbox::flex_wrap::PLUGIN,
1026    &flexbox::place_content::PLUGIN,
1027    &flexbox::place_items::PLUGIN,
1028    &flexbox::align_content::PLUGIN,
1029    &flexbox::align_items::PLUGIN,
1030    &flexbox::justify_content::PLUGIN,
1031    &flexbox::justify_items::PLUGIN,
1032    &grid::gap::PLUGIN.0,
1033    &grid::gap::PLUGIN.1,
1034    &grid::gap::PLUGIN_X.0,
1035    &grid::gap::PLUGIN_X.1,
1036    &grid::gap::PLUGIN_Y.0,
1037    &grid::gap::PLUGIN_Y.1,
1038    &spacing::space_between::PLUGIN_X_1,
1039    &spacing::space_between::PLUGIN_X_2,
1040    &spacing::space_between::PLUGIN_X_3,
1041    &spacing::space_between::PLUGIN_Y_1,
1042    &spacing::space_between::PLUGIN_Y_2,
1043    &spacing::space_between::PLUGIN_Y_3,
1044    &border::divide_width::PLUGIN_X_1,
1045    &border::divide_width::PLUGIN_X_2,
1046    &border::divide_width::PLUGIN_X_3,
1047    &border::divide_width::PLUGIN_Y_1,
1048    &border::divide_width::PLUGIN_Y_2,
1049    &border::divide_width::PLUGIN_Y_3,
1050    &border::divide_style::PLUGIN,
1051    &border::divide_color::PLUGIN,
1052    &border::divide_color::PLUGIN_ARBITRARY,
1053    &flexbox::place_self::PLUGIN,
1054    &flexbox::align_self::PLUGIN,
1055    &flexbox::justify_self::PLUGIN,
1056    &layout::overflow::PLUGIN,
1057    &layout::overscroll_behavior::PLUGIN,
1058    &interactivity::scroll_behavior::PLUGIN,
1059    &typography::text_overflow::PLUGIN,
1060    &typography::whitespace::PLUGIN,
1061    &typography::text_wrap::PLUGIN,
1062    &typography::word_break::PLUGIN,
1063    &border::border_radius::PLUGIN.0,
1064    &border::border_radius::PLUGIN.1,
1065    &border::border_radius::PLUGIN_START.0,
1066    &border::border_radius::PLUGIN_START.1,
1067    &border::border_radius::PLUGIN_END.0,
1068    &border::border_radius::PLUGIN_END.1,
1069    &border::border_radius::PLUGIN_TOP.0,
1070    &border::border_radius::PLUGIN_TOP.1,
1071    &border::border_radius::PLUGIN_RIGHT.0,
1072    &border::border_radius::PLUGIN_RIGHT.1,
1073    &border::border_radius::PLUGIN_BOTTOM.0,
1074    &border::border_radius::PLUGIN_BOTTOM.1,
1075    &border::border_radius::PLUGIN_LEFT.0,
1076    &border::border_radius::PLUGIN_LEFT.1,
1077    &border::border_radius::PLUGIN_START_START.0,
1078    &border::border_radius::PLUGIN_START_START.1,
1079    &border::border_radius::PLUGIN_START_END.0,
1080    &border::border_radius::PLUGIN_START_END.1,
1081    &border::border_radius::PLUGIN_END_END.0,
1082    &border::border_radius::PLUGIN_END_END.1,
1083    &border::border_radius::PLUGIN_END_START.0,
1084    &border::border_radius::PLUGIN_END_START.1,
1085    &border::border_radius::PLUGIN_TOP_RIGHT.0,
1086    &border::border_radius::PLUGIN_TOP_RIGHT.1,
1087    &border::border_radius::PLUGIN_TOP_LEFT.0,
1088    &border::border_radius::PLUGIN_TOP_LEFT.1,
1089    &border::border_radius::PLUGIN_BOTTOM_RIGHT.0,
1090    &border::border_radius::PLUGIN_BOTTOM_RIGHT.1,
1091    &border::border_radius::PLUGIN_BOTTOM_LEFT.0,
1092    &border::border_radius::PLUGIN_BOTTOM_LEFT.1,
1093    &border::border_width::PLUGIN.0,
1094    &border::border_width::PLUGIN.1,
1095    &border::border_width::PLUGIN_X.0,
1096    &border::border_width::PLUGIN_X.1,
1097    &border::border_width::PLUGIN_Y.0,
1098    &border::border_width::PLUGIN_Y.1,
1099    &border::border_width::PLUGIN_START.0,
1100    &border::border_width::PLUGIN_START.1,
1101    &border::border_width::PLUGIN_END.0,
1102    &border::border_width::PLUGIN_END.1,
1103    &border::border_width::PLUGIN_TOP.0,
1104    &border::border_width::PLUGIN_TOP.1,
1105    &border::border_width::PLUGIN_RIGHT.0,
1106    &border::border_width::PLUGIN_RIGHT.1,
1107    &border::border_width::PLUGIN_BOTTOM.0,
1108    &border::border_width::PLUGIN_BOTTOM.1,
1109    &border::border_width::PLUGIN_LEFT.0,
1110    &border::border_width::PLUGIN_LEFT.1,
1111    &border::border_style::PLUGIN,
1112    &border::border_style::PLUGIN_ARBITRARY,
1113    &border::border_color::PLUGIN.0,
1114    &border::border_color::PLUGIN.1,
1115    &border::border_color::PLUGIN_X.0,
1116    &border::border_color::PLUGIN_X.1,
1117    &border::border_color::PLUGIN_Y.0,
1118    &border::border_color::PLUGIN_Y.1,
1119    &border::border_color::PLUGIN_START.0,
1120    &border::border_color::PLUGIN_START.1,
1121    &border::border_color::PLUGIN_END.0,
1122    &border::border_color::PLUGIN_END.1,
1123    &border::border_color::PLUGIN_TOP.0,
1124    &border::border_color::PLUGIN_TOP.1,
1125    &border::border_color::PLUGIN_RIGHT.0,
1126    &border::border_color::PLUGIN_RIGHT.1,
1127    &border::border_color::PLUGIN_BOTTOM.0,
1128    &border::border_color::PLUGIN_BOTTOM.1,
1129    &border::border_color::PLUGIN_LEFT.0,
1130    &border::border_color::PLUGIN_LEFT.1,
1131    &background::background_color::PLUGIN,
1132    &background::background_color::PLUGIN_ARBITRARY,
1133    &background::background_image::PLUGIN,
1134    &background::background_image::PLUGIN_ARBITRARY,
1135    &background::background_image::PLUGIN_LINEAR_1,
1136    &background::background_image::PLUGIN_LINEAR_2,
1137    &background::background_image::PLUGIN_LINEAR_3,
1138    &background::background_image::PLUGIN_RADIAL_1,
1139    &background::background_image::PLUGIN_RADIAL_2,
1140    &background::background_image::PLUGIN_CONIC_1,
1141    &background::background_image::PLUGIN_CONIC_2,
1142    &background::background_image::PLUGIN_CONIC_3,
1143    &background::gradient_color_stops::PLUGIN_FROM_1,
1144    &background::gradient_color_stops::PLUGIN_FROM_2,
1145    &background::gradient_color_stops::PLUGIN_VIA_1,
1146    &background::gradient_color_stops::PLUGIN_VIA_2,
1147    &background::gradient_color_stops::PLUGIN_TO_1,
1148    &background::gradient_color_stops::PLUGIN_TO_2,
1149    &layout::box_decoration_break::PLUGIN,
1150    &background::background_size::PLUGIN,
1151    &background::background_size::PLUGIN_ARBITRARY,
1152    &background::background_attachment::PLUGIN,
1153    &background::background_clip::PLUGIN,
1154    &background::background_position::PLUGIN,
1155    &background::background_position::PLUGIN_ARBITRARY,
1156    &background::background_repeat::PLUGIN,
1157    &background::background_origin::PLUGIN,
1158    &svg::fill::PLUGIN,
1159    &svg::fill::PLUGIN_ARBITRARY,
1160    &svg::stroke::PLUGIN,
1161    &svg::stroke::PLUGIN_ARBITRARY,
1162    &svg::stroke_width::PLUGIN,
1163    &svg::stroke_width::PLUGIN_ARBITRARY,
1164    &layout::object_fit::PLUGIN,
1165    &layout::object_position::PLUGIN,
1166    &layout::object_position::PLUGIN_ARBITRARY,
1167    &spacing::padding::PLUGIN.0,
1168    &spacing::padding::PLUGIN.1,
1169    &spacing::padding::PLUGIN_X.0,
1170    &spacing::padding::PLUGIN_X.1,
1171    &spacing::padding::PLUGIN_Y.0,
1172    &spacing::padding::PLUGIN_Y.1,
1173    &spacing::padding::PLUGIN_START.0,
1174    &spacing::padding::PLUGIN_START.1,
1175    &spacing::padding::PLUGIN_END.0,
1176    &spacing::padding::PLUGIN_END.1,
1177    &spacing::padding::PLUGIN_TOP.0,
1178    &spacing::padding::PLUGIN_TOP.1,
1179    &spacing::padding::PLUGIN_RIGHT.0,
1180    &spacing::padding::PLUGIN_RIGHT.1,
1181    &spacing::padding::PLUGIN_BOTTOM.0,
1182    &spacing::padding::PLUGIN_BOTTOM.1,
1183    &spacing::padding::PLUGIN_LEFT.0,
1184    &spacing::padding::PLUGIN_LEFT.1,
1185    &typography::text_align::PLUGIN,
1186    &typography::text_indent::PLUGIN,
1187    &typography::text_indent::PLUGIN_ARBITRARY,
1188    &typography::vertical_align::PLUGIN,
1189    &typography::font_family::PLUGIN,
1190    &typography::font_family::PLUGIN_ARBITRARY,
1191    &typography::font_size::PLUGIN,
1192    &typography::font_size::PLUGIN_ARBITRARY,
1193    &typography::font_weight::PLUGIN,
1194    &typography::font_weight::PLUGIN_ARBITRARY,
1195    &typography::text_transform::PLUGIN,
1196    &typography::font_style::PLUGIN,
1197    &typography::font_variant_numeric::PLUGIN,
1198    &typography::letter_spacing::PLUGIN,
1199    &typography::letter_spacing::PLUGIN_ARBITRARY,
1200    &typography::line_height::PLUGIN_LIST,
1201    &typography::line_height::PLUGIN_SPACING,
1202    &typography::line_height::PLUGIN_ARBITRARY,
1203    &typography::text_color::PLUGIN,
1204    &typography::text_color::PLUGIN_ARBITRARY,
1205    &typography::text_decoration::PLUGIN,
1206    &typography::text_decoration_color::PLUGIN,
1207    &typography::text_decoration_color::PLUGIN_ARBITRARY,
1208    &typography::text_decoration_style::PLUGIN,
1209    &typography::text_decoration_thickness::PLUGIN_LIST,
1210    &typography::text_decoration_thickness::PLUGIN_NUMBER,
1211    &typography::text_decoration_thickness::PLUGIN_ARBITRARY,
1212    &typography::text_underline_offset::PLUGIN_LIST,
1213    &typography::text_underline_offset::PLUGIN_NUMBER,
1214    &typography::text_underline_offset::PLUGIN_ARBITRARY,
1215    &typography::font_smoothing::PLUGIN,
1216    &interactivity::caret_color::PLUGIN,
1217    &interactivity::caret_color::PLUGIN_ARBITRARY,
1218    &interactivity::accent_color::PLUGIN,
1219    &interactivity::accent_color::PLUGIN_ARBITRARY,
1220    &effect::opacity::PLUGIN,
1221    &effect::background_blend_mode::PLUGIN,
1222    &effect::mix_blend_mode::PLUGIN,
1223    &effect::text_shadow::PLUGIN,
1224    &effect::text_shadow::PLUGIN_ARBITRARY,
1225    &effect::text_shadow_color::PLUGIN,
1226    &effect::text_shadow_color::PLUGIN_ARBITRARY,
1227    &effect::box_shadow::PLUGIN,
1228    &effect::box_shadow::PLUGIN_ARBITRARY,
1229    &effect::box_shadow_color::PLUGIN,
1230    &effect::box_shadow_color::PLUGIN_ARBITRARY,
1231    &effect::box_shadow::PLUGIN_INSET_1,
1232    &effect::box_shadow::PLUGIN_INSET_2,
1233    &effect::box_shadow_color::PLUGIN_INSET_1,
1234    &effect::box_shadow_color::PLUGIN_INSET_2,
1235    &border::outline_style::PLUGIN_LIST_1,
1236    &border::outline_style::PLUGIN_LIST_2,
1237    &border::outline_width::PLUGIN,
1238    &border::outline_width::PLUGIN_ARBITRARY,
1239    &border::outline_offset::PLUGIN,
1240    &border::outline_offset::PLUGIN_ARBITRARY,
1241    &border::outline_color::PLUGIN,
1242    &border::outline_color::PLUGIN_ARBITRARY,
1243    &border::ring_width::PLUGIN,
1244    &border::ring_width::PLUGIN_ARBITRARY,
1245    &border::ring_color::PLUGIN,
1246    &border::ring_color::PLUGIN_ARBITRARY,
1247    &border::ring_width::PLUGIN_INSET_1,
1248    &border::ring_width::PLUGIN_INSET_2,
1249    &border::ring_color::PLUGIN_INSET_1,
1250    &border::ring_color::PLUGIN_INSET_2,
1251    &border::ring_offset_width::PLUGIN,
1252    &border::ring_offset_width::PLUGIN_ARBITRARY,
1253    &border::ring_offset_color::PLUGIN,
1254    &border::ring_offset_color::PLUGIN_ARBITRARY,
1255    &filter::blur::PLUGIN,
1256    &filter::blur::PLUGIN_ARBITRARY,
1257    &filter::brightness::PLUGIN,
1258    &filter::contrast::PLUGIN,
1259    &filter::drop_shadow::PLUGIN,
1260    &filter::drop_shadow::PLUGIN_ARBITRARY,
1261    &filter::grayscale::PLUGIN,
1262    &filter::hue_rotate::PLUGIN,
1263    &filter::hue_rotate::PLUGIN_ARBITRARY,
1264    &filter::invert::PLUGIN,
1265    &filter::saturate::PLUGIN,
1266    &filter::sepia::PLUGIN,
1267    &filter::filter_type::PLUGIN,
1268    &filter::backdrop_blur::PLUGIN,
1269    &filter::backdrop_blur::PLUGIN_ARBITRARY,
1270    &filter::backdrop_brightness::PLUGIN,
1271    &filter::backdrop_contrast::PLUGIN,
1272    &filter::backdrop_grayscale::PLUGIN,
1273    &filter::backdrop_hue_rotate::PLUGIN,
1274    &filter::backdrop_hue_rotate::PLUGIN_ARBITRARY,
1275    &filter::backdrop_invert::PLUGIN,
1276    &filter::backdrop_saturate::PLUGIN,
1277    &filter::backdrop_sepia::PLUGIN,
1278    &filter::backdrop_filter::PLUGIN,
1279    &transition::transition_property::PLUGIN,
1280    &transition::transition_property::PLUGIN_ARBITRARY,
1281    &transition::transition_delay::PLUGIN,
1282    &transition::transition_delay::PLUGIN_ARBITRARY,
1283    &transition::transition_duration::PLUGIN,
1284    &transition::transition_duration::PLUGIN_ARBITRARY,
1285    &transition::transition_timing_function::PLUGIN,
1286    &transition::transition_timing_function::PLUGIN_ARBITRARY,
1287    &interactivity::will_change::PLUGIN,
1288    &interactivity::will_change::PLUGIN_ARBITRARY,
1289    &typography::content::PLUGIN,
1290    &typography::content::PLUGIN_ARBITRARY,
1291    &typography::line_clamp::PLUGIN_NUMBER,
1292    &typography::line_clamp::PLUGIN_LIST,
1293    &layout::at_container::PLUGIN,
1294    &layout::at_container::PLUGIN_ARBITRARY,
1295];
1296
1297/// Configuration for the [`Theme::dark_mode`] field.
1298///
1299/// It defines how the `dark:` variant should behave.
1300///
1301/// The default value is [`DarkMode::Media`] which enables the automatic detection of the theme based
1302/// on  user preference.
1303#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Default)]
1304#[serde(rename_all = "lowercase")]
1305pub enum DarkMode {
1306    /// The `dark:` variant will modify the class of the selector. You'll then need to toggle this
1307    /// class to enable the dark theme.
1308    ///
1309    /// # Example
1310    ///
1311    /// ```
1312    /// use encre_css::{Config, config::DarkMode};
1313    ///
1314    /// let mut config = Config::default();
1315    /// config.theme.dark_mode = DarkMode::new_class("body.dark");
1316    ///
1317    /// let generated = encre_css::generate(
1318    ///     ["dark:text-white"],
1319    ///     &config,
1320    /// );
1321    ///
1322    /// assert!(generated.ends_with(r#"body.dark .dark\:text-white {
1323    ///   color: #fff;
1324    /// }"#));
1325    /// ```
1326    Class(Cow<'static, str>),
1327
1328    /// The `dark:` variant will generates a `@media (prefers-color-scheme: dark)` rule to enable
1329    /// the dark theme following user preference.
1330    ///
1331    /// This is the default value.
1332    ///
1333    /// # Example
1334    ///
1335    /// ```
1336    /// use encre_css::{Config, config::DarkMode};
1337    ///
1338    /// let mut config = Config::default();
1339    /// config.theme.dark_mode = DarkMode::Media;
1340    ///
1341    /// let generated = encre_css::generate(
1342    ///     ["dark:text-white"],
1343    ///     &config,
1344    /// );
1345    ///
1346    /// assert!(generated.ends_with(r#"@media (prefers-color-scheme: dark) {
1347    ///   .dark\:text-white {
1348    ///     color: #fff;
1349    ///   }
1350    /// }"#));
1351    /// ```
1352    #[default]
1353    Media,
1354}
1355
1356impl DarkMode {
1357    /// Quickly build a [`DarkMode::Class`] value.
1358    pub fn new_class<T: Into<Cow<'static, str>>>(class: T) -> Self {
1359        Self::Class(class.into())
1360    }
1361}
1362
1363/// Configuration for the [`Theme::aria`] field.
1364///
1365/// It defines a list of custom ARIA states.
1366#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1367pub struct Aria(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1368
1369impl Aria {
1370    /// Add an ARIA state to the list.
1371    #[inline]
1372    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1373        &mut self,
1374        key: T1,
1375        val: T2,
1376    ) {
1377        self.0.insert(key.into(), val.into());
1378    }
1379
1380    /// Remove an ARIA state from the list.
1381    #[inline]
1382    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1383        self.0.remove(&key.into());
1384    }
1385
1386    #[inline]
1387    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1388        self.0.iter()
1389    }
1390
1391    pub(crate) fn len(&self) -> usize {
1392        self.0.len()
1393    }
1394}
1395
1396/// Configuration for the [`Theme::screens`] field.
1397///
1398/// It defines a list of custom screen breakpoints.
1399#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1400pub struct Screens(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1401
1402impl Screens {
1403    /// Add a custom screen breakpoint to the list.
1404    #[inline]
1405    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1406        &mut self,
1407        key: T1,
1408        val: T2,
1409    ) {
1410        self.0.insert(key.into(), val.into());
1411    }
1412
1413    /// Remove a custom screen breakpoint from the list.
1414    #[inline]
1415    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1416        self.0.remove(&key.into());
1417    }
1418
1419    #[inline]
1420    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1421        self.0.iter()
1422    }
1423
1424    pub(crate) fn len(&self) -> usize {
1425        self.0.len()
1426    }
1427}
1428
1429/// Configuration for the [`Theme::containers`] field.
1430///
1431/// It defines a list of custom container size breakpoints.
1432#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1433pub struct Containers(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1434
1435impl Containers {
1436    /// Add a custom container size breakpoint to the list.
1437    #[inline]
1438    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1439        &mut self,
1440        key: T1,
1441        val: T2,
1442    ) {
1443        self.0.insert(key.into(), val.into());
1444    }
1445
1446    /// Remove a custom container size breakpoint from the list.
1447    #[inline]
1448    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1449        self.0.remove(&key.into());
1450    }
1451
1452    #[inline]
1453    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1454        self.0.iter()
1455    }
1456
1457    pub(crate) fn len(&self) -> usize {
1458        self.0.len()
1459    }
1460}
1461
1462/// Configuration for the [`Theme::colors`] field.
1463///
1464/// It defines a list of custom colors.
1465#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1466pub struct Colors(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1467
1468impl Colors {
1469    /// Add a custom color to the list.
1470    #[inline]
1471    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1472        &mut self,
1473        key: T1,
1474        val: T2,
1475    ) {
1476        self.0.insert(key.into(), val.into());
1477    }
1478
1479    /// Remove a custom color from the list.
1480    #[inline]
1481    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1482        self.0.remove(&key.into());
1483    }
1484
1485    #[inline]
1486    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
1487        self.0.get(&key.into())
1488    }
1489
1490    #[inline]
1491    pub(crate) fn contains<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> bool {
1492        self.0.contains_key(&key.into())
1493    }
1494}
1495
1496/// Configuration for the [`Config::shortcuts`] field.
1497///
1498/// It defines a list of shortcuts used to combine several utility classes into one.
1499///
1500/// # Example
1501///
1502/// ```
1503/// use encre_css::Config;
1504///
1505/// let mut config = Config::default();
1506/// config.shortcuts.add("btn", "border-1 rounded-xl bg-red-500");
1507///
1508/// let generated = encre_css::generate(
1509///     [r#"<button class="btn">Click me</button>"#],
1510///     &config,
1511/// );
1512///
1513/// assert!(generated.ends_with(r#".btn {
1514///   border-radius: 0.75rem;
1515/// }
1516///
1517/// .btn {
1518///   border-width: 1px;
1519/// }
1520///
1521/// .btn {
1522///   background-color: oklch(63.7% .237 25.331);
1523/// }"#));
1524/// ```
1525///
1526/// ### Corresponding TOML configuration
1527///
1528/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[shortcuts]</span>
1529/// btn = <span class="string">"border-1 rounded-xl bg-red-500"</span></code></pre></div>
1530#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1531pub struct Shortcuts(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1532
1533impl Shortcuts {
1534    /// Add a shortcut to the list.
1535    #[inline]
1536    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1537        &mut self,
1538        key: T1,
1539        val: T2,
1540    ) {
1541        self.0.insert(key.into(), val.into());
1542    }
1543
1544    /// Remove a shortcut from the list.
1545    #[inline]
1546    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1547        self.0.remove(&key.into());
1548    }
1549
1550    #[inline]
1551    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
1552        self.0.get(&key.into())
1553    }
1554}
1555
1556/// Configuration for the [`Config::layers`] field.
1557///
1558/// It defines a list of layers used to change the ordering of the generated classes.
1559///
1560/// ### Layers and ordering
1561///
1562/// By default, every class is ordered based on its plugin, variants and modifier.
1563///
1564/// If you want more control over the ordering of a specific set of classes, you can define a
1565/// _layer_ in the configuration (see the example below) and use the variant `l-<layer_name>` to
1566/// put the class in the corresponding layer.
1567///
1568/// By default, the layer containing builtin plugins has the index `0` and the layer containing
1569/// custom plugins has index `-1`.
1570///
1571/// The index is an `i8`, so it's between -128 and 127.
1572///
1573/// # Example
1574///
1575/// ```
1576/// use encre_css::Config;
1577///
1578/// let mut config = Config::default();
1579/// config.layers.add("components", -2);
1580/// config.layers.add("utilities", 2);
1581///
1582/// let generated = encre_css::generate(
1583///     [r#"<button class="l-components:bg-red-500 l-utilities:bg-red-100">Click me</button>"#],
1584///     &config,
1585/// );
1586///
1587/// // Without the use of layers, `bg-red-100` would have been generated first and would have been
1588/// // overridden by `bg-red-500`.
1589/// // Here the layer `components` has an index smaller than the layer
1590/// // `utilities`, so all the classes on this layer will be generated first.
1591/// assert!(generated.ends_with(r#".l-components\:bg-red-500 {
1592///   background-color: oklch(63.7% .237 25.331);
1593/// }
1594///
1595/// .l-utilities\:bg-red-100 {
1596///   background-color: oklch(93.6% .032 17.717);
1597/// }"#));
1598/// ```
1599///
1600/// ### Corresponding TOML configuration
1601///
1602/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[layers]</span>
1603/// components = <span class="number">-2</span>
1604/// utilities = <span class="number">2</span></code></pre></div>
1605#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1606pub struct Layers(BTreeMap<Cow<'static, str>, i8>);
1607
1608impl Layers {
1609    /// Add a layer to the list.
1610    #[inline]
1611    pub fn add<T1: Into<Cow<'static, str>>>(&mut self, key: T1, val: i8) {
1612        self.0.insert(key.into(), val);
1613    }
1614
1615    /// Remove a layer from the list.
1616    #[inline]
1617    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1618        self.0.remove(&key.into());
1619    }
1620
1621    #[inline]
1622    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&'a self, key: T) -> Option<&'a i8> {
1623        self.0.get(&key.into())
1624    }
1625}
1626
1627/// The maximum depth at which shortcuts will be resolved.
1628///
1629/// During the shortcut expansion, if the depth is greater than `max_shortcut_depth`, only the already expanded selectors until the maximum depth will be added.
1630///
1631/// By default it is set to `5`.
1632#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
1633pub struct MaxShortcutDepth(usize);
1634
1635impl MaxShortcutDepth {
1636    /// Create a new `MaxShortcutDepth`.
1637    pub fn new(v: usize) -> Self {
1638        Self(v)
1639    }
1640
1641    /// Get the inner depth as an `usize`.
1642    pub fn get(&self) -> usize {
1643        self.0
1644    }
1645}
1646
1647impl From<usize> for MaxShortcutDepth {
1648    fn from(v: usize) -> Self {
1649        Self(v)
1650    }
1651}
1652
1653impl From<MaxShortcutDepth> for usize {
1654    fn from(val: MaxShortcutDepth) -> Self {
1655        val.0
1656    }
1657}
1658
1659impl Default for MaxShortcutDepth {
1660    fn default() -> Self {
1661        Self(5)
1662    }
1663}
1664
1665/// Configuration for the [`Config::safelist`] field.
1666///
1667/// It defines a list of selectors that are manually forced to be present in the generated CSS.
1668/// It should be used when you dynamically create selectors (for example, in Javascript
1669/// `text-${ active ? "blue" : "gray" }-400`, in this case, `text-blue-400` and `text-gray-400`
1670/// should be added to the safelist).
1671///
1672/// # Example
1673///
1674/// ```
1675/// use encre_css::Config;
1676///
1677/// let mut config = Config::default();
1678/// config.safelist.add("text-blue-400");
1679/// config.safelist.add("text-gray-400");
1680///
1681/// let generated = encre_css::generate(
1682///     [r#"<button class="bg-red-500">Click me</button>"#],
1683///     &config,
1684/// );
1685///
1686/// assert!(generated.ends_with(r#".bg-red-500 {
1687///   background-color: oklch(63.7% .237 25.331);
1688/// }
1689///
1690/// .text-blue-400 {
1691///   color: oklch(70.7% .165 254.624);
1692/// }
1693///
1694/// .text-gray-400 {
1695///   color: oklch(70.7% .022 261.325);
1696/// }"#));
1697/// ```
1698///
1699/// ### Corresponding TOML configuration
1700///
1701/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code>safelist = [<span class="string">"text-blue-400"</span>, <span class="string">"text-gray-400"</span>]</code></pre></div>
1702#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1703pub struct Safelist(BTreeSet<Cow<'static, str>>);
1704
1705impl Safelist {
1706    /// Add a selector to the safelist.
1707    #[inline]
1708    pub fn add<T: Into<Cow<'static, str>>>(&mut self, val: T) {
1709        self.0.insert(val.into());
1710    }
1711
1712    /// Remove a selector from the safelist.
1713    #[inline]
1714    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, val: T) {
1715        self.0.remove(&val.into());
1716    }
1717
1718    #[inline]
1719    pub(crate) fn iter(&self) -> impl Iterator<Item = &Cow<'static, str>> {
1720        self.0.iter()
1721    }
1722}
1723
1724/// Configuration for the [`Config::extra`] field.
1725///
1726/// It defines some extra fields that can be used to store arbitrary values usable in plugins.
1727/// The fields are represented as [`toml::Value`] to allow all types to be serialized.
1728/// It is recommended to use a table by plugin (e.g. the `encre-css-icons`'s plugin uses the
1729/// `icons` key containing a table grouping all configuration fields).
1730#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
1731pub struct Extra(BTreeMap<Cow<'static, str>, toml::Value>);
1732
1733impl Extra {
1734    /// Add an extra field.
1735    #[inline]
1736    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<toml::Value>>(&mut self, key: T1, val: T2) {
1737        self.0.insert(key.into(), val.into());
1738    }
1739
1740    /// Remove an extra field.
1741    #[inline]
1742    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1743        self.0.remove(&key.into());
1744    }
1745
1746    /// Get the value of an extra field.
1747    #[inline]
1748    pub fn get<'a, T: Into<Cow<'a, str>>>(&'a self, key: T) -> Option<&'a toml::Value> {
1749        self.0.get(&key.into())
1750    }
1751}
1752
1753/// Configuration for the [`Config::theme`] field.
1754///
1755/// It defines some design system specific values like custom colors or screen breakpoints.
1756///
1757/// # Example
1758///
1759/// ```
1760/// use encre_css::{Config, config::DarkMode};
1761///
1762/// let mut config = Config::default();
1763/// config.theme.dark_mode = DarkMode::new_class("body.dark");
1764/// config.theme.colors.add("primary", "#d3198c");
1765/// config.theme.screens.add("tablet", "640px");
1766/// config.theme.containers.add("medium", "640px");
1767/// config.theme.aria.add("current", r#"current="page""#);
1768///
1769/// let generated = encre_css::generate(
1770///     [r#"<div class="bg-primary tablet:block aria-current:text-primary"><button class="bg-white @medium:flex">Click me</button>"#],
1771///     &config,
1772/// );
1773///
1774/// assert!(generated.ends_with(r#"
1775/// .bg-primary {
1776///   background-color: #d3198c;
1777/// }
1778///
1779/// .bg-white {
1780///   background-color: #fff;
1781/// }
1782///
1783/// @media (width >= 640px) {
1784///   .tablet\:block {
1785///     display: block;
1786///   }
1787/// }
1788///
1789/// @container (width >= 640px) {
1790///   .\@medium\:flex {
1791///     display: flex;
1792///   }
1793/// }
1794///
1795/// .aria-current\:text-primary[aria-current="page"] {
1796///   color: #d3198c;
1797/// }"#));
1798/// ```
1799///
1800/// ### Corresponding TOML configuration
1801///
1802/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[theme]</span>
1803/// dark_mode = { class = <span class="string">"body.dark"</span> }<br>
1804/// <span class="kw">[theme.colors]</span>
1805/// primary = <span class="string">"#d3198c"</span><br>
1806/// <span class="kw">[theme.screens]</span>
1807/// tablet = <span class="string">"640px"</span><br>
1808/// <span class="kw">[theme.containers]</span>
1809/// medium = <span class="string">"640px"</span><br>
1810/// <span class="kw">[theme.aria]</span>
1811/// current = <span class="string">'current="page"'</span></code></pre></div>
1812#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1813pub struct Theme {
1814    /// Dark mode configuration.
1815    ///
1816    /// The default value is [`DarkMode::Media`].
1817    #[serde(default)]
1818    pub dark_mode: DarkMode,
1819
1820    /// Custom screen breakpoints configuration.
1821    ///
1822    /// The default value is an empty map.
1823    #[serde(default)]
1824    pub screens: Screens,
1825
1826    /// Custom container size breakpoints configuration.
1827    ///
1828    /// The default value is an empty map.
1829    #[serde(default)]
1830    pub containers: Containers,
1831
1832    /// Custom colors configuration.
1833    ///
1834    /// The default value is an empty map.
1835    #[serde(default)]
1836    pub colors: Colors,
1837
1838    /// Custom ARIA states.
1839    ///
1840    /// The default value is an empty map.
1841    #[serde(default)]
1842    pub aria: Aria,
1843}
1844
1845/// The configuration of the CSS generation done in the [`generate`] function.
1846///
1847/// You can create a configuration using one of the ways listed below:
1848///
1849/// - It can be the default one:
1850///
1851/// ```
1852/// use encre_css::Config;
1853///
1854/// let config = Config::default();
1855/// let _generated = encre_css::generate([], &config);
1856/// ```
1857///
1858/// - It can be a customized one:
1859///
1860/// ```
1861/// use encre_css::Config;
1862///
1863/// let mut config = Config::default();
1864/// config.theme.colors.add("flashy", "#ff2d20");
1865///
1866/// let _generated = encre_css::generate([], &config);
1867/// ```
1868///
1869/// - It can be loaded from a [TOML](https://toml.io) file:
1870///
1871/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment"># encre-css.toml</span>
1872/// <span class="kw">[theme]</span>
1873/// dark_mode = { class = <span class="string">".dark"</span> }
1874/// screens = { 3xl = <span class="string">"1600px"</span>, lg = <span class="string">"2000px"</span> }<br>
1875/// <span class="kw">[theme.colors]</span>
1876/// primary = <span class="string">"#e5186a"</span>
1877/// yellow-400 = <span class="string">"#ffef0e"</span></code></pre></div>
1878///
1879/// ```no_run
1880/// use encre_css::Config;
1881///
1882/// # fn main() -> encre_css::Result<()> {
1883/// let config = Config::from_file("encre-css.toml")?;
1884/// let _generated = encre_css::generate([], &config);
1885/// # Ok(())
1886/// # }
1887/// ```
1888///
1889/// Based on [Tailwind v3's configuration](https://v3.tailwindcss.com/docs/configuration).
1890///
1891/// [`generate`]: crate::generate
1892#[derive(Default, Serialize, Deserialize, Clone)]
1893pub struct Config {
1894    /// Safelist configuration.
1895    #[serde(default)]
1896    pub safelist: Safelist,
1897
1898    /// Theme configuration.
1899    #[serde(default)]
1900    pub theme: Theme,
1901
1902    /// Preflight configuration.
1903    #[serde(default)]
1904    pub preflight: Preflight,
1905
1906    /// Shortcuts configuration.
1907    #[serde(default)]
1908    pub shortcuts: Shortcuts,
1909
1910    /// Layers configuration.
1911    #[serde(default)]
1912    pub layers: Layers,
1913
1914    /// The maximum depth at which shortcuts will be resolved.
1915    #[serde(default)]
1916    pub max_shortcut_depth: MaxShortcutDepth,
1917
1918    /// Extra fields configuration.
1919    #[serde(default)]
1920    pub extra: Extra,
1921
1922    /// A list of custom plugins.
1923    #[serde(default)]
1924    pub(crate) custom_plugins: Vec<CustomPlugin>,
1925
1926    /// A custom scanner used to scan content.
1927    ///
1928    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1929    #[serde(skip)]
1930    pub scanner: Scanner,
1931
1932    /// A list of custom variants.
1933    ///
1934    /// Check [`BUILTIN_VARIANTS`] to choose the order of the custom variants you define.
1935    ///
1936    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1937    #[serde(skip)]
1938    pub(crate) custom_variants: Vec<(Cow<'static, str>, Variant<'static>)>,
1939    // TODO: Prefix (en-)
1940}
1941
1942impl Config {
1943    /// Get variants derived from other configuration fields like breakpoints and the dark mode.
1944    #[allow(clippy::too_many_lines)]
1945    pub(crate) fn get_derived_variants(&self) -> Vec<(Cow<'static, str>, Variant<'static>)> {
1946        self.theme
1947            .screens
1948            .iter()
1949            .map(|screen| {
1950                (
1951                    screen.0.clone(),
1952                    Variant {
1953                        order: BUILTIN_VARIANTS.len(),
1954                        prefixed: false,
1955                        template: Cow::Owned(format!("@media (width >= {})", screen.1)),
1956                    },
1957                )
1958            })
1959            .chain(BUILTIN_SCREENS.iter().map(|screen| {
1960                (
1961                    Cow::from(screen.0),
1962                    Variant {
1963                        order: BUILTIN_VARIANTS.len() + self.theme.screens.0.len(),
1964                        prefixed: false,
1965                        template: Cow::Owned(format!("@media (width >= {})", screen.1)),
1966                    },
1967                )
1968            }))
1969            .chain(self.theme.screens.iter().map(|screen| {
1970                (
1971                    Cow::from(format!("max-{}", screen.0)),
1972                    Variant {
1973                        order: BUILTIN_VARIANTS.len()
1974                            + self.theme.screens.len()
1975                            + BUILTIN_SCREENS.len(),
1976                        prefixed: false,
1977                        template: Cow::Owned(format!("@media (width < {})", screen.1)),
1978                    },
1979                )
1980            }))
1981            .chain(BUILTIN_SCREENS.iter().map(|screen| {
1982                (
1983                    Cow::from(format!("max-{}", screen.0)),
1984                    Variant {
1985                        order: BUILTIN_VARIANTS.len()
1986                            + 2 * self.theme.screens.len()
1987                            + BUILTIN_SCREENS.len(),
1988                        prefixed: false,
1989                        template: Cow::Owned(format!("@media (width < {})", screen.1)),
1990                    },
1991                )
1992            }))
1993            .chain(self.theme.containers.iter().map(|container| {
1994                (
1995                    Cow::from(format!("@{}", container.0)),
1996                    Variant {
1997                        order: BUILTIN_VARIANTS.len()
1998                            + 2 * self.theme.screens.len()
1999                            + 2 * BUILTIN_SCREENS.len(),
2000                        prefixed: false,
2001                        template: Cow::Owned(format!("@container (width >= {})", container.1)),
2002                    },
2003                )
2004            }))
2005            .chain(BUILTIN_CONTAINERS.iter().map(|container| {
2006                (
2007                    Cow::from(format!("@{}", container.0)),
2008                    Variant {
2009                        order: BUILTIN_VARIANTS.len()
2010                            + 2 * self.theme.screens.len()
2011                            + 2 * BUILTIN_SCREENS.len()
2012                            + self.theme.containers.len(),
2013                        prefixed: false,
2014                        template: Cow::Owned(format!("@container (width >= {})", container.1)),
2015                    },
2016                )
2017            }))
2018            .chain(self.theme.containers.iter().map(|container| {
2019                (
2020                    Cow::from(format!("@max-{}", container.0)),
2021                    Variant {
2022                        order: BUILTIN_VARIANTS.len()
2023                            + 2 * self.theme.screens.len()
2024                            + 2 * BUILTIN_SCREENS.len()
2025                            + self.theme.containers.len()
2026                            + BUILTIN_CONTAINERS.len(),
2027                        prefixed: false,
2028                        template: Cow::Owned(format!("@container (width < {})", container.1)),
2029                    },
2030                )
2031            }))
2032            .chain(BUILTIN_CONTAINERS.iter().map(|container| {
2033                (
2034                    Cow::from(format!("@max-{}", container.0)),
2035                    Variant {
2036                        order: BUILTIN_VARIANTS.len()
2037                            + 2 * self.theme.screens.len()
2038                            + 2 * BUILTIN_SCREENS.len()
2039                            + 2 * self.theme.containers.len()
2040                            + BUILTIN_CONTAINERS.len(),
2041                        prefixed: false,
2042                        template: Cow::Owned(format!("@container (width < {})", container.1)),
2043                    },
2044                )
2045            }))
2046            .chain(self.theme.aria.iter().map(|aria| {
2047                (
2048                    Cow::from(format!("aria-{}", aria.0)),
2049                    Variant {
2050                        order: BUILTIN_VARIANTS.len()
2051                            + 2 * self.theme.screens.len()
2052                            + 2 * BUILTIN_SCREENS.len()
2053                            + 2 * self.theme.containers.len()
2054                            + 2 * BUILTIN_CONTAINERS.len(),
2055                        prefixed: false,
2056                        template: Cow::Owned(format!("&[aria-{}]", aria.1)),
2057                    },
2058                )
2059            }))
2060            .chain(iter::once(match &self.theme.dark_mode {
2061                DarkMode::Media => (
2062                    Cow::from("dark"),
2063                    Variant {
2064                        order: BUILTIN_VARIANTS.len()
2065                            + 2 * self.theme.screens.len()
2066                            + 2 * BUILTIN_SCREENS.len()
2067                            + 2 * self.theme.containers.len()
2068                            + 2 * BUILTIN_CONTAINERS.len()
2069                            + self.theme.aria.0.len(),
2070                        prefixed: false,
2071                        template: Cow::Borrowed("@media (prefers-color-scheme: dark)"),
2072                    },
2073                ),
2074                DarkMode::Class(name) => (
2075                    Cow::from("dark"),
2076                    Variant {
2077                        order: BUILTIN_VARIANTS.len()
2078                            + 2 * self.theme.screens.len()
2079                            + 2 * BUILTIN_SCREENS.len()
2080                            + 2 * self.theme.containers.len()
2081                            + 2 * BUILTIN_CONTAINERS.len()
2082                            + self.theme.aria.len(),
2083                        prefixed: false,
2084                        template: Cow::Owned(format!("{name} &")),
2085                    },
2086                ),
2087            }))
2088            .collect()
2089    }
2090
2091    /// Returns the order of the last variant which can be used when defining a new variant which
2092    /// must be generated after all the other variants.
2093    ///
2094    /// Note that if you are not the maintainer of a crate providing variants, you can ignore this
2095    /// function.
2096    pub fn last_variant_order(&self) -> usize {
2097        BUILTIN_VARIANTS.len()
2098            + 2 * self.theme.screens.len()
2099            + 2 * BUILTIN_SCREENS.len()
2100            + 2 * self.theme.containers.len()
2101            + 2 * BUILTIN_CONTAINERS.len()
2102            + self.theme.aria.len()
2103            + 1
2104            + self.custom_variants.len()
2105    }
2106
2107    /// Register a custom plugin which will be used during CSS generation.
2108    ///
2109    /// Note that if you are not the maintainer of a crate providing plugins, you can ignore this
2110    /// function, see [`crate::plugins`].
2111    ///
2112    /// This function requires that the plugin uses `&[]`s, `&'static str`s and other static
2113    /// structures. If you only need to use heap-allocated structures, prefer using the
2114    /// [`Config::register_dynamic_plugin`] method.
2115    ///
2116    /// See [`crate::plugins::Plugin`] to learn how to write plugins.
2117    ///
2118    /// # Example
2119    ///
2120    /// ```
2121    /// use encre_css::{Config, prelude::build_plugin::*};
2122    ///
2123    /// const PLUGIN: StaticPlugin = Plugin::ListValues(ListValues {
2124    ///     prop: SingleProp("color"),
2125    ///     values: map! {
2126    ///         "prose" => "#333",
2127    ///         "prose-invert" => "#eee",
2128    ///     },
2129    ///     ..ListValues::default()
2130    /// });
2131    ///
2132    /// let mut config = Config::default();
2133    /// config.register_plugin(&PLUGIN);
2134    ///
2135    /// let generated = encre_css::generate(
2136    ///     ["prose", "prose-invert"],
2137    ///     &config,
2138    /// );
2139    ///
2140    /// assert!(generated.ends_with(".prose {
2141    ///   color: #333;
2142    /// }
2143    ///
2144    /// .prose-invert {
2145    ///   color: #eee;
2146    /// }"));
2147    /// ```
2148    pub fn register_plugin(&mut self, plugin: &'static StaticPlugin) {
2149        self.custom_plugins.push(CustomPlugin::Static(plugin));
2150    }
2151
2152    /// Register a custom plugin which will be used during CSS generation.
2153    ///
2154    /// Note that if you are not the maintainer of a crate providing plugins, you can ignore this
2155    /// function, see [`crate::plugins`].
2156    ///
2157    /// This function requires that the plugin uses `Vec`s, `String`s and other heap-allocated
2158    /// structures. If you only need to use static structures, prefer using the
2159    /// [`Config::register_plugin`] method.
2160    ///
2161    /// See [`crate::plugins::Plugin`] to learn how to write plugins.
2162    ///
2163    /// # Example
2164    ///
2165    /// ```
2166    /// use encre_css::{Config, prelude::build_plugin::*};
2167    ///
2168    /// const PLUGIN: StaticPlugin = Plugin::ListValues(ListValues {
2169    ///     prop: SingleProp("color"),
2170    ///     values: map! {
2171    ///         "prose" => "#333",
2172    ///         "prose-invert" => "#eee",
2173    ///     },
2174    ///     ..ListValues::default()
2175    /// });
2176    ///
2177    /// let mut config = Config::default();
2178    /// config.register_plugin(&PLUGIN);
2179    ///
2180    /// let generated = encre_css::generate(
2181    ///     ["prose", "prose-invert"],
2182    ///     &config,
2183    /// );
2184    ///
2185    /// assert!(generated.ends_with(".prose {
2186    ///   color: #333;
2187    /// }
2188    ///
2189    /// .prose-invert {
2190    ///   color: #eee;
2191    /// }"));
2192    /// ```
2193    pub fn register_dynamic_plugin(&mut self, plugin: DynamicPlugin) {
2194        self.custom_plugins.push(CustomPlugin::Dynamic(plugin));
2195    }
2196
2197    /// Register a custom variant which will be used during CSS generation.
2198    ///
2199    /// Note that if you are not the maintainer of a crate providing variants, you can ignore this
2200    /// function.
2201    ///
2202    /// # Example
2203    ///
2204    /// ```
2205    /// use encre_css::{Config, selector::Variant};
2206    /// use std::borrow::Cow;
2207    ///
2208    /// let mut config = Config::default();
2209    /// config.register_variant(
2210    ///     "headings",
2211    ///     // Insert the classes having this variant after all the other variants
2212    ///     Variant::new(config.last_variant_order(), "& :where(h1, h2, h3, h4, h5, h6)")
2213    /// );
2214    ///
2215    /// let generated = encre_css::generate(
2216    ///     ["headings:text-gray-700"],
2217    ///     &config,
2218    /// );
2219    ///
2220    /// assert!(generated.ends_with(".headings\\:text-gray-700 :where(h1, h2, h3, h4, h5, h6) {
2221    ///   color: oklch(37.3% .034 259.733);
2222    /// }"));
2223    /// ```
2224    ///
2225    /// You can also make a prefixed variant, that is a variant which has a prefix and an arbitrary
2226    /// value delimited by square brackets. When defining this kind of variant, you need to call
2227    /// [`Variant::with_prefixed`] and to insert the placeholder `{}` in the variant
2228    /// template, it will be replaced with the given arbitrary value.
2229    ///
2230    /// # Example
2231    ///
2232    /// ```
2233    /// use encre_css::{Config, selector::Variant};
2234    /// use std::borrow::Cow;
2235    ///
2236    /// let mut config = Config::default();
2237    /// config.register_variant(
2238    ///     "media",
2239    ///     // Insert the classes having this variant after all the other variants
2240    ///     Variant::new(config.last_variant_order(), "@media {}").with_prefixed()
2241    /// );
2242    ///
2243    /// let generated = encre_css::generate(
2244    ///     ["media-[print]:flex"],
2245    ///     &config,
2246    /// );
2247    ///
2248    /// assert!(generated.ends_with(r"@media print {
2249    ///   .media-\[print\]\:flex {
2250    ///     display: flex;
2251    ///   }
2252    /// }"));
2253    /// ```
2254    pub fn register_variant<T: Into<Cow<'static, str>>>(
2255        &mut self,
2256        variant_name: T,
2257        variant: Variant<'static>,
2258    ) {
2259        self.custom_variants.push((variant_name.into(), variant));
2260    }
2261
2262    /// Deserialize the content of a [TOML](https://toml.io) file to get the configuration.
2263    ///
2264    /// # Example
2265    ///
2266    /// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment"># encre-css.toml</span>
2267    /// <span class="kw">[theme]</span>
2268    /// dark_mode = { type = <span class="string">"class"</span>, class = <span class="string">".dark"</span> }
2269    /// screens = { 3xl = <span class="string">"1600px"</span>, lg = <span class="string">"2000px"</span> }<br>
2270    /// <span class="kw">[theme.colors]</span>
2271    /// primary = <span class="string">"#e5186a"</span>
2272    /// yellow-400 = <span class="string">"#ffef0e"</span></code></pre></div>
2273    ///
2274    /// ```no_run
2275    /// use encre_css::Config;
2276    ///
2277    /// # fn main() -> encre_css::Result<()> {
2278    /// let config = Config::from_file("encre-css.toml")?;
2279    /// let _generated = encre_css::generate([], &config);
2280    /// # Ok(())
2281    /// # }
2282    /// ```
2283    ///
2284    /// See [`Config`] for other ways of creating a configuration.
2285    ///
2286    /// # Errors
2287    ///
2288    /// Returns [`Error::ConfigFileNotFound`] if the given file does not exist.
2289    /// Returns [`Error::ConfigParsing`] if the given file could not be parsed.
2290    pub fn from_file<T: AsRef<Path>>(path: T) -> Result<Self> {
2291        Ok(toml::from_str(&fs::read_to_string(&path).map_err(
2292            |e| Error::ConfigFileNotFound(path.as_ref().to_path_buf(), e),
2293        )?)?)
2294    }
2295}
2296
2297impl PartialEq for Config {
2298    fn eq(&self, other: &Self) -> bool {
2299        self.safelist == other.safelist
2300            && self.theme == other.theme
2301            && self.preflight == other.preflight
2302            && self.shortcuts == other.shortcuts
2303            && self.max_shortcut_depth == other.max_shortcut_depth
2304            && self.extra == other.extra
2305            && self.custom_variants == other.custom_variants
2306    }
2307}
2308
2309impl fmt::Debug for Config {
2310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2311        f.debug_struct("Config")
2312            .field("safelist", &self.safelist)
2313            .field("theme", &self.theme)
2314            .field("preflight", &self.preflight)
2315            .field("shortcuts", &self.shortcuts)
2316            .field("max_shortcut_depth", &self.max_shortcut_depth)
2317            .field("extra", &self.extra)
2318            .field("custom_plugins", &self.custom_plugins)
2319            .field("custom_variants", &self.custom_variants)
2320            .finish_non_exhaustive()
2321    }
2322}
2323
2324#[cfg(test)]
2325mod tests {
2326    use std::collections::HashMap;
2327
2328    use super::*;
2329    use crate::{generate, utils::testing::base_config};
2330
2331    use pretty_assertions::assert_eq;
2332
2333    #[test]
2334    fn gen_css_with_custom_config() {
2335        let mut config = base_config();
2336        config.theme.colors.add("rosa-500", "#e5186a");
2337        config.theme.screens.add("3xl", "1600px");
2338
2339        let generated = generate(["3xl:text-rosa-500"], &config);
2340
2341        assert_eq!(
2342            generated,
2343            String::from(
2344                r"@media (width >= 1600px) {
2345  .\33xl\:text-rosa-500 {
2346    color: #e5186a;
2347  }
2348}"
2349            )
2350        );
2351    }
2352
2353    #[test]
2354    fn gen_css_with_shortcuts() {
2355        let mut config = base_config();
2356        config
2357            .shortcuts
2358            .add("btn", "bg-red-500 border-1 rounded-xl");
2359        config.shortcuts.add("bg", "bg-blue-100");
2360
2361        let generated = generate(["btn", "bg-yellow-500"], &config);
2362
2363        assert_eq!(
2364            generated,
2365            String::from(
2366                ".btn {
2367  border-radius: 0.75rem;
2368}
2369
2370.btn {
2371  border-width: 1px;
2372}
2373
2374.bg-yellow-500 {
2375  background-color: oklch(79.5% .184 86.047);
2376}
2377
2378.btn {
2379  background-color: oklch(63.7% .237 25.331);
2380}"
2381            )
2382        );
2383    }
2384
2385    #[test]
2386    fn gen_css_with_nested_shortcuts() {
2387        let mut config = base_config();
2388        config
2389            .shortcuts
2390            .add("btn", "bg-red-500 border-1 rounded-xl");
2391        config.shortcuts.add("btn-primary", "btn bg-blue-500");
2392
2393        let generated = generate(["btn-primary"], &config);
2394
2395        assert_eq!(
2396            generated,
2397            String::from(
2398                ".btn-primary {
2399  border-radius: 0.75rem;
2400}
2401
2402.btn-primary {
2403  border-width: 1px;
2404}
2405
2406.btn-primary {
2407  background-color: oklch(62.3% .214 259.815);
2408}
2409
2410.btn-primary {
2411  background-color: oklch(63.7% .237 25.331);
2412}"
2413            )
2414        );
2415    }
2416
2417    #[test]
2418    fn gen_css_with_shortcut_cycle() {
2419        let mut config = base_config();
2420        config
2421            .shortcuts
2422            .add("btn", "bg-red-500 border-1 rounded-xl btn-primary");
2423        config.shortcuts.add("btn-primary", "btn bg-blue-500");
2424
2425        let generated = generate(["btn-primary"], &config);
2426
2427        assert_eq!(
2428            generated,
2429            String::from(
2430                ".btn-primary {
2431  border-radius: 0.75rem;
2432}
2433
2434.btn-primary {
2435  border-width: 1px;
2436}
2437
2438.btn-primary {
2439  background-color: oklch(62.3% .214 259.815);
2440}
2441
2442.btn-primary {
2443  background-color: oklch(63.7% .237 25.331);
2444}"
2445            )
2446        );
2447    }
2448
2449    #[test]
2450    fn gen_css_with_safelist() {
2451        let mut config = base_config();
2452        config.safelist.add("text-red-500");
2453        config.safelist.add("btn");
2454        config.shortcuts.add("btn", "text-red-400");
2455
2456        let generated = generate(["bg-red-300"], &config);
2457
2458        assert_eq!(
2459            generated,
2460            String::from(
2461                ".bg-red-300 {
2462  background-color: oklch(80.8% .114 19.571);
2463}
2464
2465.btn {
2466  color: oklch(70.4% .191 22.216);
2467}
2468
2469.text-red-500 {
2470  color: oklch(63.7% .237 25.331);
2471}"
2472            )
2473        );
2474    }
2475
2476    #[test]
2477    fn gen_css_with_custom_plugin() {
2478        use crate::prelude::build_plugin::*;
2479
2480        const PLUGIN: StaticPlugin = Plugin::ListValues(ListValues {
2481            prop: SingleProp("content"),
2482            values: map! {
2483                "emoji-tada" => "\"\u{1f389}\"",
2484                "emoji-rocket" => "\"\u{1f680}\"",
2485            },
2486            ..ListValues::default()
2487        });
2488
2489        let mut config = base_config();
2490        config.register_plugin(&PLUGIN);
2491
2492        let generated = generate(["emoji-tada"], &config);
2493
2494        assert_eq!(
2495            generated,
2496            String::from(
2497                ".emoji-tada {
2498  content: \"\u{1f389}\";
2499}"
2500            )
2501        );
2502    }
2503
2504    #[test]
2505    fn gen_css_with_dynamic_plugin() {
2506        use crate::prelude::build_plugin::*;
2507
2508        let mut config = Config::default();
2509        config.preflight = Preflight::None;
2510        config.register_dynamic_plugin(Plugin::ListValues(ListValues {
2511            prop: DynamicPropertyName::SingleProp(String::from("content")),
2512            values: HashMap::from([
2513                (String::from("emoji-tada"), String::from("\"\u{1f389}\"")),
2514                (String::from("emoji-rocket"), String::from("\"\u{1f680}\"")),
2515            ]),
2516            ..ListValues::default_dynamic()
2517        }));
2518
2519        let generated = generate(["emoji-tada"], &config);
2520
2521        assert_eq!(
2522            generated,
2523            String::from(
2524                ".emoji-tada {
2525  content: \"\u{1f389}\";
2526}"
2527            )
2528        );
2529    }
2530
2531    #[test]
2532    fn gen_css_with_custom_parsed_plugin() {
2533        let config = match Config::from_file("tests/fixtures/custom-plugin-config.toml") {
2534            Ok(c) => c,
2535            Err(e) => panic!("{e}"),
2536        };
2537
2538        let generated = generate(["emoji-tada"], &config);
2539
2540        assert_eq!(
2541            generated,
2542            String::from(
2543                ".emoji-tada {
2544  content: \"\u{1f389}\";
2545}"
2546            )
2547        );
2548    }
2549
2550    #[test]
2551    fn gen_css_with_custom_parsed_plugin_and_multiple_props() {
2552        let config = match Config::from_file("tests/fixtures/custom-plugin-config-multiple.toml") {
2553            Ok(c) => c,
2554            Err(e) => panic!("{e}"),
2555        };
2556
2557        let generated = generate(["shape-roof-1"], &config);
2558
2559        assert_eq!(
2560            generated,
2561            String::from(
2562                ".shape-roof-1 {
2563  border-inline: 1px;
2564  border-top: 1px;
2565}"
2566            )
2567        );
2568    }
2569
2570    #[test]
2571    fn gen_css_with_custom_parsed_plugin_stroke() {
2572        let config = match Config::from_file("tests/fixtures/custom-plugin-stroke.toml") {
2573            Ok(c) => c,
2574            Err(e) => panic!("{e}"),
2575        };
2576
2577        let generated = generate(["custom-stroke-1", "custom-stroke-[12px]", "custom-stroke-[12%,1px]", "custom-stroke-[length:12px]"], &config);
2578
2579        assert_eq!(
2580            generated,
2581            String::from(
2582                r".custom-stroke-1 {
2583  stroke-width: 1px;
2584}
2585
2586.custom-stroke-\[12\%\,1px\] {
2587  stroke-width: 12%,1px;
2588}
2589
2590.custom-stroke-\[12px\] {
2591  stroke-width: 12px;
2592}
2593
2594.custom-stroke-\[length\:12px\] {
2595  stroke-width: 12px;
2596}"
2597            )
2598        );
2599    }
2600
2601    #[test]
2602    fn config_is_extended_and_overridden() {
2603        let config = Config::from_file("tests/fixtures/custom-config.toml").unwrap();
2604
2605        let generated = generate(
2606            [
2607                "bg-rosa-500",
2608                "bg-yellow-400",
2609                "bg-yellow-100",
2610                "3xl:underline",
2611                "lg:text-rosa-500",
2612            ],
2613            &config,
2614        );
2615
2616        assert_eq!(
2617            generated,
2618            String::from(
2619                r".bg-rosa-500 {
2620  background-color: #e5186a;
2621}
2622
2623.bg-yellow-100 {
2624  background-color: oklch(97.3% .071 103.193);
2625}
2626
2627.bg-yellow-400 {
2628  background-color: #ffef0e;
2629}
2630
2631@media (width >= 2000px) {
2632  .lg\:text-rosa-500 {
2633    color: #e5186a;
2634  }
2635}
2636
2637@media (width >= 1600px) {
2638  .\33xl\:underline {
2639    -webkit-text-decoration-line: underline;
2640    text-decoration-line: underline;
2641  }
2642}"
2643            )
2644        );
2645    }
2646
2647    #[test]
2648    fn deserialize_config() {
2649        let mut config = base_config();
2650        config.theme.colors.add("rosa-500", "#e5186a");
2651        config.theme.colors.add("yellow-400", "#ffef0e");
2652        config.theme.aria.add("current", "current=\"page\"");
2653        config.theme.screens.add("lg", "2000px");
2654        config.theme.screens.add("3xl", "1600px");
2655        config.theme.dark_mode = DarkMode::new_class(".dark");
2656
2657        assert_eq!(
2658            Config::from_file("tests/fixtures/custom-config.toml").unwrap(),
2659            config
2660        );
2661    }
2662
2663    #[test]
2664    fn serialize_config() {
2665        let mut config = Config {
2666            preflight: Preflight::None,
2667            ..Default::default()
2668        };
2669        config.theme.dark_mode = DarkMode::new_class(".dark");
2670        config.theme.screens.add("3xl", "1600px");
2671        config.theme.screens.add("lg", "2000px");
2672        config.theme.colors.add("rosa-500", "#e5186a");
2673        config.theme.colors.add("yellow-400", "#ffef0e");
2674        config.theme.aria.add("current", "current=\"page\"");
2675
2676        let result = toml::to_string(&config).unwrap();
2677
2678        let expected_config = fs::read_to_string("tests/fixtures/custom-config.toml").unwrap();
2679        assert_eq!(expected_config, result);
2680    }
2681
2682    #[test]
2683    fn toml_doc_tests() {
2684        // This function tests all TOML blocks used in the documentation
2685        // If a block is changed in this function, it should also be changed in the corresponding
2686        // documentation section and vice-versa
2687
2688        // Shortcuts
2689        {
2690            let toml_for_shortcuts = r#"
2691        [shortcuts]
2692        btn = "border-1 rounded-xl bg-red-500"
2693        "#;
2694            let config: Config = toml::from_str(toml_for_shortcuts).unwrap();
2695
2696            let generated = generate([r#"<button class="btn">Click me</button>"#], &config);
2697
2698            assert!(generated.ends_with(
2699                r#".btn {
2700  border-radius: 0.75rem;
2701}
2702
2703.btn {
2704  border-width: 1px;
2705}
2706
2707.btn {
2708  background-color: oklch(63.7% .237 25.331);
2709}"#
2710            ));
2711        }
2712
2713        // Safelist
2714        {
2715            let toml_for_safelist = r#"safelist = ["text-blue-400", "text-gray-400"]"#;
2716            let config: Config = toml::from_str(toml_for_safelist).unwrap();
2717
2718            let generated = generate([r#"<button class="bg-red-500">Click me</button>"#], &config);
2719
2720            assert!(generated.ends_with(
2721                r#".bg-red-500 {
2722  background-color: oklch(63.7% .237 25.331);
2723}
2724
2725.text-blue-400 {
2726  color: oklch(70.7% .165 254.624);
2727}
2728
2729.text-gray-400 {
2730  color: oklch(70.7% .022 261.325);
2731}"#
2732            ));
2733        }
2734
2735        // Preflight
2736        {
2737            let toml_for_preflight = r#"preflight = "none""#;
2738            let config: Config = toml::from_str(toml_for_preflight).unwrap();
2739            assert!(generate([], &config).is_empty());
2740
2741            let toml_for_preflight = r#"preflight = { custom = "html, body { width: 100vw; height: 100vh; margin: 0; }" }"#;
2742            let config: Config = toml::from_str(toml_for_preflight).unwrap();
2743            assert_eq!(
2744                generate([], &config),
2745                "html, body { width: 100vw; height: 100vh; margin: 0; }"
2746            );
2747
2748            let toml_for_preflight =
2749                r#"preflight = { full = { font_family_mono = "'Fira Code'" } }"#;
2750            let config: Config = toml::from_str(toml_for_preflight).unwrap();
2751            assert!(generate([], &config).contains(
2752                "code, kbd, samp, pre {
2753  font-family: 'Fira Code';"
2754            ));
2755        }
2756
2757        // Theme
2758        {
2759            let toml_for_theme = r##"
2760            [theme]
2761            dark_mode = { class = "body.dark" }
2762
2763            [theme.colors]
2764            primary = "#d3198c"
2765
2766            [theme.screens]
2767            tablet = "640px"
2768
2769            [theme.containers]
2770            medium = "640px"
2771
2772            [theme.aria]
2773            current = 'current="page"'
2774            "##;
2775            let config: Config = toml::from_str(toml_for_theme).unwrap();
2776
2777            let generated = generate(
2778                [
2779                    r#"<div class="bg-primary tablet:block aria-current:text-primary"><button class="bg-white @medium:flex">Click me</button>"#,
2780                ],
2781                &config,
2782            );
2783
2784            assert!(generated.ends_with(
2785                r#"
2786.bg-primary {
2787  background-color: #d3198c;
2788}
2789
2790.bg-white {
2791  background-color: #fff;
2792}
2793
2794@media (width >= 640px) {
2795  .tablet\:block {
2796    display: block;
2797  }
2798}
2799
2800@container (width >= 640px) {
2801  .\@medium\:flex {
2802    display: flex;
2803  }
2804}
2805
2806.aria-current\:text-primary[aria-current="page"] {
2807  color: #d3198c;
2808}"#
2809            ));
2810        }
2811    }
2812}