v158 · css · custom highlight api

Universal base style

Everything on this page except one selector ships in every current Chrome: Highlight objects over real Ranges, the CSS.highlights registry, named ::highlight(name) rules. The one new thing — ::highlight(*) — is injected through a constructed stylesheet so you see the parser’s real verdict, and the per-name repetition you ship today runs beside it as the clearly-labelled fallback.

Probing this browser…

Three live highlights

The paragraph below has three registered highlights — no <span> wrappers, inspect the DOM — built with a TreeWalker and Range objects at load time.

The Custom Highlight API lets a spel-chekker underline suspect words, lets a grammar pass flag sentences what read badly, and lets a glossary tint a term like pseudo-element — all without wrapping text in extra elements.

Named highlight rules are active. Inject the universal rule to probe the new selector.

what the inject button really does

const sheet = new CSSStyleSheet();
try {
  // An unknown selector makes insertRule throw SyntaxError —
  // this probe CAN fail, and does on every pre-158 engine.
  sheet.insertRule(
    "::highlight(*) { background-color: var(--bg-stone); " +
    "text-decoration: underline dotted; }");
  document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
} catch (e) {
  // SyntaxError: ::highlight(*) is not a valid selector here
}

// The fallback you ship today — same declarations, once per name:
// ::highlight(spelling), ::highlight(grammar), ::highlight(term) { … }

Note the double check available: CSS.supports("selector(::highlight(*))") gives the static answer, and insertRule gives the enforcing one. The page reports both — they must agree.

see also