Skip to content

Instantly share code, notes, and snippets.

@tak-dcxi
Last active January 21, 2026 12:39
Show Gist options
  • Select an option

  • Save tak-dcxi/0f8b924d6dd81aaeb58dc2e287f2ab3a to your computer and use it in GitHub Desktop.

Select an option

Save tak-dcxi/0f8b924d6dd81aaeb58dc2e287f2ab3a to your computer and use it in GitHub Desktop.
タイポグラフィCSS

タイポグラフィ

font-family

  • フォントは Web サイトの印象に直結するため、一概にこれが良いとは言えない。
  • 特にこれと言った指定がされていない場合は font-family: sans-serif のみで良い。
    • Windows 11/10 では 2025 年のアップデートにより Noto Sans JP が標準搭載された。色々と問題があった游ゴシックの呪縛から解放されたのは大きい。
    • Android はメーカーにより削除されている可能性はあるが、そうでない場合は原則的に Noto Sans CJK JP が適用される。
    • Mac/iOS はヒラギノ角ゴ ProN が適用される。
    • アップデートによるフォントの変更の懸念はあるものの、ディスクリシアの方々は UD デジタル教科書体などの読みやすいフォントを設定している可能性があるため、アクセシビリティの観点では font-family: sans-serif の指定を推奨する。
  • 総称フォントには system-ui が存在するが、こちらは Windows 環境では Noto Sans JP ではなく游ゴシックのUI書体がこんにちはするので注意。日本語環境では避けたほうが無難である。
    • 海外製のUIフレームワークやリセットCSSでは system-ui が適用されている場合があるので注意する。特にTailwind CSSではフォントの設定を怠ると system-ui が適用されるので気をつけること。
  • デザインで Noto Sans JP が使用されている場合は、デバイスフォントを尊重して良いかデザイナーに確かめた方が良い。全デバイスで Noto Sans JP を表示するというオーダーを受けた際は Windows と Android で重複して読み込まれないように以下のように指定すること。(参考
/* @font-face を利用してローカルフォントを優先させる */
@font-face {
  font-family: 'Local Noto Sans JP';
  src: 
    local('Noto Sans JP'), /* Windows */
    local('Noto Sans CJK JP Regular') /* Android */;
}

:where(:root) {
  font-family: "Local Noto Sans JP", "Noto Sans JP", sans-serif;
}
  • 明朝体を使用する場合は Android 端末だとメーカー側で明朝体そのものが削除されている可能性があるため、Noto Serif JP などの Web フォントの利用を検討する。
    • なお、Noto Serif JP は Windows 11 には搭載されているものの、10 には搭載されていない。
    • Noto Serif JP も同様に @font-face を利用してローカルフォントを優先させるようにする。

汎用的な文章の折り返し指定

  • 下のような指定を:rootに指定しておく。
:where(:root) {
  overflow-wrap: anywhere; /* 収まらない場合に折り返す */
  /* word-break: initial; 単語の分割はデフォルトに依存(初期値のため指定しなくて良い) */
  line-break: strict; /* 禁則処理を厳格に適用 */
}

font-style

  • <em><cite> といった要素は通常イタリック体(斜体)で表示されるが、日本語において斜体は一般的に使用されない。
  • <em>は「強調」を意味するが、日本語における強調は多くの場合は太字で表現される。
  • 以下は kiso.css で定義している日本語向けのリセット。kiso.css を採用しない場合は以下のような指定を行うようにする。
:where(em:lang(ja)) {
  font-weight: bolder;
}

:where(:is(address, i, cite, em, dfn):lang(ja)) {
  font-style: unset;
}
  • デフォルトで斜体表示される要素のうち、数式やプログラムコード内の変数の名前を表す <var> は除外している。

font-weight

  • 原則的にはキーワードではなく数値指定を優先する。(参考
  • そのうえで --font-bold: 700 のようにデザイントークンとして変数化する。
  • kiso.css では UA スタイルシートと合わせるためにキーワードを使用しているため、適宜 base レイヤーで上書きを行う。

text-align

  • text-align: center を指定する場合は、後述する text-wrap: balance とセットで指定する。
  • text-align: justify はラインボックスの端に揃うように間隔が空けられるため段落を美しく表示するために便利であるように思えるが、英語などが含まれた際に一気に表示が崩れるリスクがあるため指定は原則禁止とする。
    • text-align: justify の揃え方を決定する text-justify が Chrome でサポートされるものの、先述した問題の解決策にはならない。
    • text-align-last: justify はテーブル見出しを均等割にする際に便利である(参考)。しかし、単一行であることが条件であるため、改行しないように工夫する必要がある。

text-wrap

  • テキストの行の折り返し方法を指定するプロパティ。
  • 基本的に明示する値はprettyorbalanceの 2 択で、balanceではすべての行が同じくらいの長さになるように調整されるのに対して、prettyは最後の行が一つの単語だけで終わることを防ぐ。
  • 英語では最後の行に一つだけ配置された単語を widows and orphans と呼び、テキストが読みにくくなるとして避けるべき対象とされている。そのため、:lang(en)の場合はテキスト全体にprettyを指定するのが良い。
  • 英語の見出しの場合はbalance を指定するのが美しいという見解もある。もっとも全ての英語の見出しが balance が最適とは限らないため、kiso.css では pretty とし、ベースで balance を指定する方針とする。
  • 日本語では本文はベタ組みにすることが原則であるため、段落にtext-wrapの措定は行わない。見出しの引き締めには貢献できるので、text-alignstartorendならprettycenterならbalanceを指定するのが良い。
  • しかし、現在の Safari では日本語におけるそれの挙動がバグっている(balance と似たような動作をする)ため、日本語の場合はtext-wrap: prettyの指定をしないほうが良い。Safari のバグが修正されたら指定することを推奨する。
:where(:is(h1, h2, h3, h4, h5, h6, caption):lang(en)) {
  text-wrap: balance;
}

:where(p:lang(en)) {
  text-wrap: pretty;
}

.-text-center {
  text-align: center;
  text-wrap: balance;
}
  • kiso.cssでは現在は英語のみ適用している。

font-feature-settings

  • font-feature-settings: 'palt' で文字詰めができるが、日本語では本文はベタ組みにすることが原則であるためデフォルトでは指定しない。
  • 見出しに関しては文字詰めを行ったほうが可読性が向上するので指定を行う。
:where(h1, h2, h3, h4, h5, h6, caption) {
  &:lang(ja) {
    font-feature-settings: "palt";
  }
}
  • 縦書きの場合は palt だと文章全体が横にズレてしまうので font-feature-settings: "vpal" とする。

font-variant

  • フォントの特定のスタイルや装飾を制御することができるプロパティ。
  • 原則的には font-feature-settingsの上位互換であるが、使用頻度の高い'palt', 'pkna'に代わる方法が提供されていないので使い所は限られる。
  • 文字を可変幅で表示する font-feature-settings: pwid を使用する場合は、上位互換となる font-variant-east-asian: proportional-width を指定するようにする。
  • 数字リストやローディング、料金テーブルのように数字を目立たせる箇所では font-variant-numeric: tabular-nums を指定して数字の幅を均等にするのに用いると良い。
.-tabular-nums {
  font-variant-numeric: tabular-nums;
}

font-kerning

  • プロポーショナルフォントの文字間隔を、隣り合う文字の組み合わせによって文字詰めすることを「カーニング」と呼ぶが、メトリクスカーニングと呼ばれる方法で、カーニングを制御するプロパティ。
  • 初期値は auto でブラウザ側に委ねられる。
  • 日本語の場合は本文はベタ組みにすることが原則であるため、font-kerning: normal の適用は可読性を悪化させる原因となる。基本的には見出しに利用し、デフォルトでは none にしておく。
  • 英語の場合はカーニングがあったほうが可読性は上げるため、デフォルトで normal を指定する。
:where(:lang(en)) {
  font-kerning: normal;
}

:where(:lang(ja)) {
  font-kerning: none;
}

:where(h1, h2, h3, h4, h5, h6, caption) {
  font-kerning: normal;
}
  • <hgroup> 内の <p> など、見出し以外にもカーニングさせたい要素がある場合に備えて font-feature-settings: 'palt' と併せてユーティリティクラスも用意しておくと良い。
.-kerning {
  font-kerning: normal;

  &:lang(ja) {
    font-feature-settings: "palt";
  }
}

text-autospace

  • 日本語のなどの文字と英数字の間にスペースを入れるかどうかを制御するプロパティ。
  • Web に関しては原則的に日本語のと英数字の間にスペースを入れるのが可読性が高くなるとされており、デフォルトで text-autospace: normal を指定するのが良い。
  • ただし、以下の要素についてはtext-autospace: no-autospace を明示する。
    • 等幅フォントでずれる可能性がある→ <pre>
    • 慣習的に日付表記には空白は入らないため、日付表記に用いられる要素→ <time>
    • ユーザーが入力する要素では自動挿入が入ると挙動不審になる恐れがある→ <input> <textarea> [contenteditable]
:where(:root) {
  text-autospace: normal;
}

:where(pre, time, input:not([type="button" i], [type="submit" i], [type="reset" i]), textarea, [contenteditable]) {
  text-autospace: no-autospace;
}
  • kiso.cssを採用している場合は定義されているので不要である。

text-spacing-trim

  • 約物(句読点や括弧など)とそれ以外の文字との間のスペースを制御するプロパティ。現状は Chrome のみ。
  • 通常の Web の文章は段落を字下げすることはなく、かつ見出しの最初の括弧の空白を消すほうが審美性に優れているためデフォルトではtext-spacing-trim: trim-startを指定する。
  • ただし、pre 要素はズレる可能性があるため、継承されないようにし、ズレないことを保証するために text-spacing-trim: space-all を明示する。
:where(:root) {
  text-spacing-trim: trim-start;
}

:where(pre) {
  text-spacing-trim: space-all;
}
  • kiso.cssを採用している場合は定義されているので不要である。

line-height

  • デフォルトの line-height はアクセシビリティの観点から 1.5 以上とする。
  • 原則的に単位なしの number として定義する。
  • その上で各見出し、段落において適切な line-height を適用する。デザインに従うが、大体次のような行間が読みやすい。(参考
    • 段落 → 和文は 1.7 〜 2、英文は 1.5 〜 1.8
    • 見出し → 和文は 1.25 〜 1.5、英文は 1.2 〜 1.4
  • line-height は命名を tight snug normal relaxed loose をベースにデザイントークンとして変数化する。
  • 改行した際に著しく表示を損ねることから line-height: 1 は指定を禁止とする( Stylelint で弾く)。デザイン上 1 とされている箇所は後述する text-box-trim / text-box-edge などを用いてハーフレディングを取り除く方針にする。

text-box-trim / text-box-edge

  • ハーフレディングを除去するプロパティ。
  • text-box-trim はテキストコンテンツの上端と下端のどちらを切り取るかを指定する。trim-start は上端、trim-end は下端、trim-both は両方。
    • ハーフレディングを除去する目的なら trim-both 、ハーフレディングの除去は無くていいけど横並びになった画像と見出しの上端を合わせたい…というケースでは trim-start を指定するケースが多い。
  • text-box-edge は切り取る空間の大きさを指定する。既定値は text で、上部を text-over baseline、下部を text-under baseline の位置でトリミングする。
    • 原則的には英文は cap alphabetic で上端を X の上限、下端を x の下限でトリミングするのが良い。しかし、日本語の場合は詰まりすぎる場合があるので既定値の text が良い。
    • 切り取る空間の大きさはフォントに依存する。フォントによっては text だと多少のアキが発生する。厳格に除去したいのであれば margin-block: calc((1em - 1lh) / 2) で詰めるのを推奨する。
  • 先述したように line-height: 1 の代替手段としても使用する。
  • Figma の場合はハーフレディング込みで余白が算出される(ハーフレディングを切り取る定義がされていない場合)が、 Adobe 製のツールはハーフレディングを含まずに余白が算出される。 Adobe 製のツールでカンプが作成されている場合は全称セレクタですべての要素に適用するのも選択肢。
  • 現状では Firefox のみ非サポートであるが、ハーフレディングの有無で表示が著しく変化する or 崩れるとかでなければプログレッシブ・エンハンスメントで対応する方針。
.-trim-both {
  text-box-trim: trim-both;
  
  &:lang(en) {
    text-box-edge: cap alphabetic;
  }
}

hanging-punctuation

  • インライン軸に句読点を包括できる余白が存在するのが条件だが、hanging-punctuation: last allow-end の指定で「段落最後の閉じカッコ」と「行末の句読点」がぶら下がるようにできる。現在は Safari のみだが、そのうち Chrome にも来る予定である。
  • 行頭や行末に位置する約物文字(句読点や括弧、引用符など)を行ボックスの外側にぶら下げることは、行揃えを改善するために有効である。印刷組版では、段落の開始引用符「“」や行末の句点「。」を行の外側に出して、他の行と揃えを取る手法が用いられている。
  • 原則的には段落でのみ適用する。
  • 注意点としては、約物を行外に出すことでコンテナの論理幅からはみ出すため、場合によっては水平スクロールバーが出ることがある。インライン軸に padding とセットで指定したほうがいい。
  • 英語の場合は hanging-punctuation: first allow-end last とする。(参考
.-hanging {
  hanging-punctuation: last allow-end;
  
  &:lang(en) {
    hanging-punctuation: first allow-end last;
  }
}

line-clamp

  • 指定した行数でテキストを省略するプロパティ。ベンダープレフィックスなしの line-clamp は全コアブラウザでサポートされていないため、 flex のベンダープレフィックス版の指定 display:-webkit-boxflex-direction に価する -webkit-box-orient と合わせてベンダープレフィックス付きで指定する必要がある。
  • そのままだと三点リーダー込でオーバーフローするので、overflow プロパティではみ出した部分を非表示にする必要がある。この際、overflow-y: clip とすること。 先述した hanging-punctuation と組み合わせた時に overflow: hidden だと約物が切り取られ、overflow-y: hidden だとX軸の overflowauto にマッピングされてスクロールバーが表示されるためである。
    • こういったイレギュラーを防ぐため、また Scroll-driven Animation や position: sticky の動きを阻害しないためにも基本的に overflow: hidden ではなく overflow: clip を優先して使用する。
    • grid-template 系のアニメーションのような overflow: hidden でないと動作しないものでのみ overflow: hidden は使用するようにする。
.-line-clamp {
  display: -webkit-box;
  overflow-block: clip;
  -webkit-box-orient: block-axis;
  -webkit-line-clamp: var(--line-clamp--limit, 3);

  @supports not (overflow-block: clip) {
    overflow-y: clip;
  }
}

文節区切りでの改行

  • 日本語の見出しは文節区切りでの改行と可読性がよくなる。
  • word-break: auto-phrase で文節区切りでの改行を行うことができるが、Chrome 系のみ。
  • Safari or Firefox で文節区切りでの改行を行う場合は BudouX を導入する。しかし、導入コストはあるため Chrome 系のみ文節区切りでの改行を行い、その他は従来のままというプログレッシブ・エンハンスメントの考えを持つのも選択肢。
  • 本文まで全て文節改行すると、段落内で妙な空行が生まれて可読性を悪化させるので避けること。見出し・キャプション・短文詩などは文節改行が望ましい典型例であり、そういった必要な箇所に限定して使うようにする。
  • word-break: auto-phrase を指定する場合は text-wrap: balance を指定すると、より美しい改行を行うことができる(参考)。
:where(h1, h2, h3, h4, h5, h6, caption) {
  &:lang(ja) {
    @supports (word-break: auto-phrase) {
      word-break: auto-phrase;
      text-wrap: balance;
    }
  }
}
  • ついでにカーニング同様ユーティリティクラスも別途用意しておく。
.-auto-phrase {
  &:lang(ja) {
    @supports (word-break: auto-phrase) {
      word-break: auto-phrase;
      text-wrap: balance;
    }
  }
}

hyphens

  • 長い単語がすべて次の行へ送られると、その前後の行で不自然なスペースが生じてしまう。単語の途中で改行しつつ、不自然にならない音節の位置にハイフンを挿入して改行する処理のことを「ハイフネーション」と呼ぶ。
  • hyphens: auto を指定することで、自動的に単語の途中でハイフネーションを行うことができる。
  • 注意点としては hyphens: auto は言語依存のため、 lang="en" 属性などを付与して現在の言語を明示化する必要がある。
<div class="-hyphens" lang="en">
  <p>You think that's where it's at</p>
  <p>But is that where it's supposed to be?</p>
  <p>You're getting it all over me, ex-rated</p>
</div>
.-hyphens {
  hyphens: auto;
}

text-transform

  • テキストを大文字表記にする text-transform: uppercase が最も使用される。
  • VoiceOver では略語として定義されているものはアルファベット毎に読み上げる(例:IT→「アイティー」、ADD→「エーディーディー」)ので、読み間違いを防ぐために依然としてこのテクニックは重要。
  • ただし、CSSで各セレクタ毎に指定すると「全て大文字」とそれ以外が入れ込んだ時に辛いのでユーティリティクラスとして定義しておく。
.-uppercase {
  text-transform: uppercase;
}

text-indent

  • テキストの字下げを行うためのプロパティ。とは言え、text-spacing-trim で触れたように通常の Web の文章は段落を字下げすることはない。
  • 多くの場合は注釈の「※」のように逆方向に字下げをする場合に用いる。
  • 従来は padding-inline-start: 1em; text-indent: -1em のように padding とセットで指定することが多かったが、現在は先頭行以外のすべての行を字下げする hanging キーワードが追加されているのでこちらを使用する。(参考
    • Safari, Firefox は既にサポートされ、Chrome は 146 でサポートされる予定である。字下げがなくても表示に影響は無いためプログレッシブ・エンハンスメントで良い。
  • 注釈をマークアップする際は note role を指定する。
    • 注釈リストをマークアップする場合は <ul> <li> の暗黙的なロールを消さないように <div> でラップしてそれに指定する。
    • 単一の注釈には <p> 要素に指定する。
<p role="note">単一の注釈として表示する際のサンプルです。実際の表示・挙動は環境や設定により異なる場合があります。あらかじめご了承ください。</p>

<div role="note">
  <ul>
    <li>リストとして表示する際のサンプルです。実際の表示・挙動は環境や設定により異なる場合があります。あらかじめご了承ください。</li>
    <li>記載の情報は作成時点のものです。最新の内容は公式情報をご確認ください。</li>
    <li>本資料に含まれる数値・表現は仮のものです。正式な値は確定次第更新されます。</li>
  </ul>
</div>
:where(p[role="note"], [role="note"] li) {
  text-indent: hanging 1em;

  &::before {
    content: "※";
  }
}

流体タイポグラフィ

  • font-size をメディアクエリ or コンテナクエリで切り分けるのは辛いので clamp() 関数を使って最小値〜最大値の範囲でフォントサイズを滑らかに変化させるようにする。
  • ただし、推奨値の計算を CSS のみで行うのも辛い。progress()関数もしくは CSS 標準の @function が広まればこのあたりは改善するが、現状では厳しい。
  • オンラインジェネレーターで算出するのは行き来するコストが掛かり、コメントを残さないと推奨値の計算手順が分からない、といった理由からなるべく避けたほうがいい。
  • Sass を使ってるなら自前の @function を作成するようにする。CSS 標準でやりたいならユーティリティクラスを作ってカスタムプロパティを受け渡しできるようにすると良い。
  • サイト全体の調和を優先するなら svi 、コンポーネント毎にスコープを切り分けるならコンテナクエリ + cqi のように相対先の単位を出し分けできるようにしておく。
.-fluid-text {
  --_u-relative-max-width: var(--fluid-text--relative-max-width, 1280);
  --_u-relative-min-width: var(--fluid-text--relative-min-width, 375);
  --_u-min-font-size: var(--fluid-text--min-font-size, 14);
  --_u-max-font-size: var(--fluid-text--max-font-size, 16);
  --_u-base-font-size: var(--fluid-text--base-font-size, 16);
  --_u-relative-unit: var(
    --fluid-text--relative-unit,
    100svi
  ); /* 100svi or 100cqi */

  /* slope and intercept calculations */
  --_u-fluid-slope: calc(
    (var(--_u-max-font-size) - var(--_u-min-font-size)) /
      (var(--_u-relative-max-width) - var(--_u-relative-min-width))
  );
  --_u-fluid-intercept: calc(
    var(--_u-min-font-size) - var(--_u-fluid-slope) *
      var(--_u-relative-min-width)
  );

  font-size: clamp(
    var(--_u-min-font-size) / var(--_u-base-font-size) * 1rem,
    var(--_u-fluid-slope) * var(--_u-relative-unit) + var(--_u-fluid-intercept) /
      var(--_u-base-font-size) * 1rem,
    var(--_u-max-font-size) / var(--_u-base-font-size) * 1rem
  );
}
  • コンポーネントのローカルカスタムプロパティや API 的カスタムプロパティとのバッティングを防ぐために、 API 的に受け取る用のカスタムプロパティと内部カスタムプロパティを分離しつつ、ローカルには _u-、API には fluid-text-- プレフィックスを付与する。
  • svivilvi でも問題ないケースが殆どだと思うが、 cqi は基準コンテナが見つからない場合に svi を返すという仕様があるため、それと合わせている。

px vs rem

  • 今までは rem 指定が有効に働くケースは Chrome の文字拡大機能(not ズーム機能)のみであり、「誰が使ってるか分からないマイナーな機能をサポートするコストが見合わない」「Chrome が px でも文字拡大できるようにすればいい」といった意見にも賛同できるところがあったため「どっちでもいい」というスタンスを取ってきたが、<meta name="text-scale" /> の登場によって考えが変わった。(参考
  • text-scale が有効な場合、OS の文字スケール設定とブラウザの文字スケール設定の両方に比例してフォントサイズが拡大・縮小される。すべてのデバイスにおいて OS レベルの文字スケール設定を尊重する簡単な方法は存在しないから、rem とか em 使って実装してるサイトであればそれを OS レベルの文字スケール設定として再定義すればいいのでは?って感じらしい。px では動作しないことが(Draft段階であるものの)仕様にも書かれている
  • 単純に「rem が常に正解」「px はダメ」というような二者択一ではない。全てを rem で定義するのはズーム機能との棲み分けができていない。
  • px or rem は「この値はユーザーがデフォルトフォントサイズを大きくしたとき、一緒に大きくなるべきか?」 を使い分けの基準にすべき。
    • テキストサイズ、段落の垂直マージン、行間などはフォントサイズと連動させた方がいいので rem などを使う。
    • 一部装飾的な境界線幅、細かいデザインディテールなどは文字拡大に伴って大きくなっても嬉しくないので px を使う。
    • テキストを含むコンテンツの幅やメディアクエリやコンテナクエリのブレイクポイントは文字拡大時に窮屈になったりレイアウトが崩壊する可能性があるから rem などを使った方がいい。
    • 水平方向の padding は文字サイズと連動すると一行あたりの文字数が減り、可読性を落とす可能性があるので px を検討する。
  • 重要なことはremを使う以上ブラウザの文字拡大機能を有効にして検証すべきだということ。アクセシビリティ重視のために rem を使いましょうと言いつつ、 :rootfont-size: 10px などの固定値を指定する実装をたまに見かけるが、文字拡大機能を有効にして検証すれば動いていないことは明白であるはず。「font-size には rem を使う」というルールが独り歩きして肝心なことを見落としている。

  • 余談であるが、メディアクエリやコンテナクエリのブレイクポイントには calc()max() などの数学関数が利用できる。
  • ブレイクポイントを remem に変換する場合は @media (width >= calc(768 / 16 * 1em)) のように指定したほうが計算機を使う手間が省け、元のピクセル値がわかりやすくなるメリットがあるので推奨する。
    • メディアクエリはビューポートを基準とするため、メディアクエリの中ではブラウザの文字サイズを基準に計算される。そのため、remem もフォントサイズの変更が行われていない場合は 16px 相当になる。
    • コンテナクエリの場合は container-type: inline-size を指定した要素のフォントサイズを基準として計算される。

手動改行

  • 各見出しにおいて手動で改行を行う場合は、ポエムなどの文章構造的に意味のある改行以外では <br> は使わずに CSS で改行制御を行う。
  • 原則的にはdisplay: inline flow-rootを指定した<span>要素か、親要素にdisplay: block flex + flex-wrap: wrapを指定するようにする。
  • 手動改行は多くのケースでは日本語都合であるため、:lang(ja) の時のみ上記のアプローチを行い、その他の言語では「<span>そのものを無いもの」とする方針にする。
  • 段落上の手動改行は word-break: auto-phrase 同様に可読性を悪化させる可能性があり、特にレスポンシブとの相性が悪いので避ける。デザイン上の改行が行われている場合はデザイナーに掛け合って「無理です」と伝えた方が良い。
.-br {
  display: contents;

  &:lang(ja) {
    display: block flow;
  }
}

.-wbr {
  display: contents;

  &:lang(ja) {
    display: inline flow-root;
  }
}

分離禁止の明示

文章によっては改行によって分割されると不都合なワードも存在する。

分割させたくない文字と文字の間には「&zwj;(zeroゼロ width幅 joiner接合子)」を挿入することで分離禁止な単語として明示することができる。

<p>marginが相&zwj;殺するのを防止するために、なるべくmarginの向きは上方向に統一しておきます。</p>

上記のケースでは「相殺する」の「殺」が頭に来ると不穏な言葉が生まれてしまうので、それを防止している。

結論

  • リセット CSS には kiso.css を採用しているのを前提とする。採用しない場合は kiso.css から該当箇所をコピペして base レイヤーに指定する。
  • base レイヤーには次のような記述を記述を行う。
:where(:lang(en)) {
  font-kerning: normal;
}

:where(:lang(ja)) {
  font-kerning: none;
}

:where(h1, h2, h3, h4, h5, h6, caption) {
  font-kerning: normal;
  
  &:lang(en) {
    text-wrap: balance;
  }

  &:lang(ja) {
    font-feature-settings: "palt";
    
    @supports (word-break: auto-phrase) {
      word-break: auto-phrase;
      text-wrap: balance;
    }
  }
}
  • reset レイヤーには次のような記述を行う。
.-fluid-text {
  --_u-relative-max-width: var(--fluid-text--relative-max-width, 1280);
  --_u-relative-min-width: var(--fluid-text--relative-min-width, 375);
  --_u-min-font-size: var(--fluid-text--min-font-size, 14);
  --_u-max-font-size: var(--fluid-text--max-font-size, 16);
  --_u-base-font-size: var(--fluid-text--base-font-size, 16);
  --_u-relative-unit: var(
    --fluid-text--relative-unit,
    100svi
  ); /* 100svi or 100cqi */

  /* slope and intercept calculations */
  --_u-fluid-slope: calc(
    (var(--_u-max-font-size) - var(--_u-min-font-size)) /
      (var(--_u-relative-max-width) - var(--_u-relative-min-width))
  );
  --_u-fluid-intercept: calc(
    var(--_u-min-font-size) - var(--_u-fluid-slope) *
      var(--_u-relative-min-width)
  );

  font-size: clamp(
    var(--_u-min-font-size) / var(--_u-base-font-size) * 1rem,
    var(--_u-fluid-slope) * var(--_u-relative-unit) + var(--_u-fluid-intercept) /
      var(--_u-base-font-size) * 1rem,
    var(--_u-max-font-size) / var(--_u-base-font-size) * 1rem
  );
}

.-text-center {
  text-align: center;
  text-wrap: balance;
}

.-trim-both {
  text-box-trim: trim-both;

  &:lang(en) {
    text-box-edge: cap alphabetic;
  }
}

.-kerning {
  font-kerning: normal;

  &:lang(ja) {
    font-feature-settings: "palt";
  }
}

.-auto-phrase {
  &:lang(ja) {
    @supports (word-break: auto-phrase) {
      word-break: auto-phrase;
      text-wrap: balance;
    }
  }
}

.-hanging {
  hanging-punctuation: last allow-end;

  &:lang(en) {
    hanging-punctuation: first allow-end last;
  }
}

.-uppercase {
  text-transform: uppercase;
}

.-hyphens {
  hyphens: auto;
}

.-line-clamp {
  display: -webkit-box;
  overflow-block: clip;
  -webkit-box-orient: block-axis;
  -webkit-line-clamp: var(--line-clamp--limit, 3);

  @supports not (overflow-block: clip) {
    overflow-y: clip;
  }
}

.-br {
  display: contents;

  &:lang(ja) {
    display: block flow;
  }
}

.-wbr {
  display: contents;

  &:lang(ja) {
    display: inline flow-root;
  }
}
@ln-north
Copy link

人の gist に唐突に横槍すみません、間違いの指摘ではありませんが、状況を共有したく… 🙇

https://gist.github.com/tak-dcxi/0f8b924d6dd81aaeb58dc2e287f2ab3a#font-feature-settings

詰め組みは palt だけでなく kern を併用して実現することがほとんどです。
font-kerning: normalkern 相当の意図かと思われますが、Firefox では日本語(CJK)では kern 無効です。
font-kerning: normal で日本語に kern だけが効くこと自体があまり意図しない挙動です)
また、縦組みでは vpal, vkrn になることに注意が必要です。

余談ですが、まだ使われていませんが、OpenType 1.9.1apkn, vapk が 入りました。

縦組みや apkn などの経緯により、詰め組みは将来的(遠い先の話)に font-feature-settings ではなく、 font-kerning で解決するほうが望ましいように思います。

https://gist.github.com/tak-dcxi/0f8b924d6dd81aaeb58dc2e287f2ab3a#font-kerning

釈迦に説法かと思いますが、 lang="ja"font-kerning: none にすると、文字種によらず Kerning が OFF になり、欧文中のカーニングも効きません。 en 環境下で normal を利用するのは良いともいますが、 ja 環境下で none | normal どちらにするかは、日本語のベタを優先するか、欧文の kerning を優先するかになるかのトレードオフになるのが現状かと思います。

ちなみに、今のところ、詰め組みは font-kerning: always / all のようなものが議論されており、

  • font-kerning: normal => 日本語ベタ・欧文詰め(kern)
  • font-kerning: always / all => 日本語詰め (palt + kern)・欧文詰め(kern)

のように整理されるかもしれません。(だいぶ先だと思いますが…)

重箱の隅かつ指摘でもなく恐縮ですが、共有まで。

@tak-dcxi
Copy link
Author

@ln-north
ご共有ありがとうございます🙇‍♂️

縦組みや apkn などの経緯により、詰め組みは将来的(遠い先の話)に font-feature-settings ではなく、 font-kerning で解決するほうが望ましいように思います。

こちらについては初耳でしたので大変参考になりました。

釈迦に説法かと思いますが、 lang="ja" で font-kerning: none にすると、文字種によらず Kerning が OFF になり、欧文中のカーニングも効きません。 en 環境下で normal を利用するのは良いともいますが、 ja 環境下で none | normal どちらにするかは、日本語のベタを優先するか、欧文の kerning を優先するかになるかのトレードオフになるのが現状かと思います。

こちらについては色々と迷いましたが、一旦は以下のように変更いたしました。

:where(:lang(en)) {
  font-kerning: normal;
}

:where(:lang(ja)) {
  font-kerning: none;
}

その上で日本語内の英語表記などについては妥協する(日本語の表示を優先する)のがベターかなと思います。

font-kerning: normal => 日本語ベタ・欧文詰め(kern)
font-kerning: always / all => 日本語詰め (palt + kern)・欧文詰め(kern)

この指定が早く来ることを望みます。

この度はコメントありがとうございました🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment