v158 · css · custom highlight api
CSS Custom Highlight Universal Selector ::highlight(*)
The Custom Highlight API styles arbitrary text ranges with no extra DOM — but every registered highlight needed its own ::highlight(name) rule, so shared styling meant repeating the same declarations for each name. ::highlight(*) matches the highlight pseudo-elements of all registered highlights, and its * argument contributes zero specificity: it is a base layer that any named rule overrides through the cascade, whatever the source order.
concepts
-
Universal base style
Register three real
Highlightobjects over live text — that part ships everywhere Chrome 105+. Then inject a::highlight(*)rule through a constructed stylesheet and watch the exact parse verdict: one rule styles every highlight where supported, with the honest per-name repetition fallback shown working beside it. -
Zero-specificity cascade
The subtle half of the contract. Flip the source order of a universal rule and a named rule and read the painted colours back with
getComputedStyle(el, "::highlight(name)")— the named rule must win even when the universal rule comes last, because*adds nothing to specificity. -
Multi-category annotator
A working find-in-page tool: search the article, every match becomes a
search-matchhighlight and the active one a higher-prioritysearch-currenthighlight with keyboard next/previous.::highlight(*)is the shared chrome every category inherits — one rule instead of one per category.
why it shipped
Authors building spell-checkers, collaborative cursors, syntax colouring, or search highlighting register many named highlights whose styles share a base (a contrast-safe background, an underline, a transition). Without a universal form, those declarations are duplicated per name and drift apart. The CSSWG resolved to allow ::highlight(*) with zero specificity from the argument so it layers cleanly under existing named rules — the same shape as * in selectors generally. Chrome 158 implements it behind CSSCustomHighlightUniversalSelector on the way to stable.
the API
/* base layer for EVERY registered highlight — zero specificity */
::highlight(*) {
background-color: oklch(95% 0.05 95);
text-decoration: underline dotted;
}
/* any named rule overrides the base, regardless of order */
::highlight(spelling-error) { text-decoration: underline wavy red; }
Only highlight-safe properties apply (color, background-color, text-decoration and friends) — that part is unchanged from the shipped Custom Highlight API.