Home Business & Finance Introducing MicroLighter
Business & Finance

Introducing MicroLighter

Key Points

I made a smol client-side syntax highlighter that uses the CSS Custom Highlights API called MicroLighter. I’ll talk about why I made it in a bit but first I wanted to… ahem… highlight… some of the features. - Zero-dependencies - ~2kb minifed+gzipped - Uses CSS ::highlight(token-name) , not spans - Leverages Textmate’s language grammars - Human-readable light-dark() themes - All languages/grammars loaded on-demand - Moves all non-highlight functionality to a custom element At some point I...

I made a smol client-side syntax highlighter that uses the CSS Custom Highlights API called MicroLighter. I’ll talk about why I made it in a bit but first I wanted to… ahem… highlight… some of the features. - Zero-dependencies - ~2kb minifed+gzipped - Uses CSS ::highlight(token-name) , not spans - Leverages Textmate’s language grammars - Human-readable light-dark() themes - All languages/grammars loaded on-demand - Moves all non-highlight functionality to a custom element At some point I broke syntax highlighting on my Jekyll blog. I’ve used a handful of syntax highlighters over the years (Highlight.js, PrismJS, Rouge, Shiki, etc, etc) and I’ve felt the trade-offs between different client-side and server-side implementations. Faced with picking another, I knew I wanted to explore Bramus’s technique of syntax highlighting with the CSS Custom Highlights API. There’s some limitations with the CSS ::highlight() pseudo; no italics, no bold, no font swapping – but otherwise it’s pretty cool syntax to idiomatically express “I want to highlight this token” via CSS instead of injecting spans everywhere. Using the Highlight API means I avoid any DOM mutation and the scope of the library shrinks down to: scan code blocks using regex patterns and send CSS.highlights.set(category, textRanges) to highlight the code blocks. I don’t need much syntax highlighting on this site. Not all posts have code and my code samples are a whole fifteen lines long at best. My struggle is that I swap languages often. I’ll do HTML, CSS, and JavaScript all in the same post. A little bash here, a little ruby there, some markdown as a treat. Using so all those languages grew the complexity beyond the limits of my regex-fu, so I decided to lean on Textmate’s established collections of patterns used by VS Code. And before I knew it, my little highlighter could do almost any language. Knowing I use different languages often, one principle I established was that all language grammars should be auto-loaded on-demand to reduce configuration and bundle size. That way you only pay for what you use. Inspired by PrismJS’s simplified token categories I flattened down Textmate’s granular token categories to a more human-friendly set, making it easier to style. In addition to that, one enormous nit-pick I have with codeblock styling is that light and dark themes are separate entities, so I merged them into one theme using light-dark() . The last big opinion I baked in was that I wanted the syntax highlighter to do one job: infer language and highlight code in that language. With that a guideline, I moved all extra functionality (like line-numbers, etc) over to a web component. The vanilla web component adds about ~1 KiB in size, but co-locating UI into a UI primitive like native custom elements feels right and the ShadowDOM encapsulation makes it easy to separate the code from the presentational UI. Obviously, I’m a web component pervert but it feels like a great separation of concerns versus trying to cram everything into core library. Try it out To get started on your site, I’d use the self-initializing minified bundle, but I’m also shipping ESM and a web component. npm install microlighter Like I said above, not all my posts have syntax highlighting, so I even wait to import the script unless I know there’s a page with code on it. if(document.querySelector('pre>code').length) { import('path/to/microlighter/microlighter.min.js'); } You can use the ESM version if you’re going to do something fancy yourself: import { highlightAll } from 'microlighter' highlightAll({ selector: 'pre.onlyTheseGetHighlights' }) And you can also use the web component if you want those extra features I talked about: Code goes here Web component classes are pretty extendible too so if I don’t support something you need, you can “fork” it by extending the base class and adding your own features. Lastly, you can use one of the pre-supplied themes or roll your own. The basic structure is: /** * Setup semantic `--syntax-*` tokens * @value background | foreground | comment | keyword | * operator |string | constant | function | type | variable | * property | tag | selector | inserted | deleted */ [data-syntax-theme="my-theme-name"] { color-scheme: light dark; /* Code block tokens */ --syntax-background: light-dark(#f8f8f8, #3a3a3a); --syntax-foreground: light-dark(#3a3a3a, #f8f8f8); /* Highlight tokens */ --syntax-comment: light-dark(#6e7781, #8b949e); --syntax-function: light-dark(#8250df, #d2a8ff); /* ...etc... */ } [data-syntax-theme="my-theme-name"] pre:has(code) { background-color:var(--syntax-background); color:var(--syntax-foreground) } ::highlight(comment) { color: var(--syntax-comment) } ::highlight(function) { color: var(--syntax-function) } /* ...etc... */ And that’s MicroLighter. If you end up using it and trying it out, let me know what you think.
CSS (ORG) MicroLighter (ORG) minifed+gzipped (ORG) Jekyll (ORG) Highlight.js (LOCATION) Rouge (ORG) Shiki (PERSON) Bramus (PERSON) DOM (ORG) Textmate (ORG) KiB (PERSON) UI (ORG) ShadowDOM (ORG)
Originally published by Hacker News Read original →