Library / SDK
csstools/sanitize.css avatar
csstools/sanitize.css

sanitize.css: a zero-specificity CSS foundation for cross-browser defaults

A best-practices CSS foundation

5,310 stars299 forksCSSCC0-1.0

At a glance

What is it?
sanitize.css packages browser normalization and opinionated defaults into a stylesheet that wraps its rules in :where() so your own styles always win. It suits teams that want sane defaults without fighting specificity, and it is the wrong tool if you want a blank slate.
Who is it for?
Adopt sanitize.css if you want documented, cross-browser defaults that never outrank your own selectors, and check the :where() browser support line before you ship to old browsers. Skip it if you want a full reset or a design system with real component styles.
Can I use it commercially?
Yes. CC0-1.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly CSS, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What sanitize.css fixes, and who it is for

Browsers ship different default stylesheets. A textarea resizes in both directions in one engine and only vertically in another. Tables add border spacing. Lists inside nav carry markers. sanitize.css collects those differences into one file: the README says it provides "consistent, cross-browser default styling of HTML elements alongside useful defaults."

The library is developed alongside normalize.css, so every normalization from that project is included, and the README states that every normalization and opinion is marked and documented in the source. That marking is the actual product. You can read a rule, see a comment explaining why it exists, and decide whether you agree.

It is for teams starting a new stylesheet layer who do not want to write their own reset and do not want to reason about browser quirks from scratch. It is also for people who already use normalize.css and want the same normalizations plus a set of defaults such as border-box sizing and middle-aligned media elements.

The topics on the repository list the project as css, normalization and opinionated, and that last word matters. This is not a neutral baseline.

How the :where() wrapper changes the specificity game

The README states that sanitize.css wraps styles in zero-specificity selectors using :where(). That single design decision is the main difference from a conventional reset. A rule such as `nav ol, nav ul { list-style: none; padding: 0; }` contributes no specificity weight, so a later `.sidebar ul` selector wins without an !important or a longer selector chain.

The practical effect is that you stop fighting the foundation. With a reset that uses element selectors, a plain `ul` rule can beat a class-based rule in some cascade positions, and you end up escalating selectors to compensate. With :where(), the foundation sits at the bottom of the cascade by construction.

The trade-off is browser support. :where() is a modern selector, and the README's browser support list includes Internet Explorer 9+, which predates it. The README does not explain how the zero-specificity behaviour degrades in engines that do not understand :where(). If you must support IE, treat that as an open question to verify against the published stylesheet rather than an assumption.

The file is also split. sanitize.css is the base, and forms.css, assets.css, typography.css, reduce-motion.css, system-ui.css and ui-monospace.css are separate stylesheets you opt into. Nothing in the README suggests the base file pulls the others in, so linking sanitize.css alone gives you normalization and the core defaults, not form control styling or system font typography.

Installing sanitize.css from npm and linking it in a page

The README gives an npm install as the primary package route. Run it in your project root:

bash
npm install sanitize.css --save

After that, the quickest first use is a link tag to a CDN build. The README's example points at Skypack:

html
<link href="https://cdn.skypack.dev/sanitize.css" rel="stylesheet" />

Load the page and you should see the defaults applied: box-sizing is border-box on every element and pseudo-element, body margin is zero, and html line-height is 1.5. If nothing changes, check that the link resolves; the README also offers a download path at https://csstools.github.io/sanitize.css/latest/sanitize.css.

If you build with webpack, the README shows importing the stylesheets directly from CSS:

css
@import '~sanitize.css';
@import '~sanitize.css/forms.css';
@import '~sanitize.css/typography.css';

The same README section shows the equivalent in JavaScript with `import 'sanitize.css';` and notes that webpack.config.js needs `style-loader` and `css-loader` for the `.css` test rule. Add forms.css only if you want the form control normalization; it sets transparent backgrounds, inherits font and color, and removes the native select arrow in favour of an inline SVG background image.

Where sanitize.css is the wrong choice

The opinionated defaults are the limitation. `html { word-break: break-all; }` is not a neutral normalization; it changes how long words wrap everywhere, and it can produce breaks in the middle of words in headings and code. If your design depends on normal word breaking, you are overriding the foundation on day one, which defeats the point of adopting it.

Other defaults carry similar weight. Backgrounds do not repeat by default, textareas resize only vertically, and `svg:not([fill])` falls back to currentColor. Each is defensible and each is a decision you inherit.

A reset stylesheet is the clearer wrong-tool case. If you want every element unstyled so your design system controls everything, sanitize.css adds defaults you will spend time undoing. The README draws this line itself: normalize.css styles adhere to CSS specifications, sanitize.css styles adhere to common developer expectations and preferences, and reset.css unstyles all elements.

There is also no component layer here. sanitize.css does not give you buttons, grids, spacing scales or color tokens. It is a foundation, and treating it as a UI kit will leave you writing all of that yourself.

sanitize.css vs normalize.css vs a full reset

normalize.css and sanitize.css correct browser bugs while carefully testing and documenting changes, according to the README, and the two are maintained in sync. The difference is the target: normalize.css aims at the CSS specifications, sanitize.css aims at common developer expectations.

That means sanitize.css is a superset in intent. It includes the normalizations from normalize.css and adds opinions such as border-box sizing, no-repeat backgrounds, 1.5 line height, tab-size 4, collapsed table borders and touch-action manipulation on interactive elements. If you already run normalize.css, moving to sanitize.css is mostly a question of whether you accept those opinions.

A reset takes the opposite approach. It strips margins, padding and list styles wholesale so nothing is inherited. That is simpler to reason about and heavier to rebuild from. Choosing between them is choosing whether you want defaults you keep or defaults you write.

The zero-specificity wrapping is the other axis. A reset written with element selectors can outrank your classes; sanitize.css is designed not to. If cascade fights are your actual pain, that is the feature to evaluate.

Maintenance, licensing and upgrade cost

The last push to the repository was on 2026-03-26. The most recent release listed is v13.0.0 from 2021-09-14, with 12.0.1 and 12.0.0 before it in 2020. The release cadence is slow, which fits the subject: browser defaults change slowly, and a normalization layer does not need frequent releases.

Upgrade cost is low in one direction and real in another. A version bump can change a default you relied on, and since the rules are zero-specificity, a changed default will not announce itself through a cascade conflict. It will just render differently. The CHANGELOG.md at the repository root is the place to read before bumping the version in package.json.

The licence is CC0-1.0. That is a public domain dedication rather than a permissive software licence, and package.json records it in the license field. CC0-1.0 is not a licence that grants patent rights in the way some software licences do, and it is unusual for an npm package. If your organization has a policy list of approved licence identifiers, check that CC0-1.0 is on it before you add the dependency. This is a policy question for your legal team, not something the README addresses.

Testing is minimal by design: the package's test script runs stylelint over the CSS files with stylelint-config-standard.

Editorial conclusion

Adopt sanitize.css if you want documented, cross-browser defaults that never outrank your own selectors, and check the :where() browser support line before you ship to old browsers. Skip it if you want a full reset or a design system with real component styles. Verify first that the modular files you plan to link (forms.css, typography.css, assets.css, reduce-motion.css) are the ones your layout actually relies on, because linking sanitize.css alone does not include them.

Frequently asked questions

How do I install sanitize.css with npm?

Run npm install sanitize.css --save, then either link the published stylesheet in HTML or import it from CSS or JavaScript in a webpack build. The README also lists a CDN link through Skypack and a download at the project's site.

What is the difference between sanitize.css and normalize.css?

Both correct browser bugs and are maintained in sync, but normalize.css styles adhere to CSS specifications while sanitize.css styles adhere to common developer expectations and preferences, per the README. sanitize.css includes every normalization from normalize.css and adds its own defaults.

Where can I download sanitize.css?

The README points to https://csstools.github.io/sanitize.css/latest/sanitize.css for a direct download, and the package is also installable from npm or linkable from a CDN.

How do I use sanitize.css in a project?

Add a stylesheet link for sanitize.css, or import it through your bundler, then optionally link the separate forms.css, assets.css, typography.css, reduce-motion.css, system-ui.css or ui-monospace.css files. Each separate file covers a different area, so you only include what you need.

Why do my styles not override sanitize.css?

The README states that sanitize.css wraps its styles in zero-specificity selectors using :where(), so its rules should not outrank your own selectors. If a rule still loses, the cause is more likely your own selector order or an !important elsewhere in your stylesheet than the foundation itself.

Official sources

  1. csstools/sanitize.css on GitHub
  2. License: CC0-1.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/csstools-sanitize-css.svg)](https://hysenlabs.com/projects/csstools-sanitize-css)