CSS validation errors fall into two camps: syntax errors that break the parser, and semantic errors (unknown properties, deprecated values) that the browser silently ignores. Browsers apply error recovery generously, so most errors don't visibly break pages — but ignored rules quietly fail, codebases get harder to maintain, and old hacks accumulate. This guide walks through audit, fixing, and adding CI linting to prevent recurrence. For related fixes, see the CSS Checker Fixes index.
/* Missing semicolon */
.button {
background: blue
color: white; /* This rule is now part of background's value */
}
/* Unclosed brace */
.card {
padding: 20px;
/* missing } */
.button { ... } /* parser confused */
/* Typo in property name */
.text {
colour: red; /* colour not color - unknown property */
}
/* Extra characters */
.box {
margin: 10px;; /* double semicolon */
}
npm install --save-dev stylelint stylelint-config-standard
echo '{"extends": "stylelint-config-standard"}' > .stylelintrc.json
npx stylelint "**/*.css"
Properties that don't exist in any CSS spec are ignored by browsers. Causes: typos, IE-only properties from the 2000s, vendor-specific properties no longer needed.
/* IE 6 hack — drop it */
.box {
*display: inline;
zoom: 1;
}
/* Old prefixed properties no longer needed */
.flex {
-ms-flex-direction: column; /* IE10 — only matters if you support IE10 */
-webkit-flex-direction: column; /* Old WebKit — Autoprefixer handles if needed */
flex-direction: column;
}
/* Modern unprefixed version */
.flex {
flex-direction: column;
}
/* Invalid colour */
.text {
color: bluergh; /* not a colour */
}
/* Invalid unit */
.spacer {
height: 10pixels; /* should be 10px */
}
/* Out-of-range value */
.box {
z-index: -99999999999999; /* exceeds integer range, browser-dependent */
}
/* Wrong type for property */
.button {
padding: red; /* padding takes length, not colour */
}
Some CSS properties were dropped, renamed, or replaced by modern equivalents. The CSS Checker flags these as deprecated.
| Deprecated | Modern replacement |
|---|---|
filter: alpha(opacity=50) | opacity: 0.5 |
-ms-filter with progid | Remove — IE9- only |
behavior: url(...) | Remove — IE-only htc files |
scrollbar-base-color etc | scrollbar-color (CSS spec) |
-webkit-box-flex | flex |
page-break-* | break-* |
.browserslistrc:
> 0.5% last 2 versions not deadAutoprefixer reads this and adds ONLY the prefixes those browsers need. No more guessing whether to add
-webkit- or -moz-.
/* DON'T write this manually */
.box {
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
-o-transition: all 0.3s;
transition: all 0.3s;
}
/* WRITE this — Autoprefixer adds what's needed */
.box {
transition: all 0.3s;
}
Most sites compile CSS from preprocessors (Sass, Less, PostCSS) or CSS-in-JS. The line numbers in the CSS Checker report point at the compiled output, not the source.
tailwind.config.js theme extensions and plugins.
name: CSS lint
on: [pull_request]
jobs:
stylelint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npx stylelint "**/*.css" "**/*.scss"
Fail PRs that introduce new validation errors. Zero new errors policy is achievable; gradually reducing pre-existing errors is the way to fix legacy CSS over time.
stylelint-config-standard is a sensible baseline. Add stylelint-order to enforce property ordering and stylelint-no-unsupported-browser-features to catch usage of CSS your target browsers don't support.