learning-front

Nivel 1 · HTML y CSS: la estructura y la piel

Color, unidades y tipografía

Antes de tocar el layout, el CSS necesita un vocabulario: cómo se escribe un color (nombre, hex, rgb, hsl), qué unidad elegir para qué (px, rem, em, %, vw) y cómo construir una jerarquía tipográfica legible. Son las decisiones que están detrás de cualquier interfaz con aspecto profesional.

El HTML ya está en su sitio. Las cajas están estructuradas, los elementos son semánticos, los selectores funcionan. Ahora viene la capa que decide cómo se ve todo eso: qué color tiene el texto, qué tamaño tienen los títulos, con qué unidad mides las cosas para que no se rompan cuando el usuario tiene configuraciones distintas.

En este capítulo seguimos trabajando sobre el Overwatch Team Builder. La ficha de Tracer ya tiene marcado; tu trabajo es darle aspecto con color, unidades y tipografía. El layout llega en el capítulo siguiente —y las propiedades de caja como margin, padding o border, que dan espacio y separación a los elementos, las verás en Box model y Flexbox.

Color#

CSS entiende el color de cuatro formas distintas. Cada una tiene su momento.

Nombres de color#

La más directa: red, teal, coral, steelblue. Son fáciles de escribir y se leen bien en el código.

css
.alerta {
  /* color por nombre: directo de escribir, útil para pruebas */
  color: red;
}

El problema es que hay colores que existen en la especificación pero no en la paleta que tiene el diseño de tu proyecto. red es demasiado brillante para casi cualquier interfaz real; teal puede no coincidir con el verde exacto que usa tu diseñador. Los nombres son útiles para pruebas rápidas y para aprender, no para el código de producción.

Hexadecimal#

La notación más habitual en proyectos reales: un # seguido de seis caracteres que representan los canales rojo, verde y azul en base 16.

css
.brand-acento {
  /* color en hexadecimal: b8 de rojo, 33 de verde, 6a de azul */
  color: #b8336a;
}

#b8336a se lee como: b8 de rojo, 33 de verde, 6a de azul. No hace falta memorizar la base 16: en la práctica eliges los colores desde un selector visual (en Figma, en las DevTools del navegador, en cualquier paleta) y copias el hex resultante.

Si los dos caracteres de cada canal son iguales, puedes abreviar a tres: #336699#369. Para diseños con variables CSS, la forma larga es más legible y común.

rgb() y rgba(): canal rojo, verde, azul#

La misma información que el hex, escrita en decimal y separada por comas. Más larga de escribir, pero más cómoda cuando necesitas opacidad:

css
/* Sin opacidad */
background-color: rgb(184, 51, 106);

/* Con opacidad: el cuarto valor va de 0 (transparente) a 1 (opaco) */
background-color: rgba(184, 51, 106, 0.15);

rgba con un valor bajo de alpha es el truco habitual para crear un fondo tintado sutil sobre una card, o para una sombra de texto sin usar colores grises fijos.

hsl(): tono, saturación y luminosidad#

HSL (Hue, Saturation, Lightness) es la notación más intuitiva para los ajustes humanos sobre el color:

css
/* hsl(tono en grados, saturación %, luminosidad %) */
color: hsl(336, 57%, 46%);
  • Tono: un ángulo en la rueda de color (0 y 360 = rojo, 120 = verde, 240 = azul).
  • Saturación: 0% es gris puro; 100% es el color más vivo.
  • Luminosidad: 0% es negro; 100% es blanco; 50% es el color “normal”.

Para oscurecer el acento del Team Builder, reduces el tercer valor. Para una versión más suave, reduces la saturación. Son ajustes que se entienden sin calcular nada.

Con rgb o hex, hacer lo mismo requeriría recalcular los tres canales a mano.

La forma moderna (2026): espacios y oklch#

Las notaciones de arriba —con comas— son las clásicas y siguen funcionando en todas partes, pero en 2026 la forma vigente separa los valores con espacios y mete la opacidad detrás de una barra /. Vale igual para rgb() y para hsl(), y hace innecesarios rgba()/hsla(), que hoy son solo alias antiguos del mismo color:

css
/* misma opacidad que rgba(184, 51, 106, 0.15), en sintaxis moderna */
background-color: rgb(184 51 106 / 0.15);
/* hsl con espacios y la opacidad después de la barra */
color: hsl(336 57% 46% / 0.4);

Y hay un paso más allá que conviene reconocer: oklch(), el espacio de color que se ha vuelto el estándar de los sistemas de diseño actuales. Sus tres valores son la luminosidad, el croma (cuánto color tiene) y el tono en grados; a diferencia de hsl, mantiene el brillo estable cuando cambias solo el tono:

css
/* oklch(luminosidad  croma  tono-en-grados) */
color: oklch(0.55 0.15 350);

No necesitas dominar oklch ahora —lo verás a fondo cuando montes un sistema de diseño—; basta con que lo reconozcas y tengas claro que la sintaxis con comas que has aprendido es la versión clásica, no la única ni la más moderna.

Contraste: una nota rápida#

Cualquier combinación de color de texto y fondo tiene un ratio de contraste. Un texto oscuro sobre fondo claro con poco contraste es difícil de leer para muchas personas, especialmente en pantallas brillantes o con baja visión.

La guía práctica: textos de cuerpo necesitan al menos 4.5:1 contra su fondo; textos grandes o de interfaz pueden bajar a 3:1. Las herramientas de DevTools del navegador te indican el ratio al hacer clic en un color. Un capítulo posterior (accesibilidad) cubre esto con detalle; por ahora, elige colores que no te cueste leer tú mismo.

Unidades#

Cuándo usar px, rem, em, % o vw es de las decisiones que más marcan la diferencia entre código frágil y código que aguanta.

px: la unidad absoluta#

Un píxel de CSS es un píxel de pantalla (con algún detalle de densidad en pantallas retina que no importa ahora). Es la unidad más predecible y precisa.

css
.borde {
  /* los bordes suelen quedar bien en px */
  border: 1px solid #e6e3ee;
}

El problema de px para tipografía y espaciado es que ignora las preferencias del usuario. Si alguien tiene la fuente base del navegador a 20px porque le cuesta leer, y tú declaras font-size: 14px, esa preferencia queda anulada. Con rem, 14px se convierte en 0.875rem y escala con la configuración del usuario.

Reserva px para bordes, sombras y radios de esquina: cosas donde un píxel exacto tiene sentido semántico y no importa que no escalen con la tipografía.

rem: relativo a la raíz#

rem (root em) es relativo al font-size del elemento <html>. El navegador lo pone a 16px por defecto, pero el usuario puede cambiarlo. 1rem = 16px en condiciones normales; 1.25rem = 20px; 0.875rem = 14px.

css
.hero-nombre {
  /* 20px en condiciones normales, más si el usuario lo amplió */
  font-size: 1.25rem;
}

.hero-rol {
  /* 12px en condiciones normales */
  font-size: 0.75rem;
}

Usa rem para font-size y espaciados de componente (padding, margin, gap). Es la unidad recomendada para cualquier cosa que el usuario debería poder ampliar desde la configuración del navegador.

em: relativo al elemento#

em es relativo al font-size del elemento actual, no del raíz. Eso lo hace útil cuando quieres que algo escale con el texto de su propio contenedor:

css
.boton {
  /* tamaño de texto del botón en rem: escala con la preferencia del usuario */
  font-size: 1.1rem;
  /* 0.5 × 1.1rem arriba/abajo, 1 × 1.1rem lateral */
  padding: 0.5em 1em;
}

Si el botón cambia de tamaño, el padding crece proporcionalmente sin tocar otra línea. El truco: usa em cuando el espaciado debe ser proporcional al texto local. Usa rem cuando quieres consistencia global.

%: relativo al padre#

Los porcentajes son relativos al elemento padre, generalmente en su dimensión correspondiente (ancho → width, altura → height):

css
.columna {
  /* ocupa la mitad del ancho de su contenedor */
  width: 50%;
}

Para anchos de layout y para hacer cosas fluidas que se adapten al espacio disponible. No uses % para tipografía: el cálculo depende del padre y puede ser impredecible en jerarquías profundas.

vw y vh: relativo a la ventana#

vw (viewport width) y vh (viewport height) son porcentajes del tamaño de la ventana del navegador, no del padre:

css
.pantalla-completa {
  /* ocupa exactamente la altura visible del navegador */
  height: 100vh;
}

Útil para secciones que deben ocupar toda la pantalla, o para tamaños que deben escalar con el tamaño de la ventana independientemente de la jerarquía.

La regla práctica#

¿Para qué?Usa
Bordes, sombras, radiospx
Tipografía y espaciadorem
Espaciado relativo al texto localem
Anchos fluidos dentro de un contenedor%
Tamaños relativos a la ventanavw / vh

Tipografía#

La tipografía no es solo elegir una fuente bonita. Es decidir qué leer primero y con qué esfuerzo.

font-family: el font stack#

Los navegadores solo tienen acceso a las fuentes instaladas en el sistema operativo. Para no depender de que el usuario tenga una fuente concreta, se declara un font stack: una lista de opciones en orden de preferencia. Si la primera no está, el navegador prueba la segunda, y así.

css
body {
  /* font stack: prueba cada fuente en orden hasta encontrar una disponible */
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

system-ui es la fuente del sistema operativo del usuario: San Francisco en macOS, Segoe UI en Windows, Roboto en Android. Es nítida, familiar y carga al instante porque ya está en el sistema. Para una aplicación de equipo como el Team Builder, es la opción más sensata: profesional y sin fricción de carga.

Los fallbacks (-apple-system, "Segoe UI", Roboto) cubren sistemas más antiguos que no entienden system-ui. sans-serif al final es el comodín del navegador.

font-size y la jerarquía#

El tamaño del texto construye la jerarquía visual: el ojo va primero al texto más grande. En la ficha del Team Builder hay tres niveles claros:

css
/* lo más importante: quién es — el tamaño mayor atrae la vista primero */
.hero-nombre    { font-size: 1.25rem; }
/* contexto de lectura cómoda — cuerpo de texto, ni destacado ni secundario */
.hero-descripcion { font-size: 0.9375rem; }
/* etiquetas: lo menos urgente — tamaño menor las relega visualmente */
.hero-rol, dt   { font-size: 0.75rem; }

La regla práctica: una escala de dos o tres pasos con proporciones claras (por ejemplo, × 1.25 entre cada nivel) es más coherente que tamaños escogidos al azar.

font-weight#

El grosor de la fuente, del más fino al más grueso: 100, 200… 900. Los más usados son 400 (normal) y 700 (negrita). Muchas fuentes solo tienen esos dos.

css
/* negrita: el nombre del héroe es el dato más importante */
.hero-nombre { font-weight: 700; }
/* semibold: más peso que normal, menos que bold */
.stat-value  { font-weight: 600; }

line-height: interlineado#

El espacio entre líneas afecta directamente la legibilidad. En cuerpo de texto largo, un line-height de 1.51.65 es más cómodo de leer que 1.2. En títulos de una sola línea, 1.2 está bien; no necesitan tanto aire.

css
/* base para todo el documento */
body            { line-height: 1.5; }
/* prosa larga: más espacio */
.hero-descripcion { line-height: 1.65; }
/* título de una línea: ajustado */
.hero-nombre    { line-height: 1.2; }

line-height sin unidad (solo 1.5, no 1.5rem) es lo correcto: es un múltiplo del font-size del elemento, así que escala con él automáticamente. La única excepción es el truco de centrado vertical que viste en el retrato del héroe —un line-height con la misma altura que la caja, en rem—: ahí la unidad es justo lo que centra el texto, pero es un caso aislado, y cuando llegues a Flexbox lo cambiarás por una forma más directa.

text-align#

Alinea el texto dentro de su caja: left, center, right, justify. En interfaces de aplicación, left es casi siempre lo correcto. Centrar títulos puede quedar bien en páginas de marketing; en una lista de datos de héroes, el texto centrado dificulta la lectura cuando los nombres tienen longitudes distintas.

letter-spacing#

Ajusta el espacio entre caracteres. En etiquetas pequeñas en mayúsculas, un poco de separación mejora la legibilidad:

css
.hero-rol {
  /* convierte el texto a mayúsculas: da aire de etiqueta compacta */
  text-transform: uppercase;
  /* em aquí: proporcional al tamaño de la etiqueta */
  letter-spacing: 0.06em;
}

No uses letter-spacing en positivo en texto de cuerpo: dificulta la lectura en vez de mejorarla.

background-color#

Para rellenar el fondo de un elemento. Funciona igual que color pero para la superficie de la caja:

css
.hero-retrato {
  /* el círculo con las iniciales del héroe */
  background-color: #b8336a;
}

En la ficha del Team Builder el retrato usa el color de acento como fondo para que las iniciales blancas contrasten sobre él.

Pruébalo#

Esta es la ficha de Tracer con color y tipografía ya aplicados (todavía sin layout: los elementos se apilan en el flujo normal). Abre /styles.css y cambia el color de .brand-acento, o el font-size de .hero-nombre en rem, y mira cómo responde al instante. Fíjate en que el acento #b8336a está escrito a mano en tres reglas: cambiarlo del todo serían tres ediciones, y eso es justo lo que resolverán las variables del capítulo 10.

Comprueba lo que sabes#

Pregunta 1 de 5

Un diseñador te pide "el mismo rojo pero con un 40% de opacidad". ¿Cuál de estas notaciones lo resuelve directamente?

Tu turno#

El HTML de la ficha de Tracer ya está completo. Tu tarea es dar vida al CSS, aquí mismo. Completa los TODO de styles.css: da color al texto y al fondo, elige las unidades correctas para cada caso y construye una jerarquía tipográfica que ayude al ojo a saber qué es el dato más importante. Cuando creas que lo tienes, despliega las soluciones y compáralas con la tuya.

Ejercicio · en esta página

Aplica color y tipografía a la ficha del héroe

El HTML de la ficha de Tracer ya está hecho. Tu tarea es completar los TODO de starter/styles.css: dar color al texto y al fondo, elegir las unidades correctas para cada caso y construir una jerarquía tipográfica que ayude al ojo a saber qué es el dato más importante. No hay Flexbox todavía (eso viene en el capítulo siguiente); el layout ya funciona con el flujo normal.

Paso 1: Que tenga color

  • El texto tiene color y el fondo también; ya no es HTML sin estilo.
  • Hay un color de acento visible en el brand y en el retrato.
  • Las estadísticas se distinguen del cuerpo de texto.
  • Vale aunque todo esté en px y los colores estén repetidos.
Ver soluciones
/* Nivel OK: se ve estilado, pero con decisiones flojas.
   - Todo en px (no escala con la preferencia del usuario).
   - El acento #c0392b repetido a mano en cuatro reglas.
   - Sin jerarquía tipográfica: el nombre y las stats miden lo mismo.
   - Grises flojos con poco contraste. */

body {
  margin: 0;
  font-family: Arial, sans-serif;
  font-size: 16px;
  color: #444444;
  background-color: #eeeeee;
}

.page {
  max-width: 520px;
  margin: 0 auto;
  padding: 24px 16px;
}

.site-header {
  padding-bottom: 16px;
  margin-bottom: 24px;
  border-bottom: 1px solid #cccccc;
}
.brand {
  margin: 0;
  font-size: 22px;
}
.brand-acento {
  color: #c0392b;
}
.site-nav a {
  font-size: 16px;
  color: #888888;
  text-decoration: none;
  margin-right: 16px;
}
.site-nav a:hover {
  color: #c0392b;
}

.hero-card {
  background-color: #ffffff;
  border: 1px solid #cccccc;
  padding: 24px;
}
.hero-retrato {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background-color: #c0392b;
  color: #ffffff;
  font-weight: 700;
  font-size: 14px;
  text-align: center;
  line-height: 44px;
  margin-bottom: 12px;
}
.hero-nombre {
  margin: 0;
  font-size: 18px;
}
.hero-rol {
  margin: 4px 0 0;
  font-size: 16px;
  color: #999999;
}
.hero-descripcion {
  margin: 12px 0 0;
  font-size: 16px;
}

.hero-stats {
  margin: 16px 0 0;
  padding-top: 16px;
  border-top: 1px solid #cccccc;
}
.hero-stats dt {
  font-size: 16px;
  color: #999999;
}
.hero-stats dd {
  margin: 0 0 12px;
  font-size: 18px;
}
.stat-positivo {
  color: #c0392b;
}

Por qué este nivel

  • La ficha tiene color: fondo, texto y un acento visible. Eso ya es un salto enorme respecto al HTML sin estilos.
  • Todos los tamaños están en px. Si el usuario tiene la fuente base del navegador a 20px (por accesibilidad), ninguno de estos valores escala con esa preferencia: 16px sigue siendo 16px.
  • El acento #c0392b está repetido a mano en .brand-acento, .site-nav a:hover, .hero-retrato y hasta en .stat-positivo (el winrate). Cuatro sitios; si el diseño cambia el acento, son cuatro ediciones y cuatro posibles olvidos. Y usar el rojo de marca también para el winrate confunde identidad con dato.
  • No hay jerarquía tipográfica: el nombre (18px) y las estadísticas (18px) miden lo mismo, así que el ojo no sabe a dónde mirar primero. Y los grises flojos (#999 sobre blanco) se leen con esfuerzo.