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}