Skip to content
This page has been auto-translated and may contain errors.View in English

Selectores CSS

docs.scrimba.com

Tienes una página llena de elementos y un conjunto de estilos que quieres aplicar, pero un color o una fuente no sirven de nada hasta que puedas decir a qué elementos pertenecen. Un selector es cómo apuntas a los correctos: este párrafo, cada botón, solo los enlaces dentro del nav. Familiarízate con los selectores y el resto de CSS se convierte en una cuestión de elegir qué cambiar, porque ya sabes cómo alcanzarlo.

Selectores de tipo, clase e id

Una regla CSS tiene dos partes: un selector que elige qué elementos estilizar, y un bloque de declaraciones que dice cómo. El selector más simple es el nombre del elemento, llamado selector de tipo. Escribe p y cada párrafo en la página tendrá ese estilo.

Eso a menudo es demasiado amplio. Rara vez quieres que todos los párrafos se vean igual. Entonces das a los elementos una etiqueta llamada clase, usando el atributo HTML class, y apuntas a esa etiqueta desde CSS con un punto adelante. Piensa en una clase como una etiqueta de nombre: pegas la misma etiqueta en cada elemento que quieres tratar como grupo, y luego estilizas la etiqueta una sola vez.

Hay tres formas básicas de apuntar a un elemento. Un selector de tipo coincide por nombre de etiqueta (p coincide con cada párrafo). Un selector de clase coincide por el atributo class y se escribe con un punto adelante (.button). Un selector de id coincide por el atributo id y se escribe con una almohadilla adelante (#header).

Usa clases como tu gancho de estilización predeterminado. Una clase puede ir en tantos elementos como quieras y un elemento puede llevar varias clases, así que las clases describen grupos reutilizables: todo con .card comparte una apariencia. Los selectores de tipo son útiles para estilos base amplios (establecer la fuente del cuerpo, por ejemplo) pero son rudos, ya que golpean cada elemento de ese tipo. Los ids están pensados para ser únicos en una página, así que estilizar a través de ellos bloquea una regla a un solo elemento y, como verás bajo especificidad, llevan peso extra que los hace incómodos de anular.

Tres tipos de selectores cubren lo básico, y la distinción entre ellos es en parte sobre coincidencia y en parte sobre peso. Un selector de tipo (también llamado selector de etiqueta o elemento) coincide por nombre de elemento: p, section, input. Un selector de clase, escrito .card, coincide con cualquier elemento cuyo atributo class contenga ese token, y el atributo está separado por espacios así que un elemento puede llevar muchas clases. Un selector de id, escrito #header, coincide con el único elemento cuyo atributo id es igual a ese valor; se requiere que un id sea único dentro de un documento.

La regla práctica es estilizar a través de clases casi exclusivamente. Las clases son la única de las tres que es tanto reutilizable como baja en especificidad (la puntuación de prioridad que el navegador usa para resolver conflictos, cubierta al final de este capítulo), lo que mantiene tus estilos planos y anulables. Los selectores de tipo pertenecen a un pequeño conjunto de reglas base: un reset, la fuente predeterminada en body, valores sensatos en a o input. Los ids son legítimos como ganchos de JavaScript y como objetivos de enlaces de fragmento (#pricing en una URL), pero como gancho de estilización son una trampa: son únicos así que no pueden describir un grupo, y su especificidad alta significa que una regla de clase posterior no puede anularlos sin escalada. Estiliza con clases, reserva los ids para anclas y scripts.

html
<button class="btn-primary">Registrarse</button>
<button class="btn-primary">Comprar ahora</button>
css
/* selector de tipo: cada botón en la página */
button {
  font-family: inherit;
}

/* selector de clase: solo elementos etiquetados btn-primary */
.btn-primary {
  background: rebeccapurple;
  color: white;
}

Ambos botones recogen el estilo .btn-primary de una regla, porque comparten la clase. Ese reuso es la razón completa por la que las clases son la herramienta que usas. Esto se construye directamente sobre cómo funciona CSS, donde cada regla empareja un selector con un conjunto de declaraciones.

JunoSelectores de tipo, clase e id Un selector de tipo como p golpea cada elemento de ese tipo, que es usualmente más de lo que quieres. Una clase es una etiqueta de nombre que pegas en los elementos que te importan y estilizas con un punto, como .btn-primary. Comienza con clases para casi todo y no irás muy lejos.
JunoSelectores de tipo, clase e id Las clases son tu gancho predeterminado: reutilizables, y un elemento puede llevar varias. Mantén los selectores de tipo para estilos base amplios como la fuente del cuerpo, y trata los ids como demasiado específicos y demasiado de un solo uso para estilizar. Ahórrate el dolor de cabeza futuro y alcanza una clase primero.
JunoSelectores de tipo, clase e id Las clases son el único selector básico que es tanto reutilizable como de baja especificidad, que es exactamente por qué estilizas a través de ellas y mantienes tus reglas planas. Los selectores de tipo se ganan su lugar en una pequeña capa base, e ids pertenecen a scripts y anclas de enlaces, no a estilización. El día que una regla de id se niega a ser anulada por una clase posterior, estarás contento de haberlas mantenido fuera.

Combinadores: seleccionar por relación

A veces quieres estilizar un elemento solo cuando se sienta dentro de otro. Los enlaces en tu navegación deberían verse diferentes de enlaces en un párrafo, aunque ambos sean elementos <a>. Describes esa relación con un combinador: pon dos selectores uno al lado del otro con un espacio, y CSS lo lee como "el segundo, pero solo dentro del primero".

Entonces .nav a significa "cualquier enlace dentro de un elemento con la clase nav". El espacio está haciendo trabajo real aquí. No es decoración, es la instrucción para mirar dentro.

Un combinador une dos selectores y coincide basado en cómo los elementos se relacionan en el HTML. El más común es el combinador de descendiente, escrito como un espacio: .nav a coincide con cada <a> en cualquier lugar dentro de un .nav, no importa qué tan profundamente anidado.

Cuando quieres solo hijos directos y no descendientes más profundos, usa el combinador de hijo >. .menu > li coincide con elementos de lista que son hijos inmediatos de .menu, saltando cualquier lista anidada. También hay dos combinadores de hermanos para elementos que comparten un padre: el hermano adyacente + coincide con el elemento inmediatamente después de otro (h2 + p estiliza el párrafo justo después de un encabezado), y el hermano general ~ coincide con cualquier hermano que sigue. Descendiente e hijo cubren la mayoría del trabajo real; los combinadores de hermanos son útiles para espaciado y patrones de formularios.

Un combinador es el carácter entre dos selectores que define la relación estructural que deben satisfacer. El combinador de descendiente (un espacio) coincide con el selector de la derecha cuando está anidado en cualquier lugar dentro de la izquierda, a cualquier profundidad. El combinador de hijo (>) restringe eso solo a hijos directos: .menu > li ignora elementos de lista enterrados en un .submenu anidado. El combinador de hermano adyacente (+) coincide con un elemento que sigue inmediatamente a otro con el mismo padre, y el combinador de hermano general (~) coincide con cualquier hermano posterior con el mismo padre.

Hay dos cosas que vale la pena retener en producción. Primero, un selector compuesto se lee de derecha a izquierda en el matcher del navegador: para .nav a el motor encuentra los elementos <a> primero, luego sube para verificar que cada uno tenga un ancestro .nav. Esto es por qué cadenas de descendientes muy largas cuestan más y por qué un .nav > a apretado puede ser más barato que un .nav a suelto. Segundo, los selectores de descendiente acoplan tu CSS a tu estructura de marcado: .card .title se rompe en el momento en que alguien envuelve el título en otro elemento. Favorecer una clase directa en el elemento (.card-title) mantiene los estilos resilientes a cambios de marcado, y desciendes a combinadores cuando una relación es lo que realmente quieres expresar.

css
/* descendiente: cualquier enlace dentro del nav */
.nav a {
  text-decoration: none;
}

/* hijo: solo elementos de lista que son hijos directos de .menu */
.menu > li {
  display: inline-block;
}

La primera regla alcanza cada enlace anidado dentro del nav; la segunda alcanza solo los elementos de nivel superior del menú y deja cualquier lista anidada en paz. Elegir entre ellas es elegir qué tan lejos abajo quieres que viaje la regla.

JunoCombinadores Un espacio entre dos selectores significa "dentro": .nav a estiliza enlaces que se sientan dentro del nav. Ese espacio es una instrucción, no espaciado por apariencia, así que no lo elimines por accidente. Este patrón cubre mucho de la estilización cotidiana.
JunoCombinadores El espacio es el combinador de descendiente y coincide a cualquier profundidad; > lo estrecha solo a hijos directos. Hermanos + y ~ están allí cuando necesitas estilizar un elemento basado en lo que viene antes. Descendiente e hijo manejarán la mayoría de lo que escribes día a día.
JunoCombinadores Los combinadores son útiles, y también atan tu CSS a tu estructura HTML, así que una cadena larga de descendientes como .card .title está a un refactor de distancia de romperse. Prefiere una clase directa en el elemento y desciende a combinadores cuando la relación es lo que realmente quieres expresar. La coincidencia también corre de derecha a izquierda, así que un &gt; apretado a menudo vence a una cadena de descendientes suelta.

Pseudo-clases: estilizar por estado

Los elementos pueden estar en diferentes estados. Un enlace puede estar quieto o tener el mouse pasando sobre él. Una pseudo-clase te permite estilizar un elemento basado en su estado, y la escribes con dos puntos después del selector.

La que usarás primero es :hover. Escribe .btn-primary:hover y esos estilos se aplican solo mientras el puntero está sobre el botón, que es cómo los botones y enlaces dan esa pequeña respuesta visual cuando apuntas a ellos.

Una pseudo-clase apunta a un elemento en un estado o posición particular, escrita con dos puntos: :hover mientras el puntero está sobre él, :focus cuando está seleccionado para entrada de teclado, :first-child cuando es el primer elemento dentro de su padre.

:focus merece atención porque es una preocupación de accesibilidad, no una comodidad. Las personas que navegan con un teclado tabulan de un control a otro, y el estilo :focus es la única señal que les dice dónde están. Nunca elimines los contornos de enfoque sin reemplazarlos con una alternativa visible. También hay pseudo-clases estructurales como :nth-child(), que toma un patrón entre paréntesis: :nth-child(odd) coincide con cada otro elemento, útil para rayar filas de tabla. :first-child y :last-child agarran los extremos de un grupo sin necesitar una clase extra.

Una pseudo-clase (una palabra clave después de dos puntos) coincide con un elemento que está en un estado dado o posición estructural, sin que agregues ningún marcado para ello. Las pseudo-clases de estado incluyen :hover, :active, y :focus; las estructurales incluyen :first-child, :last-child, y la parametrizada :nth-child(), cuyo argumento es un patrón an+b (:nth-child(2n) es cada segundo elemento, :nth-child(3n+1) es el primero de cada grupo de tres).

Dos notas de producción. :focus es innegociable para accesibilidad de teclado: los usuarios de teclado dependen del anillo de enfoque para saber su posición, así que despojar el contorno sin un reemplazo visible hace una interfaz inutilizable para ellos. Prefiere :focus-visible, que muestra el anillo para interacción de teclado pero lo suprime en clic de mouse, así obtienes tanto una experiencia de puntero limpia como una de teclado accesible. En el lado estructural, recuerda que :nth-child(n) cuenta todos los hermanos sin importar el tipo, mientras que :nth-of-type(n) cuenta solo hermanos del mismo elemento, que es la diferencia entre una regla que funciona y una que silenciosamente miscuenta cuando un elemento extraño se sienta en la lista.

css
.btn-primary:hover {
  background: purple;
}

/* los usuarios de teclado necesitan un indicador de enfoque visible */
.btn-primary:focus-visible {
  outline: 2px solid rebeccapurple;
  outline-offset: 2px;
}

La primera regla se dispara mientras el puntero está sobre el botón; la segunda dibuja un anillo claro cuando el botón está seleccionado por teclado, que es lo que mantiene la página usable sin un mouse. Las elecciones de color y contorno aquí se conectan a cómo funciona CSS y los valores que estableces en cualquier propiedad.

JunoPseudo-clases Una pseudo-clase estiliza un elemento en un cierto estado, escrita con dos puntos, como :hover para cuando el puntero está sobre él. Ahí es de dónde viene el efecto hover de un botón. Lo atas directamente al selector, sin espacio en medio.
JunoPseudo-clases Las pseudo-clases coinciden con estado y posición: :hover, :focus, :nth-child(), :first-child. Trata :focus como un requisito de accesibilidad, porque los usuarios de teclado necesitan ver dónde están, así que nunca elimines el contorno sin un reemplazo visible. Ese hábito mantendrá tus páginas usables para todos.
JunoPseudo-clases Usa :focus-visible sobre bare :focus así el anillo se muestra para usuarios de teclado pero no en clic de mouse, y nunca envíes una interfaz que elimine el indicador de enfoque completamente. Mira la trampa de :nth-child versus :nth-of-type: una cuenta cada hermano, la otra solo hermanos del mismo tipo, y la elección equivocada miscuenta en el momento en que un elemento extraño llega a la lista. Es el tipo de bug que se ve bien hasta que no.

Pseudo-elementos: estilizar una parte de un elemento

Una pseudo-clase estiliza un elemento completo en un cierto estado. Un pseudo-elemento es diferente: te permite estilizar una parte de un elemento, o agregar un pequeño pedazo de contenido que ni siquiera está en tu HTML.

Los dos que encontrarás primero son ::before y ::after. Insertan contenido al inicio o final de un elemento, y les dices qué insertar con la propiedad content. Observa los dos puntos: ese doble punto es cómo CSS marca un pseudo-elemento aparte de una pseudo-clase.

Un pseudo-elemento estiliza una parte específica de un elemento en lugar de la cosa completa, y usa un doble punto (::) para distinguirlo de una pseudo-clase de un punto. Los más usados son ::before y ::after, que crean un hijo generado al inicio o final de un elemento. Requieren una propiedad content para renderizar, incluso si está vacía (content: ""), y ese contenido generado es decorativo: los lectores de pantalla pueden saltarlo, así que nunca pongas significado allí.

Otro común es ::placeholder, que estiliza el texto de pista tenue dentro de una entrada vacía. Porque los pseudo-elementos apuntan a un pedazo del elemento, te permiten construir pequeñas piezas visuales (un icono antes de una etiqueta, una línea decorativa después de un encabezado) sin agregar etiquetas extra a tu marcado.

Un pseudo-elemento expone una parte de un elemento que no tiene etiqueta propia, estilizada a través de una palabra clave de doble punto. ::before y ::after generan una caja en línea como el primer o último hijo del elemento; se renderizan solo cuando content está establecido (usa content: "" para una caja puramente decorativa), y ese contenido generado se sienta fuera del árbol de accesibilidad, así que debe permanecer decorativo y nunca llevar significado que un usuario necesita. ::placeholder apunta al texto de pista de una entrada, ::selection apunta al texto resaltado, ::first-line y ::first-letter apuntan a pedazos tipográficos.

La historia de punto único versus doble punto vale la pena conocer. Los pseudo-elementos modernos usan :: (dos puntos) para separarlos de las pseudo-clases, una distinción introducida en CSS3. Para los cuatro pseudo-elementos originales (:before, :after, :first-line, :first-letter), la forma de punto único aún se analiza por razones heredadas, pero escribe la forma de doble punto; el punto único es una reliquia de compatibilidad, no un estilo que deberías llevar a código nuevo. En producción, ::before y ::after hacen mucho trabajo silencioso: tooltips, marcadores de lista personalizados, clearfix en diseños más antiguos, y florituras decorativas que de otro modo enturbirían el HTML.

css
/* agregue una flecha decorativa después de cada enlace del nav */
.nav a::after {
  content: " →";
  color: rebeccapurple;
}

/* estilizar el texto de pista dentro de una caja de búsqueda */
.search-input::placeholder {
  color: grey;
  font-style: italic;
}

La regla ::after inserta una flecha que vive solo en la estilización, no en el HTML, mientras que ::placeholder llega dentro de la entrada para estilizar el texto que el navegador dibuja para ti. Ambos estilizan algo que de otro modo no podrías alcanzar con un selector simple. Si estás estilizando texto de pista y etiquetas, el capítulo de tipografía profundiza en el lado del texto.

JunoPseudo-elementos Un pseudo-elemento estiliza una parte de un elemento o agrega un pequeño contenido, y usa dos puntos, como ::before y ::after. Esos dos necesitan una propiedad content para mostrarse. El doble punto es la forma rápida de diferenciar un pseudo-elemento de una pseudo-clase.
JunoPseudo-elementos Los pseudo-elementos usan un doble punto y estilizan un pedazo de un elemento: ::before, ::after, ::placeholder. Los dos primeros necesitan un valor content, incluso un "" vacío, y ese contenido es decorativo, así que mantén significado real en tu HTML. Te ahorran agregar etiquetas solo para colgar una decoración.
JunoPseudo-elementos Escribe pseudo-elementos con dos puntos; la forma de punto único solo sobrevive para los cuatro originales como una rareza heredada. El contenido de ::before y ::after vive fuera del árbol de accesibilidad, así que permanece decorativo y nunca lleva nada que un usuario necesita leer. Manejan silenciosamente tooltips, marcadores personalizados, y florituras que de otro modo inflarían tu marcado.

Especificidad: qué regla gana

Tarde o temprano dos reglas intentarán estilizar el mismo elemento de diferentes maneras. Una dice que el texto es azul, otra dice que es rojo. El navegador tiene que elegir una, y la forma en que decide se llama especificidad: el selector más específico gana.

La versión corta es un orden de fortaleza. Un selector de id vence a un selector de clase, y un selector de clase vence a un selector de tipo. Así que si #header y .title ambos establecen un color en el mismo elemento, el id gana. Cuando dos reglas son igualmente específicas, la escrita después en el archivo gana.

Cuando varias reglas apuntan al mismo elemento y establecen la misma propiedad, el navegador resuelve el conflicto con especificidad, una puntuación basada en los tipos de selectores involucrados. El orden de más fuerte a más débil es: estilos en línea (un atributo style en el elemento), luego ids, luego clases (y pseudo-clases y selectores de atributo), luego selectores de tipo (y pseudo-elementos). Un selector más específico gana sin importar el orden de origen; solo cuando la especificidad se empata es que la regla posterior gana.

Esta es la razón práctica para apoyarse en clases y evitar ids para estilización. Si estilizas con ids, una regla de clase posterior no puede anularlos, así que terminas escalando. Y !important, que puedes agregar a una declaración para forzar que gane sobre todo, debe tratarse como un último recurso: rompe la cascada normal y la única forma de vencer un !important es otro !important, que es una espiral que no quieres comenzar.

La especificidad es el algoritmo que el navegador usa para elegir entre declaraciones conflictivas. Se calcula como un conteo de tres partes, a menudo escrito (a, b, c): a es el número de selectores de id, b es el número de selectores de clase, atributo, y pseudo-clase, y c es el número de selectores de tipo y pseudo-elemento. Las partes se comparan de izquierda a derecha, así que cualquier id vence a cualquier número de clases, y cualquier clase vence a cualquier número de selectores de tipo. #header .title a puntúa (1, 1, 1); .nav .menu .item a puntúa (0, 3, 1); el primero gana solo en el id. Los atributos style en línea se sientan sobre toda la especificidad del selector, e !important (una bandera que agregas a un valor para forzarlo) anula incluso eso; el selector universal * y los combinadores no agregan nada a la puntuación.

Dos herramientas modernas cambian cómo manejas esto. :is() toma una lista de selectores y coincide si cualquiera de ellos lo hace, pero toma la especificidad de su argumento más específico, así :is(#header, .title) hereda el peso del id. :where() hace la misma coincidencia pero tiene especificidad cero siempre, lo que la hace la herramienta para escribir estilos base de bajo peso que cualquier cosa puede anular. :has(), la pseudo-clase relacional, coincide con un elemento que contiene algo ("un .card que tiene una imagen", .card:has(img)) y toma la especificidad de su argumento. La práctica de mantenibilidad debajo de todo esto es mantener la especificidad baja y plana: prefiere selectores de clase única, evita ids e !important, y usa :where() para reducir a cero el peso donde quieres que los valores por defecto cedan. Especificidad plana significa que una regla posterior siempre puede ganar por ser posterior, que es la cascada funcionando para ti en lugar de contra ti.

css
/* selector de tipo, especificidad (0, 0, 1) */
p {
  color: grey;
}

/* selector de clase, especificidad (0, 1, 0): este gana */
.intro {
  color: black;
}
html
<p class="intro">Este párrafo es negro, porque la regla de clase supera la regla de tipo.</p>

Ambas reglas apuntan al párrafo y ambas establecen color, pero el selector de clase es más específico que el selector de tipo, así que el párrafo es negro. Mover un selector hacia arriba o hacia abajo en la escalera de especificidad a menudo es todo lo que se necesita para resolver un conflicto sin recurrir a herramientas más pesadas. El capítulo del modelo de caja se encuentra con la misma cascada cuando las reglas de espaciado chocan.

JunoEspecificidad Cuando dos reglas luchan por el mismo elemento, la más específica gana: un id vence a una clase, y una clase vence a un selector de tipo. Si son igualmente específicas, la que viene última en el archivo gana. Eso explica la mayoría de conflictos.
JunoEspecificidad La fortaleza corre en línea, luego id, luego clase, luego tipo, y una regla más específica gana sin importar dónde se siente en el archivo. Por eso estilizar con clases y evitar ids mantiene tu CSS anulable. Mantén !important como último recurso, porque lo único que vence a uno es otro.
JunoEspecificidad La especificidad cuenta como (a, b, c) para ids, clases, y tipos, comparado de izquierda a derecha, así que un id supera cualquier montón de clases. Mantenlo bajo y plano: clases únicas, sin ids para estilización, sin !important, e :where() para reducir a cero el peso cuando quieres que los valores por defecto cedan. Especificidad plana significa que una regla posterior siempre puede ganar por ser posterior, y esa es la cascada funcionando contigo.