Communitygithub.com

sridhar-raj/blur-craft

打造高品質的毛玻璃與模糊效果 UI,並驗證其可讀性:Apple 級霜凍玻璃(具厚度等級的實體材料、發光邊框與色調分層)、現成玻璃表面(黏性導航欄、側邊欄/面板、Dock、選單、對話框疊於變暗模糊頁面、提示、工作表)、液態玻璃折射、漸進模糊、模糊揭示、發光與深度效果,附 React、Vue、Svelte、Tailwind 及原生平台(SwiftUI、Flutter、React Native、Compose)指南,以及對比度與霜凍檢測工具。

blur-craft 是什麼?

Blur Craft 是一個全面的 CSS 與 UI 工具組,專為在現代前端框架中建立高品質、Apple 級別的毛玻璃效果而設計。它超越簡單的 `backdrop-filter`,將玻璃視為一種具備厚度層級(清晰、薄、常規、厚、實心)的材料,每種層級都配對特定的模糊半徑與填充不透明度,以確保在不同光線條件下的可讀性。此工具組提供現成的表面元件(黏性導航欄、Dock、選單、對話框)、進階效果(液態玻璃折射、漸進模糊、模糊橋接淡入淡出),以及實用腳本,用於測量模糊效果對細節的保留程度,並在文字上最暗的像素處驗證 WCAG 對比度(4.5:1)。Blur Craft 支援 React、Vue、Svelte、Tailwind v4,並提供 SwiftUI、Flutter、React Native 與 Compose 的原生指南。其架構以 CSS 自訂屬性和類別切換為基礎,避免在 Safari 中已知會破壞模糊效果的 `var()` 後備。它還包含效能預算(將模糊區域優先於半徑或數量)、設備效能分層(透過 `glass-tier.js`)、輔助功能支援(`prefers-contrast`、Windows 高對比度)以及截圖/列印的擷取模式。核心文件強調光線構圖(將光源置於玻璃下方)、材質設定(厚度、色調、中性度)以及強制性的檢測步驟(霜凍檢測、對比度檢測),以確保玻璃看起來昂貴、可讀且在不同配置與裝置上保持流暢。

相容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/sridhar-raj/blur-craft/tree/HEAD

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

毛玻璃工藝

網路上的多數玻璃看起來廉價,原因有三:背後沒有有趣的內容、材質僅為單一色調加單一模糊、以及上方的文字是未經測量的混濁灰色。看起來高級(Apple 的材質、visionOS、頂尖產品網站)的玻璃,會依序正確處理三件事:

  1. 玻璃後方的光線經過設計。 玻璃會展示後方的內容,因此需在後方放置光源。
  2. 玻璃是一種材料,而非色調。 飽和模糊、漸層填充、發光邊框、鏡面光澤、深度、紋理。請參考 assets/glass.css。
  3. 文字經過測量。 半透明玻璃會因背後每個像素而改變對比度,因此需在三個狀態下,透過腳本在最差位置檢查對比度(scripts/glass-contrast.mjs)。

快速路徑

我正在建構…使用備註
黏性導航欄.gs-bar(surfaces.css)延伸背景;加上 gs-bar--on-scroll 僅在滾動後才呈現玻璃質感
播放器、Dock 或覆蓋於藝術圖上的藥丸形按鈕.gs-dock.glass.glass--clear + liquid-glass.js清晰的液態玻璃,每個畫面一個;若藝術圖變化大則使用自適應色調
含文字的浮動卡片或面板.glass.glass--regular在細節照片上使用 thick
選單、彈出視窗或指令列.gs-menu.glass32px 模糊於 ~86% 填充,0.5px 頂部高光
對話框.gs-veil + .gs-dialog.glass.glass--thick確認金錢、刪除或簽署時使用 glass--solid;玻璃鍍鉻層在下方時使用 gs-veil--flat + .gs-modal-open
提示或底部工作表.gs-toast.glass.glass--regular / .gs-sheet.glass.glass--thickgs- 類別僅負責佈局;材質來自 .glass
內容在橫條下方淡出漸進模糊(配方 2)每個視圖一個滾動邊緣效果
標題出現或數值變化模糊揭示(4)/ 2px 交叉淡入淡出(4b)每個畫面一次
頁面被藝術圖顏色浸染藝術圖背景,烘焙(配方 8)固定玻璃下絕不使用即時模糊
Tailwind v4 專案glass.css + glass.tailwind.css(glass-blur-24,…)Tailwind 自身的 backdrop-blur-* 在 Sonoma Safari 上會失效
原生應用(SwiftUI、Flutter、React Native、Compose)references/native.md使用相同方法,各工具包的材質
照片、影片或設計編輯器的鍍鉻層.glass.glass--thick.glass--neutral中性:無飽和度提升,玻璃不得重新著色作品

接著:構圖光線(§1)、設定材質(§2)、檢查(§5)。assets/demo.html 以即時控制項展示每種表面;在選擇前開啟該檔案了解每個類別的作用。

1. 先構圖光線

決定每個玻璃表面正後方是什麼,並使其成為該處最明亮的元素。

  • 在玻璃下方放置光源: 光暈、藝術圖中的太陽、產品的顏色、即時 WebGL 元件。模糊的平坦顏色仍是相同的平坦顏色;虛空上方的玻璃只是個灰色盒子。

  • 讓光線越過邊緣。 最好的玻璃一半位於光線上方,一半位於暗處,因此顏色會在其寬度上產生變化。

  • 將邊框朝向光源。 將 --glass-light 設定為光源的顏色(例如夕陽的 255 200 160),並將 --glass-rim-angle 設定為其方向。邊框的亮端朝向光源:

    光源方向左上上右上右右下下左下左
    角度135deg180deg225deg270deg315deg0deg45deg90deg

    在玻璃下方的光源,其角度取其最近邊緣的角度加上約 15deg 朝向中心。此角度是已註冊的 @property,因此可平滑地跟隨指標或滾動(Apple 的高光會隨著光線移動而在材質周圍移動)。

  • 將玻璃塑造成其應有的英雄尺寸。 當玻璃夠大,能顯示其下的場景變化時,它就讀起來像一種材料。Apple 讓較大的玻璃表現得像更厚的玻璃:更深的陰影、更柔和的光散射。

2. 設定材質

使用 assets/glass.css(複製時重新命名 glass 前綴)和 assets/surfaces.css 以取得現成表面,或使用元件:Glass.tsx(React)、Glass.vue(Vue 3)、Glass.svelte(Svelte 5)。請勿手寫一行式的 backdrop-filter 卡片。

使用文字值書寫 backdrop-filter,絕不使用 var(),不帶前綴的寫在前面,-webkit- 寫在最後。 macOS Sonoma 上的 Safari 18 和 26 在使用自訂屬性構建 filter 時會渲染不出模糊(WebKit 297620,未解決),而 Tailwind v4 的 backdrop-blur-* 正是以這種方式構建。同一份錯誤報告顯示,即使使用文字值,不帶前綴的屬性在那裡也會失敗,因此帶前綴的宣告放在最後,Safari 會應用它(Chrome 和 Firefox 會忽略它)。glass.css 對每個厚度 × 色調組合都有文字值的 filter,並透過類別切換;變數適用於填充、尺寸和顏色。

厚度。 根據玻璃承載的文字量來選擇。這些值是透過在 demo.html 中對明亮光源使用對比度檢查器校準的:

類別模糊、變暗在明亮光源上通過 4.5:1?用途
glass--clear16px, 22%否:僅限粗體或大字型文字及圖示Dock、播放按鈕、媒體上的藥丸形按鈕(Apple:清晰需要粗體、明亮的內容和變暗圖層)
glass--thin24px, 30%在最亮光源上不通過控制項、標籤、中等背景上的小卡片
glass--regular32px, 48%在校準頁面上通過;播放器後方的明亮夕陽需要 thick 搭配 0.62播放器、面板、提示:預設值。模糊關閉(僅色調)狀態通常是首先失敗的
glass--thick44px, 56%通過選單、工作表、對話框,以及任何文字繁重的內容
glass--solid4px, 92%通過付款、刪除、簽名(填充約 85% 以上時,大模糊無法被看見,因此使用 4px)

對於淺色玻璃搭配墨水色文字,加上 glass--light;在色彩關鍵的應用(照片、影片和設計編輯器)中使用 glass--neutral:無飽和度提升或色調偏移,因為玻璃不得改變作品的觀感。在細節照片上,從 thick 搭配 --glass-dim: rgb(10 10 18 / 0.65–0.75) 開始(這是顏色,不是數字):鮮豔的玻璃和可讀的文字在此相互拉扯,因此將鮮豔度用在邊框、光澤和玻璃周圍溢散的光線上。

兩層級,如同 iOS 26.1: 一個色調層級(透明度更高,絕不更低)在 prefers-contrast: more(Safari 用戶可達的設定;Safari 沒有 prefers-reduced-transparency)時啟用,透過 glass--tinted,或透過 <html data-glass="tinted"> 全站啟用。glass.css 也處理 Windows 高對比度(實色 Canvas 表面)、列印(純邊框盒子)、螢幕擷取工具的捕捉模式,以及 RTL。

玻璃屬於控制層(導航欄、工具列、Dock、選單、工作表、對話框),浮動於內容之上,絕非內容本身,也絕非玻璃疊玻璃(Apple,WWDC25)。僅對主要操作進行色調處理。符合簡報的風格:沉浸式簡報在主要控制項上採用強光與清晰玻璃;可信賴的簡報(金融、健康)採用厚或實心玻璃及極少光線。

references/material.md 包含其餘資訊:每個圖層的作用及原因、從 Apple.com、Linear、Raycast、Radix、Material 和 Fluent 讀取的預設、Apple 完整的 WWDC25 規則列表,以及風格表。

3. 標誌性效果

references/recipes.md 中的程式碼:

  • 液態玻璃折射: 邊框像透鏡一樣彎曲後方的內容,可選色差(0.6–1)。兩個版本:liquid-glass.ts/.js 可折射任何頁面內容,但僅限於 Chromium(Safari 對 SVG 背景 filter 不繪製任何東西,WebKit 245510);liquid-glass-webgl.js 可折射頁面自身繪製的背景(一個 <canvas>、<img> 或 <video>,例如英雄壁紙),在每個瀏覽器中都有效,已在 Chromium 和 WebKit 中驗證渲染。在其他地方,霜凍材料本身必須看起來完整。折射和霜凍之間需要取捨:彎曲透過清晰玻璃顯示(blur 0–6);霜凍(12–20)從邊緣到邊緣都霜凍,彎曲變成了柔和的色彩偏移。清晰適用於單詞或圖示,霜凍適用於句子,文字在邊緣內側一個邊框寬度處。絕不用於遮罩上的對話框:那裡沒有尖銳的東西可以彎曲。
  • 黏性橫條的延伸背景:顏色在內容滾入玻璃下方之前就已到達。gs-bar--on-scroll 使用純 CSS(滾動驅動動畫)讓橫條僅在滾動後才呈現玻璃質感。
  • 漸進模糊: 內容在橫條下方溶解,而非被切斷。在 CSS 獲得 linear-blur() 之前(已提議,csswg #13285),疊加遮罩圖層是唯一方法。
  • 模糊橋接交叉淡入淡出: 在狀態交換期間使用 2px 模糊(Emil Kowalski)。
  • 模糊揭示: 從 blur(8px) 開始,僅一次,作為出現的瞬間。
  • 焦點遮罩: 對話框後方的頁面後退 4–8px。
  • 玻璃變形與按壓: 在相鄰玻璃形狀之間的 layoutId 變形(Cursor:0.34 秒,[0.32, 0.72, 0, 1]),以及按壓時的內部光暈脈衝,而非縮放。
  • 合併玻璃形狀: 一個由 goo 濾波器 SVG 遮罩切割的玻璃圖層,使相鄰形狀像 Apple 的玻璃容器一樣以液態頸部連接(配方 11,已在 Chromium 中測試)。
  • 來自影片的環境光暈: 一個 10×6 像素的畫布,對正在播放的影片進行取樣並放大(配方 8b)。
  • 色調處理: 結構性玻璃的色調約 0.03–0.05,色調使用相對顏色 alpha(配方 12)。
  • 景深、速度動態模糊、藝術圖背景(烘焙)、鎖定內容、紋理。

4. 保持快速

先設計外觀,然後遵守以下規則:

  • 長時間或循環動畫應使用 opacity 和 transform,而非模糊半徑。短暫的一次性動畫沒問題:Apple.com 將其選單簾幕的模糊 0 → 20px 動畫設為 320ms。
  • 絕不同時模糊兩個疊加的表面(Apple 的導航欄在彈出視窗開啟時會放棄自身的模糊)。當對話框在玻璃鍍鉻層上方開啟時,將 .gs-modal-open 添加到 <html>(鍍鉻層停止模糊並變為接近不透明),並使用 gs-veil--flat,或從圖像或畫布背景烘焙遮罩。在照片編輯器測試中測量:五個玻璃面板上的一個即時全螢幕遮罩模糊運行在 31fps;沒有它時,60fps。
  • 固定或黏性模糊每個滾動幀都會重新執行。每個畫面一個大的;在其下方烘焙大的模糊圖像(測量:即時模糊從 60 → 22fps,烘焙後回到 60)。
  • 手機:七個 18px 面板在 Mali-G57 上每幀花費約 3.5ms(75–80% 的幀按時完成,而沒有模糊時為 99%)。如果面板超過幾個,請在佈局的電話斷點處放棄 backdrop-filter 並保留半透明填充(或讓 glass-tier.js 根據設備決定)。
  • 預算模糊面積,而非半徑或數量。 此技能測量結果(Chromium,軟體渲染 2×,移動背景,vsync 開啟):在相同總面積下,半徑 4 → 64px 和 1 → 9 個表面沒有差異;模糊 90% 的螢幕降至 51fps(p95 33ms)。這與 Skia 對大面積模糊進行降採樣的情況相符。數值是相對的(一台快速 Mac 在軟體中渲染),排名是可轉移的。
  • 為 html 提供一個不透明的背景色:Safari 隨後會為帶有玻璃的頁面跳過一個額外的全螢幕合成圖層(WebKit 271340:此修復僅對不透明文件跳過該圖層)。
  • 不要在不透明填充下進行模糊。Framer 和 Dia 在實色背景上使用 backdrop-filter,為了一個無人能見的模糊而付出代價。填充約 85% 以上時,使用 ≤ 4px 或不使用(Arc 的提示:85% 和 3px)。
  • 根據設備對玻璃進行分級:assets/glass-tier.js 根據設備記憶體、Save-Data、幀率探測(iOS 低耗電模式會刻意將動畫限制在 30fps)和 Compute Pressure,將 data-glass-tier 設為 full、lite(一個 8px 模糊)或 flat(無模糊,更多色調),如同 Android、Fluent 和 Apple 在弱電或節能設備上放棄模糊的方式。
  • 在影片上方,不要模糊正在播放的影片:將其取樣到一個小畫布中(配方 8b),並在全螢幕模式下放棄玻璃。Safari 永遠不會將影片像素傳遞給 SVG filter。
  • 背景根:一個父級元素,其 opacity < 1、filter、遮罩、clip-path、mix-blend-mode、backdrop-filter 或 will-change 應用於其中任何一個,都會使內部的玻璃扁平化。這包括 Framer Motion 父級淡入淡出和玻璃水滴父級上的 goo filter。應淡入淡出玻璃本身;在完成時清除揭示 filter(filter: none)。
  • backdrop-filter 會使元素成為 position: fixed 子元素的包含塊:入口選單、提示和對話框應脫離玻璃容器。
  • 祖先上持續存在的 view-transition-name 會在 Chromium 中使玻璃扁平化;僅在轉換期間設定名稱。包裝器上的框架淡入淡出轉換也會產生相同效果:應淡入淡出玻璃本身。
  • 對帶有玻璃的頁面進行截圖、OG 圖像或 PDF 生成時:html2canvas、Satori 和列印會丟失或破壞模糊。在擷取期間將 data-glass-capture 設於 <html>(glass.css 會使玻璃變不透明),並參閱 quirks.md。

5. 檢查(必要)

在將玻璃視為完成之前,運行以下所有檢查:

  1. 霜凍檢查: node <skill>/scripts/frost-check.mjs <url or file.html> 測量每個玻璃表面模糊移除了多少細節(模糊開啟 vs 關閉)。NO BLUR 表示存在背景根、前綴或 var() 問題,或液態玻璃映射過時;FLAT 表示背後沒有可測量的內容。若要以肉眼檢查,請將粗體紅色文字放在玻璃後方,在 deviceScaleFactor: 1 下擷取並放大。
  2. 對比度,在最差背景上測量: node <skill>/scripts/glass-contrast.mjs <url or file.html>。將其指向最明亮的現實狀態,而非漂亮的預設狀態:最亮的照片、白天的幀、曝光 +2 EV 的圖像、滾動到橫條下方的白色頁面部分(添加 URL 參數或測試狀態,以便腳本可以載入)。在某個編輯器上,預設照片以 8.7:1 通過,而一個明亮的幀以 4.4:1 失敗。也要檢查手機寬度(--width 390 --height 844)。它會隱藏文字,找出每行下方最亮和最暗的像素,並在三個狀態下檢查 4.5:1(3:1 用於大字型):正常、僅色調(模糊關閉,如同 iOS 低耗電模式顯示的狀態)和 prefers-contrast: more。透過將厚度提高一級、提高 --glass-dim 中的 alpha 值來修復失敗(例如

…

[翻譯時已截斷]

相關技能