
2026/08/20 3:28
MicroLighter の導入を発表
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
MicroLighter は、現代的な CSS Custom Highlights API に基づく、軽量で依存関係のないクライアントサイドのシンタックスハイライターです。その主な利点は、DOM を変更せずに CSS の擬似クラスを通じてスタイルを適用できる点にあり、足跡は約 2kb のみ(バニラの Web コンポーネントコンポーネントを追加すると約 1KiB)という極めて小さなサイズを実現します。この効率性により、開発者は npm(
npm install microlighter)またはネイティブタグを通じてツールを容易に採用でき、重い外部ライブラリに依存する必要がありません。行番号などハイライト以外の機能を、ShadowDOM 内にある別々のカスタム要素に分離することで、懸念事項の明確な分離を保証します。ハイライターは VS Code でも使用されている Textmate グラマーからの強力なパターンマッチングを活用し、多数のプログラミング言語をデフォルトでサポートするとともに、オンデマンドでロードすることでバンドルサイズを削減します。そのアーキテクチャは、light-dark() 関数を用いてライトモードとダークモードをシームレスに統合し、事前供給されたセマンティックな --syntax-* トークンを通じてテーマ間で一貫した視覚的アイデンティティを確保します。ユーザーは、提供された Web コンポーネントクラスから継承すること、またはコードブロックが存在するのを待って自己初期化するバンドルを利用することで、さらに機能性を拡張できます(document.querySelector('pre>code').length)。最終的に、MicroLighter はパフォーマンスと簡潔さが最優先である現代の Web 開発プロジェクトにおいて、柔軟な解決策を提供しながら、特定の擬似クラス構文(例:::highlight(token))を通じてチームが視覚体験を容易にカスタマイズできるようにします。本文
MicroLighter:軽量なクライアントサイド構文ハイライトライブラリ
MicroLighter は、CSS Custom Highlights API を活用した、軽量なクライアントサイド用構文ハイライトライブラリです。以下に主な特徴と開発経緯、使い方を解説します。
主な特徴
- ゼロ依存関係
2KB の最小化および Gzip 圧縮サイズ(※注釈あり)
タグを使用せず、span
擬似要素を採用::highlight(token-name)- Textmate の言語文法を基調としている
- ヒューマンリダブルな
テーマ対応light-dark() - すべての言語・文法をオンデマンドで読み込み
- ハイライト以外の機能は
カスタムエレメントへ集約<micro-lighter>
開発経緯
背景と課題
ある日、Jekyll ブログの構文ハイライトが動作しなくなりました。これまでに Highlight.js、PrismJS、Rouge、Shiki などさまざまなライブラリを試してきましたが、クライアントサイドとサーバーサイド実装の間には常にトレードオフが存在していました。そこで、Bramus の CSS Custom Highlights API を採用する手法に挑戦することを決めました。
技術的な利点
::highlight() 擬似要素には斜体化や太字化などの制限がありますが、DOM 内に span タグを注入する必要がなくなります。
- 「このトークンをハイライトしたい」という意図をCSS で自然かつ明確に表現できる点が非常に利便性が高いです。
- Highlight API を活用することで DOM マニピュレーションが不要となり、ライブラリ自体の役割は単にコードブロックを正規表現でスキャンし、
を呼び出すことのみとなります。CSS.highlights.set(category, textRanges)
設計上の決断
本サイトでの使用頻度が限定的であり(投稿数は限定、コードは約 15 行程度)、言語の頻繁な切り替えが課題でした。HTML/CSS/JavaScript の混在や Bash/Ruby/Markdown の扱いなど、すべての言語に対応しようとすると正規表現の複雑化に限界がありました。
これに対し以下の設計原則を確立しました。
- Textmate パターンの採用
- VS Code でも採用されている Textmate の確立されたパターンコレクションを活用し、ほぼすべての言語に対応可能になりました。
- 「必要な分だけ」の実装(オンデマンド読み込み)
- 最初の原則として「言語文法はオンデマンドで自動的に読み込む」仕組みを実現しました。これにより設定の複雑化やバンドルサイズの肥大化を回避できます。
- Human Friendly なトークンカテゴリ
- PrismJS の単純化されたカテゴリからヒントを得て、Textmate の細かいカテゴリをヒューマンフレンドリーなセットに整理・スタイリングしやすい形式にしました。
- 「Light/Dark」テーマの統合
- ライトテーマとダークテーマを別々の実体として管理する不満を解消。
を用いて一つのテーマで両方のスタイルをカバーするようにしました。light-dark()
- ライトテーマとダークテーマを別々の実体として管理する不満を解消。
- 役割の明確化と関心の分離
- 構文ハイライトライブラリが担うべき役割は「言語の推測とコードのハイライトのみ」という一点に絞り込みました。
- 行番号表示などの追加機能は、約1KiBのサイズ増加を伴う Web コンポーネントへ移行しています。これにより、Shadow DOM のカプセル化でコード層とプレゼンテーション層(UI)を容易に分離でき、関心の分離を実現しました。
使い始める方法
サイトへの導入には、自己初期化するミニファイされたバンドルをお勧めしますが、ESM モジュールや Web コンポーネントも用意しています。
npm install microlighter
1. 自動実行型(minified bundle)
すべての投稿にハイライト処理を適用したい場合、または簡易的な利用の場合は以下のスクリプトタグを使用します。
<script type="module" src="path/to/microlighter/microlighter.min.js"></script>
-
条件付き読み込み: すべての投稿で構文ハイライトが必要なわけではありません。コードが含まれているページのみを特定し、その際にスクリプトをインポートすることも可能です。
if(document.querySelector('pre>code').length) { import('path/to/microlighter/microlighter.min.js'); }
2. ESM バージョン(高度な利用)
ご自身でより柔軟に制御したい場合は、ESM モジュールをご利用ください。
import { highlightAll } from 'microlighter' highlightAll({ selector: 'pre.onlyTheseGetHighlights' })
3. Web コンポーネント(追加機能付き)
行番号表示やコピーボタンなどの追加機能を必要とされる場合は、以下の Web コンポーネントを使用します。
<micro-lighter data-syntax-theme="github" line-numbers controls="copy"> <pre><code>Code goes here</code></pre> </micro-lighter>
- 高度なカスタマイズ: Web コンポーネントのクラスは非常に拡張性に富んでいます。ベースクラスを継承して独自機能を追加する「フォーク」も可能です。
テーマのカスタマイズ
事前に用意されたテーマを使用するか、ご自身で独自に作成することも可能です。基本的な構造は以下の通りです。
/** * セマンティックな `--syntax-*` トークンのセットアップ */ [data-syntax-theme="my-theme-name"] { color-scheme: light dark; /* コードブロック全体の色 */ --syntax-background: light-dark(#f8f8f8, #3a3a3a); --syntax-foreground: light-dark(#3a3a3a, #f8f8f8); /* ハイライト用のトークン色 */ --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... */