CSS Vendor Prefixes: Fixing Browser-Prefix Gaps
A prefix gap is a style that works in one browser and quietly does nothing in another, because the rule was written only in a form that browser does not understand. The usual victims are iPhone and Mac visitors on Safari, where a frosted header, clipped text or a truncated card title can fall back to something plainer or broken.
The opposite problem is just as common: stylesheets full of prefixes nobody has needed for years. This guide shows how to find both, decide which browsers you actually support, and hand the job to a build tool so you stop writing prefixes by hand.
What a prefix gap actually is
Vendor prefixes date from a period when browser makers shipped experimental CSS under their own names:
-webkit- for Safari and, later, Chrome; -moz- for Firefox; -ms-
for Internet Explorer and old Edge; -o- for the old Presto-based Opera. The idea was that
developers would use the prefixed version while a feature settled, then switch to the standard name.
In practice many sites kept both forever, and some features never lost their prefix at all.
A CSS Checker prefix finding can mean one of three different things, and each needs a different response:
| Pattern | Example | What happens | What to do |
|---|---|---|---|
| Missing prefix | backdrop-filter: blur(8px) with no -webkit-
copy | Older Safari versions that only knew the prefixed name ignore the effect | Let Autoprefixer add it if your browser list includes those versions |
| Prefix only, no standard | -webkit-user-select: none alone | Browsers that dropped or never had the prefix ignore it | Add the standard property after the prefixed one |
| Outdated prefix | -moz-border-radius, -webkit-box-shadow | Nothing breaks, but bytes are wasted and the file is harder to read | Delete, or let the build tool strip it |
Only the first two affect visitors. The third is housekeeping. Keep that distinction in mind when you prioritise, because a long list of outdated prefixes can look alarming while being harmless, and a single prefix-only declaration on your checkout button can be the one that matters.
Finding the gaps in your own stylesheets
Start with the CSS Checker report, which scans your stylesheets for prefix coverage. Then confirm the findings in your source files, because the checker sees the compiled CSS the browser receives, while you need to fix the file you edit.
Search the source
A quick search shows how many prefixed declarations you have and where they live:
grep -rnoE "(-webkit-|-moz-|-ms-|-o-)[a-z-]+" src/ --include="*.css" --include="*.scss" | sort | uniq -c | sort -rn | head -40
Read the top of that list. If it is dominated by -webkit-transition, -webkit-transform,
-moz-box-sizing or -webkit-border-radius, you are looking at legacy code that
can simply go. If you see -webkit-line-clamp, -webkit-text-stroke or -webkit-tap-highlight-color,
those are features that only exist in prefixed form, and they need to stay.
Check each property against real support data
For anything you are unsure about, look it up on Can I Use. The page for each property shows which
browser versions need a prefix, and the "usage relative" view shows how much of your audience that represents.
Pay attention to the notes under the table: a property can be listed as supported while one value, such
as background-clip: text, has a separate history.
See the failure in the browser
In browser developer tools, select the element and open the Styles pane. Declarations the browser does not recognise are struck through, and Chromium shows a warning icon with "Invalid property name" or "Invalid property value". Firefox shows a similar warning. Safari's Web Inspector does the same, which is the view you most need, because Safari is where most real prefix gaps show up.
-moz-appearance is expected. The question is always whether some browser in your supported
list gets neither the prefixed nor the standard version.Choosing a browser list you can defend
Prefix tools do not guess. They read a list of target browsers, usually written in the browserslist format, and add only the prefixes those browsers need. The same list is read by Babel, PostCSS, Lightning CSS, Stylelint plugins and several bundlers, so setting it once keeps your CSS and JavaScript aligned.
The simplest sensible starting point is the built-in defaults query, which combines a
usage threshold, recent versions, the Firefox extended support release and a rule to exclude browsers
that no longer receive updates. Put it in package.json or a .browserslistrc
file at the project root:
# .browserslistrc
defaults
not op_mini all
Then check what that list expands to, rather than trusting a query you have not read:
npx browserslist
npx update-browserslist-db@latest
The first command prints every browser version your query currently includes. The second refreshes the usage data the queries are based on; if you skip it for a long time, a build tool can keep adding prefixes for browsers that have long since faded away.
Use your own analytics where you can
Global usage figures are a proxy. A trade supplier whose customers use locked-down office PCs, a charity whose visitors skew towards older iPads, and a design studio whose visitors are all on the latest Safari have very different real audiences. Browserslist can read a usage file exported from your own analytics, so the list reflects your visitors rather than the world average. Even without that, look at your analytics browser report once a year and adjust the query if a group of older devices matters to you.
The fix, step by step
Step 1
List every prefixed and prefix-only rule
Run the CSS Checker on your main templates and note each prefix finding with its file and
selector. Then search your source files for -webkit-, -moz-, -ms-
and -o- and count them by property. Sort the results into three piles: prefixes that are
missing for a browser you care about, prefixed declarations with no standard version beside them, and
outdated prefixes that no current browser uses. Only the first two piles affect visitors.
Step 2
Decide which browsers you support
Create
a .browserslistrc file at the project root, or a browserslist key in package.json,
and start from the defaults query. Run npx browserslist to see the exact versions
it includes and compare them with the browser report in your analytics. Add or exclude versions deliberately,
write a short comment explaining the choice, and refresh the usage data with npx update-browserslist-db@latest
so the list does not drift out of date.
Step 3
Let a build tool add and remove prefixes
Install Autoprefixer as a PostCSS plugin, or turn on prefixing in Lightning CSS if your
bundler already uses it, and point it at the same browser list. Then delete the hand-written prefixes
for standard properties such as transform, transition, box-shadow,
user-select and backdrop-filter from your source. The tool adds back only what
your list needs and, by default, strips prefixes that no listed browser requires, so the output stays
correct as browsers improve.
Step 4
Handle the non-standard -webkit- features by hand
Some features exist only in prefixed form and build tools will not invent them
for you. Keep -webkit-line-clamp together with display: -webkit-box and -webkit-box-orient: vertical
for multi-line truncation, -webkit-text-stroke for outlined text, -webkit-tap-highlight-color
for touch feedback and ::-webkit-scrollbar styling alongside the standard scrollbar-width
and scrollbar-color. Give each one a sensible fallback so unsupported browsers still see
readable content, and add a short comment so nobody deletes them during a later clean-up.
Step 5
Put the standard property last
Where a prefixed and a standard declaration sit in the same rule, the standard one must come after the prefixed copy so browsers that understand both use the finished, specified behaviour. Check hand-written blocks and third-party CSS you have copied in, because older snippets often reverse the order. For values that changed syntax over time, such as early gradient notation, remove the legacy prefixed version completely rather than leaving an outdated form in place.
Step 6
Test in real Safari and Firefox
Open the affected pages in the current Safari on a Mac or iPhone and in Firefox, not just in Chrome with a different window size. Check the specific components the checker flagged: blurred headers, clipped gradient text, truncated card titles, custom form controls and scrollbars. Use the Styles pane to confirm nothing important is struck through, then run the CSS Checker again and confirm the prefix findings for supported browsers have cleared.
Setting up Autoprefixer or Lightning CSS
Most modern build setups already include one of these two tools, so check before adding another.
Autoprefixer through PostCSS
Autoprefixer is a PostCSS plugin. Vite, Next.js, Parcel and most starter kits run PostCSS automatically if a config file exists.
npm install --save-dev postcss autoprefixer
// postcss.config.js
module.exports = {
plugins: {
autoprefixer: {}
}
};
With no options, Autoprefixer reads your browserslist file, adds prefixes the listed browsers need and removes outdated ones. To see what it will do before changing any files, run it from the command line against a single stylesheet and compare the output with the input.
Lightning CSS
Lightning CSS combines parsing, minifying and prefixing in one fast tool, and Vite can use it as its
CSS transformer. It takes a targets setting, which you can build from the same browserslist
query:
// vite.config.js
import { browserslistToTargets } from 'lightningcss';
import browserslist from 'browserslist';
export default {
css: {
transformer: 'lightningcss',
lightningcss: {
targets: browserslistToTargets(browserslist('defaults'))
}
},
build: { cssMinify: 'lightningcss' }
};
Do not run both tools on the same CSS with different targets. You will get prefixes added by one and removed by the other, and a result that depends on plugin order.
Sites with no build step
A plain static site or a theme edited through a web interface has no build pipeline to hook into. You still have options: run Autoprefixer once from the command line on the stylesheet before uploading it, or keep a short, deliberate set of hand-written prefixes for the handful of properties that need them. What you should not do is copy prefix blocks from old tutorials, which is how most dead prefixes arrive in the first place.
Properties that still trip people up
Support changes over time, so treat the notes below as a checklist of where to look, and confirm the current state on Can I Use before relying on it.
| Feature | Why it causes gaps | Safe pattern |
|---|---|---|
backdrop-filter | Safari shipped it under -webkit- for years
before the standard name | Write the standard property and let the build tool add the prefix for older Safari; give the element a semi-opaque background as a fallback |
user-select | Safari has long relied on -webkit-user-select | Write the standard property and let the build tool add the prefix for older Safari |
| Multi-line truncation | -webkit-line-clamp only works with the old -webkit-box
display mode | Keep all three prefixed declarations together plus overflow: hidden |
| Gradient text | background-clip: text needed the -webkit- form
in many browsers for a long time | Keep a solid color fallback so text is never invisible |
| Scrollbar styling | WebKit pseudo-elements and the standard properties are two separate systems | Use scrollbar-color and scrollbar-width, plus ::-webkit-scrollbar
rules for browsers without them |
text-size-adjust | Mobile Safari uses the prefixed form to stop text inflation in landscape | -webkit-text-size-adjust: 100% alongside the standard property |
Form control appearance | Old snippets use -webkit-appearance
and -moz-appearance only | Use the standard appearance: none; prefixes
are rarely needed now |
Multi-line truncation, written correctly
.card-title {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
}
The unprefixed line-clamp line is there for the future; the prefixed trio is what does
the work today in current browsers.
Gradient text with a fallback
.headline {
color: #1a1a2e;
background: linear-gradient(90deg, #1a1a2e, #3b5bdb);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
Using -webkit-text-fill-color rather than setting color to transparent keeps
the solid colour as the fallback for any browser that does not understand the fill property. For extra
safety, wrap the clip and fill lines in a feature query for background-clip: text, so they
are only applied where the clip itself works.
Using @supports instead of hoping
Prefixes answer "what is this feature called in that browser". Feature queries answer "does this browser support it at all". For anything visual that must degrade gracefully, a feature query is often the cleaner tool:
.site-header {
background: rgba(255, 255, 255, 0.95);
}
@supports ((backdrop-filter: blur(8px)) or (-webkit-backdrop-filter: blur(8px))) {
.site-header {
background: rgba(255, 255, 255, 0.7);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
}
The header is readable everywhere. Browsers that can blur get a lighter, more transparent background; browsers that cannot keep a near-solid one. Without the query, a see-through header with no blur can make navigation text hard to read over busy images.
Feature queries are also the right way to adopt newer layout features such as subgrid or container
queries: write a working baseline first, then layer the enhancement inside @supports.
Keeping prefixes from creeping back
- Ban hand-written prefixes in source. Stylelint's
property-no-vendor-prefix,value-no-vendor-prefixandselector-no-vendor-prefixrules flag them during editing. Add exceptions for the genuinely prefix-only features you rely on. - Refresh browser data on a schedule. Run
npx update-browserslist-db@latestas part of regular dependency updates so prefixes are added and removed based on current usage. - Review the browser list yearly. Compare it with your analytics. Dropping a long-gone browser version can remove prefixes and fallbacks across the whole site.
- Keep a Safari check in your release routine. Most prefix problems are Safari problems. A two-minute look at key pages on an iPhone catches what automated checks miss.
- Be wary of pasted snippets. CSS copied from old tutorials, answers and code generators often carries prefixes from years ago, sometimes in the wrong order.
Once prefixes are generated rather than written, they stop being a thing you maintain. For the related problem of rules that browsers reject outright, see Fixing CSS validation errors; validators often list prefixed properties as warnings, and that guide explains which of those you can ignore.
1. Do I still need vendor prefixes at all?
For standard properties, you rarely need to write them yourself. Most everyday CSS, including flexbox, grid, transforms, transitions, animations and rounded corners, has worked unprefixed in every current browser for years. A few features still need a prefix in some Safari versions, and a small group of useful features only exist in prefixed form. The practical answer is to stop writing prefixes by hand and let Autoprefixer or Lightning CSS add the ones your chosen browser list actually needs.
2. Will removing old prefixes break my site?
Removing prefixes for properties that every supported browser understands unprefixed will not change anything a visitor sees, as long as the standard declaration is present in the same rule. The risk is with prefix-only features such as line clamping, text stroke and WebKit scrollbar styling, which have no standard equivalent that works everywhere yet. Remove the outdated ones through your build tool rather than by hand, then check key pages in Safari and Firefox before and after the change.
3. Why does my design look different in Safari?
Safari is the browser most likely to need a prefixed property, and it is also the engine behind every browser on older iPhones and iPads. If a blur, gradient text effect, truncated title or custom form control looks wrong only there, check the element in Safari's Web Inspector. A struck-through declaration or a missing prefixed copy is the usual cause. Sometimes the feature is simply unsupported in that version, in which case a fallback inside a feature query is the fix.
4. What is the difference between Autoprefixer and browserslist?
Browserslist is a shared way of describing which browsers your project supports, written as short queries such as defaults or last 2 versions. Autoprefixer is a tool that reads that list and adds or removes vendor prefixes in your CSS to match it. Other tools, including Babel, Lightning CSS and some linters, read the same browserslist setting. Keeping one list in package.json or a .browserslistrc file means all of them agree on what your site needs to support.
5. Why does the CSS validator complain about my -webkit- properties?
Vendor-prefixed properties are deliberately outside the standard, so a strict validator reports them as unknown or as warnings. That does not mean they are wrong. A prefix that a supported browser needs, or a prefix-only feature you use on purpose, is fine to keep. What matters is that each prefixed declaration is either genuinely required for a browser on your list, or is paired with the standard property placed after it, so current browsers use the finished version.
6. Should I write -moz- and -ms- prefixes for Firefox and Edge?
Almost never in new code. Modern Edge uses the same Chromium engine as Chrome, and Internet Explorer no longer receives updates, so -ms- prefixes only serve browsers most sites have stopped supporting. Firefox has dropped the prefix requirement for nearly every mainstream property. If a -moz- prefix is genuinely needed for a browser in your list, Autoprefixer will add it automatically. Hand-written -moz- and -ms- lines in older stylesheets can usually be deleted once a build tool is in place.
7. Can Autoprefixer add the -webkit-line-clamp prefix for me?
No. Autoprefixer adds prefixes for standard properties that some browsers only understand in prefixed form. Line clamping works through an older WebKit display mode that needs three prefixed declarations together, and the build tool does not create those from the standard line-clamp property. Write the display, box-orient and line-clamp declarations yourself, add overflow hidden, and include the unprefixed line-clamp line as well so the rule keeps working as browsers adopt the standard version.
The CSS Checker checks vendor-prefix coverage alongside W3C validation, render-blocking and unused CSS, specificity issues and stylesheet size, so you can see which prefix gaps affect real browsers.
Open CSS Checker Fixes โ