range thumb, placeholder, upload button and autofill under control
Some parts of a form control can still only be styled through vendor specific pseudo-elements, because there is no standardized selector for them. Anyone who wants range sliders, file upload buttons and autofill backgrounds to look consistent cannot avoid webkit and moz prefixes, but has to use them in a structured, maintainable way.
Table of Contents
- 1. Why browser prefixes are still needed for form controls
- 2. Overview: which pseudo-elements exist where
- 3. Range slider thumb with webkit-slider-thumb and moz-range-thumb
- 4. Placeholder styling with ::placeholder and fallbacks
- 5. File upload button with webkit-file-upload-button
- 6. Search cancel button and spin buttons on number inputs
- 7. Overriding the autofill background with webkit-autofill
- 8. Maintenance strategy: mixins and custom properties instead of duplication
- 9. Pseudo-element support compared across browsers
- 10. Summary
- 11. FAQ
1. Why browser prefixes are still needed for form controls
Most CSS prefixes like -webkit-transform or -moz-user-select are history today, because the underlying properties have long since been standardized. The situation looks different for internal parts of form controls: the thumb of a range slider, the button of a file upload, or the spin arrows of a number field are not part of the open DOM, but part of the user agent shadow root, which every browser implements individually. There is still no standardized, prefix free selector for these parts, which is why vendor specific pseudo-elements remain indispensable in this area.
Concretely this means: anyone who wants to style the thumb of an input type range writes, in practice, two nearly identical rule blocks, one for ::-webkit-slider-thumb and one for ::-moz-range-thumb, because Chrome, Safari and Edge use the WebKit engine or Blink, while Firefox uses its own pseudo-elements with the -moz- prefix. This duplication looks inelegant at first glance, but it is the only approach that actually works, because a selector the browser does not recognize invalidates the entire rule if it sits in the same selector list.
2. Overview: which pseudo-elements exist where
Before styling individual form controls, it helps to get an overview of which pseudo-elements exist in which browser family. WebKit and Blink, that is Safari, Chrome, Edge and most Chromium based browsers, largely share the same ::-webkit-* pseudo-elements, because Blink historically originated from WebKit. Firefox uses its own set with the ::-moz-* prefix, structurally similar but not identically named.
The most important pseudo-elements for forms are ::-webkit-slider-thumb and ::-moz-range-thumb for range sliders, ::-webkit-slider-runnable-track and ::-moz-range-track for the track, ::-webkit-file-upload-button for file selection buttons, ::-webkit-search-cancel-button for the cross in search fields, and ::-webkit-inner-spin-button for the arrows in input type number. Firefox renders some of these elements fundamentally differently or skips them entirely, which has to be considered when planning.
/* Must be split into separate selector lists, one invalid selector
invalidates the entire rule in the same list */
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
}
input[type="range"]::-moz-range-thumb {
appearance: none;
}
/* WRONG: combining vendor pseudo-elements in one selector list
invalidates the whole rule in most browsers */
input[type="range"]::-webkit-slider-thumb,
input[type="range"]::-moz-range-thumb {
background: red; /* never applied anywhere */
}
3. Range slider thumb with webkit-slider-thumb and moz-range-thumb
The thumb of a range slider is the element most frequently styled individually, say with a shadow, a custom shape, or a hover effect. To fully replace the native thumb, appearance: none is first set on the pseudo-element itself, after which width, height, radius, background and transition can be freely defined, exactly like any other block element. It matters that -webkit-appearance: none is set in addition to the standardized appearance: none, because Safari supported the standard property with a delay in some versions.
A common stumbling block: the size of the range element itself also has to be reset via appearance: none on the base element, otherwise the native border overlaps the custom thumb styling. In addition, the vertical offset of the thumb behaves differently in WebKit than in Firefox, because WebKit positions the thumb via margin-top relative to the track height, while Firefox centers it automatically. These detail differences are the main reason a range slider rarely looks consistent on the first try without testing in several real browsers.
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
background: #e5e7eb;
border-radius: 999px;
}
/* Chrome, Safari, Edge (Blink/WebKit) */
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #7c3aed;
box-shadow: 0 2px 6px rgba(124, 58, 237, 0.4);
cursor: pointer;
/* WebKit positions the thumb relative to the track height manually */
margin-top: -7px;
}
/* Firefox */
input[type="range"]::-moz-range-thumb {
width: 20px;
height: 20px;
border: none;
border-radius: 50%;
background: #7c3aed;
box-shadow: 0 2px 6px rgba(124, 58, 237, 0.4);
cursor: pointer;
}
4. Placeholder styling with ::placeholder and fallbacks
The ::placeholder pseudo-element is standardized by now and no longer needs a prefix in current browsers, yet it is historically closely related to the prefix topic, because older Firefox versions used ::-moz-placeholder with a single colon and a different opacity. In current projects ::placeholder alone is enough, but anyone needing legacy support for Firefox below version 51 still adds ::-moz-placeholder as a separate rule.
A detail that regularly surprises developers: Firefox long applied a reduced opacity of 0.54 to placeholder text, which led to inconsistent color perception compared to Chrome, where the full defined color was shown. The fix is to explicitly set opacity: 1 in the ::placeholder rule, regardless of browser, to ensure the chosen color renders identically everywhere.
/* Standardized, no prefix needed in current browsers */
input::placeholder,
textarea::placeholder {
color: #9ca3af;
opacity: 1; /* Firefox historically applied reduced opacity by default */
}
/* Legacy fallback for very old Firefox versions (single colon syntax) */
input:-moz-placeholder {
color: #9ca3af;
opacity: 1;
}
5. File upload button with webkit-file-upload-button
The button inside an input type file, which by default shows "Choose File" in Chrome and Safari, can only be styled individually through ::-webkit-file-upload-button, in WebKit and Blink based browsers. Firefox renders this button internally in a different way and offers no direct equivalent pseudo-element, which is why file upload fields in Firefox are usually styled via a hidden native input plus a custom label element, instead of direct pseudo-element styling.
For a consistent appearance across all browsers, the label technique is generally recommended: the native input type file is visually hidden but kept accessible, with opacity: 0 and exact positioning over the visible label, which is styled as a button. This technique works independently of the browser, because it does not rely on pseudo-element styling, while ::-webkit-file-upload-button remains useful in projects that only need to support Chromium browsers, say internal admin tools.
/* Direct pseudo-element styling, Chromium/WebKit only */
input[type="file"]::-webkit-file-upload-button {
background: #7c3aed;
color: white;
border: none;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
font-weight: 600;
cursor: pointer;
margin-right: 1rem;
}
input[type="file"]::-webkit-file-upload-button:hover {
background: #6d28d9;
}
/* Cross-browser fallback: visually hidden native input plus styled label */
.file-input-wrapper input[type="file"] {
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.file-input-wrapper label {
display: inline-block;
background: #7c3aed;
color: white;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
font-weight: 600;
}
6. Search cancel button and spin buttons on number inputs
Two more form details that can only be controlled through vendor specific pseudo-elements: the small cross icon for clearing an input type search, controlled through ::-webkit-search-cancel-button, which can be removed or restyled, and the up and down arrows of an input type number, addressed through ::-webkit-inner-spin-button and ::-webkit-outer-spin-button. Many design systems remove these spin buttons entirely, since custom increment and decrement buttons built with JavaScript or Alpine.js fit the layout more precisely.
Firefox handles both cases differently: the search cancel button simply does not exist there as a separate element, and the spin buttons of a number input are removed via the standard property appearance: textfield on the input itself, not through a pseudo-element. A robust, cross-browser pattern therefore combines both approaches: the WebKit pseudo-element for Chrome and Safari, plus the standard property appearance: textfield, which takes effect immediately in Firefox and acts as a redundant but harmless addition in WebKit browsers.
/* Remove the search cancel cross (WebKit/Blink only) */
input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
}
/* Remove spin buttons on number inputs, cross-browser approach */
input[type="number"] {
-moz-appearance: textfield; /* Firefox: removes spinners via standard property */
appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
appearance: none;
margin: 0;
}
7. Overriding the autofill background with webkit-autofill
One of the most stubborn styling problems in forms is the yellowish background that Chrome and Safari automatically apply to filled fields as soon as the browser autofills credentials or address data. This background cannot be overridden with background-color, because the browser renders the background color of the ::-webkit-autofill state with higher priority than regular CSS rules. The established solution is a trick using box-shadow: a large, invisible inset shadow in the desired background color completely covers the autofill background, without any real shadow effect becoming visible.
In addition, -webkit-text-fill-color should be set, because Chrome sometimes overrides the text color in filled fields too and ignores the regular color property while doing so. A very long transition-delay on background-color is another widespread trick that effectively delays the automatic color change indefinitely, so the custom background color is never visually overridden. Firefox does not have this problem in the same form, because it does not mark autofill with its own background color.
/* The classic box-shadow trick to override the yellow autofill background */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
box-shadow: 0 0 0 1000px white inset;
-webkit-text-fill-color: #1f2937;
/* Delay the color transition so the browser's own background never shows */
transition: background-color 600000s 0s, color 600000s 0s;
}
/* Dark mode variant: match the surrounding form background */
@media (prefers-color-scheme: dark) {
input:-webkit-autofill {
box-shadow: 0 0 0 1000px #1e293b inset;
-webkit-text-fill-color: #f1f5f9;
}
}
8. Maintenance strategy: mixins and custom properties instead of duplication
Because vendor specific pseudo-elements almost always come in pairs, webkit plus moz, it is worth adopting a structured maintenance strategy that reduces duplication without avoiding the need for separate selector lists. In Sass or Less, a mixin can be defined that generates both rule blocks from a shared set of declarations, so changes only need to happen in one place in the source. In plain CSS without a preprocessor, custom properties take on this role: a shared set of variables for color, size and shadow is referenced in both pseudo-element rules.
This strategy pays off especially for range sliders and file upload buttons, because here typically five to eight properties are identical across both prefix variants and only one or two properties differ by browser, say the vertical offset of the range thumb. Anyone maintaining a design system should bundle these pseudo-element rules centrally in a single form base file, so that later brand color adjustments do not have to be manually replicated in several places throughout the project.
/* Shared design tokens referenced by both vendor pseudo-elements */
:root {
--thumb-size: 20px;
--thumb-color: #7c3aed;
--thumb-shadow: 0 2px 6px rgba(124, 58, 237, 0.4);
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: var(--thumb-size);
height: var(--thumb-size);
border-radius: 50%;
background: var(--thumb-color);
box-shadow: var(--thumb-shadow);
margin-top: calc((var(--thumb-size) - 6px) / -2);
}
input[type="range"]::-moz-range-thumb {
width: var(--thumb-size);
height: var(--thumb-size);
border: none;
border-radius: 50%;
background: var(--thumb-color);
box-shadow: var(--thumb-shadow);
}
9. Pseudo-element support compared across browsers
The following overview shows which of the most important vendor specific pseudo-elements are available in which browser family, and whether an alternative exists through standard properties.
| Form detail | Chrome / Safari / Edge | Firefox | Standard alternative |
|---|---|---|---|
| Range thumb | ::-webkit-slider-thumb |
::-moz-range-thumb |
None |
| Placeholder | ::placeholder |
::placeholder |
Standardized |
| File upload button | ::-webkit-file-upload-button |
No equivalent | Label overlay technique |
| Number spin buttons | ::-webkit-inner-spin-button |
appearance: textfield |
Combination of both |
| Autofill background | :-webkit-autofill |
No special styling needed | box-shadow inset trick |
The table shows a clear pattern: only the placeholder is fully standardized, all other details either require a pseudo-element per browser family or a completely different approach, say the label overlay technique for file upload. Anyone building a form system for production use should treat this table as a checklist for browser testing before a release is approved.
Mironsoft
Cross-browser forms and CSS design systems
Form controls that look the same in every browser?
We build range sliders, file upload fields and autofill styles that work consistently in Chrome, Firefox and Safari, with a clean maintenance strategy instead of duplicated CSS.
Browser testing
Check forms for consistency across all relevant engines
Custom widget build
Implement range sliders and upload buttons with a mixin strategy
Autofill fixes
Fix background and text color for automatically filled fields
10. Summary
Vendor specific pseudo-elements remain necessary because parts of form controls like the range thumb, the file upload button and the autofill background are still not fully standardized today. The rule: separate selector lists for ::-webkit-* and ::-moz-*, because an unrecognized selector invalidates the entire rule if both sit in the same list. Placeholder styling is standardized by now and no longer needs a prefix, while range thumb, upload button and autofill still require prefix bound solutions.
For sustainable maintainability, a central set of custom properties or a Sass mixin that generates both prefix variants from a shared source is worth the effort. The box-shadow inset trick against the autofill yellow and the combination of appearance: textfield and ::-webkit-inner-spin-button against number field arrows are among the most frequently needed patterns in production forms.
Pseudo-elements for inputs — the essentials at a glance
Range slider
Define webkit-slider-thumb and moz-range-thumb separately, never combine in one selector list.
Placeholder
Standardized, set opacity: 1 explicitly because of historical Firefox opacity behavior.
Autofill
box-shadow inset trick instead of background-color, plus webkit-text-fill-color for text color.
Maintainability
Use custom properties or mixins to reduce duplication between webkit and moz.