encre_css/
preflight.rs

1//! Define the default set of base CSS styles used to make websites consistent across browsers.
2//!
3//! Based on [Tailwind's default preflight](https://tailwindcss.com/docs/preflight).
4//!
5//! ### Full preflight
6//!
7//! By default, this base CSS is included in the generated CSS and you can customize it
8//! by manually setting the [`Config::preflight`] configuration field to `Preflight::new_full()`
9//! and by using the various associated methods, e.g [`Preflight::font_family_sans`]
10//! or [`Preflight::font_family_mono`].
11//!
12//! ```
13//! use encre_css::{Preflight, Config};
14//!
15//! let mut config = Config::default();
16//! config.preflight = Preflight::new_full()
17//!     .font_family_mono("'Fira Code'");
18//!
19//! assert!(encre_css::generate([], &config).contains("code, kbd, samp, pre {
20//!   font-family: 'Fira Code';"));
21//! ```
22//!
23//! Using TOML:
24//!
25//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code>preflight = { full = { font_family_mono = <span class="string">"'Fira Code'"</span> } }</code></pre></div>
26//!
27//! ### Custom preflight
28//!
29//! You can also use your own default CSS using [`Preflight::new_custom`].
30//!
31//! ```
32//! use encre_css::{Preflight, Config};
33//!
34//! let mut config = Config::default();
35//! config.preflight = Preflight::new_custom("html, body {
36//!   width: 100vw;
37//!   height: 100vh;
38//!   margin: 0;
39//! }");
40//!
41//! assert_eq!(encre_css::generate([], &config), "html, body {
42//!   width: 100vw;
43//!   height: 100vh;
44//!   margin: 0;
45//! }");
46//! ```
47//!
48//! Using TOML:
49//!
50//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code>preflight = { custom = <span class="string">"html, body { width: 100vw; height: 100vh; margin: 0; }"</span> }</code></pre></div>
51//!
52//! Note that newlines [are not yet supported in TOML](https://github.com/toml-rs/toml/issues/397)
53//! so it might be better to define it in Rust if you have a long custom preflight.
54//!
55//! ### Empty preflight
56//! Finally you can disable it using [`Preflight::new_none`].
57//!
58//! ```
59//! use encre_css::{Preflight, Config};
60//!
61//! let mut config = Config::default();
62//! config.preflight = Preflight::new_none();
63//!
64//! assert!(encre_css::generate([], &config).is_empty());
65//! ```
66//!
67//! Using TOML:
68//!
69//! <div class="example-wrap"><pre class="rust rust-example-rendered"><code>preflight = <span class="string">"none"</span></code></pre></div>
70//!
71//! [`Config::preflight`]: crate::config::Config::preflight
72use serde::{Deserialize, Serialize};
73use std::borrow::Cow;
74
75const DEFAULT_FONT_FEATURE_SETTINGS: &str = "normal";
76const DEFAULT_FONT_VARIATION_SETTINGS: &str = "normal";
77const DEFAULT_FONT_FAMILY_SANS: &str = "ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'";
78const DEFAULT_FONT_FAMILY_MONO: &str = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
79
80const DEFAULT_PREFLIGHT: &str = concat!(
81    /*
82    1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
83    2. Remove default margins and padding
84    3. Reset all borders.
85    */
86    r#"*, ::after, ::before, ::backdrop, ::file-selector-button {
87  box-sizing: border-box;
88  margin: 0;
89  padding: 0;
90  border: 0 solid;
91}
92
93::before, ::after {
94  --en-content: '';
95}
96
97"#,
98    /*
99    1. Use a consistent sensible line-height in all browsers.
100    2. Prevent adjustments of font size after orientation changes in iOS.
101    3. Use a more readable tab size.
102    4. Use the user's configured `sans` font-family by default.
103    5. Use the user's configured `sans` font-feature-settings by default.
104    6. Use the user's configured `sans` font-variation-settings by default.
105    7. Disable tap highlights on iOS.
106    */
107    r#"html, :host {
108  line-height: 1.5;
109  -webkit-text-size-adjust: 100%;
110  tab-size: 4;
111  font-family: theme('fontFamily.sans');
112  font-feature-settings: theme('fontFeatureSettings.sans');
113  font-variation-settings: theme('fontVariationSettings.sans');
114  -webkit-tap-highlight-color: transparent;
115}
116
117"#,
118    /*
119    1. Add the correct height in Firefox.
120    2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
121    3. Reset the default border style to a 1px solid border.
122    */
123    r#"hr {
124  height: 0;
125  color: inherit;
126  border-top-width: 1px;
127}
128
129"#,
130    // Add the correct text decoration in Chrome, Edge, and Safari.
131    r#"abbr:where([title]) {
132  -webkit-text-decoration: underline dotted;
133  text-decoration: underline dotted;
134}
135
136"#,
137    // Remove the default font size and weight for headings.
138    r#"h1, h2, h3, h4, h5, h6 {
139  font-size: inherit;
140  font-weight: inherit;
141}
142
143"#,
144    // Reset links to optimize for opt-in styling instead of opt-out.
145    r#"a {
146  color: inherit;
147  -webkit-text-decoration: inherit;
148  text-decoration: inherit;
149}
150
151"#,
152    // Add the correct font weight in Edge and Safari.
153    r#"b, strong {
154  font-weight: bolder;
155}
156
157"#,
158    /*
159    1. Use the user's configured `mono` font-family by default.
160    2. Use the user's configured `mono` font-feature-settings by default.
161    3. Use the user's configured `mono` font-variation-settings by default.
162    4. Correct the odd `em` font sizing in all browsers.
163    */
164    r#"code, kbd, samp, pre {
165  font-family: theme('fontFamily.mono');
166  font-feature-settings: theme('fontFeatureSettings.mono');
167  font-variation-settings: theme('fontVariationSettings.mono');
168  font-size: 1em;
169}
170
171"#,
172    // Add the correct font size in all browsers.
173    r#"small {
174  font-size: 80%;
175}
176
177"#,
178    // Prevent `sub` and `sup` elements from affecting the line height in all browsers.
179    r#"sub, sup {
180  font-size: 75%;
181  line-height: 0;
182  position: relative;
183  vertical-align: baseline;
184}
185
186"#,
187    r#"sub {
188  bottom: -0.25em;
189}
190
191"#,
192    r#"sup {
193  top: -0.5em;
194}
195
196"#,
197    /*
198    1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
199    2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
200    3. Remove gaps between table borders by default.
201    */
202    r#"table {
203  text-indent: 0;
204  border-color: inherit;
205  border-collapse: collapse;
206}
207
208"#,
209    // Use the modern Firefox focus style for all focusable elements.
210    r#":-moz-focusring {
211  outline: auto;
212}
213
214"#,
215    // Add the correct vertical alignment in Chrome and Firefox.
216    r#"progress {
217  vertical-align: baseline;
218}
219
220"#,
221    // Add the correct display in Chrome and Safari.
222    r#"summary {
223  display: list-item;
224}
225
226"#,
227    // Make lists unstyled by default.
228    r#"ol, ul, menu {
229  list-style: none;
230}
231
232"#,
233    /*
234    1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
235    2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
236        This can trigger a poorly considered lint error in some tools but is included by design.
237    */
238    r#"img, svg, video, canvas, audio, iframe, embed, object {
239  display: block;
240  vertical-align: middle;
241}
242
243"#,
244    // Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
245    r#"img, video {
246  max-width: 100%;
247  height: auto;
248}
249
250"#,
251    /*
252    1. Inherit font styles in all browsers.
253    2. Remove border radius in all browsers.
254    3. Remove background color in all browsers.
255    4. Ensure consistent opacity for disabled states in all browsers.
256    */
257    r#"button, input, select, optgroup, textarea, ::file-selector-button {
258  font: inherit;
259  font-feature-settings: inherit;
260  font-variation-settings: inherit;
261  letter-spacing: inherit;
262  color: inherit;
263  border-radius: 0;
264  background-color: transparent;
265  opacity: 1;
266}
267
268"#,
269    // Restore default font weight.
270    r#":where(select:is([multiple], [size])) optgroup {
271  font-weight: bolder;
272}
273
274"#,
275    // Restore indentation.
276    r#":where(select:is([multiple], [size])) optgroup option {
277  padding-inline-start: 20px;
278}
279
280"#,
281    // Restore space after button.
282    r#"::file-selector-button {
283  margin-inline-end: 4px;
284}
285
286"#,
287    // Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
288    r#"::placeholder {
289  opacity: 1;
290}
291
292"#,
293    /*
294    Set the default placeholder color to a semi-transparent version of the current text color in browsers that do not
295    crash when using `color-mix(…)` with `currentcolor`. (https://github.com/tailwindlabs/tailwindcss/issues/17194)
296    */
297    r#"@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or (contain-intrinsic-size: 1px) /* Safari 17+ */ {
298  ::placeholder {
299    color: color-mix(in oklab, currentcolor 50%, transparent);
300  }
301}
302
303"#,
304    // Prevent resizing textareas horizontally by default.
305    r#"textarea {
306  resize: vertical;
307}
308
309"#,
310    // Remove the inner padding in Chrome and Safari on macOS.
311    r#"::-webkit-search-decoration {
312  -webkit-appearance: none;
313}
314
315"#,
316    /*
317    1. Ensure date/time inputs have the same height when empty in iOS Safari.
318    2. Ensure text alignment can be changed on date/time inputs in iOS Safari.
319    */
320    r#"::-webkit-date-and-time-value {
321  min-height: 1lh;
322  text-align: inherit;
323}
324
325"#,
326    // Prevent height from changing on date/time inputs in macOS Safari when the input is set to `display: block`.
327    r#"::-webkit-datetime-edit {
328  display: inline-flex;
329}
330
331"#,
332    // Remove excess padding from pseudo-elements in date/time inputs to ensure consistent height across browsers.
333    r#"::-webkit-datetime-edit-fields-wrapper {
334  padding: 0;
335}
336
337"#,
338    r#"::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
339  padding-block: 0;
340}
341
342"#,
343    // Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
344    r#":-moz-ui-invalid {
345  box-shadow: none;
346}
347
348"#,
349    // Correct the inability to style the border radius in iOS Safari.
350    r#"button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button {
351  appearance: button;
352}
353
354"#,
355    // Correct the cursor style of increment and decrement buttons in Safari.
356    r#"::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
357  height: auto;
358}
359
360"#,
361    // Make elements with the HTML hidden attribute stay hidden by default.
362    r#"[hidden]:where(:not([hidden='until-found'])) {
363  display: none !important;
364}
365
366"#,
367    // Defaults
368    r#"*, ::before, ::after, ::backdrop, ::-webkit-backdrop {
369  --en-border-spacing-x: 0;
370  --en-border-spacing-y: 0;
371  --en-translate-x: 0;
372  --en-translate-y: 0;
373  --en-translate-z: 0;
374  --en-rotate-x: 0;
375  --en-rotate-y: 0;
376  --en-rotate-z: 0;
377  --en-skew-x: 0;
378  --en-skew-y: 0;
379  --en-scale-x: 1;
380  --en-scale-y: 1;
381  --en-scale-z: 1;
382  --en-pan-x: ;
383  --en-pan-y: ;
384  --en-pinch-zoom: ;
385  --en-scroll-snap-strictness: proximity;
386  --en-ordinal: ;
387  --en-slashed-zero: ;
388  --en-numeric-figure: ;
389  --en-numeric-spacing: ;
390  --en-numeric-fraction: ;
391  --en-ring-inset: ;
392  --en-ring-offset-width: 0px;
393  --en-ring-offset-color: #fff;
394  --en-ring-color: currentColor;
395  --en-ring-offset-shadow: 0 0 #0000;
396  --en-ring-shadow: 0 0 #0000;
397  --en-shadow: 0 0 #0000;
398  --en-shadow-colored: 0 0 #0000;
399  --en-blur: ;
400  --en-brightness: ;
401  --en-contrast: ;
402  --en-grayscale: ;
403  --en-hue-rotate: ;
404  --en-invert: ;
405  --en-saturate: ;
406  --en-sepia: ;
407  --en-drop-shadow: ;
408  --en-backdrop-blur: ;
409  --en-backdrop-brightness: ;
410  --en-backdrop-contrast: ;
411  --en-backdrop-grayscale: ;
412  --en-backdrop-hue-rotate: ;
413  --en-backdrop-invert: ;
414  --en-backdrop-opacity: ;
415  --en-backdrop-saturate: ;
416  --en-backdrop-sepia: ;
417}"#
418);
419
420/// The set of default styles.
421///
422/// The default value is
423///
424/// ```
425/// # use encre_css::Preflight;
426/// # let _ =
427/// Preflight::Full {
428///     font_feature_settings_sans: None,
429///     font_variation_settings_sans: None,
430///     font_feature_settings_mono: None,
431///     font_variation_settings_mono: None,
432///     font_family_sans: None,
433///     font_family_mono: None,
434/// }
435/// # ;
436/// ```
437///
438/// i.e it will include the full preflight with the default values for all fields. Check the methods
439/// to see what they are.
440///
441/// See [`crate::preflight`].
442#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
443#[serde(rename_all = "lowercase")]
444pub enum Preflight {
445    /// No preflight will be generated.
446    None,
447
448    /// A custom preflight will be used.
449    Custom(Cow<'static, str>),
450
451    /// The full default preflight will be generated with some configuration options.
452    Full {
453        /// Set the [font feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings) of the whole document for the sans-serif font.
454        font_feature_settings_sans: Option<Cow<'static, str>>,
455
456        /// Set the [font variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings) of the whole document for the sans-serif font.
457        font_variation_settings_sans: Option<Cow<'static, str>>,
458
459        /// Set the [font feature settings](https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings) of the whole document for the monospace font.
460        font_feature_settings_mono: Option<Cow<'static, str>>,
461
462        /// Set the [font variation settings](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings) of the whole document for the monospace font.
463        font_variation_settings_mono: Option<Cow<'static, str>>,
464
465        /// Set the default sans-serif font family.
466        font_family_sans: Option<Cow<'static, str>>,
467
468        /// Set the default monospace font family.
469        font_family_mono: Option<Cow<'static, str>>,
470    },
471}
472
473impl Preflight {
474    /// Create a new [`Preflight::None`].
475    pub fn new_none() -> Self {
476        Self::None
477    }
478
479    /// Create a new [`Preflight::Custom`].
480    pub fn new_custom<T: Into<Cow<'static, str>>>(css: T) -> Self {
481        Self::Custom(css.into())
482    }
483
484    /// Create a new [`Preflight::Full`] with default values for options.
485    pub fn new_full() -> Self {
486        Self::Full {
487            font_feature_settings_sans: None,
488            font_variation_settings_sans: None,
489            font_feature_settings_mono: None,
490            font_variation_settings_mono: None,
491            font_family_sans: None,
492            font_family_mono: None,
493        }
494    }
495
496    /// Set the font feature settings of the whole document for the sans-serif font.
497    ///
498    /// The default value is `normal`.
499    #[must_use]
500    pub fn font_feature_settings_sans<T: Into<Cow<'static, str>>>(
501        mut self,
502        new_font_feature_settings: T,
503    ) -> Self {
504        if let Self::Full {
505            ref mut font_feature_settings_sans,
506            ..
507        } = self
508        {
509            *font_feature_settings_sans = Some(new_font_feature_settings.into());
510        }
511
512        self
513    }
514
515    /// Set the font variation settings of the whole document for the sans-serif font.
516    ///
517    /// The default value is `normal`.
518    #[must_use]
519    pub fn font_variation_settings_sans<T: Into<Cow<'static, str>>>(
520        mut self,
521        new_font_variation_settings: T,
522    ) -> Self {
523        if let Self::Full {
524            ref mut font_variation_settings_sans,
525            ..
526        } = self
527        {
528            *font_variation_settings_sans = Some(new_font_variation_settings.into());
529        }
530
531        self
532    }
533
534    /// Set the font feature settings of the whole document for the monospace font.
535    ///
536    /// The default value is `normal`.
537    #[must_use]
538    pub fn font_feature_settings_mono<T: Into<Cow<'static, str>>>(
539        mut self,
540        new_font_feature_settings: T,
541    ) -> Self {
542        if let Self::Full {
543            ref mut font_feature_settings_mono,
544            ..
545        } = self
546        {
547            *font_feature_settings_mono = Some(new_font_feature_settings.into());
548        }
549
550        self
551    }
552
553    /// Set the font variation settings of the whole document for the monospace font.
554    ///
555    /// The default value is `normal`.
556    #[must_use]
557    pub fn font_variation_settings_mono<T: Into<Cow<'static, str>>>(
558        mut self,
559        new_font_variation_settings: T,
560    ) -> Self {
561        if let Self::Full {
562            ref mut font_variation_settings_mono,
563            ..
564        } = self
565        {
566            *font_variation_settings_mono = Some(new_font_variation_settings.into());
567        }
568
569        self
570    }
571
572    /// Set the default sans-serif font family.
573    ///
574    /// The default value is `ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"`.
575    #[must_use]
576    pub fn font_family_sans<T: Into<Cow<'static, str>>>(mut self, new_font_family_sans: T) -> Self {
577        if let Self::Full {
578            ref mut font_family_sans,
579            ..
580        } = self
581        {
582            *font_family_sans = Some(new_font_family_sans.into());
583        }
584
585        self
586    }
587
588    /// Set the default monospace font family.
589    ///
590    /// The default value is `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace`.
591    #[must_use]
592    pub fn font_family_mono<T: Into<Cow<'static, str>>>(mut self, new_font_family_mono: T) -> Self {
593        if let Self::Full {
594            ref mut font_family_mono,
595            ..
596        } = self
597        {
598            *font_family_mono = Some(new_font_family_mono.into());
599        }
600
601        self
602    }
603
604    pub(crate) fn build(&self) -> Cow<'static, str> {
605        match self {
606            Self::None => Cow::from(""),
607            Self::Custom(css) => css.clone(),
608            Self::Full {
609                font_feature_settings_sans,
610                font_variation_settings_sans,
611                font_feature_settings_mono,
612                font_variation_settings_mono,
613                font_family_sans,
614                font_family_mono,
615            } => Cow::from(
616                DEFAULT_PREFLIGHT
617                    .replace(
618                        "theme('fontFeatureSettings.sans')",
619                        font_feature_settings_sans
620                            .as_ref()
621                            .unwrap_or(&Cow::Borrowed(DEFAULT_FONT_FEATURE_SETTINGS)),
622                    )
623                    .replace(
624                        "theme('fontVariationSettings.sans')",
625                        font_variation_settings_sans
626                            .as_ref()
627                            .unwrap_or(&Cow::Borrowed(DEFAULT_FONT_VARIATION_SETTINGS)),
628                    )
629                    .replace(
630                        "theme('fontFeatureSettings.mono')",
631                        font_feature_settings_mono
632                            .as_ref()
633                            .unwrap_or(&Cow::Borrowed(DEFAULT_FONT_FEATURE_SETTINGS)),
634                    )
635                    .replace(
636                        "theme('fontVariationSettings.mono')",
637                        font_variation_settings_mono
638                            .as_ref()
639                            .unwrap_or(&Cow::Borrowed(DEFAULT_FONT_VARIATION_SETTINGS)),
640                    )
641                    .replace(
642                        "theme('fontFamily.sans')",
643                        font_family_sans
644                            .as_ref()
645                            .unwrap_or(&Cow::Borrowed(DEFAULT_FONT_FAMILY_SANS)),
646                    )
647                    .replace(
648                        "theme('fontFamily.mono')",
649                        font_family_mono
650                            .as_ref()
651                            .unwrap_or(&Cow::Borrowed(DEFAULT_FONT_FAMILY_MONO)),
652                    ),
653            ),
654        }
655    }
656}
657
658impl Default for Preflight {
659    fn default() -> Self {
660        Self::Full {
661            font_feature_settings_sans: None,
662            font_variation_settings_sans: None,
663            font_feature_settings_mono: None,
664            font_variation_settings_mono: None,
665            font_family_sans: None,
666            font_family_mono: None,
667        }
668    }
669}
670
671#[cfg(test)]
672mod tests {
673    use super::*;
674    use crate::{generate, Config};
675
676    use pretty_assertions::assert_eq;
677
678    #[test]
679    #[allow(clippy::too_many_lines)]
680    fn full_preflight() {
681        let preflight = Preflight::new_full()
682            .font_feature_settings_sans("tnum")
683            .font_variation_settings_sans("'xhgt' 0.7")
684            .font_feature_settings_mono("'liga' 0")
685            .font_variation_settings_mono("'whgt' 850")
686            .font_family_sans("sans-serif")
687            .font_family_mono("monospace");
688        let config = Config {
689            preflight,
690            ..Default::default()
691        };
692
693        let generated = generate(["w-full"], &config);
694
695        assert_eq!(
696            generated,
697            String::from(
698                r#"*, ::after, ::before, ::backdrop, ::file-selector-button {
699  box-sizing: border-box;
700  margin: 0;
701  padding: 0;
702  border: 0 solid;
703}
704
705::before, ::after {
706  --en-content: '';
707}
708
709html, :host {
710  line-height: 1.5;
711  -webkit-text-size-adjust: 100%;
712  tab-size: 4;
713  font-family: sans-serif;
714  font-feature-settings: tnum;
715  font-variation-settings: 'xhgt' 0.7;
716  -webkit-tap-highlight-color: transparent;
717}
718
719hr {
720  height: 0;
721  color: inherit;
722  border-top-width: 1px;
723}
724
725abbr:where([title]) {
726  -webkit-text-decoration: underline dotted;
727  text-decoration: underline dotted;
728}
729
730h1, h2, h3, h4, h5, h6 {
731  font-size: inherit;
732  font-weight: inherit;
733}
734
735a {
736  color: inherit;
737  -webkit-text-decoration: inherit;
738  text-decoration: inherit;
739}
740
741b, strong {
742  font-weight: bolder;
743}
744
745code, kbd, samp, pre {
746  font-family: monospace;
747  font-feature-settings: 'liga' 0;
748  font-variation-settings: 'whgt' 850;
749  font-size: 1em;
750}
751
752small {
753  font-size: 80%;
754}
755
756sub, sup {
757  font-size: 75%;
758  line-height: 0;
759  position: relative;
760  vertical-align: baseline;
761}
762
763sub {
764  bottom: -0.25em;
765}
766
767sup {
768  top: -0.5em;
769}
770
771table {
772  text-indent: 0;
773  border-color: inherit;
774  border-collapse: collapse;
775}
776
777:-moz-focusring {
778  outline: auto;
779}
780
781progress {
782  vertical-align: baseline;
783}
784
785summary {
786  display: list-item;
787}
788
789ol, ul, menu {
790  list-style: none;
791}
792
793img, svg, video, canvas, audio, iframe, embed, object {
794  display: block;
795  vertical-align: middle;
796}
797
798img, video {
799  max-width: 100%;
800  height: auto;
801}
802
803button, input, select, optgroup, textarea, ::file-selector-button {
804  font: inherit;
805  font-feature-settings: inherit;
806  font-variation-settings: inherit;
807  letter-spacing: inherit;
808  color: inherit;
809  border-radius: 0;
810  background-color: transparent;
811  opacity: 1;
812}
813
814:where(select:is([multiple], [size])) optgroup {
815  font-weight: bolder;
816}
817
818:where(select:is([multiple], [size])) optgroup option {
819  padding-inline-start: 20px;
820}
821
822::file-selector-button {
823  margin-inline-end: 4px;
824}
825
826::placeholder {
827  opacity: 1;
828}
829
830@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or (contain-intrinsic-size: 1px) /* Safari 17+ */ {
831  ::placeholder {
832    color: color-mix(in oklab, currentcolor 50%, transparent);
833  }
834}
835
836textarea {
837  resize: vertical;
838}
839
840::-webkit-search-decoration {
841  -webkit-appearance: none;
842}
843
844::-webkit-date-and-time-value {
845  min-height: 1lh;
846  text-align: inherit;
847}
848
849::-webkit-datetime-edit {
850  display: inline-flex;
851}
852
853::-webkit-datetime-edit-fields-wrapper {
854  padding: 0;
855}
856
857::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
858  padding-block: 0;
859}
860
861:-moz-ui-invalid {
862  box-shadow: none;
863}
864
865button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button {
866  appearance: button;
867}
868
869::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
870  height: auto;
871}
872
873[hidden]:where(:not([hidden='until-found'])) {
874  display: none !important;
875}
876
877*, ::before, ::after, ::backdrop, ::-webkit-backdrop {
878  --en-border-spacing-x: 0;
879  --en-border-spacing-y: 0;
880  --en-translate-x: 0;
881  --en-translate-y: 0;
882  --en-translate-z: 0;
883  --en-rotate-x: 0;
884  --en-rotate-y: 0;
885  --en-rotate-z: 0;
886  --en-skew-x: 0;
887  --en-skew-y: 0;
888  --en-scale-x: 1;
889  --en-scale-y: 1;
890  --en-scale-z: 1;
891  --en-pan-x: ;
892  --en-pan-y: ;
893  --en-pinch-zoom: ;
894  --en-scroll-snap-strictness: proximity;
895  --en-ordinal: ;
896  --en-slashed-zero: ;
897  --en-numeric-figure: ;
898  --en-numeric-spacing: ;
899  --en-numeric-fraction: ;
900  --en-ring-inset: ;
901  --en-ring-offset-width: 0px;
902  --en-ring-offset-color: #fff;
903  --en-ring-color: currentColor;
904  --en-ring-offset-shadow: 0 0 #0000;
905  --en-ring-shadow: 0 0 #0000;
906  --en-shadow: 0 0 #0000;
907  --en-shadow-colored: 0 0 #0000;
908  --en-blur: ;
909  --en-brightness: ;
910  --en-contrast: ;
911  --en-grayscale: ;
912  --en-hue-rotate: ;
913  --en-invert: ;
914  --en-saturate: ;
915  --en-sepia: ;
916  --en-drop-shadow: ;
917  --en-backdrop-blur: ;
918  --en-backdrop-brightness: ;
919  --en-backdrop-contrast: ;
920  --en-backdrop-grayscale: ;
921  --en-backdrop-hue-rotate: ;
922  --en-backdrop-invert: ;
923  --en-backdrop-opacity: ;
924  --en-backdrop-saturate: ;
925  --en-backdrop-sepia: ;
926}
927
928.w-full {
929  width: 100%;
930}"#
931            )
932        );
933    }
934
935    #[test]
936    fn custom_preflight() {
937        let preflight = Preflight::new_custom(
938            "html, body {
939  width: 100%;
940  height: 100%;
941  padding: 0;
942  margin: 0;
943  overflow-x: hidden;
944}",
945        );
946        let config = Config {
947            preflight,
948            ..Default::default()
949        };
950
951        let generated = generate(["w-full"], &config);
952
953        assert_eq!(
954            generated,
955            "html, body {
956  width: 100%;
957  height: 100%;
958  padding: 0;
959  margin: 0;
960  overflow-x: hidden;
961}
962
963.w-full {
964  width: 100%;
965}"
966        );
967    }
968
969    #[test]
970    fn no_preflight() {
971        let config = Config {
972            preflight: Preflight::new_none(),
973            ..Default::default()
974        };
975
976        let generated = generate(["w-full"], &config);
977
978        assert_eq!(
979            generated,
980            ".w-full {
981  width: 100%;
982}"
983        );
984    }
985}