Módulo 08: Texto, color y unidades
Objetivos del módulo
- Elegir tipografías con pilas de fuentes seguras
- Cargar fuentes web con Google Fonts y con
@font-face - Controlar tamaño, peso, interlineado, espaciado, alineación y decoración del texto
- Expresar colores con nombres, hexadecimal,
rgb(),hsl()yoklch() - Trabajar con transparencias y
currentColor - Dominar los fondos: imágenes, tamaño, posición, repetición y fondos múltiples
- Crear degradados lineales, radiales y cónicos
- Saber qué unidad usar en cada caso:
px,%,em,rem,vw,vh,dvh,ch… - Calcular valores con
calc(),min()ymax() - Definir variables CSS (custom properties) y crear un tema claro/oscuro
1. Tipografía: font-family
La propiedad font-family indica qué fuente usar. Como no sabes qué fuentes tiene instaladas cada usuario, se escribe una pila de fuentes (font stack): una lista de opciones separadas por comas. El navegador usa la primera disponible.
body {
font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
📘 Concepto: Una pila de fuentes es como pedir en un bar: “Un café con leche de avena; si no hay, de soja; y si no, normal”. La última opción siempre debe ser una familia genérica, que existe en todos los sistemas.
| Familia genérica | Aspecto | Ejemplos |
|---|---|---|
serif | Con remates (serifas), clásica | Times New Roman, Georgia |
sans-serif | Sin remates, limpia | Arial, Helvetica, Roboto |
monospace | Todos los caracteres del mismo ancho | Consolas, Courier New |
cursive | Imita la escritura a mano | Comic Sans MS |
system-ui | La fuente de la interfaz del sistema operativo | Segoe UI (Windows), San Francisco (macOS) |
- Los nombres con espacios van entre comillas:
"Times New Roman". - Las familias genéricas van sin comillas:
serif, no"serif".
Pilas de fuentes útiles
/* Fuente del sistema: rápida (no se descarga nada) y con aspecto nativo */
.sistema { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
/* Serif clásica para textos largos */
.editorial { font-family: Georgia, "Times New Roman", serif; }
/* Código */
.codigo { font-family: ui-monospace, Consolas, "Courier New", monospace; }
2. Fuentes web
Si quieres una tipografía concreta que no esté en el sistema del usuario, tienes que hacer que el navegador la descargue.
2.1. Google Fonts
Google Fonts ofrece cientos de fuentes libres. Eliges la fuente y los pesos, y copias el código que te genera:
<head>
<!-- Conexión anticipada a los servidores de fuentes (acelera la carga) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Inter en pesos 400 y 700, y Merriweather 700 -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Merriweather:wght@700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/estilos.css">
</head>
body {
font-family: "Inter", system-ui, sans-serif; /* siempre con alternativas */
}
h1, h2 {
font-family: "Merriweather", Georgia, serif;
}
⚠️ Importante: Cada peso y estilo es un fichero que se descarga. Pide solo los que uses (normalmente 2 o 3). El parámetro
display=swaphace que el texto se muestre de inmediato con la fuente alternativa y se cambie cuando llegue la fuente web, en lugar de dejar el texto invisible.
2.2. @font-face: fuentes alojadas en tu servidor
Puedes descargar los ficheros de la fuente y servirlos tú mismo. Ventajas: no dependes de un servidor externo y mejoras la privacidad (Google no recibe la IP de tus visitantes, algo relevante con el RGPD europeo).
mi-web/
├── css/estilos.css
└── fuentes/
├── inter-regular.woff2
└── inter-bold.woff2
/* Declaramos la fuente: le damos un nombre y decimos dónde está cada fichero */
@font-face {
font-family: "Inter";
src: url("../fuentes/inter-regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Inter";
src: url("../fuentes/inter-bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* Y la usamos como cualquier otra */
body {
font-family: "Inter", system-ui, sans-serif;
}
📘 Concepto: Hoy basta con el formato WOFF2: está comprimido y lo soportan todos los navegadores actuales. Los formatos antiguos (
.ttf,.eot,.svg) ya no son necesarios en la web.
Fuentes variables
Una fuente variable incluye todos los pesos (y a veces otros ejes, como el ancho) en un único fichero. Se declara con un rango de pesos:
@font-face {
font-family: "Inter";
src: url("../fuentes/InterVariable.woff2") format("woff2");
font-weight: 100 900; /* rango: cualquier peso entre 100 y 900 */
font-display: swap;
}
.titulo { font-weight: 650; } /* ¡un peso intermedio! */
3. Propiedades del texto
Tamaño, peso y estilo
p {
font-size: 1rem; /* tamaño (las unidades se ven en la sección 7) */
font-weight: 400; /* 100-900; normal = 400, bold = 700 */
font-style: italic; /* normal | italic */
}
font-weight | Nombre habitual |
|---|---|
| 100 | Thin |
| 300 | Light |
| 400 | Regular (normal) |
| 500 | Medium |
| 600 | Semibold |
| 700 | Bold (bold) |
| 900 | Black |
⚠️ Importante: Si pides un peso que la fuente no tiene (por ejemplo 600 cuando solo has cargado 400 y 700), el navegador usa el más cercano o “falsea” una negrita. Carga los pesos que vayas a usar.
Interlineado: line-height
Controla la altura de cada línea. Un buen interlineado es la diferencia entre un texto agradable y uno agotador.
body {
line-height: 1.6; /* ✓ sin unidad: 1,6 veces el tamaño de letra de CADA elemento */
}
h1 {
line-height: 1.2; /* los títulos grandes necesitan menos interlineado */
}
💡 Consejo: Usa siempre
line-heightsin unidad. Con unidades (24px,1.5em), el valor calculado se hereda tal cual y un título grande heredaría un interlineado pensado para texto pequeño, con líneas solapadas. Para texto de lectura, entre 1.5 y 1.7.
Espaciado, alineación y transformación
.titulo-seccion {
letter-spacing: 0.1em; /* espacio entre letras (admite negativos) */
word-spacing: 0.2em; /* espacio entre palabras */
text-align: center; /* left | right | center | justify | start | end */
text-transform: uppercase; /* uppercase | lowercase | capitalize | none */
text-indent: 2em; /* sangría de la primera línea */
}
💡 Consejo: Escribe el texto en el HTML con sus mayúsculas normales y usa
text-transform: uppercasepara mostrarlo en mayúsculas. Así los lectores de pantalla no lo deletrean y, si cambias el diseño, no tienes que reescribir el contenido. Evitatext-align: justifyen pantallas estrechas: crea “ríos” de espacios en blanco.
Decoración: text-decoration
a {
text-decoration: underline; /* none | underline | overline | line-through */
text-decoration-color: #e63946;
text-decoration-thickness: 2px;
text-underline-offset: 4px; /* separa el subrayado del texto */
}
.precio-antiguo { text-decoration: line-through; color: #888; }
/* Abreviada: línea, estilo, color y grosor */
.error { text-decoration: underline wavy #dc2626 2px; }
Sombra de texto: text-shadow
Sintaxis: desplazamiento-x desplazamiento-y desenfoque color. Admite varias sombras separadas por comas.
.hero-titulo { text-shadow: 2px 2px 4px rgb(0 0 0 / 0.5); } /* sombra suave */
.neon { color: #fff; text-shadow: 0 0 5px #0ff, 0 0 15px #0ff, 0 0 30px #0ff; }
Espacios en blanco y desbordamiento
white-space controla cómo se tratan los espacios y saltos de línea del HTML:
| Valor | Espacios múltiples | Saltos de línea del HTML | Ajuste automático |
|---|---|---|---|
normal | Se colapsan | Se ignoran | Sí |
nowrap | Se colapsan | Se ignoran | No |
pre | Se respetan | Se respetan | No |
pre-wrap | Se respetan | Se respetan | Sí |
pre-line | Se colapsan | Se respetan | Sí |
El patrón clásico para cortar con puntos suspensivos un texto de una línea necesita tres propiedades a la vez (y un ancho limitado):
.nombre-archivo {
max-width: 200px;
white-space: nowrap; /* 1. no partir en varias líneas */
overflow: hidden; /* 2. ocultar lo que sobresale */
text-overflow: ellipsis; /* 3. mostrar "…" al final */
}
Para limitar un texto a varias líneas (por ejemplo, el resumen de una tarjeta a 3 líneas):
.resumen {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; /* pese al prefijo, funciona en todos los navegadores actuales */
overflow: hidden;
}
Y para que las palabras muy largas (URLs, por ejemplo) no rompan el diseño: overflow-wrap: break-word;.
Títulos equilibrados
h1, h2, h3 {
text-wrap: balance; /* reparte las palabras para que las líneas tengan longitud similar */
}
La abreviada font
/* font: [estilo] [peso] tamaño/interlineado familia (tamaño y familia son obligatorios) */
.cita { font: italic 400 1.25rem/1.5 Georgia, serif; }
✏️ Ejercicio: Crea un artículo de blog con un título, un subtítulo y tres párrafos. Carga desde Google Fonts una fuente serif para los títulos y una sans-serif para el texto. Ajusta
line-height, limita el ancho del texto a65ch(lo verás en la sección 7) y pon el subtítulo en mayúsculas conletter-spacing.
4. Colores
CSS ofrece varias formas de escribir un mismo color. Todas estas líneas producen el mismo rojo:
.a { color: red; }
.b { color: #ff0000; }
.c { color: #f00; }
.d { color: rgb(255 0 0); }
.e { color: hsl(0 100% 50%); }
4.1. Nombres de color
CSS reconoce unos 140 nombres en inglés: red, navy, tomato, rebeccapurple, transparent… Son cómodos para pruebas, pero en un diseño real necesitas el tono exacto de la marca.
4.2. Hexadecimal
Formato #RRGGBB: dos dígitos hexadecimales (0-9, a-f) por canal rojo, verde y azul, de 00 (nada) a ff (máximo, 255).
.negro { color: #000000; }
.blanco { color: #ffffff; }
.marca { color: #1d3557; }
.corto { color: #1a5; } /* forma corta: equivale a #11aa55 */
.semi { color: #1d355780; } /* 8 dígitos: los dos últimos son la opacidad (80 ≈ 50 %) */
4.3. rgb()
Indica la cantidad de rojo, verde y azul de 0 a 255. La sintaxis moderna separa los valores con espacios y la opacidad con una barra:
.rojo { color: rgb(230 57 70); }
.rojo-50 { color: rgb(230 57 70 / 50%); } /* 50 % de opacidad */
.antigua { color: rgba(230, 57, 70, 0.5); } /* sintaxis antigua con comas: sigue funcionando */
4.4. hsl()
Hue (tono), Saturation (saturación) y Lightness (luminosidad). Es mucho más intuitivo para crear variaciones de un mismo color:
flowchart LR
H["Tono (0-360°)<br/>0 rojo · 120 verde · 240 azul"] --> C["hsl(220 80% 50%)"]
S["Saturación (0-100 %)<br/>0 % gris · 100 % puro"] --> C
L["Luminosidad (0-100 %)<br/>0 % negro · 50 % puro · 100 % blanco"] --> C
/* Paleta de azules cambiando solo la luminosidad */
.azul-claro { background-color: hsl(220 80% 85%); }
.azul { background-color: hsl(220 80% 50%); }
.azul-oscuro { background-color: hsl(220 80% 25%); }
.azul-50 { background-color: hsl(220 80% 50% / 0.5); }
4.5. oklch(): el color moderno
oklch() (Luminosidad, Croma, Hue/tono) es un espacio de color diseñado según la percepción humana, soportado por todos los navegadores actuales. Su gran ventaja: si dos colores tienen la misma L, se perciben igual de claros, algo que no pasa con hsl() (un amarillo y un azul con L: 50% se ven muy distintos). Además permite colores más vivos en pantallas modernas (gama P3).
/* oklch(luminosidad croma tono) */
.primario { background-color: oklch(55% 0.2 260); } /* azul */
.exito { background-color: oklch(55% 0.2 145); } /* verde, misma claridad percibida */
.peligro { background-color: oklch(55% 0.2 25); } /* rojo, misma claridad percibida */
.transluc { background-color: oklch(55% 0.2 260 / 40%); }
| Componente | Rango | Significado |
|---|---|---|
| L | 0 %-100 % | Luminosidad percibida (0 % negro, 100 % blanco) |
| C | 0-0,4 aprox. | Croma: intensidad del color (0 = gris) |
| H | 0-360 | Tono en grados sobre la rueda de color |
Comparativa de formatos
| Formato | Ejemplo | Ventajas | Inconvenientes |
|---|---|---|---|
| Nombre | tomato | Legible | Pocos colores |
| Hex | #1d3557 | Compacto, el más usado en diseño | Poco intuitivo |
rgb() | rgb(29 53 87) | Fácil de obtener de herramientas | Difícil crear variaciones |
hsl() | hsl(215 50% 23%) | Intuitivo para variaciones | Claridad percibida irregular |
oklch() | oklch(33% 0.07 255) | Uniforme, gama amplia, ideal para paletas | Menos conocido |
4.6. Transparencia: canal alfa frente a opacity
/* Canal alfa: solo el fondo es semitransparente; el texto sigue opaco */
.caja-alfa { background-color: rgb(0 0 0 / 50%); color: white; }
/* opacity: TODO el elemento (fondo, texto, imágenes, hijos) se vuelve transparente */
.caja-opacity { background-color: black; color: white; opacity: 0.5; }
⚠️ Importante: Si quieres una capa oscura semitransparente con texto legible encima, usa el canal alfa en el color de fondo, no
opacity, que haría también transparente el texto.
4.7. currentColor
La palabra clave currentColor equivale al valor actual de la propiedad color del elemento. Es perfecta para componentes que deben adaptarse al color del texto:
.boton-borde {
color: #2563eb;
border: 2px solid currentColor; /* el borde es del mismo color que el texto */
}
.boton-borde:hover {
color: #dc2626; /* cambiando solo color, cambia también el borde */
}
/* Iconos SVG que toman el color del texto que los rodea */
.icono { fill: currentColor; }
Accesibilidad: el contraste
⚠️ Importante: El texto debe contrastar lo suficiente con su fondo. Las pautas WCAG piden una relación mínima de 4,5:1 para texto normal y 3:1 para texto grande. Las DevTools de Chrome y Firefox muestran la relación de contraste al inspeccionar un color: si no llega al mínimo, oscurece el texto o aclara el fondo. El gris claro sobre blanco “tan elegante” suele suspender.
5. Fondos
Propiedades de fondo
.hero {
background-color: #1d3557; /* color de respaldo, por si la imagen no carga */
background-image: url("../img/montanas.jpg"); /* ruta relativa al fichero CSS */
background-size: cover; /* cubrir toda la caja */
background-position: center; /* centrar la imagen */
background-repeat: no-repeat; /* no repetir en mosaico */
min-height: 60vh;
}
| Propiedad | Valores habituales | Qué hace |
|---|---|---|
background-color | Cualquier color | Color de fondo (detrás de la imagen) |
background-image | url(...), degradados | Imagen o degradado de fondo |
background-size | cover, contain, auto, 200px, 50% auto | Tamaño de la imagen |
background-position | center, top right, 20% 80%, 10px 0 | Posición de la imagen |
background-repeat | repeat, no-repeat, repeat-x, repeat-y, space, round | Repetición en mosaico |
background-attachment | scroll, fixed, local | Si el fondo se desplaza con la página |
background-origin / background-clip | border-box, padding-box, content-box | Desde dónde se pinta el fondo |
cover frente a contain
| Valor | Comportamiento | Riesgo |
|---|---|---|
cover | La imagen cubre toda la caja, manteniendo proporciones | Se recortan los bordes de la imagen |
contain | La imagen cabe entera en la caja, manteniendo proporciones | Quedan huecos vacíos |
💡 Consejo: Con
cover, usabackground-positionpara controlar qué parte se conserva. Si el sujeto de una foto está arriba,background-position: center topevita que le cortes la cabeza.
Efecto parallax simple
.parallax {
background-image: url("../img/ciudad.jpg");
background-attachment: fixed; /* la imagen queda quieta mientras el contenido se desplaza */
background-size: cover;
}
⚠️ Importante:
background-attachment: fixedno funciona en muchos navegadores móviles (se comporta comoscroll) y puede afectar al rendimiento. Úsalo como un extra, no como parte esencial del diseño.
La abreviada background
/* color imagen posición / tamaño repetición attachment */
.hero {
background: #1d3557 url("../img/montanas.jpg") center / cover no-repeat;
}
⚠️ Importante: En la abreviada, el tamaño va después de la posición y separado por una barra (
center / cover). Y recuerda que cualquier propiedad abreviada resetea las subpropiedades que no indicas:background: red;elimina unabackground-imagedeclarada antes.
Fondos múltiples
Puedes apilar varios fondos separándolos por comas. El primero queda encima. El caso más habitual es oscurecer una foto con un degradado para que el texto se lea bien:
.hero {
background:
linear-gradient(rgb(0 0 0 / 60%), rgb(0 0 0 / 20%)), /* capa 1 (encima): velo oscuro */
url("../img/montanas.jpg") center / cover no-repeat; /* capa 2 (debajo): la foto */
color: white;
}
📘 Concepto: ¿Imagen de fondo o
<img>? Si la imagen es contenido (la foto de un producto, una ilustración de un artículo), usa<img>con sualt. Si es decoración (una textura, un fondo de cabecera), usabackground-image.
6. Degradados
Los degradados son imágenes generadas por el navegador, así que se usan en background-image (o background), no en background-color.
6.1. linear-gradient()
/* De arriba a abajo (dirección por defecto) */
.g1 { background-image: linear-gradient(#2563eb, #7c3aed); }
/* Con dirección por palabras clave */
.g2 { background-image: linear-gradient(to right, #f59e0b, #ef4444); }
.g3 { background-image: linear-gradient(to bottom right, #10b981, #3b82f6); }
/* Con ángulo: 0deg = hacia arriba, 90deg = hacia la derecha */
.g4 { background-image: linear-gradient(135deg, #ec4899, #8b5cf6); }
/* Varias paradas de color con posición */
.g5 { background-image: linear-gradient(to right, red 0%, yellow 50%, green 100%); }
/* Paradas "duras": bandas sin transición (bandera de España) */
.bandera {
background-image: linear-gradient(#aa151b 25%, #f1bf00 25% 75%, #aa151b 75%);
}
6.2. radial-gradient()
Parte de un punto y se expande en círculo o elipse:
.r1 { background-image: radial-gradient(#fde68a, #f59e0b); } /* elipse centrada */
.r2 { background-image: radial-gradient(circle, white, #3b82f6); } /* círculo */
.r3 { background-image: radial-gradient(circle at top left, #a7f3d0, #065f46); } /* desde una esquina */
/* Foco de luz sutil sobre fondo oscuro */
.foco {
background: radial-gradient(circle at 50% 0%, rgb(255 255 255 / 15%), transparent 60%), #0f172a;
}
6.3. conic-gradient()
Los colores giran alrededor de un centro, como las agujas de un reloj. Ideal para gráficos circulares y ruedas de color:
/* Gráfico circular: 40 % rojo, 35 % azul, 25 % verde */
.grafico-tarta {
width: 200px;
aspect-ratio: 1;
border-radius: 50%;
background-image: conic-gradient(#e63946 0 40%, #457b9d 40% 75%, #2a9d8f 75% 100%);
}
/* Rueda de color */
.rueda {
width: 200px;
aspect-ratio: 1;
border-radius: 50%;
background-image: conic-gradient(red, yellow, lime, cyan, blue, magenta, red);
}
6.4. Degradados repetidos
/* Rayas diagonales tipo "zona de obras" */
.rayas {
background-image: repeating-linear-gradient(45deg, #facc15 0 20px, #1f2937 20px 40px);
}
| Función | Forma | Uso típico |
|---|---|---|
linear-gradient() | En línea recta | Fondos, botones, velos sobre imágenes |
radial-gradient() | Desde un punto hacia fuera | Focos de luz, viñeteados |
conic-gradient() | Girando alrededor de un centro | Gráficos de tarta, indicadores circulares |
repeating-*-gradient() | Patrón repetido | Rayas, tramas |
✏️ Ejercicio: Crea un botón con un degradado de 135° entre dos colores de tu elección, texto blanco y una sombra de texto suave. Después crea un gráfico circular con
conic-gradientque represente el reparto de tu tiempo en un día (dormir, trabajar, ocio…).
7. Unidades de medida
7.1. Unidades absolutas: px
El píxel CSS es la unidad de referencia. No es un píxel físico de la pantalla (en un móvil de alta densidad, un px CSS puede ocupar 3×3 píxeles reales), sino una medida estable y predecible. Otras unidades absolutas (cm, mm, pt) solo tienen sentido en hojas de impresión.
7.2. Porcentaje: %
Relativo a algo del elemento padre, que depende de la propiedad: en width es el ancho del padre; en font-size, el tamaño de letra del padre.
.columna { width: 50%; } /* la mitad del ancho de su contenedor */
7.3. em y rem
| Unidad | Relativa a | Ejemplo con raíz de 16px |
|---|---|---|
rem | El tamaño de letra del elemento raíz (html) | 2rem = 32px, siempre |
em | El tamaño de letra del propio elemento (o del padre, si se usa en font-size) | Depende del contexto |
El problema de em es que se acumula al anidar:
<ul class="lista">
<li>Nivel 1
<ul class="lista">
<li>Nivel 2
<ul class="lista"><li>Nivel 3</li></ul>
</li>
</ul>
</li>
</ul>
.lista { font-size: 1.2em; } /* 16 → 19,2 → 23,04 → 27,65px... ¡crece en cada nivel! */
.lista { font-size: 1.2rem; } /* 19,2px en todos los niveles */
¿Entonces em no sirve? Sí: es ideal cuando quieres que algo escale con el texto del propio componente:
.boton {
font-size: 1rem;
padding: 0.5em 1em; /* el relleno crece proporcionalmente si el botón es más grande */
}
.boton--grande {
font-size: 1.5rem; /* solo cambiamos el tamaño: el padding se ajusta solo */
}
⚠️ Importante: Usa
remparafont-sizeen lugar depx. Si un usuario aumenta el tamaño de letra por defecto de su navegador (por ejemplo, por baja visión), losremlo respetan y lospxno. Y no fijeshtml { font-size: 10px; }ni similares: anula esa preferencia.
7.4. Unidades del viewport
El viewport es el área visible de la ventana del navegador.
| Unidad | Equivale a |
|---|---|
vw | 1 % del ancho del viewport |
vh | 1 % del alto del viewport |
vmin / vmax | 1 % de la dimensión menor / mayor |
svh | 1 % del alto más pequeño (con las barras del navegador móvil visibles) |
lvh | 1 % del alto más grande (con las barras ocultas) |
dvh | 1 % del alto dinámico: se actualiza cuando las barras aparecen y desaparecen |
/* Portada a pantalla completa */
.portada {
min-height: 100vh; /* respaldo para navegadores antiguos */
min-height: 100dvh; /* en móviles, no queda tapada por la barra de direcciones */
}
📘 Concepto: En los móviles, la barra de direcciones aparece y desaparece al hacer scroll, así que “el alto de la pantalla” cambia.
100vhequivale al alto con la barra oculta y el contenido queda parcialmente tapado cuando la barra está visible.dvhresuelve este problema clásico. Fíjate en el truco de declarar la propiedad dos veces: si un navegador no entiendedvh, ignora esa línea y se queda con la anterior.
⚠️ Importante:
100vwincluye el ancho de la barra de desplazamiento vertical en escritorio, por lo quewidth: 100vwsuele provocar un scroll horizontal molesto. Para ocupar todo el ancho usawidth: 100%.
7.5. ch: ancho de carácter
1ch es el ancho del carácter “0” en la fuente actual. Es perfecto para limitar la longitud de línea de un texto: lo ideal para leer cómodamente son entre 45 y 75 caracteres por línea.
.articulo {
max-width: 65ch; /* unos 65 caracteres por línea */
margin-inline: auto; /* centrado (lo verás en el módulo 09) */
}
7.6. fr: adelanto
La unidad fr (fracción) solo existe en CSS Grid y reparte el espacio disponible: grid-template-columns: 1fr 2fr; crea dos columnas, la segunda el doble de ancha. La estudiarás en el módulo 11.
¿Qué unidad uso?
| Para… | Usa | Por qué |
|---|---|---|
| Tamaño de letra | rem | Respeta la preferencia del usuario y no se acumula |
| Relleno de botones y componentes de texto | em | Escala con el texto del componente |
| Márgenes y espacios generales | rem | Coherencia con la tipografía |
| Bordes finos, sombras | px | Precisión; no necesitan escalar |
| Anchos de columnas | %, fr | Se adaptan al contenedor |
| Longitud de línea de texto | ch | Legibilidad |
| Secciones a pantalla completa | dvh, svh | Alto real en móviles |
| Tipografía fluida | vw dentro de clamp() | Escala con la ventana (módulo 12) |
line-height | Sin unidad | Se hereda correctamente |
8. Funciones matemáticas: calc(), min() y max()
calc()
Permite hacer operaciones mezclando unidades, algo imposible de calcular de antemano:
.contenido {
width: calc(100% - 250px); /* todo el ancho menos una barra lateral fija */
}
.cabecera-fija + main {
padding-top: calc(4rem + 20px);
}
.tercio {
width: calc(100% / 3);
}
⚠️ Importante: Los operadores
+y-deben ir rodeados de espacios:calc(100% - 20px)✓,calc(100%-20px)✗ (es inválido y se ignora). Con*y/no es obligatorio, pero por legibilidad ponlos también.
min() y max()
min() devuelve el menor de los valores; max(), el mayor. Se evalúan en cada momento según el tamaño real:
.contenedor {
/* 90 % del ancho, pero como máximo 1200px: en pantallas grandes, 1200px; en pequeñas, 90 % */
width: min(90%, 1200px);
margin-inline: auto;
}
.texto-legible {
/* al menos 1rem aunque 2vw dé menos (en móviles) */
font-size: max(1rem, 2vw);
}
💡 Consejo:
width: min(90%, 1200px)sustituye en una línea al clásicowidth: 90%; max-width: 1200px;. Recuerda la regla mnemotécnica:min()pone un tope máximo ymax()pone un suelo mínimo (parece al revés, pero piénsalo: “el mínimo entre 90 % y 1200px nunca superará 1200px”).
clamp(): un avance
clamp(mínimo, preferido, máximo) combina ambas: usa el valor preferido, pero nunca por debajo del mínimo ni por encima del máximo.
h1 {
font-size: clamp(1.75rem, 4vw + 1rem, 3.5rem); /* crece con la ventana, entre 28px y 56px */
}
La usarás a fondo para la tipografía fluida en el módulo 12.
9. Variables CSS (custom properties)
📘 Concepto: Una variable CSS (oficialmente, propiedad personalizada) es un valor con nombre que defines una vez y reutilizas en toda la hoja. Si decides cambiar el color corporativo, lo cambias en un solo sitio.
Declarar y usar
- Se declaran como una propiedad cuyo nombre empieza por dos guiones:
--color-primario. - Se usan con la función
var(). - Normalmente se declaran en
:rootpara que estén disponibles en todo el documento.
:root {
/* Colores */
--color-primario: #2563eb;
--color-primario-oscuro: #1e40af;
--color-texto: #1f2937;
--color-fondo: #ffffff;
/* Tipografía */
--fuente-base: "Inter", system-ui, sans-serif;
--fuente-titulos: "Merriweather", Georgia, serif;
/* Espaciado y formas */
--espacio: 1rem;
--radio: 8px;
--sombra: 0 4px 12px rgb(0 0 0 / 0.1);
}
body {
font-family: var(--fuente-base);
color: var(--color-texto);
background-color: var(--color-fondo);
}
.boton {
background-color: var(--color-primario);
padding: calc(var(--espacio) / 2) var(--espacio);
border-radius: var(--radio);
}
.boton:hover {
background-color: var(--color-primario-oscuro);
}
.tarjeta {
padding: calc(var(--espacio) * 1.5);
border-radius: var(--radio);
box-shadow: var(--sombra);
}
Valor por defecto
var() admite un segundo argumento que se usa si la variable no está definida:
.aviso {
color: var(--color-aviso, #f59e0b); /* si --color-aviso no existe, usa naranja */
}
Ámbito y herencia
Las variables se heredan y se pueden redefinir en cualquier elemento. El nuevo valor afecta solo a ese elemento y a sus descendientes. Esto permite crear variantes de componentes de forma muy elegante:
.boton {
--boton-color: #2563eb; /* valor por defecto del componente */
background-color: var(--boton-color);
border: 2px solid var(--boton-color);
color: white;
}
/* Las variantes solo cambian la variable */
.boton--exito { --boton-color: #16a34a; }
.boton--peligro { --boton-color: #dc2626; }
/* Una sección oscura redefine la paleta para todo su contenido */
.seccion-oscura {
--color-texto: #f9fafb;
--color-fondo: #111827;
color: var(--color-texto);
background-color: var(--color-fondo);
}
⚠️ Importante: Los nombres de las variables distinguen mayúsculas (
--Colory--colorson distintas). Y solo funcionan como valores de propiedades: no puedes usarlas como nombre de propiedad ni de selector, ni (de momento) dentro de las condiciones de una media query.
Variables CSS frente a variables de Sass
Si has oído hablar de Sass (lo verás con Bootstrap en el módulo 17), sus variables ($color) se sustituyen al compilar y desaparecen del CSS final. Las variables CSS existen en el navegador: se pueden cambiar con media queries, heredar y modificar con JavaScript en tiempo real.
// Cambiar una variable desde JavaScript
document.documentElement.style.setProperty('--color-primario', '#db2777');
10. Tema claro y oscuro
Los sistemas operativos permiten elegir un modo claro u oscuro, y el navegador informa de esa preferencia a CSS mediante la media query prefers-color-scheme. Combinada con variables, crear un tema oscuro es sorprendentemente sencillo.
10.1. Tema automático según el sistema
/* Tema claro (por defecto) */
:root {
--color-fondo: #ffffff;
--color-superficie: #f3f4f6;
--color-texto: #1f2937;
--color-texto-suave: #6b7280;
--color-primario: #2563eb;
color-scheme: light dark; /* avisa al navegador de que soportamos ambos modos */
}
/* Tema oscuro: solo redefinimos las variables */
@media (prefers-color-scheme: dark) {
:root {
--color-fondo: #0f172a;
--color-superficie: #1e293b;
--color-texto: #e2e8f0;
--color-texto-suave: #94a3b8;
--color-primario: #60a5fa; /* en oscuro, los colores suelen necesitar más luminosidad */
}
}
/* El resto del CSS solo usa variables: funciona en ambos temas sin cambios */
body {
background-color: var(--color-fondo);
color: var(--color-texto);
}
.tarjeta {
background-color: var(--color-superficie);
}
.tarjeta small {
color: var(--color-texto-suave);
}
a {
color: var(--color-primario);
}
📘 Concepto: La propiedad
color-scheme: light darkle dice al navegador que tu página está preparada para ambos modos. Así adapta automáticamente los elementos nativos que tú no estilas: barras de desplazamiento, controles de formulario, el fondo por defecto…
💡 Consejo: Para probar el modo oscuro sin cambiar la configuración de tu sistema, en las DevTools de Chrome abre el menú (⋮) → More tools → Rendering y busca Emulate CSS media feature prefers-color-scheme.
10.2. La función light-dark()
Los navegadores actuales admiten una forma aún más compacta: light-dark(valor-claro, valor-oscuro), que elige automáticamente según el esquema activo. Requiere haber declarado color-scheme.
:root {
color-scheme: light dark;
--color-fondo: light-dark(#ffffff, #0f172a);
--color-texto: light-dark(#1f2937, #e2e8f0);
}
10.3. Selector manual de tema
Muchas webs permiten además que el usuario elija el tema con un botón. Una técnica habitual es usar un atributo en <html>:
<html lang="es" data-tema="claro">
...
<button id="cambiar-tema" type="button">🌓 Cambiar tema</button>
:root {
--color-fondo: #ffffff;
--color-texto: #1f2937;
}
:root[data-tema="oscuro"] {
--color-fondo: #0f172a;
--color-texto: #e2e8f0;
}
body {
background-color: var(--color-fondo);
color: var(--color-texto);
}
// Alterna el atributo data-tema y recuerda la elección
const html = document.documentElement;
const guardado = localStorage.getItem('tema');
if (guardado) {
html.dataset.tema = guardado;
}
document.getElementById('cambiar-tema').addEventListener('click', () => {
html.dataset.tema = html.dataset.tema === 'oscuro' ? 'claro' : 'oscuro';
localStorage.setItem('tema', html.dataset.tema);
});
💡 Consejo: Bootstrap 5.3 usa exactamente esta idea con el atributo
data-bs-theme="dark". Lo verás en el módulo 17.
11. Ejercicios
Ejercicio 1: Tipografía de un artículo
Maqueta un artículo de blog con título, autor, fecha y cinco párrafos:
- Carga desde Google Fonts una fuente para títulos y otra para el cuerpo (máximo dos pesos cada una), con pilas de respaldo.
- Cuerpo a
1.125rem,line-height: 1.7y ancho máximo de65ch, centrado. - Autor y fecha en mayúsculas, más pequeños y con
letter-spacing: 0.05em. - Primer párrafo con letra capital usando
::first-letter.
Ejercicio 2: Paleta con HSL y OKLCH
Crea una fila de 5 cuadrados con el mismo tono en hsl() variando solo la luminosidad (90 %, 70 %, 50 %, 30 %, 10 %). Debajo, otra fila con 5 colores de distinto tono en oklch() con la misma luminosidad y croma. Observa qué fila parece más “uniforme” en claridad.
Ejercicio 3: Cabecera hero
Crea una cabecera que ocupe todo el alto de la pantalla (100dvh con respaldo 100vh), con una imagen de fondo con cover, un velo oscuro hecho con un degradado en fondos múltiples, un título blanco centrado con text-shadow y un botón. Comprueba en DevTools (modo dispositivo) que la imagen siempre cubre la cabecera.
Ejercicio 4: Galería de degradados
Crea 6 tarjetas de 200×200 px, cada una con un degradado distinto: lineal vertical, lineal a 45° con tres colores, con paradas duras (bandera de tres franjas), radial desde una esquina, cónico tipo gráfico de tarta y rayas con repeating-linear-gradient.
Ejercicio 5: Unidades en acción
Crea un componente .boton cuyo relleno esté en em y tres variantes de tamaño que solo cambien font-size en rem. Después crea un .contenedor con width: min(90%, 1100px) y una barra lateral fija de 250px con un contenido principal de calc(100% - 250px). Redimensiona la ventana y explica lo que ocurre.
Ejercicio 6: Sistema de diseño con variables
Define en :root variables para 4 colores, 2 fuentes, 3 tamaños de espaciado, radio de borde y sombra. Construye con ellas una tarjeta de producto y un botón con variantes (--primario, --exito, --peligro) que solo redefinan una variable local del componente.
Ejercicio 7: Tema claro/oscuro
Sobre el ejercicio anterior, añade un tema oscuro automático con prefers-color-scheme, color-scheme: light dark y un botón que permita forzar el tema manualmente con data-tema, guardando la elección en localStorage. Comprueba ambos modos con la emulación de DevTools y verifica el contraste del texto en los dos.
Resumen
| Concepto | Sintaxis / idea clave |
|---|---|
| Pila de fuentes | font-family: "Inter", system-ui, sans-serif; (genérica al final) |
| Fuente propia | @font-face { font-family; src: url() format("woff2"); font-display: swap; } |
| Interlineado | line-height: 1.6; sin unidad |
| Puntos suspensivos | white-space: nowrap; overflow: hidden; text-overflow: ellipsis; |
| Colores | #1d3557, rgb(29 53 87 / 50%), hsl(215 50% 23%), oklch(55% 0.2 260) |
| Color del texto actual | currentColor |
| Fondo abreviado | background: color url() posición / tamaño repetición; |
| Fondos múltiples | Separados por comas; el primero queda encima |
| Degradados | linear-gradient(), radial-gradient(), conic-gradient() |
| Unidades de texto | rem para tamaños, em para componentes, ch para longitud de línea |
| Viewport | vw, vh, dvh (alto dinámico en móviles) |
| Cálculos | calc(100% - 2rem), min(90%, 1200px), max(1rem, 2vw) |
| Variables | --nombre: valor; y var(--nombre, respaldo) |
| Tema oscuro | @media (prefers-color-scheme: dark) + color-scheme: light dark |