Using the CSS Custom Highlight API for Text Highlighting, Rainbow Text, Search Highlight, and Code Editors
This article introduces the browser‑native CSS Custom Highlight API, explains its pseudo‑element ::highlight, shows how to create ranges and Highlight objects with JavaScript, and demonstrates practical examples such as custom text styling, rainbow text, live search highlighting, performance comparisons, and a lightweight code‑highlight editor.
The CSS Custom Highlight API is a new browser feature (supported natively in Chrome 105+) that allows developers to style arbitrary text without modifying the DOM structure. It introduces the pseudo‑element ::highlight(custom‑highlight‑name) and a set of CSS properties (color, background‑color, text‑decoration, text‑shadow, -webkit-text-stroke, -webkit-text-fill-color) similar to ::selection.
1. The ::highlight pseudo‑element
To use the API you need both CSS and JavaScript. The CSS side defines a custom pseudo‑element:
::highlight(custom-highlight-name) {
color: red;
}Only a subset of text‑related properties is supported; background‑image (gradients) is not.
2. CSS Custom Highlight API workflow
The process consists of three steps:
Create a Range – use the Range constructor to define a text selection.
const parentNode = document.getElementById("foo");
const range1 = new Range();
range1.setStart(parentNode, 10);
range1.setEnd(parentNode, 20);
const range2 = new Range();
range2.setStart(parentNode, 40);
range2.setEnd(parentNode, 60);Create a Highlight – instantiate a Highlight with one or more ranges.
const highlight = new Highlight(range1, range2, ...);Register the Highlight – add it to CSS.highlights using a key that matches the name used in the CSS pseudo‑element.
CSS.highlights.set("highlight1", highlight1);
CSS.highlights.set("highlight2", highlight2);Because browser support is still limited, you should guard the code with if (CSS.highlights) { … }.
3. Rainbow text example
Define seven custom highlight names, each with a different color:
::highlight(rainbow-color-1) { color: #ad26ad; text-decoration: underline; }
::highlight(rainbow-color-2) { color: #5d0a99; text-decoration: underline; }
::highlight(rainbow-color-3) { color: #0000ff; text-decoration: underline; }
::highlight(rainbow-color-4) { color: #07c607; text-decoration: underline; }
::highlight(rainbow-color-5) { color: #b3b308; text-decoration: underline; }
::highlight(rainbow-color-6) { color: #ffa500; text-decoration: underline; }
::highlight(rainbow-color-7) { color: #ff0000; text-decoration: underline; }Then create 7 highlights and assign each character of the target paragraph to one of them in a loop:
const textNode = document.getElementById("rainbow-text").firstChild;
if (CSS.highlights) {
const highlights = [];
for (let i = 0; i < 7; i++) {
const colorHighlight = new Highlight();
highlights.push(colorHighlight);
CSS.highlights.set(`rainbow-color-${i + 1}`, colorHighlight);
}
for (let i = 0; i < textNode.textContent.length; i++) {
const range = new Range();
range.setStart(textNode, i);
range.setEnd(textNode, i + 1);
highlights[i % 7].add(range);
}
}4. Search‑highlight implementation
By listening to an input field, you can locate all occurrences of a query string, create ranges for each match, and register a single highlight called search-results:
const query = document.getElementById("query");
const article = document.querySelector("article");
const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const allTextNodes = [];
let currentNode = treeWalker.nextNode();
while (currentNode) { allTextNodes.push(currentNode); currentNode = treeWalker.nextNode(); }
query.addEventListener("input", () => {
if (!CSS.highlights) { article.textContent = "CSS Custom Highlight API not supported."; return; }
CSS.highlights.clear();
const str = query.value.trim().toLowerCase();
if (!str) return;
const ranges = allTextNodes.map(el => {
const text = el.textContent.toLowerCase();
const indices = [];
let startPos = 0;
while (startPos < text.length) {
const index = text.indexOf(str, startPos);
if (index === -1) break;
indices.push(index);
startPos = index + str.length;
}
return indices.map(index => {
const range = new Range();
range.setStart(el, index);
range.setEnd(el, index + str.length);
return range;
});
}).flat();
const searchResultsHighlight = new Highlight(...ranges);
CSS.highlights.set("search-results", searchResultsHighlight);
});Style the highlight with CSS:
::highlight(search-results) {
background-color: #f06;
color: white;
}5. Code‑highlight editor
A lightweight editable <pre> element can be turned into a syntax‑highlighting editor by combining the Highlight API with a parser such as highlight.js :
hljs.highlight(pre.textContent, { language: 'css' })._emitter.rootNode.childrenFor each token you create a Range, group ranges by token type, instantiate a Highlight, and register it with a matching pseudo‑element name (e.g., ::highlight(keyword), ::highlight(string), etc.). Example CSS for token colours:
::highlight(built_in) { color: #c18401; }
::highlight(comment) { color: #a0a1a7; font-style: italic; }
::highlight(number), ::highlight(selector-class) { color: #986801; }
::highlight(attr) { color: #986801; }
::highlight(string) { color: #50a14f; }
::highlight(selector-pseudo) { color: #986801; }
::highlight(attribute) { color: #50a14f; }
::highlight(keyword) { color: #a626a4; }Because the API works on pure text ranges, the editor remains highly performant even with thousands of nodes, avoiding DOM re‑creation and preserving cursor position.
6. Summary
The CSS Custom Highlight API enables developers to apply custom styles to arbitrary text without altering the DOM, offering broader use cases, superior performance (often >100× faster than DOM‑based approaches), and minimal side effects. Its current limitation is that only text‑related CSS properties are supported and browser compatibility is still limited to recent Chrome versions.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
