{"id":363724,"date":"2022-03-01T09:41:13","date_gmt":"2022-03-01T17:41:13","guid":{"rendered":"https:\/\/css-tricks.com\/?p=363724"},"modified":"2022-05-03T10:37:21","modified_gmt":"2022-05-03T17:37:21","slug":"css-custom-highlight-api-early-look","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/css-custom-highlight-api-early-look\/","title":{"rendered":"CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web"},"content":{"rendered":"\n<p><strong>Styling ranges of text<\/strong> in software is a very useful thing to be able to do. Thankfully, we have the CSS Custom Highlight API to look forward to because it represents the future of styling text ranges on the web.<\/p>\n\n\n\n<!--more-->\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"733\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1644484463021_highlight-api-demo-no-text-deco.gif?resize=800%2C733&#038;ssl=1\" alt=\"Animation screenshot of the CSS Custom Highlight API demo.\" class=\"wp-image-363729\"\/><\/figure>\n\n\n\n<p>One example: if you\u2019ve ever used text editing software like Google Docs, Word, or Dropbox Paper, you\u2019ll see they detect spelling and grammar errors and displaying nice little squiggly underlines below them to attract attention. Code editors like VS Code do the same for code errors.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"977\" height=\"269\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042116795_image.png?resize=977%2C269&#038;ssl=1\" alt=\"\" class=\"wp-image-363725\" srcset=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042116795_image.png?w=977&amp;ssl=1 977w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042116795_image.png?resize=300%2C83&amp;ssl=1 300w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042116795_image.png?resize=768%2C211&amp;ssl=1 768w\" sizes=\"auto, (min-width: 735px) 864px, 96vw\" \/><\/figure>\n\n\n\n<p>Another very common use case for highlighting text is <strong>search and highlight<\/strong>, where you\u2019re given a text input box and typing in it searches matching results on the page, and highlights them. Try pressing <code>Ctrl<\/code>\/<code>\u2318<\/code>+ <code>F<\/code> in your web browser right now and type in some text from this article.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1117\" height=\"951\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042176497_image.png?resize=1117%2C951&#038;ssl=1\" alt=\"\" class=\"wp-image-363726\" srcset=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042176497_image.png?w=1117&amp;ssl=1 1117w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042176497_image.png?resize=300%2C255&amp;ssl=1 300w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042176497_image.png?resize=1024%2C872&amp;ssl=1 1024w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042176497_image.png?resize=768%2C654&amp;ssl=1 768w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643042176497_image.png?resize=1000%2C851&amp;ssl=1 1000w\" sizes=\"auto, (min-width: 735px) 864px, 96vw\" \/><\/figure>\n\n\n\n<p>The browser itself often handles these styling situations. Editable areas (like a <code>&lt;textarea&gt;<\/code>) get spelling squiggles automatically. The find command highlights found text automatically.<\/p>\n\n\n\n<p>But what about when we want to do this type of styling ourselves? Doing this on the web has been a common problem for a long time. It has probably costed many people a lot more time than it should have.<\/p>\n\n\n\n<p>This isn\u2019t a simple problem to solve. We aren\u2019t just wrapping text in a <code>&lt;span&gt;<\/code> with a class and applying some CSS. Indeed, this requires being able to correctly highlight <em>multiple<\/em> ranges of text across an arbitrarily complex DOM tree, and possibly crossing the boundaries of DOM elements.<\/p>\n\n\n\n<p>There are two common solutions to this, including:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>styling text range pseudo-elements, and<\/li><li>creating your own text highlighting system.<\/li><\/ol>\n\n\n\n<p>We\u2019ll review them first and then take a look at&nbsp;the&nbsp;upcoming&nbsp;CSS Custom Highlight API&nbsp;that can change it all. but if you&#8217;re <\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"potential-solution-1-style-able-text-ranges\">Potential Solution #1: Style-able Text Ranges<\/h3>\n\n\n<p>Probably the most well-known style-able text range is the user selection. When you use your pointing device to select a piece of text in a web page, a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Selection\" rel=\"noopener\"><code>Selection<\/code><\/a> object is automatically created. In fact, try selecting text on this page right now, and then run <code>document.getSelection()<\/code> in the DevTools console. You should see location information about the selected text.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1846\" height=\"1196\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?resize=1846%2C1196&#038;ssl=1\" alt=\"DevTools window showing the position of the current selection in the console.\" class=\"wp-image-363727\" srcset=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?w=1846&amp;ssl=1 1846w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?resize=300%2C194&amp;ssl=1 300w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?resize=1024%2C663&amp;ssl=1 1024w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?resize=768%2C498&amp;ssl=1 768w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?resize=1536%2C995&amp;ssl=1 1536w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/Screen-Shot-2022-02-14-at-7.33.16-AM.png?resize=1000%2C648&amp;ssl=1 1000w\" sizes=\"auto, (min-width: 735px) 864px, 96vw\" \/><\/figure>\n\n\n\n<p>It turns out that you can also create a text selection programmatically from JavaScript. Here is an example:<\/p>\n\n\n\n<pre rel=\"JavaScript\" class=\"wp-block-csstricks-code-block language-javascript\" data-line=\"\"><code markup=\"tt\">\/\/ First, create a Range object.\nconst range = new Range();\n\n\/\/ And set its start and end positions.\nrange.setStart(parentNode, startOffset);\nrange.setEnd(parentNode, endOffset);\n\n\/\/ Then, set the current selection to this range.\ndocument.getSelection().removeAllRanges();\ndocument.getSelection().addRange(range);<\/code><\/pre>\n\n\n\n<p>The last piece of the puzzle is to style this range. CSS has a pseudo-element called <a href=\"https:\/\/css-tricks.com\/almanac\/selectors\/s\/selection\/\"><code>::selection<\/code><\/a> to do just that, and it\u2019s supported across <a href=\"https:\/\/caniuse.com\/css-selection\" rel=\"noopener\">all browsers<\/a>.<\/p>\n\n\n\n<pre rel=\"CSS\" class=\"wp-block-csstricks-code-block language-css\" data-line=\"\"><code markup=\"tt\">::selection {\n  background-color: #f06;\n  color: white;\n}<\/code><\/pre>\n\n\n\n<p>Here is an example using this technique to highlight all words in a page one after the other:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_eYeYJBx\" src=\"\/\/codepen.io\/anon\/embed\/eYeYJBx?height=450&amp;theme-id=1&amp;slug-hash=eYeYJBx&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed eYeYJBx\" title=\"CodePen Embed eYeYJBx\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>On top of the <code>::selection<\/code> pseudo-element, there are a number of other pseudo-elements:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>::target-text<\/code> selects the text that has been scrolled to in browsers that support the <a href=\"https:\/\/wicg.github.io\/scroll-to-text-fragment\/\" rel=\"noopener\">scroll-to-text<\/a> feature. (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/::target-text\" rel=\"noopener\">MDN<\/a>)<\/li><li><code>::spelling-error<\/code> selects text that is flagged by the browser as containing a spelling error. (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/::spelling-error\" rel=\"noopener\">MDN<\/a>)<\/li><li><code>::grammar-error<\/code> selects text that is flagged by the browser as containing a grammar error. (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/::grammar-error\" rel=\"noopener\">MDN<\/a>)<\/li><\/ul>\n\n\n\n<p>Unfortunately browser support isn\u2019t great here and although these ranges are useful in each of their own right, they can\u2019t be used to style custom pieces of text \u2014 only browser-predefined ones<\/p>\n\n\n\n<p>So the user text selection is nice because it\u2019s relatively simple to put in place and doesn\u2019t change the DOM of the page. Indeed, <code>Range<\/code> objects are essentially coordinates of segments in the page, rather than HTML elements that need to be created to exist.<\/p>\n\n\n\n<p>One major drawback, however, is that creating a selection resets whatever the user has already manually selected. Try selecting text in the demo above to test this. You\u2019ll see how it goes away as soon as the code moves the selection somewhere else.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"potential-solution-2-custom-highlighting-system\">Potential Solution #2: Custom Highlighting System<\/h3>\n\n\n<p>This second solution is pretty much the only thing you can do if using the <code>Selection<\/code> object is insufficient for you. This solution revolves around doing everything yourself, using JavaScript to insert new HTML elements in the DOM where you want the highlighting to appear.<\/p>\n\n\n\n<p>Unfortunately, this means way more JavaScript to write and maintain, not to mention it forces the browser to re-create the layout of the page whenever the highlighting changes. Plus, there are complicated edge cases, for example, when you want to highlight a piece of text that spans across multiple DOM elements.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1129\" height=\"100\" src=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643109885151_image.png?resize=1129%2C100&#038;ssl=1\" alt=\"Illustration showing a line of HTML with an emphasis element and a strong element with a bright yellow highlight running through them.\" class=\"wp-image-363728\" srcset=\"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643109885151_image.png?w=1129&amp;ssl=1 1129w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643109885151_image.png?resize=300%2C27&amp;ssl=1 300w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643109885151_image.png?resize=1024%2C91&amp;ssl=1 1024w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643109885151_image.png?resize=768%2C68&amp;ssl=1 768w, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/s_8E0FC85C45E73C25EFCF623C768360F2F95DBDDEC338D5F6DE316BB0830F6F67_1643109885151_image.png?resize=1000%2C89&amp;ssl=1 1000w\" sizes=\"auto, (min-width: 735px) 864px, 96vw\" \/><\/figure>\n\n\n\n<p>Interestingly, <a href=\"https:\/\/codemirror.net\" rel=\"noopener\">CodeMirror<\/a> and <a href=\"https:\/\/microsoft.github.io\/monaco-editor\/\" rel=\"noopener\">Monaco<\/a> (the JavaScript text editor library that powers VS Code) have their own highlighting logic. They use a slightly different approach where the highlights are contained in a separate part of the DOM tree. The lines of text and the highlighted segments are rendered in two different places in the DOM which are then positioned over each other. If you inspect the DOM sub-tree that contains the text, there are no highlights. This way, the highlights can be re-rendered without impacting the lines of text and having to introduce new elements within them.<\/p>\n\n\n\n<p>Overall, it feels like a browser-powered highlighting feature is missing. Something that would help solve all of these drawbacks (no interference with user text selection, multi-selection support, simple code) and be faster than custom-made solutions.<\/p>\n\n\n\n<p>Fortunately, that\u2019s what we\u2019re here to talk about!<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"enter-the-css-custom-highlight-api\">Enter the CSS Custom Highlight API<\/h3>\n\n\n<p>The <a href=\"https:\/\/www.w3.org\/TR\/css-highlight-api-1\/\" rel=\"noopener\">CSS Custom Highlight API<\/a> is a new W3C specification (currently in Working Draft status) that makes it possible to style arbitrary text ranges from JavaScript! The approach here is very similar to the user text selection technique we reviewed earlier. It gives developers a way to create arbitrary ranges, from JavaScript, and then style them using CSS.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"creating-ranges-of-text\">Creating Ranges of Text<\/h4>\n\n\n<p>The first step is to create the ranges of text that you want to highlight. which can be done using a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Range\" rel=\"noopener\"><code>Range<\/code><\/a> in JavaScript. So, like we did when setting the current selection:<\/p>\n\n\n\n<pre rel=\"JavaScript\" class=\"wp-block-csstricks-code-block language-javascript\" data-line=\"\"><code markup=\"tt\">const range = new Range();\nrange.setStart(parentNode, startOffset);\nrange.setEnd(parentNode, endOffset);<\/code><\/pre>\n\n\n\n<p>It\u2019s worth noting that the <code>setStart<\/code> and <code>setEnd<\/code> methods work differently if the node passed as the first argument is a text node or not. For text nodes, the offset corresponds to the number of characters within the node. For other nodes, the offset corresponds to the number of child nodes within the parent node.<\/p>\n\n\n\n<p>Also worth noting is that <code>setStart<\/code> and <code>setEnd<\/code> aren\u2019t the only ways to describe where a range starts and ends. Take a look at the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Range#methods\" rel=\"noopener\">other methods<\/a> available on the <code>Range<\/code> class to see other options.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"creating-highlights\">Creating Highlights<\/h4>\n\n\n<p>The second step consists in creating <a href=\"https:\/\/www.w3.org\/TR\/css-highlight-api-1\/#highlight\" rel=\"noopener\"><code>Highlight<\/code><\/a> objects for the ranges created in that last step. A <code>Highlight<\/code> object can receive one or more <code>Range<\/code>s. So if you want to highlight a bunch of pieces of text in exactly the same way, you should probably create a single <code>Highlight<\/code> object and initialize it with all of the <code>Range<\/code>s that correspond to these pieces of text.<\/p>\n\n\n\n<pre rel=\"JavaScript\" class=\"wp-block-csstricks-code-block language-javascript\" data-line=\"\"><code markup=\"tt\">const highlight = new Highlight(range1, range2, ..., rangeN);<\/code><\/pre>\n\n\n\n<p>But you can also create as many <code>Highlight<\/code> objects as you need. For example, if you are building a collaborative text editor where each user gets a different text color, then you can create one <code>Highlight<\/code> object per user. Each object can then be styled differently, as we\u2019ll see next.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"registering-highlights\">Registering Highlights<\/h4>\n\n\n<p>Now Highlight objects on their own don\u2019t do anything. They first need to be registered in what is called the highlight registry. This is done by using the <a href=\"https:\/\/www.w3.org\/TR\/css-highlight-api-1\/#highlight-registry\" rel=\"noopener\">CSS Highlights API<\/a>. The registry works like a map where you can register new highlights by giving them names, as well as remove highlights (or even clear the entire registry).<\/p>\n\n\n\n<p>Here is how to register a single highlight.<\/p>\n\n\n\n<pre rel=\"JavaScript\" class=\"wp-block-csstricks-code-block language-javascript\" data-line=\"\"><code markup=\"tt\">CSS.highlights.set('my-custom-highlight', highlight);<\/code><\/pre>\n\n\n\n<p>Where <code>my-custom-highlight<\/code> is the name of your choosing and <code>highlight<\/code> is a <code>Highlight<\/code> object created in the last step.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"styling-highlights\">Styling Highlights<\/h4>\n\n\n<p>The final step is to actually style the registered highlights. This is done with the new CSS <a href=\"https:\/\/www.w3.org\/TR\/css-highlight-api-1\/#custom-highlight-pseudo\" rel=\"noopener\"><code>::highlight()<\/code><\/a> pseudo-element, using the name you chose when registering the <code>Highlight<\/code> object (which is <code>my-custom-highlight<\/code> in our example above).<\/p>\n\n\n\n<pre rel=\"CSS\" class=\"wp-block-csstricks-code-block language-css\" data-line=\"\"><code markup=\"tt\">::highlight(my-custom-highlight) {\n  background-color: yellow;\n  color: black;\n}<\/code><\/pre>\n\n\n\n<p>It\u2019s worth noting that, just like <code>::selection<\/code>, a subset of CSS properties only can be used with the <code>::highlight()<\/code> pseudo-element:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/b\/background-color\/\"><code>background-color<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/c\/caret-color\/\"><code>caret-color<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/c\/color\/\"><code>color<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/c\/cursor\/\"><code>cursor<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/f\/fill\/\"><code>fill<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/s\/stroke\/\"><code>stroke<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/s\/stroke-width\/\"><code>stroke-width<\/code><\/a><\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/t\/text-decoration\/\"><code>text-decoration<\/code><\/a> (which will likely only be supported in the version 2 of the specification)<\/li><li><a href=\"https:\/\/css-tricks.com\/almanac\/properties\/t\/text-shadow\/\"><code>text-shadow<\/code><\/a><\/li><\/ul>\n\n\n<h4 class=\"wp-block-heading\" id=\"updating-highlights\">Updating Highlights<\/h4>\n\n\n<p>There are multiple ways to update highlighted text on the page.<\/p>\n\n\n\n<p>For example, you can clear the highlight registry altogether with <code>CSS.highlights.clear()<\/code> and then start again from the beginning. Or, you can also update the underlying ranges without having to re-create any of the objects all. For this, use the <code>range.setStart<\/code> and <code>range.setEnd<\/code> methods again (or any of the other <code>Range<\/code> methods) and the highlights will be re-painted by the browser.<\/p>\n\n\n\n<p>But, the <code>Highlight<\/code> object works like a JavaScript <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Set\" rel=\"noopener\"><code>Set<\/code><\/a>, so this means you also add new <code>Range<\/code> objects to an existing <code>Highlight<\/code> with <code>highlight.add(newRange)<\/code> or remove a <code>Range<\/code> with <code>highlight.delete(existingRange)<\/code>.<\/p>\n\n\n\n<p>Third, you can also add or remove specific <code>Highlight<\/code> objects from the <code>CSS.highlights<\/code> registry. Since this API works like a JavaScript <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\" rel=\"noopener\"><code>Map<\/code><\/a>, you can <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\/set\" rel=\"noopener\"><code>set<\/code><\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\/delete\" rel=\"noopener\"><code>delete<\/code><\/a> to update the currently registered <code>Highlight<\/code>s.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"browser-support\">Browser Support<\/h4>\n\n\n<p>The specification for the CSS Custom Highlight API is relatively new and its implementation in browsers is still incomplete. So, although this is going to be a very useful addition to the web platform, it\u2019s not quite ready for production use.<\/p>\n\n\n\n<p>The Microsoft Edge team is implementing the CSS Custom Highlight API in Chromium at the moment. In fact, the feature can already be used in Canary versions right now by enabling the Experimental Web Platform features flag (under <code>about:flags<\/code>). There is currently no firm plan as to when the feature will ship in Chrome, Edge, and other Chromium-based browsers, but it\u2019s getting very close.<\/p>\n\n\n\n<p>The API is also supported in <a href=\"https:\/\/developer.apple.com\/safari\/technology-preview\/release-notes\/#r99\" rel=\"noopener\">Safari 99+<\/a> but behind an experiment flag (Develop \u2192 Experimental Features \u2192 Highlight API), and the interface is a little bit different in that it uses <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/StaticRange\" rel=\"noopener\"><code>StaticRange<\/code><\/a> objects instead.<\/p>\n\n\n\n<p>Firefox does not support the API yet, though you can <a href=\"https:\/\/github.com\/mozilla\/standards-positions\/issues\/482\" rel=\"noopener\">read Mozilla\u2019s position about it<\/a> for more information.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"demo\">Demo<\/h3>\n\n\n<p>Speaking of Microsoft Edge, they have a demo set up where you can take the CSS Custom Highlight API for a test drive. But Before trying the demo, be sure you\u2019re using either Chrome or Edge Canary with the Experimental Web Platform features flag in the <code>about:flags<\/code> page.<\/p>\n\n\n\n<p>\/button <a href=\"https:\/\/microsoftedge.github.io\/Demos\/custom-highlight-api\/\" rel=\"noopener\">View the demo<\/a><\/p>\n\n\n\n<p>The demo uses the Custom Highlight API to highlight ranges of text in the page based on what you type in the search field at the top of the page.<\/p>\n\n\n\n<p>After the page loads, JavaScript code retrieves all the text nodes in the page (using a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/TreeWalker\" rel=\"noopener\">TreeWalker<\/a>) and when the user types in the search field, the code iterates over these nodes until it finds matches. Those matches are then used to create <code>Range<\/code> objects, which are then highlighted with the Custom Highlight API.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"closing-thoughts\">Closing Thoughts<\/h3>\n\n\n<p>So, is this new browser-provided highlighting API really worth it? Totally!<\/p>\n\n\n\n<p>For one, even if the CSS Custom Highlight API may seem a bit complicated at first (i.e. having to create ranges, then highlights, then registering them, and finally styling them), it\u2019s still way simpler than having to create new DOM elements and insert them in the right places.<\/p>\n\n\n\n<p>More importantly, browser engines can style these ranges very, very fast.<\/p>\n\n\n\n<p>The reason only a subset of CSS properties is allowed to be used with the <code>::highlight()<\/code> pseudo-element is that the subset only contains properties that can be applied by the browser very effectively without having to recreate the layout of the page. Highlighting ranges of text by inserting new DOM elements in the page around them requires the engine to do much more work.<\/p>\n\n\n\n<p>But don\u2019t take my word for it. <a href=\"https:\/\/github.com\/ffiori\" rel=\"noopener\">Fernando Fiori<\/a>, who worked on the API, created this nice <a href=\"https:\/\/ffiori.github.io\/highlight-api-demos\/demo-performance.html\" rel=\"noopener\">performance comparison demo<\/a>. On my computer, the CSS Custom Highlight API performs on average 5\u2715 as fast as the DOM-based highlighting.<\/p>\n\n\n\n<p>With Chromium and Safari experimental support already here, we\u2019re getting close to something that can be used in production. I can\u2019t wait for browsers to support the Custom Highlight API consistently and see what features this will unlock!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Styling ranges of text in software is a very useful thing to be able to do. Thankfully, we have the [&hellip;]<\/p>\n","protected":false},"author":285586,"featured_media":363733,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web by @patrickbrosset ","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_share_on_mastodon":"0","_share_on_mastodon_status":"%title% %permalink%"},"categories":[4],"tags":[1495,19034,810],"class_list":["post-363724","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-articles","tag-selection","tag-custom-highlight-api","tag-selecting-text"],"acf":{"show_toc":"No"},"share_on_mastodon":{"url":"","error":""},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/02\/text-selection-styling.jpg?fit=1200%2C600&ssl=1","jetpack-related-posts":[{"id":391705,"url":"https:\/\/css-tricks.com\/how-to-style-the-new-search-text-and-other-highlight-pseudo-elements\/","url_meta":{"origin":363724,"position":0},"title":"Styling ::search-text and Other Highlight-y Pseudo-Elements","author":"Daniel Schwarz","date":"January 28, 2026","format":false,"excerpt":"The new ::search-text pseudo (Chrome 144) matches are yellow while the current target (::search-text:current) is orange, but ::search-text enables us to change that.","rel":"","context":"In &quot;Articles&quot;","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"A selected portion of the words premier league highlights showing the highlights styled in a variety of overlapping colors.","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2026\/01\/search-text-pseudo-example.webp?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2026\/01\/search-text-pseudo-example.webp?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2026\/01\/search-text-pseudo-example.webp?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2026\/01\/search-text-pseudo-example.webp?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2026\/01\/search-text-pseudo-example.webp?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":302288,"url":"https:\/\/css-tricks.com\/web-component-for-a-code-block\/","url_meta":{"origin":363724,"position":1},"title":"Web Component for a Code Block","author":"Chris Coyier","date":"February 18, 2020","format":false,"excerpt":"We'll get to that, but first, a long-winded introduction. I'm still not in a confident place knowing a good time to use native web components. The templating isn't particularly robust, so that doesn't draw me in. There is no state management, and I like having standard ways of handling that.\u2026","rel":"","context":"In &quot;Articles&quot;","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/01\/code-block-screenshot.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/01\/code-block-screenshot.png?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/01\/code-block-screenshot.png?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/01\/code-block-screenshot.png?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/01\/code-block-screenshot.png?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":336380,"url":"https:\/\/css-tricks.com\/platform-news-defaulting-to-logical-css-fugu-apis-custom-media-queries-and-wordpress-vs-italics\/","url_meta":{"origin":363724,"position":2},"title":"Platform News: Defaulting to Logical CSS, Fugu APIs, Custom Media Queries, and WordPress vs. Italics","author":"\u0160ime Vidas","date":"March 12, 2021","format":false,"excerpt":"Looks like 2021 is the time to start using CSS Logical Properties! Plus, Chrome recently shipped a few APIs that have raised eyebrows, SVG allows us to disable its aspect ratio, WordPress focuses on the accessibility of its typography, and there's still no update (or progress) on the development of\u2026","rel":"","context":"In &quot;Articles&quot;","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/03\/wpn-20210312.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/03\/wpn-20210312.jpg?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/03\/wpn-20210312.jpg?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/03\/wpn-20210312.jpg?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/03\/wpn-20210312.jpg?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":365510,"url":"https:\/\/css-tricks.com\/syntax-highlighting-prism-on-a-next-js-site\/","url_meta":{"origin":363724,"position":3},"title":"Syntax Highlighting (and More!) With Prism on a Static Site","author":"Adam Rackis","date":"May 4, 2022","format":false,"excerpt":"So, you've decided to build a blog with Next.js. Like any dev blogger, you'd like to have code snippets in your posts that are formatted nicely with syntax highlighting. Perhaps you also want to display line numbers in the snippets, and maybe even have the ability to call out certain\u2026","rel":"","context":"In &quot;Articles&quot;","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/04\/prism-next-syntax-highlighting.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/04\/prism-next-syntax-highlighting.png?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/04\/prism-next-syntax-highlighting.png?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/04\/prism-next-syntax-highlighting.png?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/04\/prism-next-syntax-highlighting.png?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":337892,"url":"https:\/\/css-tricks.com\/intrinsic-typography-is-the-future-of-styling-text-on-the-web\/","url_meta":{"origin":363724,"position":4},"title":"Intrinsic Typography is the Future of Styling Text on the Web","author":"Scott Kellum","date":"April 20, 2021","format":false,"excerpt":"The way we style text hasn\u2019t changed much over the years. There have been numerous advancements to help make things more flexible, like layouts, but in terms of styling, most finite aspects of our designs, like text, remain relatively unchanged. This is especially true of text styling. We write code\u2026","rel":"","context":"In &quot;Articles&quot;","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/04\/type-scale.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/04\/type-scale.png?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/04\/type-scale.png?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/04\/type-scale.png?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/04\/type-scale.png?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":341572,"url":"https:\/\/css-tricks.com\/are-custom-properties-a-menu-of-what-will-change\/","url_meta":{"origin":363724,"position":5},"title":"Are Custom Properties a &#8220;Menu of What Will Change&#8221;?","author":"Chris Coyier","date":"June 2, 2021","format":false,"excerpt":"PPK laid out an interesting situation in \"Two options for using custom properties\" where he and Stefan Judis had two different approaches for doing the same thing with custom properties. In one approach, hover and focus styles for a link are handled with two different custom properties, one for each\u2026","rel":"","context":"In &quot;Articles&quot;","block_context":{"text":"Articles","link":"https:\/\/css-tricks.com\/category\/articles\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/06\/custom-property-componentcolor.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/06\/custom-property-componentcolor.jpg?fit=1200%2C600&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/06\/custom-property-componentcolor.jpg?fit=1200%2C600&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/06\/custom-property-componentcolor.jpg?fit=1200%2C600&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/06\/custom-property-componentcolor.jpg?fit=1200%2C600&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/363724","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/285586"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=363724"}],"version-history":[{"count":5,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/363724\/revisions"}],"predecessor-version":[{"id":364775,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/363724\/revisions\/364775"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/363733"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=363724"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=363724"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=363724"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}