EM VS REM: ¿Cuál debería usar?
¡Hola gente bonita!👋
En este articulo voy a explicar todo lo que tienes que saber sobre las unidades em
y rem
y porque se llaman de esa manera. Además voy a responder a la pregunta más común que se hacen todos: ¿Cuándo usar em
y cuando usar rem
?
🤔 ¿De donde viene el nombre em?
Si pensabas que la em tiene algo que ver con la letra “m”, te has equivocado❌ Pero si pensabas que tiene algo que ver con la letra “M”, has acertado ✅
La em debe su nombre originalmente a la anchura de la letra M mayúscula. La unidad em tiene una larga tradición en tipografía, esto significa que no ha sido creada por CSS sino que lleva décadas utilizándose en el campo de la tipografía, donde se ha utilizado para medir anchos horizontales. Por ejemplo, el guion largo ( — ) se conoce como “em-dash”, ya que históricamente ha tenido la misma anchura que la letra “M”.
✅ En la web las unidades em y rem no se basan en el ancho de la letra M, sino que se basan en el valor del font-size
.
📐 EM & REM
em
y rem
son unidades CSS pensadas para trabajar con tipografía, aunque también se utilizan para especificar dimensiones en otros elementos HTML.
Tanto em
como rem
son considerados unidades relativas, pero ¿relativo a que? o ¿respecto a que? 🤔 la respuesta es que son relativas al font-size
.
🔄 em
y rem
se computan o se traducen a valores de pixel. Por ejemplo si colocas 1em
o 1rem
como valor a un elemento HTML el navegador va a computar o convertir ese valor en un valor de pixel (px). Esto lo podemos comprobar con el siguiente ejemplo:
🔎 Si inspeccionas el elemento <h1>
y lo analizas en la DevTools (herramienta de desarrollo) de tu navegador y haces click en la pestaña de "Styles" (Estilos), el elemento <h1>
tiene un font-size: 2em;
, esto es porque por defecto el navegador asigna a los elementos <h1>
un tamaño de fuente de 2em
.
Pero si haces click en la pestaña “Computed” (calculado) podrás observar que el navegador convierte el tamaño de fuente de 2em
a 32px
Pero, ¿Por que lo convierte a 32px
? 🤔 Primero tienes que saber que la mayoría de los navegadores tienen un estándar en el que el tamaño base de la fuente o font-size
que asignan al texto es de 16px
, excluyendo por supuesto a los encabezados <h1>
a <h6>
y el elemento <small>
ya que el navegador les asigna otro tamaño de fuente.
El tamaño de fuente base que son16px
puede ser cambiado (en las preferencias del navegador) por el usuario a cualquier valor entre 9px
a 72px
.
Imaginemos que el tamaño de la fuente establecida en el navegador del usuario es exactamente 16px
. Un 1em
equivaldría a 16px
, mientras que 2em
sería el doble: 32px
. Por otro lado, 0.5em
sería la mitad: 8px
.
Entonces si calculamos, el valor es: 2em
x 16px
= 32px
. Probablemente te estarás preguntando si siempre se va a multiplicar por 16px
, y la respuesta es que no, y es aquí donde se genera una diferencia entre las unidades em y rem:
👉 Las unidades em para la propiedad font-size
serán relativas al font-size
del elemento padre, pero las unidades em
en otras propiedades (que no sean font-size
) serán relativas al font-size
del elemento actual.
👉 Las unidades rem
siempre serán relativas al font-size
del elemento raíz que es html
.
Vamos a explorar estas diferencias a continuación.
📏 EM
La unidad em permite establecer el tamaño de fuente o font-size
de un elemento en base al font-size
de su elemento padre mas cercano.
Ejemplo 1
Vemos este ejemplo sencillo:
.parent {
font-size: 18px;
}
.child {
font-size: 1.5em;
}
En este ejemplo, el elemento hijo .child
tendrá un font-size
de 27px
(1.5em x 18px = 27px). Esto es porque se multiplica por el font-size
del elemento padre ya que como dice la definición la unidad em se basa en el font-size
del padre. Esta es la respuesta del porque no siempre se multiplica por los 16px
del navegador.
Pero…si el elemento padre .parent
no tuviera un valor en el font-size
, se buscara un valor mas arriba en el árbol DOM. Si no se especifica el font-size
hasta el elemento raíz (<html>
), se utilizara el valor predeterminado del navegador que es 16px
.
Ejemplo 2
Puede ser un mal entendido decir que las unidades em
son relativas al font-size
del elemento padre. De hecho, como lo especifica el W3, son relativos al font-size
“del elemento en el que son usadas”.
✅ Las unidades em
pueden usarse para mucho mas que solo para establecer el font-size
, pueden usarse en otras propiedades como padding
, margin
, width
, height
, max-width
, etc. Cuando las unidades em
se usan en otras propiedades que no sea el font-size
, el valor es relativo al propio font-size
del elemento. Te explico con este ejemplo:
h2 {
font-size: 20px;
margin-top: 2em;
}
h3 {
margin-top: 2.5em;
}<h2>Hello</h2>
<h3>World</h3>
¿Cuál es el valor real del margin-top
del elemento <h2>
? La respuesta es 40px
puedes comprobarlo tu misma, de nuevo abre las DevTools de tu navegador, selecciona el <h2>
y ve a la pestaña “Computed”, como puedes ver el valor calculado que se muestra es 40px
.
¿Cómo se ha obtenido este valor? La formula del calculo para la propiedad CSS considerada (margin-top
) del elemento <h2>
es:
Multiplicar el valor em (2em
) por el valor real del font-size
en px
del elemento HTML al que se va aplicar el estilo (20px
). Para nuestro ejemplo, esto significa: 2em
x 20px
= 40px
.
Ahora, ¿Cuál seria el valor del margin-top
para el elemento <h3>
de este ejemplo?. En este caso el valor final es: 46.8px
. ¿Por que? 🤔
Como puedes ver en el código del ejemplo ⬆️, ambos elementos (<h2>
y <h3>
) son hermanos y son hijos de <body>
. Sin embargo no hemos declarado explícitamente un valor en el font-size
del elemento <h3>
como hicimos con el elemento <h2>
.
Pero en realidad el elemento <h3>
si tiene un valor en el font-size
que es de 1.17em
ya que este es el valor que asigna por defecto el navegador a los encabezados <h3>
. Lo puedes comprobar tu misma en la hoja de estilos del navegador:
Bien, pero esto tampoco es un valor en pixeles px
. Entonces tenemos que subir por la jerarquía de elementos padre hasta encontrar un valor en px
.
Con la ayuda de DevTools nos podemos dar cuenta que el elemento <body>
tiene un valor absoluto en font-size
que es de 16px
derivado del valor por defecto del navegador ya que no hemos especificado un valor personalizado.
Entonces con esta información podemos calcular el valor especifico: 2.5em x 1.17em x 16px = 46.8px
Así que recuerda lo siguiente: Cuando se utilizan unidades em en el font-size
el tamaño es relativo al font-size
del padre. Cuando se utiliza en otras propiedades es relativo al font-size
del propio elemento.
Ejemplo 3
Veamos otro ejemplo para que quede mas claro:
.parent {
font-size: 18px;
}
.child {
font-size: 1.5em; 👆 /* = 27px */
padding: 2em 1em; 👆
}
El padding-top
y padding-bottom
del elemento hijo .child
será de 54px
, si hacemos los cálculos: 2em x 27px = 54px.
El padding-left
y padding-right
del elemento hijo .child
sera de 27px
, si hacemos los cálculos: 1em x 27px = 27px.
👨👦 El efecto compuesto: la herencia en unidades em
Hasta aquí todo va bien con el uso de la unidad em, pero puede complicarse cuando se trata de la herencia, porque cada elemento HTML automáticamente hereda su valor del font-size
de su elemento padre y esto puede llevar a un comportamiento no deseado en elementos anidados con valores em. Cabe mencionar que las unidades como px
, vw
o rem
NO están sujetas a la herencia.
Como bien sabes la propiedad font-size
es una propiedad heredable. Las propiedades heredables y no heredables las explique en mi articulo de la herencia, así que te invito a leerlo.
Vamos a explicar esto con un ejemplo, solo que con varios elementos anidados, así que vamos a utilizar las listas:
En nuestro ejemplo, el font-size
de los elementos anidados mas profundos es mayor de lo esperado. ¿A que se debe esto? 🤔 Vamos analizar el código:
- El
font-size
para el nivel 1(top level) es de32px
:2em
(selector<li>
*16px
(font-size
heredado del<html>
). - El
font-size
para el nivel 2 (second level) es de64px
:2em
(selector<li>
*32px
(font-size
heredado del nivel 1<li>
). - El
font-size
para el nivel 3 (third level) es de128px
:2em
(selector<li>
*64px
(font-size
heredado del nivel 2<li>
). - El
font-size
para el nivel 4 (fourth level) es de256px
:2em
(selector<li>
*128px
(font-size
heredado del nivel 3<li>
).
Para solucionar este problema de forma que se garantice que todos los elementos <li>
tengan el mismo tamaño de fuente, se podría añadir:
ul { font-size: 16px };
De manera que todos los <ul>
tienen un valor fijo de 16px
entonces los <li>
ya no heredan del anterior <li>
sino del <ul>
que tienen cerca, por lo tanto todos los <li>
tendrán un tamaño de fuente de 32px
.
Lo mas probable es que no quieras usar em
en este escenario, en su lugar, usa rem
ya que em
puede convertirse en un problema y puede llevar a consecuencias no deseadas en tus diseños.
Este problema es la razón por la que se creo la unidad rem
. El uso de rem
no causa un “problema de herencia” del font-size
, rem
no cambiara sin importar la profundidad a la que este anidado por lo que es una opción fiable y es la medida preferida para la propiedad font-size
.
📏REM
La unidad rem
, abreviatura de root em, se basa siempre en el valor del font-size
del elemento raíz, que es el elemento <html>
Si el elemento <html>
no tiene un font-size
especificado, se utiliza el valor por defecto del navegador que es 16px
.
Los valores en rems se calculan multiplicando en base al valor del font-size del elemento root (<html>
) del documento:
Si el font-size
del <html>
es 16px
, 1rem sería igual a 16px en cualquier parte del documento.
Al utilizar la unidad rem
se ignoran los valores del font-size
de los elementos padre y solo se tiene en cuenta el valor del font-size
del <html>
.
Como puedes ver en el ejemplo anterior de Codepen 👆, el uso de unidades rem
nos permite evitar el “efecto compuesto” de las unidades em
. Con rem
las cosas se basan siempre y consistentemente en el font-size
del <html>
.
Lo mismo ocurre con otros valores distintos del font-size
(margin
, padding
,…). El uso de las unidades rem
en ellos seguirá siendo relativo al font-size
del <html>
.
Cuando el navegador analiza un documento HTML, crea una representación en memoria de todos los elementos de la pagina. Esta representación se llama DOM (Document Object Model). Es como una estructura de arbol, donde cada elemento esta representado por un nodo. El elemento <html> es el nodo de nivel superior, después de el están los nodos hijos que son <head> y <body> y debajo de estos están sus hijos, después los hijos de estos, y así sucesivamente.
El nodo raíz, es decir el <html>
es el ancestro de todos los demás elementos del documento. Dispone de un selector especial de pseudoclase llamado :root
que puede utilizarse para dirigirse a el. Esto equivale a utilizar el selector de tipo <html>
Como dije al principio el elemento <html>
heredara cualquier tamaño de fuente predeterminado, establecido en el navegador. Por ejemplo, considera un sitio web donde no se haya establecido la propiedad font-size
en el <html>
, si un usuario tiene su navegador con el tamaño de fuente predeterminado que es 16px
, el font-size
del <html>
será de 16px
.
Si el usuario sube el tamaño de fuente de su navegador, por ejemplo a 18px
, el tamaño de fuente del <html>
se convierte a 18px
.
🥴 ¿Cuándo usar em y rem?
El uso de em y rem van de la mano con los temas de responsive design, usabilidad y accesibilidad. En realidad no hay una unidad mejor, todo depende de tus preferencias personales.
A muchos desarrolladores les gusta diseñar todo en unidades rem para mantener la coherencia. Mientras que a otros les gusta utilizar también unidades em solo en lugares específicos en los que la influencia de elementos padre tendría sentido.
Sin embargo, te daré algunas reglas generales que se usan en algunas situaciones.
🧐 Cuando y como utilizar em:
- Para la propiedad
padding
en componentes como botones o inputs.
El siguiente ejemplo se muestra cómo se puede utilizar em para el diseño de componentes responsive.
El border, el padding y el border-radius utilizan unidades em que se relacionan con el valor del font-size. El botón crece de manera proporcional al tamaño de fuente del texto. Los diferentes tamaños de los botones se definen con los selectores de clase concretos (por ejemplo, size-l).
La ventaja de este enfoque es que puedes reutilizar fácilmente estos componentes en diferentes lugares. Sólo tienes que definir diferentes valores de tamaño de fuente para los diferentes contenedores de contexto, y luego la herencia hace el resto.
- Puedes usar em Para la propiedad
letter-spacing
, usando un micro valor como0.03em
, también puede ser un valor negativo. - Puedes usar la unidad
em
en los breakpoints de las media queries.
Muchas veces se configura los breakpoints en unidades em
para mantener la coherencia en todos los navegadores porque Safari hace algo diferente si usa rem o pixeles en comparación con los otros navegadores.
@media (min-width: 62em){ } ✅
Zell Liew tiene un articulo de pruebas que hizo con diferentes unidades (em, rem y px), concluyendo que los em fueron “la única unidad que se mostro constante en los cuatro navegadores”. Hay algunos errores en Safari que Dan Burzo ha documentado, en los que al hacer zoom el breakpoint de un Smartphone se muestra antes de lo que debería.
🧐 Cuando y como utilizar rem:
- Para la propiedad
font-size
.
En mi articulo anterior te mencione que es recomendable usar unidades rem para el texto porque tiene importantes beneficios de accesibilidad, se adaptan a las preferencias del usuario, mientras que si usa pixeles puede sobrescribir estas preferencias.
1️⃣ Muchos desarrolladores restablecen el font-size
del <html>
a62.5%
. De este modo reducen el valor predeterminado de 16px
hasta 10px
y así pueden realizar los cálculos en múltiplos de 10, más simple y sencillo; de manera que si quieres un texto en 21px
puede establecer el font-size
del texto en 2.1rem
porque 2.1 * 10 = 21.
Entonces 1rem
= 10px
html {
font-size: 62.5%; /* = 10px */
}
⚠️Este patrón, o mejor dicho, antipatrón (nombrado así en el libro de CSS in Depth) ha sido común durante los últimos años.
Algunos desarrolladores no recomiendan esto, porque esto toma el tamaño de fuente por defecto del navegador que es 16px, y lo reduce a 10px. Esta práctica simplifica las matemáticas: Si tu diseñador te dice que hagas la fuente de un elemento en 14px, puedes fácilmente dividir por 10 en tu cabeza y escribir 1.4 rem, todo mientras sigues unidades relativas.
Inicialmente, esto puede ser conveniente, pero hay un problema con este enfoque. En primer lugar, le obliga a escribir un montón de estilos duplicados. Diez píxeles es demasiado pequeño para la mayor parte del texto, por lo que tendrá que anularlo en toda la página.
Es recomendable que se trabaje en una base 16 (no de 10px), ya que esta es el tamaño de fuente al que esta acostumbrado el usuario.
Te encontrarás configurando los párrafos a 1.6 rem (16px) y en todos los componentes en los que quieras ese tamaño de fuente. Esto introduce más lugares de error, más puntos de contacto en su código cuando necesita cambiar, y aumenta el tamaño de su hoja de estilos.
2️⃣ Otra que es parecida a la anterior es la de Elad Shechter que la mencionó en su cuenta de Twitter:
3️⃣ Algunos desarrolladores establecen el 100%
como valor base para el font-size
del elemento <html>
lo que equivale a 16px que es el tamaño de fuente de la configuración del navegador.
html {
font-size: 100%; /* 100% = 16px */ ✅
}
Con este enfoque, estamos utilizando un tamaño de fuente base del 100%
y rems para todo lo demás, por lo que el sitio web será accesible. El usuario puede aumentar el tamaño del contenido mostrado ajustando la configuración del tamaño de fuente de su navegador, o haciendo zoom.
Este es el enfoque estándar, que es accesible y es uno de los mas recomendados. Aunque también puedes colocar en el <body>
el valor de 1rem
ya que de todas maneras la unidad rem se basara en el valor del font-size de <html>
que es 16px derivado de la configuración del navegador.
body {
font-size: 1rem; /* 1rem = 16px */ ✅
}
4️⃣Otro enfoque es el tema de la tipografía fluida o Fluid Typography que se refiere a una forma de ajustar las fuentes para que escalen a medida que aumente el ancho del viewport y no se requiere de media queries.
Este es un tema un poco avanzado en el que necesitas conocer sobre funciones como calc()
, clamp()
, unidades de viewport, etc. Hay una variedad de opciones para realizar una tipografía responsiva.
✅ Mantener un sitio web con múltiples breakpoints donde los tamaños de fuente cambian en muchos elementos diferentes puede ser una pesadilla, pero gracias al CSS moderno hay algunos enfoques diferentes que podemos tomar para hacer esto mas fácil, por ejemplo utilizando las variables CSS o funciones como clamp()
.
Dado que las unidades em y rem van de la mano con la tipografía, si te interesa saber sobre este tema, hice un thread en Twitter sobre los artículos recomendados para aprender sobre tipografía.
🔄Convertir de px a rem
Cuando vas empezando a diseñar sitios web es común pensar en pixeles y luego convertirlos en unidades em o rem o si te ha tocado trabajar con sistemas de diseño, puede que hayas recibido especificaciones de diseño basadas en píxeles con lo cual puedes utilizar una calculadora en línea que haga el trabajo por ti.
Aunque también puedes hacer esto automáticamente con un preprocesador como Sass y hacer una función que te permita convertir de px a ems o rems. Un ejemplo interesante es el que comparte Elad Shechter en su cuenta de Twitter:
Si aun no usas Sass puedes usar la extensión de vscode llamada px to rem.
📚 Lecturas Recomendadas
- So, How Much Is An Em? | Learn — Scannerlicker!
- rem vs em Units in CSS | DigitalOcean
- Understanding relative CSS units — LogRocket Blog
- Guía Exhaustiva: Cuando Utilizar Em vs. Rem (tutsplus.com)
- https://dev.to/5t3ph/guide-to-css-units-for-relational-spacing-1mj5
- Como utilizar em y rem en CSS — CybMeta
- PX, EM or REM Media Queries? | Zell Liew (zellwk.com)
- What media query breakpoints should you use? (2021) — Coder Coder (coder-coder.com)
- Using a typographic scale (kevinpowell.co)
- The elements of responsive typography — LogRocket Blog
- Generating `font-size` CSS Rules and Creating a Fluid Type Scale | Modern CSS Solutions
- CSS In Depth (Libro)
Gracias por leer 🦸🏻♀️
Mis redes sociales donde comparto notas de código: