本ブログでも使っている、Syntax Highlighter Shiki
をはてなブログに導入する方法をここに記します。
Shiki | 式
Shiki
とは TypeScript 製の Syntax Highlighter で、対応言語やテーマの数が豊富なことからとても人気があります。(筆者の主観)
markdown-it
や VitePress
などの Integration が提供されていたり Astro
に組み込まれていたりと、触れたことがある人も多いのではないでしょうか。
はてなブログ
さて、はてなブログの Syntax Highlighter を変更するといっても既存のものを止める訳ではありません。
今のところ、HTML のレンダリングに介入することは出来ないので...
置き換える
はてなブログではヘッダに任意の HTML を挿入することができるので、HTML 読み込み時に既存のコードブロック要素を Shiki
でレンダリングしたものに後から置き換えることにします。
(ここでブラウザをリロードしてみると、コードブロックが置き換わる様子が見えるでしょう)
ドキュメントにあるブラウザ向けの CDNを利用して、はてなブログの HTML 上にあるコードブロックを置き換える処理を実装します。
<scripttype="module">import { codeToHtml } from"https://esm.sh/shiki@1.12.0"constpres=document.querySelectorAll("pre.code") pres.forEach((pre)=>{const lang = pre.dataset.lang const rawCode = pre.textContent ;(async()=>{const code =awaitcodeToHtml(rawCode,{theme:"github-dark",lang: lang,})const dummy =document.createElement("div") dummy.innerHTML = code pre.replaceWith(dummy.firstChild)})()})</script>
あとは、ブログの 設定 > 詳細設定 > head内タグ > <head>要素にメタデータを追加
から上記のスクリプトを張り付ければ完成です。
ブログのテーマによっては、CLS(Cumulative Layout Shift)の原因となるので元のコードブロックのフォントサイズなどを揃えておきましょう。
HTML 編集モード
HTML 編集モードでは、はてなブログで生成されるコードブロックの HTML に合わせて <pre>
タグに code
classと、data-lang
attribute に言語を指定してください。
<preclass="code"data-lang="javascript"> console.log("Hello, World!") </pre>
設定
Themes
Shiki
には次のテーマが指定可能です。
prefers-color-scheme
Light/Dark Dual Themes | Shiki
端末のダークモードに追従させることもできます。
- theme: "github-dark",+ themes: { + light: 'github-light',+ dark: 'github-dark',+ },
上記のようにテーマを設定した後、以下のスタイルをブログの 設定から <head>要素
に追加してください。
<style>@media (prefers-color-scheme: dark) {.shiki, .shikispan{color: var(--shiki-dark)!important; background-color: var(--shiki-dark-bg)!important; /* Optional, if you also want font styles */font-style: var(--shiki-dark-font-style)!important; font-weight: var(--shiki-dark-font-weight)!important; text-decoration: var(--shiki-dark-text-decoration)!important; }}</style>
おまけ
Shiki
は v1 から ESM となりました。
つまり、JavaScript モジュールで使う必要があります。
もしも、あなたが過去を生きるユーザーも救いたいということであれば、以下に古いバージョンのコードを用意したのでお使いください。
<scriptdefersrc="https://unpkg.com/shiki@0.14.6"></script><script>window.addEventListener("DOMContentLoaded",function(){const pres =document.querySelectorAll("pre.code")const langs =[]for(const pre of pres){const lang = pre.dataset.lang langs.push(lang)} shiki.getHighlighter({theme:'github-dark',langs: langs,}).then(highlighter =>{ pres.forEach(pre =>{const lang = pre.dataset.lang const rawCode = pre.textContent const code = highlighter.codeToHtml(rawCode,{lang: lang })const dummy =document.createElement('div') dummy.innerHTML = code pre.replaceWith(dummy.firstChild)})})})</script>
inline スクリプトに defer 使いたいナ