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