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 (min-width: 640px) {
30//!   .dark .tablet\:dark\:bg-primary {
31//!     --en-bg-opacity: 1;
32//!     background-color: rgb(211 25 140 / var(--en-bg-opacity));
33//!   }
34//! }"#));
35//! ```
36//!
37//! The previous example is equivalent to the following TOML configuration file:
38//!
39//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="kw">[theme]</span>
40//! dark_mode = { type = <span class="string">"class"</span>, class = <span class="string">".dark"</span> }
41//! colors = { primary = <span class="string">"#d3198c"</span>, secondary = <span class="string">"#fff"</span> }
42//! screens = { tablet = <span class="string">"640px"</span>, laptop = <span class="string">"1024px"</span>, desktop = <span class="string">"1280px"</span> }
43//! </code></pre></div>
44//!
45//! [`generate`]: crate::generate
46use crate::{
47    error::{Error, Result},
48    preflight::Preflight,
49    scanner::Scanner,
50    selector::VariantType,
51};
52
53#[allow(clippy::wildcard_imports)]
54use crate::plugins::*;
55
56use phf::phf_map;
57use serde::{Deserialize, Serialize};
58use std::{
59    borrow::Cow,
60    collections::{BTreeMap, BTreeSet},
61    fmt, fs, iter,
62    path::Path,
63};
64
65/// The list of all default colors.
66///
67/// <table style="display: table;">
68///     <thead>
69///         <tr>
70///             <th style="text-align: center;">Name</th>
71///             <th style="text-align: center;">RGB Value</th>
72///             <th style="text-align: center;">Color</th>
73///         </tr>
74///     </thead>
75///     <tbody>
76///         <tr><td>slate-50</td><td>rgb(248 250 252)</td><td style="background-color: rgb(248 250 252);"></td></tr>
77///         <tr><td>slate-100</td><td>rgb(241 245 249)</td><td style="background-color: rgb(241 245 249);"></td></tr>
78///         <tr><td>slate-200</td><td>rgb(226 232 240)</td><td style="background-color: rgb(226 232 240);"></td></tr>
79///         <tr><td>slate-300</td><td>rgb(203 213 225)</td><td style="background-color: rgb(203 213 225);"></td></tr>
80///         <tr><td>slate-400</td><td>rgb(148 163 184)</td><td style="background-color: rgb(148 163 184);"></td></tr>
81///         <tr><td>slate-500</td><td>rgb(100 116 139)</td><td style="background-color: rgb(100 116 139);"></td></tr>
82///         <tr><td>slate-600</td><td>rgb(71 85 105)</td><td style="background-color: rgb(71 85 105);"></td></tr>
83///         <tr><td>slate-700</td><td>rgb(51 65 85)</td><td style="background-color: rgb(51 65 85);"></td></tr>
84///         <tr><td>slate-800</td><td>rgb(30 41 59)</td><td style="background-color: rgb(30 41 59);"></td></tr>
85///         <tr><td>slate-900</td><td>rgb(15 23 42)</td><td style="background-color: rgb(15 23 42);"></td></tr>
86///         <tr><td>gray-50</td><td>rgb(249 250 251)</td><td style="background-color: rgb(249 250 251);"></td></tr>
87///         <tr><td>gray-100</td><td>rgb(243 244 246)</td><td style="background-color: rgb(243 244 246);"></td></tr>
88///         <tr><td>gray-200</td><td>rgb(229 231 235)</td><td style="background-color: rgb(229 231 235);"></td></tr>
89///         <tr><td>gray-300</td><td>rgb(209 213 219)</td><td style="background-color: rgb(209 213 219);"></td></tr>
90///         <tr><td>gray-400</td><td>rgb(156 163 175)</td><td style="background-color: rgb(156 163 175);"></td></tr>
91///         <tr><td>gray-500</td><td>rgb(107 114 128)</td><td style="background-color: rgb(107 114 128);"></td></tr>
92///         <tr><td>gray-600</td><td>rgb(75 85 99)</td><td style="background-color: rgb(75 85 99);"></td></tr>
93///         <tr><td>gray-700</td><td>rgb(55 65 81)</td><td style="background-color: rgb(55 65 81);"></td></tr>
94///         <tr><td>gray-800</td><td>rgb(31 41 55)</td><td style="background-color: rgb(31 41 55);"></td></tr>
95///         <tr><td>gray-900</td><td>rgb(17 24 39)</td><td style="background-color: rgb(17 24 39);"></td></tr>
96///         <tr><td>zinc-50</td><td>rgb(250 250 250)</td><td style="background-color: rgb(250 250 250);"></td></tr>
97///         <tr><td>zinc-100</td><td>rgb(244 244 245)</td><td style="background-color: rgb(244 244 245);"></td></tr>
98///         <tr><td>zinc-200</td><td>rgb(228 228 231)</td><td style="background-color: rgb(228 228 231);"></td></tr>
99///         <tr><td>zinc-300</td><td>rgb(212 212 216)</td><td style="background-color: rgb(212 212 216);"></td></tr>
100///         <tr><td>zinc-400</td><td>rgb(161 161 170)</td><td style="background-color: rgb(161 161 170);"></td></tr>
101///         <tr><td>zinc-500</td><td>rgb(113 113 122)</td><td style="background-color: rgb(113 113 122);"></td></tr>
102///         <tr><td>zinc-600</td><td>rgb(82 82 91)</td><td style="background-color: rgb(82 82 91);"></td></tr>
103///         <tr><td>zinc-700</td><td>rgb(63 63 70)</td><td style="background-color: rgb(63 63 70);"></td></tr>
104///         <tr><td>zinc-800</td><td>rgb(39 39 42)</td><td style="background-color: rgb(39 39 42);"></td></tr>
105///         <tr><td>zinc-900</td><td>rgb(24 24 27)</td><td style="background-color: rgb(24 24 27);"></td></tr>
106///         <tr><td>neutral-50</td><td>rgb(250 250 250)</td><td style="background-color: rgb(250 250 250);"></td></tr>
107///         <tr><td>neutral-100</td><td>rgb(245 245 245)</td><td style="background-color: rgb(245 245 245);"></td></tr>
108///         <tr><td>neutral-200</td><td>rgb(229 229 229)</td><td style="background-color: rgb(229 229 229);"></td></tr>
109///         <tr><td>neutral-300</td><td>rgb(212 212 212)</td><td style="background-color: rgb(212 212 212);"></td></tr>
110///         <tr><td>neutral-400</td><td>rgb(163 163 163)</td><td style="background-color: rgb(163 163 163);"></td></tr>
111///         <tr><td>neutral-500</td><td>rgb(115 115 115)</td><td style="background-color: rgb(115 115 115);"></td></tr>
112///         <tr><td>neutral-600</td><td>rgb(82 82 82)</td><td style="background-color: rgb(82 82 82);"></td></tr>
113///         <tr><td>neutral-700</td><td>rgb(64 64 64)</td><td style="background-color: rgb(64 64 64);"></td></tr>
114///         <tr><td>neutral-800</td><td>rgb(38 38 38)</td><td style="background-color: rgb(38 38 38);"></td></tr>
115///         <tr><td>neutral-900</td><td>rgb(23 23 23)</td><td style="background-color: rgb(23 23 23);"></td></tr>
116///         <tr><td>stone-50</td><td>rgb(250 250 249)</td><td style="background-color: rgb(250 250 249);"></td></tr>
117///         <tr><td>stone-100</td><td>rgb(245 245 244)</td><td style="background-color: rgb(245 245 244);"></td></tr>
118///         <tr><td>stone-200</td><td>rgb(231 229 228)</td><td style="background-color: rgb(231 229 228);"></td></tr>
119///         <tr><td>stone-300</td><td>rgb(214 211 209)</td><td style="background-color: rgb(214 211 209);"></td></tr>
120///         <tr><td>stone-400</td><td>rgb(168 162 158)</td><td style="background-color: rgb(168 162 158);"></td></tr>
121///         <tr><td>stone-500</td><td>rgb(120 113 108)</td><td style="background-color: rgb(120 113 108);"></td></tr>
122///         <tr><td>stone-600</td><td>rgb(87 83 78)</td><td style="background-color: rgb(87 83 78);"></td></tr>
123///         <tr><td>stone-700</td><td>rgb(68 64 60)</td><td style="background-color: rgb(68 64 60);"></td></tr>
124///         <tr><td>stone-800</td><td>rgb(41 37 36)</td><td style="background-color: rgb(41 37 36);"></td></tr>
125///         <tr><td>stone-900</td><td>rgb(28 25 23)</td><td style="background-color: rgb(28 25 23);"></td></tr>
126///         <tr><td>red-50</td><td>rgb(254 242 242)</td><td style="background-color: rgb(254 242 242);"></td></tr>
127///         <tr><td>red-100</td><td>rgb(254 226 226)</td><td style="background-color: rgb(254 226 226);"></td></tr>
128///         <tr><td>red-200</td><td>rgb(254 202 202)</td><td style="background-color: rgb(254 202 202);"></td></tr>
129///         <tr><td>red-300</td><td>rgb(252 165 165)</td><td style="background-color: rgb(252 165 165);"></td></tr>
130///         <tr><td>red-400</td><td>rgb(248 113 113)</td><td style="background-color: rgb(248 113 113);"></td></tr>
131///         <tr><td>red-500</td><td>rgb(239 68 68)</td><td style="background-color: rgb(239 68 68);"></td></tr>
132///         <tr><td>red-600</td><td>rgb(220 38 38)</td><td style="background-color: rgb(220 38 38);"></td></tr>
133///         <tr><td>red-700</td><td>rgb(185 28 28)</td><td style="background-color: rgb(185 28 28);"></td></tr>
134///         <tr><td>red-800</td><td>rgb(153 27 27)</td><td style="background-color: rgb(153 27 27);"></td></tr>
135///         <tr><td>red-900</td><td>rgb(127 29 29)</td><td style="background-color: rgb(127 29 29);"></td></tr>
136///         <tr><td>orange-50</td><td>rgb(255 247 237)</td><td style="background-color: rgb(255 247 237);"></td></tr>
137///         <tr><td>orange-100</td><td>rgb(255 237 213)</td><td style="background-color: rgb(255 237 213);"></td></tr>
138///         <tr><td>orange-200</td><td>rgb(254 215 170)</td><td style="background-color: rgb(254 215 170);"></td></tr>
139///         <tr><td>orange-300</td><td>rgb(253 186 116)</td><td style="background-color: rgb(253 186 116);"></td></tr>
140///         <tr><td>orange-400</td><td>rgb(251 146 60)</td><td style="background-color: rgb(251 146 60);"></td></tr>
141///         <tr><td>orange-500</td><td>rgb(249 115 22)</td><td style="background-color: rgb(249 115 22);"></td></tr>
142///         <tr><td>orange-600</td><td>rgb(234 88 12)</td><td style="background-color: rgb(234 88 12);"></td></tr>
143///         <tr><td>orange-700</td><td>rgb(194 65 12)</td><td style="background-color: rgb(194 65 12);"></td></tr>
144///         <tr><td>orange-800</td><td>rgb(154 52 18)</td><td style="background-color: rgb(154 52 18);"></td></tr>
145///         <tr><td>orange-900</td><td>rgb(124 45 18)</td><td style="background-color: rgb(124 45 18);"></td></tr>
146///         <tr><td>amber-50</td><td>rgb(255 251 235)</td><td style="background-color: rgb(255 251 235);"></td></tr>
147///         <tr><td>amber-100</td><td>rgb(254 243 199)</td><td style="background-color: rgb(254 243 199);"></td></tr>
148///         <tr><td>amber-200</td><td>rgb(253 230 138)</td><td style="background-color: rgb(253 230 138);"></td></tr>
149///         <tr><td>amber-300</td><td>rgb(252 211 77)</td><td style="background-color: rgb(252 211 77);"></td></tr>
150///         <tr><td>amber-400</td><td>rgb(251 191 36)</td><td style="background-color: rgb(251 191 36);"></td></tr>
151///         <tr><td>amber-500</td><td>rgb(245 158 11)</td><td style="background-color: rgb(245 158 11);"></td></tr>
152///         <tr><td>amber-600</td><td>rgb(217 119 6)</td><td style="background-color: rgb(217 119 6);"></td></tr>
153///         <tr><td>amber-700</td><td>rgb(180 83 9)</td><td style="background-color: rgb(180 83 9);"></td></tr>
154///         <tr><td>amber-800</td><td>rgb(146 64 14)</td><td style="background-color: rgb(146 64 14);"></td></tr>
155///         <tr><td>amber-900</td><td>rgb(120 53 15)</td><td style="background-color: rgb(120 53 15);"></td></tr>
156///         <tr><td>yellow-50</td><td>rgb(254 252 232)</td><td style="background-color: rgb(254 252 232);"></td></tr>
157///         <tr><td>yellow-100</td><td>rgb(254 249 195)</td><td style="background-color: rgb(254 249 195);"></td></tr>
158///         <tr><td>yellow-200</td><td>rgb(254 240 138)</td><td style="background-color: rgb(254 240 138);"></td></tr>
159///         <tr><td>yellow-300</td><td>rgb(253 224 71)</td><td style="background-color: rgb(253 224 71);"></td></tr>
160///         <tr><td>yellow-400</td><td>rgb(250 204 21)</td><td style="background-color: rgb(250 204 21);"></td></tr>
161///         <tr><td>yellow-500</td><td>rgb(234 179 8)</td><td style="background-color: rgb(234 179 8);"></td></tr>
162///         <tr><td>yellow-600</td><td>rgb(202 138 4)</td><td style="background-color: rgb(202 138 4);"></td></tr>
163///         <tr><td>yellow-700</td><td>rgb(161 98 7)</td><td style="background-color: rgb(161 98 7);"></td></tr>
164///         <tr><td>yellow-800</td><td>rgb(133 77 14)</td><td style="background-color: rgb(133 77 14);"></td></tr>
165///         <tr><td>yellow-900</td><td>rgb(113 63 18)</td><td style="background-color: rgb(113 63 18);"></td></tr>
166///         <tr><td>lime-50</td><td>rgb(247 254 231)</td><td style="background-color: rgb(247 254 231);"></td></tr>
167///         <tr><td>lime-100</td><td>rgb(236 252 203)</td><td style="background-color: rgb(236 252 203);"></td></tr>
168///         <tr><td>lime-200</td><td>rgb(217 249 157)</td><td style="background-color: rgb(217 249 157);"></td></tr>
169///         <tr><td>lime-300</td><td>rgb(190 242 100)</td><td style="background-color: rgb(190 242 100);"></td></tr>
170///         <tr><td>lime-400</td><td>rgb(163 230 53)</td><td style="background-color: rgb(163 230 53);"></td></tr>
171///         <tr><td>lime-500</td><td>rgb(132 204 22)</td><td style="background-color: rgb(132 204 22);"></td></tr>
172///         <tr><td>lime-600</td><td>rgb(101 163 13)</td><td style="background-color: rgb(101 163 13);"></td></tr>
173///         <tr><td>lime-700</td><td>rgb(77 124 15)</td><td style="background-color: rgb(77 124 15);"></td></tr>
174///         <tr><td>lime-800</td><td>rgb(63 98 18)</td><td style="background-color: rgb(63 98 18);"></td></tr>
175///         <tr><td>lime-900</td><td>rgb(54 83 20)</td><td style="background-color: rgb(54 83 20);"></td></tr>
176///         <tr><td>green-50</td><td>rgb(240 253 244)</td><td style="background-color: rgb(240 253 244);"></td></tr>
177///         <tr><td>green-100</td><td>rgb(220 252 231)</td><td style="background-color: rgb(220 252 231);"></td></tr>
178///         <tr><td>green-200</td><td>rgb(187 247 208)</td><td style="background-color: rgb(187 247 208);"></td></tr>
179///         <tr><td>green-300</td><td>rgb(134 239 172)</td><td style="background-color: rgb(134 239 172);"></td></tr>
180///         <tr><td>green-400</td><td>rgb(74 222 128)</td><td style="background-color: rgb(74 222 128);"></td></tr>
181///         <tr><td>green-500</td><td>rgb(34 197 94)</td><td style="background-color: rgb(34 197 94);"></td></tr>
182///         <tr><td>green-600</td><td>rgb(22 163 74)</td><td style="background-color: rgb(22 163 74);"></td></tr>
183///         <tr><td>green-700</td><td>rgb(21 128 61)</td><td style="background-color: rgb(21 128 61);"></td></tr>
184///         <tr><td>green-800</td><td>rgb(22 101 52)</td><td style="background-color: rgb(22 101 52);"></td></tr>
185///         <tr><td>green-900</td><td>rgb(20 83 45)</td><td style="background-color: rgb(20 83 45);"></td></tr>
186///         <tr><td>emerald-50</td><td>rgb(236 253 245)</td><td style="background-color: rgb(236 253 245);"></td></tr>
187///         <tr><td>emerald-100</td><td>rgb(209 250 229)</td><td style="background-color: rgb(209 250 229);"></td></tr>
188///         <tr><td>emerald-200</td><td>rgb(167 243 208)</td><td style="background-color: rgb(167 243 208);"></td></tr>
189///         <tr><td>emerald-300</td><td>rgb(110 231 183)</td><td style="background-color: rgb(110 231 183);"></td></tr>
190///         <tr><td>emerald-400</td><td>rgb(52 211 153)</td><td style="background-color: rgb(52 211 153);"></td></tr>
191///         <tr><td>emerald-500</td><td>rgb(16 185 129)</td><td style="background-color: rgb(16 185 129);"></td></tr>
192///         <tr><td>emerald-600</td><td>rgb(5 150 105)</td><td style="background-color: rgb(5 150 105);"></td></tr>
193///         <tr><td>emerald-700</td><td>rgb(4 120 87)</td><td style="background-color: rgb(4 120 87);"></td></tr>
194///         <tr><td>emerald-800</td><td>rgb(6 95 70)</td><td style="background-color: rgb(6 95 70);"></td></tr>
195///         <tr><td>emerald-900</td><td>rgb(6 78 59)</td><td style="background-color: rgb(6 78 59);"></td></tr>
196///         <tr><td>teal-50</td><td>rgb(240 253 250)</td><td style="background-color: rgb(240 253 250);"></td></tr>
197///         <tr><td>teal-100</td><td>rgb(204 251 241)</td><td style="background-color: rgb(204 251 241);"></td></tr>
198///         <tr><td>teal-200</td><td>rgb(153 246 228)</td><td style="background-color: rgb(153 246 228);"></td></tr>
199///         <tr><td>teal-300</td><td>rgb(94 234 212)</td><td style="background-color: rgb(94 234 212);"></td></tr>
200///         <tr><td>teal-400</td><td>rgb(45 212 191)</td><td style="background-color: rgb(45 212 191);"></td></tr>
201///         <tr><td>teal-500</td><td>rgb(20 184 166)</td><td style="background-color: rgb(20 184 166);"></td></tr>
202///         <tr><td>teal-600</td><td>rgb(13 148 136)</td><td style="background-color: rgb(13 148 136);"></td></tr>
203///         <tr><td>teal-700</td><td>rgb(15 118 110)</td><td style="background-color: rgb(15 118 110);"></td></tr>
204///         <tr><td>teal-800</td><td>rgb(17 94 89)</td><td style="background-color: rgb(17 94 89);"></td></tr>
205///         <tr><td>teal-900</td><td>rgb(19 78 74)</td><td style="background-color: rgb(19 78 74);"></td></tr>
206///         <tr><td>cyan-50</td><td>rgb(236 254 255)</td><td style="background-color: rgb(236 254 255);"></td></tr>
207///         <tr><td>cyan-100</td><td>rgb(207 250 254)</td><td style="background-color: rgb(207 250 254);"></td></tr>
208///         <tr><td>cyan-200</td><td>rgb(165 243 252)</td><td style="background-color: rgb(165 243 252);"></td></tr>
209///         <tr><td>cyan-300</td><td>rgb(103 232 249)</td><td style="background-color: rgb(103 232 249);"></td></tr>
210///         <tr><td>cyan-400</td><td>rgb(34 211 238)</td><td style="background-color: rgb(34 211 238);"></td></tr>
211///         <tr><td>cyan-500</td><td>rgb(6 182 212)</td><td style="background-color: rgb(6 182 212);"></td></tr>
212///         <tr><td>cyan-600</td><td>rgb(8 145 178)</td><td style="background-color: rgb(8 145 178);"></td></tr>
213///         <tr><td>cyan-700</td><td>rgb(14 116 144)</td><td style="background-color: rgb(14 116 144);"></td></tr>
214///         <tr><td>cyan-800</td><td>rgb(21 94 117)</td><td style="background-color: rgb(21 94 117);"></td></tr>
215///         <tr><td>cyan-900</td><td>rgb(22 78 99)</td><td style="background-color: rgb(22 78 99);"></td></tr>
216///         <tr><td>sky-50</td><td>rgb(240 249 255)</td><td style="background-color: rgb(240 249 255);"></td></tr>
217///         <tr><td>sky-100</td><td>rgb(224 242 254)</td><td style="background-color: rgb(224 242 254);"></td></tr>
218///         <tr><td>sky-200</td><td>rgb(186 230 253)</td><td style="background-color: rgb(186 230 253);"></td></tr>
219///         <tr><td>sky-300</td><td>rgb(125 211 252)</td><td style="background-color: rgb(125 211 252);"></td></tr>
220///         <tr><td>sky-400</td><td>rgb(56 189 248)</td><td style="background-color: rgb(56 189 248);"></td></tr>
221///         <tr><td>sky-500</td><td>rgb(14 165 233)</td><td style="background-color: rgb(14 165 233);"></td></tr>
222///         <tr><td>sky-600</td><td>rgb(2 132 199)</td><td style="background-color: rgb(2 132 199);"></td></tr>
223///         <tr><td>sky-700</td><td>rgb(3 105 161)</td><td style="background-color: rgb(3 105 161);"></td></tr>
224///         <tr><td>sky-800</td><td>rgb(7 89 133)</td><td style="background-color: rgb(7 89 133);"></td></tr>
225///         <tr><td>sky-900</td><td>rgb(12 74 110)</td><td style="background-color: rgb(12 74 110);"></td></tr>
226///         <tr><td>blue-50</td><td>rgb(239 246 255)</td><td style="background-color: rgb(239 246 255);"></td></tr>
227///         <tr><td>blue-100</td><td>rgb(219 234 254)</td><td style="background-color: rgb(219 234 254);"></td></tr>
228///         <tr><td>blue-200</td><td>rgb(191 219 254)</td><td style="background-color: rgb(191 219 254);"></td></tr>
229///         <tr><td>blue-300</td><td>rgb(147 197 253)</td><td style="background-color: rgb(147 197 253);"></td></tr>
230///         <tr><td>blue-400</td><td>rgb(96 165 250)</td><td style="background-color: rgb(96 165 250);"></td></tr>
231///         <tr><td>blue-500</td><td>rgb(59 130 246)</td><td style="background-color: rgb(59 130 246);"></td></tr>
232///         <tr><td>blue-600</td><td>rgb(37 99 235)</td><td style="background-color: rgb(37 99 235);"></td></tr>
233///         <tr><td>blue-700</td><td>rgb(29 78 216)</td><td style="background-color: rgb(29 78 216);"></td></tr>
234///         <tr><td>blue-800</td><td>rgb(30 64 175)</td><td style="background-color: rgb(30 64 175);"></td></tr>
235///         <tr><td>blue-900</td><td>rgb(30 58 138)</td><td style="background-color: rgb(30 58 138);"></td></tr>
236///         <tr><td>indigo-50</td><td>rgb(238 242 255)</td><td style="background-color: rgb(238 242 255);"></td></tr>
237///         <tr><td>indigo-100</td><td>rgb(224 231 255)</td><td style="background-color: rgb(224 231 255);"></td></tr>
238///         <tr><td>indigo-200</td><td>rgb(199 210 254)</td><td style="background-color: rgb(199 210 254);"></td></tr>
239///         <tr><td>indigo-300</td><td>rgb(165 180 252)</td><td style="background-color: rgb(165 180 252);"></td></tr>
240///         <tr><td>indigo-400</td><td>rgb(129 140 248)</td><td style="background-color: rgb(129 140 248);"></td></tr>
241///         <tr><td>indigo-500</td><td>rgb(99 102 241)</td><td style="background-color: rgb(99 102 241);"></td></tr>
242///         <tr><td>indigo-600</td><td>rgb(79 70 229)</td><td style="background-color: rgb(79 70 229);"></td></tr>
243///         <tr><td>indigo-700</td><td>rgb(67 56 202)</td><td style="background-color: rgb(67 56 202);"></td></tr>
244///         <tr><td>indigo-800</td><td>rgb(55 48 163)</td><td style="background-color: rgb(55 48 163);"></td></tr>
245///         <tr><td>indigo-900</td><td>rgb(49 46 129)</td><td style="background-color: rgb(49 46 129);"></td></tr>
246///         <tr><td>violet-50</td><td>rgb(245 243 255)</td><td style="background-color: rgb(245 243 255);"></td></tr>
247///         <tr><td>violet-100</td><td>rgb(237 233 254)</td><td style="background-color: rgb(237 233 254);"></td></tr>
248///         <tr><td>violet-200</td><td>rgb(221 214 254)</td><td style="background-color: rgb(221 214 254);"></td></tr>
249///         <tr><td>violet-300</td><td>rgb(196 181 253)</td><td style="background-color: rgb(196 181 253);"></td></tr>
250///         <tr><td>violet-400</td><td>rgb(167 139 250)</td><td style="background-color: rgb(167 139 250);"></td></tr>
251///         <tr><td>violet-500</td><td>rgb(139 92 246)</td><td style="background-color: rgb(139 92 246);"></td></tr>
252///         <tr><td>violet-600</td><td>rgb(124 58 237)</td><td style="background-color: rgb(124 58 237);"></td></tr>
253///         <tr><td>violet-700</td><td>rgb(109 40 217)</td><td style="background-color: rgb(109 40 217);"></td></tr>
254///         <tr><td>violet-800</td><td>rgb(91 33 182)</td><td style="background-color: rgb(91 33 182);"></td></tr>
255///         <tr><td>violet-900</td><td>rgb(76 29 149)</td><td style="background-color: rgb(76 29 149);"></td></tr>
256///         <tr><td>purple-50</td><td>rgb(250 245 255)</td><td style="background-color: rgb(250 245 255);"></td></tr>
257///         <tr><td>purple-100</td><td>rgb(243 232 255)</td><td style="background-color: rgb(243 232 255);"></td></tr>
258///         <tr><td>purple-200</td><td>rgb(233 213 255)</td><td style="background-color: rgb(233 213 255);"></td></tr>
259///         <tr><td>purple-300</td><td>rgb(216 180 254)</td><td style="background-color: rgb(216 180 254);"></td></tr>
260///         <tr><td>purple-400</td><td>rgb(192 132 252)</td><td style="background-color: rgb(192 132 252);"></td></tr>
261///         <tr><td>purple-500</td><td>rgb(168 85 247)</td><td style="background-color: rgb(168 85 247);"></td></tr>
262///         <tr><td>purple-600</td><td>rgb(147 51 234)</td><td style="background-color: rgb(147 51 234);"></td></tr>
263///         <tr><td>purple-700</td><td>rgb(126 34 206)</td><td style="background-color: rgb(126 34 206);"></td></tr>
264///         <tr><td>purple-800</td><td>rgb(107 33 168)</td><td style="background-color: rgb(107 33 168);"></td></tr>
265///         <tr><td>purple-900</td><td>rgb(88 28 135)</td><td style="background-color: rgb(88 28 135);"></td></tr>
266///         <tr><td>fuchsia-50</td><td>rgb(253 244 255)</td><td style="background-color: rgb(253 244 255);"></td></tr>
267///         <tr><td>fuchsia-100</td><td>rgb(250 232 255)</td><td style="background-color: rgb(250 232 255);"></td></tr>
268///         <tr><td>fuchsia-200</td><td>rgb(245 208 254)</td><td style="background-color: rgb(245 208 254);"></td></tr>
269///         <tr><td>fuchsia-300</td><td>rgb(240 171 252)</td><td style="background-color: rgb(240 171 252);"></td></tr>
270///         <tr><td>fuchsia-400</td><td>rgb(232 121 249)</td><td style="background-color: rgb(232 121 249);"></td></tr>
271///         <tr><td>fuchsia-500</td><td>rgb(217 70 239)</td><td style="background-color: rgb(217 70 239);"></td></tr>
272///         <tr><td>fuchsia-600</td><td>rgb(192 38 211)</td><td style="background-color: rgb(192 38 211);"></td></tr>
273///         <tr><td>fuchsia-700</td><td>rgb(162 28 175)</td><td style="background-color: rgb(162 28 175);"></td></tr>
274///         <tr><td>fuchsia-800</td><td>rgb(134 25 143)</td><td style="background-color: rgb(134 25 143);"></td></tr>
275///         <tr><td>fuchsia-900</td><td>rgb(112 26 117)</td><td style="background-color: rgb(112 26 117);"></td></tr>
276///         <tr><td>pink-50</td><td>rgb(253 242 248)</td><td style="background-color: rgb(253 242 248);"></td></tr>
277///         <tr><td>pink-100</td><td>rgb(252 231 243)</td><td style="background-color: rgb(252 231 243);"></td></tr>
278///         <tr><td>pink-200</td><td>rgb(251 207 232)</td><td style="background-color: rgb(251 207 232);"></td></tr>
279///         <tr><td>pink-300</td><td>rgb(249 168 212)</td><td style="background-color: rgb(249 168 212);"></td></tr>
280///         <tr><td>pink-400</td><td>rgb(244 114 182)</td><td style="background-color: rgb(244 114 182);"></td></tr>
281///         <tr><td>pink-500</td><td>rgb(236 72 153)</td><td style="background-color: rgb(236 72 153);"></td></tr>
282///         <tr><td>pink-600</td><td>rgb(219 39 119)</td><td style="background-color: rgb(219 39 119);"></td></tr>
283///         <tr><td>pink-700</td><td>rgb(190 24 93)</td><td style="background-color: rgb(190 24 93);"></td></tr>
284///         <tr><td>pink-800</td><td>rgb(157 23 77)</td><td style="background-color: rgb(157 23 77);"></td></tr>
285///         <tr><td>pink-900</td><td>rgb(131 24 67)</td><td style="background-color: rgb(131 24 67);"></td></tr>
286///         <tr><td>rose-50</td><td>rgb(255 241 242)</td><td style="background-color: rgb(255 241 242);"></td></tr>
287///         <tr><td>rose-100</td><td>rgb(255 228 230)</td><td style="background-color: rgb(255 228 230);"></td></tr>
288///         <tr><td>rose-200</td><td>rgb(254 205 211)</td><td style="background-color: rgb(254 205 211);"></td></tr>
289///         <tr><td>rose-300</td><td>rgb(253 164 175)</td><td style="background-color: rgb(253 164 175);"></td></tr>
290///         <tr><td>rose-400</td><td>rgb(251 113 133)</td><td style="background-color: rgb(251 113 133);"></td></tr>
291///         <tr><td>rose-500</td><td>rgb(244 63 94)</td><td style="background-color: rgb(244 63 94);"></td></tr>
292///         <tr><td>rose-600</td><td>rgb(225 29 72)</td><td style="background-color: rgb(225 29 72);"></td></tr>
293///         <tr><td>rose-700</td><td>rgb(190 18 60)</td><td style="background-color: rgb(190 18 60);"></td></tr>
294///         <tr><td>rose-800</td><td>rgb(159 18 57)</td><td style="background-color: rgb(159 18 57);"></td></tr>
295///         <tr><td>rose-900</td><td>rgb(136 19 55)</td><td style="background-color: rgb(136 19 55);"></td></tr>
296///     </tbody>
297/// </table>
298///
299/// Based on [Tailwind's default color palette](https://tailwindcss.com/docs/customizing-colors).
300pub const BUILTIN_COLORS: phf::Map<&str, (u8, u8, u8)> = phf_map! {
301    "slate-50" => (248, 250, 252),
302    "slate-100" => (241, 245, 249),
303    "slate-200" => (226, 232, 240),
304    "slate-300" => (203, 213, 225),
305    "slate-400" => (148, 163, 184),
306    "slate-500" => (100, 116, 139),
307    "slate-600" => (71, 85, 105),
308    "slate-700" => (51, 65, 85),
309    "slate-800" => (30, 41, 59),
310    "slate-900" => (15, 23, 42),
311    "gray-50" => (249, 250, 251),
312    "gray-100" => (243, 244, 246),
313    "gray-200" => (229, 231, 235),
314    "gray-300" => (209, 213, 219),
315    "gray-400" => (156, 163, 175),
316    "gray-500" => (107, 114, 128),
317    "gray-600" => (75, 85, 99),
318    "gray-700" => (55, 65, 81),
319    "gray-800" => (31, 41, 55),
320    "gray-900" => (17, 24, 39),
321    "zinc-50" => (250, 250, 250),
322    "zinc-100" => (244, 244, 245),
323    "zinc-200" => (228, 228, 231),
324    "zinc-300" => (212, 212, 216),
325    "zinc-400" => (161, 161, 170),
326    "zinc-500" => (113, 113, 122),
327    "zinc-600" => (82, 82, 91),
328    "zinc-700" => (63, 63, 70),
329    "zinc-800" => (39, 39, 42),
330    "zinc-900" => (24, 24, 27),
331    "neutral-50" => (250, 250, 250),
332    "neutral-100" => (245, 245, 245),
333    "neutral-200" => (229, 229, 229),
334    "neutral-300" => (212, 212, 212),
335    "neutral-400" => (163, 163, 163),
336    "neutral-500" => (115, 115, 115),
337    "neutral-600" => (82, 82, 82),
338    "neutral-700" => (64, 64, 64),
339    "neutral-800" => (38, 38, 38),
340    "neutral-900" => (23, 23, 23),
341    "stone-50" => (250, 250, 249),
342    "stone-100" => (245, 245, 244),
343    "stone-200" => (231, 229, 228),
344    "stone-300" => (214, 211, 209),
345    "stone-400" => (168, 162, 158),
346    "stone-500" => (120, 113, 108),
347    "stone-600" => (87, 83, 78),
348    "stone-700" => (68, 64, 60),
349    "stone-800" => (41, 37, 36),
350    "stone-900" => (28, 25, 23),
351    "red-50" => (254, 242, 242),
352    "red-100" => (254, 226, 226),
353    "red-200" => (254, 202, 202),
354    "red-300" => (252, 165, 165),
355    "red-400" => (248, 113, 113),
356    "red-500" => (239, 68, 68),
357    "red-600" => (220, 38, 38),
358    "red-700" => (185, 28, 28),
359    "red-800" => (153, 27, 27),
360    "red-900" => (127, 29, 29),
361    "orange-50" => (255, 247, 237),
362    "orange-100" => (255, 237, 213),
363    "orange-200" => (254, 215, 170),
364    "orange-300" => (253, 186, 116),
365    "orange-400" => (251, 146, 60),
366    "orange-500" => (249, 115, 22),
367    "orange-600" => (234, 88, 12),
368    "orange-700" => (194, 65, 12),
369    "orange-800" => (154, 52, 18),
370    "orange-900" => (124, 45, 18),
371    "amber-50" => (255, 251, 235),
372    "amber-100" => (254, 243, 199),
373    "amber-200" => (253, 230, 138),
374    "amber-300" => (252, 211, 77),
375    "amber-400" => (251, 191, 36),
376    "amber-500" => (245, 158, 11),
377    "amber-600" => (217, 119, 6),
378    "amber-700" => (180, 83, 9),
379    "amber-800" => (146, 64, 14),
380    "amber-900" => (120, 53, 15),
381    "yellow-50" => (254, 252, 232),
382    "yellow-100" => (254, 249, 195),
383    "yellow-200" => (254, 240, 138),
384    "yellow-300" => (253, 224, 71),
385    "yellow-400" => (250, 204, 21),
386    "yellow-500" => (234, 179, 8),
387    "yellow-600" => (202, 138, 4),
388    "yellow-700" => (161, 98, 7),
389    "yellow-800" => (133, 77, 14),
390    "yellow-900" => (113, 63, 18),
391    "lime-50" => (247, 254, 231),
392    "lime-100" => (236, 252, 203),
393    "lime-200" => (217, 249, 157),
394    "lime-300" => (190, 242, 100),
395    "lime-400" => (163, 230, 53),
396    "lime-500" => (132, 204, 22),
397    "lime-600" => (101, 163, 13),
398    "lime-700" => (77, 124, 15),
399    "lime-800" => (63, 98, 18),
400    "lime-900" => (54, 83, 20),
401    "green-50" => (240, 253, 244),
402    "green-100" => (220, 252, 231),
403    "green-200" => (187, 247, 208),
404    "green-300" => (134, 239, 172),
405    "green-400" => (74, 222, 128),
406    "green-500" => (34, 197, 94),
407    "green-600" => (22, 163, 74),
408    "green-700" => (21, 128, 61),
409    "green-800" => (22, 101, 52),
410    "green-900" => (20, 83, 45),
411    "emerald-50" => (236, 253, 245),
412    "emerald-100" => (209, 250, 229),
413    "emerald-200" => (167, 243, 208),
414    "emerald-300" => (110, 231, 183),
415    "emerald-400" => (52, 211, 153),
416    "emerald-500" => (16, 185, 129),
417    "emerald-600" => (5, 150, 105),
418    "emerald-700" => (4, 120, 87),
419    "emerald-800" => (6, 95, 70),
420    "emerald-900" => (6, 78, 59),
421    "teal-50" => (240, 253, 250),
422    "teal-100" => (204, 251, 241),
423    "teal-200" => (153, 246, 228),
424    "teal-300" => (94, 234, 212),
425    "teal-400" => (45, 212, 191),
426    "teal-500" => (20, 184, 166),
427    "teal-600" => (13, 148, 136),
428    "teal-700" => (15, 118, 110),
429    "teal-800" => (17, 94, 89),
430    "teal-900" => (19, 78, 74),
431    "cyan-50" => (236, 254, 255),
432    "cyan-100" => (207, 250, 254),
433    "cyan-200" => (165, 243, 252),
434    "cyan-300" => (103, 232, 249),
435    "cyan-400" => (34, 211, 238),
436    "cyan-500" => (6, 182, 212),
437    "cyan-600" => (8, 145, 178),
438    "cyan-700" => (14, 116, 144),
439    "cyan-800" => (21, 94, 117),
440    "cyan-900" => (22, 78, 99),
441    "sky-50" => (240, 249, 255),
442    "sky-100" => (224, 242, 254),
443    "sky-200" => (186, 230, 253),
444    "sky-300" => (125, 211, 252),
445    "sky-400" => (56, 189, 248),
446    "sky-500" => (14, 165, 233),
447    "sky-600" => (2, 132, 199),
448    "sky-700" => (3, 105, 161),
449    "sky-800" => (7, 89, 133),
450    "sky-900" => (12, 74, 110),
451    "blue-50" => (239, 246, 255),
452    "blue-100" => (219, 234, 254),
453    "blue-200" => (191, 219, 254),
454    "blue-300" => (147, 197, 253),
455    "blue-400" => (96, 165, 250),
456    "blue-500" => (59, 130, 246),
457    "blue-600" => (37, 99, 235),
458    "blue-700" => (29, 78, 216),
459    "blue-800" => (30, 64, 175),
460    "blue-900" => (30, 58, 138),
461    "indigo-50" => (238, 242, 255),
462    "indigo-100" => (224, 231, 255),
463    "indigo-200" => (199, 210, 254),
464    "indigo-300" => (165, 180, 252),
465    "indigo-400" => (129, 140, 248),
466    "indigo-500" => (99, 102, 241),
467    "indigo-600" => (79, 70, 229),
468    "indigo-700" => (67, 56, 202),
469    "indigo-800" => (55, 48, 163),
470    "indigo-900" => (49, 46, 129),
471    "violet-50" => (245, 243, 255),
472    "violet-100" => (237, 233, 254),
473    "violet-200" => (221, 214, 254),
474    "violet-300" => (196, 181, 253),
475    "violet-400" => (167, 139, 250),
476    "violet-500" => (139, 92, 246),
477    "violet-600" => (124, 58, 237),
478    "violet-700" => (109, 40, 217),
479    "violet-800" => (91, 33, 182),
480    "violet-900" => (76, 29, 149),
481    "purple-50" => (250, 245, 255),
482    "purple-100" => (243, 232, 255),
483    "purple-200" => (233, 213, 255),
484    "purple-300" => (216, 180, 254),
485    "purple-400" => (192, 132, 252),
486    "purple-500" => (168, 85, 247),
487    "purple-600" => (147, 51, 234),
488    "purple-700" => (126, 34, 206),
489    "purple-800" => (107, 33, 168),
490    "purple-900" => (88, 28, 135),
491    "fuchsia-50" => (253, 244, 255),
492    "fuchsia-100" => (250, 232, 255),
493    "fuchsia-200" => (245, 208, 254),
494    "fuchsia-300" => (240, 171, 252),
495    "fuchsia-400" => (232, 121, 249),
496    "fuchsia-500" => (217, 70, 239),
497    "fuchsia-600" => (192, 38, 211),
498    "fuchsia-700" => (162, 28, 175),
499    "fuchsia-800" => (134, 25, 143),
500    "fuchsia-900" => (112, 26, 117),
501    "pink-50" => (253, 242, 248),
502    "pink-100" => (252, 231, 243),
503    "pink-200" => (251, 207, 232),
504    "pink-300" => (249, 168, 212),
505    "pink-400" => (244, 114, 182),
506    "pink-500" => (236, 72, 153),
507    "pink-600" => (219, 39, 119),
508    "pink-700" => (190, 24, 93),
509    "pink-800" => (157, 23, 77),
510    "pink-900" => (131, 24, 67),
511    "rose-50" => (255, 241, 242),
512    "rose-100" => (255, 228, 230),
513    "rose-200" => (254, 205, 211),
514    "rose-300" => (253, 164, 175),
515    "rose-400" => (251, 113, 133),
516    "rose-500" => (244, 63, 94),
517    "rose-600" => (225, 29, 72),
518    "rose-700" => (190, 18, 60),
519    "rose-800" => (159, 18, 57),
520    "rose-900" => (136, 19, 55),
521};
522
523/// The list of all default screen breakpoints.
524///
525/// Based on [Tailwind's default screen breakpoints](https://tailwindcss.com/docs/screens).
526pub const BUILTIN_SCREENS: &[(&str, &str)] = &[
527    ("sm", "640px"),
528    ("md", "768px"),
529    ("lg", "1024px"),
530    ("xl", "1280px"),
531    ("2xl", "1536px"),
532];
533
534/// The list of all default variants (sorted following their importance in the CSS file).
535///
536/// - `first-letter`: applies the [`first-letter`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter) pseudo element
537/// - `first-line`: applies the [`first-line`](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-line) pseudo element
538/// - `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**
539/// - `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**
540/// - `file`: applies the [`file-selector-button`](https://developer.mozilla.org/en-US/docs/Web/CSS/::file-selector-button) pseudo element
541/// - `placeholder`: applies the [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/CSS/::placeholder) pseudo element
542/// - `backdrop`: applies the [`backdrop`](https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop) pseudo element
543/// - `before`: applies the [`before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) pseudo element
544/// - `after`: applies the [`before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) pseudo element
545/// - `all`: selects all nested children instead of the element itself
546/// - `children`: selects all direct children (only one level deep) instead of the element itself
547/// - `siblings`: selects all siblings of the element using the [general sibling combinator `~`](https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator)
548/// - `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)
549/// - `first`: applies the [`first-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child) pseudo class
550/// - `not-first`: opposite of `first`, applies `:not(:first-child)`
551/// - `last`: applies the [`last-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child) pseudo class
552/// - `not-last`: opposite of `last`, applies `:not(:last-child)`
553/// - `only`: applies the [`only-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child) pseudo class
554/// - `not-only`: opposite of `only`, applies `:not(:only-child)`
555/// - `odd`: applies the [`nth-child(odd)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) pseudo class
556/// - `even`: opposite of `odd`, applies the [`nth-child(even)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) pseudo class
557/// - `first-of-type`: applies the [`first-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type) pseudo class
558/// - `last-of-type`: applies the [`last-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type) pseudo class
559/// - `only-of-type`: applies the [`only-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type) pseudo class
560/// - `not-first-of-type`: opposite of `first-of-type`, applies `:not(:first-of-type)`
561/// - `not-last-of-type`: opposite of `last-of-type`, applies `:not(:last-of-type)`
562/// - `not-only-of-type`: opposite of `only-of-type`, applies `:not(:only-of-type)`
563/// - `visited`: applies the [`visited`](https://developer.mozilla.org/en-US/docs/Web/CSS/:visited) pseudo class
564/// - `target`: applies the [`target`](https://developer.mozilla.org/en-US/docs/Web/CSS/:target) pseudo class
565/// - `open`: selects all elements having the `open` attributes, useful for [`<dialog>` elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
566/// - `default`: applies the [`default`](https://developer.mozilla.org/en-US/docs/Web/CSS/:default) pseudo class
567/// - `checked`: applies the [`checked`](https://developer.mozilla.org/en-US/docs/Web/CSS/:checked) pseudo class
568/// - `not-checked`: opposite of `checked`, applies `:not(:checked)`
569/// - `indeterminate`: applies the [`indeterminate`](https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate) pseudo class
570/// - `placeholder-shown`: applies the [`placeholder-shown`](https://developer.mozilla.org/en-US/docs/Web/CSS/:placeholder-shown) pseudo class
571/// - `autofill`: applies the [`autofill`](https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill) pseudo class
572/// - `optional`: applies the [`optional`](https://developer.mozilla.org/en-US/docs/Web/CSS/:optional) pseudo class
573/// - `required`: applies the [`required`](https://developer.mozilla.org/en-US/docs/Web/CSS/:required) pseudo class
574/// - `valid`: applies the [`valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:valid) pseudo class
575/// - `invalid`: applies the [`invalid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid) pseudo class
576/// - `in-range`: applies the [`in-range`](https://developer.mozilla.org/en-US/docs/Web/CSS/:in-range) pseudo class
577/// - `out-of-range`: applies the [`out-of-range`](https://developer.mozilla.org/en-US/docs/Web/CSS/:out-of-range) pseudo class
578/// - `read-only`: applies the [`read-only`](https://developer.mozilla.org/en-US/docs/Web/CSS/:read-only) pseudo class
579/// - `read-write`: applies the [`read-write`](https://developer.mozilla.org/en-US/docs/Web/CSS/:read-write) pseudo class
580/// - `empty`: applies the [`empty`](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty) pseudo class
581/// - `focus-within`: applies the [`focus-within`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within) pseudo class
582/// - `hover`: applies the [`hover`](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover) pseudo class
583/// - `focus`: applies the [`focus`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus) pseudo class
584/// - `focus-visible`: applies the [`focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible) pseudo class
585/// - `active`: applies the [`active`](https://developer.mozilla.org/en-US/docs/Web/CSS/:active) pseudo class
586/// - `enabled`: applies the [`enabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:enabled) pseudo class
587/// - `disabled`: applies the [`disabled`](https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled) pseudo class
588/// - `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
589/// - `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
590/// - `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
591/// - `motion-reduce`: applies the [`@media (prefers-reduced-motion: reduce)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) at-rule
592/// - `print`: applies the [`@media print`](https://developer.mozilla.org/en-US/docs/Web/Guide/Printing#using_media_queries_to_improve_layout) at-rule
593/// - `portrait`: applies the [`@media (orientation: portrait)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation) at-rule
594/// - `landscape`: applies the [`@media (orientation: landscape)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/orientation) at-rule
595/// - `contrast-more`: applies the [`@media (prefers-contrast: more)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) at-rule
596/// - `contrast-less`: applies the [`@media (prefers-contrast: less)`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) at-rule
597/// - `aria-busy`: selects all elements having the [`aria-busy="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-busy)
598/// - `aria-checked`: selects all elements having the [`aria-checked="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked)
599/// - `aria-disabled`: selects all elements having the [`aria-disabled="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled)
600/// - `aria-expanded`: selects all elements having the [`aria-expanded="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-expanded)
601/// - `aria-hidden`: selects all elements having the [`aria-hidden="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden)
602/// - `aria-pressed`: selects all elements having the [`aria-pressed="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
603/// - `aria-readonly`: selects all elements having the [`aria-readonly="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-readonly)
604/// - `aria-required`: selects all elements having the [`aria-required="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-required)
605/// - `aria-selected`: selects all elements having the [`aria-selected="true"` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-selected)
606///
607/// Besides the default variants, some others are auto-generated from the configuration like
608/// breakpoints (see [`BUILTIN_SCREENS`]) and the dark mode (`dark:` variant).
609///
610/// Based on [Tailwind's default variants](https://tailwindcss.com/docs/hover-focus-and-other-states).
611#[rustfmt::skip]
612pub const BUILTIN_VARIANTS: phf::Map<&'static str, (usize, VariantType)> = phf_map! {
613    // --- Pseudo element ---
614    "first-letter" => (0, VariantType::PseudoElement("first-letter")),
615    "first-line" => (1, VariantType::PseudoElement("first-line")),
616    "marker" => (2, VariantType::WrapClass(Cow::Borrowed("& *::marker, &::marker"))),
617    "selection" => (3, VariantType::WrapClass(Cow::Borrowed("& *::selection, &::selection"))),
618    "file" => (4, VariantType::WrapClass(Cow::Borrowed("&::file-selector-button, &::-webkit-file-upload-button"))),
619    "placeholder" => (5, VariantType::PseudoElement("placeholder")),
620    "backdrop" => (6, VariantType::PseudoElement("backdrop")),
621    "before" => (7, VariantType::PseudoElement("before")),
622    "after" => (8, VariantType::PseudoElement("after")),
623    "all" => (9, VariantType::WrapClass(Cow::Borrowed("& *"))),
624    "children" => (10, VariantType::WrapClass(Cow::Borrowed("& > *"))),
625    "siblings" => (11, VariantType::WrapClass(Cow::Borrowed("& ~ *"))),
626    "sibling" => (12, VariantType::WrapClass(Cow::Borrowed("& + *"))),
627
628    // --- Pseudo class ---
629    "first" => (13, VariantType::PseudoClass("first-child")),
630    "not-first" => (14, VariantType::PseudoClass("not(:first-child)")),
631    "last" => (16, VariantType::PseudoClass("last-child")),
632    "not-last" => (17, VariantType::PseudoClass("not(:last-child)")),
633    "only" => (18, VariantType::PseudoClass("only-child")),
634    "not-only" => (18, VariantType::PseudoClass("not(:only-child)")),
635    "odd" => (19, VariantType::PseudoClass("nth-child(odd)")),
636    "even" => (20, VariantType::PseudoClass("nth-child(even)")),
637    "first-of-type" => (21, VariantType::PseudoClass("first-of-type")),
638    "last-of-type" => (22, VariantType::PseudoClass("last-of-type")),
639    "only-of-type" => (23, VariantType::PseudoClass("only-of-type")),
640    "not-first-of-type" => (24, VariantType::PseudoClass("not(:first-of-type)")),
641    "not-last-of-type" => (25, VariantType::PseudoClass("not(:last-of-type)")),
642    "not-only-of-type" => (26, VariantType::PseudoClass("not(:only-of-type)")),
643    "visited" => (27, VariantType::PseudoClass("visited")),
644    "target" => (28, VariantType::PseudoClass("target")),
645    "open" => (29, VariantType::WrapClass(Cow::Borrowed("&[open]"))),
646    "default" => (30, VariantType::PseudoClass("default")),
647    "checked" => (31, VariantType::PseudoClass("checked")),
648    "not-checked" => (32, VariantType::PseudoClass("not(:checked)")),
649    "indeterminate" => (33, VariantType::PseudoClass("indeterminate")),
650    "placeholder-shown" => (34, VariantType::PseudoClass("placeholder-shown")),
651    "autofill" => (35, VariantType::PseudoClass("autofill")),
652    "optional" => (36, VariantType::PseudoClass("optional")),
653    "required" => (37, VariantType::PseudoClass("required")),
654    "valid" => (38, VariantType::PseudoClass("valid")),
655    "invalid" => (39, VariantType::PseudoClass("invalid")),
656    "in-range" => (40, VariantType::PseudoClass("in-range")),
657    "out-of-range" => (41, VariantType::PseudoClass("out-of-range")),
658    "read-only" => (42, VariantType::PseudoClass("read-only")),
659    "read-write" => (43, VariantType::PseudoClass("read-write")),
660    "empty" => (44, VariantType::PseudoClass("empty")),
661    "focus-within" => (45, VariantType::PseudoClass("focus-within")),
662    "hover" => (46, VariantType::PseudoClass("hover")),
663    "focus" => (47, VariantType::PseudoClass("focus")),
664    "focus-visible" => (48, VariantType::PseudoClass("focus-visible")),
665    "active" => (49, VariantType::PseudoClass("active")),
666    "enabled" => (50, VariantType::PseudoClass("enabled")),
667    "disabled" => (51, VariantType::PseudoClass("disabled")),
668    "ltr" => (52, VariantType::WrapClass(Cow::Borrowed("[dir=\"ltr\"] &"))),
669    "rtl" => (53, VariantType::WrapClass(Cow::Borrowed("[dir=\"rtl\"] &"))),
670
671    // --- At rules ---
672    "motion-safe" => (54, VariantType::AtRule(Cow::Borrowed("@media (prefers-reduced-motion: no-preference)"))),
673    "motion-reduce" => (55, VariantType::AtRule(Cow::Borrowed("@media (prefers-reduced-motion: reduce)"))),
674    "print" => (56, VariantType::AtRule(Cow::Borrowed("@media print"))),
675    "portrait" => (57, VariantType::AtRule(Cow::Borrowed("@media (orientation: portrait)"))),
676    "landscape" => (58, VariantType::AtRule(Cow::Borrowed("@media (orientation: landscape)"))),
677    "contrast-more" => (59, VariantType::AtRule(Cow::Borrowed("@media (prefers-contrast: more)"))),
678    "contrast-less" => (60, VariantType::AtRule(Cow::Borrowed("@media (prefers-contrast: less)"))),
679
680    // --- ARIA states ---
681    "aria-busy" => (61, VariantType::WrapClass(Cow::Borrowed("&[aria-busy=\"true\"]"))),
682    "aria-checked" => (62, VariantType::WrapClass(Cow::Borrowed("&[aria-checked=\"true\"]"))),
683    "aria-disabled" => (63, VariantType::WrapClass(Cow::Borrowed("&[aria-disabled=\"true\"]"))),
684    "aria-expanded" => (64, VariantType::WrapClass(Cow::Borrowed("&[aria-expanded=\"true\"]"))),
685    "aria-hidden" => (65, VariantType::WrapClass(Cow::Borrowed("&[aria-hidden=\"true\"]"))),
686    "aria-pressed" => (66, VariantType::WrapClass(Cow::Borrowed("&[aria-pressed=\"true\"]"))),
687    "aria-readonly" => (67, VariantType::WrapClass(Cow::Borrowed("&[aria-readonly=\"true\"]"))),
688    "aria-required" => (68, VariantType::WrapClass(Cow::Borrowed("&[aria-required=\"true\"]"))),
689    "aria-selected" => (69, VariantType::WrapClass(Cow::Borrowed("&[aria-selected=\"true\"]"))),
690};
691
692/// The list of all default plugins.
693///
694/// Sorted following [Tailwind's order](https://github.com/tailwindlabs/tailwindcss/blob/master/src/corePlugins.js).
695#[rustfmt::skip]
696pub const BUILTIN_PLUGINS: &[(Cow<'static, str>, &'static (dyn Plugin + Send + Sync))] = &[
697    (Cow::Borrowed("container"), &layout::container::PluginDefinition),
698    (Cow::Borrowed(""), &accessibility::screen_reader::PluginDefinition),
699    (Cow::Borrowed("pointer-events"), &interactivity::pointer_events::PluginDefinition),
700    (Cow::Borrowed(""), &layout::visibility::PluginDefinition),
701    (Cow::Borrowed(""), &layout::position::PluginDefinition),
702    (Cow::Borrowed("inset"), &layout::placement::PluginInsetDefinition),
703    (Cow::Borrowed("inset-x"), &layout::placement::PluginInsetXDefinition),
704    (Cow::Borrowed("inset-y"), &layout::placement::PluginInsetYDefinition),
705    (Cow::Borrowed("start"), &layout::placement::PluginStartDefinition),
706    (Cow::Borrowed("end"), &layout::placement::PluginEndDefinition),
707    (Cow::Borrowed("top"), &layout::placement::PluginTopDefinition),
708    (Cow::Borrowed("right"), &layout::placement::PluginRightDefinition),
709    (Cow::Borrowed("bottom"), &layout::placement::PluginBottomDefinition),
710    (Cow::Borrowed("left"), &layout::placement::PluginLeftDefinition),
711    (Cow::Borrowed(""), &layout::isolation::PluginDefinition),
712    (Cow::Borrowed("z"), &layout::z_index::PluginDefinition),
713    (Cow::Borrowed("order"), &flexbox::order::PluginDefinition),
714    (Cow::Borrowed("col"), &grid::grid_column::PluginDefinition),
715    (Cow::Borrowed("row"), &grid::grid_row::PluginDefinition),
716    (Cow::Borrowed("float"), &layout::floats::PluginDefinition),
717    (Cow::Borrowed("clear"), &layout::clear::PluginDefinition),
718    (Cow::Borrowed("m"), &spacing::margin::PluginDefinition),
719    (Cow::Borrowed("mx"), &spacing::margin::PluginXDefinition),
720    (Cow::Borrowed("my"), &spacing::margin::PluginYDefinition),
721    (Cow::Borrowed("ms"), &spacing::margin::PluginStartDefinition),
722    (Cow::Borrowed("me"), &spacing::margin::PluginEndDefinition),
723    (Cow::Borrowed("mt"), &spacing::margin::PluginTopDefinition),
724    (Cow::Borrowed("mr"), &spacing::margin::PluginRightDefinition),
725    (Cow::Borrowed("mb"), &spacing::margin::PluginBottomDefinition),
726    (Cow::Borrowed("ml"), &spacing::margin::PluginLeftDefinition),
727    (Cow::Borrowed("box"), &layout::box_sizing::PluginDefinition),
728    (Cow::Borrowed(""), &layout::display::PluginDefinition),
729    (Cow::Borrowed("aspect"), &layout::aspect_ratio::PluginDefinition),
730    (Cow::Borrowed("h"), &sizing::height::PluginDefinition),
731    (Cow::Borrowed("max-h"), &sizing::max_height::PluginDefinition),
732    (Cow::Borrowed("min-h"), &sizing::min_height::PluginDefinition),
733    (Cow::Borrowed("w"), &sizing::width::PluginDefinition),
734    (Cow::Borrowed("min-w"), &sizing::min_width::PluginDefinition),
735    (Cow::Borrowed("max-w"), &sizing::max_width::PluginDefinition),
736    (Cow::Borrowed("flex"), &flexbox::flex::PluginDefinition),
737    (Cow::Borrowed("shrink"), &flexbox::flex_shrink::PluginDefinition),
738    (Cow::Borrowed("grow"), &flexbox::flex_grow::PluginDefinition),
739    (Cow::Borrowed("basis"), &flexbox::flex_basis::PluginDefinition),
740    (Cow::Borrowed("table"), &table::table_layout::PluginDefinition),
741    (Cow::Borrowed("caption"), &table::caption_side::PluginDefinition),
742    (Cow::Borrowed("border"), &table::border_collapse::PluginDefinition),
743    (Cow::Borrowed("border-spacing"), &table::border_spacing::PluginDefinition),
744    (Cow::Borrowed("border-spacing-x"), &table::border_spacing::PluginXDefinition),
745    (Cow::Borrowed("border-spacing-y"), &table::border_spacing::PluginYDefinition),
746    (Cow::Borrowed("origin"), &transform::transform_origin::PluginDefinition),
747    (Cow::Borrowed("translate-x"), &transform::translate::PluginXDefinition),
748    (Cow::Borrowed("translate-y"), &transform::translate::PluginYDefinition),
749    (Cow::Borrowed("rotate"), &transform::rotate::PluginDefinition),
750    (Cow::Borrowed("skew-x"), &transform::skew::PluginXDefinition),
751    (Cow::Borrowed("skew-y"), &transform::skew::PluginYDefinition),
752    (Cow::Borrowed("scale"), &transform::scale::PluginDefinition),
753    (Cow::Borrowed("scale-x"), &transform::scale::PluginXDefinition),
754    (Cow::Borrowed("scale-y"), &transform::scale::PluginYDefinition),
755    (Cow::Borrowed("transform"), &transform::transform_type::PluginDefinition),
756    (Cow::Borrowed("animate"), &transition::animation::PluginDefinition),
757    (Cow::Borrowed("cursor"), &interactivity::cursor::PluginDefinition),
758    (Cow::Borrowed("touch"), &interactivity::touch_action::PluginDefinition),
759    (Cow::Borrowed("select"), &interactivity::user_select::PluginDefinition),
760    (Cow::Borrowed("resize"), &interactivity::resize::PluginDefinition),
761    (Cow::Borrowed("snap"), &interactivity::scroll_snap_type::PluginDefinition),
762    (Cow::Borrowed("snap"), &interactivity::scroll_snap_align::PluginDefinition),
763    (Cow::Borrowed("snap"), &interactivity::scroll_snap_stop::PluginDefinition),
764    (Cow::Borrowed("scroll-m"), &interactivity::scroll_margin::PluginDefinition),
765    (Cow::Borrowed("scroll-mx"), &interactivity::scroll_margin::PluginXDefinition),
766    (Cow::Borrowed("scroll-my"), &interactivity::scroll_margin::PluginYDefinition),
767    (Cow::Borrowed("scroll-ms"), &interactivity::scroll_margin::PluginStartDefinition),
768    (Cow::Borrowed("scroll-me"), &interactivity::scroll_margin::PluginEndDefinition),
769    (Cow::Borrowed("scroll-mt"), &interactivity::scroll_margin::PluginTopDefinition),
770    (Cow::Borrowed("scroll-mr"), &interactivity::scroll_margin::PluginRightDefinition),
771    (Cow::Borrowed("scroll-mb"), &interactivity::scroll_margin::PluginBottomDefinition),
772    (Cow::Borrowed("scroll-ml"), &interactivity::scroll_margin::PluginLeftDefinition),
773    (Cow::Borrowed("scroll-p"), &interactivity::scroll_padding::PluginDefinition),
774    (Cow::Borrowed("scroll-px"), &interactivity::scroll_padding::PluginXDefinition),
775    (Cow::Borrowed("scroll-py"), &interactivity::scroll_padding::PluginYDefinition),
776    (Cow::Borrowed("scroll-ps"), &interactivity::scroll_padding::PluginStartDefinition),
777    (Cow::Borrowed("scroll-pe"), &interactivity::scroll_padding::PluginEndDefinition),
778    (Cow::Borrowed("scroll-pt"), &interactivity::scroll_padding::PluginTopDefinition),
779    (Cow::Borrowed("scroll-pr"), &interactivity::scroll_padding::PluginRightDefinition),
780    (Cow::Borrowed("scroll-pb"), &interactivity::scroll_padding::PluginBottomDefinition),
781    (Cow::Borrowed("scroll-pl"), &interactivity::scroll_padding::PluginLeftDefinition),
782    (Cow::Borrowed("list"), &typography::list_style_position::PluginDefinition),
783    (Cow::Borrowed("list"), &typography::list_style_type::PluginDefinition),
784    (Cow::Borrowed("appearance"), &interactivity::appearance::PluginDefinition),
785    (Cow::Borrowed("columns"), &layout::columns::PluginDefinition),
786    (Cow::Borrowed("break-before"), &layout::break_before::PluginDefinition),
787    (Cow::Borrowed("break-inside"), &layout::break_inside::PluginDefinition),
788    (Cow::Borrowed("break-after"), &layout::break_after::PluginDefinition),
789    (Cow::Borrowed("auto-cols"), &grid::grid_auto_columns::PluginDefinition),
790    (Cow::Borrowed("grid-flow"), &grid::grid_auto_flow::PluginDefinition),
791    (Cow::Borrowed("auto-rows"), &grid::grid_auto_rows::PluginDefinition),
792    (Cow::Borrowed("grid-cols"), &grid::grid_template_columns::PluginDefinition),
793    (Cow::Borrowed("grid-rows"), &grid::grid_template_rows::PluginDefinition),
794    (Cow::Borrowed("flex"), &flexbox::flex_direction::PluginDefinition),
795    (Cow::Borrowed("flex"), &flexbox::flex_wrap::PluginDefinition),
796    (Cow::Borrowed("place-content"), &flexbox::place_content::PluginDefinition),
797    (Cow::Borrowed("place-items"), &flexbox::place_items::PluginDefinition),
798    (Cow::Borrowed("content"), &flexbox::align_content::PluginDefinition),
799    (Cow::Borrowed("items"), &flexbox::align_items::PluginDefinition),
800    (Cow::Borrowed("justify"), &flexbox::justify_content::PluginDefinition),
801    (Cow::Borrowed("justify-items"), &flexbox::justify_items::PluginDefinition),
802    (Cow::Borrowed("gap"), &grid::gap::PluginDefinition),
803    (Cow::Borrowed("gap-x"), &grid::gap::PluginXDefinition),
804    (Cow::Borrowed("gap-y"), &grid::gap::PluginYDefinition),
805    (Cow::Borrowed("space-x"), &spacing::space_between::PluginXDefinition),
806    (Cow::Borrowed("space-y"), &spacing::space_between::PluginYDefinition),
807    (Cow::Borrowed("divide-x"), &border::divide_width::PluginXDefinition),
808    (Cow::Borrowed("divide-y"), &border::divide_width::PluginYDefinition),
809    (Cow::Borrowed("divide"), &border::divide_style::PluginDefinition),
810    (Cow::Borrowed("divide"), &border::divide_color::PluginDefinition),
811    (Cow::Borrowed("divide-opacity"), &border::divide_opacity::PluginDefinition),
812    (Cow::Borrowed("place-self"), &flexbox::place_self::PluginDefinition),
813    (Cow::Borrowed("self"), &flexbox::align_self::PluginDefinition),
814    (Cow::Borrowed("justify-self"), &flexbox::justify_self::PluginDefinition),
815    (Cow::Borrowed("overflow"), &layout::overflow::PluginDefinition),
816    (Cow::Borrowed("overscroll"), &layout::overscroll_behavior::PluginDefinition),
817    (Cow::Borrowed("scroll"), &interactivity::scroll_behavior::PluginDefinition),
818    (Cow::Borrowed(""), &typography::text_overflow::PluginDefinition),
819    (Cow::Borrowed("whitespace"), &typography::whitespace::PluginDefinition),
820    (Cow::Borrowed("text"), &typography::text_wrap::PluginDefinition),
821    (Cow::Borrowed("break"), &typography::word_break::PluginDefinition),
822    (Cow::Borrowed("rounded"), &border::border_radius::PluginDefinition),
823    (Cow::Borrowed("rounded-s"), &border::border_radius::PluginStartDefinition),
824    (Cow::Borrowed("rounded-e"), &border::border_radius::PluginEndDefinition),
825    (Cow::Borrowed("rounded-t"), &border::border_radius::PluginTopDefinition),
826    (Cow::Borrowed("rounded-r"), &border::border_radius::PluginRightDefinition),
827    (Cow::Borrowed("rounded-b"), &border::border_radius::PluginBottomDefinition),
828    (Cow::Borrowed("rounded-l"), &border::border_radius::PluginLeftDefinition),
829    (Cow::Borrowed("rounded-ss"), &border::border_radius::PluginStartStartDefinition),
830    (Cow::Borrowed("rounded-se"), &border::border_radius::PluginStartEndDefinition),
831    (Cow::Borrowed("rounded-ee"), &border::border_radius::PluginEndEndDefinition),
832    (Cow::Borrowed("rounded-es"), &border::border_radius::PluginEndStartDefinition),
833    (Cow::Borrowed("rounded-tr"), &border::border_radius::PluginTopRightDefinition),
834    (Cow::Borrowed("rounded-tl"), &border::border_radius::PluginTopLeftDefinition),
835    (Cow::Borrowed("rounded-br"), &border::border_radius::PluginBottomRightDefinition),
836    (Cow::Borrowed("rounded-bl"), &border::border_radius::PluginBottomLeftDefinition),
837    (Cow::Borrowed("border"), &border::border_width::PluginDefinition),
838    (Cow::Borrowed("border-x"), &border::border_width::PluginXDefinition),
839    (Cow::Borrowed("border-y"), &border::border_width::PluginYDefinition),
840    (Cow::Borrowed("border-s"), &border::border_width::PluginStartDefinition),
841    (Cow::Borrowed("border-e"), &border::border_width::PluginEndDefinition),
842    (Cow::Borrowed("border-t"), &border::border_width::PluginTopDefinition),
843    (Cow::Borrowed("border-r"), &border::border_width::PluginRightDefinition),
844    (Cow::Borrowed("border-b"), &border::border_width::PluginBottomDefinition),
845    (Cow::Borrowed("border-l"), &border::border_width::PluginLeftDefinition),
846    (Cow::Borrowed("border"), &border::border_style::PluginDefinition),
847    (Cow::Borrowed("border"), &border::border_color::PluginDefinition),
848    (Cow::Borrowed("border-x"), &border::border_color::PluginXDefinition),
849    (Cow::Borrowed("border-y"), &border::border_color::PluginYDefinition),
850    (Cow::Borrowed("border-s"), &border::border_color::PluginStartDefinition),
851    (Cow::Borrowed("border-e"), &border::border_color::PluginEndDefinition),
852    (Cow::Borrowed("border-t"), &border::border_color::PluginTopDefinition),
853    (Cow::Borrowed("border-r"), &border::border_color::PluginRightDefinition),
854    (Cow::Borrowed("border-b"), &border::border_color::PluginBottomDefinition),
855    (Cow::Borrowed("border-l"), &border::border_color::PluginLeftDefinition),
856    (Cow::Borrowed("border-opacity"), &border::border_opacity::PluginDefinition),
857    (Cow::Borrowed("bg"), &background::background_color::PluginDefinition),
858    (Cow::Borrowed("bg-opacity"), &background::background_opacity::PluginDefinition),
859    (Cow::Borrowed("bg"), &background::background_image::PluginDefinition),
860    (Cow::Borrowed("from"), &background::gradient_color_stops::PluginFromDefinition),
861    (Cow::Borrowed("via"), &background::gradient_color_stops::PluginViaDefinition),
862    (Cow::Borrowed("to"), &background::gradient_color_stops::PluginToDefinition),
863    (Cow::Borrowed("box-decoration"), &layout::box_decoration_break::PluginDefinition),
864    (Cow::Borrowed("bg"), &background::background_size::PluginDefinition),
865    (Cow::Borrowed("bg"), &background::background_attachment::PluginDefinition),
866    (Cow::Borrowed("bg-clip"), &background::background_clip::PluginDefinition),
867    (Cow::Borrowed("bg"), &background::background_position::PluginDefinition),
868    (Cow::Borrowed("bg"), &background::background_repeat::PluginDefinition),
869    (Cow::Borrowed("bg-origin"), &background::background_origin::PluginDefinition),
870    (Cow::Borrowed("fill"), &svg::fill::PluginDefinition),
871    (Cow::Borrowed("stroke"), &svg::stroke::PluginDefinition),
872    (Cow::Borrowed("stroke"), &svg::stroke_width::PluginDefinition),
873    (Cow::Borrowed("object"), &layout::object_fit::PluginDefinition),
874    (Cow::Borrowed("object"), &layout::object_position::PluginDefinition),
875    (Cow::Borrowed("p"), &spacing::padding::PluginDefinition),
876    (Cow::Borrowed("px"), &spacing::padding::PluginXDefinition),
877    (Cow::Borrowed("py"), &spacing::padding::PluginYDefinition),
878    (Cow::Borrowed("ps"), &spacing::padding::PluginStartDefinition),
879    (Cow::Borrowed("pe"), &spacing::padding::PluginEndDefinition),
880    (Cow::Borrowed("pt"), &spacing::padding::PluginTopDefinition),
881    (Cow::Borrowed("pr"), &spacing::padding::PluginRightDefinition),
882    (Cow::Borrowed("pb"), &spacing::padding::PluginBottomDefinition),
883    (Cow::Borrowed("pl"), &spacing::padding::PluginLeftDefinition),
884    (Cow::Borrowed("text"), &typography::text_align::PluginDefinition),
885    (Cow::Borrowed("indent"), &typography::text_indent::PluginDefinition),
886    (Cow::Borrowed("align"), &typography::vertical_align::PluginDefinition),
887    (Cow::Borrowed("font"), &typography::font_family::PluginDefinition),
888    (Cow::Borrowed("text"), &typography::font_size::PluginDefinition),
889    (Cow::Borrowed("font"), &typography::font_weight::PluginDefinition),
890    (Cow::Borrowed(""), &typography::text_transform::PluginDefinition),
891    (Cow::Borrowed(""), &typography::font_style::PluginDefinition),
892    (Cow::Borrowed(""), &typography::font_variant_numeric::PluginDefinition),
893    (Cow::Borrowed("tracking"), &typography::letter_spacing::PluginDefinition),
894    (Cow::Borrowed("leading"), &typography::line_height::PluginDefinition),
895    (Cow::Borrowed("text"), &typography::text_color::PluginDefinition),
896    (Cow::Borrowed("text-opacity"), &typography::text_opacity::PluginDefinition),
897    (Cow::Borrowed(""), &typography::text_decoration::PluginDefinition),
898    (Cow::Borrowed("decoration"), &typography::text_decoration_color::PluginDefinition),
899    (Cow::Borrowed("decoration"), &typography::text_decoration_style::PluginDefinition),
900    (Cow::Borrowed("decoration"), &typography::text_decoration_thickness::PluginDefinition),
901    (Cow::Borrowed("underline-offset"), &typography::text_underline_offset::PluginDefinition),
902    (Cow::Borrowed(""), &typography::font_smoothing::PluginDefinition),
903    (Cow::Borrowed("caret"), &interactivity::caret_color::PluginDefinition),
904    (Cow::Borrowed("accent"), &interactivity::accent_color::PluginDefinition),
905    (Cow::Borrowed("opacity"), &effect::opacity::PluginDefinition),
906    (Cow::Borrowed("bg-blend"), &effect::background_blend_mode::PluginDefinition),
907    (Cow::Borrowed("mix-blend"), &effect::mix_blend_mode::PluginDefinition),
908    (Cow::Borrowed("shadow"), &effect::box_shadow::PluginDefinition),
909    (Cow::Borrowed("shadow"), &effect::box_shadow_color::PluginDefinition),
910    (Cow::Borrowed("outline"), &border::outline_style::PluginDefinition),
911    (Cow::Borrowed("outline"), &border::outline_width::PluginDefinition),
912    (Cow::Borrowed("outline-offset"), &border::outline_offset::PluginDefinition),
913    (Cow::Borrowed("outline"), &border::outline_color::PluginDefinition),
914    (Cow::Borrowed("ring"), &border::ring_width::PluginDefinition),
915    (Cow::Borrowed("ring"), &border::ring_color::PluginDefinition),
916    (Cow::Borrowed("ring-opacity"), &border::ring_opacity::PluginDefinition),
917    (Cow::Borrowed("ring-offset"), &border::ring_offset_width::PluginDefinition),
918    (Cow::Borrowed("ring-offset"), &border::ring_offset_color::PluginDefinition),
919    (Cow::Borrowed("blur"), &filter::blur::PluginDefinition),
920    (Cow::Borrowed("brightness"), &filter::brightness::PluginDefinition),
921    (Cow::Borrowed("contrast"), &filter::contrast::PluginDefinition),
922    (Cow::Borrowed("drop-shadow"), &filter::drop_shadow::PluginDefinition),
923    (Cow::Borrowed("grayscale"), &filter::grayscale::PluginDefinition),
924    (Cow::Borrowed("hue-rotate"), &filter::hue_rotate::PluginDefinition),
925    (Cow::Borrowed("invert"), &filter::invert::PluginDefinition),
926    (Cow::Borrowed("saturate"), &filter::saturate::PluginDefinition),
927    (Cow::Borrowed("sepia"), &filter::sepia::PluginDefinition),
928    (Cow::Borrowed("filter"), &filter::filter_type::PluginDefinition),
929    (Cow::Borrowed("backdrop-blur"), &filter::backdrop_blur::PluginDefinition),
930    (Cow::Borrowed("backdrop-brightness"), &filter::backdrop_brightness::PluginDefinition),
931    (Cow::Borrowed("backdrop-contrast"), &filter::backdrop_contrast::PluginDefinition),
932    (Cow::Borrowed("backdrop-grayscale"), &filter::backdrop_grayscale::PluginDefinition),
933    (Cow::Borrowed("backdrop-hue-rotate"), &filter::backdrop_hue_rotate::PluginDefinition),
934    (Cow::Borrowed("backdrop-invert"), &filter::backdrop_invert::PluginDefinition),
935    (Cow::Borrowed("backdrop-opacity"), &filter::backdrop_opacity::PluginDefinition),
936    (Cow::Borrowed("backdrop-saturate"), &filter::backdrop_saturate::PluginDefinition),
937    (Cow::Borrowed("backdrop-sepia"), &filter::backdrop_sepia::PluginDefinition),
938    (Cow::Borrowed("backdrop-filter"), &filter::backdrop_filter::PluginDefinition),
939    (Cow::Borrowed("transition"), &transition::transition_property::PluginDefinition),
940    (Cow::Borrowed("delay"), &transition::transition_delay::PluginDefinition),
941    (Cow::Borrowed("duration"), &transition::transition_duration::PluginDefinition),
942    (Cow::Borrowed("ease"), &transition::transition_timing_function::PluginDefinition),
943    (Cow::Borrowed("will-change"), &interactivity::will_change::PluginDefinition),
944    (Cow::Borrowed("content"), &typography::content::PluginDefinition),
945    (Cow::Borrowed("line-clamp"), &typography::line_clamp::PluginDefinition),
946];
947
948/// Configuration for the [`Theme::dark_mode`] field.
949///
950/// It defines how the `dark:` variant should behave.
951#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
952#[serde(rename_all = "lowercase")]
953#[serde(tag = "type", content = "class")]
954pub enum DarkMode {
955    /// The `dark:` variant will modify the class of the selector. You'll then need to toggle this
956    /// class to enable the dark theme.
957    ///
958    /// # Example
959    ///
960    /// ```
961    /// use encre_css::{Config, config::DarkMode};
962    ///
963    /// let mut config = Config::default();
964    /// config.theme.dark_mode = DarkMode::new_class("body.dark");
965    ///
966    /// let generated = encre_css::generate(
967    ///     ["dark:text-white"],
968    ///     &config,
969    /// );
970    ///
971    /// assert!(generated.ends_with(r#"body.dark .dark\:text-white {
972    ///   --en-text-opacity: 1;
973    ///   color: rgb(255 255 255 / var(--en-text-opacity));
974    /// }"#));
975    /// ```
976    Class(Cow<'static, str>),
977
978    /// The `dark:` variant will generates a `@media (prefers-color-scheme: dark)` rule to enable
979    /// the dark theme following user preference.
980    ///
981    /// # Example
982    ///
983    /// ```
984    /// use encre_css::{Config, config::DarkMode};
985    ///
986    /// let mut config = Config::default();
987    /// config.theme.dark_mode = DarkMode::Media;
988    ///
989    /// let generated = encre_css::generate(
990    ///     ["dark:text-white"],
991    ///     &config,
992    /// );
993    ///
994    /// assert!(generated.ends_with(r#"@media (prefers-color-scheme: dark) {
995    ///   .dark\:text-white {
996    ///     --en-text-opacity: 1;
997    ///     color: rgb(255 255 255 / var(--en-text-opacity));
998    ///   }
999    /// }"#));
1000    /// ```
1001    Media,
1002}
1003
1004impl Default for DarkMode {
1005    fn default() -> Self {
1006        Self::Media
1007    }
1008}
1009
1010impl DarkMode {
1011    /// Quickly build a [`DarkMode::Class`] value.
1012    pub fn new_class<T: Into<Cow<'static, str>>>(class: T) -> Self {
1013        Self::Class(class.into())
1014    }
1015}
1016
1017/// Configuration for the [`Theme::aria`] field.
1018///
1019/// It defines a list of custom ARIA states.
1020#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1021pub struct Aria(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1022
1023impl Aria {
1024    /// Add an ARIA state to the list.
1025    #[inline]
1026    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1027        &mut self,
1028        key: T1,
1029        val: T2,
1030    ) {
1031        self.0.insert(key.into(), val.into());
1032    }
1033
1034    /// Remove an ARIA state from the list.
1035    #[inline]
1036    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1037        self.0.remove(&key.into());
1038    }
1039
1040    #[inline]
1041    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1042        self.0.iter()
1043    }
1044}
1045
1046/// Configuration for the [`Theme::screens`] field.
1047///
1048/// It defines a list of custom screen breakpoints.
1049#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1050pub struct Screens(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1051
1052impl Screens {
1053    /// Add a custom screen breakpoint to the list.
1054    #[inline]
1055    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1056        &mut self,
1057        key: T1,
1058        val: T2,
1059    ) {
1060        self.0.insert(key.into(), val.into());
1061    }
1062
1063    /// Remove a custom screen breakpoint from the list.
1064    #[inline]
1065    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1066        self.0.remove(&key.into());
1067    }
1068
1069    #[inline]
1070    pub(crate) fn iter(&self) -> impl Iterator<Item = (&Cow<'static, str>, &Cow<'static, str>)> {
1071        self.0.iter()
1072    }
1073}
1074
1075/// Configuration for the [`Theme::colors`] field.
1076///
1077/// It defines a list of custom colors.
1078#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1079pub struct Colors(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1080
1081impl Colors {
1082    /// Add a custom color to the list.
1083    #[inline]
1084    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1085        &mut self,
1086        key: T1,
1087        val: T2,
1088    ) {
1089        self.0.insert(key.into(), val.into());
1090    }
1091
1092    /// Remove a custom color from the list.
1093    #[inline]
1094    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1095        self.0.remove(&key.into());
1096    }
1097
1098    #[inline]
1099    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
1100        self.0.get(&key.into())
1101    }
1102
1103    #[inline]
1104    pub(crate) fn contains<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> bool {
1105        self.0.contains_key(&key.into())
1106    }
1107}
1108
1109/// Configuration for the [`Config::shortcuts`] field.
1110///
1111/// It defines a list of shortcuts used to combine several utility classes into one.
1112///
1113/// # Example
1114///
1115/// ```
1116/// use encre_css::Config;
1117///
1118/// let mut config = Config::default();
1119/// config.shortcuts.add("btn", "border-1 rounded-xl bg-red-500");
1120///
1121/// let generated = encre_css::generate(
1122///     [r#"<button class="btn">Click me</button>"#],
1123///     &config,
1124/// );
1125///
1126/// assert!(generated.ends_with(r#".btn {
1127///   border-radius: 0.75rem;
1128/// }
1129///
1130/// .btn {
1131///   border-width: 1px;
1132/// }
1133///
1134/// .btn {
1135///   --en-bg-opacity: 1;
1136///   background-color: rgb(239 68 68 / var(--en-bg-opacity));
1137/// }"#));
1138/// ```
1139#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1140pub struct Shortcuts(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
1141
1142impl Shortcuts {
1143    /// Add a shortcut to the list.
1144    #[inline]
1145    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<Cow<'static, str>>>(
1146        &mut self,
1147        key: T1,
1148        val: T2,
1149    ) {
1150        self.0.insert(key.into(), val.into());
1151    }
1152
1153    /// Remove a shortcut from the list.
1154    #[inline]
1155    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1156        self.0.remove(&key.into());
1157    }
1158
1159    #[inline]
1160    pub(crate) fn get<'a, T: Into<Cow<'a, str>>>(&self, key: T) -> Option<&Cow<'a, str>> {
1161        self.0.get(&key.into())
1162    }
1163}
1164
1165/// The maximum depth at which shortcuts will be resolved.
1166///
1167/// 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.
1168///
1169/// By default it is set to `5`.
1170#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
1171pub struct MaxShortcutDepth(usize);
1172
1173impl MaxShortcutDepth {
1174    /// Create a new `MaxShortcutDepth`.
1175    pub fn new(v: usize) -> Self {
1176        Self(v)
1177    }
1178
1179    /// Get the inner depth as an `usize`.
1180    pub fn get(&self) -> usize {
1181        self.0
1182    }
1183}
1184
1185impl From<usize> for MaxShortcutDepth {
1186    fn from(v: usize) -> Self {
1187        Self(v)
1188    }
1189}
1190
1191impl From<MaxShortcutDepth> for usize {
1192    fn from(val: MaxShortcutDepth) -> Self {
1193        val.0
1194    }
1195}
1196
1197impl Default for MaxShortcutDepth {
1198    fn default() -> Self {
1199        Self(5)
1200    }
1201}
1202
1203/// Configuration for the [`Config::safelist`] field.
1204///
1205/// It defines a list of selectors that are manually forced to be present in the generated CSS.
1206/// It should be used when you dynamically create selectors (for example, in Javascript
1207/// `text-${ active ? "blue" : "gray" }-400`, in this case, `text-blue-400` and `text-gray-400`
1208/// should be added to the safelist).
1209#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1210pub struct Safelist(BTreeSet<Cow<'static, str>>);
1211
1212impl Safelist {
1213    /// Add a selector to the safelist.
1214    #[inline]
1215    pub fn add<T: Into<Cow<'static, str>>>(&mut self, val: T) {
1216        self.0.insert(val.into());
1217    }
1218
1219    /// Remove a selector from the safelist.
1220    #[inline]
1221    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, val: T) {
1222        self.0.remove(&val.into());
1223    }
1224
1225    #[inline]
1226    pub(crate) fn iter(&self) -> impl Iterator<Item = &Cow<'static, str>> {
1227        self.0.iter()
1228    }
1229}
1230
1231/// Configuration for the [`Config::extra`] field.
1232///
1233/// It defines some extra fields that can be used to store arbitrary values usable in plugins.
1234/// The fields are represented as [`toml::Value`] to allow all types to be serialized.
1235/// It is recommended to use a table by plugin (e.g. the `encre-css-icons`'s plugin uses the
1236/// `icons` key containing a table grouping all configuration fields).
1237#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
1238pub struct Extra(BTreeMap<Cow<'static, str>, toml::Value>);
1239
1240impl Extra {
1241    /// Add an extra field.
1242    #[inline]
1243    pub fn add<T1: Into<Cow<'static, str>>, T2: Into<toml::Value>>(&mut self, key: T1, val: T2) {
1244        self.0.insert(key.into(), val.into());
1245    }
1246
1247    /// Remove an extra field.
1248    #[inline]
1249    pub fn remove<T: Into<Cow<'static, str>>>(&mut self, key: T) {
1250        self.0.remove(&key.into());
1251    }
1252
1253    /// Get the value of an extra field.
1254    #[inline]
1255    pub fn get<'a, T: Into<Cow<'a, str>>>(&'a self, key: T) -> Option<&'a toml::Value> {
1256        self.0.get(&key.into())
1257    }
1258}
1259
1260/// Configuration for the [`Config::theme`] field.
1261///
1262/// It defines some design system specific values like custom colors or screen breakpoints.
1263#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone)]
1264pub struct Theme {
1265    /// Dark mode configuration.
1266    ///
1267    /// The default value is [`DarkMode::Media`].
1268    #[serde(default)]
1269    pub dark_mode: DarkMode,
1270
1271    /// Custom screen breakpoints configuration.
1272    ///
1273    /// The default value is an empty map.
1274    #[serde(default)]
1275    pub screens: Screens,
1276
1277    /// Custom colors configuration.
1278    ///
1279    /// The default value is an empty map.
1280    #[serde(default)]
1281    pub colors: Colors,
1282
1283    /// Custom ARIA states.
1284    ///
1285    /// The default value is an empty map.
1286    #[serde(default)]
1287    pub aria: Aria,
1288}
1289
1290/// The configuration of the CSS generation done in the [`generate`] function.
1291///
1292/// You can create a configuration using one of the ways listed below:
1293///
1294/// - It can be the default one:
1295///
1296/// ```
1297/// use encre_css::Config;
1298///
1299/// let config = Config::default();
1300/// let _generated = encre_css::generate([], &config);
1301/// ```
1302///
1303/// - It can be a customized one:
1304///
1305/// ```
1306/// use encre_css::Config;
1307///
1308/// let mut config = Config::default();
1309/// config.theme.colors.add("flashy", "#ff2d20");
1310///
1311/// let _generated = encre_css::generate([], &config);
1312/// ```
1313///
1314/// - It can be loaded from a [TOML](https://toml.io) file:
1315///
1316/// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment"># encre-css.toml</span>
1317/// <span class="kw">[theme]</span>
1318/// dark_mode = { type = <span class="string">"class"</span>, class = <span class="string">".dark"</span> }
1319/// screens = { 3xl = <span class="string">"1600px"</span>, lg = <span class="string">"2000px"</span> }<br>
1320/// <span class="kw">[theme.colors]</span>
1321/// primary = <span class="string">"#e5186a"</span>
1322/// yellow-400 = <span class="string">"#ffef0e"</span></code></pre></div>
1323///
1324/// ```no_run
1325/// use encre_css::Config;
1326///
1327/// # fn main() -> encre_css::Result<()> {
1328/// let config = Config::from_file("encre-css.toml")?;
1329/// let _generated = encre_css::generate([], &config);
1330/// # Ok(())
1331/// # }
1332/// ```
1333///
1334/// Based on [Tailwind's configuration](https://tailwindcss.com/docs/configuration).
1335///
1336/// [`generate`]: crate::generate
1337#[derive(Default, Serialize, Deserialize, Clone)]
1338pub struct Config {
1339    /// Safelist configuration.
1340    #[serde(default)]
1341    pub safelist: Safelist,
1342
1343    /// Theme configuration.
1344    #[serde(default)]
1345    pub theme: Theme,
1346
1347    /// Preflight configuration.
1348    #[serde(default)]
1349    pub preflight: Preflight,
1350
1351    /// Shortcuts configuration.
1352    #[serde(default)]
1353    pub shortcuts: Shortcuts,
1354
1355    /// The maximum depth at which shortcuts will be resolved.
1356    #[serde(default)]
1357    pub max_shortcut_depth: MaxShortcutDepth,
1358
1359    /// Extra fields configuration.
1360    #[serde(default)]
1361    pub extra: Extra,
1362
1363    /// A custom scanner used to scan content.
1364    ///
1365    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1366    #[serde(skip)]
1367    pub scanner: Scanner,
1368
1369    /// A list of custom plugins.
1370    ///
1371    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1372    #[serde(skip)]
1373    pub(crate) custom_plugins: Vec<(Cow<'static, str>, &'static (dyn Plugin + Send + Sync))>,
1374
1375    /// A list of custom variants.
1376    ///
1377    /// This field is skipped when deserializing from a [TOML](https://toml.io) file.
1378    #[serde(skip)]
1379    pub(crate) custom_variants: Vec<(Cow<'static, str>, VariantType)>,
1380    // TODO: Prefix (en-)
1381}
1382
1383impl Config {
1384    /// Get variants derived from other configuration fields like breakpoints and the dark mode.
1385    pub(crate) fn get_derived_variants(&self) -> Vec<(Cow<'static, str>, VariantType)> {
1386        self.theme
1387            .screens
1388            .iter()
1389            .map(|screen| {
1390                (
1391                    screen.0.clone(),
1392                    VariantType::AtRule(Cow::Owned(format!("@media (min-width: {})", screen.1))),
1393                )
1394            })
1395            .chain(BUILTIN_SCREENS.iter().map(|screen| {
1396                (
1397                    Cow::from(screen.0),
1398                    VariantType::AtRule(Cow::Owned(format!("@media (min-width: {})", screen.1))),
1399                )
1400            }))
1401            .chain(self.theme.aria.iter().map(|aria| {
1402                (
1403                    Cow::from(format!("aria-{}", aria.0)),
1404                    VariantType::WrapClass(Cow::Owned(format!("&[aria-{}]", aria.1))),
1405                )
1406            }))
1407            .chain(iter::once(match &self.theme.dark_mode {
1408                DarkMode::Media => (
1409                    Cow::from("dark"),
1410                    VariantType::AtRule(Cow::from("@media (prefers-color-scheme: dark)")),
1411                ),
1412                DarkMode::Class(name) => (
1413                    Cow::from("dark"),
1414                    VariantType::WrapClass(name.clone() + " &"),
1415                ),
1416            }))
1417            .collect()
1418    }
1419
1420    /// Register a custom plugin which will be used during CSS generation.
1421    ///
1422    /// Note that if you are not the maintainer of a crate providing plugins, you can ignore this
1423    /// function, see [`crate::plugins`].
1424    ///
1425    /// # Example
1426    ///
1427    /// ```
1428    /// use encre_css::{Config, prelude::build_plugin::*};
1429    ///
1430    /// #[derive(Debug)]
1431    /// struct Prose;
1432    ///
1433    /// impl Plugin for Prose {
1434    ///     fn can_handle(&self, context: ContextCanHandle) -> bool {
1435    ///         matches!(context.modifier, Modifier::Builtin { value: "" | "invert", .. })
1436    ///     }
1437    ///
1438    ///     fn handle(&self, context: &mut ContextHandle) {
1439    ///         if let Modifier::Builtin { value, .. } = context.modifier {
1440    ///             match *value {
1441    ///                 "" => context.buffer.line("color: #333;"),
1442    ///                 "invert" => context.buffer.line("color: #eee;"),
1443    ///                 _ => unreachable!(),
1444    ///             }
1445    ///         }
1446    ///     }
1447    /// }
1448    ///
1449    /// let mut config = Config::default();
1450    /// config.register_plugin("prose", &Prose);
1451    ///
1452    /// let generated = encre_css::generate(
1453    ///     ["prose", "prose-invert"],
1454    ///     &config,
1455    /// );
1456    ///
1457    /// assert!(generated.ends_with(".prose {
1458    ///   color: #333;
1459    /// }
1460    ///
1461    /// .prose-invert {
1462    ///   color: #eee;
1463    /// }"));
1464    /// ```
1465    pub fn register_plugin<T: Into<Cow<'static, str>>>(
1466        &mut self,
1467        namespace: T,
1468        plugin: &'static (dyn Plugin + Send + Sync),
1469    ) {
1470        self.custom_plugins.push((namespace.into(), plugin));
1471    }
1472
1473    /// Register a custom variant which will be used during CSS generation.
1474    ///
1475    /// Note that if you are not the maintainer of a crate providing variants, you can ignore this
1476    /// function.
1477    ///
1478    /// # Example
1479    ///
1480    /// ```
1481    /// use encre_css::{Config, selector::VariantType};
1482    /// use std::borrow::Cow;
1483    ///
1484    /// let mut config = Config::default();
1485    /// config.register_variant("headings", VariantType::WrapClass(Cow::Borrowed("& :where(h1, h2, h3, h4, h5, h6)")));
1486    ///
1487    /// let generated = encre_css::generate(
1488    ///     ["headings:text-gray-700"],
1489    ///     &config,
1490    /// );
1491    ///
1492    /// assert!(generated.ends_with(".headings\\:text-gray-700 :where(h1, h2, h3, h4, h5, h6) {
1493    ///   --en-text-opacity: 1;
1494    ///   color: rgb(55 65 81 / var(--en-text-opacity));
1495    /// }"));
1496    /// ```
1497    pub fn register_variant<T: Into<Cow<'static, str>>>(
1498        &mut self,
1499        variant_name: T,
1500        variant_type: VariantType,
1501    ) {
1502        self.custom_variants
1503            .push((variant_name.into(), variant_type));
1504    }
1505
1506    /// Deserialize the content of a [TOML](https://toml.io) file to get the configuration.
1507    ///
1508    /// # Example
1509    ///
1510    /// <div class="example-wrap"><pre class="rust rust-example-rendered"><code><span class="comment"># encre-css.toml</span>
1511    /// <span class="kw">[theme]</span>
1512    /// dark_mode = { type = <span class="string">"class"</span>, class = <span class="string">".dark"</span> }
1513    /// screens = { 3xl = <span class="string">"1600px"</span>, lg = <span class="string">"2000px"</span> }<br>
1514    /// <span class="kw">[theme.colors]</span>
1515    /// primary = <span class="string">"#e5186a"</span>
1516    /// yellow-400 = <span class="string">"#ffef0e"</span></code></pre></div>
1517    ///
1518    /// ```no_run
1519    /// use encre_css::Config;
1520    ///
1521    /// # fn main() -> encre_css::Result<()> {
1522    /// let config = Config::from_file("encre-css.toml")?;
1523    /// let _generated = encre_css::generate([], &config);
1524    /// # Ok(())
1525    /// # }
1526    /// ```
1527    ///
1528    /// See [`Config`] for other ways of creating a configuration.
1529    ///
1530    /// # Errors
1531    ///
1532    /// Returns [`Error::ConfigFileNotFound`] if the given file does not exist.
1533    /// Returns [`Error::ConfigParsing`] if the given file could not be parsed.
1534    pub fn from_file<T: AsRef<Path>>(path: T) -> Result<Self> {
1535        Ok(toml::from_str(&fs::read_to_string(&path).map_err(
1536            |e| Error::ConfigFileNotFound(path.as_ref().to_path_buf(), e),
1537        )?)?)
1538    }
1539}
1540
1541impl PartialEq for Config {
1542    fn eq(&self, other: &Self) -> bool {
1543        self.safelist == other.safelist
1544            && self.theme == other.theme
1545            && self.preflight == other.preflight
1546            && self.shortcuts == other.shortcuts
1547            && self.max_shortcut_depth == other.max_shortcut_depth
1548            && self.extra == other.extra
1549            && self.custom_variants == other.custom_variants
1550    }
1551}
1552
1553impl fmt::Debug for Config {
1554    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1555        f.debug_struct("Config")
1556            .field("safelist", &self.safelist)
1557            .field("theme", &self.theme)
1558            .field("preflight", &self.preflight)
1559            .field("shortcuts", &self.shortcuts)
1560            .field("max_shortcut_depth", &self.max_shortcut_depth)
1561            .field("extra", &self.extra)
1562            .field("custom_plugins", &self.custom_plugins)
1563            .field("custom_variants", &self.custom_variants)
1564            .finish_non_exhaustive()
1565    }
1566}
1567
1568#[cfg(test)]
1569mod tests {
1570    use super::*;
1571    use crate::{generate, utils::testing::base_config};
1572
1573    use pretty_assertions::assert_eq;
1574
1575    #[test]
1576    fn gen_css_with_custom_config() {
1577        let mut config = base_config();
1578        config.theme.colors.add("rosa-500", "#e5186a");
1579        config.theme.screens.add("3xl", "1600px");
1580
1581        let generated = generate(["3xl:text-rosa-500"], &config);
1582
1583        assert_eq!(
1584            generated,
1585            String::from(
1586                r"@media (min-width: 1600px) {
1587  .\33xl\:text-rosa-500 {
1588    --en-text-opacity: 1;
1589    color: rgb(229 24 106 / var(--en-text-opacity));
1590  }
1591}"
1592            )
1593        );
1594    }
1595
1596    #[test]
1597    fn gen_css_with_shortcuts() {
1598        let mut config = base_config();
1599        config
1600            .shortcuts
1601            .add("btn", "bg-red-500 border-1 rounded-xl");
1602        config.shortcuts.add("bg", "bg-blue-100");
1603
1604        let generated = generate(["btn", "bg-yellow-500"], &config);
1605
1606        assert_eq!(
1607            generated,
1608            String::from(
1609                ".btn {
1610  border-radius: 0.75rem;
1611}
1612
1613.btn {
1614  border-width: 1px;
1615}
1616
1617.bg-yellow-500 {
1618  --en-bg-opacity: 1;
1619  background-color: rgb(234 179 8 / var(--en-bg-opacity));
1620}
1621
1622.btn {
1623  --en-bg-opacity: 1;
1624  background-color: rgb(239 68 68 / var(--en-bg-opacity));
1625}"
1626            )
1627        );
1628    }
1629
1630    #[test]
1631    fn gen_css_with_nested_shortcuts() {
1632        let mut config = base_config();
1633        config
1634            .shortcuts
1635            .add("btn", "bg-red-500 border-1 rounded-xl");
1636        config.shortcuts.add("btn-primary", "btn bg-blue-500");
1637
1638        let generated = generate(["btn-primary"], &config);
1639
1640        assert_eq!(
1641            generated,
1642            String::from(
1643                ".btn-primary {
1644  border-radius: 0.75rem;
1645}
1646
1647.btn-primary {
1648  border-width: 1px;
1649}
1650
1651.btn-primary {
1652  --en-bg-opacity: 1;
1653  background-color: rgb(239 68 68 / var(--en-bg-opacity));
1654}"
1655            )
1656        );
1657    }
1658
1659    #[test]
1660    fn gen_css_with_shortcut_cycle() {
1661        let mut config = base_config();
1662        config
1663            .shortcuts
1664            .add("btn", "bg-red-500 border-1 rounded-xl btn-primary");
1665        config.shortcuts.add("btn-primary", "btn bg-blue-500");
1666
1667        let generated = generate(["btn-primary"], &config);
1668
1669        assert_eq!(
1670            generated,
1671            String::from(
1672                ".btn-primary {
1673  border-radius: 0.75rem;
1674}
1675
1676.btn-primary {
1677  border-width: 1px;
1678}
1679
1680.btn-primary {
1681  --en-bg-opacity: 1;
1682  background-color: rgb(239 68 68 / var(--en-bg-opacity));
1683}"
1684            )
1685        );
1686    }
1687
1688    #[test]
1689    fn gen_css_with_safelist() {
1690        let mut config = base_config();
1691        config.safelist.add("text-red-500");
1692        config.safelist.add("btn");
1693        config.shortcuts.add("btn", "text-red-400");
1694
1695        let generated = generate(["bg-red-300"], &config);
1696
1697        assert_eq!(
1698            generated,
1699            String::from(
1700                ".bg-red-300 {
1701  --en-bg-opacity: 1;
1702  background-color: rgb(252 165 165 / var(--en-bg-opacity));
1703}
1704
1705.btn {
1706  --en-text-opacity: 1;
1707  color: rgb(248 113 113 / var(--en-text-opacity));
1708}
1709
1710.text-red-500 {
1711  --en-text-opacity: 1;
1712  color: rgb(239 68 68 / var(--en-text-opacity));
1713}"
1714            )
1715        );
1716    }
1717
1718    #[test]
1719    fn gen_css_with_custom_plugin_and_extra_fields() {
1720        use crate::prelude::build_plugin::*;
1721        use std::collections::HashMap;
1722
1723        #[derive(Debug)]
1724        struct EmojiPlugin;
1725
1726        impl Plugin for EmojiPlugin {
1727            fn can_handle(&self, context: ContextCanHandle) -> bool {
1728                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))))
1729            }
1730
1731            fn handle(&self, context: &mut ContextHandle) {
1732                if let Modifier::Builtin { value, .. } = context.modifier {
1733                    context.buffer.line(format_args!(
1734                        r#"content: {};"#,
1735                        context
1736                            .config
1737                            .extra
1738                            .get("emojis")
1739                            .unwrap()
1740                            .as_table()
1741                            .unwrap()
1742                            .get(*value)
1743                            .unwrap()
1744                    ));
1745                }
1746            }
1747        }
1748
1749        let mut config = base_config();
1750        config.register_plugin("emoji", &EmojiPlugin);
1751        config.extra.add(
1752            "emojis",
1753            HashMap::from_iter([("tada", "\u{1f389}"), ("rocket", "\u{1f680}")]),
1754        );
1755
1756        let generated = generate(["emoji-tada"], &config);
1757
1758        assert_eq!(
1759            generated,
1760            String::from(
1761                ".emoji-tada {
1762  content: \"\u{1f389}\";
1763}"
1764            )
1765        );
1766    }
1767
1768    #[test]
1769    fn gen_css_with_custom_plugin_extra_fields_and_parsed_config() {
1770        use crate::prelude::build_plugin::*;
1771
1772        #[derive(Debug)]
1773        struct EmojiPlugin;
1774
1775        impl Plugin for EmojiPlugin {
1776            fn can_handle(&self, context: ContextCanHandle) -> bool {
1777                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))))
1778            }
1779
1780            fn handle(&self, context: &mut ContextHandle) {
1781                if let Modifier::Builtin { value, .. } = context.modifier {
1782                    context.buffer.line(format_args!(
1783                        r#"content: {};"#,
1784                        context
1785                            .config
1786                            .extra
1787                            .get("emojis")
1788                            .unwrap()
1789                            .as_table()
1790                            .unwrap()
1791                            .get(*value)
1792                            .unwrap()
1793                    ));
1794                }
1795            }
1796        }
1797
1798        let mut config = Config::from_file("tests/fixtures/extra-fields-config.toml").unwrap();
1799        config.register_plugin("emoji", &EmojiPlugin);
1800
1801        let generated = generate(["emoji-tada"], &config);
1802
1803        assert_eq!(
1804            generated,
1805            String::from(
1806                ".emoji-tada {
1807  content: \"\u{1f389}\";
1808}"
1809            )
1810        );
1811    }
1812
1813    #[test]
1814    fn config_is_extended_and_overridden() {
1815        let config = Config::from_file("tests/fixtures/custom-config.toml").unwrap();
1816
1817        let generated = generate(
1818            [
1819                "bg-rosa-500",
1820                "bg-yellow-400",
1821                "bg-yellow-100",
1822                "3xl:underline",
1823                "lg:text-rosa-500",
1824            ],
1825            &config,
1826        );
1827
1828        assert_eq!(
1829            generated,
1830            String::from(
1831                r".bg-rosa-500 {
1832  --en-bg-opacity: 1;
1833  background-color: rgb(229 24 106 / var(--en-bg-opacity));
1834}
1835
1836.bg-yellow-100 {
1837  --en-bg-opacity: 1;
1838  background-color: rgb(254 249 195 / var(--en-bg-opacity));
1839}
1840
1841.bg-yellow-400 {
1842  --en-bg-opacity: 1;
1843  background-color: rgb(255 239 14 / var(--en-bg-opacity));
1844}
1845
1846@media (min-width: 1600px) {
1847  .\33xl\:underline {
1848    -webkit-text-decoration-line: underline;
1849    text-decoration-line: underline;
1850  }
1851}
1852
1853@media (min-width: 2000px) {
1854  .lg\:text-rosa-500 {
1855    --en-text-opacity: 1;
1856    color: rgb(229 24 106 / var(--en-text-opacity));
1857  }
1858}"
1859            )
1860        );
1861    }
1862
1863    #[test]
1864    fn deserialize_config() {
1865        let mut config = base_config();
1866        config.theme.colors.add("rosa-500", "#e5186a");
1867        config.theme.colors.add("yellow-400", "#ffef0e");
1868        config.theme.aria.add("current", "current=\"page\"");
1869        config.theme.screens.add("lg", "2000px");
1870        config.theme.screens.add("3xl", "1600px");
1871        config.theme.dark_mode = DarkMode::new_class(".dark");
1872
1873        assert_eq!(
1874            Config::from_file("tests/fixtures/custom-config.toml").unwrap(),
1875            config
1876        );
1877    }
1878
1879    #[test]
1880    fn serialize_config() {
1881        let mut config = Config {
1882            preflight: Preflight::None,
1883            ..Default::default()
1884        };
1885        config.theme.dark_mode = DarkMode::new_class(".dark");
1886        config.theme.screens.add("3xl", "1600px");
1887        config.theme.screens.add("lg", "2000px");
1888        config.theme.colors.add("rosa-500", "#e5186a");
1889        config.theme.colors.add("yellow-400", "#ffef0e");
1890        config.theme.aria.add("current", "current=\"page\"");
1891
1892        let result = toml::to_string(&config).unwrap();
1893
1894        let expected_config = fs::read_to_string("tests/fixtures/custom-config.toml").unwrap();
1895        assert_eq!(expected_config, result);
1896    }
1897}