1. はじめに
筆者の下記の記事において Qiita の MathJax における不具合および回避方法を述べた。残念ながら Math-Renderer を CHTML モードから SVG モードに切り替える方策は SVG モードに深刻な不具合があり,直ちには使えないことが分かった。
※その後,SVG モードは使用できなくなったので SVG モードの不具合については考えなくてよい。
本記事では CHTML モードのまま不具合を改善する方策について検討したい。
2. 主な不具合の症状
@tsubolabo さんからは計4つの不具合事例が報告されているが,このうち代表的な3つについて具体的な症状を示す。OS やブラウザなどの環境によって見た目が変わるため,筆者のメイン環境(Windows11 Mozilla Firefox)におけるスクリーンショットを貼った。
(1) 文字の上の装飾記号の位置が高すぎる。
一例としてドット記号を用いるニュートン式の微分記法の例を示す。一番右の黒板太字 $\mathbb{e}$ を除けば,左と中央のドット記号はさほど不自然には見えないかもしれない。ただ,ニュートンの記法(wikipedia)等と見比べると確かにドット記号が離れている。
```math
\dot{e} = \dot{\mathbf{e}} = \dot{\mathbb{e}}
```
(2) 大型括弧(場合分けなど)が上下に分断する。
OS やブラウザなどの環境によって分断される位置や分断距離が多少変わる。
```math
\left\{ \begin{eqnarray}
a! \equiv 0 \pmod 3 \\
-4m^2 \equiv -m^2 \pmod 3 \\
4^c \equiv 1 \pmod 3 \\
2024 \equiv 2 \pmod 3
\end{eqnarray} \right. \tag{6}
```
(3) 数式番号の上下方向の位置ずれ。
数式番号 $(7)$ は5行目の等号の位置(高さ)にあることが期待されている。
```math
\begin{align}
\tau_s &= \int d\tau_s \\
&= \int_{-\pi}^{\pi} \frac{GM_s}{R_e^3} \cdot \mu \cdot r_p^3 \cos^2 \phi \sin 2 \varepsilon \cdot d\phi \\
&= \frac{GM_s}{R_e^3} \cdot \mu \cdot r_p^3 \sin 2 \varepsilon \int_{-\pi}^{\pi} \cos^2 \phi \cdot d\phi \\
&= \frac{GM_s}{R_e^3} \cdot \mu \cdot \pi \cdot r_p^3 \sin 2 \varepsilon \\
&= \frac{2GM_s}{3R_e^3} \cdot \rho \cdot \pi \cdot r_p^3 \cdot (r_e^2 - r_p^2) \sin 2 \varepsilon \tag{7}
\end{align}
```
3. 調査方針
それでは如何にして原因を特定するか?まず調査方針について述べる。
上記の不具合 (1)~(3) はいずれも上下方向の位置ズレであることから,Qiita の記事で設定しているスタイル要素が MathJax の設定と不整合を起こしているものと考えられる。
-
marginもしくはmargin-top,margin-bottom -
paddingもしくはpadding-top,padding-bottom font-sizeline-heihgt
とはいえ Qiita の記事のページは中を見てもよく分からない。記事の適当な場所(ただし数式ブロック以外)で右クリックして「ページのソース表示」により,HTML の生ソースを見ても理解できない。JavaScript を駆使した高度で動的なページが構成されており,加工前のインプットデータを見てもよく分からないのだ。こういうときは加工後のアウトプットデータを見るべきだろう。具体的には Mozilla Firefox であれば右クリックして「調査」を選び,「インスペクター」でどこか適当な HTML タグ,例えば <html lang="ja"> の上で右クリックして「HTML として編集」を選ぶ。さらに右クリックして「すべて選択」を選び,続けて右クリックして「コピー」する。後は適当なテキストエディタ(メモ帳でも可)に貼り付けて保存する。文字コードは UTF-8 で保存すること。
さらに適当な HTML コード整形ツールあるいはサイトで見易いように加工すると,おぼろげに構造が見えてくる。まず先頭のほうに Qiita が提供しているスタイルシートを参照している部分が見える。
<link rel="stylesheet" href="https://cdn.qiita.com/assets/public/article-f243fbeee784b89cecda0ffa895ccc56.min.css" media="all">
続いて MathJax のスタイル定義と思われる領域が1000行以上続く。
<style id="MJX-CHTML-styles">
mjx-container[jax="CHTML"] {
line-height: 0;
}
mjx-container [space="1"] {
margin-left: .111em;
}
mjx-container [space="2"] {
margin-left: .167em;
}
mjx-container [space="3"] {
margin-left: .222em;
}
mjx-container [space="4"] {
margin-left: .278em;
}
mjx-container [space="5"] {
margin-left: .333em;
}
/* 中略 */
</style>
さらに読み進めると以下のように数式を構成するタグ構造が現れる。隙間や間隔などの数値が直打ちされているので,ここを修正するとなれば一大事である。
<mjx-container class="MathJax CtxtMenu_Attached_0" jax="CHTML" style="font-size: 113.1%; position: relative;" display="true" tabindex="0" ctxtmenu_counter="4">
<mjx-math display="true" style="margin-left: 0px; margin-right: 0px;" class="MJX-TEX" aria-hidden="true">
<mjx-texatom texclass="ORD">
<mjx-texatom texclass="ORD">
<mjx-mover>
<mjx-over style="padding-bottom: 0.105em; padding-left: 0.289em; margin-bottom: -0.549em;">
<mjx-mo class="mjx-n" style="width: 0px; margin-left: -0.25em;">
<mjx-c class="mjx-c2D9">
</mjx-c>
</mjx-mo>
</mjx-over>
<mjx-base>
<mjx-mi class="mjx-i">
<mjx-c class="mjx-c1D452 TEX-I">
</mjx-c>
</mjx-mi>
</mjx-base>
</mjx-mover>
</mjx-texatom>
<!-- 中略 -->
</mjx-container>
おそらくは,これらの3つの設定が不整合を起こしているものと考えられる。
4. Markdown ビュアーとの比較
Qiita の MathJax の利用方法自体に何か問題があると考えられるので,他のサイト,たとえば Zenn や note と比べようと思ったが,残念ながら両社とも数式表示には KaTex を使っているようだ。github は MathJax を使用しているが,多くの機能を使えないようマスクしており参考にならないと判断した。
そこで,ブラウザの Markdown Viewer と比較することにした。ブラウザの「拡張機能」もしくは「アドオン」でインストールする。下記サイトのものは MathJax や mermaid もサポートしているので筆者は愛用している。
こうして上記の不具合事例 (1)~(3) を打ち込んでみる。
$$
\dot{e} = \dot{\mathbf{e}} = \dot{\mathbb{e}}
$$
$$
\left\{ \begin{eqnarray}
a! \equiv 0 \pmod 3 \\
-4m^2 \equiv -m^2 \pmod 3 \\
4^c \equiv 1 \pmod 3 \\
2024 \equiv 2 \pmod 3
\end{eqnarray} \right. \tag{6}
$$
$$
\begin{align}
\tau_s &= \int d\tau_s \\
&= \int_{-\pi}^{\pi} \frac{GM_s}{R_e^3} \cdot \mu \cdot r_p^3 \cos^2 \phi \sin 2 \varepsilon \cdot d\phi \\
&= \frac{GM_s}{R_e^3} \cdot \mu \cdot r_p^3 \sin 2 \varepsilon \int_{-\pi}^{\pi} \cos^2 \phi \cdot d\phi \\
&= \frac{GM_s}{R_e^3} \cdot \mu \cdot \pi \cdot r_p^3 \sin 2 \varepsilon \\
&= \frac{2GM_s}{3R_e^3} \cdot \rho \cdot \pi \cdot r_p^3 \cdot (r_e^2 - r_p^2) \sin 2 \varepsilon \tag{7}
\end{align}
$$
ブラウザでプレビューすると(大型括弧の途中の線が細くなることを除けば)いい感じである。
で,実際にどのような HTML タグが動的に生成されているのか,前記の右クリックメニューで確認してみたところ,1000行以上のスタイル定義領域および数式を構成するタグ構造の領域のいずれもほぼ一致したのだ。もちろん完全一致する訳ではないが,数式の上下方向に関する位置や隙間・間隔,フォントの大きさや高さなどの主要数値は完全に一致したのだ。さすがに Qiita は MathJax のコードジェネレーターまでカスタマイズしていないということであろう。
つまり Qiita 提供のスタイルシート article-f243fbeee784b89cecda0ffa895ccc56.min.css が怪しいと思われる。
5. Qiita 提供のスタイルシートをマスクする
Qiita 提供のスタイルシートの影響を排除するためには下記のようにスタイルシートをリンクしている行をコメントアウトすればいい。
<!--
<link rel="stylesheet" href="https://cdn.qiita.com/assets/public/article-f243fbeee784b89cecda0ffa895ccc56.min.css" media="all">
-->
こうするとコードブロックなど数式以外の領域はスタイル未定義のためデタラメな表示となってしまうが,数式部分は HTML ファイル本体にスタイルが書かれているので問題なく表示される。以降はスクリーンショット画像を貼り付けたものだ。
文字の上の装飾記号の位置は(黒板太字を除けば)しっかり下がっている。
大型括弧は無事繋がる。ただし,途中で線が細くなってしまう課題は Markdown ビュアーと同じである。
数式番号の位置もしっかり5行目の等号の位置(高さ)にある。
以上より,HTML ファイル本体には数式に必要かつ十分なスタイルが定義されており,Qiita 提供のスタイルシートファイルがバランスを崩しているものと考えられる。
6. スタイルシートのどこが原因か?
まず,Qiita 提供のスタイルシートファイルをダウンロードする。直接 URL アドレスを指定してダウンロードしても良いと思うが,筆者は記事ページをまるまる「名前を付けて保存」した。例えば,保存するファイル名を Qiita.html とすると Qiita_files というフォルダが作られ,参照しているスタイルシートや JavaScript ファイル等がまとめて保存される。
スタイルシートはスペースや改行コードが圧縮されている形(minified)で配布されているので,適当な CSS 整形ツールやサイト等を利用して整形する。このときファイル名を article.css と短い名前に変えてコピーしておいたほうがよい。文字コードは UTF-8 にすること。
次にスタイルシートを書き換える C# プログラムを作成した。
using System;
using System.IO;
using System.Text;
class COMMENTOUT {
static int Main(string[] args) {
if(args.Length < 5) {
Console.Error.WriteLine("commentout(.exe) [input-file] [output-file] [start-line] [end-line] [keyword]");
return -1;
}
var input = args[0];
var output = args[1];
var start = int.Parse(args[2]);
var end = int.Parse(args[3]);
var keyword = args[4];
var reader = new StreamReader(input, Encoding.UTF8);
var writer = new StreamWriter(output, false, Encoding.UTF8);
int line = 1;
while(!reader.EndOfStream) {
string s = reader.ReadLine();
if(start <= line && line <= end && s.IndexOf(keyword) >= 0) {
Console.Error.WriteLine("{0:###,0} {1}", line, s);
writer.WriteLine("//" + s);
} else {
writer.WriteLine(s);
}
line++;
}
reader.Close();
writer.Close();
return 0;
}
}
ビルド方法の説明は省略する。使い方は下記のようにコマンドラインから①オリジナルのスタイルシートファイル名,②書き換え後のスタイルシートファイル名,③開始行,④終了行,⑤検索キーワードの順で引数を与える。ちなみに整形後のスタイルシートファイルの行数が10780行だったので,下記は全ての font-size 要素をコメントアウトするという意味である。
commentout Qiita_files\article.css Qiita_files\article2.css 1 10780 font-size
そして変更後のスタイルシートファイルを参照するよう元の HTML ファイルを書き換える。
<link rel="stylesheet" href="Qiita_files/article2.css" media="all">
まず全ての font-size 行をコメントアウトしたが,数式の表示乱れは直らなかった。次に検索キーワードを line-height に変えると数式の表示乱れは改善された。あとは捜査範囲を絞っていけばいい。下記はファイルの後半に絞って捜索した場合であるが,いきなり容疑者は以下の5行に絞られた。
c:\Qiita>commentout Qiita_files\article.css Qiita_files\article2.css 5000 10780 line-height
5,203 line-height: .2;
5,589 line-height: var(--line-height-body-dense);
5,719 line-height: 1.3;
5,772 line-height: 1.6
5,786 line-height: 1.4
そして5203行目が犯人だったという訳である。該当行前後のスタイルシートを以下示す。
/* 5201 */ .it-MdContent mjx-math,
/* 5202 */ .it-MdContent mjx-assistive-mml {
/* 5203 */ line-height: .2;
/* 5204 */ max-width: 100%;
/* 5205 */ overflow-x: auto;
/* 5206 */ overflow-y: hidden;
/* 5207 */ padding-inline: 2px;
/* 5208 */ scrollbar-width: thin;
/* 5209 */ scrollbar-color: var(--color-surfaceVariant) rgba(0, 0, 0, 0)
/* 5210 */ }
数式表示は改善されたので,以下スクリーンショット画像を貼る。
まず,文字の上の装飾記号の位置は(黒板太字を除けば)しっかり下がる。
大型括弧は無事繋がるし,途中で線が細くなってしまうこともない。
数式番号の位置もしっかり5行目の等号の位置(高さ)にある。
7. 結論
対策を以下まとめる。Qiita の記事で参照しているスタイルシート設定ファイル article-f243fbeee784b89cecda0ffa895ccc56.min.css を修正すること。
※ファイル名は記事によって変わる。
実際のスタイルシートはスペースや改行コードが圧縮されている形(minified)で配布されており,適当な整形ツールで整形(beautified)すると全10780行となった。このうち下記5203行目(line-height の行)をコメントアウトするだけで不具合が解決する。
/* 5201 */ .it-MdContent mjx-math,
/* 5202 */ .it-MdContent mjx-assistive-mml {
/* 5203 */ /* line-height: .2; 👈 おまわりさん,こいつが犯人です */
/* 5204 */ max-width: 100%;
/* 5205 */ overflow-x: auto;
/* 5206 */ overflow-y: hidden;
/* 5207 */ padding-inline: 2px;
/* 5208 */ scrollbar-width: thin;
/* 5209 */ scrollbar-color: var(--color-surfaceVariant) rgba(0, 0, 0, 0)
/* 5210 */ }
8. ユーザーの回避手段
上記の変更案を Qiita 運営に提案する前にテストしてみよう。世の中には既定のスタイル設定を上書きしてくれるという便利な拡張機能 Stylus があるので,これを利用する。
詳しいインストール方法や細かい使い方などは参考文献1234を参照して欲しい。
今回,URL は https://qiita.com として,上書きするスタイルシートを以下のように設定した。自分の執筆する記事だけで良いのなら URL をさらに限定しても良いかもしれない。
.it-MdContent mjx-math,
.it-MdContent mjx-assistive-mml {
line-height: 0;
}
Stylus の設定画面を以下に示す。※この記事の読者層を考えて Microsoft Edge の画面にした。
これで冒頭に述べた3つの主要な不具合はほぼ改善される。なお,
インライン数式のベースラインが不揃いになる。
という課題については,この対策でも解決できない。
Microsoft Edge ユーザーの方へ,上記の拡張機能は chrome ウェブストアからインストールすること。Microsoft Edge アドオンサイトからだと同名の他機能がヒットするので要注意。※2025年10月現在
9. 対策の副作用
本対策を Qiita 運営へ提案する前に十分検証しなくては・・・と思っていたら,いきなり不具合が見つかった。それは英字の上端あるいは下端が消えてしまうという問題だ。通常であれば下記文字列は二行とも問題なく見えているはずだ。
$$
BCDEFGHIJKLMNOPQRSTUVWXYZ
$$
$$
BCDEFGHIJKLMNOPQRSTUVWXYZ
$$
上記の二行はいずれも一行の数式であり,内容は全く同じだ。
$$
BCDEFGHIJKLMNOPQRSTUVWXYZ
$$
参考までに Microsoft Edge のスクリーンショット画像を貼る。当たり前だが Stylus 拡張機能が無効のときは二行とも問題ない。
ところが Stylus 拡張機能を有効にすると,いずれか一行の英字の上端が消えてしまう。
もしかすると,これを嫌って Qiita のデザイナーは line-height: 0.2; という設定にしたのかもしれない。ということで,本対策はまだまだ不十分であると判断し,Qiita 運営への提案は暫く保留したい。