En el capítulo anterior aprendiste qué es una regla CSS y cómo escribirla: selector { propiedad: valor; }. El selector era el nombre de la etiqueta —p, h1— y funcionaba,
pero de forma muy poco precisa. Si estilabas p dabas estilo a todos los párrafos de
la página, aunque solo quisieras cambiar uno.
Los selectores son la forma de apuntarle al navegador exactamente al elemento que quieres
cambiar. Con ellos puedes decir “este <p> concreto”, “todos los <a> que estén dentro de
la cabecera” o “el enlace cuando el usuario pasa el ratón”. Esa precisión es lo que separa
un CSS que crece limpio de uno que se convierte en un enredo de reglas que se machacan unas
a otras.
Seguimos con el Overwatch Team Builder. El HTML de la cabecera y las cartas de héroe ya está hecho. En este capítulo le damos estilo apuntando con precisión: cada selector afecta exactamente a lo que queremos, y nada más.
Selector de tipo, de clase y de ID#
Ya conoces los tres básicos del capítulo anterior. Vale la pena tenerlos claros antes de avanzar:
/* Tipo: afecta a todos los <p> de la página */
p {
color: #5b5966;
}
/* Clase: afecta a todos los elementos con class="hero-card" */
.hero-card {
background: white;
border: 1px solid #e6e3ee;
}
/* ID: afecta al único elemento con id="cabecera" */
#cabecera {
background: #1c1b22;
}La regla práctica que aplica la industria:
- Selector de tipo para resets y normalizaciones que de verdad afectan a todos los elementos
de ese tipo: quitar el margen de
body, o elpaddingy ellist-stylede todos losul. - Clase para prácticamente todo lo demás. Es reutilizable (varios elementos pueden tener la misma clase), fácil de sobrescribir cuando hace falta y no añade especificidad excesiva.
- ID para anclar enlaces dentro de la página (
href="#cabecera"), no para dar estilo. Los ID tienen mayor especificidad que las clases y son difíciles de sobrescribir. En la práctica, casi nunca merece la pena usarlos en CSS.
Agrupación: un bloque para varios selectores#
Si h1, h2 y h3 comparten el mismo font-family, no tiene sentido escribir tres reglas
idénticas. La coma agrupa selectores que comparten las mismas declaraciones:
h1,
h2,
h3 {
font-family: system-ui, sans-serif;
}Esto es equivalente a tres reglas separadas, pero si cambia la fuente solo hay que tocar un lugar. En el Team Builder, el brand de la cabecera y los enlaces del nav comparten el mismo color blanco: se agrupan y se declaran una vez.
Combinadores: relaciones entre elementos#
Un combinador es un símbolo entre dos selectores que describe la relación entre el padre y el hijo. Los dos que vas a usar más:
Combinador descendiente (el espacio)#
.hero-card p {
color: #5b5966;
}El espacio entre .hero-card y p significa “un <p> en cualquier punto del interior de
.hero-card”, por muy anidado que esté. Selecciona descendientes a cualquier nivel.
Combinador hijo directo (>)#
.hero-list > li {
margin-bottom: 1rem;
}El > es más estricto: solo afecta a los hijos inmediatos. En el Team Builder, .hero-list
contiene <li> con las cartas, pero cada carta tiene a su vez una .hero-stats que también es
una <ul> con sus propios <li>. Con el espacio (descendiente), el estilo llegaría a todos;
con >, solo a los hijos directos de .hero-list.
La diferencia importa cuando tienes listas anidadas o cualquier estructura donde el mismo tipo de elemento aparece en varios niveles del árbol.
Pseudo-clases: el estado del elemento#
Una pseudo-clase selecciona un elemento en un estado concreto. Se escribe con dos puntos después del selector:
.hero-link:hover {
text-decoration: underline;
}Las dos pseudo-clases que más vas a usar en este nivel:
:hover— cuando el puntero del ratón está sobre el elemento. Funciona en cualquier elemento, no solo en enlaces.:focus— cuando el elemento recibe el foco: al hacer clic en él, pero también al llegar a él navegando con la tecla Tab. Es la contrapartida de:hoverpara el teclado.
Escríbelas siempre juntas en los elementos interactivos:
.site-nav a:hover,
.site-nav a:focus {
color: #b8336a;
}Ignorar :focus es uno de los errores de accesibilidad más habituales. Quien usa el teclado
para navegar —personas con movilidad reducida, usuarios de lectores de pantalla, cualquiera
que prefiera no soltar las manos del teclado— necesita saber en qué enlace está. Si solo hay
:hover, el indicador visual desaparece para ellos.
El selector universal *#
El asterisco selecciona todos los elementos del documento:
* {
box-sizing: border-box;
}Úsalo con cuidado. Tiene sentido para resets que de verdad deben afectar a cada elemento de
la página (el reset de box-sizing, que verás en el capítulo de Box model, es el caso clásico).
Para colores, fuentes o espaciado hay opciones más precisas: body para propiedades que los
hijos heredan, o clases para elementos concretos.
Una nota sobre la especificidad#
Como viste en el capítulo “Qué es CSS”, los selectores tienen distinto peso. La regla de oro en proyectos reales: usa la especificidad mínima suficiente. No encadenes cinco selectores para sobrescribir otro, y no tires de ID para ganar por fuerza bruta. Si necesitas veinte selectores encadenados para que una regla “gane”, algo está mal en la arquitectura del CSS, no en el selector que intentas añadir.
El objetivo es que el CSS sea predecible: sabes que .hero-name afecta a los nombres de los
héroes, y solo a ellos. Sin sorpresas.
Los colores y unidades de los ejemplos —#b8336a, 1rem, 0.9rem— los verás en detalle en
el siguiente capítulo. 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. Por ahora vale con
entender que son valores válidos; no hace falta memorizarlos todavía.
Pruébalo#
La cabecera y las cartas del Team Builder con los selectores del capítulo aplicados. Abre la
pestaña /styles.css y prueba a:
- Cambiar
.hero-nameporh2y observar quéh2más aparece afectado (ninguno en este HTML, pero en uno con cabecera con unh2se vería enseguida). - Cambiar
.hero-list > lipor.hero-list li(sin>) y ver si hay diferencia visible. En este HTML no la hay, pero si abres las DevTools y miras los.stat, sí que la encontrarás. - Pasar el ratón sobre un enlace (
:hover) y luego navegar con Tab hasta él (:focus).
Comprueba lo que sabes#
Pregunta 1 de 5
Tienes varias cartas de héroe en la página. ¿Qué selector es más adecuado para darles estilo?
Tu turno#
El HTML está completo. Tu tarea es escribir el CSS usando los selectores vistos en el capítulo, aquí mismo. Los comentarios TODO del starter te guían sobre qué selector practicar en cada bloque. Edita el CSS en el playground; cuando creas que lo tienes, despliega las soluciones y compáralas con la tuya.
Ejercicio · en esta página
Dale estilo a la ficha del equipo con los selectores correctos
El HTML de la página ya está hecho: una cabecera con navegación, una lista de tres cartas de héroe y un pie. Tu tarea es escribir el CSS en styles.css usando la variedad de selectores vista en el capítulo. Los comentarios TODO te indican qué selector practicar en cada bloque.
Paso 1: Que se vea
- La página tiene estilos visibles: cabecera con fondo oscuro, cartas con fondo blanco y borde.
- Vale aunque use ID para la cabecera y selectores de tipo donde deberían ir clases.
- Los enlaces cambian al pasar el ratón (aunque falte :focus).
Paso 2: El selector correcto para cada cosa
- Clase (.site-header) en lugar de ID (#cabecera) para la cabecera.
- Clase (.hero-name) en lugar de tipo (h2) para los títulos de la carta.
- Combinador hijo directo (.hero-list > li) para no afectar a los .stat.
- :hover y :focus siempre juntos en los enlaces.
- Agrupación con coma para no repetir el mismo valor dos veces.
Paso 3: Especificidad justa
- Combinadores descendientes en .hero-stats .stat-label y .hero-stats .stat-value: precisión sin sobre-especificar.
- Sin ningún ID para dar estilo; todo por clase.
- :focus en todos los elementos interactivos, no solo en los del nav.
- Cada selector hace exactamente lo que necesita: ni más amplio ni más específico de lo necesario.
Ver soluciones
/* =============================================================================
NIVEL OK — "funciona y se ve"
=============================================================================
Resuelve el ejercicio y la página se ve. Ese es el objetivo mínimo.
Sus límites (lo que arregla el nivel "Mejor"):
- Estila los <h2> de toda la página con selector de tipo: si más adelante
añades un h2 en la cabecera o el pie, se estilará igual que los nombres
de héroe, sin que lo hayas pedido.
- Usa el ID #cabecera para el fondo oscuro de la cabecera. Los ID tienen
mayor especificidad que las clases y son difíciles de sobrescribir.
- Reglas repetidas: el color del brand y el color de los enlaces del nav
se declaran por separado, aunque son idénticos.
- Sin :focus en los enlaces: quien navega con teclado no ve qué enlace
tiene el foco.
Aun así: todo se ve correctamente. Es un buen punto de partida.
============================================================================= */
body {
margin: 0;
font-family: system-ui, sans-serif;
}
ul {
padding: 0;
list-style: none;
}
/* Usa el ID en lugar de la clase */
#cabecera {
background: #1c1b22;
color: #f7f6fb;
padding: 0.75rem 1.5rem;
}
/* Selector de tipo: afecta a todos los <a> de la página */
a {
color: #f7f6fb;
text-decoration: none;
}
/* Repetición: el brand se redeclara por separado */
.brand {
font-size: 1.2rem;
font-weight: 700;
color: #f7f6fb;
}
.brand-accent {
color: #b8336a;
}
.site-nav a {
font-size: 0.9rem;
}
.site-nav a:hover {
color: #b8336a;
}
/* Sin :focus */
.page {
max-width: 52rem;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.intro {
color: #5b5966;
}
/* Selector de tipo: afecta a TODOS los <li>, no solo a los de .hero-list */
li {
margin-bottom: 1rem;
}
.hero-card {
background: #fff;
border: 1px solid #e6e3ee;
border-radius: 10px;
padding: 1.25rem;
}
/* Selector de tipo: si añades un h2 fuera de la carta, recibirá estos estilos */
h2 {
font-size: 1.1rem;
margin: 0;
}
.hero-role {
color: #5b5966;
font-size: 0.8rem;
text-transform: uppercase;
}
.stat-label {
font-size: 0.72rem;
color: #5b5966;
}
.stat-value {
font-weight: 600;
}
.hero-link {
color: #b8336a;
text-decoration: none;
}
.hero-link:hover {
text-decoration: underline;
}
/* Sin :focus */
.site-footer {
margin-top: 2rem;
padding: 1rem 1.5rem;
border-top: 1px solid #e6e3ee;
color: #5b5966;
font-size: 0.85rem;
} Por qué este nivel
- La página se ve: cabecera oscura, cartas con fondo blanco y bordes redondeados, enlaces que cambian al pasar el ratón. Ese es el objetivo mínimo.
- Usa `#cabecera` (ID) para el fondo oscuro de la cabecera. Los ID tienen mayor especificidad que las clases y son difíciles de sobrescribir. Si en el futuro necesitas cambiar ese fondo en un contexto distinto, te costará más trabajo.
- El selector `h2 { ... }` apunta a todos los encabezados de nivel 2 del documento. Si la página crece y aparece un h2 en la cabecera o el pie, recibirá los mismos estilos que el nombre del héroe, sin que lo hayas pedido.
- El `li { ... }` afecta a todos los `<li>` de la página, incluidos los de `.hero-stats`. El margin-bottom acaba aplicándose también a cada estadística dentro de la carta.
- Falta `:focus` en los enlaces: quien navega con el teclado no ve qué enlace tiene el foco.
/* =============================================================================
NIVEL MEJOR — "el selector correcto para cada cosa"
=============================================================================
Mismo resultado visual. La diferencia está en la precisión de cada selector.
Sus límites (lo que arregla el nivel "Excelente"):
- Los .stat-label y .stat-value se seleccionan sueltos, sin combinador:
si hubiera otro .stat-label fuera de .hero-stats, recibiría el mismo
estilo. En un proyecto pequeño no es un problema; en uno grande, sí.
- No hay agrupación de los selectores de cabecera que comparten color.
No es un error, pero repetiría valores si cambia la paleta.
============================================================================= */
body {
margin: 0;
font-family: system-ui, sans-serif;
}
/* Selector de tipo solo donde de verdad hace falta: los <ul>
de toda la página sí queremos que pierdan el list-style por defecto. */
ul {
padding: 0;
list-style: none;
}
/* Clase en lugar de ID: más reutilizable y más fácil de sobrescribir */
.site-header {
background: #1c1b22;
color: #f7f6fb;
padding: 0.75rem 1.5rem;
}
.brand {
font-size: 1.2rem;
font-weight: 700;
}
.brand-accent {
color: #b8336a;
}
/* Agrupación: brand y enlaces del nav comparten el mismo color blanco.
Escribirlo una vez evita que se desincronicen si el diseño cambia. */
.brand,
.site-nav a {
color: #f7f6fb;
text-decoration: none;
}
/* Combinador descendiente: solo los <a> dentro de .site-nav */
.site-nav a {
font-size: 0.9rem;
}
/* :hover Y :focus juntos: el comportamiento de teclado y ratón debe ser
el mismo. No es accesible tener :hover sin :focus. */
.site-nav a:hover,
.site-nav a:focus {
color: #b8336a;
}
.page {
max-width: 52rem;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.intro {
color: #5b5966;
}
/* Combinador hijo directo: solo los <li> hijos directos de .hero-list,
no cualquier <li> de la página. */
.hero-list > li {
margin-bottom: 1rem;
}
.hero-card {
background: #fff;
border: 1px solid #e6e3ee;
border-radius: 10px;
padding: 1.25rem;
}
/* Clase en lugar de selector de tipo: solo el .hero-name,
no todos los <h2> de la página. */
.hero-name {
font-size: 1.1rem;
margin: 0;
}
.hero-role {
color: #5b5966;
font-size: 0.8rem;
text-transform: uppercase;
}
.stat-label {
font-size: 0.72rem;
color: #5b5966;
}
.stat-value {
font-weight: 600;
}
.hero-link {
color: #b8336a;
text-decoration: none;
}
.hero-link:hover,
.hero-link:focus {
text-decoration: underline;
}
.site-footer {
margin-top: 2rem;
padding: 1rem 1.5rem;
border-top: 1px solid #e6e3ee;
color: #5b5966;
font-size: 0.85rem;
} Por qué es mejor que el anterior
- Clase `.site-header` en lugar de `#cabecera`: la especificidad es menor y el selector es reutilizable. Nada cambia visualmente, pero el CSS es más fácil de mantener.
- Clase `.hero-name` en lugar de `h2`: ahora el selector apunta solo a los títulos de la carta, sin tocar ningún otro `h2` que pudiera aparecer en el documento.
- Combinador hijo directo `.hero-list > li`: solo los `<li>` hijos directos reciben el `margin-bottom`. Los `.stat` —que son `<li>` de una lista anidada dentro de `.hero-stats`— no se ven afectados.
- `:hover` y `:focus` siempre juntos: el comportamiento visual al pasar el ratón y al navegar con el teclado es idéntico. Es la costumbre correcta para cualquier elemento interactivo.
- Agrupación `.brand, .site-nav a { color: #f7f6fb }`: el color blanco compartido se declara una vez. Si el diseño cambia, es un único punto de edición.
/* =============================================================================
NIVEL EXCELENTE — "especificidad justa, sin sobre-seleccionar"
=============================================================================
Mismo resultado visual. Aquí cada selector hace justo lo que necesita,
ni más ni menos. Reutilizable y fácil de mantener.
============================================================================= */
body {
margin: 0;
font-family: system-ui, sans-serif;
}
/* Selector de tipo en el único lugar donde tiene sentido global:
quitar el padding y el list-style de todos los <ul> es una normalización
válida para todo el documento. */
ul {
padding: 0;
list-style: none;
}
/* Clase para la cabecera: semántica, reutilizable y sin guerra de especificidad. */
.site-header {
background: #1c1b22;
color: #f7f6fb;
padding: 0.75rem 1.5rem;
}
/* Agrupación: todo lo que comparte color blanco en la cabecera declarado una sola vez.
Cuando el diseño cambie, el cambio es en un único punto. */
.brand,
.site-nav a {
color: #f7f6fb;
text-decoration: none;
}
.brand {
font-size: 1.2rem;
font-weight: 700;
}
/* Clase hija del brand: selector de clase con especificidad mínima suficiente. */
.brand-accent {
color: #b8336a;
}
/* Combinador descendiente: precisión suficiente sin encadenar más selectores.
.site-nav a es específico porque .site-nav ya delimita el contexto. */
.site-nav a {
font-size: 0.9rem;
}
/* :hover y :focus siempre juntos en interacciones. El teclado es un dispositivo
de entrada tan válido como el ratón; no hay excusa para ignorarlo. */
.site-nav a:hover,
.site-nav a:focus {
color: #b8336a;
}
.page {
max-width: 52rem;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.intro {
color: #5b5966;
}
/* Combinador hijo directo: .hero-list > li afecta solo a los li que son hijos
inmediatos de .hero-list, no a los .stat que son li de una lista anidada. */
.hero-list > li {
margin-bottom: 1rem;
}
.hero-card {
background: #fff;
border: 1px solid #e6e3ee;
border-radius: 10px;
padding: 1.25rem;
}
/* Clase sobre selector de tipo: .hero-name alcanza exactamente las dos palabras
"nombre del héroe" dentro de cada carta, sin tocar otros h2 del documento. */
.hero-name {
font-size: 1.1rem;
margin: 0;
}
.hero-role {
color: #5b5966;
font-size: 0.8rem;
text-transform: uppercase;
}
/* Combinadores descendientes para las estadísticas: .hero-stats .stat-label
y .hero-stats .stat-value garantizan que estas reglas solo se aplican dentro
del bloque de estadísticas, aunque .stat-label exista en otro contexto. */
.hero-stats .stat-label {
font-size: 0.72rem;
color: #5b5966;
}
.hero-stats .stat-value {
font-weight: 600;
}
/* :hover y :focus juntos, igual que en el nav.
text-decoration: none en el estado base, underline al interactuar:
hace evidente que el enlace responde. */
.hero-link {
color: #b8336a;
text-decoration: none;
}
.hero-link:hover,
.hero-link:focus {
text-decoration: underline;
}
.site-footer {
margin-top: 2rem;
padding: 1rem 1.5rem;
border-top: 1px solid #e6e3ee;
color: #5b5966;
font-size: 0.85rem;
} Por qué es mejor que el anterior
- Combinadores descendientes en las estadísticas: `.hero-stats .stat-label` y `.hero-stats .stat-value` garantizan que estas reglas solo se aplican dentro del bloque de estadísticas. Si en otra parte de la página apareciera un `.stat-label`, no recibiría estos estilos.
- Sin ningún ID para dar estilo. Los ID tienen mayor especificidad que cualquier clase y encadenar selectores para sobrescribirlos es costoso. En el nivel Excelente, todo se estila por clase.
- `:focus` en todos los elementos interactivos, no solo en los del nav. Los `.hero-link` también responden al foco del teclado: el comportamiento de ratón y teclado es consistente en toda la página.
- Cada selector tiene la especificidad mínima necesaria para su propósito. No hay reglas encadenadas con cinco selectores ni IDs que "ganen" por fuerza bruta: la cascada funciona de forma predecible.