以高度為高度:自動;(以及其他內建大小關鍵字) 在 CSS 中

使用 interpolate-size 屬性或 calc-size() 函式,即可在長度和內在大小關鍵字之間順暢轉換,並加上動畫效果。

發布日期:2024 年 9 月 17 日

簡介

CSS 常見要求功能是能夠以動畫效果顯示 height: auto。這項要求的微小變化是轉換 width 屬性,而非 height,或是轉換至 min-contentmax-contentfit-content 等關鍵字所代表的任何其他內在大小

舉例來說,在下列示範中,如果標籤在游標懸停在圖示上時,能以流暢的動畫效果調整至自然寬度,那就太棒了。

使用的 CSS 如下:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease; /* 👈 Transition the width */

    &:hover,
    &:focus-visible {
        width: max-content; /* 👈 Doesn't work with transitions */
    }
}

雖然已宣告 transition 來轉換 width 屬性,並在 :hover 上宣告 width: auto,但系統不會順利轉換。而是突然改變。

使用 interpolate-size 設定動畫,在內在大小關鍵字之間轉換

瀏覽器支援

  • Chrome:129。
  • Edge:不支援。
  • Firefox:不支援。
  • Safari:不支援。

資料來源

您可以透過 CSS interpolate-size 屬性,控制是否允許使用 CSS 內在大小設定關鍵字的動畫和轉場效果。

預設值為 numeric-only,不會啟用插補。將屬性設為 allow-keywords 時,如果瀏覽器可以為這些關鍵字製作動畫,您可以選擇從長度到 CSS 內在大小關鍵字的插補。

根據規格

  • numeric-only:無法對 <intrinsic-size-keyword> 進行插補。
  • allow-keywords:如果其中一個值是 <intrinsic-size-keyword>,另一個值是 <length-percentage>,則可以插補兩個值。[…]

由於 interpolate-size 屬性是可繼承的屬性,因此您可以在 :root 上宣告該屬性,以便在整份文件中切換內在大小關鍵字。這是我們建議的方法。

/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

在以下示範中,我們會將這項規則加入程式碼。因此,從 width: auto 傳送和接收的動畫都能正常運作 (在支援的瀏覽器中):

縮小選取器範圍,限制參與意願的觸及範圍

如果您想將 allow-keywords 選擇加入功能限制在文件的子樹狀結構中,請將選取器從 :root 調整為只選取您要指定的元素。舉例來說,如果網頁的 <header> 與這類轉場不相容,您可以將選擇加入的範圍限制在 <main> 元素及其子項,如下所示:

main { /* 👈 Scope the opt-in to only <main> and its descendants */
    interpolate-size: allow-keywords;
}

為什麼不預設允許動畫大小調整關鍵字?

針對這項選擇加入機制,常見的意見回饋是瀏覽器應該只允許從內在大小關鍵字到長度的轉場和動畫,預設為此。

我們在開發這項功能時,就研究過啟用這項行為的選項。工作小組發現,預設啟用這項功能無法向後相容,因為許多樣式表都假設內在大小關鍵字 (例如 automin-content) 無法製作動畫。詳情請參閱這篇 CSS Working Group 問題的評論

因此,這項屬性為選用屬性。由於繼承特徵,在整份文件中選擇只需在 :root 上宣告 interpolate-size: allow-sizes,如前文所述。

使用 calc-size() 設定動畫,在內在大小關鍵字之間轉換

瀏覽器支援

  • Chrome:129。
  • Edge:129。
  • Firefox:不支援。
  • Safari:不支援。

資料來源

另一種啟用內在大小設定關鍵字內插的做法,是使用 calc-size() 函式。這可讓您以安全且明確的方式,對內在大小執行數學運算。

這個函式會依序接受兩個引數:

  • 計算大小依據,可以是 <intrinsic-size-keyword>,也可以是巢狀 calc-size()
  • 計算大小計算,可讓您使用計算大小基礎執行計算。如要參照計算大小依據,請使用 size 關鍵字。

例如:

width: calc-size(auto, size);        // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered

calc-size() 新增至原始示範,程式碼如下所示:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease;

    &:hover,
    &:focus-visible {
        width: calc-size(max-content, size); /* 👈 */
    }
}

從視覺效果來看,結果與使用 interpolate-size 時完全相同。因此,在這種情況下,您應使用 interpolate-size

calc-size() 的優點在於可進行計算,而 interpolate-size 則無法執行這項操作:

width: calc-size(auto, size - 10px); // = The auto width minus 10 pixels
width: calc-size(min-content, size + 1rem); // = The min-content width plus 1rem
width: calc-size(max-content, size * .5);   // = Half the max-content width

舉例來說,如果您希望頁面上的所有段落都以 50px 為單位,並且大小為最接近的倍數,可以使用以下語法:

p {
    width: calc-size(fit-content, round(up, size, 50px));
    height: calc-size(auto, round(up, size, 50px));
}

當兩個 calc-size() 的計算大小基底相同時,calc-size() 還可讓您在兩者之間內插。這也是 interpolate-size 無法達成的目標。

#element {
    width: min-content; /* 👈 */
    transition: width 0.35s ease;

    &:hover {
        width: calc-size(min-content, size + 10px); /* 👈 */
    }
}

為什麼不允許在 calc() 中使用 <intrinsic-size-keyword>

calc-size() 常見的問題是,CSS 工作小組為何不調整 calc() 函式,以支援內在大小關鍵字。

其中一個原因是,您在計算時不得混用內在尺寸關鍵字。舉例來說,您可能會想寫下看似有效的 calc(max-content - min-content),但實際上並非如此。calc-size() 會強制執行正確性,因為與 calc() 不同,它只接受單一 <intrinsic-size-keyword> 做為第一個引數。

另一個原因是情境感知。部分版面配置演算法會針對特定內在大小關鍵字執行特殊行為。calc-size() 是明確定義的內在大小,而非 <length>。因此,這些演算法可將 calc-size(<intrinsic-size-keyword>, …) 視為 <intrinsic-size-keyword>,並維持該關鍵字的特殊行為。

採用哪種做法?

在大多數情況下,請在 :root 上宣告 interpolate-size: allow-keywords。這是啟用內在大小關鍵字動畫的簡單方法,因為它基本上是單行程式碼。

/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

這段程式碼是很好的漸進式增強功能,因為不支援這項功能的瀏覽器會改為不使用轉場效果。

如需精細控制某些項目 (例如進行計算),或是想使用只有 calc-size() 可執行的行為,您可以改用 calc-size()

#specific-element {
    width: 50px;

    &:hover {
        width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
    }
}

不過,如果您在程式碼中使用 calc-size(),就必須為不支援 calc-size() 的瀏覽器加入備用方案。例如新增額外的大小宣告,或改用 @supports 進行功能偵測。

width: fit-content;
width: calc-size(fit-content, size + 1em);
       /* 👆 Browsers with no calc-size() support will ignore this second declaration,
             and therefore fall back to the one on the line before it. */

更多示範

以下是一些充分發揮 interpolate-size: allow-keywords 優點的示範。

通知

以下示範是這個 @starting-style 示範的分支。調整程式碼,讓系統可新增不同高度的項目。

為達到這項目的,整個網頁都會選擇使用大小關鍵字插補,而每個 .item 元素的 height 都會設為 auto。否則,程式碼與分支前完全相同。

:root {
    interpolate-size: allow-keywords; /* 👈 */
}

.item {
    height: auto; /* 👈 */

    @starting-style {
        height: 0px;
    }
}

<details> 元素設定動畫效果

您會想使用這類插補的典型用途,是為展開的揭露小工具或專屬摺疊式選單製作動畫。在 HTML 中,您可以使用 <details> 元素來執行這項操作。

有了 interpolate-size: allow-keywords,您可以執行以下操作:

@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }
    
    details {
        transition: height 0.5s ease;
        height: 2.5rem;
        
        &[open] {
            height: auto;
            overflow: clip; /* Clip off contents while animating */
        }
    }
}

不過,您可以看到,動畫只會在展開小工具開啟時執行。為因應這項需求,Chrome 正在開發 ::details-content 擬似元素,並將於今年稍晚在 Chrome 中推出 (日後的文章將會介紹這項元素)。結合 interpolate-size: allow-keywords::details-content,您可以獲得雙向動畫: