wandres.dev
FONDOS Y BORDES · Capas, repetición y radios

Radios elípticos y la sintaxis de la barra

Los ocho números que caben en border-radius, el algoritmo de reducción cuando dos radios no caben en un lado, y por qué el radio interior de una caja anidada no es el mismo que el exterior.

⏱ 16 min

border-radius acepta ocho valores, no cuatro, y esa segunda mitad —los radios verticales, detrás de una barra— es lo que separa una esquina de circunferencia de una esquina de elipse. Con ella se dibujan formas que parecen exigir SVG, y con el algoritmo de reducción que la especificación aplica cuando los radios no caben se entiende por fin por qué border-radius: 50% produce una elipse perfecta y no cuatro cuartos de círculo cortados.

🎯 Al terminar esta lección sabrás
  • Escribir los ocho radios de una caja con la sintaxis de barra.
  • Aplicar el algoritmo de reducción proporcional y saber cuándo se dispara.
  • Calcular el radio interior correcto de un elemento anidado.
  • Reconocer qué sigue el radio y qué no.

Ocho números, no cuatro

La forma completa es dos listas separadas por una barra: primero los radios horizontales de las cuatro esquinas, después los verticales.

/* Cuatro esquinas circulares de 12px. */
.a { border-radius: 12px; }

/* Horizontales 40px, verticales 20px: esquinas elipticas. */
.b { border-radius: 40px / 20px; }

/* Cada esquina con su par. */
.c { border-radius: 40px 10px 40px 10px / 20px 30px 20px 30px; }

El orden de las esquinas es el de las agujas del reloj empezando arriba a la izquierda: superior izquierda, superior derecha, inferior derecha, inferior izquierda. Con menos de cuatro valores se aplica la abreviatura habitual: con uno, las cuatro iguales; con dos, la primera y tercera toman el primer valor; con tres, la segunda y cuarta toman el segundo.

Sin barra, la lista sirve para los dos ejes y todas las esquinas son arcos de circunferencia. Con barra, cada esquina es un cuarto de elipse cuyos semiejes son su radio horizontal y su radio vertical.

Los porcentajes se resuelven de forma distinta según el eje: los horizontales contra el ancho de la caja de borde, los verticales contra el alto. Por eso border-radius: 50% produce una elipse inscrita exacta en cualquier proporción, y no cuatro arcos que no casan.

De ahí sale una de las formas más útiles de la propiedad, la hoja o el pétalo:

.hoja { border-radius: 60% 0 60% 0 / 60% 0 60% 0; }

Y también las formas orgánicas de fondo, que se consiguen dando ocho porcentajes distintos y son animables porque todos los valores son del mismo tipo:

.mancha {
  border-radius: 42% 58% 63% 37% / 44% 39% 61% 56%;
  transition: border-radius 600ms;
}
.mancha:hover {
  border-radius: 58% 42% 37% 63% / 39% 44% 56% 61%;
}

Cuando los radios no caben

Si la suma de los dos radios de un mismo lado supera la longitud de ese lado, los arcos se cruzarían. La especificación no descarta la declaración ni recorta: escala todos los radios a la vez.

El algoritmo calcula, para cada uno de los cuatro lados, el cociente entre la longitud del lado y la suma de los dos radios que le tocan. Si el menor de esos cuatro cocientes es menor que 1, todos los radios se multiplican por él.

Dos consecuencias que importan.

La reducción es global, no local. Basta con que un solo lado se pase para que las cuatro esquinas encojan. Un elemento con radios grandes que se estrecha por debajo de cierto ancho ve cómo se redondean menos también las esquinas del lado largo, y a menudo eso se percibe como un bug de la hoja de estilos cuando es el comportamiento especificado.

Es proporcional, así que la forma se conserva. Todos los radios se multiplican por el mismo factor, con lo que las relaciones entre ellos no cambian. Una forma de hoja sigue siendo una hoja, más pequeña.

De aquí sale por qué border-radius: 9999px produce la píldora perfecta: el factor de escala reduce los radios exactamente hasta la mitad de la dimensión menor, que es el máximo posible, y el resultado es siempre un semicírculo en cada extremo sea cual sea el tamaño. Es más robusto que calcular border-radius: calc(var(--alto) / 2), porque no depende de conocer la altura.

El radio de una caja anidada

Cuando un elemento redondeado contiene otro, poner el mismo radio a los dos produce un desajuste visible: la curva interior parece demasiado abierta y queda una franja de grosor desigual entre ambas.

La razón es geométrica. Las dos curvas tienen que ser concéntricas, no iguales. El radio de la curva interior es el de la exterior menos la distancia que las separa:

radio interior = radio exterior - separacion

Y la separación es el grosor del borde más el relleno.

.tarjeta {
  --radio: 16px;
  --relleno: 12px;

  border-radius: var(--radio);
  padding: var(--relleno);
}

.tarjeta > .contenido {
  border-radius: calc(var(--radio) - var(--relleno));
}

Con radios pequeños la resta puede dar negativo, y un border-radius negativo es inválido y descarta la declaración. Ponle suelo:

border-radius: max(0px, calc(var(--radio) - var(--relleno)));

El mismo cálculo se aplica de forma automática dentro de un elemento con borde: la curva del borde interior —la que usan background-clip: padding-box y el recorte de overflow— es la exterior menos el grosor del borde, exactamente con esta fórmula.

ℹ️
Qué sigue el radio y qué no

Siguen la curva: el color de fondo, las capas de imagen de fondo, el recorte de overflow cuando no vale visible, box-shadow en su versión exterior e interior, y el borde. No la siguen: outline, que solo la sigue si el navegador implementa el redondeado automático del contorno, y border-image, que dibuja siempre sobre un rectángulo y es el motivo de que un borde de degradado hecho con border-image pierda las esquinas redondeadas.

La curva no es un arco de circunferencia, y las esquinas anidadas nunca cuadran del todo

Dos verdades incómodas sobre las esquinas redondeadas de CSS que explican por qué el resultado nunca acaba de verse como en la maqueta. La primera es sobre la curva en sí. Un border-radius produce un arco de circunferencia o de elipse, y esa curva tiene una discontinuidad de curvatura en el punto donde se une con el lado recto: la curvatura pasa de golpe de un valor constante a cero. El ojo lo detecta, y ese salto es lo que hace que las esquinas de CSS se vean ligeramente “duras” comparadas con las de los sistemas de diseño que usan curvas de continuidad G2, que interpolan la curvatura en vez de cortarla. Es la razón de que los iconos de las plataformas móviles no usen circunferencias sino superelipses, y de que redondear más un elemento en CSS no produzca la sensación de suavidad que se esperaba: no falta radio, falta continuidad, y CSS no tiene forma de expresarla en border-radius. Si de verdad importa, la salida es clip-path con una ruta que sí describa la superelipse. La segunda es sobre el anidamiento. La fórmula del radio interior es exacta solo cuando la separación es la misma en los cuatro lados. En cuanto el relleno es distinto en horizontal y en vertical, la curva interior concéntrica ya no es un arco de circunferencia sino una elipse cuyos semiejes son radio - relleno-horizontal y radio - relleno-vertical, y hay que escribirla con la sintaxis de barra: border-radius: calc(R - Ph) / calc(R - Pv). Casi nadie lo hace, y por eso las tarjetas con relleno asimétrico tienen esquinas interiores que no siguen a las exteriores. Es un detalle de un píxel o dos, invisible en una captura y perfectamente visible en una pantalla grande cuando hay veinte tarjetas seguidas.

⚔️ Dibuja con radios
  1. Construye una hoja con la sintaxis de barra y anímala hacia otra combinación.
  2. Reduce el ancho de un elemento con radios grandes hasta que se dispare la reducción y observa las cuatro esquinas.
  3. Comprueba que border-radius: 9999px da la misma píldora a cualquier altura.
  4. Anida dos cajas redondeadas con relleno asimétrico y escribe el radio interior con barra.