GlassmorphismGratuit

Liquid Glass Refraction

Une plaque de verre qui tord vraiment le décor derrière elle : backdrop-filter enchaîne flou de 8 px, saturation à 180 % et un filtre SVG feDisplacementMap (bruit fractal, décalage jusqu'à 13 px) qui ondule en 22 s, sur quatre taches de couleur en mouvement ; reflet qui suit le pointeur, teintes OKLCH et trois replis (sans flou, mouvement réduit, transparence réduite).

SVG Filterbackdrop-filterOKLCH

Mis à jour le

Liquid Glass

La lumière se réfracte à travers la surface. Bougez la souris.

Survolez ou cliquez la scène pour interagir

Cet effet est gratuit — la catégorie Glassmorphism en contient 47 au total, dont 6 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →

Exemples d'usage

Marée Basse — live à la Salle Mistral

Titre 7 sur 14
4K 47:12 / 1:32:08

① Commandes de lecture sur un concert filmé — plateforme de concerts en ligne

QuandLe spectateur met en pause un concert filmé : les commandes apparaissent dans une plaque de verre posée sur l'image de la scène, mur de LED et faisceaux compris.
PourquoiLa réfraction tord légèrement les points du mur de LED sous la plaque : les commandes se détachent sans bandeau noir qui mangerait l'image, et le reflet qui suit la souris montre que la plaque est un objet qu'on manipule.
RéglagesPlaque de 420 px en bas de la scène, marges réduites (14 × 18 px), titre, barre de progression, trois vrais <button> (précédent, lecture, suivant) dont lecture en aria-pressed ; décor : mur de LED en points nets de 9 px (c'est sur un bord net que la réfraction se voit), trois faisceaux qui balaient en mix-blend-mode: screen, public en ombres chinoises.

Parc naturel des Hautes-Combes

Cascade du Saut-Gris

Accès
Libre · parking 8 h – 20 h
Sentier
1,2 km · 25 min · 90 m de dénivelé

Baignade interdite dans la vasque

En direct · webcam de la cascade

② Horaires d'accès sur la vidéo de la cascade — site d'un parc naturel régional

QuandPage d'un site naturel : la vidéo de la cascade tourne en fond, la plaque de verre donne l'accès, le sentier, la meilleure heure et l'interdiction de baignade.
PourquoiVoir les filets d'eau se tordre derrière le verre fait le lien avec le sujet ; et le repli de transparence réduite donne un fond plein à qui le demande, utile pour des horaires qu'on doit pouvoir lire d'un coup d'œil.
RéglagesPlaque de 232 px sur toute la hauteur à droite, posée à cheval sur la chute : filets d'eau en rayures nettes qui descendent en boucle (1,6 s), coupés par un masque en bandes ; liste en <dl>, avertissement sur fond rouge à 55 %, pastille « En direct » hors du verre ; sur téléphone (scène de 260 px de haut), l'avertissement et la pastille sont masqués pour que la plaque tienne entière.

Le Phare · bar de l'hôtel · signature

Brise d'Iode

  • Gin infusé à la criste marine 4 cl
  • Liqueur de sureau 2 cl
  • Citron vert, orange sanguine 3 cl
  • Tonic, glace taillée top

Servi dès 18 h16 €

③ Carte d'un cocktail signature — bar d'un hôtel

QuandFiche du cocktail de la maison sur le site de l'hôtel : la coupe, ses glaçons et la rondelle de citron vert, la plaque liquide avec les ingrédients, les doses et le prix.
PourquoiLa distorsion évoque le liquide et la glace ; les arêtes des glaçons et les bouteilles du bar ondulent sous la plaque, et le texte reste lisible grâce aux ombres de texte prévues dans le code.
RéglagesPlaque de 252 px à droite, à cheval sur la coupe ; étagère de bouteilles en bandes de couleur nettes derrière la plaque ; ingrédients en liste à pointillés, doses alignées à droite, prix en 20 px.

Comment ça marche

Trois couches dans une scène en 16/10 (.lgr-scene, coins de 14 px, overflow: hidden, isolation: isolate, fond #0b1024). Dessous, .lgr-bg déborde de 10 % et porte quatre taches rose, bleue, verte et violette (46 % de large, filter: blur(28px)) qui dérivent sur une boucle de 14 s décalée pour chacune. Au-dessus, la plaque .lgr-glass — min(72%, 340px) de large marge comprise (box-sizing: border-box), coins de 26 px — a un fond en deux teintes OKLCH presque blanches à 20 % puis 7 %, un liseré blanc à 28 %, une ombre portée et deux reflets intérieurs. Le cœur est sa ligne backdrop-filter: blur(var(--lgr-blur, 8px)) saturate(180%) url(#lgrRefract) : le décor est flouté, saturé, puis déplacé pixel par pixel par le filtre SVG livré dans le HTML.

Le filtre #lgrRefract fabrique un bruit fractal (feTurbulence, fréquences 0,009 et 0,013, deux octaves, graine 7), l'adoucit d'un flou de 1,4, puis s'en sert comme carte de déplacement (feDisplacementMap, scale="26", canaux rouge et vert) : chaque pixel du fond est décalé jusqu'à 13 px selon la valeur du bruit. Une balise <animate> fait permuter les deux fréquences en 22 s, et la distorsion ondule lentement comme une eau. Sur des rayures nettes posées en fond, la mesure dans Chromium est sans appel : les bandes deviennent des vagues sous la plaque. Le reflet est un ::after en dégradé radial de 120 px, blanc à 45 % en mix-blend-mode: screen, centré sur --mx / --my ; le script les recalcule à chaque pointermove sur la plaque (mesuré : pointeur à 80 % / 70 % de la plaque, --mx à 80 %) et les remet à 30 % / 20 % quand le pointeur sort.

Deux choix du code protègent la page où il est collé. Sous prefers-reduced-motion, le script met en pause la <svg> qui contient le filtre (disp.ownerSVGElement), et pas « la première <svg> de la page » : avec un logo animé posé avant l'effet, c'est le logo qui s'arrêterait et la distorsion qui continuerait (mesuré). Ensuite, Firefox et Safari ignorent la liste backdrop-filter entière dès qu'elle contient url() : mesuré par capture d'écran réelle de Firefox 153 et de WebKit 26 sur des rayures, ni flou, ni saturation, ni réfraction — la plaque ne serait qu'un voile blanc. Un bloc @supports (-moz-appearance: none) or (-webkit-backdrop-filter: blur(1px)), vrai seulement dans ces deux moteurs (mesuré : faux dans Chromium), leur donne le flou et la saturation sans le filtre SVG ; Chromium garde la réfraction. Le script cible des id uniques (#lgrScene, #lgrGlass, #lgrDisp) : une seule plaque par page telle quelle. Une variante plus ancienne de cette vitre liquide existe aussi dans la catégorie.

Accessibilité

  • Mouvement réduit : sous prefers-reduced-motion, les taches s'arrêtent, le reflet ne suit plus le pointeur et descend à 50 % d'opacité, et l'ondulation du filtre est mise en pause (mesuré : animationsPaused() vrai sur la <svg> du filtre, faux sur un logo animé voisin). La plaque reste réfractée, mais figée.
  • Transparence réduite : @media (prefers-reduced-transparency: reduce) donne à la plaque un fond plein #14162a, retire le flou et le reflet. Mesuré : la requête répond dans Chromium ; Firefox 153 et WebKit 26 ne connaissent pas encore ce réglage (ni reduce ni no-preference ne répondent), le repli n'y joue donc pas.
  • Contraste : le texte blanc tient grâce à ses ombres portées (text-shadow noir à 35 % sur le titre, 55 % sur le paragraphe). Sans elles, mesuré sur la plaque à six instants de l'animation : 2,05:1 en valeur médiane et 1,45:1 sur les zones les plus claires, quand la tache verte passe dessous. Gardez ces ombres, ou foncez la plaque (première teinte OKLCH à 0,4 de luminosité) si le texte s'allonge.
  • Sémantique : la plaque est un div avec un h3 et un paragraphe, le filtre SVG est en aria-hidden="true". Le reflet ne dépend que du pointeur : au clavier et au toucher rien ne se perd, il reste à sa place par défaut. Si la plaque porte des commandes, utilisez de vrais <button> avec un style :focus-visible visible sur le verre.

Compatibilité navigateur

CSS + SVG + 1,2 Ko de JavaScript, aucune dépendance. Mesuré par capture d'écran réelle des fenêtres (les captures automatiques de Firefox et WebKit n'affichent jamais backdrop-filter, même sur un cas témoin) : Chromium applique flou, saturation et réfraction SVG ; Firefox 153 et WebKit 26 appliquent flou et saturation grâce au bloc @supports du code vendu, sans la réfraction — sans ce bloc, ils n'appliqueraient rien du tout. La plaque y reste un verre dépoli lisible, sans l'ondulation. -webkit-backdrop-filter est déclaré pour Safari avant la version 18. Les couleurs OKLCH demandent Chrome 111, Firefox 113, Safari 15.4 ; plus ancien, le fond de la plaque est ignoré et seul le liseré reste.

Chrome 111+✓ Complet (réfraction SVG comprise)
Edge 111+✓ Complet (réfraction SVG comprise)
Firefox 113+✓ Flou et saturation, sans réfraction
Safari 15.4+✓ Flou et saturation, sans réfraction
Mobile iOS 15.4+✓ Flou et saturation, sans réfraction
Android Chrome 111+✓ Complet (réfraction SVG comprise)

Sans backdrop-filter du tout, le bloc @supports not donne à la plaque un fond plein à 86 % (rgba(20, 22, 40, .86)) : le texte reste lisible sur les taches. Sans OKLCH, le fond translucide de la plaque disparaît mais le liseré et les ombres restent. Sans JavaScript, le reflet reste fixe à 30 % / 20 % de la plaque et rien d'autre ne change.

Le code

Copiez les trois blocs dans votre page. Aucune dépendance.

HTML
<div class="lgr-scene" id="lgrScene">
    <div class="lgr-bg">
      <span class="lgr-blob lgr-blob--1"></span>
      <span class="lgr-blob lgr-blob--2"></span>
      <span class="lgr-blob lgr-blob--3"></span>
      <span class="lgr-blob lgr-blob--4"></span>
    </div>
    <div class="lgr-glass" id="lgrGlass">
      <h3>Liquid Glass</h3>
      <p>La lumière se réfracte à travers la surface. Bougez la souris.</p>
    </div>
  </div>
  <svg width="0" height="0" aria-hidden="true" style="position:absolute">
    <filter id="lgrRefract" x="-20%" y="-20%" width="140%" height="140%">
      <feTurbulence type="fractalNoise" baseFrequency="0.009 0.013" numOctaves="2" seed="7" result="noise">
        <animate attributeName="baseFrequency" dur="22s" values="0.009 0.013; 0.013 0.009; 0.009 0.013" repeatCount="indefinite"/>
      </feTurbulence>
      <feGaussianBlur in="noise" stdDeviation="1.4" result="sturb"/>
      <feDisplacementMap id="lgrDisp" in="SourceGraphic" in2="sturb" scale="26" xChannelSelector="R" yChannelSelector="G"/>
    </filter>
  </svg>
CSS
.lgr-scene {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: 14px;
  overflow: hidden;
  isolation: isolate;
  display: grid;
  place-items: center;
  background: #0b1024;
}

.lgr-bg {
  position: absolute;
  inset: -10%;
  z-index: 0;
  filter: saturate(1.1);
}

.lgr-blob {
  position: absolute;
  width: 46%;
  aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(28px);
  opacity: 0.95;
  animation: lgr-float 14s ease-in-out infinite;
}

.lgr-blob--1 {
  background: #ff5d8f;
  top: -6%;
  left: 4%;
}

.lgr-blob--2 {
  background: #5d9bff;
  bottom: -8%;
  right: 2%;
  animation-delay: -4s;
}

.lgr-blob--3 {
  background: #3ad6b3;
  top: 28%;
  left: 38%;
  animation-delay: -8s;
}

.lgr-blob--4 {
  background: #c77dff;
  bottom: 10%;
  left: 10%;
  animation-delay: -11s;
}

.lgr-glass {
  position: relative;
  z-index: 2;
  box-sizing: border-box;
  width: min(72%, 340px);
  padding: 30px 28px;
  border-radius: 26px;
  color: #fff;
  background: linear-gradient(135deg, oklch(0.98 0.02 255 / 0.20), oklch(0.94 0.04 300 / 0.07));
  border: 1px solid rgba(255, 255, 255, 0.28);
  box-shadow:
    0 10px 40px rgba(0, 0, 0, 0.28),
    inset 0 1px 1px rgba(255, 255, 255, 0.55),
    inset 0 -2px 6px rgba(255, 255, 255, 0.10);
  -webkit-backdrop-filter: blur(var(--lgr-blur, 8px)) saturate(180%) url(#lgrRefract);
  backdrop-filter: blur(var(--lgr-blur, 8px)) saturate(180%) url(#lgrRefract);
  overflow: hidden;
}

.lgr-glass::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(120px 120px at var(--mx, 30%) var(--my, 20%), rgba(255, 255, 255, 0.45), transparent 60%);
  mix-blend-mode: screen;
  opacity: 0.8;
  transition: opacity .3s;
}

.lgr-glass h3 {
  margin: 0 0 6px;
  font-size: 19px;
  letter-spacing: -0.01em;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.35);
}

.lgr-glass p {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
}

/* Firefox et Safari ignorent TOUTE la liste backdrop-filter dès qu'elle contient url() (mesuré : ni flou ni
   saturation). Ces deux moteurs — repérés par une propriété qu'eux seuls connaissent — reçoivent le flou et la
   saturation sans la réfraction SVG, que seul Chromium applique. */
@supports (-moz-appearance: none) or (-webkit-backdrop-filter: blur(1px)) {
  .lgr-glass {
    -webkit-backdrop-filter: blur(var(--lgr-blur, 8px)) saturate(180%);
    backdrop-filter: blur(var(--lgr-blur, 8px)) saturate(180%);
  }
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lgr-glass {
    background: rgba(20, 22, 40, 0.86);
    border-color: rgba(255, 255, 255, 0.35);
  }
}

@media (prefers-reduced-motion: reduce) {
  .lgr-blob {
    animation: none;
  }

  .lgr-glass::after {
    opacity: 0.5;
  }
}

@media (prefers-reduced-transparency: reduce) {
  .lgr-glass {
    background: #14162a;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-color: rgba(255, 255, 255, 0.4);
  }

  .lgr-glass::after {
    display: none;
  }
}

@keyframes lgr-float {

  0%,
  100% {
    transform: translate(0, 0) scale(1);
  }

  33% {
    transform: translate(12%, 8%) scale(1.12);
  }

  66% {
    transform: translate(-8%, -10%) scale(0.94);
  }
}
JavaScript (fx-0788)
(function() {
  /* ============================================================
     Sprint juillet 2026 — JS effets glassmorphism (5 IIFE scopées)
  ============================================================ */

  /* fx-0788 : Liquid Glass Refraction */
  (function() {
    var scene = document.getElementById('lgrScene');
    var glass = document.getElementById('lgrGlass');
    var disp = document.getElementById('lgrDisp');
    if (!scene || !glass) return;

    var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    glass.addEventListener('pointermove', function(e) {
      if (reduceMotion) return;
      var r = glass.getBoundingClientRect();
      glass.style.setProperty('--mx', ((e.clientX - r.left) / r.width * 100) + '%');
      glass.style.setProperty('--my', ((e.clientY - r.top) / r.height * 100) + '%');
    });
    glass.addEventListener('pointerleave', function() {
      glass.style.setProperty('--mx', '30%');
      glass.style.setProperty('--my', '20%');
    });

    if (reduceMotion) {
      try {
        /* la <svg> du filtre, pas la première <svg> de la page */
        disp.ownerSVGElement.pauseAnimations();
      } catch (e) {}
    }
  })();
})();

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Force de la réfraction (SVG — <feDisplacementMap scale="26">) 26 (décalage jusqu'à 13 px) 10 donne un verre à peine ondulé, 50 un verre de bouteille. Au-delà de 60, le texte du fond devient illisible sous la plaque et des bords apparaissent à la limite du filtre (zone de −20 % à 140 %).
Taille des ondulations (SVG — <feTurbulence baseFrequency="0.009 0.013">) 0,009 × 0,013, deux octaves, graine 7 Plus la fréquence monte, plus les vagues sont petites : 0,03 donne un verre martelé. numOctaves="1" lisse les vagues et allège le calcul. Changez aussi les valeurs de l'<animate>, sinon il les remplace.
Ondulation animée (SVG — <animate dur="22s">) 22 s, aller-retour entre les deux fréquences Supprimez la balise <animate> pour un verre figé, moins coûteux à afficher. dur="8s" donne une eau agitée.
Flou (CSS — --lgr-blur) 8px Posez --lgr-blur: 14px sur .lgr-glass pour un dépoli plus fort. La variable sert aux deux déclarations, celle de Chromium et celle du bloc @supports pour Firefox et Safari.
Teinte de la plaque (CSS — background: linear-gradient(135deg, oklch(0.98 0.02 255 / .20), oklch(0.94 0.04 300 / .07))) blanc bleuté à 20 % vers blanc mauve à 7 % Pour une plaque teintée : oklch(0.75 0.12 200 / .25) (turquoise). Pour lire un texte long : montez la première opacité à .35 ou baissez la luminosité à 0,4.
Reflet (CSS — .lgr-glass::after) dégradé radial de 120 px, blanc à 45 %, opacité .8 180px 180px pour un reflet plus large, opacité .5 pour un reflet discret. Position de repos : les valeurs 30 % / 20 % du pointerleave dans le script.
Fond derrière le verre (HTML + CSS — .lgr-bg, .lgr-blob) 4 taches floues qui dérivent en 14 s Remplacez-les par une image ou une vidéo (object-fit: cover) : la réfraction se voit surtout sur des bords nets, beaucoup moins sur des taches déjà floues.

FAQ

Pourquoi la plaque n'ondule-t-elle pas dans Firefox ou Safari ?

Parce que ces deux moteurs n'appliquent pas de filtre SVG (url()) dans backdrop-filter, et qu'ils rejettent alors la liste entière, flou compris — mesuré par capture d'écran réelle de Firefox 153 et de WebKit 26 sur un fond rayé : avec url() dans la liste, aucun flou, la plaque n'est qu'un voile blanc. Le code vendu leur donne un bloc à part, repéré par une propriété qu'eux seuls reconnaissent (-moz-appearance pour Firefox, -webkit-backdrop-filter pour Safari), avec le même flou et la même saturation sans le filtre. Le jour où l'un d'eux appliquera url(), il gardera ce verre dépoli : le bloc ne casse rien, il prive seulement de l'ondulation.

Peut-on mettre plusieurs plaques dans la même page ?

Le CSS et le filtre, oui : toutes les plaques .lgr-glass réfractent avec le même #lgrRefract, déclaré une seule fois. Le script, non : il cherche #lgrScene et #lgrGlass par id et ne règle le reflet que de la première plaque. Pour plusieurs plaques, remplacez getElementById par une boucle sur document.querySelectorAll('.lgr-glass') et posez les mêmes écouteurs pointermove / pointerleave sur chacune — c'est ce que font les scènes de cette page.

La scène est coupée en haut et en bas avec le code complet, pourquoi ?

Cela n'arrive que si vous récupérez le code complet avec son cadre de démonstration (par l'API ou le MCP) : le code copié sur cette page ne contient pas ce cadre. Le cadre .demo-preview fait 250 px de haut avec overflow: hidden, et la scène prend toute sa largeur au format 16/10. Mesuré à 784 px de large : la scène mesure 490 px, seule sa bande centrale de 250 px se voit (la plaque reste entière et centrée) ; à 374 px, la scène fait 234 px et deux bandes grises de 8 px apparaissent dessus et dessous. Dans votre page, retirez la hauteur de .demo-preview (ou le cadre entier) et donnez à .lgr-scene la largeur voulue : le format 16/10 fixe alors sa hauteur.