encre_css/
config.rs

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