【CSS】JSもclamp()も不要に。文字サイズをコンテナ幅にフィットさせるtext-fitプロパティの使い方


Webのレイアウト設計において、エリア内の文字を枠いっぱいに広げたり、スマホ画面で文字をはみ出させずに最大化したいケースはよくあります。
特に昨今のHTMLで構築するバナーデザインでも、コンテナいっぱいに文字を広げて、画面幅に合わせて配置したいという場面は少なくありません。

これまでは、JavaScriptで文字数を監視して動的にフォントサイズを書き換えるか、CSSのclamp()関数にvwやコンテナクエリ単位(cqw)を組み込んで細かく計算するしかありませんでした。しかし、clamp()を使った設計は文字数が増減したときの微調整が難しく、ブレイクポイントごとに記述が肥大化しがちという課題がありました。

そんな課題を解決する機能として、Chrome 150から本格導入されるのがtext-fitプロパティです。
text-fitを使えば、複雑な計算やJavaScriptを記述することなく、純粋なCSSだけでコンテナの幅に合わせたテキストの自動拡縮が実現できます。

今回は、実戦でそのまま使えるnoteのセール告知 のようなバナーデザインを例に、text-fitプロパティの具体的な使い方を、日本語環境で文字を綺麗に折り返すテクニックを合わせて解説していきます。

text-fitプロパティの実装サンプル


今回ご紹介するサンプルでは、各要素の横幅がお互いに影響し合わないよう、バッジ、タイトル、説明文を上から順に縦に積み重ねるシンプルなブロック配置を採用しました。

まずはHTMLから。

HTML

<div class="banner-wrap">
  <div class="banner-card">
    <div class="banner-badge">note期間限定セール</div>
    <h2 class="banner-title">
      <span class="segment">実践的なノウハウ記事が</span>
      <span class="segment">今だけ特別価格に。</span>
    </h2>
    <div class="banner-desc">スキルアップや日々の業務にすぐ活かせる確かな知見をお届けします。</div>
  </div>
</div>


まずはバナーの外枠となる.banner-cardです。各パーツが横に並んでしまうと文字サイズが意図しない挙動になるのを防ぐため、要素をそのまま縦に積み上げる構成にしています。

ここで押さえておきたいポイントは、見出し(h2)の中にあるspanタグ(.segment)です。
単語の区切りにスペースを挟まない日本語でこの自動拡縮(text-fit)を使うと、1文字単位でバラバラに変な位置で改行されてしまう問題が起こります。
そのため、あらかじめテキストを「実践的なノウハウ記事が」と「今だけ特別価格に。」という意味の区切り(文節)ごとにマークアップしておくことが、表示崩れを防ぎ、綺麗なレイアウトを保つための大きなポイントになります。

次にCSSです。

CSS

.banner-wrap {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  padding: 20px;
}

/* 1つにまとまったバナー本体 */
.banner-card {
  background: linear-gradient(135deg, #1e293b, #0f172a);
  color: white;
  padding: 32px; 
  border-radius: 12px;
  max-width: 800px;
  margin: 0 auto;
  
  /* 中のtext-fitが、このバナー自身の幅を基準に100%正しく計算できるようにする */
  container-type: inline-size; 
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}

/* 1段目:バッジ */
.banner-badge {
  display: inline-block;
  background: #ef4444; 
  color: white;
  padding: 6px 12px;
  font-weight: bold;
  font-size: 0.85rem;
  border-radius: 6px;
  margin-bottom: 20px;
  letter-spacing: 0.05em;
}

/* タイトル(text-fitを適用) */
.banner-title {
  margin: 0 0 20px 0;
  font-weight: 800;
  line-height: 1.3;
  font-size: 1.2rem; 
  
  /* バナーの幅(最大800px〜スマホ幅まで)に合わせて文字を最大化 */
  text-fit: grow consistent;
}

/* 単語の途中での不自然な改行を防ぐ */
.banner-title .segment {
  white-space: nowrap; 
}

/* 説明文 */
.banner-desc {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  opacity: 0.85;
  white-space: normal; 
  word-break: break-all;
}


まず、一番外側の.banner-wrapですが、ここにはバナー全体のベースとなるフォント(font-family)を指定して、中の要素に一括で適用させています。
次に、バナーの枠組みとなる.banner-cardcontainer-type: inline-size;を指定します。これで要素自体がコンテナクエリの基準点となり、内部のテキストがバナーの横幅を正しく参照して拡縮できるようになります。

メインとなる.banner-titleに指定したtext-fit: grow consistent;が、この実装のポイントです。

grow
テキストをコンテナの横幅いっぱいに収まるまで自動で拡大します。
consistent
画面幅が狭くなってテキストが複数行に折り返された際、すべての行のフォントサイズを同じ大きさに統一します。文字数の違いで、行ごとにサイズがバラバラになってしまうのを防ぐための指定です。


ただし、これだけでは日本語が変な位置で改行されてしまうため、先ほどHTML側で区切った.segmentに対してwhite-space: nowrap;を指定します。文節の途中での改行を禁止することで、ブラウザに「大きな2つの単語」として横幅を計算させることができます。

この2つの指定を組み合わせておくことで、PCなどの広い画面では1行に収まったままきれいに拡大し、スマホなどの狭い画面では、意図した文節の切れ目で2行に折り返されます。折り返された後も2つの行が同じサイズを保ったまま枠ぴったりにフィットするので、レスポンシブ対応が数行のCSSだけで綺麗に収まります。

text-fitプロパティの値のバリエーション

今回はgrow consistentを使用しましたが、text-fitプロパティには目的やデザインに応じて以下のような値が用意されています。

/* 基本的な値の例 */
text-fit: none;
text-fit: grow;
text-fit: shrink;
text-fit: grow consistent;


実務では、主に以下の2つのパターンを使い分けることになります。

  • 基本サイズより「大きく」して幅に合わせたい場合
    text-fit: grow consistent;
    (今回のサンプルのタイトルのようなケース)

  • 基本サイズより「小さく」して枠内に収めたい場合
    text-fit: shrink consistent;
    (長文の説明文を領域からはみ出させたくないケース)

デベロッパーツールでの検証

実装しました、レスポンシブWebデザインに適用させたHTMLで構築したバナーを、デベロッパーツールで確認してみます。

PCなどの広い画面幅では、設定したフォントサイズに応じてタイトルが大きく表示されます。

CSSのtext-fitプロパティを使った文字サイズの調整



タブレットやスマートフォンなど、画面幅が狭くなった際も、自動で最適なフォントサイズに調整されます。

CSSのtext-fitプロパティを使ったレスポンシブWebデザインでの文字サイズの自動調整

まとめ


これまでJavaScriptによるリサイズ監視や、clamp()関数を用いた細かい文字サイズ調整に追われていた手間が、この text-fitプロパティによってなくなります。

親要素にコンテナクエリの基準を明示し、日本語の文節をnowrapで保護してブラウザに計算させる設定を組み合わせれば、文字数の増減があってもレイアウトが崩れないバナーが作れます。今後のレスポンシブデザインの主要な選択肢になっていくと思います。