rem vs em en una demo anidada
rem es relativo al tamaño de letra de la raíz (el elemento <html>, 16px por defecto). em es relativo al tamaño de letra actual, que en font-size significa el del padre. Anida la misma clase tres veces y se ve la diferencia:
Los tamaños impresos son los que calculó el navegador. Cada 1.25em multiplica al de su padre: 20px, luego 25px, luego 31.25px. Cada 1.25rem solo mira a la raíz: 20px, 20px, 20px. Esa acumulación es la razón por la que los tamaños de letra en em se descontrolan en listas y menús anidados, y por la que la mayoría de los sitios definen los tamaños de letra en rem.
Respecto a qué es relativa cada unidad
| Unidad | Relativa a | 1 unidad por defecto |
|---|---|---|
px | nada (un píxel CSS) | 1px |
rem | el tamaño de letra del elemento raíz (html) | 16px |
em | el tamaño de letra del elemento; para el propio font-size, el del padre | 16px en el nivel superior |
% | depende de la propiedad (ver abajo) | |
vw / vh | el 1% del ancho / alto del viewport | |
dvh, svh, lvh | el 1% de la altura dinámica, pequeña o grande del viewport | |
vmin / vmax | el 1% del lado menor / mayor del viewport | |
ch | el ancho del "0" en la fuente actual | más o menos medio em |
ex | la altura de la x de la fuente actual | más o menos medio em |
Un píxel CSS no es un píxel del dispositivo: en un teléfono con pantalla 3x, 1px mide 3 píxeles del dispositivo. Mantiene más o menos el mismo tamaño visual en todas las pantallas, y por eso px sigue sirviendo para bordes y sombras.
em para espaciados que escalan con el texto
em no es solo para tamaños de letra. En padding, margin, width o border-radius significa "veces el tamaño de letra de este elemento". Eso hace que una sola clase de botón funcione a cualquier tamaño:
El padding de cada botón es la mitad de su tamaño de letra en vertical y un tamaño de letra en horizontal, así que los tres parecen el mismo botón en tres tamaños. Con un padding en px necesitarías una regla de padding distinta para cada tamaño.
Ten en cuenta que los botones no heredan por defecto el tamaño de letra de la página (los navegadores les dan a los controles de formulario el suyo), así que el botón del medio mide aquí 13.33px en Chrome, no 16px. Agrega font: inherit a los botones e inputs cuando quieras que coincidan con la página.
Porcentajes: depende de la propiedad
% no tiene un significado único. Cada propiedad indica de qué es porcentaje:
| Propiedad | % es relativo a |
|---|---|
width, max-width, left, right | el ancho del padre (del bloque contenedor) |
height, top, bottom | la altura del padre, y solo si el padre tiene una altura definida |
padding, margin (los cuatro lados) | el ancho del padre |
font-size | el tamaño de letra del padre (150% = 1.5em) |
line-height | el tamaño de letra del propio elemento |
transform: translate() | el tamaño del propio elemento |
width: 50% son 200px, la mitad del padre. padding-top: 5% son 20px: el 5% del ancho del padre, no de su alto. Y height: 50% no hace nada, porque el padre no tiene una altura definida, así que el hijo solo mide lo que su texto más ese padding. La página de width y height explica esa regla en detalle.
vw, vh y la barra de direcciones del celular
1vw es el 1% del ancho del viewport y 1vh el 1% de su altura. Son útiles para secciones de pantalla completa y para tamaños fluidos (a menudo dentro de clamp()). En los teléfonos, vh tiene una trampa: la barra de direcciones del navegador entra y sale al desplazarte, y 100vh es la altura sin la barra, así que un hero con height: 100vh queda cortado abajo cuando se ve la barra. Las unidades más nuevas lo resuelven:
| Unidad | Altura que usa |
|---|---|
100vh | el viewport grande (barras ocultas) en la mayoría de los navegadores móviles |
100svh | el viewport pequeño (barras visibles): siempre cabe |
100lvh | el viewport grande, de forma explícita |
100dvh | el viewport dinámico: cambia a medida que se mueven las barras |
.hero {
min-height: 100vh; /* fallback for older browsers */
min-height: 100svh; /* fits the screen even with the address bar visible */
}
vw tiene su propia trampa: 100vw incluye el ancho de una barra de desplazamiento vertical cuando esa barra ocupa espacio (como en Windows), así que width: 100vw puede ser más ancho que la página y agregar una barra horizontal. Usa width: 100% para "todo el ancho del padre".
ch para una longitud de línea legible
ch es el ancho del carácter "0", así que max-width: 65ch limita un párrafo a unos 65 caracteres por línea, una longitud de lectura cómoda, con cualquier tamaño de letra:
Qué unidad usar
| Para | Usa | Por qué |
|---|---|---|
| Tamaños de letra | rem | Coherente en todas partes, respeta la configuración de tamaño de letra del lector |
| Padding y gaps en componentes | rem o em | em cuando debe crecer con el propio texto del componente |
| Bordes, líneas finas, sombras | px | Deben seguir siendo finos con cualquier tamaño de letra |
| Anchos de layout | %, fr, max-width en rem o ch | Siguen al contenedor, limitan la longitud de línea |
| Secciones de pantalla completa | svh / dvh (con vh de respaldo) | Manejan las barras de direcciones móviles |
| Tipografía fluida | clamp() combinando rem y vw | Crece con la pantalla dentro de unos límites |
Por qué rem respeta a los lectores y px no
Los navegadores tienen una configuración para el tamaño de letra por defecto, que usan las personas que necesitan un texto más grande. Cambia el tamaño de letra de la raíz, así que todo lo que está en rem y em crece con él. Un font-size: 14px se queda en 14px elija lo que elija el lector. El zoom del navegador también escala los px, pero la configuración de tamaño de letra es la que usan muchos lectores. Esta es la razón principal para definir los tamaños de letra en rem.
Algunos sitios definen html { font-size: 62.5%; } para que 1rem equivalga a 10px y las cuentas sean más fáciles. Sigue respetando la configuración del lector (el 62.5% de lo que haya elegido), pero acuérdate de poner un font-size legible en body, o todos los elementos sin tamaño propio muestran texto de 10px.
Errores comunes
- Tamaños de letra en
emen elementos anidados. Se multiplican en cada nivel. Usarempara los tamaños de letra. - Un
height: 50%que no hace nada. El padre necesita una altura definida para que funcione una altura en porcentaje. - Esperar que
padding-top: 10%siga la altura del padre. Los porcentajes de padding y margin verticales usan el ancho del padre. width: 100vwque provoca una barra de desplazamiento horizontal. Incluye la barra de desplazamiento. Usa100%.- Un
height: 100vhcortado en los teléfonos. Usa100svho100dvh. - Definir
html { font-size: 12px }. Un valor en px en la raíz sobrescribe la configuración de tamaño de letra del lector en toda la página. No toques la raíz o usa un porcentaje.
Preguntas frecuentes
¿Qué diferencia hay entre rem y em?
rem es relativo al tamaño de letra del elemento raíz (<html>), así que 1.5rem tiene el mismo tamaño en toda la página. em es relativo al tamaño de letra del propio elemento (en font-size, al del padre), así que se acumula cuando los elementos están anidados.
¿Cuántos píxeles es 1rem?
16px por defecto, porque los navegadores ponen el tamaño de letra de la raíz en 16px. Si el usuario sube el tamaño de letra por defecto en la configuración de su navegador, o defines html { font-size: ... }, 1rem cambia con él.
¿Uso rem o px para el tamaño de letra?
Usa rem para los tamaños de letra. Respeta la configuración de tamaño de letra por defecto del lector en el navegador, que px ignora. El zoom del navegador escala los dos, pero no todos los lectores usan el zoom.
¿Cuándo uso em en lugar de rem?
Usa em para espaciados que deben escalar con el propio texto del elemento, como el padding de un botón o el tamaño de un ícono junto al texto. Así la misma clase se ve bien con cualquier tamaño de letra. Usa rem para los tamaños de letra y los espaciados de la página, para que nada se acumule.
¿Qué diferencia hay entre 100vh y 100dvh?
En los navegadores móviles, 100vh es la altura con la barra de direcciones oculta, así que una sección de 100vh es más alta que la pantalla visible mientras se ve la barra. 100dvh (altura dinámica del viewport) sigue a la barra cuando aparece y desaparece, y 100svh es siempre la altura más pequeña.