海報風格的鎖定標誌會縮放每一行,使其橫跨容器的整個寬度,可打造出視覺效果驚人的標題。
使用 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 時才載入。