Evaluating MicroLighter for Syntax and Citation Highlights

Executive summary

MicroLighter is a promising replacement for Seed's current browser-side syntax-highlighting implementation. It uses TextMate grammars and the browser's CSS Custom Highlight API, so it can color code without wrapping every token in a <span>. This could simplify the DOM and reduce the number of ProseMirror decorations used by editable code blocks.

The same browser API can also paint document fragments and citation ranges, but MicroLighter itself is not a general-purpose fragment-highlighting library. Seed's citations need more than visual highlighting: they must retain citation metadata, handle overlapping citations, respond to clicks and hover, validate block revisions, and remain correctly mapped as the document changes. Seed's existing ProseMirror citation plugin already provides those behaviors.

The recommended architecture is therefore:

    use MicroLighter for syntax highlighting, subject to a focused prototype;

    keep ProseMirror decorations for persistent, interactive citation highlights;

    optionally use raw CSS Custom Highlights for temporary, noninteractive fragment focus.

Current implementation in Seed

Syntax highlighting

Seed currently uses lowlight and highlight.js. The editor tokenizes the contents of each code block, turns the resulting token runs into ProseMirror inline decorations, and renders syntax classes such as hljs-keyword onto spans in the editable code DOM.

The same tokenization is also used by Seed's server renderer, allowing server-rendered documents to contain syntax-highlighted spans before the browser hydrates the page.

Citation fragment highlighting

Seed has a dedicated CitationFragmentHighlightPlugin built on ProseMirror decorations. It:

    converts stored Unicode code-point offsets into ProseMirror positions;

    finds the target block and checks its revision;

    divides overlapping citations into independently styled segments;

    retains the citation records associated with every segment;

    maps citation ranges through ProseMirror transactions;

    excludes text inserted inside an existing citation from the cited range;

    emits the relevant citation records when a highlighted range is clicked;

    supports interactive and noninteractive rendering modes;

    provides overlap, pointer, and hover styling through ordinary CSS classes.

These are document-model and interaction responsibilities, not merely painting responsibilities.

How MicroLighter works

MicroLighter is a small, dependency-free syntax highlighter built around TextMate grammars and the CSS Custom Highlight API. Its public highlightAll() function:

    finds code blocks with language metadata;

    loads the required TextMate grammars on demand;

    parses each code block into semantic token ranges;

    creates browser Range objects for those tokens;

    groups the ranges into named Highlight sets such as keyword, string, and comment;

    registers those sets in CSS.highlights;

    lets theme CSS paint them using selectors such as ::highlight(keyword).

Because the color is painted by the browser, the code remains plain text in the DOM. This is the main advantage over Seed's current token-span approach.

MicroLighter currently exposes syntax highlighting only. Its internal range creation and grammar categorization are not public APIs for arbitrary application highlights.

Suitability by requirement

Requirement

MicroLighter as published

Underlying CSS Highlight API

Recommended Seed implementation

Syntax highlighting

Yes

Yes

Prototype MicroLighter

Static fragment background

No

Yes

Use only where interaction is unnecessary

Overlapping citation ranges

No

Yes

Keep the existing citation plugin

Citation metadata

No

No

Keep it in ProseMirror plugin state

Citation click handling

No

Limited browser hit-testing

Keep ProseMirror decorations

Hover and pointer feedback

No

More limited than DOM elements

Keep ordinary citation spans and CSS

Mapping ranges through edits

No

No

Keep ProseMirror transaction mapping

Block revision validation

No

No

Keep the existing citation plugin

Server-side syntax rendering

No

No

Retain a server strategy

Can syntax and citations coexist?

Yes. The CSS Custom Highlight registry supports multiple named highlights at the same time. MicroLighter registers syntax categories using names such as keyword, string, and comment. Seed can avoid collisions by namespacing any application-owned highlights, for example:

seed-fragment-focus seed-citation-overlap-1 seed-citation-overlap-2

MicroLighter also checks that a registry entry still refers to the Highlight object it created before deleting that entry during a refresh. It should therefore leave independently owned Seed highlights alone.

More importantly, Seed does not need to move citations to the Custom Highlight API for the two systems to coexist. A practical arrangement is:

    MicroLighter paints the foreground color of syntax tokens;

    ProseMirror citation decorations paint citation backgrounds and expose interactive DOM spans;

    both layers cover the same text when a citation points into a code block.

This retains citation behavior while removing syntax-only token spans.

Why citations should remain ProseMirror decorations

A CSS custom highlight is not a DOM element. It cannot directly carry attributes such as data-citation-ids, and normal event delegation cannot discover it with event.target.closest(). Browser APIs for finding highlights at a screen coordinate are newer and require a separate compatibility review. Even with reliable hit testing, Seed would still need its current plugin state to recover citation records and map their ranges through document changes.

Moving citation paint to CSS.highlights would therefore remove some spans but would not remove most of the citation implementation. It would add DOM-range synchronization, browser hit testing, and another lifecycle to the existing ProseMirror transaction logic.

ProseMirror decorations already match the citation feature's semantics:

    they are derived from document positions;

    they map through transactions;

    they can carry classes and data attributes;

    they participate naturally in editor event handling;

    they support the existing hover and overlap presentation.

The smaller DOM benefit of converting citations does not currently justify the additional complexity.

Where custom fragment highlights could help

The CSS Custom Highlight API may still be valuable for temporary, noninteractive states. One example is briefly focusing a fragment after navigating to it. Such a highlight only needs to paint a range and can be discarded after the navigation state changes.

A useful division of responsibility would be:

CSS Custom Highlights ├── syntax token colors └── temporary, noninteractive fragment focus ProseMirror decorations └── persistent, interactive citation ranges

This should be treated as an optional follow-up rather than part of the initial MicroLighter adoption.

Risks and questions for a prototype

Editable DOM structure

MicroLighter only processes a code element when it contains exactly one normalized text node. Seed must confirm that live code blocks preserve that structure after typing, paste, undo, collaboration updates, language changes, and editor remounts.

Refresh lifecycle

MicroLighter offers an automatic document scan, but repeatedly scanning the entire editor after every transaction would be wasteful. A Seed integration should call the programmatic API from a controlled editor lifecycle and limit work to code blocks that need refreshing where possible.

Multiple editors and previews

MicroLighter retains its syntax Highlight sets in module-global state. Calling highlightAll() clears and rebuilds those syntax sets globally, even when the call receives a narrow root. This needs testing when multiple editors or document previews are mounted at once.

Performance

MicroLighter rescans the complete text node of a code block. The prototype should compare editing latency and memory use with the current Lowlight decorations, especially for large blocks and collaborative updates. A smaller DOM does not automatically guarantee less parsing work.

Server rendering and hydration

Seed currently renders syntax-highlighted spans on the server. MicroLighter depends on browser APIs including Range, Highlight, and CSS.highlights, so it cannot provide the same server output.

The prototype must choose one of these strategies:

    retain Lowlight only for server rendering and switch to MicroLighter in the browser;

    render plain code on the server and apply highlighting after hydration;

    introduce a separate server-compatible TextMate tokenizer.

The second option is the simplest but may produce a visible flash from plain code to highlighted code.

Language behavior

Seed currently configures Lowlight's common language set and can fall back to automatic detection. MicroLighter has a fixed collection of bundled grammars and expects recognized language metadata. The prototype should compare the languages and aliases exposed in Seed's language selector and decide whether automatic detection is still required.

Browser and Electron compatibility

The baseline CSS Custom Highlight API and the exact Electron/browser versions supported by Seed must be checked together. Any proposal to make citations interactive through highlight hit testing would require a stricter compatibility review than syntax painting alone.

Styling constraints

The properties supported inside ::highlight() are intentionally limited. They are well suited to syntax foreground colors and simple fragment backgrounds, but they are less flexible than regular elements for interactive citation presentation.

Recommended prototype

The first experiment should change syntax highlighting only and leave citation behavior untouched.

Scope

    Integrate MicroLighter into live editable code blocks.

    Preserve the existing language selector and serialized code-block model.

    Keep the current citation fragment plugin unchanged.

    Test citations that overlap ordinary text and code-block text.

    Decide explicitly how server-rendered code behaves.

    Do not introduce CSS-based citation hit testing in this prototype.

Success criteria

    Syntax highlighting remains correct while typing and collaborating.

    Copy and paste preserve plain code without syntax markup.

    Citation backgrounds, overlap levels, hover states, and click handling still work.

    Multiple mounted documents do not clear one another's syntax highlights.

    Supported languages and aliases have an acceptable migration path.

    Server rendering does not cause broken hydration or an unacceptable visual flash.

    Large code blocks perform at least as well as the existing Lowlight implementation.

Decision after the prototype

Adopt MicroLighter if its clean-DOM advantage produces a measurable maintenance or performance benefit without regressing SSR, multi-editor behavior, language support, or citation interaction. Otherwise, retain Lowlight and revisit only if the syntax decoration layer becomes a demonstrated bottleneck.

Conclusion

MicroLighter can coexist with Seed's fragment and citation highlights, and it is a credible option for syntax highlighting. It should not replace Seed's citation plugin.

The cleanest architecture is to use each mechanism for the job it models best:

    MicroLighter and CSS Custom Highlights for browser-painted syntax tokens;

    ProseMirror decorations for persistent, interactive citations tied to document transactions;

    possibly CSS Custom Highlights for temporary, noninteractive fragment focus.

This approach captures MicroLighter's main benefit—a clean editable code DOM—without giving up Seed's existing citation semantics and interaction model.

References

Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime