等寬文字行

海報風格的鎖定標誌會縮放每一行,使其橫跨容器的整個寬度,可打造出視覺效果驚人的標題。 使用 text-fit 時,瀏覽器會在版面配置期間縮放每一行,因此在調整大小、變更文字和載入字型時,都能保持正確。

如果單行文字填滿容器 (例如標題或引文),請直接對該元素套用 text-fit;本指南涵蓋多行鎖定。

基本導入

強制線:

  • 使用 display: block 將 <span> 中的每一行包裝起來。
  • 將 white-space: nowrap 套用至每個強制行 <span>。

導入指南:

  • 在標題本身使用 text-fit: grow per-line-all <percentage>。
  • 請使用 per-line-all,而非 per-line。per-line 會略過最後一行和任何以強制換行結尾的行,因此每行一個區塊時不會縮放任何內容。
  • grow 無法與 shrink 合併使用。請根據哪種方式更適合做為備用方案或更容易計算,將基本 font-size 設為最大值或最小值,並據此將 text-fit 設為 shrink 或 grow。如果沒有使用 shrink 的理由,建議使用 grow + 將 font-size 設為最小值,因為在大多數情況下,這樣做比較安全。
  • 請務必以百分比 (例如 300%) 做為限制,避免過度放大或縮小。
  • 容器必須有明確的內嵌大小。text-fit 絕不會變更固有大小,因此對 fit-content/max-content 寬度或內容大小的彈性/格線項目沒有任何作用。
  • 修剪第一行上方和最後一行下方的行距:請參閱「精確文字對齊」。
  • line-height 應保持在 1 以下,否則不同字型大小的行距會變得難以控制。
  • 重要事項:請注意,計算出的 font-size 不會變更,因此 em 間距不會縮放。
  • 為避免意外過度強調孤立字詞,請在有空行時套用 text-wrap: balance 或 text-wrap: pretty。

間距

如果強制所有行,則可進一步控制間距:

  • 將 text-box: trim-both cap alphabetic 套用至每個行區塊,讓方塊緊貼字元。並使用經過調整的指標進行修剪。詳情請參閱「精確文字對齊」。
  • 使用 Flexbox 或 Grid + gap 調整標題的行距,而非 line-height。這樣一來,間距就會保持一致,不會隨著每行的字型大小而增加。

如果有些行是空白的,請勿使用 text-box:這只會修剪第一行和最後一行,因此外側行會緊貼字元,而內側間隙則會保留行距。讓 line-height 管理所有間距。

視覺設計

  • 如果各行字型大小有明顯差異,鎖定標誌的效果最好。避免使用線條大致相同的文字建立鎖定組合,否則會導致字型大小不一,或差異不夠明顯,違反對比設計原則。如要確保這一點,可以強制使用短行:將要強調的字詞包在一個區塊中,其餘字詞則包在另一個區塊中,也就是混合鎖定。與 text-wrap: balance 搭配使用時,這點尤其重要。
  • 搭配西方文字使用時,建議與 text-transform: uppercase 結合,因為上緣和下緣往往會產生受限空間和/或重疊相鄰行,導致美觀度降低。
  • 強制使用短行時,請挑選強調後能強化訊息效果的字詞。
  • 限制:文字放大時,視覺權重也會增加,筆畫看起來會更粗。目前無法透過縮減較大文字的 font-weight 等方式來抵銷這項影響,因為沒有可用於計算的單位,且縮放不會影響 em 等字型相關單位。

備用策略

text-box 的登入憑證

text-box 不需要另外設定備用標誌:固定式標誌組合也請使用小型 line-height。在 text-box 下,這項屬性不會執行任何操作,並在不支援的情況下保持間距緊密,但代價是間距會隨著每行的字型大小而縮放。

text-wrap: balance 的登入憑證

text-fit 的登入憑證

強制換行時,系統會採用簡短、忠實的備用方案:以基本大小測量每行,並按照與容器的比例縮放,上限相同。

@supports not (text-fit: grow) {
  .lockup > span {
    /* Shrink-wrap each line so its width is the text width */
    width: fit-content;
    /* --text-fit-scale is measured by the script; the cap mirrors the text-fit percentage */
    font-size: min(var(--text-fit-scale, 1) * 1em, 300%);
  }
}
if (!CSS.supports('text-fit', 'grow per-line-all')) {
  const fit = lockup => {
    const lines = [...lockup.querySelectorAll(':scope > span')];
    // Unscale first: on a refit, offsetWidth would otherwise include the previous scale
    for (const line of lines) line.style.removeProperty('--text-fit-scale');
    // MANDATORY: all reads, then all writes. Interleaving them (e.g. reading
    // lockup.clientWidth inside the write loop) forces one reflow per line.
    const scales = lines.map(line => lockup.clientWidth / line.offsetWidth);
    lines.forEach((line, i) => line.style.setProperty('--text-fit-scale', scales[i]));
  };
  // Refit on resize and font load
  for (const lockup of document.querySelectorAll('.lockup')) {
    new ResizeObserver(() => fit(lockup)).observe(lockup);
  }
}

自由行沒有可靠的備用方案 (在版面配置前,換行符號未知):逐步增強至平衡的標題,並採用流動基本大小,例如 clamp(2rem, 1rem + 5cqi, 5rem)。在混合鎖定中,上述指令碼仍符合強制行;換行區塊的寬度與容器相同,因此會保持比例 1,且只會取得流體基本大小。

如果文字調整功能至關重要,建議使用 JS 程式庫 (例如 fitty),並設定條件,只有在不支援 text-fit 時才載入。