What?CSS has function?!
Introduction
你能想像嗎?上週還在為 CSS if() 讚嘆不已,沒想到今天就看到了另一個神兵利器 — CSS @function。是的!現在我們也可以在 CSS 裡頭下 function 了,透過 function 幫我們做一些簡單的運算、條件判斷以及資料整理,讓整體的撰寫與呈現可以更加簡約、便利。
來看一下它的基本用法:
@function --negative(--value) {
result: calc(-1 * var(--value));
}首先它透過 @ function 起頭來定義 function,然後 function name 的組成就如同 custom property 一樣,都是兩根 dash 去連結名稱,再接上需要的 params (params 亦是變數所以也要用兩根 dash 去定義)即可。最後再把整理好的資料 assign 給 result 即可。此即類似其他語言 function 中 return 的作用。
也就是說上面這個 negative() 的作用就是把帶進去的值乘上 -1 後再回傳。那麼實際上我們可以怎麼使用呢?
div {
--x: 100;
inset-inline-start: calc(--negative(100) * 1px);
/* or */
inset-inline-start: calc(--negative(var(--x)) * 1px);
}如上所示,我們可以在任何地方中呼叫這個 function,而帶入的參數可以直接帶入數值或者 custom property。上面這兩種用法均可以設定 inset-inline-start 為 -100px。
再來看一個 Chrome official blog 舉的例子:
@function --light-dark(--light, --dark) {
result: var(--light);
@media (prefers-color-scheme: dark) {
result: var(--dark);
}
}
div {
color: --light-dark(black, white);
}light-dark() 裡頭搭配了 media query 來動態判斷當前環境為 light / dark mode 更換對應的文字顏色,等於透過自定義 function 來達到與 CSS native function light-dark() 一樣的功能。
目前已經可以在 Chrome Canary 玩到這樣的支援,stable version 則仍需要等到 Chrome VER.139 才有支援。
看到這裡,相信大家對於 CSS function 的用法已經有了初步的了解了,接下來筆者將分享幾個自己實作中發現的眉眉角角讓大伙們可以更快知曉它的能與不能。
Do not define function in nesting
CSS Nesting 的寫法大家應該都不陌生,筆者曾試著把 function 的定義塞到 Nesting 當中,不過這樣是不 work 的喔!所以撰寫時需要特別注意此點。
Wrong:
div {
@function --light-dark(--light, --dark) {
result: var(--light);
@media (prefers-color-scheme: dark) {
result: var(--dark);
}
}
background-image: --light-dark(black, white);
}Correct:
@function --light-dark(--light, --dark) {
result: var(--light);
@media (prefers-color-scheme: dark) {
result: var(--dark);
}
}
div {
background-image: --light-dark(black, white);
}Can I use custom property as function parameter?
答案是肯定的,我們可以使用 custom property 當作 function parameter 餵進去。
@function --get-element-size(--mobile-size, --desktop-size) {
result: var(--desktop-size);
@media (width < 767px) {
result: var(--mobile-size);
}
}
div {
--size-value1: 10em;
--size-value2: 5em;
--size: --get-element-size(var(--size-value1), var(--size-value2));
inline-size: var(--size);
}如上所示,筆者定義了 get-element-size(),透過 media query 來決定要使用參數 1 還是參數 2 作為 div 的 inline-size。
Will function refresh when relative custom property change?
從上例我們可以清楚得知 CSS function 是可以使用 custom property 當作參數餵入 function 中。那麼當 custom property 異動時,function 會怎麼處理呢?
custom property 有個特性,只要有異動,那麼與之相關聯的 custom property 亦會即時更新,這個定義其實也擴展到 CSS function 。
@function --get-element-size(--mobile-size, --desktop-size) {
result: var(--desktop-size);
@media (width < 767px) {
result: var(--mobile-size);
}
}
div {
--size-value1: 10em;
--size-value2: 5em;
--size: --get-element-size(var(--size-value1), var(--size-value2));
inline-size: var(--size);
&.bigger {
--size-value1: 20em;
--size-value2: 10em;
}
}倘若這個 div 掛上了 bigger 這個 classname,那麼 — size-value1 以及 — size-value2 便會被重新定義為 20em 以及 10em。由於 custom property 異動的關係,所以便會連帶驅動與之有關係的 function 進行 refresh 的動作,也就是說 div.bigger 的 inline-size 便會依照當前 viewport size 去換出 20em 或者 10em 的大小出來。
Can I use if() in CSS function?
可以喔!甚至筆者懷疑當初 CSS if() 的支援就是希望可以更廣泛的應用在 CSS function 當中,所以我們可以 refactor 上面的 function:
/* original */
@function --get-element-size(--mobile-size, --desktop-size) {
result: var(--desktop-size);
@media (width < 767px) {
result: var(--mobile-size);
}
}
/* refactor with if() */
@function --get-element-size(--mobile-size, --desktop-size) {
result: if(
media(width < 767px): --mobile-size;
else: --desktop-size;
);
}搭配 if() 後,便讓整體的撰寫更加程式語言化了,亦可更靈活的來定應 function 的內容。
Conclusion
以上便是筆者針對 CSS function 的一些簡單使用心得分享,其實就算沒有 CSS function,一樣可以透過 CSS custom property 搭配 JavaScript 進行運算來達到一樣的功能。不過 CSS 能支援自是慎好,讓 web developers 視當前環境許可來選擇最適宜的 solution。
倘若有其他心得或是意會,筆者亦會同步更新本文,感謝您的閱讀~