Curso de CSS / diseño fluido: anchuras relativas, min() y clamp()

Un diseño fluido permite que las dimensiones se adapten al espacio disponible mientras impone límites útiles. Los anchos relativos, max-width y funciones como clamp() pueden mantener el contenido legible sin forzar las mismas dimensiones fijas en cada pantalla.

Distingue el ancho del viewport y el ancho disponible

El área donde se muestra una página web en el navegador se llama ventana gráfica (viewport). Sin embargo, el ancho disponible para las tarjetas dentro del cuerpo puede ser menor que el de la ventana gráfica. Esto se debe a que la tarjeta debe ajustarse al espacio restante excluyendo el margen del contenedor padre, el espacio de otras columnas y el límite máximo de ancho.

El porcentaje en width: 100% utiliza el ancho del área base que rodea el elemento en un diseño de bloque típico. 100vw se basa en el ancho de la ventana gráfica, por lo que incluso si lo colocas dentro de una tarjeta pequeña, puede ser tan grande como toda la pantalla. Cuando se agregan barras de desplazamiento verticales y márgenes exteriores, puede ocurrir desbordamiento horizontal. Para el ancho de los elementos dentro del cuerpo, primero considera el porcentaje o el tamaño automático.

designación padres estrechos padres anchos Estándar de uso
width: 48rem Puede ser más grande que los padres Mantener 48rem No adecuado como valor predeterminado para contenido que debe adaptarse a una menor anchura
width: 100% Reducido para coincidir con los padres Continúa aumentando hasta el ancho del padre Elementos que necesitan llenar el padre
width: 100%; max-width: 48rem Reducido a menos de 48rem Hasta 48rem Texto que requiere un ancho de lectura limitado
min-width: 48rem No se reduce por debajo de 48rem Decidido según diferentes reglas de tamaño Usar solo cuando hay un tamaño mínimo requerido

Limita y centra el área de contenido

*, *::before, *::after { box-sizing: border-box; }
.content {
  width: 100%;
  max-width: 48rem;
  margin-inline: auto;
  padding-inline: 1rem;
}

Con border-box, el relleno y los bordes se ajustan dentro del ancho declarado. Si la fuente raíz es de 16px y el contenedor padre tiene 390px de ancho, el ancho exterior de este cuadro de contenido es de 390px y su relleno de 16px en cada lado deja 358px para el contenido. Con un ancho de contenedor padre de 1000px, el ancho exterior se detiene en 768px y los márgenes automáticos lo centran. Para más detalles, ver anchura, altura, límites min/max y box-sizing con CSS.

Aplicar content-box al mismo elemento agregará relleno a width: 100%, haciéndolo más ancho que su contenedor. Por otro lado, los márgenes tampoco se incluyen en border-box. Al especificar width: 100% y márgenes fijos en ambos lados juntos, comprueba la suma total. Si min-width y max-width entran en conflicto, comprueba min-width también, ya que el min-width puede hacer que sea más grande.

Usa min() y clamp()

min() selecciona el valor más pequeño entre los candidatos. width: min(100%, 48rem) usa el menor entre el ancho del contenedor y 48rem, por lo que los límites de ancho del 100% y el máximo especificados anteriormente pueden expresarse en una sola declaración.

.content { width: min(100%, 48rem); }
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); }

clamp(minimum, preferred, maximum) usa el valor preferido limitado por el mínimo y el máximo; no calcula una media aritmética. En el título de arriba, rem se basa en el tamaño de fuente raíz y vw se basa en el 1% del ancho de la ventana gráfica. Si el tamaño raíz es de 16px, el mínimo es 24px, el máximo es 40px y el valor preferido es 16px más el 2% del ancho de la pantalla.

ancho de la ventana gráfica Calcular valor central tamaño real del título
320px 16 + 6.4 = 22.4px Mínimo 24px
600px 16 + 12 = 28px 28px
1200px 16 + 24 = 40px 40px
1600px 16 + 32 = 48px Máximo 40px

Si haces que el texto del cuerpo dependa únicamente del ancho de la pantalla, puede volverse demasiado pequeño en pantallas estrechas o en situaciones con zoom. Al usar tamaños fluidos en los títulos, un buen punto de partida es tener un límite inferior legible y mantener el cuerpo en un tamaño basado en rem. Incluir rem en la expresión no garantiza por sí solo que el texto se amplíe de forma accesible, por lo que también necesitarás verificar la magnificación real del navegador y la configuración de fuente predeterminada. Puedes revisar el papel de cada argumento en la definición de clamp() y la explicación de accesibilidad de MDN.

Ejecuta el ejemplo de tarjeta fluida e imagen

Guarda el siguiente código como fluid-size.html en UTF-8 y ábrelo en el navegador. La imagen es datos SVG incluidos en el archivo, por lo que no es necesario descargar una imagen separada o una biblioteca externa. Compara el ancho del cuerpo, el número de columnas de la tarjeta y el tamaño del título en una pantalla amplia y en una pantalla estrecha de aproximadamente 390px. Si quieres colocar varias tarjetas de manera flexible, consulta Grid: repeat, auto-fit, auto-fill y áreas con nombre para ver cómo establecer el ancho mínimo para las columnas.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>fluid-size.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
.content { width: min(100%, 48rem); margin-inline: auto; padding: 1rem; }
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); line-height: 1.3; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1rem; }
.card { min-width: 0; padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.card img { display: block; max-width: 100%; height: auto; margin-bottom: 1rem; }
.token { font-family: monospace; }
</style>
</head>
<body>
<main class="content">
<h1>Size elements to fit the screen and content</h1>
<p>The content is at most 48rem wide and shrinks to fit its parent on smaller screens.</p>
<div class="cards">
  <article class="card"><img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22400%22%20height%3D%22300%22%20viewBox%3D%220%200%20400%20300%22%3E%3Crect%20width%3D%22400%22%20height%3D%22300%22%20fill%3D%22%23287699%22%2F%3E%3Crect%20width%3D%22400%22%20height%3D%2235%22%20fill%3D%22%23205c32%22%2F%3E%3Crect%20y%3D%22265%22%20width%3D%22400%22%20height%3D%2235%22%20fill%3D%22%239e3131%22%2F%3E%3Ccircle%20cx%3D%22200%22%20cy%3D%22150%22%20r%3D%2270%22%20fill%3D%22%23f6ae4c%22%2F%3E%3Crect%20x%3D%2212%22%20y%3D%22120%22%20width%3D%2230%22%20height%3D%2260%22%20fill%3D%22white%22%2F%3E%3Crect%20x%3D%22358%22%20y%3D%22120%22%20width%3D%2230%22%20height%3D%2260%22%20fill%3D%22white%22%2F%3E%3C%2Fsvg%3E" width="400" height="300" alt="Practice drawing with upper and lower bands and center circle"><h2>card with picture</h2><p>The image stays within the card width and preserves its original aspect ratio.</p></article>
  <article class="card"><h2>A heading and description that wrap onto several lines</h2><p>Without a fixed height, the card grows as its text gets longer.</p></article>
  <article class="card"><h2>Long identifier</h2><p class="token">ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</p></article>
</div></main>
</body>
</html>

El minmax(min(100%, 14rem), 1fr) de la tarjeta utiliza un ancho mínimo predeterminado de 14rem, pero si el contenedor padre es más estrecho, el mínimo pasa a ser el 100% de la anchura disponible del contenedor. auto-fit crea tantas columnas como caben según ese mínimo y colapsa las que quedan vacías, y el ancho restante se divide en columnas de 1fr. Debido al relleno de la tarjeta, el ancho real del texto y las imágenes es menor que el ancho exterior de la tarjeta. No juzgues el número de columnas por el nombre del dispositivo, sino por el ancho disponible para el contenedor padre.

min-width: 0 elimina una restricción de tamaño mínimo que podría impedir que la tarjeta se reduzca. overflow-wrap: anywhere proporciona saltos dentro de una URL larga o un identificador cuando es necesario. Tienen diferentes roles: permitir que la caja se reduzca no hace por sí solo que el texto se ajuste. El ajuste cambia la presentación, no el valor de la cadena copiada.

Prueba un cambio de tamaño a la vez

  1. Reduce la pantalla a 390px. El texto debe caber dentro de la pantalla, la tarjeta debe ir verticalmente, y las bandas sobre y debajo de la imagen y el círculo en el centro deben ser visibles.
  2. Cambia solo el ancho de .content a 48rem. Después de verificar por qué el texto se desborda en la pantalla estrecha, devuélvelo a min(100%, 48rem).
  3. Cambia solo el valor máximo del título a 3rem y observa la diferencia en una pantalla más ancha. Calcula el valor preferido para entender por qué este cambio no aumenta el título en pantallas estrechas. Después restaura el máximo de 2.5rem.
  4. Solo elimina overflow-wrap: anywhere de .card. Comprueba si los identificadores largos desbordan y restaura la declaración. Prueba también el tamaño de fuente predeterminado y el zoom del navegador por separado, registrando cada condición.

Antes de insertar overflow-x: hidden, que oculta el desbordamiento, en toda la página, comprueba el ancho, margen, ancho mínimo y cadena. Si solo ocultas el desbordamiento, el contenido o los elementos operativos pueden cortarse sin resolver la causa. Para los principios básicos de la configuración adaptable, también puedes consultar la guía de diseño responsivo de MDN.