The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| 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 range | const range = new Range(); range.setStart(text, 0); range.setEnd(text, 6); | View examples |
| Register a named highlight | CSS.highlights.set("search-match", new Highlight(range)); | View examples |
| Remove one highlight | CSS.highlights.delete("search-match"); | View examples |
| Clear the registry | CSS.highlights.clear(); | View examples |
| Add another range | highlight.add(nextRange); | View examples |
| Remove one range | highlight.delete(range); | View examples |
| Create immutable boundaries | new StaticRange( {
startContainer:
text, startOffset: 0, endContainer: text, endOffset: 6;
} | View examples |
| Set overlap priority | highlight.priority = 10; | View examples |
| Declare semantic highlight type | highlight.type = "spelling-error"; | View examples |
| Detect the registry | if ("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
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 { color:#fff; background-color:#1d4ed8; }
::spelling-error { text-decoration: wavy underline #dc2626; }
::grammar-error { text-decoration: wavy underline #2563eb; } 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.
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)); 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.
const matches = CSS.highlights.get("search-match");
matches.add(nextRange);
matches.delete(staleRange);
// On teardown:
CSS.highlights.delete("search-match"); 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.
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); 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.
::highlight(search-match) { color:#172033; background-color:#fde68a; text-decoration:underline; text-decoration-thickness:.12em; } 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.
<p><mark class="fallback-match">deployment</mark> completed successfully.</p>
<p id="result-count" role="status">1 match</p> .fallback-match { background:#fde68a; color:#172033; }
.highlights-ready .fallback-match { background:transparent; color:inherit; } 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.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



