DroidGlass : Verre Liquide pour toute plateforme
Le verre liquide est le matériau d'Apple qui réfracte ce qui se trouve en dessous. Près du bord, l'arrière-plan est tiré vers le centre comme une lentille épaisse, un léger arc-en-ciel apparaît sur le bord, un fin liseré de surbrillance longe la bordure, et au toucher, le verre gonfle, s'étire vers le doigt et rebondit. Le glassmorphisme ordinaire ne fait que flouter l'arrière-plan sous une brume blanche, ce qui lui donne un aspect bon marché et mat.
La règle principale du bon goût : le verre est transparent. Si vous ne pouvez pas lire ce qui se trouve derrière, il s'agit de verre dépoli iOS 7, pas de verre liquide. Ajoutez du flou et une surface uniquement là où le texte repose sur le verre.
Indiquez honnêtement à l'utilisateur où se situent les limites. Le matériau réel n'existe que sur iOS, iPadOS et macOS 26 et ultérieurs ; tout le reste est une imitation. Sur Android, Backdrop offre une imitation complète (la lentille nécessite Android 13). Sur le Web, la réfraction ne fonctionne que dans Chromium (Chrome, Edge, Samsung Internet, Android WebView, Electron). Safari, Firefox et tous les navigateurs sur iOS obtiennent un léger flou avec les mêmes surbrillances, ombres et ressorts, mais sans déformation.
Flux de travail
- Identifiez la plateforme.
- SwiftUI, UIKit, AppKit, WidgetKit sur iOS/macOS 26 : API système, voir
references/apple.md. N'imitez rien. - Jetpack Compose ou Compose Multiplatform :
references/android-compose.md. N'écrivez pas de shaders à partir de zéro, utilisez la bibliothèque. - Web, Electron, Android WebView, Capacitor, Cordova, Tauri sur Windows/Android : le moteur dans
assets/, voirreferences/web.md. Dans WebView et Electron, la réfraction fonctionne toujours. - React Native, Flutter : pas d'équivalent système. Pour React Native, suggérez une couche WebView ou un module natif sur Backdrop ; pour Flutter,
BackdropFiltercomme solution de repli et unFragmentProgramavec les formules dereferences/math-and-motion.md. Vérifiez vous-même les packages actuels, cette compétence ne les liste pas.
- SwiftUI, UIKit, AppKit, WidgetKit sur iOS/macOS 26 : API système, voir
- Choisissez 1 à 3 éléments de la couche de contrôle : barre d'onglets, bouton flottant, barre supérieure, feuille, menu, curseur ou pouce de toggle au toucher. Ne rendez pas le contenu en verre.
- Choisissez une recette dans le tableau ci-dessous. N'inventez pas de paramètres : les valeurs proviennent du catalogue de Kyant et ont été vérifiées à l'écran.
- Vérifiez l'arrière-plan sous le verre. Le verre n'est visible que sur un contenu riche : photos, couleurs, graphiques, texte en défilement. Sur une page blanche unie, le verre transparent disparaît presque : mettez de la couleur derrière, ajoutez une couche de fondu, ou supprimez le verre à cet endroit.
- Construisez à partir de la bonne référence et vérifiez par vous-même en utilisant la liste de contrôle à la fin. Ne remettez pas le travail sans captures d'écran au repos, en pression et en glissement.
Règles d'Apple
D'après les Human Interface Guidelines, WWDC25 « Meet Liquid Glass » et les compétences communautaires populaires. Enfreindre l'une d'elles trahit immédiatement l'imitation.
- Seulement la couche de navigation et de contrôle qui flotte au-dessus du contenu. Les listes, lignes, cartes et l'arrière-plan de la page ne sont pas en verre. Exception : les pouces de toggle et de curseur deviennent en verre lorsqu'ils sont touchés.
- Pas de verre sur du verre. Les éléments au-dessus du verre sont séparés par un remplissage et une opacité. Exception de Backdrop : un pouce de curseur sur une feuille en verre réfracte la feuille (
exportedBackdrop) : une lentille sur une autre, pas deux plaques mates. - Utilisez-en peu. Seulement les contrôles les plus importants.
- Le verre est incolore. La couleur et la luminosité proviennent de l'arrière-plan : clair sur clair, sombre sur sombre, la surbrillance est teintée par l'arrière-plan. Pas de surface blanche ou grise propre.
- Deux modes, ne les mélangez pas. Clair (boutons, pouces, loupe, une barre d'onglets regroupée avec un bouton) : flou 0 à 4, pas de surface. Régulier (feuille, dialogue avec texte long) : flou 8 à 16 ; ajoutez une surface de 40 à 60 % uniquement si le texte est illisible sans elle.
- Teintez uniquement l'action principale.
- Forme. Capsule : rayon égal à la moitié de la hauteur. Coins concentriques : rayon intérieur égal au rayon du conteneur moins le remplissage.
- Plus grand signifie plus épais : les grands éléments ont un bord incurvé plus large et une réfraction plus profonde.
- Ne laissez jamais de contenu reposer à moitié sous le verre.
- Liquide en mouvement. À la pression, le verre grandit de quelques pixels et s'incline vers le doigt avec amortissement ; un pouce gonfle de 1,5× et s'étire en goutte à grande vitesse. Tout fonctionne avec des ressorts, pas de transitions linéaires.
Recettes (valeurs du catalogue de Kyant)
| Élément | Effets | Surbrillance et ombres | Toucher |
|---|---|---|---|
| Bouton, capsule 48 dp | vibrancy, flou 2, lens(12, 24) | Default, ombre par défaut | lueur ; croît de 4 dp en hauteur ; décalage min × tanh(0.05 × décalage / min) ; s'étire jusqu'à 4 dp vers le doigt |
| Bouton teinté | même + teinte | identique | identique |
| Bouton avec surface | même + blanc 30% | identique | identique |
| Pouce de toggle 40×24 sur une piste 64×28 | au repos blanc 100% et flou 8 ; pressé p : flou 8(1-p), lens(5p, 10p, dispersion), blanc (1-p) | Ambient, largeur /1.5, alpha p ; rayon d'ombre 4, 5% ; rayon d'ombre intérieure 4p, alpha p | échelle à 1.5 ; la piste apparaît à travers la lentille, réduite à 0.75 |
| Pouce de curseur 40×24, piste 6 dp | comme le toggle, lens(10p, 14p) | comme le toggle | échelle à 1.5 ; taper sur la piste saute au point touché |
| Barre d'onglets 64 dp | vibrancy, flou 8, lens(24, 24), surface #FAFAFA 40% (sombre #121212 40%) | Default | à la pression, le panneau entier croît de 16 dp en largeur (les étiquettes avec) et s'incline jusqu'à 4 dp après le doigt |
Copie de ligne masquée 56 dp (tabsBackdrop) | le même panneau dans la couleur d'accent, alpha(0) : vibrancy, flou 8, lens(24p, 24p), surface du panneau | Default avec alpha p, InteractiveHighlight | icônes et étiquettes ×1.2 à la pression (LocalLiquidBottomTabScale est défini uniquement sur cette ligne) |
| Pouce de barre d'onglets 56 dp | au repos gris 10% ; pressé lens(10p, 14p, dispersion) sur l'arrière-plan et la copie de ligne (rememberCombinedBackdrop(backdrop, tabsBackdrop)) | Default, ombre et ombre intérieure 8p avec alpha p | échelle 78/56 ; à l'intérieur de la goutte, une « barre sous la barre » : son propre élément en accent, agrandi, voisins visibles aux bords |
| Dialogue, rayon 48 | luminosité 0.2 (clair), saturation 1.5, flou 16 (sombre 8), lens(24, 48, profondeur) | Plain | assombrit l'arrière-plan #29293A 23% (sombre #121212 56%) |
| Tuile du centre de contrôle | vibrancy, lens(24, 48, profondeur) | Default, angle d'inclinaison de l'appareil, atténuation 2 | entrée : décalage 48 dp et alpha |
| Loupe 128×96 | lens(8, 24, profondeur, dispersion), arrière-plan zoomé 1.5× | ombre intérieure 16 | glisser |
Les ressorts, les gestes et les mathématiques partagées pour chaque plateforme se trouvent dans references/math-and-motion.md.
Web et Android WebView en une minute
Copiez les trois fichiers de assets/ dans le projet sans les modifier et configurez-les avec des options. Ne réécrivez pas le moteur.
<link rel="stylesheet" href="liquid-glass.css">
<button id="buy">Acheter</button>
<div id="wifi" aria-label="Wi-Fi"></div>
<div id="volume" style="width: 280px"></div>
<nav id="tabs" style="position: fixed; left: 16px; right: 16px; bottom: 16px"></nav>
<script src="liquid-glass.js"></script>
<script src="liquid-ui.js"></script>
<script>
LiquidUI.button(document.getElementById('buy'), { tint: '#0088FF', onClick: () => {} });
LiquidUI.toggle(document.getElementById('wifi'), { checked: true, onChange: (on) => {} });
LiquidUI.slider(document.getElementById('volume'), { value: 0.4, onInput: (v) => {}, label: 'Volume' });
const tabs = LiquidUI.tabs(document.getElementById('tabs'), {
items: [{ label: 'Accueil', icon: '<svg>…</svg>' }, { label: 'Recherche', icon: '<svg>…</svg>' }],
index: 0, onChange: (i) => {},
});
// fusion : une barre et un bouton à côté d'elle fusionnent en une seule goutte ; les boutons extensibles voisins le font automatiquement
// LiquidUI.blob([tabs.panel, fab], { blur: 3.5 }); LiquidUI.autoMerge(document.body); après un changement d'écran LiquidUI.sweep()
// verre nu pour votre propre balisage
const g = LiquidGlass.attach(document.getElementById('panel'), { radius: 28, blur: 8, refractionHeight: 24, refractionAmount: 24, surface: 'rgb(250 250 250 / .4)' });
// g.set({ lens: 0..1, blur, surface, highlightAlpha, angle, shadowAlpha, innerShadow, press: { p, x, y }, tx, ty, sx, sy, sleep })
</script>
| Fichier | Ce que c'est |
|---|---|
assets/liquid-glass.js | moteur de verre : cartes de réfraction, dispersion, surbrillance, ombres, groupes de fusion (LiquidGlass) |
assets/liquid-glass.css | styles de base, solutions de repli pour l'accessibilité, styles de composants |
assets/liquid-ui.js | ressorts, gestes et composants : bouton, toggle, curseur, onglets, loupe, blob, autoMerge (LiquidUI) |
assets/demo.html | tous les composants sur une seule page ; ouvrez-la pour voir à quoi ressemble un rendu correct |
Avant de modifier quoi que ce soit ou de l'intégrer dans une application, lisez references/web.md : il liste les faits Chromium qui cassent presque toutes les implémentations (backdrop roots, position: fixed, touch-action, arrière-plans du body) et toutes les options. Quand quelque chose semble anormal, allez dans references/troubleshooting.md ; quand quelque chose saccade, references/performance.md.
Cinq choses à ne jamais se tromper sur le Web :
- L'arrière-plan doit être un élément réel. Un arrière-plan défini sur
bodyqui se propage à la racine n'est pas vu parbackdrop-filter. - N'animez jamais
opacity,filter,maskouclip-pathsur un ancêtre du verre (un fondu d'écran, par exemple) : cela devient une backdrop root et le verre perd son arrière-plan et s'assombrit. Déplacez les écrans avectransformet révélez-les avec une couche de voile au-dessus. - Ne définissez pas votre propre
transformsur un élément en verre ; écrivez le décalage de base dans--lg-base. - Les boutons extensibles nécessitent
touch-action: none(par défaut). Avecpan-y, le navigateur vole le glissement et envoiepointercancel. - Dans un Android WebView, désactivez l'appui long et les retours haptiques :
setOnLongClickListener(v -> true)etsetHapticFeedbackEnabled(false).
Accessibilité et lisibilité
- Le moteur Web respecte
prefers-reduced-transparency: reduce(verre désactivé, surface presque opaque ; Chrome et Edge 119+, pas encore Safari ou Firefox),prefers-contrast: more(surface et bordure pleines) etprefers-reduced-motion(les ressorts deviennent instantanés). - Les composants reçoivent des rôles et la prise en charge du clavier : bouton (Entrée, Espace),
switch(Entrée, Espace),slider(flèches),tablist(flèches gauche et droite). Étiquetez-les avecaria-labelouaria-labelledby. - Sur Android, définissez
semantics { role = Role.Switch }et les étiquettes, comme dans le catalogue de Kyant ; sur Android ancien, vérifiez que la couleuronDrawSurfaceest lisible sans effets. - Vérifiez le contraste du texte sur le contenu le plus clair et le plus chargé qui peut se retrouver sous le panneau : 4.5:1 pour le corps du texte. Si cela échoue, ajoutez du flou et une surface ou déplacez le contenu. Une ombre de texte ne résout pas le problème.
À ne pas faire
- Une surface blanche translucide plus
blur(10px)sur les boutons : c'est du verre dépoli, pas du verre liquide. La densité n'est acceptable que pour les barres d'onglets, les feuilles et les dialogues avec texte. - Des cartes de contenu en verre et un arrière-plan de page en verre.
- De la dispersion sur de grands panneaux : coûteuse et criarde.
- Une bordure blanche épaisse au lieu d'un fin liseré de surbrillance.
- La même teinte sur chaque bouton.
- Des
transitions linéaires au lieu de ressorts pour la pression et le glissement. - L'animation de
widthetheightdu verre sur le Web : la carte est reconstruite à chaque image. Animez l'échelle. - Des étapes de filtre lourdes (flou, déplacement, convolution, morphologie) sous un résultat que plusieurs étapes lisent : Chrome les calcule une fois par branche et les téléphones gèlent.
- Peindre le verre avec votre propre surface « pour l'esthétique ». Seuls les panneaux denses avec du texte reçoivent une surface.
- Écrire vos propres shaders Compose alors que Backdrop convient.
- Les symboles SF et les logos Apple en dehors des plateformes Apple : leur licence l'interdit. Utilisez des jeux d'icônes ouverts.
- Appeler le résultat Web ou Android « Liquid Glass officiel ». C'est une imitation dans son style.
Liste de contrôle avant la remise
Captures d'écran à l'échelle 2, regardez-les vous-même :
- Au repos sur une grille ou du texte : les lignes sont visibles à travers le verre transparent, tirées vers le centre au bord, la surbrillance est fine. Sur fond blanc, le verre est séparé de la page par l'ombrage du bord et l'ombre ; sur fond sombre, par une ligne lumineuse sur le côté éclairé.
- Pression : le bouton a grandi et s'incline vers le doigt ; un pouce est gonflé et transparent, la piste se voit à travers, l'arc-en-ciel est doux, pas de trous colorés.
- Glissement : un pouce s'étire à vitesse et se dégonfle seulement après son arrivée ; la barre d'onglets s'incline et revient. La goutte de la barre d'onglets sur les onglets de bord et près du bord inférieur de l'écran : son propre élément à l'intérieur, les voisins aux bords, pas de zones noires, de bandes ou de pièces en miroir.
- Tirage : un bouton suit le doigt sur un élastique et rebondit ; une barre tirée vers un bouton voisin fusionne avec lui, le pont grandit à mesure qu'ils s'approchent, pas de couture ni de double bord ; au repos et sur une simple pression, ils restent séparés ; un tap normal fonctionne, un bouton tiré loin et relâché ne se déclenche pas.
- Sur une photo et sur fond blanc : la couleur sous le verre est riche, le texte est lisible.
- Thème sombre.
- Solution de repli : simulez le moteur (user agent Safari et
navigator.userAgentDatavide) et vérifiez que les éléments ne se sont pas transformés en trous et que les icônes sous le pouce de la barre d'onglets ne sont pas floues. - Redimensionnement et rotation : la carte est reconstruite, pas de marches d'escalier sur le bord.
- Transitions d'écran : capturez plusieurs images consécutives pendant qu'un écran entre ; les boutons en verre ne sont jamais plus sombres qu'au repos, les couches de fusion restent exactement sous leurs boutons après l'animation.
- Aucune erreur dans la console et pas de blocage : après une rafale de taps, la page répond aussi rapidement. Une boucle d'animation, pas de
requestAnimationFrames errants. - Pas plus de 3 grands éléments en verre sur un écran de téléphone, pas de verre sur du verre, pas de contenu en verre.
Si quelque chose échoue, corrigez-le d'abord, puis montrez-le à l'utilisateur.
Sources
- Kyant0/AndroidLiquidGlass, bibliothèque Backdrop et catalogue (Apache-2.0) : formules de lentille, dispersion, surbrillance et ombre, recettes de composants, constantes de ressorts et de gestes. Le moteur Web est un portage de ce schéma.
- Apple : Human Interface Guidelines « Materials », « Adopting Liquid Glass », WWDC25 « Meet Liquid Glass ».
- kube.io, « Liquid Glass in the Browser » : réfraction via
feDisplacementMapà l'intérieur debackdrop-filter. - iyinchao/liquid-glass-studio (MIT) : fusion de formes via
smin, réfraction de Snell, verre incolore avec une haute...
… [tronqué pour la traduction]