Smashing Magazine 在 10 月 20 日刊出 Brecht De Ruyte 的〈Embracing Playful Imperfection With The CSS random() Function〉,主張 design system 靠一致性運作,但裝飾性的部分可以用原生 CSS 的 random() 加一點可控的不完美,不必再靠 JavaScript 或 Sass 迴圈產生固定的亂數樣式。今天整篇讀這份教學:語法怎麼寫、六個示範各解決什麼視覺問題、無障礙要補哪些防護,以及哪些地方不該用。
與 昨天的 UIUX 換了題材:前兩天是 NN/g 的 AI 研究,今天回到介面實作。讀到的程度:我用網頁抽取工具讀了這篇文章的摘要,語法與數字是抽取結果,不是逐字讀完整原文,也沒有親手在瀏覽器跑過範例;凡是「自算」都是我的推論。
📖 學
一、語法:四個參數與一個容易踩的單位限制
文章依 CSS Values and Units Level 5 的規格,把函式寫成 random( <random-key>?, <min>, <max>, <step>? )。最常用的形式只有上下界,例如 rotate: random(-3deg, 3deg);,每個元素會各抽一個落在範圍內的連續值。文章強調最小值與最大值必須是同一種單位,random(2rem, 100px) 是無效的,要混用就得在參數裡用 calc() 先合成。
第三個選用參數是步進。沒有步進時值是連續的;有步進時會吸附到固定間隔,例如 random(0px, 60px, 15px) 只會得到 0、15、30、45、60 像素五種結果。步進的設計用途是避免出現「3.17 度」這種沒有意義的細碎差異,也讓結果可預測、容易檢查。
二、快取鍵:讓兩個屬性共用同一個亂數
第一個選用參數是以雙連字號開頭的識別字,例如 --square-size,作用是讓多個屬性拿到同一個隨機值。文章的例子是方形磁磚:inline-size 與 block-size 用同一個鍵,才能保持 1 比 1,否則會得到隨機長方形。
這裡有一個作者自己也在文中修正的細節:單寫識別字時,種子是整份文件共用的,所以所有符合的元素會長得一模一樣;加上 element-scoped(寫成 --square-size element-scoped)才會讓鍵只在單一元素內連結、每個元素各抽各的。文章第一個卡片片段漏寫了這個關鍵字,作者後面才更正,照抄前要留意。另有 random-item() 可以從一串值(例如一組 oklch() 色票)裡挑一個,但文章說它目前還沒有瀏覽器支援。
三、六個示範各自解決什麼
第一個是字母不完美:每個字元套 rotate: random(-2.8deg, 2.8deg)、位移 random(-1.5px, 1.5px) random(-2.8px, 2.8px) 與 opacity: random(0.8, 1, 0.02),製造手寫、印刷的質感,滑過時全部歸正。第二個是程序式彈跳:每個字母的跳高、傾斜、時長(1 秒到 1.2 秒、每 0.1 秒一階)都各自抽值,延遲用負值 random(element-scoped, -3s, 0s, 0.1s),這樣載入時每個字母已經跳到動畫中途,不會等計時器走完才一起啟動。
第三個是歪斜卡片:--tilt: random(-3.5deg, 3.5deg) 加上 --shift-y: random(-6px, 6px),陰影偏移跟著傾斜走,滑過或 :focus-visible 時扶正。第四個是扇形牌堆:用 sibling-index() 與 sibling-count() 排出每張 12 度的扇形,再讓 random() 加抖動,並以 animation-timeline: scroll(root) 與 animation-range: 0% 80% 讓捲動驅動展開。第五個是隨機速度的橫向行列:calc(pow(-1, sibling-index())) 讓每列方向交替,random(15%, 44%) 決定每列位移量。第六個是散景景深:blur(random(0px, 8px, 1px))、opacity: random(0.25, 1, 0.05)、scale: random(0.85, 1.25, 0.05),滑過時像對焦一樣變清楚。
自算:第六個示範每個元素的離散組合是模糊 9 階、透明度 16 階、縮放 9 階,三者互相獨立的話共 9 乘 16 乘 9,等於 1,296 種外觀。這個數字只在三個屬性各自獨立抽值(沒共用鍵)時成立,且依我對步進端點含頭含尾的讀法;實際視覺上相鄰階差很小,肉眼分辨不出那麼多種,所以它說明的是「步進把空間收斂到可檢查的大小」,不是「有 1,296 種可辨識的樣子」。
四、無障礙:文章留的三個防護與我認為還缺的一個
文章對無障礙講得相當具體。第一,把文字拆成逐字 span 會讓螢幕閱讀器逐字母朗讀,所以視覺用的 span 要加 aria-hidden="true",完整單字用 aria-label 提供。第二,prefers-reduced-motion: reduce 下要關掉動畫並重設 rotate 與 translate;文章還建議連隨機角度本身也一起壓平,因為捲動時大量歪斜本身就會讓人不舒服,不只是動畫。不過摘要提到這段程式只涵蓋字母示範,其他五個示範得自己補等價規則。第三,按鈕與連結不可以一直維持歪斜或位移,滑過與取得焦點時要歸位。
我補一點:aria-label 加在沒有角色的 span 上,支援度在不同螢幕閱讀器之間並不一致,這是我的一般性印象,沒有在這篇文章裡查證;穩妥做法是把文字本身留在 DOM 裡、只對視覺拆字的部分處理,上線前用真實螢幕閱讀器聽過一遍。
五、哪裡該用、哪裡不該用,以及支援度
文章列的適用範圍是純裝飾:拍立得與便利貼式的歪斜、錯開節奏的環境動畫、生成式背景、編輯式版面,以及疊在結構性數學上的小偏移。不該碰的是核心版面幾何(欄距、表單欄位寬度、容器內距)、內文排印(字級、行高、字距),以及會動卻不歸位的互動元件。
支援度方面,文章說 Safari/WebKit 最先實作,Chrome 與 Edge 隨 Chrome 156 推出中,Firefox 在標準化流程上有正面訊號但沒有已出貨的說法。我沒有另外核對 Chrome 156 的發佈狀態,這是文章的說法,上線前請查 caniuse 或各瀏覽器的狀態頁。建議寫法是用 @supports (rotate: random(-2deg, 2deg)) 包起來,不支援時退回 rotate: 0deg;,也就是不歪的版本,這剛好符合「裝飾可以消失、內容不能壞」的原則。
六、檢查表
| 要做的事 | 設計與前端動作 | 驗證方式 |
|---|---|---|
| 裝飾性隨機 | 只套在插圖、卡片外觀、背景 | 關掉隨機後版面與內容不變 |
| 成對屬性 | 共用快取鍵並加 element-scoped | 檢查方形元素是否真的是 1 比 1 |
| 支援度 | @supports 包住並提供不歪的退路 | 在不支援的瀏覽器看畫面 |
| 減少動態 | 壓平角度與動畫 | 開啟系統「減少動態」後逐個示範檢查 |
| 互動元件 | 滑過與 :focus-visible 歸位 | 只用鍵盤 Tab 走一遍 |
🧠 記
- 來源: Smashing Magazine,2026-10-20,Brecht De Ruyte,約 16 分鐘閱讀;我讀的是抽取摘要。
- 語法:
random( <key>?, <min>, <max>, <step>? ),上下界單位必須相同;有步進才會吸附,如random(0px, 60px, 15px)得五種值。 - 快取鍵: 識別字讓多屬性共用一個值;要每個元素各抽各的,必須加
element-scoped。 - 無障礙: 拆字用
aria-hidden加aria-label;減少動態要連角度一起壓平;互動元件滑過與聚焦要歸位。 - 用途: 只用在裝飾,不碰核心版面與內文排印;
random-item()目前沒有瀏覽器支援(依文章)。
邊界: 沒親手跑範例;Chrome 156 與 Firefox 狀態是文章說法、我沒核對;1,296 種組合是自算且有前提;aria-label 在 span 上的支援差異是我的印象、未查證。
✍️ 實踐
用 30 分鐘為自己的專案做一個「安全的不完美」實驗。
- 挑一個純裝飾元素,例如卡片縮圖或空狀態插圖,不要挑按鈕或表單。
- 寫
@supports (rotate: random(-2deg, 2deg))區塊,裡面放rotate: random(-2deg, 2deg);,外面保留rotate: 0deg;。 - 如果同時控制寬高,先用
--box element-scoped這種寫法共用鍵,再確認每個元素各自不同又保持比例。 - 加上
@media (prefers-reduced-motion: reduce)把角度與位移歸零。 - 在系統開啟減少動態、只用鍵盤、和螢幕閱讀器三種情況各走一遍,記下哪裡壞掉。
🔗 延伸學習
- Embracing Playful Imperfection With The CSS random() Function(Smashing Magazine):10 月 20 日,本篇主角,我抽取過一次。
- Smashing Magazine 文章列表:我讀過列表頁,近期還有 10 月 6 日的〈A Practical Guide To Naming Things〉與 10 月 23 日的 Accordion 圖示。
- CSS Values and Units Module Level 5(CSSWG 草稿):
random()的規格來源,我沒有逐段讀,只引用文章對它的描述。
💬 問 AI
我要評估 Smashing Magazine 2026-10-20 的〈Embracing Playful Imperfection With The CSS random() Function〉(Brecht De Ruyte)。請嚴格分成「文章說的」「你查證到的」「你的推論」三區回答,不確定就直說,不要編造連結、數字或引文,也不要給任何投資或財務建議。
背景(請當待查證主張):
1. 語法為 random( <random-key>?, <min>, <max>, <step>? ),上下界必須同單位;加上步進才會吸附,例如 random(0px, 60px, 15px) 得 5 種值。
2. 單寫識別字作快取鍵時種子為整份文件共用,要每個元素各抽需加 element-scoped。
3. Safari 最先支援,Chrome 隨 156 推出中,Firefox 只有正面訊號;random-item() 尚無支援。
4. 我自算:blur 9 階、opacity 16 階、scale 9 階各自獨立時共 1,296 種組合。
請做:
A. 逐點對照 CSS Values and Units Level 5 規格與 MDN、caniuse,標示「成立/部分成立/查不到」並附一手連結。
B. 檢查第 4 點算法,說明步進端點是否含頭含尾。
C. 說明在逐字 span 上使用 aria-hidden 加 aria-label 的螢幕閱讀器支援差異,並給一個更穩妥的替代寫法。
D. 為一個 6 人前端團隊設計一份「裝飾性隨機」的使用規範,含 prefers-reduced-motion、@supports 退路與驗收清單。
只談 CSS 與設計實務,不評論公司或人事。