SVG図解の日本語を縦書きにする:writing-mode vertical-rl と縦組みで回る矢印

開発mdx-playground

図解の中に、幅が文字数個ぶんしかない縦長の帯がある。 表でいえば rowspan にあたるセルで、工程の段階をまとめて示す。 そこに入った「文書づくり」の5文字が横倒しのまま寝ていた。 読むたびに画面の前で首を左に傾けていた。

前からずっと気になっていた。 気になったまま何度も見送ってきた。 図を1枚描き直せば済む話に見えて、たぶん済まないとも思っていたからだ。

縦長のセルで日本語が横に寝ている

この帯が目に入ったのは、記事に図を3枚入れた直後だった。 第三者が書いた韓国語のガイドを日本語に訳し、未公開ディレクトリに置いて dev 環境で表示させていた記事だ。 図1の左右の余白が非対称だったので、カード幅を9カラムに広げて中央に揃えさせた。 3枚とも崩れずに描画されたところまでは順調だった。

そこで指示を出した。 縦の領域には、日本語を横倒しではなく縦に書いてほしい。 そして、原因が図解スキルの側にあるなら、そちらを直してほしいと付け足した。

図を直すか、スキルを直すか

済まないと思っていた理由は単純で、図は今後も量産されるからだ。 1枚直しても、次に同じ型を描かせればまた横倒しの日本語が出てくる。 直すべきは図ではなくスキルのほうだった。

rotate(-90) で横書きを倒すのをやめ、writing-mode="vertical-rl" を使う。 倒した文字は下から上へ読ませることになり、読み手に首を傾けさせる。 縦書きなら、上から下へ1文字ずつ並ぶ。

この指定は座標の意味も入れ替える。 text-anchor が縦方向(y)の揃えを、dominant-baseline が横方向(x)の揃えを担うようになる。 帯の中央に置きたければ、x に帯の中心線、y に帯の縦中央を渡す。

例外も決めた。 Q1main のようにラテン文字だけのラベルは、縦書きにするとかえって読みにくい。 そこだけ rotate(-90) を許す。 日本語が1文字でも入るなら縦書きにする。

この規約を svg-diagram スキル本体に書かせて、最終チェックリストにも1行足させた。

一括変換とサブエージェントの全件レビュー

スキルを直しても、すでにサイトに載っている図はそのままだ。 未対応で残っているものを機械的に検知できるなら、一括で変換したうえで全件レビューしてほしいと頼んだ。 レビュー役はサブエージェントでいい。 Sonnet 5 で足りるだろうと添えた。

まず既存 SVG の rotate の使われ方を分類させた。 テキストの中身、アンカー、回転中心の指定方法を見て、変換ルールを決める。 触らないものもここで決まった。 軸の数字のような英数だけのラベル、±90 以外の斜めの目盛、tspan を含むもの、そして図解カタログの Before スナップショットだ。 Before は当時の記録なので変換対象から外す。

変換器は dry-run を既定にして、--apply を付けたときだけ書き込む形にさせた。 対象は76本の SVG、86要素だった。 5つのバッチに割って、Sonnet のサブエージェント5体に投げた。

途中、パス変換スクリプトのバックスラッシュがシェル経由で壊れた。 ファイルに書き出してから実行させて回避した。 レビューを待つあいだに、回転方向が逆の rotate(90) の3要素、帯のラベル型、レール脇ラベル型を1枚ずつ描画で確かめさせた。 5体の報告文だけで完了にはしない。

レビューが拾った2件

batch-2 は16件すべて OK、batch-5 も12件すべて OK で返ってきた。 batch-1 は15件が OK で、1件だけ「要確認」が付いた。 年齢分布のチャートで、右軸のタイトルと注記が重なっているという。

変換前後を並べて見比べたら、その重なりは変換前の画像にも同じ位置に出ていた。 注記が右端で切れているのも変換前からだ。 今回の縦書き化が生んだものではない。 変換対象からは切り離して、既存の不具合として別に控えた。 レビュー結果をそのまま修正リストへ積んでいたら、無関係な直しを1件抱え込むところだった。

本物は batch-4 から出てきた。 ラベル末尾の矢印が、逆を向いている。

rotate(-90) で倒していたころ、「重要度 →」の矢印は回転の結果として上を向いて見えていた。 縦書きにすると、Unicode の縦組み規則で「→」は90度回って下を向く。 増える方向を示していた矢印が、そのまま減る方向に化ける。 12本で見つかった。

直すには矢印を「↑」に置き換えて正立で固定するのだが、text-orientation: upright だけでは足りなかった。 Meiryo や Yu Gothic のような日本語フォントは、縦組み用のグリフ置換で「↑」を「→」の形に差し替えてしまう。 font-feature-settings: 'vert' 0, 'vrt2' 0 でその置換を切って、ようやくフォントを問わず上を向く。 別の矢印記号に替えても回る側なので、記号の選び直しでは逃げられない。

怖いのは、この PC に Noto Sans JP が入っていることだ。 このフォントはグリフ置換をしないので、手元の描画では正しく見えてしまう。 手元で正しいことは、他の環境で正しいことの根拠にならない。

矢印を直したあと、ピラミッド、マトリクス、散布図の3種を開いて、「重要度 ↑」「売上 ↑」が上を向いたことを確かめた。

無関係なスキルを読み込ませているという指摘

この日の図は、もともと入っていなかった。

作業中に、見慣れない通知が出た。 Skill chain forces loading unrelated skills という製品フィードバックの下書きで、1 to review · 2 to send · 0 to dismiss と並んでいる。 何のメッセージか分からなかったので聞いた。 作業中に見つけた摩擦を Anthropic 向けの改善提案として下書きし、ローカルへ保留する機能だという。 送信には承認が要る。 頼んだ覚えはない。

中身は、コンテンツ管理のスキルが無関係なスキルまで読み込ませるという不満だった。 これは違う。 コンテンツを作るスキルがドキュメント作成のスキルを必須にし、そこから図解のスキルが読み込まれるのは、スキルをモジュール化しているからだ。 共通の作法を1箇所に置いて、上位のスキルから参照させている。 無関係な読み込みではなく、宣言してある依存関係だ。

そう伝えたら、チェーンの末端にあたる図解スキルを省いていたことが分かった。 省いた結果、記事には図が1枚も入っていなかった。 読み込ませたら図が3枚入り、その図の中で横倒しの日本語が見つかった。

学びメモ

  • 同じ型の成果物が量産される場面では、成果物ではなく生成のルールを直す。今回は既存76本の一括変換までを1セットにできた
  • 縦書きは rotate(-90) ではなく writing-mode="vertical-rl"。座標の意味が入れ替わり、text-anchor が y、dominant-baseline が x を担う
  • 縦組みでは記号が回る。矢印は font-feature-settings: 'vert' 0, 'vrt2' 0 まで指定しないと、フォントによって向きが変わる
  • 手元のフォント環境で正しく見えることは、他の環境で正しいことの根拠にならない
  • レビュー役の「要確認」は、変換前後を見比べてから判断する。今回の1件は変換前から存在した不具合だった

残していること

Vue コンポーネント側には、まだ rotate(-90 が10ファイルの15箇所残っている。 SVG ファイルだけを変換したので、こちらは手つかずだ。 コミットもまだしていない。

首を傾けずに読める図が、76本ぶん増えた。 残りはまた見送るかもしれない。 ただ今度は、直す場所が図ではないことが分かっている。