wandres.dev
SOMBRAS Y PROFUNDIDAD · box-shadow, drop-shadow, elevación

Anatomía de box-shadow: desplazamiento, desenfoque y expansión

Qué significa exactamente el radio de desenfoque en términos de la gaussiana que lo produce, cómo funciona el spread sobre esquinas redondeadas, y en qué orden se pintan varias sombras.

⏱ 16 min

Los cuatro números de box-shadow no son cuatro ajustes independientes: son dos operaciones geométricas y una convolución, aplicadas en un orden concreto. Saber en qué orden se aplican explica por qué un spread negativo con blur produce un halo estrecho, por qué el borde de la sombra empieza dentro de la caja, y por qué duplicar el radio de desenfoque no duplica la zona difuminada.

🎯 Al terminar esta lección sabrás
  • Interpretar los cuatro valores de box-shadow y su orden obligatorio.
  • Explicar qué desviación típica corresponde a un radio de desenfoque dado.
  • Predecir el efecto del spread sobre esquinas rectas y redondeadas.
  • Ordenar varias sombras y saber dónde se pintan respecto al fondo.

Los cuatro valores

box-shadow: <offset-x> <offset-y> <blur> <spread> <color>;

Solo los dos desplazamientos son obligatorios. El desenfoque y la expansión valen cero si faltan, y el color sin especificar es currentColor, cosa que se olvida y produce sombras del color del texto en componentes que cambian de color.

El orden de las longitudes es fijo y no se puede reordenar: la tercera longitud siempre es el desenfoque y la cuarta siempre la expansión. El color y la palabra inset, en cambio, pueden ir antes o después del grupo de longitudes.

.a { box-shadow: 0 4px 12px rgb(0 0 0 / 0.2); }
.b { box-shadow: 0 4px 12px -4px rgb(0 0 0 / 0.2); }
.c { box-shadow: rgb(0 0 0 / 0.2) 0 4px 12px; }   /* color delante: valido */

El desenfoque no admite valores negativos; la declaración se descarta entera. La expansión sí.

Las sombras no ocupan espacio de layout. No desplazan a nada, no generan barras de desplazamiento y no cuentan para el tamaño del elemento. Esa es su diferencia esencial con un borde, y la razón de que se usen para anillos de foco.

Qué es el radio de desenfoque

Aquí está el número que más se malinterpreta. El radio de desenfoque no es la distancia que se difumina hacia fuera: es la anchura total de la transición, centrada en el borde de la sombra.

La especificación lo define así: para un borde recto y largo, el desenfoque debe producir una transición de color cuya longitud sea igual al radio de desenfoque, perpendicular al borde de la sombra y centrada en él, que vaya desde el color completo en el extremo interior hasta transparente en el exterior.

Es decir, con blur: 20px la transición ocupa veinte píxeles: diez hacia dentro de la sombra y diez hacia fuera. La sombra “crece” solo la mitad del radio que escribes.

En implementación, eso es una convolución con una gaussiana de desviación típica igual a la mitad del radio. blur: 20px es una gaussiana de sigma 10. Esa equivalencia es la que hay que tener en la cabeza cuando comparas una sombra con un desenfoque hecho de otra forma, y vuelve a aparecer al comparar box-shadow con drop-shadow(), donde la convención no es la misma.

Consecuencia práctica inmediata: para que una sombra se vea a cierta distancia hay que escribir el doble. Una sombra que quieras que se extienda unos doce píxeles más allá de la caja necesita blur: 24px, no 12.

El spread y las esquinas

La expansión modifica la forma de la sombra antes de desenfocarla. Un valor positivo infla el rectángulo en todas las direcciones; uno negativo lo encoge.

El orden importa: primero se toma la caja de borde, después se aplica el spread, después se desplaza, y por último se desenfoca. Por eso 0 4px 12px -4px produce una sombra que asoma solo por abajo: el spread negativo la mete cuatro píxeles por dentro por los cuatro lados, y el desplazamiento vertical la vuelve a sacar solo por uno.

Ese patrón —desplazamiento hacia abajo compensado con spread negativo— es el que produce la sombra que parece nacer del propio elemento en vez de rodearlo, y es la base de los sistemas de elevación decentes.

Sobre esquinas redondeadas, el spread ajusta los radios con una regla que sorprende: un radio de cero se queda en cero, y un radio distinto de cero crece con la expansión. Es decir, una caja de esquinas rectas con spread grande produce una sombra de esquinas rectas, no redondeadas; y una caja con border-radius: 4px y spread: 10px produce una sombra con radio de unos catorce.

La consecuencia visible es que la sombra de una caja poco redondeada se ve más redondeada que la caja, proporcionalmente. Con radios pequeños y spreads grandes conviene mirarlo, porque el desajuste se nota.

Varias sombras y dónde se pintan

box-shadow acepta una lista separada por comas. La primera de la lista se pinta encima, igual que las capas de fondo.

.anillo-doble {
  box-shadow:
    0 0 0 2px #1e1e2e,
    0 0 0 4px #89b4fa;
}

Ese ejemplo enseña el uso de la sombra sin desenfoque como anillo: 0 0 0 Npx color dibuja un contorno de N píxeles que sigue el border-radius y no ocupa layout. Apilando dos con radios crecientes tienes un anillo con separación, que es el patrón canónico de un :focus-visible visible sobre cualquier fondo.

El orden de pintado respecto al elemento es:

Las sombras exteriores se pintan debajo del fondo del elemento. Por eso una sombra exterior nunca se ve a través de un fondo semitransparente propio: está detrás.

Las sombras interiores se pintan encima del fondo y debajo del borde. Por eso una sombra inset sí tapa el fondo, y por eso el borde sigue viéndose por encima de ella.

text-shadow tiene la misma sintaxis con dos diferencias: no acepta spread ni inset. Sus tres longitudes son desplazamiento horizontal, vertical y desenfoque, con la misma relación de sigma igual a la mitad.

💡
La sombra sigue al border-radius, el outline no siempre

box-shadow: 0 0 0 3px produce un anillo que sigue exactamente la curva de las esquinas. outline con outline-offset hace algo parecido y su seguimiento del radio depende del motor. Si el anillo tiene que verse idéntico en todas partes, la sombra es más predecible; si tiene que verse también en modo de colores forzados, el outline es el que sobrevive, porque el sistema lo respeta y las sombras las elimina.

El spread negativo es un control de perspectiva, no un ajuste de tamaño

El uso de spread que aparece en los tutoriales es “hacer la sombra más grande o más pequeña”, y con esa lectura el valor negativo no tiene ningún sentido: si querías una sombra más pequeña, baja el desenfoque. Lo que el spread negativo hace de verdad es desacoplar el tamaño de la sombra de su distancia, y eso es exactamente lo que distingue un objeto apoyado de un objeto flotando. Piensa en la física: cuando un objeto está muy cerca de la superficie, su sombra es del mismo tamaño que él, está muy próxima y tiene el borde bastante definido. Cuando se separa, la sombra se aleja, se agranda y se difumina. Los tres cambios ocurren juntos porque los tres tienen la misma causa. En CSS, subir el desenfoque produce lo segundo y lo tercero pero también agranda la sombra por los cuatro lados por igual, cosa que en la realidad no pasa: la sombra crece hacia el lado contrario a la luz, no en todas las direcciones. Ahí entra el spread negativo. 0 8px 16px -8px produce una sombra que se ha metido ocho píxeles por dentro en los cuatro lados y ha salido ocho hacia abajo, es decir, que asoma únicamente por debajo del objeto y que arriba y a los lados está prácticamente oculta bajo él. Eso es lo que hace una luz que viene de arriba. Sin el spread negativo, la misma sombra asoma también por arriba, y el cerebro lee de inmediato que hay una segunda fuente de luz debajo del objeto, que es imposible, y todo el efecto se cae. La regla operativa que se deriva es sencilla y transforma un sistema de sombras entero: el spread negativo debería ser aproximadamente la mitad del desplazamiento vertical. Con 0 4px 8px -2px, 0 8px 16px -4px, 0 16px 32px -8px tienes tres niveles de elevación que se leen como tres alturas distintas de un mismo objeto bajo una misma luz, en lugar de como tres sombras de distinto tamaño.

⚔️ Descompón la sombra
  1. Aplica box-shadow: 0 0 20px black y mide con el cuentagotas dónde empieza y acaba la transición.
  2. Compara 0 8px 16px con 0 8px 16px -8px en el mismo elemento.
  3. Pon border-radius: 2px y spread: 12px y observa el desajuste de curvatura entre caja y sombra.
  4. Construye un anillo de foco de dos capas con sombras sin desenfoque.
  5. Comprueba que una sombra exterior no se ve a través de un fondo con alfa 0.5.