The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Style selected text::selection { color: #fff; background-color: #1d4ed8; }View examples
Scope selection styling.editor::selection { background-color: #facc15; color: #172033; }View examples
Style spelling errors::spelling-error { text-decoration: wavy underline #dc2626; }View examples
Style grammar errors::grammar-error { text-decoration: wavy underline #2563eb; }View examples
Style find-in-page matches::search-text { background-color: #fde68a; color: #172033; }View examples
Style a text fragment::target-text { background-color: #bbf7d0; color: #172033; }View examples
Style a named highlight::highlight(search-match) { background-color: #fde68a; color: #172033; }View examples
Create a text rangeconst range = new Range(); range.setStart(text, 0); range.setEnd(text, 6);View examples
Register a named highlightCSS.highlights.set("search-match", new Highlight(range));View examples
Remove one highlightCSS.highlights.delete("search-match");View examples
Clear the registryCSS.highlights.clear();View examples
Add another rangehighlight.add(nextRange);View examples
Remove one rangehighlight.delete(range);View examples
Create immutable boundariesnew StaticRange( { startContainer: text, startOffset: 0, endContainer: text, endOffset: 6; }View examples
Set overlap priorityhighlight.priority = 10;View examples
Declare semantic highlight typehighlight.type = "spelling-error";View examples
Detect the registryif ("highlights" in CSS && "Highlight" in window) { enableHighlights(); }View examples
Use system highlight colors@media (forced-colors: active) { ::highlight(search-match) { color: HighlightText; background-color: Highlight; }; }View examples
Use interoperable highlight paint::highlight(search-match) { color: #172033; background-color: #fde68a; text-decoration: underline; }View examples

Highlight pseudo-elements paint over portions of text without creating ordinary boxes. Built-in highlights cover selection and user-agent states; the Custom Highlight API maps named collections of ranges to ::highlight() styles without wrapping the DOM. This guide explains the restricted property set, inheritance model, overlap priority, live ranges, fallbacks, and the accessibility work CSS highlights do not perform.

Step by step

Detailed examples

01

Style user-agent highlight states with paired colors

::selection, ::spelling-error, ::grammar-error, ::target-text, and newer search highlights represent ranges chosen by users or the UA. They inherit through a special highlight inheritance chain, not exactly like ordinary elements. Only a restricted set of paint and text-decoration properties applies; layout properties such as padding and display do not. Specify foreground and background together so inherited color cannot become unreadable.

Selection and error treatments
::selection { color:#fff; background-color:#1d4ed8; }
::spelling-error { text-decoration: wavy underline #dc2626; }
::grammar-error { text-decoration: wavy underline #2563eb; }
Back to quick reference ↑
02

Paint arbitrary ranges without wrapping the DOM

A Highlight is a setlike collection of AbstractRange objects. Register it in the document HighlightRegistry at CSS.highlights under a custom identifier, then style ::highlight(identifier). Custom highlights do not add elements, alter text selection, create DOM semantics, or affect layout. Registration replaces an existing entry with the same name. Feature-detect both the registry and constructor before using them.

Highlight every exact text match
const root = document.querySelector(".article").firstChild;
const range = new Range();
range.setStart(root, 0);
range.setEnd(root, 8);
CSS.highlights.set("search-match", new Highlight(range));
Back to quick reference ↑
03

Maintain boundaries as content changes

Range boundary points refer to nodes and offsets and generally adjust as the DOM mutates; StaticRange boundaries do not update. A collapsed range paints nothing. Ranges can cross element boundaries, and overlapping ranges within one Highlight paint as their union. Keep references when you need to add or delete individual ranges, clear obsolete registrations during navigation, and rebuild matches after text replacement or virtualization.

Update an existing highlight set
const matches = CSS.highlights.get("search-match");
matches.add(nextRange);
matches.delete(staleRange);
// On teardown:
CSS.highlights.delete("search-match");
Back to quick reference ↑
04

Control overlap without assuming ordinary stacking

Custom highlight overlays paint below built-in highlight overlays. Among custom highlights, priority determines relative order; registration order resolves ties. The API includes evolving support for type and hit-testing/event behavior, so treat those features as progressive enhancement. Highlight paint does not produce a normal box and cannot host controls or tooltips. Use DOM elements when content needs focus, activation, an accessible name, or durable annotation semantics.

Prioritize active over passive matches
const passive = new Highlight(...passiveRanges);
const active = new Highlight(activeRange);
passive.priority = 0;
active.priority = 10;
CSS.highlights.set("matches", passive);
CSS.highlights.set("active-match", active);
Back to quick reference ↑
05

Stay within the restricted paint model

Historically color and background-color are the most interoperable highlight properties; specifications also define selected text-decoration and text-shadow behavior. Unsupported properties are ignored. Custom highlights avoid inserting many wrapper spans, which can reduce DOM complexity for editors and search, but finding text and maintaining thousands of ranges still consumes CPU and memory. Batch updates and discard off-screen or stale results.

Interoperable core before decorative enhancement
::highlight(search-match) { color:#172033; background-color:#fde68a; text-decoration:underline; text-decoration-thickness:.12em; }
Back to quick reference ↑
06

Provide semantics, navigation, and contrast separately

A visual highlight alone is not announced and cannot communicate why text matters. Pair search with a result count and previous/next buttons; use mark for semantically relevant highlighted text when DOM markup is appropriate; expose annotations through accessible controls or descriptions. Test forced colors, dark mode, selection overlap, zoom, screen readers, keyboard navigation, and find-in-page. A DOM fallback must remain usable when the API is absent.

Semantic fallback plus optional custom paint
<p><mark class="fallback-match">deployment</mark> completed successfully.</p>
<p id="result-count" role="status">1 match</p>
Hide only fallback paint after enhancement
.fallback-match { background:#fde68a; color:#172033; }
.highlights-ready .fallback-match { background:transparent; color:inherit; }
Back to quick reference ↑

Local code tester

Style built-in and semantic fallback highlights

Select text and compare it with semantic mark elements; the playground works without JavaScript while documenting the Custom Highlight enhancement path.

Runs in your browser
Preview

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. CSS Working GroupCSS Custom Highlight API Module Level 1drafts.csswg.org
  2. CSS Working GroupCSS Pseudo-Elements Module Level 4drafts.csswg.org
  3. WHATWGDOM Standard: Rangesdom.spec.whatwg.org
  4. CSS Working GroupCSS Painting Orderdrafts.csswg.org
  5. MDN Web DocsCSS Custom Highlight APIdeveloper.mozilla.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback