Blur craft
La mayoría de los vidrios en la web se ven baratos por tres razones: nada interesante se encuentra detrás, el material es un solo tono plano con un solo desenfoque, y el texto encima es un gris turbio que nadie midió. El vidrio que se ve caro (los materiales de Apple, visionOS, los mejores sitios de productos) hace tres cosas bien, en este orden:
- La luz detrás del vidrio está diseñada. El vidrio muestra lo que tiene detrás, así que pon una luz allí.
- El vidrio es un material, no un tono. Desenfoque saturado, un relleno degradado, un borde iluminado, un brillo especular, profundidad, grano. Eso es
assets/glass.css. - El texto está medido. El vidrio translúcido cambia el contraste con cada píxel detrás de él, por lo que el contraste se verifica en el peor punto, mediante un script, en tres estados (
scripts/glass-contrast.mjs).
Ruta rápida
| Estoy construyendo… | Usar | Notas |
|---|---|---|
| Una barra de navegación sticky | .gs-bar (surfaces.css) | Fondo extendido; añade gs-bar--on-scroll para que se vuelva vidrioso solo después de hacer scroll |
| Un reproductor, dock o píldora sobre arte | .gs-dock.glass.glass--clear + liquid-glass.js | Liquid Glass claro, uno por pantalla; tono adaptativo si el arte cambia mucho |
| Una tarjeta o panel flotante con texto | .glass.glass--regular | thick sobre una foto detallada |
| Un menú, popover o barra de comandos | .gs-menu.glass | 32px sobre ~86%, resalte superior de 0.5px |
| Un diálogo | .gs-veil + .gs-dialog.glass.glass--thick | glass--solid cuando confirma dinero, elimina o firma; gs-veil--flat + .gs-modal-open cuando el vidrio cromado está debajo |
| Un toast o una hoja inferior | .gs-toast.glass.glass--regular / .gs-sheet.glass.glass--thick | Las clases gs- son solo de diseño; el material proviene de .glass |
| Contenido desvaneciéndose bajo una barra | Desenfoque progresivo (receta 2) | Un efecto de borde de scroll por vista |
| Un titular que aparece, o un valor que cambia | Revelado con desenfoque (4) / crossfade de 2px (4b) | Una vez por pantalla |
| Una página bañada en los colores del arte | Fondo de arte, horneado (receta 8) | Nunca un desenfoque en vivo bajo vidrio fijo |
| Un proyecto de Tailwind v4 | glass.css + glass.tailwind.css (glass-blur-24, …) | El backdrop-blur-* de Tailwind se rompe en Sonoma Safari |
| Una app nativa (SwiftUI, Flutter, React Native, Compose) | references/native.md | Mismo método, el material de cada toolkit |
| Chrome para un editor de fotos, video o diseño | .glass.glass--thick.glass--neutral | Neutral: sin aumento de saturación, el vidrio no debe recolorar el trabajo |
Luego: compón la luz (§1), configura el material (§2), verifica (§5). assets/demo.html muestra cada superficie en vivo con controles; ábrelo para ver lo que hace cada clase antes de elegir.
1. Compón la luz primero
Decide qué estará directamente detrás de cada superficie de vidrio, y hazlo lo más luminoso allí.
-
Coloca una luz debajo del vidrio: un brillo, el sol en el arte, los colores del producto, un componente WebGL en vivo. El color plano desenfocado es el mismo color plano; el vidrio sobre un vacío es una caja gris.
-
Deja que la luz cruce el borde. El mejor vidrio se sitúa mitad sobre la luz y mitad sobre la oscuridad, para que el color cambie a lo largo de su ancho.
-
Apunta el borde hacia la luz. Configura
--glass-lightal color de la luz (255 200 160para un atardecer) y--glass-rim-anglea su dirección. El extremo brillante del borde mira hacia la luz:Luz desde superior-izquierda superior superior-derecha derecha inferior-derecha inferior inferior-izquierda izquierda ángulo 135deg 180deg 225deg 270deg 315deg 0deg 45deg 90deg Una luz debajo del vidrio toma el ángulo de su borde más cercano más ~15deg hacia el centro. El ángulo es una
@propertyregistrada, por lo que puede seguir el puntero o el scroll suavemente (los resaltes de Apple viajan alrededor del material a medida que la luz se mueve). -
Dimensiona el vidrio como el héroe que es. Se lee como un material cuando es lo suficientemente grande como para mostrar el cambio de escena debajo de él. Apple hace que el vidrio más grande se comporte como vidrio más grueso: sombra más profunda, dispersión más suave.
2. Configura el material
Usa assets/glass.css (renombra el prefijo glass al copiar) y assets/surfaces.css para las superficies prefabricadas, o los componentes: Glass.tsx (React), Glass.vue (Vue 3), Glass.svelte (Svelte 5). No escribas a mano una tarjeta de backdrop-filter de una línea.
Escribe backdrop-filter con valores literales, nunca var(), sin prefijo primero y -webkit- al final. Safari 18 y 26 en macOS Sonoma no renderiza desenfoque cuando el filtro se construye a partir de propiedades personalizadas (WebKit 297620, abierto), y backdrop-blur-* de Tailwind v4 está construido exactamente de esa manera. El mismo informe de error muestra que la propiedad sin prefijo falla allí incluso con un valor literal, por lo que la declaración con prefijo va al final, donde Safari la aplica (Chrome y Firefox la ignoran). glass.css tiene un filtro literal por grosor × tono y cambia por clase; las variables están bien para rellenos, atenuaciones y colores.
Grosor. Elige según la cantidad de texto que lleva el vidrio. Los valores fueron calibrados con el verificador de contraste sobre una luz brillante en demo.html:
| Clase | Desenfoque, atenuación | ¿Pasa 4.5:1 sobre una luz brillante? | Usar para |
|---|---|---|---|
glass--clear | 16px, 22% | No: solo texto en negrita o grande e íconos | Docks, botones de reproducción, píldoras sobre medios (Apple: clear necesita contenido brillante y en negrita y una capa de atenuación) |
glass--thin | 24px, 30% | No sobre la luz más brillante | Controles, chips, tarjetas pequeñas sobre fondos moderados |
glass--regular | 32px, 48% | Sí en la página de calibración; un atardecer brillante detrás de un reproductor necesitó thick a 0.62 | Reproductores, paneles, toasts: el valor predeterminado. El estado sin desenfoque (solo tono) suele ser el que falla primero |
glass--thick | 44px, 56% | Sí | Menús, hojas, diálogos, cualquier cosa con mucho texto |
glass--solid | 4px, 92% | Sí | Pagos, eliminaciones, firmas (por encima de ~85% de relleno un gran desenfoque no se ve, por lo tanto 4px) |
Añade glass--light para vidrio claro con texto en tinta, y glass--neutral en aplicaciones críticas para el color (editores de fotos, video y diseño): sin aumento de saturación ni cambio de tono, porque el vidrio no debe cambiar cómo se ve el trabajo. Sobre una foto detallada, comienza con thick y --glass-dim: rgb(10 10 18 / 0.65–0.75) (es un color, no un número): el vidrio vívido y el texto legible tiran el uno contra el otro allí, así que gasta la viveza en el borde, el brillo y la luz que se derrama alrededor del vidrio.
Dos niveles, como iOS 26.1: un nivel teñido (más opacidad, nunca menos) se activa para prefers-contrast: more (la configuración a la que los usuarios de Safari pueden acceder; Safari no tiene prefers-reduced-transparency), con glass--tinted, o en todo el sitio con <html data-glass="tinted">. glass.css también maneja High Contrast de Windows (superficie Canvas sólida), impresión (cuadro bordeado simple), modo de captura para herramientas de captura de pantalla y RTL.
El vidrio pertenece a la capa de control (nav, barras de herramientas, docks, menús, hojas, diálogos) flotando sobre el contenido, nunca al contenido mismo, y nunca vidrio sobre vidrio (Apple, WWDC25). Tiñe solo la acción principal. Coincide con el registro del brief: los briefs inmersivos toman luz fuerte y vidrio claro en el control principal; los de confianza (finanzas, salud) toman vidrio grueso o sólido y apenas luz.
references/material.md tiene el resto: lo que hace cada capa y por qué, los preajustes leídos de Apple.com, Linear, Raycast, Radix, Material y Fluent, la lista completa de reglas de WWDC25 de Apple y la tabla de registro.
3. Efectos característicos
Código en references/recipes.md:
- Refracción Liquid Glass: el borde dobla lo que está detrás como una lente, con aberración cromática opcional (0.6–1). Dos versiones:
liquid-glass.ts/.jsrefracta cualquier contenido de página pero solo en Chromium (Safari no pinta nada para filtros de fondo SVG, WebKit 245510);liquid-glass-webgl.jsrefracta un fondo que la página dibuja por sí misma (un<canvas>,<img>o<video>, como un fondo de pantalla principal) en todos los navegadores, verificado en Chromium y WebKit. En otros lugares, el material esmerilado debe verse terminado por sí solo. La refracción y el esmerilado se compensan: la curvatura se ve a través del vidrio claro (blur0–6); el esmerilado (12–20) esmerila de borde a borde y la curvatura se convierte en un cambio de color suave. Claro para una palabra o un ícono, esmerilado para oraciones, texto a un ancho de bisel dentro del borde. Nunca en un diálogo sobre un velo: no queda nada nítido para doblar. - Fondo extendido para barras sticky: el color llega al vidrio antes de que el contenido se desplace debajo de él.
gs-bar--on-scrollhace que la barra sea vidriosa solo después de hacer scroll, en CSS puro (animaciones impulsadas por scroll). - Desenfoque progresivo: el contenido se disuelve debajo de una barra en lugar de cortarse. Las capas apiladas enmascaradas son la única forma hasta que CSS obtenga
linear-blur()(propuesto, csswg #13285). - Crossfade puenteado por desenfoque: desenfoque de 2px durante un intercambio de estado (Emil Kowalski).
- Revelado con desenfoque: desde
blur(8px), una vez, como el momento de llegada. - Velo de enfoque: la página detrás de un diálogo retrocede 4–8px.
- Morph y presión de vidrio: un morph
layoutIdentre formas de vidrio cercanas (Cursor: 0.34s,[0.32, 0.72, 0, 1]) y un pulso de brillo interior al presionar, no una escala. - Fusión de formas de vidrio: una capa de vidrio cortada por una máscara SVG con filtro goo, para que las formas cercanas se unan con un cuello líquido como los contenedores de vidrio de Apple (receta 11, probada en Chromium).
- Brillo ambiental desde video: una muestra de 10×6 canvas del video en reproducción, escalada (receta 8b).
- Teñido: chroma ~0.03–0.05 para vidrio estructural, alfa de color relativo para tintes (receta 12).
- Profundidad de campo, desenfoque de movimiento por velocidad, fondo de arte (horneado), contenido bloqueado, grano.
4. Mantenlo rápido
Diseña el aspecto primero, luego mantén esto:
- Las animaciones largas o en bucle van en opacidad y transform, no en el radio de desenfoque. Las cortas únicas están bien: Apple.com anima el desenfoque de la cortina de su menú 0 → 20px en 320ms.
- Nunca desenfoques dos superficies apiladas a la vez (el nav de Apple elimina su propio desenfoque mientras un flyout está abierto). Cuando se abre un diálogo sobre un vidrio cromado, añade
.gs-modal-opena<html>(el cromado deja de desenfocar y se vuelve casi opaco) y usags-veil--flat, o hornea el velo desde una imagen o un fondo de canvas. Medido en una prueba de editor de fotos: un velo de pantalla completa en vivo sobre cinco paneles de vidrio funcionó a 31fps; sin él, 60. - Un desenfoque fijo o sticky se vuelve a ejecutar en cada fotograma de scroll. Uno grande por pantalla; hornea imágenes grandes desenfocadas debajo de él (medido: 60 → 22fps con desenfoque en vivo, de vuelta a 60 horneado).
- Móviles: siete paneles de 18px cuestan ~3.5ms por fotograma en un Mali-G57 (75–80% de los fotogramas a tiempo vs 99% sin ellos). Con más de unos pocos paneles, elimina
backdrop-filteren el punto de ruptura de tu diseño para móviles y mantén el relleno translúcido (o deja que glass-tier.js decida por dispositivo). - Presupuesta el área desenfocada, no el radio o la cantidad. Medido para esta skill (Chromium, renderizado por software a 2×, fondo en movimiento, vsync activado): el radio 4 → 64px y 1 → 9 superficies con la misma área total no hizo diferencia; desenfocar el 90% de la pantalla cayó a 51fps (p95 33ms). Eso coincide con el submuestreo de Skia para grandes desenfoques. Los números son relativos (un Mac rápido renderizando en software), la clasificación es lo que se transfiere.
- Dale a
htmlun color de fondo opaco: Safari entonces se salta una capa de composición de pantalla completa adicional para páginas con vidrio (WebKit 271340: la corrección salta la capa solo para documentos opacos). - No desenfoques debajo de un relleno opaco. Framer y Dia envían
backdrop-filteren fondos sólidos, pagando por un desenfoque que nadie puede ver. Por encima de ~85% de relleno, usa ≤ 4px o ninguno (los toasts de Arc: 85% y 3px). - Clasifica el vidrio por dispositivo:
assets/glass-tier.jsestablecedata-glass-tiera full, lite (un desenfoque de 8px) o flat (sin desenfoque, más tono) a partir de la memoria del dispositivo, Save-Data, una sonda de velocidad de fotogramas (el Modo de Bajo Consumo de iOS limita la animación a 30fps a propósito) y Compute Pressure, de la misma manera que Android, Fluent y Apple eliminan el desenfoque en dispositivos débiles o de bajo consumo. - Sobre video, no desenfoques el video en reproducción: muéstralo en un canvas pequeño (receta 8b), y elimina el vidrio en pantalla completa. Safari nunca pasa píxeles de video a los filtros SVG.
- Raíz del fondo: un padre con
opacity < 1,filter, una máscara,clip-path,mix-blend-mode,backdrop-filterowill-changeen cualquiera de ellos aplana el vidrio dentro de él. Eso incluye un desvanecimiento de un padre de Framer Motion y unfiltergoo en un padre de blobs de vidrio. Desvanece el vidrio mismo; limpia los filtros de revelado (filter: none) cuando terminen. backdrop-filterconvierte a un elemento en el bloque contenedor para hijosposition: fixed: menús de portal, toasts y diálogos fuera de los contenedores de vidrio.- Un
view-transition-namepersistente en un ancestro aplana el vidrio en Chromium; establece nombres solo durante la transición. Las transiciones de desvanecimiento del framework en un wrapper hacen lo mismo: desvanece el vidrio mismo. - Tomar capturas de pantalla, imágenes OG o PDFs de vidrio: html2canvas, Satori y la impresión eliminan o rompen el desenfoque. Establece
data-glass-captureen<html>mientras se captura (glass.css vuelve el vidrio opaco), y consulta quirks.md.
5. Verifícalo (obligatorio)
Ejecuta todos estos antes de dar por terminado el vidrio:
- Verificación de escarcha:
node <skill>/scripts/frost-check.mjs <url o file.html>mide, para cada superficie de vidrio, cuánto detalle elimina el desenfoque (desenfoque activado vs desactivado). NO BLUR significa una raíz de fondo, un prefijo roto ovar(), o un mapa Liquid Glass obsoleto; FLAT significa que no hay nada detrás que medir. Para verificar a ojo en su lugar, coloca texto rojo en negrita detrás del vidrio, captura adeviceScaleFactor: 1y amplía. - Contraste, medido, en el peor fondo:
node <skill>/scripts/glass-contrast.mjs <url o file.html>. Apúntalo al estado realista más brillante, no al predeterminado bonito: la foto más clara, un fotograma diurno, una imagen empujada +2 EV, la sección de página blanca que se desplaza debajo de la barra (añade parámetros URL o un estado de prueba para que el script pueda cargarlo). En un editor, la foto predeterminada pasó a 8.7:1 y un fotograma brillante falló a 4.4:1. Verifica también el ancho del teléfono (--width 390 --height 844). Oculta el texto, encuentra el píxel más brillante y el más oscuro debajo de cada línea, y verifica 4.5:1 (3:1 grande) en tres estados: normal, solo tono (desenfoque apagado, como lo muestra el Modo de Bajo Consumo de iOS) yprefers-contrast: more. Arregla los fallos subiendo un grosor, aumentando el alfa en--glass-dim(por ejemplo...