Pseudo-Elements for Inputs: Using ::-webkit- and ::-moz- Precisely
AI generated
{ }
@
CSS · Forms · Pseudo-elements · Browser Compatibility
Pseudo-Elements for Inputs: Using ::-webkit- and ::-moz- Precisely
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.

16 min read webkit-slider-thumb · moz-range-thumb · placeholder · file-upload-button Chrome · Firefox · Safari · Edge

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.

11. FAQ: Pseudo-elements for inputs

1Why no standard range thumb selector?
The thumb lives in the browser internal shadow root, which was never standardized. webkit-slider-thumb and moz-range-thumb remain necessary.
2Can I combine both prefixes?
No, use separate rule blocks, otherwise an unrecognized selector invalidates the whole rule.
3Does placeholder still need a prefix?
No, fully standardized. Only very old Firefox versions need moz-placeholder additionally.
4Why doesn't background-color work against autofill?
The autofill background renders with higher priority. The box-shadow inset trick is the established workaround.
5How do I style the upload button in Firefox?
Firefox has no equivalent, the usual solution is a hidden input plus a styled label as the button.
6How do I remove number spin arrows?
appearance: textfield for Firefox, plus webkit-inner-spin-button and webkit-outer-spin-button with appearance none for Chrome and Safari.
7What does the search cancel button do?
Controls the cross for clearing an input type search. Firefox has no such element.
8How do I reduce duplication?
Use custom properties or a Sass mixin generating both prefix variants from a shared declaration.
9Why does the thumb position differ?
WebKit positions via margin-top relative to track height, Firefox centers automatically.
10Should I replace spin buttons?
Often useful in design systems, native arrows are usually enough for internal tools.