>
Software

microlighter does syntax highlighting with pure css and no parser

A web component that leans on the platform instead of shipping a parser

Most syntax highlighters in 2026 still work the same way they did in 2014. Ship a JavaScript library, let it tokenize the code at runtime, wrap every token in a span, attach a class, and style those classes with CSS. It works. It is also heavier than it needs to be. The library parses the source, builds a token tree, walks it, and injects markup the browser then has to render. Every page view pays that cost, and the markup is no longer human-readable when you view source.

MicroLighter, a project from Dave Rupert, flips the script. It is a small web component (a custom HTML element with attached behavior, defined using the standard browser Custom Elements API) that does almost nothing in JavaScript and lets the browser do the highlighting with CSS Custom Highlights, an API (application programming interface) that has shipped broadly enough to be called “Baseline” (a status that means the feature is supported in every major browser). The code is wrapped in semantic markup once, the stylesheet paints the colors, and that is the whole pipeline. No tokenizer. No runtime parser. Just CSS variables and a ::highlight() pseudo-element (a CSS hook that lets you paint a region of text without wrapping it in extra HTML).

What the code actually does

Inside, the web component is a thin shell around a

 and  block. It does three things:

  • Wraps the code in a Custom Element so the markup is portable across frameworks
  • Applies a configurable set of theme variables through CSS custom properties (named values like --syntax-keyword that you can redefine per theme)
  • Optionally renders controls (a copy button) and line numbers

The actual syntax coloring happens in the stylesheet. MicroLighter uses the browser's built-in JavaScript tokenizer (the engine that powers DevTools and view-source) via CSS Custom Highlights. You write the styles once, the browser finds the tokens, and the styles paint them. There is no JavaScript string parsing in the critical path.

The result is a single, small JavaScript file plus a stylesheet. You can ship it as a regular web component, drop it into a static site, or use it inside a framework. The component is also modular: you can pull in just the bits you need. If you only want line numbers, you take line numbers. If you only want the JavaScript grammar, you take that. The full library is small enough that you can read the source in one sitting, which is rare in this corner of the web.

What you get for free

Custom Highlights is the kind of feature that quietly does a lot. Because the highlighting is applied through CSS and not through extra DOM nodes (the document object model, the browser's internal tree of HTML elements), the underlying markup stays clean. Screen readers see

const answer = 42;

, not

const answer = 42;

. That matters for accessibility. It also matters for performance. Fewer nodes, no runtime parsing, and the browser can paint the colors in a layer the compositor (the part of the browser that turns styles into pixels) can cache.

Practical benefits include:

  • Theming becomes a CSS variables change. Light and dark themes are a one-line swap using light-dark(), a modern CSS function that picks the right value for the current color scheme.
  • The component is framework-agnostic. It works in any setup that emits HTML.
  • Source code stays human-readable when you view it without CSS. The actual content is not polluted with span soup.
  • Copy to clipboard still works, because the source is real text, not a token tree.

Where the project sits

MicroLighter is one entry in a broader shift in how syntax highlighting is built. Shiki runs at build time and produces static HTML, which is a good fit for static sites. Prism and highlight.js run at request time and ship a parser, which is the model MicroLighter is reacting against. The web-component-plus-CSS-Custom-Highlights approach is the newest of the three, and it leans hardest on the platform. Each of these is a valid choice for a different workload.

A small docs site with a handful of languages will be happy with any of the three. A high-traffic blog that wants theming and a clean view-source is where MicroLighter pays off. A reference site that needs to highlight twenty languages, including some obscure ones, is still Prism or Shiki territory.

A worked example

Drop this into a static page and you have highlighted JavaScript:

<micro-lighter language="javascript">
  <pre><code>const answer = 42;</code></pre>
</micro-lighter>
<script type="module">
  import "microlighter/micro-lighter-element.min.js";
</script>

Add a stylesheet that defines your theme, and the browser does the rest. The library is small, the markup is clean, and the page works without JavaScript enabled (it just shows unhighlighted code, which is the right fallback).

What you give up

On the other side, the browser-built-in tokenizer is not as flexible as Prism or highlight.js. A few trade-offs to be aware of:

  • Custom grammars are not really a thing. You get what the browser ships, which is the standard set of web languages and a few others.
  • Line numbers and language labels are presentation choices you have to wire up yourself. The component gives you the hook, not the layout.
  • If you need to highlight a niche language, you may be back to shipping a parser. MicroLighter is not a Prism replacement for every grammar.
  • Browser support is broad now, but "Baseline this year" is a recent milestone. If you need to support older browsers, the graceful-degradation story is "no highlighting," which is the same thing that happens when any highlighter's CSS does not load.

When MicroLighter is the right call

In short, if your site highlights JavaScript, HTML, CSS, JSON, and the other web-native languages, and you are tired of shipping a parser to do it, MicroLighter is worth a serious look. The wins compound. Less JavaScript, cleaner markup, real theming through CSS, and the source code in your posts is still readable when the CSS does not load.

If you are running a docs site that highlights twenty different languages, including a few obscure ones, you probably want Prism or Shiki (a syntax highlighter that runs at build time and produces static HTML) instead. The browser's built-in coverage is good for the web, less good for, say, a custom DSL (domain-specific language) you invented last quarter.

Trade-offs

  • Less JavaScript vs less flexibility: the platform approach is fast and clean, but you are limited to what the browser can tokenize.
  • Cleaner markup vs less control: no span tree means no per-token styling past what CSS Custom Highlights can do.
  • Theming by CSS variables vs theming by configuration: the theming surface is CSS, not a JSON config, which is great if you live in stylesheets and less great if you want a runtime theme switcher.
  • Web component vs framework wrapper: you can wrap it in React or Vue, but you are buying a thin layer for portability, not for power.
  • Modern browsers vs older ones: "Baseline this year" is a recent status. If you have a long tail of older browsers, the fallback is no highlighting.

What to do next

If you are curious, the fastest test is to drop the micro-lighter-element.min.js script into a static page, wrap a code block in , and load the included stylesheet. If your site already uses a custom WordPress block or a static site generator, integrating the component is a 20-minute job. The stylesheet is the part you will spend the most time on, because that is where the theming lives. Once you have a theme you like, you can reuse it across the whole site with a single CSS file.

Leave a comment