L'introduction de l'UI Liquid Glass d'Apple dans macOS Tahoe 26 a établi un nouveau standard pour le design d'interface. La bonne nouvelle ? Vous pouvez maintenant implémenter des effets similaires dans vos applications React. Ce guide complet vous guide à travers la création de composants Liquid Glass inspirés d'Apple pour applications web.
Liquid Glass représente l'évolution de design d'interface la plus sophistiquée d'Apple depuis l'introduction de la translucidité dans macOS. L'effet combine :
Transparence dynamique qui répond au contenu
Réfraction de lumière en temps réel créant de la profondeur
Animations fluides qui semblent naturelles et réactives
Adaptabilité contextuelle basée sur l'interaction utilisateur
L'effet Liquid Glass repose sur plusieurs technologies clés :
Shaders WebGL pour rendu en temps réel
CSS backdrop-filter pour effets de flou
Filtres SVG pour manipulation avancée de lumière
Accélération GPU pour animations fluides
D'abord, installez le composant liquid-glass-react :
npm install liquid-glass-react
# ou
yarn add liquid-glass-react
import React from 'react' ;
import { LiquidGlass } from 'liquid-glass-react' ;
import './App.css' ;
function App () {
return (
< div className = "app" >
< LiquidGlass
displacementScale = { 2.0 }
blurAmount = { 1.5 }
elasticity = { 0.7 }
className = "liquid-container"
>
< div className = "content" >
< h1 >Bienvenue dans Liquid Glass</ h1 >
< p >Découvrez le design d'interface révolutionnaire d'Apple</ p >
</ div >
</ LiquidGlass >
</ div >
);
}
.liquid-container {
width : 100 % ;
height : 100 vh ;
background : linear-gradient ( 135 deg ,
rgba ( 255 , 255 , 255 , 0.1 ) 0 % ,
rgba ( 255 , 255 , 255 , 0.05 ) 100 % );
backdrop-filter : blur ( 10 px ) saturate ( 180 % );
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 );
border-radius : 16 px ;
}
.content {
padding : 40 px ;
color : rgba ( 255 , 255 , 255 , 0.9 );
text-align : center ;
}
Échelle de déplacement (0.0 - 5.0)
Contrôle l'intensité de l'effet de distorsion liquide :
< LiquidGlass displacementScale = { 2.5 }>
{ /* Valeurs plus élevées créent une déformation plus dramatique */ }
</ LiquidGlass >
Quantité de flou (0.0 - 3.0)
Détermine l'intensité du flou d'arrière-plan :
< LiquidGlass blurAmount = { 1.8 }>
{ /* Imite l'effet de profondeur de champ de macOS Tahoe */ }
</ LiquidGlass >
Élasticité (0.0 - 1.0)
Ajuste à quel point le verre apparaît fluide :
< LiquidGlass elasticity = { 0.9 }>
{ /* Valeurs plus élevées semblent plus liquides */ }
</ LiquidGlass >
Rayon de coin
< LiquidGlass cornerRadius = { 20 }>
{ /* Correspond aux éléments d'interface arrondis de macOS Tahoe */ }
</ LiquidGlass >
Intensité d'aberration
< LiquidGlass aberrationIntensity = { 0.4 }>
{ /* Crée une séparation de couleur subtile pour sensation premium */ }
</ LiquidGlass >
import React, { useState } from 'react' ;
import { LiquidGlass } from 'liquid-glass-react' ;
const MenuBar = () => {
const [ isActive , setIsActive ] = useState ( false );
return (
< LiquidGlass
displacementScale = { 1.2 }
blurAmount = { 2.0 }
elasticity = { 0.8 }
className = { `menu-bar ${ isActive ? 'active' : ''}` }
style = {{
position: 'fixed' ,
top: 0 ,
left: 0 ,
right: 0 ,
height: '32px' ,
background: 'rgba(255, 255, 255, 0.05)' ,
backdropFilter: 'blur(20px) saturate(180%)' ,
borderBottom: '1px solid rgba(255, 255, 255, 0.1)' ,
zIndex: 1000
}}
>
< div className = "menu-items" >
< span className = "menu-item" >Fichier</ span >
< span className = "menu-item" >Édition</ span >
< span className = "menu-item" >Affichage</ span >
< span className = "menu-item" >Fenêtre</ span >
< span className = "menu-item" >Aide</ span >
</ div >
</ LiquidGlass >
);
};
const CompatibilityCard = ({ macModel , compatibility }) => {
const [ isHovered , setIsHovered ] = useState ( false );
return (
< LiquidGlass
displacementScale = {isHovered ? 3.0 : 2.0 }
blurAmount = {isHovered ? 2.0 : 1.5 }
elasticity = { 0.85 }
className = "compatibility-card"
onMouseEnter = {() => setIsHovered ( true )}
onMouseLeave = {() => setIsHovered ( false )}
style = {{
padding: '24px' ,
margin: '16px' ,
background: compatibility.isSupported
? 'rgba(52, 199, 89, 0.1)'
: 'rgba(255, 69, 58, 0.1)' ,
border: '1px solid rgba(255, 255, 255, 0.15)' ,
borderRadius: '16px' ,
transition: 'all 0.3s ease'
}}
>
< h3 >{macModel}</ h3 >
< p >{compatibility.features. join ( ', ' )}</ p >
< div className = "support-indicator" >
{compatibility.isSupported ? '✅ Entièrement pris en charge' : '⚠️ Support limité' }
</ div >
</ LiquidGlass >
);
};
Activer l'accélération matérielle :
.liquid-glass-container {
transform : translateZ ( 0 );
will-change : transform, opacity;
contain : layout style paint;
}
Optimiser pour mobile :
const isMobile = window.innerWidth < 768 ;
< LiquidGlass
displacementScale = {isMobile ? 1.0 : 2.5 }
blurAmount = {isMobile ? 0.8 : 1.5 }
elasticity = {isMobile ? 0.5 : 0.8 }
>
Nettoyage de composant :
import { useEffect, useRef } from 'react' ;
const OptimizedLiquidGlass = ({ children , ... props }) => {
const containerRef = useRef ( null );
useEffect (() => {
const container = containerRef.current;
return () => {
// Nettoyer contextes WebGL et écouteurs d'événements
if (container) {
const canvas = container. querySelector ( 'canvas' );
if (canvas) {
const gl = canvas. getContext ( 'webgl' ) || canvas. getContext ( 'webgl2' );
if (gl) {
gl. getExtension ( 'WEBGL_lose_context' )?. loseContext ();
}
}
}
};
}, []);
return (
< div ref = {containerRef}>
< LiquidGlass { ... props}>{children}</ LiquidGlass >
</ div >
);
};
const hasWebGLSupport = () => {
try {
const canvas = document. createElement ( 'canvas' );
return !! (
window.WebGLRenderingContext &&
(canvas. getContext ( 'webgl' ) || canvas. getContext ( 'webgl2' ))
);
} catch (e) {
return false ;
}
};
const hasBackdropFilterSupport = () => {
return CSS . supports ( 'backdrop-filter' , 'blur(1px)' );
};
const ConditionalLiquidGlass = ({ children , fallbackComponent , ... props }) => {
const supportsLiquidGlass = hasWebGLSupport () && hasBackdropFilterSupport ();
if ( ! supportsLiquidGlass) {
return fallbackComponent || (
< div className = "fallback-glass" >{children}</ div >
);
}
return < LiquidGlass { ... props}>{children}</ LiquidGlass >;
};
.fallback-glass {
background : rgba ( 255 , 255 , 255 , 0.1 );
border : 1 px solid rgba ( 255 , 255 , 255 , 0.2 );
border-radius : 16 px ;
backdrop-filter : blur ( 10 px );
}
/* Corrections spécifiques Safari */
@supports ( -webkit-backdrop-filter : blur ( 10 px )) {
.fallback-glass {
-webkit-backdrop-filter : blur ( 10 px );
}
}
// Bibliothèque de composants de verre
const GlassButton = ({ variant = 'primary' , children , ... props }) => {
const variants = {
primary: {
displacementScale: 1.5 ,
blurAmount: 1.0 ,
background: 'rgba(0, 122, 255, 0.2)'
},
secondary: {
displacementScale: 1.2 ,
blurAmount: 0.8 ,
background: 'rgba(255, 255, 255, 0.1)'
},
danger: {
displacementScale: 1.8 ,
blurAmount: 1.2 ,
background: 'rgba(255, 69, 58, 0.2)'
}
};
return (
< LiquidGlass
{ ... variants[variant]}
elasticity = { 0.7 }
cornerRadius = { 8 }
className = { `glass-button glass-button--${ variant }` }
{ ... props}
>
< button className = "button-content" >{children}</ button >
</ LiquidGlass >
);
};
const GlassCard = ({ children , ... props }) => (
< LiquidGlass
displacementScale = { 2.0 }
blurAmount = { 1.5 }
elasticity = { 0.8 }
cornerRadius = { 16 }
className = "glass-card"
{ ... props}
>
< div className = "card-content" >{children}</ div >
</ LiquidGlass >
);
import { useSpring, animated } from '@react-spring/web' ;
const AnimatedLiquidGlass = ({ isActive , children }) => {
const springProps = useSpring ({
displacementScale: isActive ? 3.0 : 2.0 ,
blurAmount: isActive ? 2.0 : 1.5 ,
elasticity: isActive ? 0.9 : 0.7 ,
config: { tension: 200 , friction: 25 }
});
return (
< animated.div style = {springProps}>
< LiquidGlass
displacementScale = {springProps.displacementScale}
blurAmount = {springProps.blurAmount}
elasticity = {springProps.elasticity}
>
{children}
</ LiquidGlass >
</ animated.div >
);
};
const customShaderConfig = {
fragmentShader: `
precision mediump float;
uniform sampler2D u_texture;
uniform float u_time;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
vec2 uv = v_texCoord;
// Ajouter distorsion basée sur le temps
uv.x += sin(uv.y * 10.0 + u_time) * 0.01;
uv.y += sin(uv.x * 10.0 + u_time * 0.5) * 0.01;
vec4 color = texture2D(u_texture, uv);
// Appliquer réfraction type verre
float aberration = 0.005;
color.r = texture2D(u_texture, uv + vec2(aberration, 0.0)).r;
color.b = texture2D(u_texture, uv - vec2(aberration, 0.0)).b;
gl_FragColor = color;
}
`
};
< LiquidGlass
customShader = {customShaderConfig}
displacementScale = { 2.5 }
>
{children}
</ LiquidGlass >
const AccessibleLiquidGlass = ({ children , ... props }) => {
const prefersReducedMotion = window. matchMedia (
'(prefers-reduced-motion: reduce)'
).matches;
const accessibleProps = prefersReducedMotion
? {
displacementScale: 0.5 ,
elasticity: 0.3 ,
// Mouvement réduit pour utilisateurs sensibles
}
: props;
return (
< LiquidGlass { ... accessibleProps}>
{children}
</ LiquidGlass >
);
};
< LiquidGlass
aria-label = "Élément d'interface de verre interactif"
role = "presentation"
{ ... props}
>
< div aria-live = "polite" className = "sr-only" >
Contenu mis à jour avec effet de verre liquide
</ div >
{children}
</ LiquidGlass >
const PerformanceMonitor = ({ children }) => {
useEffect (() => {
let frameCount = 0 ;
let lastTime = performance. now ();
const measureFPS = () => {
frameCount ++ ;
const currentTime = performance. now ();
if (currentTime - lastTime >= 1000 ) {
console. log ( `FPS Liquid Glass : ${ frameCount }` );
frameCount = 0 ;
lastTime = currentTime;
}
requestAnimationFrame (measureFPS);
};
measureFPS ();
}, []);
return children;
};
class LiquidGlassErrorBoundary extends React . Component {
constructor ( props ) {
super (props);
this .state = { hasError: false };
}
static getDerivedStateFromError ( error ) {
return { hasError: true };
}
componentDidCatch ( error , errorInfo ) {
console. error ( 'Erreur Liquid Glass :' , error, errorInfo);
}
render () {
if ( this .state.hasError) {
return (
< div className = "fallback-glass" >
{ this .props.fallback || this .props.children}
</ div >
);
}
return this .props.children;
}
}
// webpack.config.js optimisation pour Liquid Glass
module . exports = {
optimization: {
splitChunks: {
chunks: 'all' ,
cacheGroups: {
liquidGlass: {
test: / [ \\ /] node_modules [ \\ /] liquid-glass-react [ \\ /] / ,
name: 'liquid-glass' ,
priority: 10 ,
reuseExistingChunk: true
}
}
}
}
};
// Chargement paresseux pour performance
const LiquidGlass = React. lazy (() =>
import ( 'liquid-glass-react' ). then ( module => ({
default: module .LiquidGlass
}))
);
const LazyLiquidGlass = ({ children , ... props }) => (
< Suspense fallback = {< div className = "fallback-glass" >{children}</ div >}>
< LiquidGlass { ... props}>{children}</ LiquidGlass >
</ Suspense >
);
// Interface TypeScript pour intégration système de design
interface LiquidGlassTheme {
colors : {
glassTint : string ;
borderColor : string ;
backgroundGradient : string [];
};
effects : {
displacementScale : number ;
blurAmount : number ;
elasticity : number ;
};
accessibility : {
reducedMotion : boolean ;
highContrast : boolean ;
};
}
const ThemedLiquidGlass : React . FC <{
theme : LiquidGlassTheme ;
children : React . ReactNode ;
}> = ({ theme , children }) => {
const effectProps = theme.accessibility.reducedMotion
? { ... theme.effects, displacementScale: 0.5 }
: theme.effects;
return (
< LiquidGlass
{ ... effectProps}
style = {{
background: `linear-gradient(${ theme . colors . backgroundGradient . join ( ', ' ) })` ,
border: `1px solid ${ theme . colors . borderColor }`
}}
>
{children}
</ LiquidGlass >
);
};
Implémenter l'UI Liquid Glass d'Apple en React ouvre des possibilités passionnantes pour créer des interfaces utilisateur premium et engageantes. Le composant liquid-glass-react fournit une base solide, mais la vraie magie arrive quand vous le combinez avec des principes de design réfléchis et une optimisation de performance.
Points clés à retenir :
Commencer simple avec des configurations de base avant d'ajouter de la complexité
Prioriser la performance avec une accélération GPU appropriée et des replis
Considérer l'accessibilité pour les utilisateurs avec sensibilité au mouvement
Tester minutieusement à travers différents appareils et navigateurs
Surveiller la performance pour assurer des expériences utilisateur fluides
Alors que macOS Tahoe continue d'influencer les tendances de design d'interface, maîtriser l'implémentation Liquid Glass positionne vos applications à l'avant-garde du développement UI moderne. La combinaison de sophistication technique et d'élégance visuelle en fait un outil puissant pour créer des expériences utilisateur mémorables.
Prochaines étapes :
Expérimenter avec la démo officielle
Forker le dépôt GitHub pour personnalisation
Rejoindre les discussions de la communauté développeur
Partager vos implémentations et apprendre des autres
Prêt à apporter Liquid Glass à votre projet ? Vérifiez notre guide de compatibilité pour vous assurer que vos navigateurs cibles prennent en charge les fonctionnalités avancées requises pour un rendu Liquid Glass optimal.