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-2MicroLighter 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 rangesThis 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