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, 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> = phf_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/// Based on [Tailwind's default variants](https://tailwindcss.com/docs/hover-focus-and-other-states).
705#[rustfmt::skip]
706pub const BUILTIN_VARIANTS: phf::OrderedMap<&'static str, Variant> = {
707    let mut counter = 0;
708
709    phf_ordered_map! {
710        "not" => Variant::new_const(&mut counter, "&:not({})").with_prefixed(),
711        "group" => Variant::new_const(&mut counter, "&:is(:where(.group){} *)").with_prefixed(),
712        "peer" => Variant::new_const(&mut counter, "&:is(:where(.peer){} ~ *)").with_prefixed(),
713
714        "first-letter" => Variant::new_const(&mut counter, "&::first-letter"),
715        "first-line" => Variant::new_const(&mut counter, "&::first-line"),
716        "marker" => Variant::new_const(&mut counter, "& *::marker, &::marker"),
717        "selection" => Variant::new_const(&mut counter, "& *::selection, &::selection"),
718        "file" => Variant::new_const(&mut counter, "&::file-selector-button, &::-webkit-file-upload-button"),
719        "placeholder" => Variant::new_const(&mut counter, "&::placeholder"),
720        "backdrop" => Variant::new_const(&mut counter, "&::backdrop"),
721        "details-content" => Variant::new_const(&mut counter, "&::details-content"),
722        "before" => Variant::new_const(&mut counter, "&::before"),
723        "after" => Variant::new_const(&mut counter, "&::after"),
724        "all" => Variant::new_const(&mut counter, "& *"),
725        "**" => Variant::new_const(&mut counter, "& *"),
726        "children" => Variant::new_const(&mut counter, "& > *"),
727        "*" => Variant::new_const(&mut counter, "& > *"),
728        "siblings" => Variant::new_const(&mut counter, "& ~ *"),
729        "sibling" => Variant::new_const(&mut counter, "& + *"),
730
731        "first" => Variant::new_const(&mut counter, "&:first-child"),
732        "not-first" => Variant::new_const(&mut counter, "&:not(:first-child)"),
733        "last" => Variant::new_const(&mut counter, "&:last-child"),
734        "not-last" => Variant::new_const(&mut counter, "&:not(:last-child)"),
735        "only" => Variant::new_const(&mut counter, "&:only-child"),
736        "not-only" => Variant::new_const(&mut counter, "&:not(:only-child)"),
737        "odd" => Variant::new_const(&mut counter, "&:nth-child(odd)"),
738        "even" => Variant::new_const(&mut counter, "&:nth-child(even)"),
739        "first-of-type" => Variant::new_const(&mut counter, "&:first-of-type"),
740        "last-of-type" => Variant::new_const(&mut counter, "&:last-of-type"),
741        "only-of-type" => Variant::new_const(&mut counter, "&:only-of-type"),
742        "not-first-of-type" => Variant::new_const(&mut counter, "&:not(:first-of-type)"),
743        "not-last-of-type" => Variant::new_const(&mut counter, "&:not(:last-of-type)"),
744        "not-only-of-type" => Variant::new_const(&mut counter, "&:not(:only-of-type)"),
745        "visited" => Variant::new_const(&mut counter, "&:visited"),
746        "target" => Variant::new_const(&mut counter, "&:target"),
747        "open" => Variant::new_const(&mut counter, "&[open]"),
748        "default" => Variant::new_const(&mut counter, "&:default"),
749        "checked" => Variant::new_const(&mut counter, "&:checked"),
750        "not-checked" => Variant::new_const(&mut counter, "&:not(:checked)"),
751        "indeterminate" => Variant::new_const(&mut counter, "&:indeterminate"),
752        "placeholder-shown" => Variant::new_const(&mut counter, "&:placeholder-shown"),
753        "autofill" => Variant::new_const(&mut counter, "&:autofill"),
754        "optional" => Variant::new_const(&mut counter, "&:optional"),
755        "required" => Variant::new_const(&mut counter, "&:required"),
756        "valid" => Variant::new_const(&mut counter, "&:valid"),
757        "invalid" => Variant::new_const(&mut counter, "&:invalid"),
758        "in-range" => Variant::new_const(&mut counter, "&:in-range"),
759        "out-of-range" => Variant::new_const(&mut counter, "&:out-of-range"),
760        "read-only" => Variant::new_const(&mut counter, "&:read-only"),
761        "read-write" => Variant::new_const(&mut counter, "&:read-write"),
762        "empty" => Variant::new_const(&mut counter, "&:empty"),
763        "focus-within" => Variant::new_const(&mut counter, "&:focus-within"),
764        "hover" => Variant::new_const(&mut counter, "&:hover"),
765        "focus" => Variant::new_const(&mut counter, "&:focus"),
766        "focus-visible" => Variant::new_const(&mut counter, "&:focus-visible"),
767        "active" => Variant::new_const(&mut counter, "&:active"),
768        "enabled" => Variant::new_const(&mut counter, "&:enabled"),
769        "disabled" => Variant::new_const(&mut counter, "&:disabled"),
770        "inert" => Variant::new_const(&mut counter, "&:is([inert], [inert] *)"),
771        "in" => Variant::new_const(&mut counter, ":where({}) &").with_prefixed(),
772        "has" => Variant::new_const(&mut counter, "&:has({})").with_prefixed(),
773
774        "aria-busy" => Variant::new_const(&mut counter, "&[aria-busy=\"true\"]"),
775        "aria-checked" => Variant::new_const(&mut counter, "&[aria-checked=\"true\"]"),
776        "aria-disabled" => Variant::new_const(&mut counter, "&[aria-disabled=\"true\"]"),
777        "aria-expanded" => Variant::new_const(&mut counter, "&[aria-expanded=\"true\"]"),
778        "aria-hidden" => Variant::new_const(&mut counter, "&[aria-hidden=\"true\"]"),
779        "aria-pressed" => Variant::new_const(&mut counter, "&[aria-pressed=\"true\"]"),
780        "aria-readonly" => Variant::new_const(&mut counter, "&[aria-readonly=\"true\"]"),
781        "aria-required" => Variant::new_const(&mut counter, "&[aria-required=\"true\"]"),
782        "aria-selected" => Variant::new_const(&mut counter, "&[aria-selected=\"true\"]"),
783        "aria" => Variant::new_const(&mut counter, "&[aria-{}]").with_prefixed(),
784
785        "data" => Variant::new_const(&mut counter, "&[data-{}]").with_prefixed(),
786        "nth" => Variant::new_const(&mut counter, "&:nth-child({})").with_prefixed(),
787        "nth-last" => Variant::new_const(&mut counter, "&:nth-last-child({})").with_prefixed(),
788        "nth-of-type" => Variant::new_const(&mut counter, "&:nth-of-type({})").with_prefixed(),
789        "nth-last-of-type" => Variant::new_const(&mut counter, "&:nth-last-of-type({})").with_prefixed(),
790        "supports" => Variant::new_const(&mut counter, "@supports ({})").with_prefixed(),
791
792        "motion-safe" => Variant::new_const(&mut counter, "@media (prefers-reduced-motion: no-preference)"),
793        "motion-reduce" => Variant::new_const(&mut counter, "@media (prefers-reduced-motion: reduce)"),
794        "contrast-more" => Variant::new_const(&mut counter, "@media (prefers-contrast: more)"),
795        "contrast-less" => Variant::new_const(&mut counter, "@media (prefers-contrast: less)"),
796
797        "max" => Variant::new_const(&mut counter, "@media (width < {})").with_prefixed(),
798        "min" => Variant::new_const(&mut counter, "@media (width >= {})").with_prefixed(),
799        "@max" => Variant::new_const(&mut counter, "@container (width < {})").with_prefixed(),
800        "@min" => Variant::new_const(&mut counter, "@container (width >= {})").with_prefixed(),
801
802        "portrait" => Variant::new_const(&mut counter, "@media (orientation: portrait)"),
803        "landscape" => Variant::new_const(&mut counter, "@media (orientation: landscape)"),
804        "ltr" => Variant::new_const(&mut counter, "[dir=\"ltr\"] &"),
805        "rtl" => Variant::new_const(&mut counter, "[dir=\"rtl\"] &"),
806        "starting" => Variant::new_const(&mut counter, "@starting-style"),
807        "print" => Variant::new_const(&mut counter, "@media print"),
808        "forced-colors" => Variant::new_const(&mut counter, "@media (forced-colors: active)"),
809        "inverted-colors" => Variant::new_const(&mut counter, "@media (inverted-colors: inverted)"),
810        "pointer-none" => Variant::new_const(&mut counter, "@media (pointer: none)"),
811        "pointer-coarse" => Variant::new_const(&mut counter, "@media (pointer: coarse)"),
812        "pointer-fine" => Variant::new_const(&mut counter, "@media (pointer: fine)"),
813        "any-pointer-none" => Variant::new_const(&mut counter, "@media (any-pointer: none)"),
814        "any-pointer-coarse" => Variant::new_const(&mut counter, "@media (any-pointer: coarse)"),
815        "any-pointer-fine" => Variant::new_const(&mut counter, "@media (any-pointer: fine)"),
816        "noscript" => Variant::new_const(&mut counter, "@media (scripting: none)"),
817    }
818};
819
820/// The list of all default plugins.
821///
822/// Sorted following [Tailwind's order](https://github.com/tailwindlabs/tailwindcss/blob/master/src/corePlugins.js).
823#[rustfmt::skip]
824pub const BUILTIN_PLUGINS: &[(Cow<'static, str>, &'static (dyn Plugin + Send + Sync))] = &[
825    (Cow::Borrowed("container"), &layout::container::PluginDefinition),
826    (Cow::Borrowed(""), &accessibility::screen_reader::PluginDefinition),
827    (Cow::Borrowed("pointer-events"), &interactivity::pointer_events::PluginDefinition),
828    (Cow::Borrowed(""), &layout::visibility::PluginDefinition),
829    (Cow::Borrowed(""), &layout::position::PluginDefinition),
830    (Cow::Borrowed("inset"), &layout::placement::PluginInsetDefinition),
831    (Cow::Borrowed("inset-x"), &layout::placement::PluginInsetXDefinition),
832    (Cow::Borrowed("inset-y"), &layout::placement::PluginInsetYDefinition),
833    (Cow::Borrowed("start"), &layout::placement::PluginStartDefinition),
834    (Cow::Borrowed("end"), &layout::placement::PluginEndDefinition),
835    (Cow::Borrowed("top"), &layout::placement::PluginTopDefinition),
836    (Cow::Borrowed("right"), &layout::placement::PluginRightDefinition),
837    (Cow::Borrowed("bottom"), &layout::placement::PluginBottomDefinition),
838    (Cow::Borrowed("left"), &layout::placement::PluginLeftDefinition),
839    (Cow::Borrowed(""), &layout::isolation::PluginDefinition),
840    (Cow::Borrowed("z"), &layout::z_index::PluginDefinition),
841    (Cow::Borrowed("order"), &flexbox::order::PluginDefinition),
842    (Cow::Borrowed("col"), &grid::grid_column::PluginDefinition),
843    (Cow::Borrowed("row"), &grid::grid_row::PluginDefinition),
844    (Cow::Borrowed("float"), &layout::floats::PluginDefinition),
845    (Cow::Borrowed("clear"), &layout::clear::PluginDefinition),
846    (Cow::Borrowed("m"), &spacing::margin::PluginDefinition),
847    (Cow::Borrowed("mx"), &spacing::margin::PluginXDefinition),
848    (Cow::Borrowed("my"), &spacing::margin::PluginYDefinition),
849    (Cow::Borrowed("ms"), &spacing::margin::PluginStartDefinition),
850    (Cow::Borrowed("me"), &spacing::margin::PluginEndDefinition),
851    (Cow::Borrowed("mt"), &spacing::margin::PluginTopDefinition),
852    (Cow::Borrowed("mr"), &spacing::margin::PluginRightDefinition),
853    (Cow::Borrowed("mb"), &spacing::margin::PluginBottomDefinition),
854    (Cow::Borrowed("ml"), &spacing::margin::PluginLeftDefinition),
855    (Cow::Borrowed("box"), &layout::box_sizing::PluginDefinition),
856    (Cow::Borrowed(""), &layout::display::PluginDefinition),
857    (Cow::Borrowed("aspect"), &layout::aspect_ratio::PluginDefinition),
858    (Cow::Borrowed("h"), &sizing::height::PluginDefinition),
859    (Cow::Borrowed("max-h"), &sizing::max_height::PluginDefinition),
860    (Cow::Borrowed("min-h"), &sizing::min_height::PluginDefinition),
861    (Cow::Borrowed("w"), &sizing::width::PluginDefinition),
862    (Cow::Borrowed("min-w"), &sizing::min_width::PluginDefinition),
863    (Cow::Borrowed("max-w"), &sizing::max_width::PluginDefinition),
864    (Cow::Borrowed("flex"), &flexbox::flex::PluginDefinition),
865    (Cow::Borrowed("shrink"), &flexbox::flex_shrink::PluginDefinition),
866    (Cow::Borrowed("grow"), &flexbox::flex_grow::PluginDefinition),
867    (Cow::Borrowed("basis"), &flexbox::flex_basis::PluginDefinition),
868    (Cow::Borrowed("table"), &table::table_layout::PluginDefinition),
869    (Cow::Borrowed("caption"), &table::caption_side::PluginDefinition),
870    (Cow::Borrowed("border"), &table::border_collapse::PluginDefinition),
871    (Cow::Borrowed("border-spacing"), &table::border_spacing::PluginDefinition),
872    (Cow::Borrowed("border-spacing-x"), &table::border_spacing::PluginXDefinition),
873    (Cow::Borrowed("border-spacing-y"), &table::border_spacing::PluginYDefinition),
874    (Cow::Borrowed("origin"), &transform::transform_origin::PluginDefinition),
875    (Cow::Borrowed("perspective-origin"), &transform::perspective_origin::PluginDefinition),
876    (Cow::Borrowed("perspective"), &transform::perspective::PluginDefinition),
877    (Cow::Borrowed("translate-x"), &transform::translate::PluginXDefinition),
878    (Cow::Borrowed("translate-y"), &transform::translate::PluginYDefinition),
879    (Cow::Borrowed("translate-z"), &transform::translate::PluginZDefinition),
880    (Cow::Borrowed("rotate"), &transform::rotate::PluginDefinition),
881    (Cow::Borrowed("rotate-x"), &transform::rotate::PluginXDefinition),
882    (Cow::Borrowed("rotate-y"), &transform::rotate::PluginYDefinition),
883    (Cow::Borrowed("rotate-z"), &transform::rotate::PluginZDefinition),
884    (Cow::Borrowed("skew-x"), &transform::skew::PluginXDefinition),
885    (Cow::Borrowed("skew-y"), &transform::skew::PluginYDefinition),
886    (Cow::Borrowed("scale"), &transform::scale::PluginDefinition),
887    (Cow::Borrowed("scale-x"), &transform::scale::PluginXDefinition),
888    (Cow::Borrowed("scale-y"), &transform::scale::PluginYDefinition),
889    (Cow::Borrowed("scale-z"), &transform::scale::PluginZDefinition),
890    (Cow::Borrowed("transform"), &transform::transform_type::PluginDefinition),
891    (Cow::Borrowed("animate"), &transition::animation::PluginDefinition),
892    (Cow::Borrowed("cursor"), &interactivity::cursor::PluginDefinition),
893    (Cow::Borrowed("touch"), &interactivity::touch_action::PluginDefinition),
894    (Cow::Borrowed("select"), &interactivity::user_select::PluginDefinition),
895    (Cow::Borrowed("resize"), &interactivity::resize::PluginDefinition),
896    (Cow::Borrowed("snap"), &interactivity::scroll_snap_type::PluginDefinition),
897    (Cow::Borrowed("snap"), &interactivity::scroll_snap_align::PluginDefinition),
898    (Cow::Borrowed("snap"), &interactivity::scroll_snap_stop::PluginDefinition),
899    (Cow::Borrowed("scroll-m"), &interactivity::scroll_margin::PluginDefinition),
900    (Cow::Borrowed("scroll-mx"), &interactivity::scroll_margin::PluginXDefinition),
901    (Cow::Borrowed("scroll-my"), &interactivity::scroll_margin::PluginYDefinition),
902    (Cow::Borrowed("scroll-ms"), &interactivity::scroll_margin::PluginStartDefinition),
903    (Cow::Borrowed("scroll-me"), &interactivity::scroll_margin::PluginEndDefinition),
904    (Cow::Borrowed("scroll-mt"), &interactivity::scroll_margin::PluginTopDefinition),
905    (Cow::Borrowed("scroll-mr"), &interactivity::scroll_margin::PluginRightDefinition),
906    (Cow::Borrowed("scroll-mb"), &interactivity::scroll_margin::PluginBottomDefinition),
907    (Cow::Borrowed("scroll-ml"), &interactivity::scroll_margin::PluginLeftDefinition),
908    (Cow::Borrowed("scroll-p"), &interactivity::scroll_padding::PluginDefinition),
909    (Cow::Borrowed("scroll-px"), &interactivity::scroll_padding::PluginXDefinition),
910    (Cow::Borrowed("scroll-py"), &interactivity::scroll_padding::PluginYDefinition),
911    (Cow::Borrowed("scroll-ps"), &interactivity::scroll_padding::PluginStartDefinition),
912    (Cow::Borrowed("scroll-pe"), &interactivity::scroll_padding::PluginEndDefinition),
913    (Cow::Borrowed("scroll-pt"), &interactivity::scroll_padding::PluginTopDefinition),
914    (Cow::Borrowed("scroll-pr"), &interactivity::scroll_padding::PluginRightDefinition),
915    (Cow::Borrowed("scroll-pb"), &interactivity::scroll_padding::PluginBottomDefinition),
916    (Cow::Borrowed("scroll-pl"), &interactivity::scroll_padding::PluginLeftDefinition),
917    (Cow::Borrowed("list"), &typography::list_style_position::PluginDefinition),
918    (Cow::Borrowed("list"), &typography::list_style_type::PluginDefinition),
919    (Cow::Borrowed("appearance"), &interactivity::appearance::PluginDefinition),
920    (Cow::Borrowed("columns"), &layout::columns::PluginDefinition),
921    (Cow::Borrowed("break-before"), &layout::break_before::PluginDefinition),
922    (Cow::Borrowed("break-inside"), &layout::break_inside::PluginDefinition),
923    (Cow::Borrowed("break-after"), &layout::break_after::PluginDefinition),
924    (Cow::Borrowed("auto-cols"), &grid::grid_auto_columns::PluginDefinition),
925    (Cow::Borrowed("grid-flow"), &grid::grid_auto_flow::PluginDefinition),
926    (Cow::Borrowed("auto-rows"), &grid::grid_auto_rows::PluginDefinition),
927    (Cow::Borrowed("grid-cols"), &grid::grid_template_columns::PluginDefinition),
928    (Cow::Borrowed("grid-rows"), &grid::grid_template_rows::PluginDefinition),
929    (Cow::Borrowed("flex"), &flexbox::flex_direction::PluginDefinition),
930    (Cow::Borrowed("flex"), &flexbox::flex_wrap::PluginDefinition),
931    (Cow::Borrowed("place-content"), &flexbox::place_content::PluginDefinition),
932    (Cow::Borrowed("place-items"), &flexbox::place_items::PluginDefinition),
933    (Cow::Borrowed("content"), &flexbox::align_content::PluginDefinition),
934    (Cow::Borrowed("items"), &flexbox::align_items::PluginDefinition),
935    (Cow::Borrowed("justify"), &flexbox::justify_content::PluginDefinition),
936    (Cow::Borrowed("justify-items"), &flexbox::justify_items::PluginDefinition),
937    (Cow::Borrowed("gap"), &grid::gap::PluginDefinition),
938    (Cow::Borrowed("gap-x"), &grid::gap::PluginXDefinition),
939    (Cow::Borrowed("gap-y"), &grid::gap::PluginYDefinition),
940    (Cow::Borrowed("space-x"), &spacing::space_between::PluginXDefinition),
941    (Cow::Borrowed("space-y"), &spacing::space_between::PluginYDefinition),
942    (Cow::Borrowed("divide-x"), &border::divide_width::PluginXDefinition),
943    (Cow::Borrowed("divide-y"), &border::divide_width::PluginYDefinition),
944    (Cow::Borrowed("divide"), &border::divide_style::PluginDefinition),
945    (Cow::Borrowed("divide"), &border::divide_color::PluginDefinition),
946    (Cow::Borrowed("place-self"), &flexbox::place_self::PluginDefinition),
947    (Cow::Borrowed("self"), &flexbox::align_self::PluginDefinition),
948    (Cow::Borrowed("justify-self"), &flexbox::justify_self::PluginDefinition),
949    (Cow::Borrowed("overflow"), &layout::overflow::PluginDefinition),
950    (Cow::Borrowed("overscroll"), &layout::overscroll_behavior::PluginDefinition),
951    (Cow::Borrowed("scroll"), &interactivity::scroll_behavior::PluginDefinition),
952    (Cow::Borrowed(""), &typography::text_overflow::PluginDefinition),
953    (Cow::Borrowed("whitespace"), &typography::whitespace::PluginDefinition),
954    (Cow::Borrowed("text"), &typography::text_wrap::PluginDefinition),
955    (Cow::Borrowed("break"), &typography::word_break::PluginDefinition),
956    (Cow::Borrowed("rounded"), &border::border_radius::PluginDefinition),
957    (Cow::Borrowed("rounded-s"), &border::border_radius::PluginStartDefinition),
958    (Cow::Borrowed("rounded-e"), &border::border_radius::PluginEndDefinition),
959    (Cow::Borrowed("rounded-t"), &border::border_radius::PluginTopDefinition),
960    (Cow::Borrowed("rounded-r"), &border::border_radius::PluginRightDefinition),
961    (Cow::Borrowed("rounded-b"), &border::border_radius::PluginBottomDefinition),
962    (Cow::Borrowed("rounded-l"), &border::border_radius::PluginLeftDefinition),
963    (Cow::Borrowed("rounded-ss"), &border::border_radius::PluginStartStartDefinition),
964    (Cow::Borrowed("rounded-se"), &border::border_radius::PluginStartEndDefinition),
965    (Cow::Borrowed("rounded-ee"), &border::border_radius::PluginEndEndDefinition),
966    (Cow::Borrowed("rounded-es"), &border::border_radius::PluginEndStartDefinition),
967    (Cow::Borrowed("rounded-tr"), &border::border_radius::PluginTopRightDefinition),
968    (Cow::Borrowed("rounded-tl"), &border::border_radius::PluginTopLeftDefinition),
969    (Cow::Borrowed("rounded-br"), &border::border_radius::PluginBottomRightDefinition),
970    (Cow::Borrowed("rounded-bl"), &border::border_radius::PluginBottomLeftDefinition),
971    (Cow::Borrowed("border"), &border::border_width::PluginDefinition),
972    (Cow::Borrowed("border-x"), &border::border_width::PluginXDefinition),
973    (Cow::Borrowed("border-y"), &border::border_width::PluginYDefinition),
974    (Cow::Borrowed("border-s"), &border::border_width::PluginStartDefinition),
975    (Cow::Borrowed("border-e"), &border::border_width::PluginEndDefinition),
976    (Cow::Borrowed("border-t"), &border::border_width::PluginTopDefinition),
977    (Cow::Borrowed("border-r"), &border::border_width::PluginRightDefinition),
978    (Cow::Borrowed("border-b"), &border::border_width::PluginBottomDefinition),
979    (Cow::Borrowed("border-l"), &border::border_width::PluginLeftDefinition),
980    (Cow::Borrowed("border"), &border::border_style::PluginDefinition),
981    (Cow::Borrowed("border"), &border::border_color::PluginDefinition),
982    (Cow::Borrowed("border-x"), &border::border_color::PluginXDefinition),
983    (Cow::Borrowed("border-y"), &border::border_color::PluginYDefinition),
984    (Cow::Borrowed("border-s"), &border::border_color::PluginStartDefinition),
985    (Cow::Borrowed("border-e"), &border::border_color::PluginEndDefinition),
986    (Cow::Borrowed("border-t"), &border::border_color::PluginTopDefinition),
987    (Cow::Borrowed("border-r"), &border::border_color::PluginRightDefinition),
988    (Cow::Borrowed("border-b"), &border::border_color::PluginBottomDefinition),
989    (Cow::Borrowed("border-l"), &border::border_color::PluginLeftDefinition),
990    (Cow::Borrowed("bg"), &background::background_color::PluginDefinition),
991    (Cow::Borrowed("bg"), &background::background_image::PluginDefinition),
992    (Cow::Borrowed("bg-linear"), &background::background_image::PluginLinearDefinition),
993    (Cow::Borrowed("bg-radial"), &background::background_image::PluginRadialDefinition),
994    (Cow::Borrowed("bg-conic"), &background::background_image::PluginConicDefinition),
995    (Cow::Borrowed("from"), &background::gradient_color_stops::PluginFromDefinition),
996    (Cow::Borrowed("via"), &background::gradient_color_stops::PluginViaDefinition),
997    (Cow::Borrowed("to"), &background::gradient_color_stops::PluginToDefinition),
998    (Cow::Borrowed("box-decoration"), &layout::box_decoration_break::PluginDefinition),
999    (Cow::Borrowed("bg"), &background::background_size::PluginDefinition),
1000    (Cow::Borrowed("bg"), &background::background_attachment::PluginDefinition),
1001    (Cow::Borrowed("bg-clip"), &background::background_clip::PluginDefinition),
1002    (Cow::Borrowed("bg"), &background::background_position::PluginDefinition),
1003    (Cow::Borrowed("bg"), &background::background_repeat::PluginDefinition),
1004    (Cow::Borrowed("bg-origin"), &background::background_origin::PluginDefinition),
1005    (Cow::Borrowed("fill"), &svg::fill::PluginDefinition),
1006    (Cow::Borrowed("stroke"), &svg::stroke::PluginDefinition),
1007    (Cow::Borrowed("stroke"), &svg::stroke_width::PluginDefinition),
1008    (Cow::Borrowed("object"), &layout::object_fit::PluginDefinition),
1009    (Cow::Borrowed("object"), &layout::object_position::PluginDefinition),
1010    (Cow::Borrowed("p"), &spacing::padding::PluginDefinition),
1011    (Cow::Borrowed("px"), &spacing::padding::PluginXDefinition),
1012    (Cow::Borrowed("py"), &spacing::padding::PluginYDefinition),
1013    (Cow::Borrowed("ps"), &spacing::padding::PluginStartDefinition),
1014    (Cow::Borrowed("pe"), &spacing::padding::PluginEndDefinition),
1015    (Cow::Borrowed("pt"), &spacing::padding::PluginTopDefinition),
1016    (Cow::Borrowed("pr"), &spacing::padding::PluginRightDefinition),
1017    (Cow::Borrowed("pb"), &spacing::padding::PluginBottomDefinition),
1018    (Cow::Borrowed("pl"), &spacing::padding::PluginLeftDefinition),
1019    (Cow::Borrowed("text"), &typography::text_align::PluginDefinition),
1020    (Cow::Borrowed("indent"), &typography::text_indent::PluginDefinition),
1021    (Cow::Borrowed("align"), &typography::vertical_align::PluginDefinition),
1022    (Cow::Borrowed("font"), &typography::font_family::PluginDefinition),
1023    (Cow::Borrowed("text"), &typography::font_size::PluginDefinition),
1024    (Cow::Borrowed("font"), &typography::font_weight::PluginDefinition),
1025    (Cow::Borrowed(""), &typography::text_transform::PluginDefinition),
1026    (Cow::Borrowed(""), &typography::font_style::PluginDefinition),
1027    (Cow::Borrowed(""), &typography::font_variant_numeric::PluginDefinition),
1028    (Cow::Borrowed("tracking"), &typography::letter_spacing::PluginDefinition),
1029    (Cow::Borrowed("leading"), &typography::line_height::PluginDefinition),
1030    (Cow::Borrowed("text"), &typography::text_color::PluginDefinition),
1031    (Cow::Borrowed(""), &typography::text_decoration::PluginDefinition),
1032    (Cow::Borrowed("decoration"), &typography::text_decoration_color::PluginDefinition),
1033    (Cow::Borrowed("decoration"), &typography::text_decoration_style::PluginDefinition),
1034    (Cow::Borrowed("decoration"), &typography::text_decoration_thickness::PluginDefinition),
1035    (Cow::Borrowed("underline-offset"), &typography::text_underline_offset::PluginDefinition),
1036    (Cow::Borrowed(""), &typography::font_smoothing::PluginDefinition),
1037    (Cow::Borrowed("caret"), &interactivity::caret_color::PluginDefinition),
1038    (Cow::Borrowed("accent"), &interactivity::accent_color::PluginDefinition),
1039    (Cow::Borrowed("opacity"), &effect::opacity::PluginDefinition),
1040    (Cow::Borrowed("bg-blend"), &effect::background_blend_mode::PluginDefinition),
1041    (Cow::Borrowed("mix-blend"), &effect::mix_blend_mode::PluginDefinition),
1042    (Cow::Borrowed("text-shadow"), &effect::text_shadow::PluginDefinition),
1043    (Cow::Borrowed("text-shadow"), &effect::text_shadow_color::PluginDefinition),
1044    (Cow::Borrowed("shadow"), &effect::box_shadow::PluginDefinition),
1045    (Cow::Borrowed("shadow"), &effect::box_shadow_color::PluginDefinition),
1046    (Cow::Borrowed("inset-shadow"), &effect::box_shadow::PluginInsetDefinition),
1047    (Cow::Borrowed("inset-shadow"), &effect::box_shadow_color::PluginInsetDefinition),
1048    (Cow::Borrowed("outline"), &border::outline_style::PluginDefinition),
1049    (Cow::Borrowed("outline"), &border::outline_width::PluginDefinition),
1050    (Cow::Borrowed("outline-offset"), &border::outline_offset::PluginDefinition),
1051    (Cow::Borrowed("outline"), &border::outline_color::PluginDefinition),
1052    (Cow::Borrowed("ring"), &border::ring_width::PluginDefinition),
1053    (Cow::Borrowed("ring"), &border::ring_color::PluginDefinition),
1054    (Cow::Borrowed("inset-ring"), &border::ring_width::PluginInsetDefinition),
1055    (Cow::Borrowed("inset-ring"), &border::ring_color::PluginInsetDefinition),
1056    (Cow::Borrowed("ring-offset"), &border::ring_offset_width::PluginDefinition),
1057    (Cow::Borrowed("ring-offset"), &border::ring_offset_color::PluginDefinition),
1058    (Cow::Borrowed("blur"), &filter::blur::PluginDefinition),
1059    (Cow::Borrowed("brightness"), &filter::brightness::PluginDefinition),
1060    (Cow::Borrowed("contrast"), &filter::contrast::PluginDefinition),
1061    (Cow::Borrowed("drop-shadow"), &filter::drop_shadow::PluginDefinition),
1062    (Cow::Borrowed("grayscale"), &filter::grayscale::PluginDefinition),
1063    (Cow::Borrowed("hue-rotate"), &filter::hue_rotate::PluginDefinition),
1064    (Cow::Borrowed("invert"), &filter::invert::PluginDefinition),
1065    (Cow::Borrowed("saturate"), &filter::saturate::PluginDefinition),
1066    (Cow::Borrowed("sepia"), &filter::sepia::PluginDefinition),
1067    (Cow::Borrowed("filter"), &filter::filter_type::PluginDefinition),
1068    (Cow::Borrowed("backdrop-blur"), &filter::backdrop_blur::PluginDefinition),
1069    (Cow::Borrowed("backdrop-brightness"), &filter::backdrop_brightness::PluginDefinition),
1070    (Cow::Borrowed("backdrop-contrast"), &filter::backdrop_contrast::PluginDefinition),
1071    (Cow::Borrowed("backdrop-grayscale"), &filter::backdrop_grayscale::PluginDefinition),
1072    (Cow::Borrowed("backdrop-hue-rotate"), &filter::backdrop_hue_rotate::PluginDefinition),
1073    (Cow::Borrowed("backdrop-invert"), &filter::backdrop_invert::PluginDefinition),
1074    (Cow::Borrowed("backdrop-saturate"), &filter::backdrop_saturate::PluginDefinition),
1075    (Cow::Borrowed("backdrop-sepia"), &filter::backdrop_sepia::PluginDefinition),
1076    (Cow::Borrowed("backdrop-filter"), &filter::backdrop_filter::PluginDefinition),
1077    (Cow::Borrowed("transition"), &transition::transition_property::PluginDefinition),
1078    (Cow::Borrowed("delay"), &transition::transition_delay::PluginDefinition),
1079    (Cow::Borrowed("duration"), &transition::transition_duration::PluginDefinition),
1080    (Cow::Borrowed("ease"), &transition::transition_timing_function::PluginDefinition),
1081    (Cow::Borrowed("will-change"), &interactivity::will_change::PluginDefinition),
1082    (Cow::Borrowed("content"), &typography::content::PluginDefinition),
1083    (Cow::Borrowed("line-clamp"), &typography::line_clamp::PluginDefinition),
1084    (Cow::Borrowed("@container"), &layout::at_container::PluginDefinition),
1085];
1086
1087/// Configuration for the [`Theme::dark_mode`] field.
1088///
1089/// It defines how the `dark:` variant should behave.
1090///
1091/// The default value is [`DarkMode::Media`] which enables the automatic detection of the theme based
1092/// on  user preference.
1093#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
1094#[serde(rename_all = "lowercase")]
1095pub enum DarkMode {
1096    /// The `dark:` variant will modify the class of the selector. You'll then need to toggle this
1097    /// class to enable the dark theme.
1098    ///
1099    /// # Example
1100    ///
1101    /// ```
1102    /// use encre_css::{Config, config::DarkMode};
1103    ///
1104    /// let mut config = Config::default();
1105    /// config.theme.dark_mode = DarkMode::new_class("body.dark");
1106    ///
1107    /// let generated = encre_css::generate(
1108    ///     ["dark:text-white"],
1109    ///     &config,
1110    /// );
1111    ///
1112    /// assert!(generated.ends_with(r#"body.dark .dark\:text-white {
1113    ///   color: #fff;
1114    /// }"#));
1115    /// ```
1116    Class(Cow<'static, str>),
1117
1118    /// The `dark:` variant will generates a `@media (prefers-color-scheme: dark)` rule to enable
1119    /// the dark theme following user preference.
1120    ///
1121    /// This is the default value.
1122    ///
1123    /// # Example
1124    ///
1125    /// ```
1126    /// use encre_css::{Config, config::DarkMode};
1127    ///
1128    /// let mut config = Config::default();
1129    /// config.theme.dark_mode = DarkMode::Media;
1130    ///
1131    /// let generated = encre_css::generate(
1132    ///     ["dark:text-white"],
1133    ///     &config,
1134    /// );
1135    ///
1136    /// assert!(generated.ends_with(r#"@media (prefers-color-scheme: dark) {
1137    ///   .dark\:text-white {
1138    ///     color: #fff;
1139    ///   }
1140    /// }"#));
1141    /// ```
1142    Media,
1143}
1144
1145impl Default for DarkMode {
1146    fn default() -> Self {
1147        Self::Media
1148    }
1149}
1150
1151impl DarkMode {
1152    /// Quickly build a [`DarkMode::Class`] value.
1153    pub fn new_class<T: Into<Cow<'static, str>>>(class: T) -> Self {
1154        Self::Class(class.into())
1155    }
1156}
1157
1158/// Configuration for the [`Theme::aria`] field.
1159///
1160/// It defines a list of custom ARIA states.
1161#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1162pub struct Aria(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1163
1164impl Aria {
1165    /// Add an ARIA state to the list.
1166    #[inline]
1167    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1168        &mut self,
1169        key: T1,
1170        val: T2,
1171    ) {
1172        self.0.insert(key.into(), val.into());
1173    }
1174
1175    /// Remove an ARIA state from the list.
1176    #[inline]
1177    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1178        self.0.remove(&key.into());
1179    }
1180
1181    #[inline]
1182    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1183        self.0.iter()
1184    }
1185
1186    pub(crate) fn len(&self) -> usize {
1187        self.0.len()
1188    }
1189}
1190
1191/// Configuration for the [`Theme::screens`] field.
1192///
1193/// It defines a list of custom screen breakpoints.
1194#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1195pub struct Screens(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1196
1197impl Screens {
1198    /// Add a custom screen breakpoint to the list.
1199    #[inline]
1200    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1201        &mut self,
1202        key: T1,
1203        val: T2,
1204    ) {
1205        self.0.insert(key.into(), val.into());
1206    }
1207
1208    /// Remove a custom screen breakpoint from the list.
1209    #[inline]
1210    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1211        self.0.remove(&key.into());
1212    }
1213
1214    #[inline]
1215    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1216        self.0.iter()
1217    }
1218
1219    pub(crate) fn len(&self) -> usize {
1220        self.0.len()
1221    }
1222}
1223
1224/// Configuration for the [`Theme::containers`] field.
1225///
1226/// It defines a list of custom container size breakpoints.
1227#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1228pub struct Containers(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1229
1230impl Containers {
1231    /// Add a custom container size breakpoint to the list.
1232    #[inline]
1233    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1234        &mut self,
1235        key: T1,
1236        val: T2,
1237    ) {
1238        self.0.insert(key.into(), val.into());
1239    }
1240
1241    /// Remove a custom container size breakpoint from the list.
1242    #[inline]
1243    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1244        self.0.remove(&key.into());
1245    }
1246
1247    #[inline]
1248    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1249        self.0.iter()
1250    }
1251
1252    pub(crate) fn len(&self) -> usize {
1253        self.0.len()
1254    }
1255}
1256
1257/// Configuration for the [`Theme::colors`] field.
1258///
1259/// It defines a list of custom colors.
1260#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1261pub struct Colors(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1262
1263impl Colors {
1264    /// Add a custom color to the list.
1265    #[inline]
1266    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1267        &mut self,
1268        key: T1,
1269        val: T2,
1270    ) {
1271        self.0.insert(key.into(), val.into());
1272    }
1273
1274    /// Remove a custom color from the list.
1275    #[inline]
1276    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1277        self.0.remove(&key.into());
1278    }
1279
1280    #[inline]
1281    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
1282        self.0.get(&key.into())
1283    }
1284
1285    #[inline]
1286    pub(crate) fn contains<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> bool {
1287        self.0.contains_key(&key.into())
1288    }
1289}
1290
1291/// Configuration for the [`Config::shortcuts`] field.
1292///
1293/// It defines a list of shortcuts used to combine several utility classes into one.
1294///
1295/// # Example
1296///
1297/// ```
1298/// use encre_css::Config;
1299///
1300/// let mut config = Config::default();
1301/// config.shortcuts.add("btn", "border-1 rounded-xl bg-red-500");
1302///
1303/// let generated = encre_css::generate(
1304///     [r#"<button class="btn">Click me</button>"#],
1305///     &config,
1306/// );
1307///
1308/// assert!(generated.ends_with(r#".btn {
1309///   border-radius: 0.75rem;
1310/// }
1311///
1312/// .btn {
1313///   border-width: 1px;
1314/// }
1315///
1316/// .btn {
1317///   background-color: oklch(63.7% .237 25.331);
1318/// }"#));
1319/// ```
1320///
1321/// ### Corresponding TOML configuration
1322///
1323/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[shortcuts]</span>
1324/// btn = <span class="string">"border-1 rounded-xl bg-red-500"</span></code></pre></div>
1325#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1326pub struct Shortcuts(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1327
1328impl Shortcuts {
1329    /// Add a shortcut to the list.
1330    #[inline]
1331    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1332        &mut self,
1333        key: T1,
1334        val: T2,
1335    ) {
1336        self.0.insert(key.into(), val.into());
1337    }
1338
1339    /// Remove a shortcut from the list.
1340    #[inline]
1341    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1342        self.0.remove(&key.into());
1343    }
1344
1345    #[inline]
1346    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
1347        self.0.get(&key.into())
1348    }
1349}
1350
1351/// The maximum depth at which shortcuts will be resolved.
1352///
1353/// 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.
1354///
1355/// By default it is set to `5`.
1356#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
1357pub struct MaxShortcutDepth(usize);
1358
1359impl MaxShortcutDepth {
1360    /// Create a new `MaxShortcutDepth`.
1361    pub fn new(v: usize) -> Self {
1362        Self(v)
1363    }
1364
1365    /// Get the inner depth as an `usize`.
1366    pub fn get(&self) -> usize {
1367        self.0
1368    }
1369}
1370
1371impl From<usize> for MaxShortcutDepth {
1372    fn from(v: usize) -> Self {
1373        Self(v)
1374    }
1375}
1376
1377impl From<MaxShortcutDepth> for usize {
1378    fn from(val: MaxShortcutDepth) -> Self {
1379        val.0
1380    }
1381}
1382
1383impl Default for MaxShortcutDepth {
1384    fn default() -> Self {
1385        Self(5)
1386    }
1387}
1388
1389/// Configuration for the [`Config::safelist`] field.
1390///
1391/// It defines a list of selectors that are manually forced to be present in the generated CSS.
1392/// It should be used when you dynamically create selectors (for example, in Javascript
1393/// `text-${ active ? "blue" : "gray" }-400`, in this case, `text-blue-400` and `text-gray-400`
1394/// should be added to the safelist).
1395///
1396/// # Example
1397///
1398/// ```
1399/// use encre_css::Config;
1400///
1401/// let mut config = Config::default();
1402/// config.safelist.add("text-blue-400");
1403/// config.safelist.add("text-gray-400");
1404///
1405/// let generated = encre_css::generate(
1406///     [r#"<button class="bg-red-500">Click me</button>"#],
1407///     &config,
1408/// );
1409///
1410/// assert!(generated.ends_with(r#".bg-red-500 {
1411///   background-color: oklch(63.7% .237 25.331);
1412/// }
1413///
1414/// .text-blue-400 {
1415///   color: oklch(70.7% .165 254.624);
1416/// }
1417///
1418/// .text-gray-400 {
1419///   color: oklch(70.7% .022 261.325);
1420/// }"#));
1421/// ```
1422///
1423/// ### Corresponding TOML configuration
1424///
1425/// <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>
1426#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1427pub struct Safelist(BTreeSet<Cow<'static, str>>);
1428
1429impl Safelist {
1430    /// Add a selector to the safelist.
1431    #[inline]
1432    pub fn add<T: Into<Cow<'static, str>>>(&mut self, val: T) {
1433        self.0.insert(val.into());
1434    }
1435
1436    /// Remove a selector from the safelist.
1437    #[inline]
1438    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, val: T) {
1439        self.0.remove(&val.into());
1440    }
1441
1442    #[inline]
1443    pub(crate) fn iter(&self) -> impl Iterator<Item = &Cow<'static, str>> {
1444        self.0.iter()
1445    }
1446}
1447
1448/// Configuration for the [`Config::extra`] field.
1449///
1450/// It defines some extra fields that can be used to store arbitrary values usable in plugins.
1451/// The fields are represented as [`toml::Value`] to allow all types to be serialized.
1452/// It is recommended to use a table by plugin (e.g. the `encre-css-icons`'s plugin uses the
1453/// `icons` key containing a table grouping all configuration fields).
1454#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
1455pub struct Extra(BTreeMap<Cow<'static, str>, toml::Value>);
1456
1457impl Extra {
1458    /// Add an extra field.
1459    #[inline]
1460    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<toml::Value>>(&mut self, key: T1, val: T2) {
1461        self.0.insert(key.into(), val.into());
1462    }
1463
1464    /// Remove an extra field.
1465    #[inline]
1466    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1467        self.0.remove(&key.into());
1468    }
1469
1470    /// Get the value of an extra field.
1471    #[inline]
1472    pub fn get<'a, T: Into<Cow<'a, str>>>(&'a self, key: T) -> Option<&'a toml::Value> {
1473        self.0.get(&key.into())
1474    }
1475}
1476
1477/// Configuration for the [`Config::theme`] field.
1478///
1479/// It defines some design system specific values like custom colors or screen breakpoints.
1480///
1481/// # Example
1482///
1483/// ```
1484/// use encre_css::{Config, config::DarkMode};
1485///
1486/// let mut config = Config::default();
1487/// config.theme.dark_mode = DarkMode::new_class("body.dark");
1488/// config.theme.colors.add("primary", "#d3198c");
1489/// config.theme.screens.add("tablet", "640px");
1490/// config.theme.containers.add("medium", "640px");
1491/// config.theme.aria.add("current", r#"current="page""#);
1492///
1493/// let generated = encre_css::generate(
1494///     [r#"<div class="bg-primary tablet:block aria-current:text-primary"><button class="bg-white @medium:flex">Click me</button>"#],
1495///     &config,
1496/// );
1497///
1498/// assert!(generated.ends_with(r#"
1499/// .bg-primary {
1500///   background-color: #d3198c;
1501/// }
1502///
1503/// .bg-white {
1504///   background-color: #fff;
1505/// }
1506///
1507/// @media (width >= 640px) {
1508///   .tablet\:block {
1509///     display: block;
1510///   }
1511/// }
1512///
1513/// @container (width >= 640px) {
1514///   .\@medium\:flex {
1515///     display: flex;
1516///   }
1517/// }
1518///
1519/// .aria-current\:text-primary[aria-current="page"] {
1520///   color: #d3198c;
1521/// }"#));
1522/// ```
1523///
1524/// ### Corresponding TOML configuration
1525///
1526/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[theme]</span>
1527/// dark_mode = { class = <span class="string">"body.dark"</span> }<br>
1528/// <span class="kw">[theme.colors]</span>
1529/// primary = <span class="string">"#d3198c"</span><br>
1530/// <span class="kw">[theme.screens]</span>
1531/// tablet = <span class="string">"640px"</span><br>
1532/// <span class="kw">[theme.containers]</span>
1533/// medium = <span class="string">"640px"</span><br>
1534/// <span class="kw">[theme.aria]</span>
1535/// current = <span class="string">'current="page"'</span></code></pre></div>
1536#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1537pub struct Theme {
1538    /// Dark mode configuration.
1539    ///
1540    /// The default value is [`DarkMode::Media`].
1541    #[serde(default)]
1542    pub dark_mode: DarkMode,
1543
1544    /// Custom screen breakpoints configuration.
1545    ///
1546    /// The default value is an empty map.
1547    #[serde(default)]
1548    pub screens: Screens,
1549
1550    /// Custom container size breakpoints configuration.
1551    ///
1552    /// The default value is an empty map.
1553    #[serde(default)]
1554    pub containers: Containers,
1555
1556    /// Custom colors configuration.
1557    ///
1558    /// The default value is an empty map.
1559    #[serde(default)]
1560    pub colors: Colors,
1561
1562    /// Custom ARIA states.
1563    ///
1564    /// The default value is an empty map.
1565    #[serde(default)]
1566    pub aria: Aria,
1567}
1568
1569/// The configuration of the CSS generation done in the [`generate`] function.
1570///
1571/// You can create a configuration using one of the ways listed below:
1572///
1573/// - It can be the default one:
1574///
1575/// ```
1576/// use encre_css::Config;
1577///
1578/// let config = Config::default();
1579/// let _generated = encre_css::generate([], &config);
1580/// ```
1581///
1582/// - It can be a customized one:
1583///
1584/// ```
1585/// use encre_css::Config;
1586///
1587/// let mut config = Config::default();
1588/// config.theme.colors.add("flashy", "#ff2d20");
1589///
1590/// let _generated = encre_css::generate([], &config);
1591/// ```
1592///
1593/// - It can be loaded from a [TOML](https://toml.io) file:
1594///
1595/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment"># encre-css.toml</span>
1596/// <span class="kw">[theme]</span>
1597/// dark_mode = { class = <span class="string">".dark"</span> }
1598/// screens = { 3xl = <span class="string">"1600px"</span>, lg = <span class="string">"2000px"</span> }<br>
1599/// <span class="kw">[theme.colors]</span>
1600/// primary = <span class="string">"#e5186a"</span>
1601/// yellow-400 = <span class="string">"#ffef0e"</span></code></pre></div>
1602///
1603/// ```no_run
1604/// use encre_css::Config;
1605///
1606/// # fn main() -> encre_css::Result<()> {
1607/// let config = Config::from_file("encre-css.toml")?;
1608/// let _generated = encre_css::generate([], &config);
1609/// # Ok(())
1610/// # }
1611/// ```
1612///
1613/// Based on [Tailwind v3's configuration](https://v3.tailwindcss.com/docs/configuration).
1614///
1615/// [`generate`]: crate::generate
1616#[derive(Default, Serialize, Deserialize, Clone)]
1617pub struct Config {
1618    /// Safelist configuration.
1619    #[serde(default)]
1620    pub safelist: Safelist,
1621
1622    /// Theme configuration.
1623    #[serde(default)]
1624    pub theme: Theme,
1625
1626    /// Preflight configuration.
1627    #[serde(default)]
1628    pub preflight: Preflight,
1629
1630    /// Shortcuts configuration.
1631    #[serde(default)]
1632    pub shortcuts: Shortcuts,
1633
1634    /// The maximum depth at which shortcuts will be resolved.
1635    #[serde(default)]
1636    pub max_shortcut_depth: MaxShortcutDepth,
1637
1638    /// Extra fields configuration.
1639    #[serde(default)]
1640    pub extra: Extra,
1641
1642    /// A custom scanner used to scan content.
1643    ///
1644    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1645    #[serde(skip)]
1646    pub scanner: Scanner,
1647
1648    /// A list of custom plugins.
1649    ///
1650    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1651    #[serde(skip)]
1652    pub(crate) custom_plugins: Vec<(Cow<'static, str>, &'static (dyn Plugin + Send + Sync))>,
1653
1654    /// A list of custom variants.
1655    ///
1656    /// Check [`BUILTIN_VARIANTS`] to choose the order of the custom variants you define.
1657    ///
1658    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1659    #[serde(skip)]
1660    pub(crate) custom_variants: Vec<(Cow<'static, str>, Variant<'static>)>,
1661    // TODO: Prefix (en-)
1662}
1663
1664impl Config {
1665    /// Get variants derived from other configuration fields like breakpoints and the dark mode.
1666    #[allow(clippy::too_many_lines)]
1667    pub(crate) fn get_derived_variants(&self) -> Vec<(Cow<'static, str>, Variant<'static>)> {
1668        self.theme
1669            .screens
1670            .iter()
1671            .map(|screen| {
1672                (
1673                    screen.0.clone(),
1674                    Variant {
1675                        order: BUILTIN_VARIANTS.len(),
1676                        prefixed: false,
1677                        template: Cow::Owned(format!("@media (width >= {})", screen.1)),
1678                    },
1679                )
1680            })
1681            .chain(BUILTIN_SCREENS.iter().map(|screen| {
1682                (
1683                    Cow::from(screen.0),
1684                    Variant {
1685                        order: BUILTIN_VARIANTS.len() + self.theme.screens.0.len(),
1686                        prefixed: false,
1687                        template: Cow::Owned(format!("@media (width >= {})", screen.1)),
1688                    },
1689                )
1690            }))
1691            .chain(self.theme.screens.iter().map(|screen| {
1692                (
1693                    Cow::from(format!("max-{}", screen.0)),
1694                    Variant {
1695                        order: BUILTIN_VARIANTS.len()
1696                            + self.theme.screens.len()
1697                            + BUILTIN_SCREENS.len(),
1698                        prefixed: false,
1699                        template: Cow::Owned(format!("@media (width < {})", screen.1)),
1700                    },
1701                )
1702            }))
1703            .chain(BUILTIN_SCREENS.iter().map(|screen| {
1704                (
1705                    Cow::from(format!("max-{}", screen.0)),
1706                    Variant {
1707                        order: BUILTIN_VARIANTS.len()
1708                            + 2 * self.theme.screens.len()
1709                            + BUILTIN_SCREENS.len(),
1710                        prefixed: false,
1711                        template: Cow::Owned(format!("@media (width < {})", screen.1)),
1712                    },
1713                )
1714            }))
1715            .chain(self.theme.containers.iter().map(|container| {
1716                (
1717                    Cow::from(format!("@{}", container.0)),
1718                    Variant {
1719                        order: BUILTIN_VARIANTS.len()
1720                            + 2 * self.theme.screens.len()
1721                            + 2 * BUILTIN_SCREENS.len(),
1722                        prefixed: false,
1723                        template: Cow::Owned(format!("@container (width >= {})", container.1)),
1724                    },
1725                )
1726            }))
1727            .chain(BUILTIN_CONTAINERS.iter().map(|container| {
1728                (
1729                    Cow::from(format!("@{}", container.0)),
1730                    Variant {
1731                        order: BUILTIN_VARIANTS.len()
1732                            + 2 * self.theme.screens.len()
1733                            + 2 * BUILTIN_SCREENS.len()
1734                            + self.theme.containers.len(),
1735                        prefixed: false,
1736                        template: Cow::Owned(format!("@container (width >= {})", container.1)),
1737                    },
1738                )
1739            }))
1740            .chain(self.theme.containers.iter().map(|container| {
1741                (
1742                    Cow::from(format!("@max-{}", container.0)),
1743                    Variant {
1744                        order: BUILTIN_VARIANTS.len()
1745                            + 2 * self.theme.screens.len()
1746                            + 2 * BUILTIN_SCREENS.len()
1747                            + self.theme.containers.len()
1748                            + BUILTIN_CONTAINERS.len(),
1749                        prefixed: false,
1750                        template: Cow::Owned(format!("@container (width < {})", container.1)),
1751                    },
1752                )
1753            }))
1754            .chain(BUILTIN_CONTAINERS.iter().map(|container| {
1755                (
1756                    Cow::from(format!("@max-{}", container.0)),
1757                    Variant {
1758                        order: BUILTIN_VARIANTS.len()
1759                            + 2 * self.theme.screens.len()
1760                            + 2 * BUILTIN_SCREENS.len()
1761                            + 2 * self.theme.containers.len()
1762                            + BUILTIN_CONTAINERS.len(),
1763                        prefixed: false,
1764                        template: Cow::Owned(format!("@container (width < {})", container.1)),
1765                    },
1766                )
1767            }))
1768            .chain(self.theme.aria.iter().map(|aria| {
1769                (
1770                    Cow::from(format!("aria-{}", aria.0)),
1771                    Variant {
1772                        order: BUILTIN_VARIANTS.len()
1773                            + 2 * self.theme.screens.len()
1774                            + 2 * BUILTIN_SCREENS.len()
1775                            + 2 * self.theme.containers.len()
1776                            + 2 * BUILTIN_CONTAINERS.len(),
1777                        prefixed: false,
1778                        template: Cow::Owned(format!("&[aria-{}]", aria.1)),
1779                    },
1780                )
1781            }))
1782            .chain(iter::once(match &self.theme.dark_mode {
1783                DarkMode::Media => (
1784                    Cow::from("dark"),
1785                    Variant {
1786                        order: BUILTIN_VARIANTS.len()
1787                            + 2 * self.theme.screens.len()
1788                            + 2 * BUILTIN_SCREENS.len()
1789                            + 2 * self.theme.containers.len()
1790                            + 2 * BUILTIN_CONTAINERS.len()
1791                            + self.theme.aria.0.len(),
1792                        prefixed: false,
1793                        template: Cow::Borrowed("@media (prefers-color-scheme: dark)"),
1794                    },
1795                ),
1796                DarkMode::Class(name) => (
1797                    Cow::from("dark"),
1798                    Variant {
1799                        order: BUILTIN_VARIANTS.len()
1800                            + 2 * self.theme.screens.len()
1801                            + 2 * BUILTIN_SCREENS.len()
1802                            + 2 * self.theme.containers.len()
1803                            + 2 * BUILTIN_CONTAINERS.len()
1804                            + self.theme.aria.len(),
1805                        prefixed: false,
1806                        template: Cow::Owned(format!("{name} &")),
1807                    },
1808                ),
1809            }))
1810            .collect()
1811    }
1812
1813    /// Returns the order of the last variant which can be used when defining a new variant which
1814    /// must be generated after all the other variants.
1815    ///
1816    /// Note that if you are not the maintainer of a crate providing variants, you can ignore this
1817    /// function.
1818    pub fn last_variant_order(&self) -> usize {
1819        BUILTIN_VARIANTS.len()
1820            + 2 * self.theme.screens.len()
1821            + 2 * BUILTIN_SCREENS.len()
1822            + 2 * self.theme.containers.len()
1823            + 2 * BUILTIN_CONTAINERS.len()
1824            + self.theme.aria.len()
1825            + 1
1826            + self.custom_variants.len()
1827    }
1828
1829    /// Register a custom plugin which will be used during CSS generation.
1830    ///
1831    /// Note that if you are not the maintainer of a crate providing plugins, you can ignore this
1832    /// function, see [`crate::plugins`].
1833    ///
1834    /// # Example
1835    ///
1836    /// ```
1837    /// use encre_css::{Config, prelude::build_plugin::*};
1838    ///
1839    /// #[derive(Debug)]
1840    /// struct Prose;
1841    ///
1842    /// impl Plugin for Prose {
1843    ///     fn can_handle(&self, context: ContextCanHandle) -> bool {
1844    ///         matches!(context.modifier, Modifier::Builtin { value: "" | "invert", .. })
1845    ///     }
1846    ///
1847    ///     fn handle(&self, context: &mut ContextHandle) {
1848    ///         if let Modifier::Builtin { value, .. } = context.modifier {
1849    ///             match *value {
1850    ///                 "" => context.buffer.line("color: #333;"),
1851    ///                 "invert" => context.buffer.line("color: #eee;"),
1852    ///                 _ => unreachable!(),
1853    ///             }
1854    ///         }
1855    ///     }
1856    /// }
1857    ///
1858    /// let mut config = Config::default();
1859    /// config.register_plugin("prose", &Prose);
1860    ///
1861    /// let generated = encre_css::generate(
1862    ///     ["prose", "prose-invert"],
1863    ///     &config,
1864    /// );
1865    ///
1866    /// assert!(generated.ends_with(".prose {
1867    ///   color: #333;
1868    /// }
1869    ///
1870    /// .prose-invert {
1871    ///   color: #eee;
1872    /// }"));
1873    /// ```
1874    pub fn register_plugin<T: Into<Cow<'static, str>>>(
1875        &mut self,
1876        namespace: T,
1877        plugin: &'static (dyn Plugin + Send + Sync),
1878    ) {
1879        self.custom_plugins.push((namespace.into(), plugin));
1880    }
1881
1882    /// Register a custom variant which will be used during CSS generation.
1883    ///
1884    /// Note that if you are not the maintainer of a crate providing variants, you can ignore this
1885    /// function.
1886    ///
1887    /// # Example
1888    ///
1889    /// ```
1890    /// use encre_css::{Config, selector::Variant};
1891    /// use std::borrow::Cow;
1892    ///
1893    /// let mut config = Config::default();
1894    /// config.register_variant(
1895    ///     "headings",
1896    ///     // Insert the classes having this variant after all the other variants
1897    ///     Variant::new(config.last_variant_order(), "& :where(h1, h2, h3, h4, h5, h6)")
1898    /// );
1899    ///
1900    /// let generated = encre_css::generate(
1901    ///     ["headings:text-gray-700"],
1902    ///     &config,
1903    /// );
1904    ///
1905    /// assert!(generated.ends_with(".headings\\:text-gray-700 :where(h1, h2, h3, h4, h5, h6) {
1906    ///   color: oklch(37.3% .034 259.733);
1907    /// }"));
1908    /// ```
1909    ///
1910    /// You can also make a prefixed variant, that is a variant which has a prefix and an arbitrary
1911    /// value delimited by square brackets. When defining this kind of variant, you need to call
1912    /// [`Variant::with_prefixed`] and to insert the placeholder `{}` in the variant
1913    /// template, it will be replaced with the given arbitrary value.
1914    ///
1915    /// # Example
1916    ///
1917    /// ```
1918    /// use encre_css::{Config, selector::Variant};
1919    /// use std::borrow::Cow;
1920    ///
1921    /// let mut config = Config::default();
1922    /// config.register_variant(
1923    ///     "media",
1924    ///     // Insert the classes having this variant after all the other variants
1925    ///     Variant::new(config.last_variant_order(), "@media {}").with_prefixed()
1926    /// );
1927    ///
1928    /// let generated = encre_css::generate(
1929    ///     ["media-[print]:flex"],
1930    ///     &config,
1931    /// );
1932    ///
1933    /// assert!(generated.ends_with(r"@media print {
1934    ///   .media-\[print\]\:flex {
1935    ///     display: flex;
1936    ///   }
1937    /// }"));
1938    /// ```
1939    pub fn register_variant<T: Into<Cow<'static, str>>>(
1940        &mut self,
1941        variant_name: T,
1942        variant: Variant<'static>,
1943    ) {
1944        self.custom_variants.push((variant_name.into(), variant));
1945    }
1946
1947    /// Deserialize the content of a [TOML](https://toml.io) file to get the configuration.
1948    ///
1949    /// # Example
1950    ///
1951    /// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment"># encre-css.toml</span>
1952    /// <span class="kw">[theme]</span>
1953    /// dark_mode = { type = <span class="string">"class"</span>, class = <span class="string">".dark"</span> }
1954    /// screens = { 3xl = <span class="string">"1600px"</span>, lg = <span class="string">"2000px"</span> }<br>
1955    /// <span class="kw">[theme.colors]</span>
1956    /// primary = <span class="string">"#e5186a"</span>
1957    /// yellow-400 = <span class="string">"#ffef0e"</span></code></pre></div>
1958    ///
1959    /// ```no_run
1960    /// use encre_css::Config;
1961    ///
1962    /// # fn main() -> encre_css::Result<()> {
1963    /// let config = Config::from_file("encre-css.toml")?;
1964    /// let _generated = encre_css::generate([], &config);
1965    /// # Ok(())
1966    /// # }
1967    /// ```
1968    ///
1969    /// See [`Config`] for other ways of creating a configuration.
1970    ///
1971    /// # Errors
1972    ///
1973    /// Returns [`Error::ConfigFileNotFound`] if the given file does not exist.
1974    /// Returns [`Error::ConfigParsing`] if the given file could not be parsed.
1975    pub fn from_file<T: AsRef<Path>>(path: T) -> Result<Self> {
1976        Ok(toml::from_str(&fs::read_to_string(&path).map_err(
1977            |e| Error::ConfigFileNotFound(path.as_ref().to_path_buf(), e),
1978        )?)?)
1979    }
1980}
1981
1982impl PartialEq for Config {
1983    fn eq(&self, other: &Self) -> bool {
1984        self.safelist == other.safelist
1985            && self.theme == other.theme
1986            && self.preflight == other.preflight
1987            && self.shortcuts == other.shortcuts
1988            && self.max_shortcut_depth == other.max_shortcut_depth
1989            && self.extra == other.extra
1990            && self.custom_variants == other.custom_variants
1991    }
1992}
1993
1994impl fmt::Debug for Config {
1995    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1996        f.debug_struct("Config")
1997            .field("safelist", &self.safelist)
1998            .field("theme", &self.theme)
1999            .field("preflight", &self.preflight)
2000            .field("shortcuts", &self.shortcuts)
2001            .field("max_shortcut_depth", &self.max_shortcut_depth)
2002            .field("extra", &self.extra)
2003            .field("custom_plugins", &self.custom_plugins)
2004            .field("custom_variants", &self.custom_variants)
2005            .finish_non_exhaustive()
2006    }
2007}
2008
2009#[cfg(test)]
2010mod tests {
2011    use super::*;
2012    use crate::{generate, utils::testing::base_config};
2013
2014    use pretty_assertions::assert_eq;
2015
2016    #[test]
2017    fn gen_css_with_custom_config() {
2018        let mut config = base_config();
2019        config.theme.colors.add("rosa-500", "#e5186a");
2020        config.theme.screens.add("3xl", "1600px");
2021
2022        let generated = generate(["3xl:text-rosa-500"], &config);
2023
2024        assert_eq!(
2025            generated,
2026            String::from(
2027                r"@media (width >= 1600px) {
2028  .\33xl\:text-rosa-500 {
2029    color: #e5186a;
2030  }
2031}"
2032            )
2033        );
2034    }
2035
2036    #[test]
2037    fn gen_css_with_shortcuts() {
2038        let mut config = base_config();
2039        config
2040            .shortcuts
2041            .add("btn", "bg-red-500 border-1 rounded-xl");
2042        config.shortcuts.add("bg", "bg-blue-100");
2043
2044        let generated = generate(["btn", "bg-yellow-500"], &config);
2045
2046        assert_eq!(
2047            generated,
2048            String::from(
2049                ".btn {
2050  border-radius: 0.75rem;
2051}
2052
2053.btn {
2054  border-width: 1px;
2055}
2056
2057.bg-yellow-500 {
2058  background-color: oklch(79.5% .184 86.047);
2059}
2060
2061.btn {
2062  background-color: oklch(63.7% .237 25.331);
2063}"
2064            )
2065        );
2066    }
2067
2068    #[test]
2069    fn gen_css_with_nested_shortcuts() {
2070        let mut config = base_config();
2071        config
2072            .shortcuts
2073            .add("btn", "bg-red-500 border-1 rounded-xl");
2074        config.shortcuts.add("btn-primary", "btn bg-blue-500");
2075
2076        let generated = generate(["btn-primary"], &config);
2077
2078        assert_eq!(
2079            generated,
2080            String::from(
2081                ".btn-primary {
2082  border-radius: 0.75rem;
2083}
2084
2085.btn-primary {
2086  border-width: 1px;
2087}
2088
2089.btn-primary {
2090  background-color: oklch(63.7% .237 25.331);
2091}"
2092            )
2093        );
2094    }
2095
2096    #[test]
2097    fn gen_css_with_shortcut_cycle() {
2098        let mut config = base_config();
2099        config
2100            .shortcuts
2101            .add("btn", "bg-red-500 border-1 rounded-xl btn-primary");
2102        config.shortcuts.add("btn-primary", "btn bg-blue-500");
2103
2104        let generated = generate(["btn-primary"], &config);
2105
2106        assert_eq!(
2107            generated,
2108            String::from(
2109                ".btn-primary {
2110  border-radius: 0.75rem;
2111}
2112
2113.btn-primary {
2114  border-width: 1px;
2115}
2116
2117.btn-primary {
2118  background-color: oklch(63.7% .237 25.331);
2119}"
2120            )
2121        );
2122    }
2123
2124    #[test]
2125    fn gen_css_with_safelist() {
2126        let mut config = base_config();
2127        config.safelist.add("text-red-500");
2128        config.safelist.add("btn");
2129        config.shortcuts.add("btn", "text-red-400");
2130
2131        let generated = generate(["bg-red-300"], &config);
2132
2133        assert_eq!(
2134            generated,
2135            String::from(
2136                ".bg-red-300 {
2137  background-color: oklch(80.8% .114 19.571);
2138}
2139
2140.btn {
2141  color: oklch(70.4% .191 22.216);
2142}
2143
2144.text-red-500 {
2145  color: oklch(63.7% .237 25.331);
2146}"
2147            )
2148        );
2149    }
2150
2151    #[test]
2152    fn gen_css_with_custom_plugin_and_extra_fields() {
2153        use crate::prelude::build_plugin::*;
2154        use std::collections::HashMap;
2155
2156        #[derive(Debug)]
2157        struct EmojiPlugin;
2158
2159        impl Plugin for EmojiPlugin {
2160            fn can_handle(&self, context: ContextCanHandle) -> bool {
2161                matches!(context.modifier, Modifier::Builtin { value, .. } if context.config.extra.get("emojis").map_or(false, |val| val.as_table().map_or(false, |table| table.contains_key(*value))))
2162            }
2163
2164            fn handle(&self, context: &mut ContextHandle) {
2165                if let Modifier::Builtin { value, .. } = context.modifier {
2166                    context.buffer.line(format_args!(
2167                        r#"content: {};"#,
2168                        context
2169                            .config
2170                            .extra
2171                            .get("emojis")
2172                            .unwrap()
2173                            .as_table()
2174                            .unwrap()
2175                            .get(*value)
2176                            .unwrap()
2177                    ));
2178                }
2179            }
2180        }
2181
2182        let mut config = base_config();
2183        config.register_plugin("emoji", &EmojiPlugin);
2184        config.extra.add(
2185            "emojis",
2186            HashMap::from_iter([("tada", "\u{1f389}"), ("rocket", "\u{1f680}")]),
2187        );
2188
2189        let generated = generate(["emoji-tada"], &config);
2190
2191        assert_eq!(
2192            generated,
2193            String::from(
2194                ".emoji-tada {
2195  content: \"\u{1f389}\";
2196}"
2197            )
2198        );
2199    }
2200
2201    #[test]
2202    fn gen_css_with_custom_plugin_extra_fields_and_parsed_config() {
2203        use crate::prelude::build_plugin::*;
2204
2205        #[derive(Debug)]
2206        struct EmojiPlugin;
2207
2208        impl Plugin for EmojiPlugin {
2209            fn can_handle(&self, context: ContextCanHandle) -> bool {
2210                matches!(context.modifier, Modifier::Builtin { value, .. } if context.config.extra.get("emojis").map_or(false, |val| val.as_table().map_or(false, |table| table.contains_key(*value))))
2211            }
2212
2213            fn handle(&self, context: &mut ContextHandle) {
2214                if let Modifier::Builtin { value, .. } = context.modifier {
2215                    context.buffer.line(format_args!(
2216                        r#"content: {};"#,
2217                        context
2218                            .config
2219                            .extra
2220                            .get("emojis")
2221                            .unwrap()
2222                            .as_table()
2223                            .unwrap()
2224                            .get(*value)
2225                            .unwrap()
2226                    ));
2227                }
2228            }
2229        }
2230
2231        let mut config = Config::from_file("tests/fixtures/extra-fields-config.toml").unwrap();
2232        config.register_plugin("emoji", &EmojiPlugin);
2233
2234        let generated = generate(["emoji-tada"], &config);
2235
2236        assert_eq!(
2237            generated,
2238            String::from(
2239                ".emoji-tada {
2240  content: \"\u{1f389}\";
2241}"
2242            )
2243        );
2244    }
2245
2246    #[test]
2247    fn config_is_extended_and_overridden() {
2248        let config = Config::from_file("tests/fixtures/custom-config.toml").unwrap();
2249
2250        let generated = generate(
2251            [
2252                "bg-rosa-500",
2253                "bg-yellow-400",
2254                "bg-yellow-100",
2255                "3xl:underline",
2256                "lg:text-rosa-500",
2257            ],
2258            &config,
2259        );
2260
2261        assert_eq!(
2262            generated,
2263            String::from(
2264                r".bg-rosa-500 {
2265  background-color: #e5186a;
2266}
2267
2268.bg-yellow-100 {
2269  background-color: oklch(97.3% .071 103.193);
2270}
2271
2272.bg-yellow-400 {
2273  background-color: #ffef0e;
2274}
2275
2276@media (width >= 2000px) {
2277  .lg\:text-rosa-500 {
2278    color: #e5186a;
2279  }
2280}
2281
2282@media (width >= 1600px) {
2283  .\33xl\:underline {
2284    -webkit-text-decoration-line: underline;
2285    text-decoration-line: underline;
2286  }
2287}"
2288            )
2289        );
2290    }
2291
2292    #[test]
2293    fn deserialize_config() {
2294        let mut config = base_config();
2295        config.theme.colors.add("rosa-500", "#e5186a");
2296        config.theme.colors.add("yellow-400", "#ffef0e");
2297        config.theme.aria.add("current", "current=\"page\"");
2298        config.theme.screens.add("lg", "2000px");
2299        config.theme.screens.add("3xl", "1600px");
2300        config.theme.dark_mode = DarkMode::new_class(".dark");
2301
2302        assert_eq!(
2303            Config::from_file("tests/fixtures/custom-config.toml").unwrap(),
2304            config
2305        );
2306    }
2307
2308    #[test]
2309    fn serialize_config() {
2310        let mut config = Config {
2311            preflight: Preflight::None,
2312            ..Default::default()
2313        };
2314        config.theme.dark_mode = DarkMode::new_class(".dark");
2315        config.theme.screens.add("3xl", "1600px");
2316        config.theme.screens.add("lg", "2000px");
2317        config.theme.colors.add("rosa-500", "#e5186a");
2318        config.theme.colors.add("yellow-400", "#ffef0e");
2319        config.theme.aria.add("current", "current=\"page\"");
2320
2321        let result = toml::to_string(&config).unwrap();
2322
2323        let expected_config = fs::read_to_string("tests/fixtures/custom-config.toml").unwrap();
2324        assert_eq!(expected_config, result);
2325    }
2326
2327    #[test]
2328    fn toml_doc_tests() {
2329        // This function tests all TOML blocks used in the documentation
2330        // If a block is changed in this function, it should also be changed in the corresponding
2331        // documentation section and vice-versa
2332
2333        // Shortcuts
2334        {
2335            let toml_for_shortcuts = r#"
2336        [shortcuts]
2337        btn = "border-1 rounded-xl bg-red-500"
2338        "#;
2339            let config: Config = toml::from_str(toml_for_shortcuts).unwrap();
2340
2341            let generated = generate([r#"<button class="btn">Click me</button>"#], &config);
2342
2343            assert!(generated.ends_with(
2344                r#".btn {
2345  border-radius: 0.75rem;
2346}
2347
2348.btn {
2349  border-width: 1px;
2350}
2351
2352.btn {
2353  background-color: oklch(63.7% .237 25.331);
2354}"#
2355            ));
2356        }
2357
2358        // Safelist
2359        {
2360            let toml_for_safelist = r#"safelist = ["text-blue-400", "text-gray-400"]"#;
2361            let config: Config = toml::from_str(toml_for_safelist).unwrap();
2362
2363            let generated = generate([r#"<button class="bg-red-500">Click me</button>"#], &config);
2364
2365            assert!(generated.ends_with(
2366                r#".bg-red-500 {
2367  background-color: oklch(63.7% .237 25.331);
2368}
2369
2370.text-blue-400 {
2371  color: oklch(70.7% .165 254.624);
2372}
2373
2374.text-gray-400 {
2375  color: oklch(70.7% .022 261.325);
2376}"#
2377            ));
2378        }
2379
2380        // Preflight
2381        {
2382            let toml_for_preflight = r#"preflight = "none""#;
2383            let config: Config = toml::from_str(toml_for_preflight).unwrap();
2384            assert!(generate([], &config).is_empty());
2385
2386            let toml_for_preflight = r#"preflight = { custom = "html, body { width: 100vw; height: 100vh; margin: 0; }" }"#;
2387            let config: Config = toml::from_str(toml_for_preflight).unwrap();
2388            assert_eq!(
2389                generate([], &config),
2390                "html, body { width: 100vw; height: 100vh; margin: 0; }"
2391            );
2392
2393            let toml_for_preflight =
2394                r#"preflight = { full = { font_family_mono = "'Fira Code'" } }"#;
2395            let config: Config = toml::from_str(toml_for_preflight).unwrap();
2396            assert!(generate([], &config).contains(
2397                "code, kbd, samp, pre {
2398  font-family: 'Fira Code';"
2399            ));
2400        }
2401
2402        // Theme
2403        {
2404            let toml_for_theme = r##"
2405            [theme]
2406            dark_mode = { class = "body.dark" }
2407
2408            [theme.colors]
2409            primary = "#d3198c"
2410
2411            [theme.screens]
2412            tablet = "640px"
2413
2414            [theme.containers]
2415            medium = "640px"
2416
2417            [theme.aria]
2418            current = 'current="page"'
2419            "##;
2420            let config: Config = toml::from_str(toml_for_theme).unwrap();
2421
2422            let generated = generate(
2423                [
2424                    r#"<div class="bg-primary tablet:block aria-current:text-primary"><button class="bg-white @medium:flex">Click me</button>"#,
2425                ],
2426                &config,
2427            );
2428
2429            assert!(generated.ends_with(
2430                r#"
2431.bg-primary {
2432  background-color: #d3198c;
2433}
2434
2435.bg-white {
2436  background-color: #fff;
2437}
2438
2439@media (width >= 640px) {
2440  .tablet\:block {
2441    display: block;
2442  }
2443}
2444
2445@container (width >= 640px) {
2446  .\@medium\:flex {
2447    display: flex;
2448  }
2449}
2450
2451.aria-current\:text-primary[aria-current="page"] {
2452  color: #d3198c;
2453}"#
2454            ));
2455        }
2456    }
2457}