| name | typography |
| description | CSS/HTMLタイポグラフィを設計・実装・監査する。日本語、英語、日英混植、script/word run単位の文字組み、フォント、行長、行間、約物、禁則、改行、縦書き、fluid/responsive typography、ズーム・リフロー、アクセシビリティ、ブラウザ検証を扱う。タイポグラフィ、文字組み、混植、run、lang、フォント、行間、字間、palt、カーニング、text-wrap、text-autospace、text-spacing-trim、word-break、overflow-wrap、line-break、hyphens、line-clamp、rem、clamp、writing-mode、縦中横、またはmockup-to-codeで文字の再現・検証が必要なときに使用する。 |
Typography
可読性、意味、レスポンシブ耐性を守ったうえで、文字の見た目を詰める。単一のスクリーンショット一致では完了としない。
成果物
タスクに応じて次を作る。
- 意味を保つHTMLと、言語が切り替わる箇所の
lang
- 本文・見出し・UI・数値・縦書きごとのtype token
- 混植見出しのrun ledgerとrun別CSS
- 320 CSS pxから広幅まで破綻しないresponsive rules
- a11yと対象ブラウザの検証記録
- 独立レビューに渡せる
typography-report/v1形式のJSON証跡
詳細が必要なときだけ次を読む。
実行フロー
1. 入力と制約を確定する
次を確認する。不明な値は推測と明記し、ブランドフォントや対応ブラウザを勝手に確定しない。
- 原文、主言語、混在言語、翻訳の有無
- 本文、display heading、UI label、数値、code、縦書きの役割
- 使用可能なfont family/weight、variable axes、webfont予算
- 対象viewport、container、ブラウザ、OS、最小文字サイズ
- mockup、既存DOM/CSS、design token、a11y基準
原文を先に確定する。画像から転記する場合は拡大して文字単位で照合し、似た字、全角/半角、句読点、空白、大小文字を確認する。
2. 意味とDOMを先に作る
- 文書の主言語を
<html lang="ja">などで宣言する。
- 発音規則が変わる自然言語のrunに
langを付ける。ブランド名や一般化した借用語まで機械的に分割しない。
- 見出し、段落、リンク、buttonなど正しい要素を使う。構造的な文字を画像、SVG path、
canvas、疑似要素だけで置換しない。
- 視覚調整用
spanはDOM順と読み上げ順を変えない。CSS order、absolute positioning、文字単位の分解で文章を並べ替えない。
- 文字単位spanは最後の手段とする。ligature、選択、検索、翻訳、読み上げ、改行を壊す場合は不採用とする。
- 装飾文字を支援技術から隠す場合だけ
aria-hidden="true"を使い、同じ文言の可視/不可視な重複読みを作らない。
<h1 class="hero-title">
<span class="type-run type-run--latin" lang="en">AI</span><span class="type-run type-run--ja">駆動型へ</span>
</h1>
DOM中の不要なASCII空白は、混植間隔の見た目を偶然変える。空白を内容として入れるか、CSSのtext-autospaceに委ねるかを決め、両方を重ねない。
3. type systemを決める
本文を先に決め、displayを派生させる。
font-size: 原則rem。コンポーネント内部の比例値はemも使う。
line-height: 単位なしを基本とし、フォント実測と行長で調整する。本文の出発点は日本語1.6–1.8、英語1.45–1.7とし、固定規則にはしない。
- 行長: 英文本文はおおむね
45–80ch、日本語本文はおおむね25–40emを出発点に、内容・書体・viewportで検証する。
letter-spacing: 本文は原則normal。all capsやdisplayだけ、実レンダーを見て調整する。
- weight: 実在するweightだけ指定する。synthetic bold/italicが不適切なら無効化し、fallback時も確認する。
- 数値: 表や桁比較には
font-variant-numeric: tabular-numsを検討し、本文中はproportionalを優先する。
日本語本文はベタ組みを基準にし、paltなどのプロポーショナルメトリクスを全本文へ一括適用しない。見出しは書体が対応するときだけ比較採用する。英語はkerningを有効にし、hyphenationには正しいlangが必要である。
4. 混植をrun単位で設計する
display textやmockup再現では、行を次のrunに分解する。
- Latin word/acronym
- kanji
- kana/particle
- numeral
- unit/symbol
- 意図的に強調された語
各runについて次のledgerを作る。本文の通常混植では、差が見えない限り分割を増やさない。
| field | 内容 |
|---|
text / lang | 文字列と言語 |
runId | 状態をまたいで同一runを照合する安定ID |
role | latin、kanji、kana、numeral、unit等 |
family / weight | 実際に読み込まれるface |
opticalHeightRatio | anchor runの可視glyph高を1とした比率 |
baselineOffset | baselineの視覚補正。通常em |
tracking | run固有の字間 |
wrapPolicy | wrap可否と許可位置 |
evidence | mockup cropまたはrender crop |
同じfont-sizeでも、Latin capitals、数字、かな、漢字は同じ大きさに見えない。CSS boxだけでなく可視glyph bboxを比較し、anchor(通常は主要漢字run)に対する比率で調整する。font-size、weight、letter-spacing、baselineの順に一度に一要因ずつ直す。
「行」と「run」を混同しない
expectedVisualLineCountは、読者に見える行ボックス数である。直下span数、Range.getClientRects()の生数、アイコン、矢印、装飾は数えない。
expectedRunCountは、run ledgerに記録した光学・言語run数である。1行内に複数runがあっても行数は1のままとする。
- 行数はテキストノードのrectを行のblock-startまたはbaselineの近接でcluster化して数える。同じ行の複数runは1 clusterである。
- 検査対象外の装飾には
data-typography-ignore="true"を付け、reportの除外理由に記録する。
- 旧
expectedLineCountへrun数を代入してpassにしない。旧fieldしか受け取れない連携先ではneeds_workとし、両数と移行gapを明記する。
- 原画像側の行数は
sourceReference.expectedVisualLineCount、実装側はstate別expectedVisualLineCountとして分ける。原画像のcropを見て数え、manifestに同じ値がある場合は一致させる。
span、<br>、run ledgerの数から視覚行数を作らない。block化したspanが改行を強制していても、ブラウザで同じblock-start/baselineに属する文字片をcluster化した実測値を記録する。
.hero-title { line-height: 1.04; }
.type-run { display: inline; }
.type-run--latin {
font-family: var(--font-latin-display);
font-size: 0.92em;
letter-spacing: -0.02em;
position: relative;
inset-block-start: -0.015em;
}
runをinline-blockにすると自然な改行やspacing overrideを妨げることがある。分離禁止が意味上必要な短いrunにだけ使い、320pxとtext-spacing overrideで再検証する。
5. 改行と先進CSSを段階適用する
まず安全なfallbackを置き、対応時だけenhancementを重ねる。
:where(:root) {
line-break: strict;
overflow-wrap: anywhere;
}
:where(:lang(en)) { font-kerning: normal; }
@supports (text-autospace: normal) {
:where(:root) { text-autospace: normal; }
:where(pre, code, time, input, textarea, [contenteditable]) {
text-autospace: no-autospace;
}
}
@supports (text-spacing-trim: trim-start) {
:where(:lang(ja)) { text-spacing-trim: trim-start; }
}
word-break: auto-phrase、text-spacing-trim、text-box-*、hanging-punctuationは対象ブラウザで実測し、未対応でも意味と可読性が保たれる構成にする。
- 日本語本文へ
word-break: break-allを適用しない。
- 長いURL/識別子には局所的に
overflow-wrapを使う。英単語を無条件に途中分割しない。
text-wrap: balanceは短い見出しに限定する。本文や行数の多い領域に使わない。
<br>は編集上必要な改行だけに使う。特定viewportの見た目だけを固定する改行はresponsive条件で解放する。
- line clampで重要情報を隠さない。省略するなら全内容へ到達できる操作を用意する。
日本語の短い見出しは、幅ごとに「改行したか」だけでなく「どこで改行したか」を検査する。原文から次を作る。
preferredBreakOffsets: 文節・意味の切れ目として優先する文字offset
forbiddenBreakOffsets: 送り仮名、活用語尾、助詞、単位、固有名詞を不自然に分断するoffset
orphanFragments: 1〜2文字の活用語尾や助詞だけの視覚行
例えば... 抱えなくていい ...を...抱えなく / くて / いい...としない。スクリーンショットだけでなく、各行の実文字列と、禁止offsetに実際の改行が当たっていないことをJSONに残す。auto-phraseは補助であり、対象ブラウザの実測に代えない。
6. responsiveに実装する
.prose {
font-size: clamp(1rem, 0.94rem + 0.25vi, 1.125rem);
line-height: 1.7;
max-inline-size: 38em;
}
.display {
font-size: clamp(2rem, 1.35rem + 3.2vi, 5rem);
line-height: 1.08;
overflow-wrap: normal;
}
- min/max値は
remでユーザー設定を尊重する。viewport単位だけで文字サイズを決めない。
- breakpointは端末名でなく、実際にwrap、衝突、過長行が起きる幅から決める。
- text containerに固定heightを置かない。
min-block-sizeも内容拡張を妨げないか確認する。
- 物理方向の余白より論理プロパティを優先する。
- 縦書きは読み方向とscroll方向を明記し、横書きへ切り替える場合も内容・順序・機能を保つ。
- webfont未読込、fallback、遅延swap、最長翻訳、200%文字拡大でも再flowさせる。
Accessibility gate
次を満たさなければ完了としない。
- 主言語と、発音が変わる異言語runに妥当な
langがある。
- 文字は選択・検索・コピーでき、DOM順と読み上げ順が自然である。
- 200% text resizeで内容や操作が失われない。
- 横書きは320 CSS px相当で、例外を除き二方向scrollを要求しない。縦書きは256 CSS px相当のblock方向条件も確認する。
- user stylesheetで
line-height: 1.5、letter-spacing: .12em、word-spacing: .16em、段落後2emにしても、clip、overlap、欠落、操作不能がない。
- 色、focus、link識別、画像化された文字、motion等、文字以外の該当WCAG項目も別途確認する。
WCAG 1.4.12は、作者が常に上記spacing値を指定する規則ではない。利用者がその値へ上書きしても壊れないことを検証する。
検証手順
実装後、対象ページの実URLで次を実行し、viewport・browser・結果・証跡pathを記録する。
- 静的検査:
lang、画像化テキスト、固定height、pxだけのfont-size、過剰なwhite-space: nowrap、line clamp、absolute配置された文章を検索する。
- 内容検査: 原文とrenderを文字単位で照合する。日英境界、空白、約物、数字、単位、改行を確認する。
- run検査: display混植をcropし、anchorとのglyph ink高比、baseline差、tracking、wrapをdesktop/mobileで比較する。各runに
runId、実測px、anchor比、差分px/em、測定方法、crop pathを残す。DOMRectだけをglyph ink bboxと呼ばない。
- 幅検査: 最低
320, 390, 768, 1024, 1440 CSS pxを確認する。各幅でhorizontal overflow、孤立語、禁則、衝突、clipを確認する。
- 拡大検査: 200% text resizeと400% browser zoom相当を確認する。text resizeはviewportズームの代用ではなく、著者のfont-sizeを2倍相当にした状態として別記録する。
- spacing検査: 次のoverrideをDevTools、user stylesheet、または既存a11y test helperで注入する。
- font検査: webfont成功、network block時のfallback、遅延swapの3状態を確認する。
- 支援技術検査: accessibility treeまたはscreen readerでheading、link/button名、言語切替、読み順、重複読みを確認する。
- 互換性検査: 対象ブラウザで
CSS.supports()とcomputed styleを確認し、先進CSSのfallbackを比較する。
*:not(svg *) {
line-height: 1.5 !important;
letter-spacing: 0.12em !important;
word-spacing: 0.16em !important;
}
p { margin-block-end: 2em !important; }
自動テスト基盤がある場合は既存のPlaywright/Cypress等へ幅、overflow、clip、computed styleのassertionを追加する。基盤がなければ新しいrunnerを無断導入せず、ブラウザのスクリーンショットと検査表を残す。
最小stress matrixと効率化
ページの再起動や全幅sweepを状態ごとに繰り返さない。既存のブラウザセッション1つで、少なくとも次の3状態を順に収集する。各状態は独立したassertionと証跡pathを持つ。
base-320: 320 CSS px、通常スタイル
spacing-320: 同じページにWCAG 1.4.12 spacing overrideを注入
resize-200-320: spacing overrideを除去し、テキストのみ200%相当にする
その後に必要な通常幅とdesktop/mobile cropを収集する。上記3状態のどれかが失敗したら、その状態だけに対象幅を広げる。これにより320、spacing、resizeの実行漏れを防ぎつつ、同一ページの不要な再起動を避ける。
typography-report/v1
templates/typography-report.min.jsonを複製し、schemas/typography-report.schema.jsonで検査する。reportは実装者の作文でpassにせず、次を再計算できる値とpathで渡す。
テンプレートは意図的にblockedかつ未計測である。例示値を実測値として流用しない。各stateのraw browser measurementを別JSONへ保存し、SHA-256とスクリーンショットをreportへ記録する。
- targetごとの
expectedVisualLineCountとexpectedRunCount
- 各状態の実測行cluster、cluster方法と許容px、行の文字列、run rect、glyph ink高比、baseline差
preferredBreakOffsets、forbiddenBreakOffsetsと、禁止改行・孤立断片の実測結果
- 320/spacing/200% resizeの独立state、注入CSS/JS path、text scale、screenshot path
- report生成者とreviewerの関係、参照したcrop/JSON、残存gap
schema検査だけで完了にしない。workspace root、必要ならmanifestとHTMLも渡し、意味検証CLIを実行する。通常実行はpending_reviewも非0終了にする。実装途中の正直なdraftだけ--allow-nonpassを使う。
SKILL_ROOT="${SKILL_ROOT:-${CODEX_HOME:-$HOME/.codex}/skills}"
"$SKILL_ROOT/mockup-to-code/scripts/skill-pack-python" \
"$SKILL_ROOT/typography/scripts/validate_typography_report.py" \
--report work/<page>/reports/typography-report.json \
--workspace-root work/<page> \
--manifest work/<page>/manifest.json \
--html work/<page>/site/index.html
実装者が作成したreportはpending_reviewまでとする。passには、別セッションまたは別reviewerが原画像、render、生JSONを開き、relationToProducer: independentでreviewを記録する。独立レビューを行わない案件は、機械gateと実装者自己評価を別々に返す。
合否表
| 領域 | pass | stop |
|---|
| content | 原文・意味・DOM順が一致 | 誤字、欠落、画像化された構造文字 |
| runs | glyph比とbaselineが意図に一致し、行数とrun数が別記録 | 一律sizeで混植階層が消失、読み順破壊、run数を行数に代入 |
| responsive | 全幅で自然にreflow | clip、overlap、不要な二方向scroll |
| a11y | resize/spacing/ATで内容と機能を維持 | spacing overrideで欠落または操作不能 |
| compatibility | fallbackを対象ブラウザで確認 | 未対応機能だけに意味・可読性を依存 |
mockup-to-code 接続契約
このskillはmockup-to-codeのタイポグラフィ専門工程として使う。mockup側のmanifest、asset、layout、completion gateを置換しない。
| 接続phase | input | このskillの処理 | output |
|---|
| Phase 1 hypotheses | 正規化comp、原文、ownership/anchor ledger、対象font | 行とscript/word runを分解し、文字を照合し、type bake-offを行う | type spec、run ledger、copy proof、font候補とfallback |
| Phase 2 measure | comp crop、line/run仮bbox | line bbox、可視glyph bbox、anchor比、baseline差を測る | measurement refs、opticalHeightRatio、offset |
| Phase 3 manifest | 確定copyと測定値 | 構造文字をHTML要素として登録し、run要素をfv/section-criticalへ紐付ける | manifest element/run fieldsとqa priority |
| Phase 5/5.5 implement+tune | type spec、実font、responsive container | semantic DOMとrun CSSを実装し、見た目→geometryの順で調整する | HTML/CSS、desktop/mobile crop |
| Phase 6–8 QA | render、rects、comp/build crop | bboxだけでなくcopy、glyph比、baseline、wrap、fallbackを検証する | crop pairs、差分、未解決gap |
| Phase 9 responsive | widths sweep、最長copy | resize、spacing override、font failure、各幅を検証する | responsive/a11y matrix |
| Phase 9.5/10 completion | 全証跡 | typography stop条件をartifact/completion判断へ渡す | pass/needs_work/blockedと根拠 |
優先順位
衝突時は次の順に守る。
- 原文・意味・HTML semantics・読み上げ順
- a11y、reflow、内容到達性
- mockupの情報階層、run間の光学比、fontの性格
- line break、bbox、baseline、trackingの再現
- 実験的CSSや微細な約物処理
低順位の見た目を守るために高順位を壊さない。fontが入手不能なら無断で近似完了とせず、fallback差と残差を記録する。
停止条件
次の場合は該当phaseを先へ進めない。
- Phase 1: 原文、主言語、構造文字/装飾文字の区別、critical textが確定していない。
- Phase 3: fv/section-critical混植にrun ledgerまたは測定根拠がない。
- Phase 5: 必要fontの利用権・file・weightが不明、または構造文字を画像としてしか再現できない。
- Phase 6以降: 誤字、DOM/読み順破壊、critical textのclip/overlap、font load failure未確認がある。
- Phase 9.5/10:
typography-report/v1に320px、spacing override、200% resize、対象ブラウザ、desktop/mobile cropの独立state証跡がない。
- Phase 9.5/10: critical textに禁止改行、孤立した活用語尾/助詞、または視覚行数とrun数の混同が残る。
- Phase 9.5/10: 混植critical textにglyph ink高比、baseline差、crop pathのいずれかがない。
停止時はblockedまたはneeds_workを明記し、欠けたinput、試したこと、次に必要な判断を返す。mockupのbox diffがpassでも、この契約のstop条件を満たす限りcompleteと報告しない。