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