Vendor Prefixes
In the 2000s and early 2010s, browser vendors needed a way to let developers experiment with brand-new CSS features while the specification for those features was still being finalized — and without risking that an early, non-standard implementation would conflict with the eventual standardized syntax. Their answer was the vendor prefix.
The Prefixes
Prefix | Vendor / engine |
|---|---|
-webkit- | Safari / WebKit, and early Chrome / Blink |
-moz- | Firefox / Gecko |
-ms- | Internet Explorer / old Edge |
-o- | Old Opera / Presto |
/* How prefixed CSS used to look, back when transforms and
gradients were new and experimental */
.box {
-webkit-transform: rotate(10deg);
-moz-transform: rotate(10deg);
-ms-transform: rotate(10deg);
-o-transform: rotate(10deg);
transform: rotate(10deg); /* unprefixed standard syntax, added last */
}A prefixed property let a browser ship its own interpretation of a feature (say, gradients or transforms) ahead of the spec being locked down, then later add the unprefixed version once the syntax stabilized — at which point the prefixed version was typically kept around for backwards compatibility, then eventually dropped.
Why This Practice Faded
Vendor prefixes caused real problems. Developers started writing the -webkit- version and skipping the others (since WebKit/Blink dominated market share), which pressured other browsers into implementing -webkit- prefixed properties themselves just to render sites correctly — undermining the entire point of prefixing. Having learned from that web-compatibility pain, browser vendors today are far more cautious about shipping unprefixed, experimental CSS at all; features are more often gated behind developer flags or shipped only once a spec is stable, rather than being exposed prefixed to the public web.
Where Prefixes Still Show Up Today
/* Custom scrollbar styling still needs -webkit- in Chromium/Safari */
.scroll-area::-webkit-scrollbar {
width: 10px;
}
.scroll-area::-webkit-scrollbar-thumb {
background: #888;
border-radius: 5px;
}
/* appearance often still needs -webkit- alongside the standard property */
.reset-input {
-webkit-appearance: none;
appearance: none;
}