En el capítulo 8 escribiste colores y tamaños directamente en las reglas —y al final ya notabas el problema: el acento aparecía tres veces, un radio se duplicaba—. Ese capítulo terminó señalando que la solución nativa de CSS son las custom properties (variables). Este es el capítulo que paga esa promesa.
Junto con las custom properties aprenderás CSS Grid: el sistema que coloca los componentes en una cuadrícula de dos dimensiones. El box model y Flexbox (cap. 9) ya te dan el interior de cada carta; Grid organiza las cartas entre sí.
Seguimos con el Overwatch Team Builder. El HTML de las cartas ya está; ahora las colocamos en un grid responsive y convertimos los colores y espaciados en variables que se puedan cambiar en una línea.
Grid frente a Flexbox: no se sustituyen, se complementan#
En el capítulo de Flexbox viste cómo distribuir elementos a lo largo de un eje: el retrato y el nombre de la carta en una fila, las estadísticas en otra. Flexbox es unidimensional: trabaja en fila o en columna, pero no en los dos a la vez.
CSS Grid es bidimensional: define filas y columnas simultáneamente y coloca los ítems en celdas de esa cuadrícula. Si Flexbox organiza el interior de un componente, Grid organiza los componentes entre sí.
En la práctica se usan juntos: Grid para el layout de la página (el grid de cartas), Flexbox para el interior de cada carta (el retrato junto al nombre).
La cuadrícula básica#
.hero-grid {
display: grid;
/* tres columnas iguales */
grid-template-columns: 1fr 1fr 1fr;
/* espacio entre filas y columnas */
gap: 1rem;
}1fr significa “una fracción del espacio disponible”. Con tres 1fr el espacio se
divide en tres partes iguales. Con 2fr 1fr la primera columna sería el doble de
ancha que la segunda.
gap reemplaza el viejo grid-gap (que sigue funcionando, pero es legacy). Acepta
dos valores: el primero para filas y el segundo para columnas. Con uno solo aplica
a ambas.
repeat() y minmax(): el grid que se adapta solo#
Tres columnas fijas funcionan en escritorio. En una tablet de 600px quedan estrechas; en un móvil de 360px, aplastadas. La solución sin media queries:
.hero-grid {
/* tantas columnas como quepan, cada una de 15rem como mínimo */
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}Descompuesto:
repeat(auto-fill, ...)— crea tantas columnas como quepan en el contenedor.minmax(15rem, 1fr)— cada columna mide al menos 15rem y puede crecer hasta llenar su fracción.
El resultado: en móvil cabe una columna; en tablet, dos; en escritorio, tres o más. El navegador hace el cálculo; tú no escribes ningún breakpoint.
auto-fill frente a auto-fit: la diferencia que se ve con pocas cartas#
auto-fill reserva columnas aunque estén vacías. Si en un contenedor de 60rem caben
tres columnas de 15rem pero solo tienes dos héroes, la tercera columna existe pero
está vacía: las dos cartas no llenan el ancho.
auto-fit colapsa las columnas vacías. Con dos héroes y espacio para tres, las dos
cartas se estiran para llenar el ancho disponible.
/* auto-fill: reserva columnas vacías */
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
/* auto-fit: colapsa lo que no tiene contenido */
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));Cuál usar depende del diseño. En una galería donde la alineación con otros elementos
importa, auto-fill mantiene el ritmo aunque falten ítems. En el Team Builder,
donde las cartas deben llenar el espacio, auto-fit es más natural.
Centrar en una celda: place-items#
Grid trae un atajo cómodo para centrar el contenido de una celda en los dos ejes a
la vez: place-items, que combina align-items (eje vertical) y justify-items
(eje horizontal) en una sola línea.
.celda {
display: grid;
/* centrado vertical y horizontal */
place-items: center;
}Es la forma más corta de centrar algo dentro de su caja, y por eso la verás mucho: por ejemplo, para centrar las iniciales dentro del retrato circular de un héroe.
Áreas con nombre: grid-template-areas#
Para layouts de página con regiones nombradas (cabecera, barra lateral, contenido, pie), Grid ofrece una sintaxis que “dibuja” la estructura.
Igual que grid-template-columns define el tamaño de las columnas, existe
grid-template-rows para las filas. Funciona con los mismos valores: auto deja
que la fila mida lo que ocupa su contenido; 1fr le cede el espacio sobrante.
.layout {
display: grid;
/* fila 1: lo que ocupe la cabecera; fila 2: ocupa el resto; fila 3: lo que ocupe el pie */
grid-template-rows: auto 1fr auto;
}Con esa base, grid-template-areas añade los nombres:
.layout {
/* activa CSS Grid en este contenedor */
display: grid;
/* columna fija de 16rem + columna que ocupa el resto */
grid-template-columns: 16rem 1fr;
/* tres filas: cabecera / contenido / pie */
grid-template-rows: auto 1fr auto;
/* "dibuja" el layout con nombres de zona */
grid-template-areas:
/* cabecera ocupa las dos columnas */
"header header"
/* barra lateral + contenido principal */
"sidebar main"
/* pie ocupa las dos columnas */
"footer footer";
}
/* coloca este elemento en la zona "header" */
.site-header { grid-area: header; }
/* coloca este elemento en la zona "sidebar" */
.sidebar { grid-area: sidebar; }
/* coloca este elemento en la zona "main" */
.main { grid-area: main; }
/* coloca este elemento en la zona "footer" */
.site-footer { grid-area: footer; }El CSS “habla”: quien lo lee ve el layout sin necesidad de interpretar coordenadas.
Para reorganizarlo basta con redefinir grid-template-areas y los elementos se
recolocan solos, sin tocar coordenadas. No lo usaremos en el ejercicio de este
capítulo —el grid de cartas es más simple—, pero es la base de cualquier layout de
página con regiones nombradas.
Custom properties: variables que vive en CSS#
Una custom property es una variable nativa de CSS. Se declara con --nombre y se
usa con var(--nombre):
/* :root es la raíz del documento; las variables aquí son globales */
:root {
/* custom property: el color de acento de la marca */
--color-acento: #b8336a;
/* radio de esquinas de las tarjetas, en un único sitio */
--radio-tarjeta: 12px;
}
.hero-portrait {
/* usa el valor de la custom property */
background: var(--color-acento);
}
.hero-card {
/* si el radio cambia, solo tocas :root */
border-radius: var(--radio-tarjeta);
}:root es el selector de la raíz del documento (equivale a html pero con mayor
especificidad). Declarar las variables ahí las hace disponibles en toda la página.
¿Por qué es mejor que repetir #b8336a en cada regla? Porque hay un único sitio
que tocar. Si el color de acento cambia, cambias la variable y toda la página se
actualiza. Con el valor literal disperso en diez reglas, cambiar de marca significa
buscar y reemplazar con riesgo de olvidarse alguno.
Las custom properties también se pueden cambiar desde JavaScript en tiempo de
ejecución: con una sola instrucción (document.documentElement.style.setProperty)
reasignas el valor de una variable y toda la página que la use se actualiza al
instante, sin recargar. Eso es lo que hace posible el cambio de tema de color
—claro/oscuro— con un botón. No te preocupes ahora por esa sintaxis: es JavaScript y
lo verás en el Nivel 2; aquí lo que importa es entender por qué se puede hacer tan
fácil: porque el color vive en un único sitio, la variable.
Pruébalo#
El playground muestra el grid de cartas con auto-fit y minmax. Hay una variable
--grid-col-min que controla el ancho mínimo de cada columna. Cámbiala de 12rem
a 20rem y observa cómo el grid pasa de cuatro columnas a dos sin tocar ninguna
otra regla. Eso es una palanca de diseño: un único punto que cambia el comportamiento
en cascada.
Comprueba lo que sabes#
Pregunta 1 de 5
Tienes un grid con `grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr))` y solo hay 2 cartas en un contenedor de 60rem. ¿Qué ocurre con la tercera columna que "cabría"?
Tu turno#
El HTML ya está dado. Tu trabajo es el CSS: hacer funcionar el grid, la cabecera y
las cartas. Abre styles.css en el playground y sigue los TODOs del fichero de
partida. Cuando creas que lo tienes, despliega las soluciones y compáralas con la tuya.
Ejercicio · en esta página
Dale estilo al grid del Team Builder
El HTML de la página ya está hecho y no debes tocarlo. Tu trabajo es completar styles.css siguiendo los TODOs: base, cabecera, grid de héroes y cartas. El objetivo no es que "quede bonito" —eso lo decides tú—, sino que el grid sea responsive sin media queries y que los valores repetidos (colores, espaciados) vivan en custom properties.
Paso 1: Que funcione
- El grid muestra las cartas en columnas visibles.
- Las cartas tienen fondo, borde y padding reconocibles.
- La cabecera muestra el logotipo y la navegación en la misma fila.
Paso 2: Que escale y sea mantenible
- El grid es responsive con repeat(auto-fill, minmax(...)) sin ninguna media query.
- Los colores, espaciados y radios están en custom properties en :root.
- Cambiar --color-acento en una línea actualiza el acento en toda la página.
Paso 3: Que sea un sistema
- auto-fit en lugar de auto-fill: las cartas llenan el ancho cuando son pocas.
- Una variable --grid-col-min actúa como palanca de densidad del grid.
- Los tokens están agrupados por categoría con comentarios que explican su uso.
- El CSS aguanta de 1 a N héroes sin tocar una sola regla.
Ver soluciones
/* ==========================================================================
NIVEL OK — "funciona y se ve"
==========================================================================
El grid muestra las cartas en tres columnas fijas. Cada color, espacio o
tamaño de fuente está escrito a pelo donde lo necesita, sin ningún sistema
que lo agrupe. Se ve bien; si mañana quieres cambiar el color de acento o
el espaciado base tienes que rastrear el fichero entero y cambiar a mano
en diez sitios distintos. Es el punto de partida, no el de llegada.
========================================================================== */
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
color: #1c1b22;
background: #f7f6fb;
line-height: 1.5;
}
.page {
max-width: 60rem;
margin: 0 auto;
padding: 2.5rem 1.5rem 4rem;
}
/* --- Cabecera ----------------------------------------------------------- */
.site-header {
display: flex;
align-items: baseline;
justify-content: space-between;
flex-wrap: wrap;
gap: 0.75rem;
padding-bottom: 1.25rem;
border-bottom: 1px solid #e6e3ee;
margin-bottom: 2rem;
}
.brand {
font-size: 1.4rem;
font-weight: 700;
letter-spacing: -0.01em;
margin: 0;
}
.brand span {
color: #b8336a;
}
.site-nav a {
color: #5b5966;
text-decoration: none;
font-size: 0.9rem;
margin-left: 1rem;
}
.site-nav a:hover {
color: #b8336a;
}
/* --- Título de sección -------------------------------------------------- */
.section-title {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #5b5966;
margin: 0 0 1rem;
}
/* --- Grid de héroes ----------------------------------------------------- */
/* Tres columnas fijas iguales. Funciona en pantallas anchas; en móvil las
columnas se quedan estrechas y el texto puede cortarse. Para hacerlo
responsive sin media queries necesitas minmax() y auto-fill: eso viene en
el nivel Mejor. */
.hero-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
}
/* --- Carta de héroe ----------------------------------------------------- */
.hero-card {
background: #ffffff;
border: 1px solid #e6e3ee;
border-radius: 12px;
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.hero-top {
display: flex;
align-items: center;
gap: 0.85rem;
}
.hero-portrait {
flex: none;
width: 3rem;
height: 3rem;
border-radius: 50%;
display: grid;
place-items: center;
font-weight: 700;
font-size: 0.85rem;
color: #ffffff;
background: #b8336a;
}
.hero-name {
font-size: 1.05rem;
font-weight: 700;
margin: 0;
}
.hero-role {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #5b5966;
margin: 0.1rem 0 0;
}
.hero-stats {
display: flex;
gap: 1.25rem;
margin: 0;
padding-top: 0.5rem;
border-top: 1px solid #e6e3ee;
}
.stat {
display: flex;
flex-direction: column;
}
.stat dt {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #5b5966;
margin: 0;
}
.stat dd {
font-size: 1.05rem;
font-weight: 600;
margin: 0.15rem 0 0;
}
/* --- Pie ---------------------------------------------------------------- */
.site-footer {
margin-top: 3rem;
padding-top: 1.25rem;
border-top: 1px solid #e6e3ee;
color: #5b5966;
font-size: 0.85rem;
} Por qué este nivel
- El grid funciona con tres columnas fijas (1fr 1fr 1fr): se ve bien en escritorio.
- En pantallas estrechas las columnas se quedan cortas y el texto puede cortarse; no hay ningún mecanismo que lo evite.
- Colores y espaciados aparecen repetidos a lo largo del fichero. Si el diseñador cambia el color de acento, hay que rastrear el fichero entero y tocar en diez sitios.
- Es el punto de partida válido: funciona y se puede entregar. Pero escalar desde aquí es costoso.
/* ==========================================================================
NIVEL MEJOR — custom properties + grid responsive
==========================================================================
Dos cambios que se refuerzan:
1. Todos los valores que se repiten (colores, espaciados, radios) pasan a
custom properties en :root. Ahora hay un ÚNICO sitio donde tocar si
quieres cambiar el acento, el radio de las tarjetas o el padding base.
Antes estaban desperdigados en diez declaraciones; ahora en una sola
variable. Eso es el beneficio real de --variable: no "es bonito tener
variables", sino que el día que el diseñador cambia el color de acento
de la app, tú cambias una línea y todo el fichero se actualiza.
2. El grid pasa de tres columnas fijas a auto-fill + minmax(). El navegador
calcula cuántas columnas caben según el ancho disponible: en un móvil
estrecho sale una columna; en tablet, dos; en escritorio, tres o más.
Todo sin una sola media query.
========================================================================== */
:root {
/* Colores */
--color-tinta: #1c1b22;
--color-tinta-suave: #5b5966;
--color-linea: #e6e3ee;
--color-fondo: #f7f6fb;
--color-tarjeta: #ffffff;
--color-acento: #b8336a;
/* Espaciado */
--sp-xs: 0.5rem;
--sp-sm: 0.75rem;
--sp-md: 1rem;
--sp-lg: 1.25rem;
--sp-xl: 2rem;
--sp-2xl: 2.5rem;
--sp-3xl: 3rem;
/* Forma */
--radio-tarjeta: 12px;
--radio-circulo: 50%;
/* Tipografía */
--fuente-base: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--fuente-base);
color: var(--color-tinta);
background: var(--color-fondo);
line-height: 1.5;
}
.page {
max-width: 60rem;
margin: 0 auto;
padding: var(--sp-2xl) var(--sp-lg) 4rem;
}
/* --- Cabecera ----------------------------------------------------------- */
.site-header {
display: flex;
align-items: baseline;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--sp-sm);
padding-bottom: var(--sp-lg);
border-bottom: 1px solid var(--color-linea);
margin-bottom: var(--sp-xl);
}
.brand {
font-size: 1.4rem;
font-weight: 700;
letter-spacing: -0.01em;
margin: 0;
}
.brand span {
color: var(--color-acento);
}
.site-nav a {
color: var(--color-tinta-suave);
text-decoration: none;
font-size: 0.9rem;
margin-left: var(--sp-md);
}
.site-nav a:hover {
color: var(--color-acento);
}
/* --- Título de sección -------------------------------------------------- */
.section-title {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-tinta-suave);
margin: 0 0 var(--sp-md);
}
/* --- Grid de héroes ----------------------------------------------------- */
/* auto-fill: crea tantas columnas como quepan.
minmax(15rem, 1fr): cada columna mide al menos 15rem y puede crecer.
Resultado: en móvil 1 columna, en tablet 2, en escritorio 3 o más.
Sin media queries, sin breakpoints: el layout se adapta solo. */
.hero-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
gap: var(--sp-md);
list-style: none;
margin: 0;
padding: 0;
}
/* --- Carta de héroe ----------------------------------------------------- */
.hero-card {
background: var(--color-tarjeta);
border: 1px solid var(--color-linea);
border-radius: var(--radio-tarjeta);
padding: var(--sp-lg);
display: flex;
flex-direction: column;
gap: var(--sp-sm);
}
.hero-top {
display: flex;
align-items: center;
gap: 0.85rem;
}
.hero-portrait {
flex: none;
width: 3rem;
height: 3rem;
border-radius: var(--radio-circulo);
display: grid;
place-items: center;
font-weight: 700;
font-size: 0.85rem;
color: #fff;
background: var(--color-acento);
}
.hero-name {
font-size: 1.05rem;
font-weight: 700;
margin: 0;
}
.hero-role {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-tinta-suave);
margin: 0.1rem 0 0;
}
.hero-stats {
display: flex;
gap: var(--sp-lg);
margin: 0;
padding-top: var(--sp-xs);
border-top: 1px solid var(--color-linea);
}
.stat {
display: flex;
flex-direction: column;
}
.stat dt {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-tinta-suave);
margin: 0;
}
.stat dd {
font-size: 1.05rem;
font-weight: 600;
margin: 0.15rem 0 0;
}
/* --- Pie ---------------------------------------------------------------- */
.site-footer {
margin-top: var(--sp-3xl);
padding-top: var(--sp-lg);
border-top: 1px solid var(--color-linea);
color: var(--color-tinta-suave);
font-size: 0.85rem;
} Por qué es mejor que el anterior
- El grid pasa a repeat(auto-fill, minmax(15rem, 1fr)): el navegador decide cuántas columnas caben según el ancho disponible, sin ninguna media query.
- Todos los valores que se repetían están ahora en custom properties en :root. Hay un único sitio para cambiar el acento de color, el radio de las tarjetas o el espaciado base.
- auto-fill reserva columnas aunque estén vacías: con dos héroes en un contenedor ancho puede quedar una columna vacía a la derecha. Eso lo resuelve el nivel Excelente.
/* ==========================================================================
NIVEL EXCELENTE — tokens organizados, auto-fit y diseño que escala
==========================================================================
El salto desde "Mejor" son tres mejoras que trabajan juntas:
1. auto-fit en vez de auto-fill: cuando hay pocas cartas, auto-fill deja
columnas vacías que "reservan espacio"; auto-fit las colapsa y las cartas
presentes se estiran para llenar el ancho. Con una carta sola, auto-fit
la muestra a ancho completo; auto-fill la dejaría en una columna de 15rem
flotando a la izquierda. Pruébalo: añade o quita héroes del HTML y mira
cómo se reorganiza el grid solo.
2. Tokens organizados en grupos con comentarios: no solo están en :root,
están explicados. --sp-* es la escala de espaciado, --c-* los colores,
--grid-col-min controla el comportamiento del grid desde un solo sitio.
Quieres tarjetas más anchas en una intranet de escritorio grande: cambias
--grid-col-min a 20rem y ya. El resto del CSS no se toca.
3. Una variable --grid-col-min que actúa como "palanca de densidad": quien
mantenga este CSS en el futuro sabe exactamente dónde tirar para cambiar
cuántas tarjetas caben por fila. No hay que buscar dentro del valor de
grid-template-columns. Eso es lo que distingue a un CSS mantenible de uno
que funciona: que quien lo lee pueda entender la intención, no solo el
resultado.
========================================================================== */
:root {
/* --- Paleta de color ----------------------------------------------- */
--c-tinta: #1c1b22;
--c-tinta-suave: #5b5966;
--c-linea: #e6e3ee;
--c-fondo: #f7f6fb;
--c-tarjeta: #ffffff;
--c-acento: #b8336a;
--c-acento-texto: #fff;
/* --- Tipografía ---------------------------------------------------- */
--fuente: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
/* --- Escala de espaciado (múltiplos de 0.25rem) -------------------- */
--sp-1: 0.25rem;
--sp-2: 0.5rem;
--sp-3: 0.75rem;
--sp-4: 1rem;
--sp-5: 1.25rem;
--sp-6: 1.5rem;
--sp-8: 2rem;
--sp-10: 2.5rem;
--sp-12: 3rem;
/* --- Forma --------------------------------------------------------- */
--radio-tarjeta: 12px;
/* --- Grid — palanca de densidad ------------------------------------ */
/* Cambia este valor para controlar cuántas columnas caben por fila:
- 12rem → más columnas, tarjetas compactas (panel de seguimiento)
- 15rem → equilibrio (este proyecto)
- 20rem → menos columnas, tarjetas amplias (vista de ficha detallada) */
--grid-col-min: 15rem;
}
/* =========================================================================
RESET MÍNIMO
========================================================================= */
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--fuente);
color: var(--c-tinta);
background: var(--c-fondo);
line-height: 1.5;
}
/* =========================================================================
LAYOUT DE PÁGINA
========================================================================= */
.page {
max-width: 60rem;
margin: 0 auto;
padding: var(--sp-10) var(--sp-5) 4rem;
}
/* =========================================================================
CABECERA
========================================================================= */
.site-header {
display: flex;
align-items: baseline;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--sp-3);
padding-bottom: var(--sp-5);
border-bottom: 1px solid var(--c-linea);
margin-bottom: var(--sp-8);
}
.brand {
font-size: 1.4rem;
font-weight: 700;
letter-spacing: -0.01em;
margin: 0;
}
.brand span {
color: var(--c-acento);
}
.site-nav a {
color: var(--c-tinta-suave);
text-decoration: none;
font-size: 0.9rem;
margin-left: var(--sp-4);
}
.site-nav a:hover {
color: var(--c-acento);
}
/* =========================================================================
TÍTULO DE SECCIÓN
========================================================================= */
.section-title {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--c-tinta-suave);
margin: 0 0 var(--sp-4);
}
/* =========================================================================
GRID DE HÉROES
========================================================================= */
/* auto-fit colapsa columnas vacías: si solo hay 2 héroes, el grid usa
2 columnas (no 3 con una hueca). auto-fill habría reservado la tercera.
Prueba a comentar héroes del HTML para ver la diferencia. */
.hero-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(var(--grid-col-min), 1fr));
gap: var(--sp-4);
list-style: none;
margin: 0;
padding: 0;
}
/* =========================================================================
CARTA DE HÉROE
========================================================================= */
.hero-card {
background: var(--c-tarjeta);
border: 1px solid var(--c-linea);
border-radius: var(--radio-tarjeta);
padding: var(--sp-5);
display: flex;
flex-direction: column;
gap: var(--sp-3);
}
.hero-top {
display: flex;
align-items: center;
gap: 0.85rem;
}
.hero-portrait {
flex: none;
width: 3rem;
height: 3rem;
border-radius: 50%;
display: grid;
place-items: center;
font-weight: 700;
font-size: 0.85rem;
color: var(--c-acento-texto);
background: var(--c-acento);
}
.hero-name {
font-size: 1.05rem;
font-weight: 700;
margin: 0;
}
.hero-role {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--c-tinta-suave);
margin: 0.1rem 0 0;
}
.hero-stats {
display: flex;
gap: var(--sp-5);
margin: 0;
padding-top: var(--sp-2);
border-top: 1px solid var(--c-linea);
}
.stat {
display: flex;
flex-direction: column;
}
.stat dt {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--c-tinta-suave);
margin: 0;
}
.stat dd {
font-size: 1.05rem;
font-weight: 600;
margin: 0.15rem 0 0;
}
/* =========================================================================
PIE
========================================================================= */
.site-footer {
margin-top: var(--sp-12);
padding-top: var(--sp-5);
border-top: 1px solid var(--c-linea);
color: var(--c-tinta-suave);
font-size: 0.85rem;
} Por qué es mejor que el anterior
- auto-fit colapsa las columnas vacías: si hay dos héroes, el grid usa dos columnas y las cartas llenan el ancho. auto-fill habría reservado la tercera. Prueba a añadir o quitar héroes del HTML para verlo.
- Una variable --grid-col-min actúa como palanca de densidad: cámbiala a 12rem para un panel compacto, a 20rem para una vista de ficha amplia. El resto del CSS no se toca.
- Los tokens están agrupados por categoría (colores, escala de espaciado, forma, grid) con comentarios que explican el propósito de cada grupo. Quien mantiene el fichero entiende la intención, no solo el resultado.
- El diseño aguanta de 1 a N héroes sin tocar una regla: el grid se reorganiza solo. Eso es lo que significa un sistema de diseño en CSS.