Glassmorphism con Tailwind CSS: el efecto cristal sin arruinar la legibilidad
Serie «Efectos con Tailwind» · 2 de 4
- Crear un borde animado con Tailwind CSS
- Glassmorphism sin arruinar la legibilidad ← estás aquí
- Scroll-driven animations
- View transitions en Astro
El glassmorphism es ese panel translúcido que desenfoca lo que tiene detrás, como un vidrio esmerilado. La primera vez que lo usé fue hace tanto que no recuerdo el proyecto, pero estoy seguro de que fue buscando referencias, probablemente en Behance: vi una card con ese efecto y quise replicarla.
Me sigue gustando mucho, aunque con el tiempo he aprendido que es delicado. Es fácil que afecte a la accesibilidad o que rompa la estética del sitio donde lo pones.
Volvió a ponerse de moda cuando Apple presentó Liquid Glass. Yo veo el glassmorphism como un predecesor, bastante más viejo y más simple, de lo que Apple enseñó ahí, y es lo que se puede hacer hoy en la web con un par de propiedades de CSS.
Con Tailwind CSS se hace con tres clases. Lo que cuesta más es que el texto que va encima se siga leyendo, así que en este artículo veremos las dos cosas.
blur y backdrop-blur hacen cosas distintas
Se confunden mucho, así que conviene aclararlo antes de escribir nada:
blur-*desenfoca el propio elemento. Si se lo pones a la tarjeta, se te ve borroso el texto de la tarjeta.backdrop-blur-*desenfoca lo que queda detrás del elemento. La tarjeta se ve nítida y el fondo se difumina.
Para el efecto cristal siempre quieres el segundo, que en CSS es la propiedad backdrop-filter.
Hay un detalle que se olvida fácil: backdrop-filter no hace nada si el elemento es opaco. El fondo tiene que dejar pasar algo de lo que hay detrás, así que el bg-white/10 hace falta para que el efecto funcione.
Las tres capas del cristal
Para que un panel parezca de vidrio necesito tres cosas:
- Un fondo translúcido,
bg-white/10, para que se vea a través. - El desenfoque del fondo,
backdrop-blur-md, que es el esmerilado. - Un borde claro,
border border-white/20, que hace de canto del vidrio.
El borde es lo que más se suele saltar, y se nota: sin él el panel parece una mancha borrosa en vez de una pieza de vidrio.
<div class="rounded-2xl border border-white/20 bg-white/10 p-8 backdrop-blur-md">
<p class="font-semibold text-white">Tres capas: fondo translúcido, blur y borde de luz.</p>
</div>
Además necesitas algo con color detrás. Sobre un fondo liso el efecto no se ve:
Tres capas: fondo translúcido, blur y borde de luz.
El mismo panel sin backdrop-blur. Se ve translúcido, pero no se ve como vidrio.
Entre los dos paneles solo cambia backdrop-blur-md, y es la clase que hace que el primero parezca vidrio y el segundo solo transparente.
Un poco de saturación
El vidrio de verdad también aviva un poco el color de lo que se ve a través. Tailwind tiene backdrop-saturate-* para eso, y se puede combinar con el blur:
<div
class="rounded-2xl border border-white/25 bg-white/10 p-8 backdrop-blur-lg backdrop-saturate-150"
>
<p class="font-semibold text-white">Con saturación, el color de detrás se siente más vivo.</p>
</div>
Solo blur
Blur + saturación
En Tailwind 4 el desenfoque va de backdrop-blur-xs (4px) a backdrop-blur-3xl (64px). Para paneles de interfaz yo me quedo entre md (12px) y xl (24px). Con menos no parece vidrio, y con más el fondo se convierte en una mancha de color.
Donde más sentido tiene: un header translúcido
Fuera de las demos, el uso que mejor le queda es una barra fija que deja adivinar el contenido que pasa por debajo al hacer scroll:
<header
class="sticky top-0 z-50 border-b border-white/10 bg-zinc-900/60 backdrop-blur-lg"
>
<nav class="mx-auto flex max-w-4xl items-center justify-between p-4">
<span class="font-bold text-white">EFEELE</span>
<span class="text-sm text-white/70">Blog</span>
</nav>
</header>
El bg-zinc-900/60 es un tinte oscuro al 60 % de opacidad, y es lo que hace que el patrón sea usable: limita cuánto puede cambiar el fondo que queda detrás del texto. Eso lleva directo al problema del contraste.
El contraste
Un panel de cristal tiene una pega: el fondo que hay detrás del texto cambia según lo que pase por debajo. Si al hacer scroll pasa una imagen clara bajo tu header, el texto blanco deja de verse.
Y no es solo una cuestión de gusto. El criterio 1.4.3 de las WCAG pide un contraste mínimo de 4.5:1 para texto normal, y con backdrop-filter no lo puedes calcular de antemano porque depende del contenido que pase por detrás.
Hay tres maneras de evitarlo, de la más efectiva a la menos:
- Poner un mínimo de opacidad. En vez de
bg-white/10, usar algo comobg-zinc-900/60obg-white/80. Cuanto más opaco, menos influye el fondo y más fácil es garantizar el contraste. Se pierde algo de efecto, pero es la que realmente lo resuelve. - Usar texto más grueso. Un
font-semiboldsobre un panel translúcido se lee mejor que unfont-normal, porque aguanta mejor el ruido de detrás. - Dejar el cristal para lo decorativo. Si el panel lleva algo que hay que leer sí o sí (un precio, un mensaje de error, una instrucción), mejor no ponerlo sobre vidrio. Úsalo en barras, etiquetas y contenedores.
Mi regla es sencilla: si no puedo asegurar 4.5:1 con el fondo más desfavorable, subo la opacidad hasta conseguirlo. El header de este mismo blog lo hace así: usa bg-white/90 en modo claro y un 80 % en modo oscuro, con apenas backdrop-blur-xs. Se nota poco el efecto, pero el menú se lee pase lo que pase por debajo.
Y aparte de la accesibilidad está la estética. He visto sitios que lo usan de forma desmedida, con cristal en cada tarjeta y cada botón, y después de un rato cansa a la vista. También he visto lo contrario: dos o tres paneles de vidrio metidos en un diseño que no tiene nada que ver con ellos, sin ninguna armonía con el resto de componentes. En los dos casos el efecto deja de sumar.
Fallback y rendimiento
Hoy casi todos los navegadores soportan backdrop-filter, pero no cuesta nada dejar una alternativa para los que no. Tailwind tiene la variante supports-*:
<div
class="bg-zinc-900/95 supports-[backdrop-filter]:bg-zinc-900/60 supports-[backdrop-filter]:backdrop-blur-lg"
>
<!-- Sin backdrop-filter queda casi opaco, que es exactamente lo que quieres -->
</div>
El orden importa: el fondo casi opaco es el valor por defecto, y solo si el navegador soporta el efecto se baja la opacidad y se añade el blur.
En cuanto a rendimiento, backdrop-filter se vuelve a calcular cada vez que cambia lo que tiene detrás. Un panel de vidrio sobre contenido que se desplaza es de los efectos más costosos de CSS en móviles de gama baja. Además crea su propio contexto de apilamiento, así que a veces te descoloca los z-index.
Cuando lo he implementado sí he notado el impacto en el rendimiento y en la experiencia de algunos usuarios. El valor del blur es el que más cuidado pide: cuanto más grande el radio, más trabajo para el navegador, y un backdrop-blur-3xl puesto sin pensarlo puede arruinar la experiencia en un equipo modesto. Por eso lo uso en dos o tres elementos por página como mucho, y con el blur más bajo que todavía se lea como vidrio.
En la siguiente parte de la serie toca animar cosas con el scroll sin JavaScript: scroll-driven animations con Tailwind.

