Theming browser-generated UI

Most browser-generated UI can be customized to some extent using CSS. Even if it requires modern features, it degrades gracefully in older browsers, and thus often does not require a polyfill or fallback.

Before re-creating browser UI (form controls, scrollbars, selections, error messages, etc), first verify that:

  1. the browser UI cannot be customized enough for your needs, even with modern CSS,
  2. the desired customization is sufficiently critical to justify the tradeoffs of re-creating built-in UI — most notably losing accessible semantics, keyboard handling, IME, and AT integration that the native UI provides for free.

Example customizations that are possible:

  • Use ::selection to customize highlighted text colors.
  • DON'T apply user-select: none to content text — breaks copy-paste, translation tools, and AT "read from here" gestures. Limit it to chrome (drag handles, toolbars, redundant button labels).
  • Use accent-color to apply the page's accent color to any browser-generated UI.
  • Use color-scheme to have browser UI adapt to light/dark mode.
  • Use scrollbar-color to customize scrollbar colors — keep the thumb visibly distinct from the track (contrast ≥3:1)
  • DON'T use the scrollbar-width with any value other than auto, unless another accessible option is provided to scroll content. Although scrollbar-width: thin does provide a thumb, the target size of the thumb will be too small for some users to operate.
  • Use :user-invalid / :user-valid for validity styling, not :invalid / :valid — they only match after the user has interacted with the field, avoiding the hostile default of flagging required-empty fields as errors on page load. Exclude range sliders when targeting input broadly (e.g., input:not(input[type="range"]):user-valid) so adjusting a slider does not apply field validity borders or backgrounds to it.
  • Buttons and text fields (including <textarea>) can generally be styled as normal elements.
  • Use font-size to scale and other textual properties to control typography
  • Use text color contrast between the foreground and background colors of at least 4.5:1 for regular size text or at least 3:1 for large-scale text (18 point or 14 point bold font size).
  • Use a contrast between adjacent colors of at least 3:1 for non-text content.

Styling textual fields (<input> & <textarea>)

For most styling purposes (e.g. colors, borders, backgrounds, typography, etc) treat these elements as normal text containers.

  • Use :placeholder-shown and ::placeholder to style input placeholders.
  • Use field-sizing: content to make text fields size to content.
  • For <textarea> elements, use resize: vertical to disable horizontal resizing.
  • Do not use resize: none on <textarea> elements to disable all resizing, without an alternative resizing mechanism, such as using field-sizing: content; or providing custom resize buttons.

Multiple choice controls (select, radios, checkboxes)

  • To select one among many options presented in a dropdown: Use a <select> + appearance: base-select + ::picker(select). For more info see Branded Select Styling
  • Selecting one or more among multiple options laid out inline in the page: Use a <input type=checkbox> or <input type=radio> inside a <label> for each option. Style via label:has(:checked).
  • Style checkboxes, radios and switches via appearance: none + generated content (::before/::after) or background images to draw the checked state.

Non-textual <input>s (buttons, sliders, file inputs etc.)

  • File inputs: Use ::file-selector-button to style the button.
  • Do not use <input> with a type of button, submit or reset. Use <button> instead and style it as a regular element.
  • Sliders: Use appearance: none + thumb pseudo-elements (::-webkit-slider-thumb, ::-moz-range-thumb, etc) and track pseudo-elements (::-webkit-slider-runnable-track, ::-moz-range-track, etc) for more granular control.