使用 CSS Clamp 的現代流體排版

已發表: 2022-03-10
快速總結↬在本文中,我們將探討流暢的排版原則、用例、最佳實踐、使用 CSS 箝位函數的實現以及如何計算正確的參數。 我們還將學習如何解決一些可訪問性問題,並註意一個我們目前無法解決但無論如何都必須注意的重要問題。

Web 開發中流體排版的概念已經存在多年,開發人員不得不依靠各種變通方法使其在瀏覽器中工作。 借助新的 CSS clamp功能,創建流暢的排版從未如此簡單。

通常,當我們實現響應式排版時,值會在特定斷點處發生變化。 它們是明確定義的。 因此設計人員經常為兩個、三個甚至更多的屏幕尺寸提供排版值(字體大小、行高、字母間距等),而開發人員通常通過添加媒體查詢來針對特定斷點來實現這些要求。

該圖顯示了視口寬度和具有媒體查詢斷點的不同印刷值之間的依賴關係。
(大預覽)

儘管排版元素可能看起來和設計上的一樣好,但對於接近斷點的視口寬度上的某些元素來說,情況可能並非如此。 正如我們已經知道的那樣,除了設計中提到的之外,還有許多不同的設備和屏幕尺寸可供用戶使用。 在中間添加更多斷點和样式覆蓋可能會解決問題,但我們可能會增加代碼的複雜性,創建更多的邊緣情況,並使代碼不太清晰和可維護。

在具有不同屏幕尺寸的三種不同設備上的排版示例,即:手機、平板電腦和筆記本電腦。
儘管在較低和較高視口寬度上的排版看起來不錯,但由於固定的排版值和較少的空白,斷點(中心圖像)附近的標題大小看起來不合適。 (大預覽)

流體排版根據視口寬度在最小值和最大值之間平滑縮放。 它通常以最小值開始並保持恆定值,直到它開始增加的特定屏幕寬度點。 一旦它在另一個屏幕寬度處達到最大值,它就會從那裡保持該最大值。 我們將在本文中看到,流暢的排版也可以以相反的順序流動——從最大值開始,以最小值結束。

該圖顯示了視口寬度與具有最小值終點和最大值起點的不同印刷值之間的依賴性。
(大預覽)

這種方法減少或消除了對特定斷點和其他邊緣情況的微調。 雖然它主要用於排版,但這種流體大小的方法也適用於邊距、填充、間隙等。

請注意在以下示例中,標題文本如何平滑縮放,以及它在任何視口寬度上的外觀如何。 另外,請注意內容如何仍然保留響應式排版,並且值僅在斷點處更改。

標題隨著視口寬度平滑縮放,並且我們沒有像前面示例中那樣在斷點周圍的大小不一致。
標題隨著視口寬度平滑縮放,並且我們沒有像前面示例中那樣在斷點周圍出現大小不一致的情況。

儘管流體排版解決了上述問題,但它並非適用於所有場景,也不應將流體排版視為響應式排版的替代品。 每個都有自己的一組最佳實踐和適當的用例,我們將在本文後面介紹這些內容。

在本文中,我們將深入探討流體排版,並查看開發人員過去使用的各種方法。 我們還將介紹 CSS clamp函數以及它如何簡化流體排版實現,我們將學習如何微調clamp函數參數以控制流體行為的起點和終點。 我們還將討論可訪問性問題,其中大部分問題今天都可以解決,以及一個我們目前還無法解決的重要可訪問性問題。

跳躍後更多! 繼續往下看↓

流體排版的首次嘗試

作為開發人員,我們經常使用 JavaScript 來補充缺少的 CSS 功能,直到它們在主流瀏覽器中得到開發和支持。 在響應式網頁設計的早期,像 FlowType.JS 這樣的 JavaScript 庫已被用於實現流暢的排版。

流體排版的第一個真正的 CSS 實現伴隨著 CSS calc和 viewport 單位( vwvh )的引入。

 /* Fixed minimum value below the minimum breakpoint */ .fluid { font-size: 32px; } /* Fluid value from 568px to 768px viewport width */ @media screen and (min-width: 568px) { .fluid { font-size: calc(32px + 16 * ((100vw - 568px) / (768 - 568)); } } /* Fixed maximum value above the maximum breakpoint */ @media screen and (min-width: 768px) { .fluid { font-size: 48px; } }

這個片段看起來有點複雜,計算中涉及到很多數字。 因此,讓我們將其分解為多個部分,並對正在發生的事情進行高級概述。 讓我們關注選擇器和媒體查詢,看看它們涵蓋的情況。

 .fluid { /* Min value */ } @media screen and (min-width: [breakpoint-min]) { .fluid { /* Preferred value between the minimum and maximum bound */ } @media screen and (min-width: [breakpoint-max]) { /* Max value */ }

在移動優先的方法中,第一個選擇器將值固定到最小界限。 第一個媒體查詢處理兩個斷點之間的流動行為。 最後的斷點將值固定到最大界限。 現在我們知道了每個選擇器和媒體查詢的作用,讓我們看看如何應用最小和最大界限以及如何計算流體值。

 .fluid { font-size: [value-min]; } @media (min-width: [breakpoint-min]) { .fluid { font-size: calc([value-min] + ([value-max] - [value-min]) * ((100vw - [breakpoint-min]) / ([breakpoint-max] - [breakpoint-min]))); } } @media (min-width: [breakpoint-max]) { .fluid { font-size: [value-max] } }

這是很多樣板代碼來實現一個非常簡單的任務,即在最小和最大邊界之間固定一個值,並在兩個斷點之間添加一個流動的行為。

儘管所需的樣板數量很多,但這種方法在處理一般流體尺寸方面變得如此流行,因此很明顯需要一種更簡化的方法。 這就是 CSS 箝位函數的用武之地。

CSS clamp功能

CSS clamp函數採用三個值——最小界限、首選值和最大界限,並將當前值箝制在這些界限之間。 首選值用於確定界限之間的值。 首選值通常包括視口單位、百分比或其他相關單位,以實現流體效果。 這是一個強大而靈活的函數,除了固定值之外,它甚至可以接受數學函數和表達式,以及來自attr函數的值。

 clamp([value-min], [value-preferred], [value-max]);

此函數可應用於任何接受有效值類型(如長度、頻率、時間、角度、百分比、數字等)的屬性,因此它可以在排版和大小調整之外使用。

在撰寫本文時,瀏覽器對clamp功能的支持率超過 90%,因此它已經得到很好的支持。 對於像 Internet Explorer 這樣不受支持的桌面瀏覽器,提供一個備用值就足夠了,因為不受支持的瀏覽器如果無法解析clamp函數,將忽略整個font-size表達式。

 font-size: [value-fallback]; /* Fallback value */ font-size: clamp([value-min], [value-preferred], [value-max]);

帶有 CSS clamp的流體排版

讓我們使用 CSS clamp函數並使用以下值填充它:

  • 最小值— 等於最小字體大小。
  • 最大值— 等於最大字體大小。
  • 首選值——決定了流暢的排版如何擴展——流暢的行為和變化速度的起點和終點。 該值將取決於視口大小,因此我們將使用視口寬度單位vw

讓我們看一下以下示例,並將字體大小設置為介於32px48px之間的值。 以下font-size的設置最小值為32px ,最大值為48px 。 當前值由視口寬度單位確定,或者更準確地說,如果該值位於最小和最大邊界之間,則為當前視口寬度的4%

 font-size: clamp(32px, 4vw, 48px);

讓我們快速看一下將根據視口寬度應用於此示例的值,以便我們可以很好地掌握 CSS 箝制功能的工作原理。

視口寬度 (px) 首選值 (px) 應用值 (px)
500 20 32(限制在最小範圍)
900 36 36(範圍內的首選值)
1400 56 48(限制在最大範圍內)

我們可以注意到這個箝位函數值的兩個問題:

  • 無法訪問 min 和 max 的像素值。
    最小和最大邊界用像素值表示,因此如果用戶更改其首選字體大小,它們將不會縮放。
  • 首選值的視口值不可訪問。
    與上一個案例相同。 該值僅取決於視口寬度,並且不考慮用戶偏好。
  • 首選值尚不清楚。
    我們使用的是4vw ,起初它可能看起來像一個神奇的數字。 我們需要知道流體行為何時開始和結束,以便我們可以同步各種流體字體大小的變化。

我們可以通過將px值除以 16(默認瀏覽器字體大小)將px值轉換為最小和最大邊界的rem值來輕鬆解決第一個問題。 通過這樣做,最小值和最大值將適應用戶瀏覽器的偏好。

 font-size: clamp(2rem, 4vw, 3rem);
像素值不適應瀏覽器字體大小偏好,但 rem 和 em 值會適應。
像素值不適應瀏覽器字體大小偏好,但remem值會適應。 (大預覽)

我們需要對首選值採取不同的方法,因為該值需要響應視口大小。 但是,我們可以通過將相對rem值轉換為數學表達式來輕鬆混合它。

 font-size: clamp(2rem, 4vw + 1rem, 3rem);

請注意,這不是解決所有可訪問性問題的萬無一失的解決方案,因此測試流暢的排版是否可以放大到足夠的程度以及它是否對用戶可訪問性偏好做出了足夠好的響應仍然很重要。 稍後我們將討論這些問題。

但是,我們仍然不知道如何從示例中獲得首選值 ( 4vw + 1rem ) 以實現所需的流體行為,所以讓​​我們看看如何微調首選值並充分理解其背後的數學原理.

流體施膠功能

首選值會影響流體排版功能的行為方式。 更準確地說,我們可以改變最小值在哪個視口寬度點開始變化,以及在哪個視口寬度點達到最大值。

例如,我們可能希望流體行為從視口寬度的 1200 像素開始,到視口800px1200px結束。 請注意,不同的最小和最大界限需要不同的首選值(視口值和相對大小)以保持各種流體排版同步。

例如,我們通常不希望一種流體行為發生在視口寬度的1200px800px之間,而另一種流體行為發生在視口寬度的1000px750px之間。 這可能會導致大小不一致,如下例所示。

在此示例中,我們設置了四個具有不同邊界和相同首選大小 4vw 的流暢排版值。儘管左側的結果看起來一致,但流體尺寸(右側)在某些視口寬度上不一致,因為變化發生在不同的視口寬度上。
在此示例中,我們設置了四個具有不同邊界和相同首選大小的流體排版值4vw 。 儘管左側的結果看起來一致,但流體尺寸(右側)在某些視口寬度上不一致,因為變化發生在不同的視口寬度上。 (大預覽)

為了避免這個問題,我們需要弄清楚首選值是如何計算的,並將正確的視口和相對值分配給箝制函數的首選值。

讓我們找出一個用於計算它的函數。

 font-size: clamp([min]rem, [v]vw + [r]rem, [max]rem);

$$y=\frac{v}{100}*x + r$$

  • x - 當前視口寬度值 ( px )。
  • y — 當前視口寬度值x ( px ) 的結果流體字體大小。
  • v — 影響流體值變化率 ( vw ) 的視口寬度值。
  • r — 相對大小等於瀏覽器字體大小。 默認值為16px

使用此函數,我們可以輕鬆計算流體行為的起點和終點。 對於我們的示例,最小值2rem ( 32px ) 是恆定的,直到400px視口寬度。

$$32=\frac{4}{100}*x + 16$$

$$16=\frac{1}{25}*x$$

$$x=400$$

我們可以對最大值應用相同的函數,並看到它在800px視口寬度上達到最大值3rem ( 48px )。

此示例的目的只是為了演示首選值如何影響流暢的排版行為。 讓我們將相同的函數用於稍微更真實的場景並解決更實際的真實示例。 我們將根據所需的字體大小和我們希望流體行為發生的特定點創建可訪問的流體排版。

根據特定的起點和終點計算首選值參數

讓我們看一個在現實世界場景中經常出現的實際示例。 設計師為我們提供了字體大小和斷點,作為開發人員,我們需要使用以下參數來實現流暢的排版:

  • 最小字體大小為36px (y1)
  • 最大字體大小為52px (y2)
  • 最小值應以600px視口寬度 (x1) 結束
  • 最大值應從1400px視口寬度 (x2) 開始
從示例中可視化流暢的排版要求。
從示例中可視化流暢的排版要求。 (大預覽)

讓我們將這些值添加到我們之前討論過的流體尺寸函數中。

$$y=\frac{v}{100} \cdot x + r$$

我們最終得到了兩個方程,其中有兩個參數需要計算——視口寬度值v和相對大小r

$$(1)\;\;\; y_1=\frac{v}{100} \cdot x_1 + r$$

$$(2) \;\;\; y_2 =\frac{v}{100} \cdot x_2 + r$$

我們可以取第一個等式並將其轉換為我們可以使用的以下表達式。

$$(1) \;\;\; r=y_1 - \frac{v}{100} \cdot x_1$$

我們可以用這個表達式替換第二個等式中的r並得到計算v的函數。

$$v=\frac{100 \cdot (y_2-y_1)}{x_2 - x_1}$$

$$v=\frac{100 \cdot (52-36)}{1400 - 600}$$

$$v=2$$

我們得到視口寬度值2vw 。 以類似的方式,我們可以隔離r並使用可用參數計算它。

$$r=\frac{x_1y_2 - x_2y_1}{x_1 - x_2}$$

$$r=\frac{600 \cdot 52 - 1400 \cdot 36}{600 - 1400}$$

$$r=24$$

注意:這個值是以像素為單位的,相對值需要用rem表示,所以我們將像素值除以16 ,最終得​​到1.5rem

我們還需要將52px 36px最大邊界轉換為rem並將所有值添加到 CSS clamp函數中。

 font-size: clamp(2.25rem, 2vw + 1.5rem, 3.25rem);

我們可以繪製這個函數來確認計算的值是正確的。

(大預覽)

總而言之,我們可以使用以下兩個函數從字體大小和視口寬度點計算首選值參數v (以vw表示)和r (以rem表示)。

$$v=\frac{100 \cdot (y_2-y_1)}{x_2 - x_1}$$

$$r=\frac{x_1y_2 - x_2y_1}{x_1 - x_2}$$

現在我們完全了解了clamp函數的工作原理以及首選值是如何計算的,我們可以輕鬆地在我們的項目中創建一致且易於訪問的流體排版,並避免上述陷阱。

使用負視口值進行流體調整

我們還可以通過對視口值使用負值來使大小隨著視口大小的減小而擴大。 負視口值將反轉默認的流體行為。 我們還需要通過求解前面示例中的兩個上述方程來調整相對大小,以便流體行為在某些點開始和結束。

 font-size: clamp(3rem, -4vw + 6rem, 4.5rem);

我沒有在我的項目中使用過這種反向配置,但是如果您在項目或設計中遇到過這種需求,您可能會發現它很有趣。

具有負值的視口大小的流體調整大小。請注意,尺寸隨著視口寬度的增加而減小。
具有負值的視口大小的流體調整大小。 請注意,尺寸隨著視口寬度的增加而減小。 (大預覽)
流體行為從最大值開始,直到達到某個點,然後開始下降,直到達到最小值。
流體行為從最大值開始,直到達到某個點,然後開始下降,直到達到最小值。

流體排版可視化工具

當我在做一個項目時,我必須創建多種不同的流體排版配置。 我正在測試瀏覽器中的配置,我想創建一個工具來幫助開發人員可視化和微調流暢的排版行為。 我受到 Josh W. Comeau 的“面向 JS 開發人員的 CSS”課程中的一個演示的啟發,並創建了 Modern Fluid Typography Tool。

圖形視圖允許開發人員對流體行為有一個總體的了解。
圖形視圖允許開發人員對流體行為有一個總體的了解。 跳到計算器。

開發人員可以使用此工具創建和微調流暢的排版代碼片段並可視化流暢的行為以保持多個實例同步。 該工具還可以生成指向配置的鏈接,因此開發人員可以將鏈接包含在代碼註釋或文檔中,以便其他人可以輕鬆檢查流體調整行為。

表格視圖允許開發人員在可自定義的斷點列表上跟踪流體大小。
表格視圖允許開發人員在可自定義的斷點列表上跟踪流體大小。 (大預覽)

該項目是免費和開源的,因此請隨時報告任何錯誤並做出貢獻。 我很高興聽到您的想法和功能要求!

可訪問性問題

重要的是要重申,使用rem值不會自動使所有用戶都可以使用流暢的排版,它只允許字體大小響應用戶的字體偏好。 將 CSS clamp功能與視口單元結合使用來實現流體大小會帶來另一組我們需要考慮的缺點

Adrian Roselli 在他的博客文章中對這些問題進行了廣泛的測試和記錄。

“當您使用vw單位或限制使用clamp()獲得的文本大小時,用戶可能無法將文本縮放到其原始大小的 200%。 如果發生這種情況,這是 1.4.4 Resize text (AA) 下的 WCAG 失敗,因此請務必使用縮放測試結果。”

— 阿德里安·羅塞利

我想從一開始就解決這個問題,方法是使用 JavaScript 檢測縮放事件何時發生,並應用一個類,該類將使用常規rem值覆蓋流體大小。

 /* Apply fluid typography for default zoom level (not zoomed) */ .title { font-size: clamp(2rem, 4vw + 1rem, 3rem); } /* Revert to responsive typography if zoom is active */ body.zoom-active .title { font-size: 2rem; } @media screen and (min-width: 768px) { body.zoom-active .title { font-size: 3rem; } }

您可能會感到驚訝,因為我發現我們無法使用 JavaScript 可靠地檢測縮放事件,就像我們可以檢測任何其他常規視口事件(如調整大小)一樣。

在撰寫本文時,有 92% 的瀏覽器支持的 Visual Viewport API 規範,但縮放(縮放級別)值根本不起作用——無論縮放(縮放)值如何,它都會返回相同的值。 更不用說沒有可用的文檔、工作示例或用例。 考慮到這個 API 有如此可靠的瀏覽器支持,這有點奇怪。 確實存在一些解決方法,但它們也不是完全可靠的,並且無法檢測頁面在首次加載時是否已被放大,只有在事件發生後才能檢測到。

如果 Visual Viewport API 按預期工作,我們可以輕鬆地在縮放事件上切換 CSS 類。

 /* This code won't work because visualViewport.scale is buggy * and always returns the same value. This might be fixed in the future. */ function checkZoomLevel() { if (window.visualViewport.scale === 1) { document.body.classList.remove("zoom-active"); } else { document.body.classList.add("zoom-active"); } } window.addEventListener("resize", checkZoomLevel);

不幸的是,通過應用可變大小,我們冒著使某些在瀏覽時使用縮放功能的用戶無法訪問內容的風險。 在我們能夠為流暢排版創建可靠且更易於訪問的備用方案之前,請確保謹慎使用流暢大小並測試縮放級別是否符合 Web 內容可訪問性指南 (WCAG)。

推薦用例

流暢的排版最適合大型和突出的文本元素,最小和最大尺寸之間的差異較大。 如果沒有相應地縮放,大標題在較小的視口上會顯得更加刺耳和格格不入。

流體施膠也推薦用於我們需要保持一致施膠的情況。

在較小的桌面和較大的平板電腦視口上,標題大小和容器網格間隙未正確縮放到卡片尺寸。我們可以通過使用流體尺寸來解決這個問題。
在較小的桌面和較大的平板電腦視口上,標題大小和容器網格間隙未正確縮放到卡片尺寸。 我們可以通過使用流體尺寸來解決這個問題。 (大預覽)
流體大小可用於保持排版縮放和一致的網格間隙
流體大小可用於保持排版縮放和一致的網格間隙。

Elise Hein 在她關於流暢排版最佳實踐的文章中得出了類似的結論。

“我嘗試並未能找到許多特定區域,其中相對於視口的排版在可讀性方面確實優於基於斷點的大小。 這裡有兩個:設置顯示文本保持一致的度量。”

— 伊莉絲·海因

如果最小值和最大值之間的差異只有幾個像素,那麼流暢的排版就不會像正文中的常見情況那樣有效或有用。 最小和最大字體大小之間的差異很小的正文文本在任何視口寬度上都不會顯得格格不入,因為較大的字體大小就是這種情況。 對於這些情況,建議使用帶有斷點的常規響應式排版。

結論

流體排版不應該作為響應式排版的替代品,而是作為特定用例的增強。 我們應該使用流暢的排版來平滑縮放最小和最大尺寸之間差異較大的文本,並保持一致的尺寸。

當使用具有 CSS clamp功能的多個流體排版元素時,我們必須確保流體縮放是同步的。 我們可以通過計算視口寬度和相對值並將它們用作 CSS clamp函數中的首選值來做到這一點。 我們還必須記住使用相對單位,如 rem 單位,以便流暢的排版適應用戶的字體大小偏好。

我們還看到了流暢的排版如何限制用戶縮放功能,從而導致可訪問性問題。 如果測試顯示內容不夠可縮放,則使用縮放測試流暢的排版並將其恢復為常規響應式排版非常重要。

當縮放動作發生時,我們應該能夠通過覆蓋流體排版值來解決這個問題。 但是,目前無法這樣做,因為 Visual Viewport API 無法正常工作並且不響應用戶縮放事件。

參考

  • clamp() ,MDN
  • “無論如何,為什麼類型應該是流暢的?” Elise Hein
  • “簡化的流體排版,” Chris Coyier
  • “響應式類型和縮放,”阿德里安·羅塞利
  • “使用vhvw單位的響應式和流暢的排版,”Michael Riethmuller