Que fait Blur craft ?
La plupart des verres sur le Web ont l'air bon marché pour trois raisons : rien d'intéressant ne se trouve derrière, le matériau est une seule teinte plate avec un seul flou, et le texte dessus est d'un gris boueux que personne n'a mesuré. Le verre qui a l'air cher (les matériaux d'Apple, visionOS, les meilleurs sites de produits) fait trois choses correctement, dans cet ordre :
- La lumière derrière le verre est conçue. Le verre montre ce qui se trouve derrière, donc placez une lumière là.
- Le verre est un matériau, pas une teinte. Saturation du flou, remplissage dégradé, rebord lumineux, éclat spéculaire, profondeur, grain. C'est dans
assets/glass.css. - Le texte est mesuré. Le verre translucide change le contraste avec chaque pixel derrière lui, donc le contraste est vérifié au pire endroit, par script, dans trois états (
scripts/glass-contrast.mjs).
Parcours rapide
| Je construis… | Utilisez | Remarques |
|---|---|---|
| Une barre de navigation collante | .gs-bar (surfaces.css) | Fond étendu ; ajoutez gs-bar--on-scroll pour la rendre vitreuse seulement après le défilement |
| Un lecteur, un dock ou une pilule au-dessus d'une œuvre | .gs-dock.glass.glass--clear + liquid-glass.js | Liquid Glass clair, un par écran ; ton adaptatif si l'œuvre change beaucoup |
| Une carte flottante ou un panneau avec du texte | .glass.glass--regular | thick au-dessus d'une photo détaillée |
| Un menu, un popover ou une barre de commandes | .gs-menu.glass | 32px sur ~86 %, surbrillance supérieure de 0,5 px |
| Un dialogue | .gs-veil + .gs-dialog.glass.glass--thick | glass--solid quand il confirme un paiement, une suppression ou une signature ; gs-veil--flat + .gs-modal-open quand du chrome en verre se trouve en dessous |
| Un toast ou une feuille inférieure | .gs-toast.glass.glass--regular / .gs-sheet.glass.glass--thick | Les classes gs- concernent uniquement la mise en page ; le matériau vient de .glass |
| Du contenu qui disparaît sous une barre | Flou progressif (recette 2) | Un effet de bord de défilement par vue |
| Un titre qui arrive ou une valeur qui change | Révélation par flou (4) / fondu enchaîné de 2 px (4b) | Une fois par écran |
| Une page baignée dans les couleurs de l'œuvre | Fond d'œuvre, cuit (recette 8) | Jamais de flou en direct sous du verre fixe |
| Un projet Tailwind v4 | glass.css + glass.tailwind.css (glass-blur-24, …) | Les backdrop-blur-* de Tailwind cassent sur Sonoma Safari |
| Une application native (SwiftUI, Flutter, React Native, Compose) | references/native.md | Même méthode, le matériau de chaque boîte à outils |
| Du chrome pour un éditeur photo, vidéo ou de design | .glass.glass--thick.glass--neutral | Neutre : pas d'augmentation de saturation, le verre ne doit pas recolorer le travail |
Ensuite : composez la lumière (§1), définissez le matériau (§2), vérifiez (§5). assets/demo.html montre chaque surface en direct avec des contrôles ; ouvrez-le pour voir ce que chaque classe fait avant de choisir.
1. Composer la lumière d'abord
Décidez ce qui sera directement derrière chaque surface vitreuse, et faites-en la chose la plus lumineuse à cet endroit.
-
Placez une lumière sous le verre : une lueur, le soleil dans l'œuvre, les couleurs du produit, un composant WebGL en direct. Une couleur plate floutée est la même couleur plate ; le verre au-dessus d'un vide est une boîte grise.
-
Laissez la lumière traverser le bord. Le meilleur verre est à moitié sur la lumière et à moitié sur l'obscurité, donc la couleur change sur sa largeur.
-
Dirigez le rebord vers la lumière. Définissez
--glass-lightà la couleur de la lumière (255 200 160pour un coucher de soleil) et--glass-rim-angleà sa direction. L'extrémité lumineuse du rebord fait face à la lumière :Lumière depuis haut-gauche haut haut-droite droite bas-droite bas bas-gauche gauche angle 135deg 180deg 225deg 270deg 315deg 0deg 45deg 90deg Une lumière sous le verre prend l'angle de son bord le plus proche plus ~15 degrés vers le centre. L'angle est une
@propertyenregistrée, donc il peut suivre le pointeur ou le défilement en douceur (les surbrillances d'Apple voyagent autour du matériau à mesure que la lumière se déplace). -
Dimensionnez le verre comme le héros qu'il est. Il se lit comme un matériau quand il est assez grand pour montrer le changement de scène en dessous. Apple fait en sorte que le verre plus grand se comporte comme un verre plus épais : ombre plus profonde, dispersion plus douce.
2. Définir le matériau
Utilisez assets/glass.css (renommez le préfixe glass lors de la copie) et assets/surfaces.css pour les surfaces prêtes à l'emploi, ou les composants : Glass.tsx (React), Glass.vue (Vue 3), Glass.svelte (Svelte 5). N'écrivez pas à la main une carte backdrop-filter sur une ligne.
Écrivez backdrop-filter avec des valeurs littérales, jamais var(), non préfixé en premier et -webkit- en dernier. Safari 18 et 26 sur macOS Sonoma ne rendent aucun flou lorsque le filtre est construit à partir de propriétés personnalisées (WebKit 297620, ouvert), et backdrop-blur-* de Tailwind v4 est construit exactement de cette façon. Le même rapport de bogue montre que la propriété non préfixée échoue même avec une valeur littérale, donc la déclaration préfixée va en dernier, là où Safari l'applique (Chrome et Firefox l'ignorent). glass.css a un filtre littéral par épaisseur × ton et change par classe ; les variables sont acceptables pour les remplissages, les atténuations et les couleurs.
Épaisseur. Choisissez en fonction de la quantité de texte que le verre porte. Les valeurs ont été calibrées avec le vérificateur de contraste sur une lumière vive dans demo.html :
| Classe | Flou, atténuation | Passe 4.5:1 sur une lumière vive ? | Utiliser pour |
|---|---|---|---|
glass--clear | 16px, 22% | Non : texte en gras ou grand et icônes uniquement | Docks, boutons de lecture, pilules sur des médias (Apple : clair nécessite un contenu gras et lumineux et une couche d'atténuation) |
glass--thin | 24px, 30% | Pas sur la lumière la plus vive | Contrôles, puces, petites cartes sur des fonds modérés |
glass--regular | 32px, 48% | Oui sur la page de calibration ; un coucher de soleil lumineux derrière un lecteur nécessitait thick à 0.62 | Lecteurs, panneaux, toasts : la valeur par défaut. L'état sans flou (teinte uniquement) est généralement celui qui échoue en premier |
glass--thick | 44px, 56% | Oui | Menus, feuilles, dialogues, tout ce qui est textuel |
glass--solid | 4px, 92% | Oui | Paiements, suppressions, signatures (au-dessus de ~85% de remplissage, un gros flou ne peut pas être vu, donc 4px) |
Ajoutez glass--light pour un verre clair avec du texte à l'encre, et glass--neutral dans les applications critiques pour les couleurs (éditeurs photo, vidéo et de design) : pas d'augmentation de saturation ni de changement de ton, car le verre ne doit pas modifier l'apparence du travail. Au-dessus d'une photo détaillée, commencez par thick avec --glass-dim: rgb(10 10 18 / 0.65–0.75) (c'est une couleur, pas un nombre) : le verre vif et le texte lisible se tirent l'un contre l'autre, donc dépensez la vivacité sur le rebord, l'éclat et la lumière qui se répand autour du verre.
Deux niveaux, comme iOS 26.1 : un niveau teinté (plus d'opacité, jamais moins) s'active pour prefers-contrast: more (le paramètre accessible aux utilisateurs de Safari ; Safari n'a pas de prefers-reduced-transparency), avec glass--tinted, ou sur tout le site avec <html data-glass="tinted">. glass.css gère également le contraste élevé de Windows (surface Canvas solide), l'impression (boîte bordée simple), le mode de capture pour les outils de capture d'écran, et RTL.
Le verre appartient à la couche de contrôle (navigation, barres d'outils, docks, menus, feuilles, dialogues) flottant au-dessus du contenu, jamais au contenu lui-même, et jamais du verre sur du verre (Apple, WWDC25). Teintez uniquement l'action principale. Respectez le registre du brief : les briefs immersifs prennent une lumière forte et un verre clair sur le contrôle principal ; ceux de confiance (finance, santé) prennent un verre épais ou solide et pratiquement pas de lumière.
references/material.md contient le reste : ce que fait chaque couche et pourquoi, les préréglages lus depuis Apple.com, Linear, Raycast, Radix, Material et Fluent, la liste complète des règles WWDC25 d'Apple, et la table des registres.
3. Effets signature
Code dans references/recipes.md :
- Réfraction Liquid Glass : le rebord plie ce qui se trouve derrière comme une lentille, avec aberration chromatique optionnelle (0.6–1). Deux versions :
liquid-glass.ts/.jsréfracte n'importe quel contenu de page mais uniquement dans Chromium (Safari ne peint rien pour les filtres de fond SVG, WebKit 245510) ;liquid-glass-webgl.jsréfracte un fond que la page dessine elle-même (un<canvas>,<img>ou<video>, comme un fond d'écran héros) dans tous les navigateurs, rendu vérifié dans Chromium et WebKit. Ailleurs, le matériau givré doit avoir l'air fini par lui-même. La réfraction et le givre sont un compromis : la courbure se voit à travers un verre clair (blur0–6) ; le givré (12–20) givre de bord en bord et la courbure devient un léger changement de couleur. Clair pour un mot ou une icône, givré pour des phrases, texte à une largeur de bordure à l'intérieur du bord. Jamais sur un dialogue au-dessus d'un voile : il ne reste rien de net à plier. - Fond étendu pour les barres collantes : la couleur arrive dans le verre avant que le contenu ne défile en dessous.
gs-bar--on-scrollrend la barre vitreuse seulement après le défilement, en CSS pur (animations pilotées par le défilement). - Flou progressif : le contenu se dissout sous une barre au lieu d'être coupé. Les couches masquées empilées sont la seule solution jusqu'à ce que CSS obtienne
linear-blur()(proposé, csswg #13285). - Fondu enchaîné par flou : flou de 2 px lors d'un échange d'état (Emil Kowalski).
- Révélation par flou : de
blur(8px), une fois, comme moment d'arrivée. - Voile de mise au point : la page derrière un dialogue recule de 4–8 px.
- Morphose et pression du verre : une morphose
layoutIdentre des formes de verre proches (Cursor : 0.34s,[0.32, 0.72, 0, 1]) et une impulsion de lueur interne lors de la pression, pas une échelle. - Fusion de formes de verre : une couche de verre découpée par un masque SVG filtré goo, de sorte que les formes proches se rejoignent avec un col liquide comme les conteneurs en verre d'Apple (recette 11, testée dans Chromium).
- Lueur ambiante à partir d'une vidéo : un échantillon 10×6 canvas de la vidéo en cours de lecture, mis à l'échelle (recette 8b).
- Teinture : chroma ~0.03–0.05 pour le verre structurel, alpha de couleur relative pour les teintes (recette 12).
- Profondeur de champ, flou de mouvement de vélocité, fond d'œuvre (cuit), contenu verrouillé, grain.
4. Gardez-le rapide
Concevez d'abord l'aspect, puis maintenez ceci :
- Les animations longues ou en boucle agissent sur l'opacité et la transformation, pas sur le rayon de flou. Les animations courtes et uniques sont acceptables : Apple.com anime le flou du rideau de son menu de 0 à 20 px sur 320 ms.
- Ne floutez jamais deux surfaces empilées en même temps (la navigation d'Apple abandonne son propre flou pendant qu'un volet déroulant est ouvert). Lorsqu'un dialogue s'ouvre au-dessus d'un chrome en verre, ajoutez
.gs-modal-openà<html>(le chrome cesse de flouter et devient presque opaque) et utilisezgs-veil--flat, ou cuisinez le voile à partir d'une image ou d'un fond de canvas. Mesuré sur un test d'éditeur photo : un flou de voile plein écran en direct sur cinq panneaux de verre fonctionnait à 31 ips ; sans, 60. - Un flou fixe ou collant se ré-exécute à chaque image de défilement. Un grand par écran ; cuisinez de grandes images floutées en dessous (mesuré : 60 → 22 ips avec un flou en direct, retour à 60 avec cuisson).
- Téléphones : sept panneaux de 18 px coûtent environ 3,5 ms par image sur un Mali-G57 (75–80 % des images à l'heure contre 99 % sans). Avec plus de quelques panneaux, abandonnez
backdrop-filterau point de rupture téléphone de votre mise en page et conservez le remplissage translucide (ou laissez glass-tier.js décider par appareil). - Budgetisez la zone floutée, pas le rayon ou le nombre. Mesuré pour cette compétence (Chromium, rendu logiciel à 2×, fond en mouvement, vsync activé) : rayon 4 → 64 px et 1 → 9 surfaces à la même surface totale n'ont fait aucune différence ; flouter 90 % de l'écran a chuté à 51 ips (p95 33 ms). Cela correspond au sous-échantillonnage des gros flous par Skia. Les chiffres sont relatifs (un Mac rapide rendant en logiciel), le classement est ce qui se transfère.
- Donnez à
htmlune couleur de fond opaque : Safari saute alors une couche de composition plein écran supplémentaire pour les pages avec du verre (WebKit 271340 : le correctif saute la couche uniquement pour les documents opaques). - Ne floutez pas sous un remplissage opaque. Framer et Dia livrent
backdrop-filtersur des arrière-plans unis, payant pour un flou que personne ne peut voir. Au-dessus de ~85 % de remplissage, utilisez ≤ 4 px ou rien (les toasts d'Arc : 85 % et 3 px). - Hiérarchisez le verre par appareil :
assets/glass-tier.jsdéfinitdata-glass-tiersur full, lite (un flou de 8 px) ou flat (pas de flou, plus de teinte) à partir de la mémoire de l'appareil, Save-Data, une sonde de fréquence d'images (le mode basse consommation d'iOS limite intentionnellement l'animation à 30 ips) et Compute Pressure, comme Android, Fluent et Apple abandonnent le flou sur les appareils faibles ou économes en énergie. - Sur une vidéo, ne floutez pas la vidéo en cours de lecture : échantillonnez-la dans un petit canvas (recette 8b) et abandonnez le verre en plein écran. Safari ne transmet jamais les pixels vidéo aux filtres SVG.
- Racine de fond : un parent avec
opacity < 1,filter, un masque,clip-path,mix-blend-mode,backdrop-filterouwill-changesur l'un d'eux aplatit le verre à l'intérieur. Cela inclut une atténuation parente de Framer Motion et un filtregoosur un parent de gouttes de verre. Atténuez le verre lui-même ; effacez les filtres de révélation (filter: none) quand ils se terminent. backdrop-filterfait d'un élément le bloc conteneur pour les enfantsposition: fixed: menus portails, toasts et dialogues hors des conteneurs de verre.- Un
view-transition-namepermanent sur un ancêtre aplatit le verre dans Chromium ; définissez les noms uniquement pendant la transition. Les transitions d'atténuation de framework sur un wrapper font de même : atténuez le verre lui-même. - Prise de captures d'écran, d'images OG ou de PDF de verre : html2canvas, Satori et l'impression abandonnent ou cassent le flou. Définissez
data-glass-capturesur<html>pendant la capture (glass.css rend le verre opaque) et consultez quirks.md.
5. Vérifier (obligatoire)
Exécutez tout ceci avant de considérer le verre comme terminé :
- Vérification du givre :
node <skill>/scripts/frost-check.mjs <url ou fichier.html>mesure, pour chaque surface vitreuse, la quantité de détails que le flou supprime (flou activé vs désactivé). NO BLUR signifie une racine de fond, un préfixe cassé ouvar(), ou une carte Liquid Glass obsolète ; FLAT signifie qu'il n'y a rien derrière à mesurer. Pour vérifier à l'œil, placez du texte rouge gras derrière le verre, capturez àdeviceScaleFactor: 1et agrandissez. - Contraste, mesuré, sur le pire fond :
node <skill>/scripts/glass-contrast.mjs <url ou fichier.html>. Pointez-le vers l'état réaliste le plus lumineux, pas le joli défaut : la photo la plus claire, une image de jour, une image poussée de +2 EV, la section de page blanche qui défile sous la barre (ajoutez des paramètres d'URL ou un état de test pour que le script puisse le charger). Sur un éditeur, la photo par défaut passait à 8.7:1 et une image lumineuse échouait à 4.4:1. Vérifiez également la largeur du téléphone (--width 390 --height 844). Il cache le texte, trouve le pixel le plus lumineux et le plus sombre sous chaque ligne, et vérifie 4.5:1 (3:1 grand) dans trois états : normal, teinte seule (flou désactivé, comme le montre le mode basse consommation d'iOS) etprefers-contrast: more. Corrigez les échecs en augmentant l'épaisseur, en augmentant l'alpha dans--glass-dim(par ex.rgb(10 10 18 / 0.75)au lieu de0.65), ou en augmentant--glass-tint-alphade 0.02 à la fois. N'augmentez jamais le--glass-darkenau-dessus de0.15: au-delà, le verre assombrit le contenu en dessous.