planeta jupiter planeta tierra

clamp(): adiós a los breakpoints para la tipografía

· 7 min de lectura · CSS

Fichas de letras formando la palabra CLAMP
nave extraterrestre
Fichas de letras formando la palabra CLAMP

Tienes un titular a 2 rem en móvil y a 4 rem en escritorio, y por el camino tres @media para que no se vea ridículo en la tablet. Cada vez que cambias el diseño hay que tocar los cuatro sitios. clamp() hace eso mismo en una línea, y además sin saltos: el tamaño acompaña a la ventana de forma continua.

El problema: la escalera de breakpoints

.titulo { font-size: 2rem; }

@media (min-width: 576px)  { .titulo { font-size: 2.5rem; } }
@media (min-width: 768px)  { .titulo { font-size: 3rem; } }
@media (min-width: 1200px) { .titulo { font-size: 4rem; } }

Funciona, pero el texto va a saltos: a 767 px es pequeño y a 768 px pega un brinco. Y son cuatro lugares que mantener cada vez que el diseñador mueve una medida.

Qué hace exactamente clamp()

.titulo { font-size: clamp(2rem, 1.5rem + 3vw, 4rem); }

Tres valores: mínimo, preferido y máximo. El navegador calcula el preferido y lo recorta si se sale de los límites. Es decir, lo mismo que:

font-size: max(2rem, min(1.5rem + 3vw, 4rem));

El valor del medio es el único que se mueve, porque lleva una unidad relativa a la ventana. Visto en una línea de tiempo:

ventana estrecha          ventana media           ventana ancha
   2rem (mínimo)   →   1.5rem + 3vw (crece)   →   4rem (máximo)
   ─────────────       ╱                          ─────────────
   plano               crecimiento lineal         plano otra vez

La fórmula para no elegir los números a ojo

La parte que casi nadie cuenta: el valor preferido no se inventa, se calcula. Tú decides dos puntos —qué tamaño quieres en la ventana más estrecha y en la más ancha— y sale solo.

Supongamos: 16 px a partir de 320 px de ventana, y 24 px al llegar a 1280 px.

pendiente = (24 - 16) / (1280 - 320) = 8 / 960 = 0.00833
en vw     = 0.00833 × 100 = 0.833vw

base      = 16 - (0.00833 × 320) = 13.33px = 0.833rem
font-size: clamp(1rem, 0.833rem + 0.833vw, 1.5rem);

Ese 0.833rem es lo que hace que a 320 px de ventana el resultado sea exactamente 1 rem, y a 1280 px exactamente 1.5 rem. Entre medias, una recta.

Si no te apetece hacer cuentas, hay generadores que dan la expresión a partir de esos cuatro números; pero conviene entender la fórmula para saber qué estás pegando.

No es solo para texto

Cualquier propiedad que acepte una longitud acepta clamp(). Los espaciados son un caso tan bueno como la tipografía, porque un padding fijo de 4 rem asfixia en móvil y se queda corto en un monitor grande:

.seccion {
    padding-block: clamp(2rem, 5vw, 6rem);
    gap: clamp(1rem, 2vw, 2.5rem);
}

.contenedor {
    inline-size: clamp(20rem, 90%, 70rem);   /* ancho fluido con topes */
    margin-inline: auto;
}

Ese inline-size es un buen ejemplo del patrón completo: nunca menos de 20 rem, nunca más de 70 rem y, entre medias, el 90 % de lo que haya. Sustituye al típico width: 90%; max-width: 70rem; y además pone un suelo.

Una escala tipográfica entera

Definiendo los pasos como variables tienes un sistema completo que escala solo:

:root {
    --paso--1: clamp(0.83rem, 0.78rem + 0.22vw, 0.94rem);
    --paso-0:  clamp(1rem,    0.93rem + 0.35vw, 1.19rem);
    --paso-1:  clamp(1.2rem,  1.09rem + 0.53vw, 1.5rem);
    --paso-2:  clamp(1.44rem, 1.28rem + 0.8vw,  1.89rem);
    --paso-3:  clamp(1.73rem, 1.49rem + 1.19vw, 2.38rem);
}

h1 { font-size: var(--paso-3); }
h2 { font-size: var(--paso-2); }
p  { font-size: var(--paso-0); }

Cambias los extremos en un sitio y se recalcula toda la página. Eso es lo que convierte a clamp() en algo más que un truco suelto.

Aquí mismo, en este blog

El titular que has leído arriba está escrito así:

.article-title {
    font-size: clamp(2.25rem, 1.5rem + 3vw, 4rem);
    line-height: 1.05;
}

.article-content h2 {
    font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
}

Sin un solo @media para la tipografía. Si estrechas la ventana ahora mismo, verás el título encogerse de forma continua hasta plantarse en 2.25 rem.

Lo que se rompe si no lo sabes

  • Un valor preferido solo con vw es un problema de accesibilidad. clamp(1rem, 4vw, 2rem) ignora el zoom de texto del navegador: quien necesita ampliar la letra no puede. Incluye siempre una parte en rem (0.833rem + 0.833vw), que es lo que mantiene la proporcionalidad con el ajuste del usuario.
  • vw mira la ventana, no el contenedor. El mismo componente dentro de una barra lateral estrecha seguirá calculando el tamaño con el ancho total de la pantalla. Para eso están las container queries y la unidad cqi.
  • Si el mínimo es mayor que el máximo, gana el mínimo. No da error ni aviso: simplemente se queda clavado y te vuelves loco buscando por qué no escala.
  • El line-height no escala solo. Ponlo sin unidades (1.05, 1.7) para que siga al tamaño de fuente, y recuerda que un titular grande pide interlineado más apretado que uno pequeño.
  • Comprueba los extremos de verdad. Al no haber breakpoints es fácil no mirar nunca a 320 px ni a 2560 px, que son justo donde tus dos topes entran en juego.
  • No todo tiene que ser fluido. Los grosores de borde, los iconos de interfaz o las alturas de un botón suelen quedar mejor fijos: si todo se mueve, nada parece estable.

La chuleta

Quiero… Escribo
Texto fluido con topes clamp(1rem, 0.83rem + 0.83vw, 1.5rem)
Espaciado que escala padding-block: clamp(2rem, 5vw, 6rem)
Ancho con suelo y techo inline-size: clamp(20rem, 90%, 70rem)
Solo un tope superior min(90%, 70rem)
Solo un tope inferior max(2rem, 5vw)

Y ya está

Mínimo, preferido y máximo. Con la fórmula de los dos puntos eliges los números con criterio en lugar de a ojo, y con una parte en rem en el valor preferido no te cargas la accesibilidad. A cambio, borras la mitad de tus media queries y el diseño deja de dar brincos entre tamaños de pantalla.

← Volver al blog

Mi hija de 11 años me ayudó a crear el diseño de este portfolio.