highlight.jsでクリスマステーマのシンタックスハイライトを作ってみた

## はじめに
はじめてのアドベントカレンダーです！
投稿は何回かしたことがありますが、まさかアドベントカレンダーに参加する日が来るなんて…！
公式様がきっかけを作ってくれたおかげで、一歩踏み出してみました。

https://qiita.com/advent-calendar/2023/first-time

さて、肝心の記事内容は何にしようか？
テーマは不問とのことでしたが、せっかくアドベントカレンダーに参加するならクリスマス🎄にまつわるものがいいと思ったわけです。
そこでタイトルにもある通り、highlight.jsのクリスマステーマを作ってみました！


## highlight.jsとは
highlight.jsはシンタックスハイライトのためのJavaScriptライブラリです。
HTML上の[インラインコード要素](https://developer.mozilla.org/ja/docs/Web/HTML/Element/code)の色付けが簡単にできちゃいます。
（サーバ側でも動作するが本記事では扱いません。）

GitHubでStar 22.4k（2023年11月時点）の有名ライブラリだが、恥ずかしながら直近まで知らなかったので、この機に調べてみようと思います。

https://highlightjs.org/

https://github.com/highlightjs/highlight.js


## 使ってみよう
ひとまず、インラインコードを用意しました。
ソースコードは[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management#allocation_in_javascript)のサンプルコードからです。

```highlight.html
<p>ソースコードです。</p>
<pre><code>
const n = 123; // allocates memory for a number
const s = "azerty"; // allocates memory for a string

const o = {
  a: 1,
  b: null,
}; // allocates memory for an object and contained values

// (like object) allocates memory for the array and
// contained values
const a = [1, null, "abra"];

function f(a) {
  return a + 2;
} // allocates a function (which is a callable object)

// function expressions also allocate an object
someElement.addEventListener(
  "click",
  () => {
    someElement.style.backgroundColor = "blue";
  },
  false,
);
</code></pre>
```

HTMLファイルを開くと…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/77d06563-daa5-ba49-7f7d-d4f43f299e05.png)

うんうん、表示されていますね。

でもこのままだとソースコードとしては少々読みにくいです。
それではhighlight.jsに登場してもらいましょう。
ダウンロードして組み込むやり方もありますが、今回は手軽さ重視でCDNを利用します。

```diff_html:highlight.html
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+ <script>hljs.highlightAll();</script>

<p>ソースコードです。</p>
- <pre><code>
+ <pre><code class="language-javascript">
const n = 123; // allocates memory for a number
const s = "azerty"; // allocates memory for a string

const o = {
  a: 1,
  b: null,
}; // allocates memory for an object and contained values

// (like object) allocates memory for the array and
// contained values
const a = [1, null, "abra"];

function f(a) {
  return a + 2;
} // allocates a function (which is a callable object)

// function expressions also allocate an object
someElement.addEventListener(
  "click",
  () => {
    someElement.style.backgroundColor = "blue";
  },
  false,
);
</code></pre>
```

再度HTMLファイルを開くと…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/9f15924f-a9dc-575b-b5ca-cfd62bf48334.png)

なんとたった4行の変更でシンタックスハイライトが付きました！
簡単！手軽！
今回はdefaultテーマにしましたが、公式曰く「192 languages and 496 themes」をサポートしているとのことなので、需要にぴったりなテーマも探しやすいじゃないかなと思います。


## クリスマスを感じたい
このままではただの紹介記事で終わってしまう…！
今回の本題はクリスマス🎄テーマ、クリスマスを感じたいのです。

先ほど利用したdefaultテーマのCSSをベースに、改造していきます。

https://github.com/highlightjs/highlight.js/blob/main/src/styles/default.css

残念ながらデザインセンスがないので、配色パレットを落とし込む方向に。

https://www.color-hex.com/color-palette/1483

https://www.schemecolor.com/christmas-hex-color-codes.php

`highlight-christmas.css`と命名して、HTMLファイルと同じディレクトリに配置します。

<details><summary>highlight-christmas.css</summary>

```highlight-christmas.css
/*
This is left on purpose making default.css the single file that can be lifted
as-is from the repository directly without the need for a build step

Typically this "required" baseline CSS is added by `makestuff.js` during build.
*/
pre code.hljs {
  display: block;
  overflow-x: auto;
  padding: 1em;
}

code.hljs {
  padding: 3px 5px;
}
/* end baseline CSS */

.hljs {
  background: #165B33;
  color: #FFFFFF;
}

/* Base color: saturation 0; */

.hljs-subst {
  /* default */
}

/* purposely ignored */
.hljs-formula,
.hljs-attr,
.hljs-property,
.hljs-params {}

.hljs-comment {
  color: #378B29;
}
.hljs-tag,
.hljs-punctuation {
  color: #FF7878;
}

.hljs-tag .hljs-name,
.hljs-tag .hljs-attr {
  color: #FF7878;
}


.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta .hljs-keyword,

.hljs-doctag,
.hljs-name {
  color: #EA4630;
  font-weight: bold;
}


/* User color: hue: 0 */

.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
  color: #FF7878;
}

.hljs-title,
.hljs-section {
  color: #F8B229;
  font-weight: bold;
}

.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-operator,
.hljs-selector-pseudo {
  color: #BB2528;
}

/* Language color: hue: 90; */

.hljs-literal {
  color: #74D680;
}

.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
  color: #74D680;
}


/* Meta color: hue: 200 */

.hljs-meta {
  color: #FFFFFF;
}

.hljs-meta .hljs-string {
  color: #FFFFFF;
}


/* Misc effects */

.hljs-emphasis {
  font-style: italic;
}

.hljs-strong {
  font-weight: bold;
}
```

</details>

HTML側が読み込むCSSファイルを変更します。

```diff_html:highlight.html
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+ <link rel="stylesheet" href="highlight-christmas.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>

<p>ソースコードです。</p>
<pre><code class="language-javascript">
const n = 123; // allocates memory for a number
const s = "azerty"; // allocates memory for a string

const o = {
  a: 1,
  b: null,
}; // allocates memory for an object and contained values

// (like object) allocates memory for the array and
// contained values
const a = [1, null, "abra"];

function f(a) {
  return a + 2;
} // allocates a function (which is a callable object)

// function expressions also allocate an object
someElement.addEventListener(
  "click",
  () => {
    someElement.style.backgroundColor = "blue";
  },
  false,
);
</code></pre>
```
再度HTMLファイルを開くと…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/ce348cc4-7f99-5f41-4adb-c2773105418c.png)

クリスマスっぽくなったではないでしょうか！
~~パレットがあっても配置するセンスがないなんて言わないでくださいね！~~


## おまけ

クリスマスツリーを作ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243366/66643691-db59-f2a9-4134-83fa26dc9264.png)

色の視認性を無視！コードの可読性を無視！
形ありきのソースコードが出来上がり。一応実行できるJavaScriptです。
インデントを自由にいじればもっとそれっぽく作れたと思うが、「あくまで関数ですよ」という体を崩したくないという謎のこだわりが発揮しました。
行頭インデントを守るためには右半分しかありませんが、クリスマスツリーに見えましたか？

<details><summary>highlight.html</summary>

```highlight.html
<link rel="stylesheet" href="highlight-christmas.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>

<script>hljs.highlightAll();</script>

<pre><code class="language-javascript">
// ★
function tree() {
  let a;
  a = 8;
  let bc;
  let def;
  bc = "○";
  /*****/
  "♡" != bc;
  let ghijkl;
  ghijkl = 7;
  "●" != def;
  const m = 6;
  def = "✳︎";a = 0;
  /***********/
  const n = 80;
  77777777 != m;
  const o = 6000;
  const p = 800000;
  /***********/
  let qrstuvwxyz;
  qrstuvwxyz = "✳︎";
  alert("Merry Christmas");
  def = "○";qrstuvwxyz = "●";
  alert("Merry Christmas!!!");
}
tree();
tree();
tree();
</code></pre>
```

</details>

<details><summary>highlight-christmas.css</summary>

```highlight-christmas.css
/*
This is left on purpose making default.css the single file that can be lifted
as-is from the repository directly without the need for a build step

Typically this "required" baseline CSS is added by `makestuff.js` during build.
*/
pre code.hljs {
  display: block;
  overflow-x: auto;
  padding: 1em;
}

code.hljs {
  padding: 3px 5px;
}
/* end baseline CSS */

.hljs {
  background: #132823;
  color: #397300;
}

/* Base color: saturation 0; */

.hljs-subst {
  /* default */
}

/* purposely ignored */
.hljs-formula,
.hljs-attr,
.hljs-property,
.hljs-params {}

.hljs-comment {
  color: #F8B229;
}
.hljs-tag,
.hljs-punctuation {
  color: #FF7878;
}

.hljs-tag .hljs-name,
.hljs-tag .hljs-attr {
  color: #FF7878;
}


.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta .hljs-keyword

.hljs-doctag,
.hljs-name {
  color: #2D9B3A;
  font-weight: bold;
}


/* User color: hue: 0 */

.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
  color: #FF7878;
}

.hljs-title,
.hljs-section {
  color: #800000;
  font-weight: bold;
}

.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-operator,
.hljs-selector-pseudo {
  color: brown;
}

/* Language color: hue: 90; */

.hljs-literal {
  color: #74D680;
}

.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
  color: #74D680;
}


/* Meta color: hue: 200 */

.hljs-meta {
  color: #FFFFFF;
}

.hljs-meta .hljs-string {
  color: #FFFFFF;
}


/* Misc effects */

.hljs-emphasis {
  font-style: italic;
}

.hljs-strong {
  font-weight: bold;
}
```

</details>

## 参考

https://laboradian.com/how-to-use-highlightjs/#4_CDN_highlightjs

https://qiita.com/tadnakam/items/99088d78512a20e75ff3

https://ifritjp.github.io/blog2/public/posts/2021/2021-02-01-hilightjs/

https://highlightjs.readthedocs.io/en/latest/theme-guide.html