惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
腾讯CDC
博客园 - 聂微东
爱范儿
爱范儿
罗磊的独立博客
P
Proofpoint News Feed
博客园 - Franky
博客园 - 三生石上(FineUI控件)
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
美团技术团队
MongoDB | Blog
MongoDB | Blog
WordPress大学
WordPress大学
A
About on SuperTechFans
I
InfoQ
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog

Piccalilli - Everything

The Index: Issue #197 Save 35% on all courses for two weeks only The Index: Issue #196 A CLI for adding new music to the collection The Index: Issue #195 A look at the geolocation HTML element and how it works The Index: Issue #194 A little progressive enhancement as a treat The Index: Issue #193 The Index: Issue #192 Getting started with implementing my music collection The Index: Issue #191 Use cases for aria-expanded The Index: Issue #190 Proxy and Reflect The Index: Issue #189 Rendering AT protocol posts on my /feed Loading AT protocol posts data The Index: Issue #188 Publishing on the Atmosphere with Standard.site The Index: Issue #187 The Index: Issue #186 The Index: Issue #185 A Front-end developer’s guide to the hybrid mobile app development landscape The Index: Issue #184 Navigating the age-old problem of checkmarks in UI with progressive enhancement The Index: Issue #183 Framework-agnostic design systems: a practical approach to web components The Index: Issue #182 Completing the WordPress headless CMS integration
Working with ::highlight() using progressive enhancement
AuthorSunkanmi FafoworaTechnical Writer and Software Engineer Mo · 2026-08-06 · via Piccalilli - Everything

Highlighting in CSS has been beneficial for applying a highlight on specific text or text fragments during user selection, emphasizing a piece of information on a website, or visually emphasizing a text for the sake of branding. Particularly, this is pretty helpful when users want to scan your document from top to bottom because the majority of people don’t read your document initially; they scan.

On the web, text highlights are a good way to lay emphasis on text fragments through good ol’ CSS. From ::selection for styling selected text to ::target-text which styles highlighted text from Google searches, and in my opinion, CSS’ most powerful pseudo-element for highlighting: ::highlight() which applies a custom highlight to a text fragment.

In this article, we will look into how the ::highlight() pseudo-element works, the API behind this pseudo-element, and explore a fallback feature for this technology because it relies heavily on JavaScript (JS).

AdvertSave 20% on all the courses using the code NEXTLEVEL

The CSS Custom Highlight API

Typically, a highlight or highlighted text is what you’d see during web searches or when you what to select a text fragment to copy, or even when you make a mistake in a word processor (the squiggly red underlines). In CSS, you can achieve these through the highlight pseudo-elements like::search-text, ::selection, ::spelling-error , and ::grammar-error . But, what about plain highlights like the demo above? That’s where the CSS Custom Highlight API comes in.

The CSS Custom Highlight API is an API for text highlighting on a range of text using JavaScript and CSS. It extends the pseudo-elements for highlighting (::search-text , ::selection, ::spelling-error ) and lets you customize text fragments with ::highlight() and JavaScript. ::highlight() is what will be our focus for this article, and how we can programmatically highlight text and its fragments using JavaScript and CSS.

How ::highlight() works

To create highlighted text like in the initial example, you need to know 4 total steps, which include:

  1. Creating the highlight buckets by creating instances of the Highlight() class
  2. Register each highlight instance into CSS.highlights
  3. Create Range() objects each with different points on the text for highlighting and add Range() objects to its highlight instances
  4. Style with ::highlight() pseudo-element

Before we move on with an example, let me explain something because this is where it might get tricky. What we do around here is build with progressive enhancement in mind first. So the first question you should ask before anything is “what happens when JS fails?”. Well, I’m glad you asked.

Let’s say, for our example, we want to create two highlight objects and use that to style a simple poetic text that reads “fire and ice live inside every word.” Because we care about our users, we set each text for highlight with the <mark> HTML tag setting the class attribute to either gold or ice , depending on the highlighting style we wish the text to have. This acts as a fallback highlight style in case our JavaScript fails or the browser doesn’t support custom highlighting.

We also apply an id to them in case JS is available too for our CSS Custom Highlight API. With all that in mind, our HTML would look like this:

<main>
  <h1>Two Highlights Demo</h1>
  <p id="line">
    <mark id="fire" class="gold">fire</mark> and
    <mark id="ice" class="ice">ice</mark> live inside 
    <mark id="every" class="ice">every</mark> 
    <mark id="word" class="gold">word</mark>
  </p>
</main>

Then, we proceed to query each tagged word in our JS applying the firstChild property to each of them of get the element’s first child node which we will use later:

const fireNode = document.querySelector("#fire").firstChild;
const everyNode = document.querySelector("#every").firstChild;
const iceNode = document.querySelector("#ice").firstChild;
const wordNode = document.querySelector("#word").firstChild;

Finally, we can proceed with the steps on creating a custom highlight in CSS.

AdvertSave 20% on all courses, using the code NEXTLEVEL

Create instances of Highlight() class

In order to create a custom highlight, the first step is to create an instance of the Highlight() class which will house the highlight styling we want a text or text fragment to have. For our demo, we’ll be creating two highlight objects named using the Highlight() class. One to give a golden color representing fire and the other to give a blue color representing ice:

const goldHL = new Highlight();
const iceHL = new Highlight();

Register each highlight instance in CSS.highlights

Next, we register the created highlight instances in the HighlightRegistry via CSS.highlights set() method. We map a valid CSS identifier to the instance for CSS styling later.

CSS.highlights.set("hl-gold", goldHL);
CSS.highlights.set("hl-ice", iceHL);

Create Range() objects each with different points on the text for highlighting

In this step, we will be creating a Range() object for each text fragment we queried earlier for highlighting. We will then apply the highlight we want on each selected text. For the first word “fire”, we create a Range() object called r1 , and we set the start node to the first letter “f” using setStart on r1 . setStart() accepts two values. It accepts the node we’re targeting (in our case for “fire”, its fireNode ) and the index of the text on the node.

Now, because we want to target the whole text “fire”, we have to also set where the range will stop. And this will be set using setEnd() . setEnd() accepts two values like setStart() on the range object (r1). It accepts the node we’re targeting (fireNode ) and the index of the end text “e” (as in the “e” in “fire”) using fireNode.textContent.length - 1 which gives us the last index of the text.

Finally, we add the range object into the set instance.

const r1 = new Range();
r1.setStart(fireNode, 0);
r1.setEnd(fireNode, fireNode.textContent.length - 1);
goldHL.add(r1);

This step is repeated for everyNode, iceNode, and wordNode. Typically, you’d want to use a loop for this, but because this is really small, writing it in a specific manner will suffice, especially to help you understand how this works too.

const r2 = new Range();
r2.setStart(wordNode, 0);
r2.setEnd(wordNode, wordNode.textContent.length - 1);
goldHL.add(r2);

const r3 = new Range();
r3.setStart(iceNode, 0);
r3.setEnd(iceNode, iceNode.textContent.length - 1);
iceHL.add(r3);

const r4 = new Range();
r4.setStart(everyNode, 0);
r4.setEnd(everyNode, everyNode.textContent.length - 1);
iceHL.add(r4);

Style with ::highlight() pseudo-element

Remember how we said we should think progressive enhancement first? Well, in order to achieve that for this demo in particular, we need to style the <mark>ed highlighted text first, then, we style the ::highlight() pseudo-element. For that to work, we styled text marked with the gold class to be golden in oklch() with a glowy text shadow of similar color. We style text <mark> ed with the ice class to be blueish in oklch() with a glowy text shadow of similar color:

mark {
  background: none;
}

mark.gold {
  color: oklch(88% 0.16 75);
  text-shadow: 0 0 40px oklch(65% 0.22 75 / 0.4);
}

mark.ice {
  color: oklch(82% 0.1 215);
  text-shadow: 0 0 40px oklch(60% 0.18 215 / 0.4);
}

Viola! (or how do they say it?) It looks amazing! 🤩

Even without the styled ::highlight() , it works out pretty well. But, that’s not our only aim though. We still need to add the styling for both our highlight objects.

::highlight(hl-gold) {
  color: oklch(88% 0.16 75);
  text-shadow: 0 0 40px oklch(65% 0.22 75 / 0.4);
}

::highlight(hl-ice) {
  color: oklch(82% 0.1 215);
  text-shadow: 0 0 40px oklch(60% 0.18 215 / 0.4);
}

Done! It’s pretty much the same styling we did for our <mark> tag classes and in case JS fails or the browser doesn’t support it, we wrap the ::higlight() pseudo-elements in a @supports container:

@supports selector(::highlight(h1-gold)) {
  ::highlight(hl-gold) {
    color: oklch(88% 0.16 75);
    text-shadow: 0 0 40px oklch(65% 0.22 75 / 0.4);
  }

  ::highlight(hl-ice) {
    color: oklch(82% 0.1 215);
    text-shadow: 0 0 40px oklch(60% 0.18 215 / 0.4);
  }
}

AdvertSave 20% on all of our courses using the code NEXTLEVEL

Will you be using ::highlight()?

I know for sure I will be using this feature in my work. This article explained highlighting in CSS, the CSS Custom Highlight API, and how ::higlight() works in CSS with a fallback provision, for when JavaScript isn’t available or the browser doesn’t support it.

If you’re looking to read up more about the CSS Custom Highlight API, MDN has a good guide on the topic for you.

Enjoyed this article? You can support us by leaving a tip via Open Collective