Scoping Normalized Preflight CSS
Published: 2022-02-12 21:10:28

Have you ever wanted to write a component library with, or port a legacy codebase to, a modern utility-based CSS framework such as [Tailwind CSS](https://tailwindcss.com) or [WindiCSS](https://windicss.org), just to realize that their Preflight CSS doesn't grant you the flexibility you deserve?

Preflight lives in the global CSS namespace, so you cannot reliably use it or disable it for either of those use cases without some annoyances: You need to reach for utility classes that you typically don't need to use, the reset from your legacy CSS framework will override your base styling, etc.

That's annoying, man! The reason developers love Tailwind CSS and WindiCSS is that they are largely brainless affairs. You just slap some utility classes on elements and you're good to go. Trying to hack around the shortcomings of Preflight when it fails you takes this convenience away.

Luckily, there is an easy fix!

Enter [`:where()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:where), the amazing CSS pseudo selector that gives us super powers.

This little guy **does not** effect CSS specificity. If we wrap a parent selector with it, e.g, `:where(.preflight)`, and make a few modifications to the CSS, we can remove it from the global CSS namespace and tailor it to only reset the portions of a web application that we want complete control over.

You can now create your component library or port your existing codebase to Tailwind CSS or WindiCSS and use a beautiful Preflight reset, all without worrying about global namespace collisions.

Below you will find an altered version of the [Tailwind CSS 3 Preflight](https://tailwindcss.com/docs/preflight) scoped to a parent `.preflight` class. It could use some clean up but will get you 99% of the way there.

Hopefully, Tailwind CSS or WindiCSS will bake this sort of functionality into their core. Until then, thanks to `:where()`, we have a workaround.

```css
:where(.preflight) {
  --font-sans: 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";
  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  --border-color: rgb(209 213 219);
  --placeholder-color: rgb(156 163 175);
}
:where(.preflight) *,
:where(.preflight) ::before,
:where(.preflight) ::after {
  box-sizing: border-box;
  border-width: 0;
  border-style: solid;
  border-color: var(--border-color);
}
:where(.preflight)  {
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  -moz-tab-size: 4;
  tab-size: 4;
  font-family: var(--font-sans);
}
:where(.preflight)  {
  margin: 0;
  line-height: inherit;
}
:where(.preflight) hr {
  height: 0;
  color: inherit;
  border-top-width: 1px;
}
:where(.preflight) abbr:where([title]) {
  text-decoration: underline dotted;
}
:where(.preflight) h1,
:where(.preflight) h2,
:where(.preflight) h3,
:where(.preflight) h4,
:where(.preflight) h5,
:where(.preflight) h6 {
  font-size: inherit;
  font-weight: inherit;
}
:where(.preflight) a {
  color: inherit;
  text-decoration: inherit;
}
:where(.preflight) b,
:where(.preflight) strong {
  font-weight: bolder;
}
:where(.preflight) code,
:where(.preflight) kbd,
:where(.preflight) samp,
:where(.preflight) pre {
  font-family: var(--font-mono);
  font-size: 1em;
}
:where(.preflight) small {
  font-size: 80%;
}
:where(.preflight) sub,
:where(.preflight) sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}
:where(.preflight) sub {
  bottom: -0.25em;
}
:where(.preflight) sup {
  top: -0.5em;
}
:where(.preflight) table {
  text-indent: 0;
  border-color: inherit;
  border-collapse: collapse;
}
:where(.preflight) button,
:where(.preflight) input,
:where(.preflight) optgroup,
:where(.preflight) select,
:where(.preflight) textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: inherit;
  color: inherit;
  margin: 0;
  padding: 0;
}
:where(.preflight) button,
:where(.preflight) select {
  text-transform: none;
}
:where(.preflight) button,
:where(.preflight) [type='button'],
:where(.preflight) [type='reset'],
:where(.preflight) [type='submit'] {
  -webkit-appearance: button;
  background-color: transparent;
  background-image: none;
}
:where(.preflight) :-moz-focusring {
  outline: auto;
}
:where(.preflight) :-moz-ui-invalid {
  box-shadow: none;
}
:where(.preflight) progress {
  vertical-align: baseline;
}
:where(.preflight) ::-webkit-inner-spin-button,
:where(.preflight) ::-webkit-outer-spin-button {
  height: auto;
}
:where(.preflight) [type='search'] {
  -webkit-appearance: textfield;
  outline-offset: -2px;
}
:where(.preflight) ::-webkit-search-decoration {
  -webkit-appearance: none;
}
:where(.preflight) ::-webkit-file-upload-button {
  -webkit-appearance: button;
  font: inherit;
}
:where(.preflight) summary {
  display: list-item;
}
:where(.preflight) blockquote,
:where(.preflight) dl,
:where(.preflight) dd,
:where(.preflight) h1,
:where(.preflight) h2,
:where(.preflight) h3,
:where(.preflight) h4,
:where(.preflight) h5,
:where(.preflight) h6,
:where(.preflight) hr,
:where(.preflight) figure,
:where(.preflight) p,
:where(.preflight) pre {
  margin: 0;
}
:where(.preflight) fieldset {
  margin: 0;
  padding: 0;
}
:where(.preflight) legend {
  padding: 0;
}
:where(.preflight) ol,
:where(.preflight) ul,
:where(.preflight) menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
:where(.preflight) textarea {
  resize: vertical;
}
:where(.preflight) input::placeholder,
:where(.preflight) textarea::placeholder {
  opacity: 1;
  color: var(--placeholder-color);
}
:where(.preflight) button,
:where(.preflight) [role="button"] {
  cursor: pointer;
}
:where(.preflight) :disabled {
  cursor: default;
}
:where(.preflight) img,
:where(.preflight) svg,
:where(.preflight) video,
:where(.preflight) canvas,
:where(.preflight) audio,
:where(.preflight) iframe,
:where(.preflight) embed,
:where(.preflight) object {
  display: block;
  vertical-align: middle;
}
:where(.preflight) img,
:where(.preflight) video {
  max-width: 100%;
  height: auto;
}
:where(.preflight) [hidden] {
  display: none;
}
```