Using the CSS Custom Highlight API with JavaScript
The CSS Custom Highlight API provides a performant, native way to
style arbitrary document text ranges using JavaScript without modifying
the underlying DOM tree. This article covers the core mechanics of the
API, explaining how to define DOM Range objects, bundle
them into a Highlight instance, register them in the global
CSS.highlights map, and apply visual styles using the CSS
::highlight() pseudo-element.
How the Custom Highlight API Works
Historically, styling specific text ranges—such as search result
matches, spellcheck errors, or collaborative editing cursors—required
wrapping text inside inline HTML elements like <span>
or <mark>. This DOM mutation is computationally
expensive and can disrupt accessibility and layout.
The CSS Custom Highlight API decouples text selection from DOM mutation by connecting programmatic JavaScript ranges directly to the CSS rendering engine through four key steps:
- Creating Ranges (
Range): JavaScript identifies the text boundaries in the DOM and creates one or more standardRangeobjects. - Instantiating a Highlight (
Highlight): TheRangeobjects are passed to anew Highlight()instance. - Registering the Highlight
(
CSS.highlights): The highlight is registered with a unique custom name in theCSS.highlightsregistry map. - Applying Styles (
::highlight()): CSS targets the registered identifier using the::highlight(name)pseudo-element to apply colors, text decorations, or background styles.
Step-by-Step Implementation
1. Identify and Create Text Ranges
First, use standard DOM methods to locate text nodes and create
Range objects for the start and end offsets you want to
style.
const paragraph = document.querySelector("p");
const textNode = paragraph.firstChild;
const range1 = new Range();
range1.setStart(textNode, 0);
range1.setEnd(textNode, 5);
const range2 = new Range();
range2.setStart(textNode, 10);
range2.setEnd(textNode, 18);2. Create and Register the Highlight Object
Pass the ranges to the Highlight constructor. A single
Highlight object can contain multiple ranges. Then, add the
highlight object to the global CSS.highlights registry
using a custom string identifier.
// Bundle ranges into a Highlight object
const customHighlight = new Highlight(range1, range2);
// Register it under the name "search-results"
CSS.highlights.set("search-results", customHighlight);3. Style the Highlight in CSS
In your stylesheet, use the ::highlight() pseudo-element
along with your chosen identifier to declare the styling rules.
::highlight(search-results) {
background-color: #ffeb3b;
color: #000000;
text-decoration: underline;
}Supported CSS Properties
Because custom highlights operate similarly to the
::selection pseudo-element, only a subset of CSS properties
can be applied:
colorbackground-colortext-decorationand its related properties (text-decoration-color,text-decoration-line, etc.)text-shadowstroke-color,fill-color, andstroke-width(for SVG text)
Properties that alter layout or geometry (such as
margin, padding, display, or
font-size) are not supported within
::highlight().
Dynamic Updates and Lifecycle Management
The Highlight object acts as a Set-like
collection. Modifying the ranges inside an active Highlight
automatically updates the rendered styles on the screen without
requiring a re-registration:
// Add a new range dynamically
customHighlight.add(newRange);
// Remove a range
customHighlight.delete(range1);
// Clear all highlights
CSS.highlights.delete("search-results");This reactive design makes the CSS Custom Highlight API ideal for high-frequency operations, such as live search filtering, syntax highlighting, and code annotation tools.