使用 CSS 自定義函式

CSS 自定義函式使你能夠建立可重用的 CSS 程式碼塊,這些程式碼塊可以接受引數,包含複雜的邏輯(使用 CSS if() 函式和 @media @規則等特性定義),並根據該邏輯返回值。它們的工作方式類似於 CSS 自定義屬性,但提供了更大的靈活性。

在本文中,我們將向你展示如何使用它們,並提供一些實際的例子。

函式基礎

一個基本的 CSS 自定義函式定義如下所示:

css
@function --half-opacity() {
  result: 0.5;
}

@function 語法之後,我們為函式定義一個名稱:--half-opacity。這必須是一個 <dashed-ident> 型別——它必須以雙破折號開頭,並且區分大小寫。函式名後緊跟一對圓括號(())和一對花括號({})。

注意:如果多個 CSS 函式被賦予相同的名稱,則在級聯中更強的 @layer 中的函式會獲勝。如果它們都在同一層中,則原始碼中最後定義的函式會獲勝。

花括號內是函式的主體,這裡定義了函式的邏輯。這可以包含多個宣告,包括自定義屬性(其作用域將限於函式主體)、諸如 @media 之類的 @規則,以及 result 描述符。result 描述符的值被計算以確定函式返回的值。

在這裡,我們將 result 設定為值 0.5--half-opacity() 函式將始終返回 0.5

為什麼是“result”而不是“return”?

result 描述符在功能上聽起來類似於 JavaScript 函式的 return 語句。然而,CSS 函式中不使用 return。這是因為,與 JavaScript 的 return 語句不同,CSS 函式不會在遇到 result 宣告時立即返回值。

CSS 函式的主體從頭到尾進行計算。如果主體中包含多個 result 宣告,則原始碼中最後一個會覆蓋前面的。

呼叫 CSS 函式

可以使用 <dashed-function> 語法在任何合適的屬性值位置呼叫 CSS 函式,該語法由函式名後跟一對圓括號組成,圓括號內包含要傳遞給函式的引數(如果有)。例如,我們可以像這樣呼叫我們的 --half-opacity() 函式:

css
h2 {
  opacity: --half-opacity();
}

由於此函式始終返回值 0.5,因此前面的宣告等同於 opacity: 0.5。這並不是很有用。你還不如直接使用自定義屬性或字面量值 0.5

讓我們繼續看看如何使用 CSS 函式。

CSS 函式的特性檢測

CSS 函式在沒有引數時的一個實際用途是特性檢測。在本文我們將要看到的所有示例中,我們定義了一個 --supports() 函式,它看起來像這樣:

css
@function --supports() {
  result: none;
}

然後,你可以定義一個“功能不支援”的橫幅,並將其 display 屬性設定為 --supports()

html
<p class="support">
  ⚠️ Your browser doesn't currently support CSS custom functions.
</p>
css
.support {
  /* ... */
  display: --supports();
}

在支援自定義函式的瀏覽器中,display 將被設定為 none,支援橫幅將被隱藏。在不支援的瀏覽器中,display: --supports() 宣告將是無效的,因此會被忽略;因此,橫幅將被顯示。

指定函式引數

CSS 函式引數在函式名後的圓括號內指定為逗號分隔的自定義屬性。例如:

css
@function --transparent(--color, --alpha) {
  result: oklch(from var(--color) l c h / var(--alpha));
}

該函式名為 --transparent,並接受兩個自定義屬性作為引數,--color--alpha,它們可以在函式主體內部區域性使用。主體包含一個 result 描述符,它使用 CSS 相對顏色語法將輸入的 --color 值轉換為一個 oklch() 顏色,其 alpha 通道值由輸入的 --alpha 值指定。

然後,你可以在任何想要生成現有顏色半透明版本的地方呼叫此函式。

例如

css
section {
  --base-color: #faa6ff;
  background-color: --transparent(var(--base-color), 0.8);
}

指定資料型別

可以為函式引數和返回值指定允許的資料型別。當你不指定這些時,函式將接受任何型別的值。

讓我們修改我們之前的函式以提供資料型別:

css
@function --transparent(--color type(<color>), --alpha type(<number>)) returns
  type(<color>) {
  result: oklch(from var(--color) l c h / var(--alpha));
}

每個引數的資料型別在引數名後指定,而 result 的資料型別在左花括號前指定,並以 returns 關鍵字開頭。type() 函式用於指定資料型別。

請注意,在只指定單個數據型別的情況下,可以省略 type() 語法,直接將型別寫成簡寫形式:

css
@function --transparent(--color <color>, --alpha <number>) returns <color> {
  result: oklch(from var(--color) l c h / var(--alpha));
}

現在,只有當輸入引數分別為 <color><number>,並且 result 是一個 <color> 時,該函式才會產生有效值。否則,例如:

css
section {
  --base-color: #faa6ff;
  background-color: --transparent(var(--base-color), 50%);
}

那麼該值將在計算值時變得無效(因為 50% 不是 <number> 而是 <percentage>),background-color 最終將被設定為 transparent

指定多個允許的型別

你可以使用 | 符號作為分隔符來指定多個可接受的資料型別,例如:

css
@function --transparent(--color <color>, --alpha type(<number> | <percentage>))
  returns <color> {
  result: oklch(from var(--color) l c h / var(--alpha));
}

在這種情況下,必須使用完整的 type() 語法。

經過這個調整,--transparent(var(--base-color), 50%) 函式呼叫現在是有效的。

指定預設值

你還可以在引數定義的末尾,用冒號指定引數的預設值。例如:

css
@function --transparent(--color <color>, --alpha <number>: 0.8) returns <color> {
  result: oklch(from var(--color) l c h / var(--alpha));
}

--alpha 引數的預設值現在是 0.8。如果你想使用這個值,可以在呼叫函式時省略第二個引數:

css
section {
  --base-color: #faa6ff;
  background-color: --transparent(var(--base-color));
}

注意:如果一個無效的值作為函式引數傳入,並且該引數定義中指定了預設值,那麼無效值將被忽略,而使用預設值。

顏色調整函式示例

你可以在我們的 color-adjust-functions 示例中看到 --transparent() 函式的實際效果(參見原始碼)。

這個例子還包括名為 --lighter()--darker() 的函式,它們的工作方式與 --transparent() 類似,但分別返回顏色的更亮和更暗的變體:

css
@function --transparent(--color <color>, --alpha <number>: 0.8) returns <color> {
  result: oklch(from var(--color) l c h / var(--alpha));
}

@function --lighter(--color <color>, --lightness-adjust <number>: 0.2) returns
  <color> {
  result: oklch(from var(--color) calc(l + var(--lightness-adjust)) c h);
}

@function --darker(--color <color>, --lightness-adjust <number>: 0.2) returns
  <color> {
  result: oklch(from var(--color) calc(l - var(--lightness-adjust)) c h);
}

像這樣的函式庫對於基於單一顏色定義顏色方案非常有用:

css
:root {
  --base-color: #faa6ff;
}

section {
  background-color: --transparent(var(--base-color));
  border: 3px solid --lighter(var(--base-color), 0.1);
  color: --darker(var(--base-color), 0.55);
}

包含複雜邏輯

你可以使用諸如 @media @規則和 if() 函式之類的構造在函式中包含更復雜的邏輯。

我們的 responsive-narrow-wide 示例(參見原始碼)展示了一個名為 --narrow-wide() 的函式,它可以為任何屬性提供兩個值選項。一個在視口低於特定斷點時設定,另一個在視口高於該斷點時設定。

--narrow-wide() 函式接受兩個引數,--narrow--wide。返回的 result--wide 屬性,除非視口寬度小於 700px,在這種情況下返回 --narrow

css
@function --narrow-wide(--narrow, --wide) {
  result: var(--wide);
  @media (width < 700px) {
    result: var(--narrow);
  }
}

該函式可用於在多種上下文中提供響應式的值選項:

css
body {
  display: grid;
  grid-template-columns: repeat(--narrow-wide(1, 3), 1fr);
  gap: --narrow-wide(0, 20px);
  padding: 0 20px;
}

h2 {
  font-size: --narrow-wide(2.5rem, 2rem);
}

p {
  font-size: --narrow-wide(1.4rem, 1rem);
  line-height: 1.5;
}

使用 if() 函式

我們可以使用 if() 函式來重寫 --narrow-wide() 函式:

css
@function --narrow-wide(--narrow, --wide) {
  result: if(media(width < 700px): var(--narrow) ; else: var(--wide));
}

一次編寫複雜語法,然後重用

CSS 函式的一個關鍵用例是定義一次複雜的語法片段,然後能夠透過更簡單的函式呼叫多次重用它。

我們的 gradient-function 示例(參見原始碼)提供了這方面的一個例子。它有一個名為 --shippo-pattern() 的函式,該函式接受長度和顏色引數,並返回一個複雜的 background 值,該值包含多個 radial-gradient() 背景:

css
@function --shippo-pattern(--size <length>, --tint <color>) {
  result:
    radial-gradient(closest-side, transparent 98%, rgb(0 0 0 / 0.3) 99%) 0 0 /
      var(--size) var(--size),
    radial-gradient(closest-side, transparent 98%, rgb(0 0 0 / 0.3) 99%)
      calc(var(--size) / 2) calc(var(--size) / 2) / var(--size) var(--size)
      var(--tint);
}

定義了這個函式後,我們現在可以建立具有不同色調和圓圈大小的該背景值的變體:

css
#one {
  background: --shippo-pattern(100px, #def);
}

#two {
  background: --shippo-pattern(3.5rem, lime);
}

#three {
  background: --shippo-pattern(10vw, purple);
}

另見