<h1>CSS highlight API</h1>
  <pre class="editor" id="code" lang="css">ul{
  min-height: 0;
}
.sub {
  display: grid;
  grid-template-rows: 0fr;
  transition: 0.3s;
  overflow: hidden;
}
:checked ~ .sub {
  grid-template-rows: 1fr;
}
.txt{
  animation: color .001s .5 linear forwards;
}
@keyframes color {
  from {
    color: var(--c1)
  }
  to{
    color: var(--c2)
  }
}</pre>
html,
body {
  margin: 0;
  height: 100%;
}
body {
  padding: 20px;
}
.editor {
  color: #383a42;
  background: #fafafa;
  white-space: pre-wrap;
  -webkit-user-modify: read-write-plaintext-only;
  font-size: 20px;
  padding: 1em 0.5em;
  min-height: 300px;
  outline-width: 4px;
  outline-color: cornflowerblue;
}
::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;
}
const highlights = function (pre) {
  pre.normalize();
  const words = hljs.highlight(pre.textContent, {
    language: pre.getAttribute("lang"),
  })._emitter.rootNode.children;
  console.log(words);
  CSS.highlights.clear();
  // const el = pre.firstChild
  const nodes = pre.firstChild;
  const text = nodes.textContent;
  const highlightMap = {};
  let startPos = 0;
  words
    .filter((el) => el.scope)
    .forEach((el) => {
      const str = el.children[0];
      const scope = el.scope;
      const index = text.indexOf(str, startPos);
      if (index < 0) {
        return;
      }
      const item = {
        start: index,
        scope: scope,
        end: index + str.length,
        str: str,
      };
      if (highlightMap[scope]) {
        highlightMap[scope].push(item);
      } else {
        highlightMap[scope] = [item];
      }
      startPos = index + str.length;
    });
  console.log(highlightMap);
  Object.entries(highlightMap).forEach(function ([k, v]) {
    const ranges = v.map(({ start, end }) => {
      const range = new Range();
      range.setStart(nodes, start);
      range.setEnd(nodes, end);
      return range;
    });
    const highlight = new Highlight(...ranges.flat());
    CSS.highlights.set(k, highlight);
  });
};

highlights(code);
code.addEventListener("input", function () {
  highlights(this);
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js