0Apoyar

Selectores de Custom Elements y Shadow DOM

Explora qué selecciona una regla dentro del shadow root y qué puede estilizar la página desde fuera.

Leer la guía

Preguntar a la IA

Selectores de Custom Elements y Shadow DOM

Respuesta generada con openai/gpt-5.4-nano. La IA puede cometer errores. Revisa siempre el resultado.
Contenido asignado
Soporte
No verificable en este navegador
Estilo observado
No se puede verificar
Hoja de estilo
Dentro del shadow root
Sujeto
Host del custom element
Especificidad
(0, 2, 0)
HTML
<baseline-shadow-card>
  <span slot="content" class="item">Contenido asignado</span>
</baseline-shadow-card>
CSS
:host { display: block; }

:host(:state(active)) {
  --baseline-shadow-host-state: applied;
  outline: 3px solid var(--brand-orange);
  background: color-mix(in oklch, var(--brand-orange) 16%, transparent);
}
JS
if (!customElements.get('baseline-shadow-card')) {
  customElements.define('baseline-shadow-card', class extends HTMLElement {
    #internals = this.attachInternals();

    constructor() {
      super();
      const shadow = this.attachShadow({ mode: 'open' });
      const style = document.createElement('style');
      style.textContent = ":host { display: block; }\n\n:host(:state(active)) {\n  --baseline-shadow-host-state: applied;\n  outline: 3px solid var(--brand-orange);\n  background: color-mix(in oklch, var(--brand-orange) 16%, transparent);\n}";
      shadow.append(style);
      const panel = document.createElement('div');
      panel.setAttribute('part', 'panel');
      panel.textContent = "Panel interno";
      const slot = document.createElement('slot');
      slot.name = 'content';
      panel.append(slot);
      shadow.append(panel);
    }

    setActive(active) {
      if (active) this.#internals.states.add('active');
      else this.#internals.states.delete('active');
    }
  });
}

document.querySelectorAll('baseline-shadow-card').forEach((card) => {
  card.setActive(true);
});

Guía educativa de selectores de custom elements y Shadow DOM

Dónde puede actuar cada selector

Compara la hoja que contiene la regla, el sujeto que recibe el estilo y el límite de encapsulación que atraviesa cada forma.

Volver al playground

Estado y host

El custom element es el host de su shadow root. Una regla interna usa :host o :host-context(); una regla de la página puede usar el nombre del elemento y :state(active).

Host

:host

Selecciona el host desde una hoja dentro de su shadow root.

Especificidad(0, 1, 0)
Comportamiento
  • La regla interna puede estilizar el propio custom element.
  • No exige clase, atributo ni estado propio en el host.
Ejemplos

La regla :host se coloca dentro del shadow root; el HTML muestra el host en la página.

Selector CSS · Dentro del shadow rootEspecificidad(0, 1, 0)
:host
HTML de la página
<baseline-shadow-card>
</baseline-shadow-card>

Recibe el estilo: Host del custom element

Errores frecuentes
  • Escribir :host en una hoja de la página no equivale a una regla interna.
  • El host no es un descendiente ordinario del shadow root para cualquier selector.
Consultar la definición completaAbre en una pestaña nueva

Clase del host

:host(.selected)

Selecciona el host si tiene la clase selected.

Especificidad(0, 2, 0)
Comportamiento
  • El argumento de :host() filtra el propio host.
  • La clase es visible en HTML y distinta del estado guardado en ElementInternals.states.
Ejemplos

El HTML incluye class="selected" en el host; la regla se coloca dentro del shadow root.

Selector CSS · Dentro del shadow rootEspecificidad(0, 2, 0)
:host(.selected)
HTML de la página
<baseline-shadow-card class="selected">
</baseline-shadow-card>

Recibe el estilo: Host del custom element

Errores frecuentes
  • Confundir .selected con :state(active): son condiciones independientes.
  • Aplicar la clase a un hijo interno no satisface :host(.selected).
Consultar la definición completaAbre en una pestaña nueva

Estado propio del host

:host(:state(active))

Selecciona el host con el estado propio active desde su hoja interna.

Especificidad(0, 2, 0)
Comportamiento
  • El componente activa el estado mediante ElementInternals.states.add("active").
  • El estado no se serializa como clase o atributo del HTML mostrado.
Ejemplos

El HTML por sí solo no expresa el estado: el JS generado llama setActive(true) antes de comprobar la regla interna.

Selector CSS · Dentro del shadow rootEspecificidad(0, 2, 0)
:host(:state(active))
HTML de la página
<baseline-shadow-card>
</baseline-shadow-card>

Recibe el estilo: Host del custom element

Errores frecuentes
  • Buscar un atributo active en el HTML para explicar :state(active).
  • Tratar la aceptación de la sintaxis como prueba de que la regla aplicó estilo.
Soporte y compatibilidad

Consulta el estado Baseline de :state() y abre su enlace para ver el detalle por navegador. El playground comprueba si esta regla interna aplica estilo aquí.

Consultar la definición completaAbre en una pestaña nueva

Estado desde la página

baseline-shadow-card:state(active)

Selecciona desde la página el custom element con estado active.

Especificidad(0, 1, 1)
Comportamiento
  • La regla externa alcanza el host, sin entrar en su shadow root.
  • Observa el mismo CustomStateSet que usa la forma interna :host(:state(active)).
Ejemplos

El JS generado activa el estado del host; esta regla CSS pertenece a la página.

Selector CSS · En la páginaEspecificidad(0, 1, 1)
baseline-shadow-card:state(active)
HTML de la página
<baseline-shadow-card>
</baseline-shadow-card>

Recibe el estilo: Host del custom element

Errores frecuentes
  • Creer que la regla de página puede seleccionar nodos internos por ser active el host.
  • Sustituir el estado propio por una clase y presentarlo como la misma prueba.
Soporte y compatibilidad

Esta forma usa :state() en la hoja de la página. La tarjeta muestra su estado Baseline; el playground comprueba el estilo en este navegador.

Consultar la definición completaAbre en una pestaña nueva

Contexto del host

:host-context(.theme-active)

Selecciona el host desde su hoja interna si él o un ancestro externo tiene la clase theme-active.

Especificidad(0, 2, 0)
Comportamiento
  • La condición mira el contexto que rodea al host, aunque la regla CSS vive dentro del shadow root.
  • El sujeto que recibe el estilo sigue siendo el host.
Ejemplos

El div exterior tiene theme-active; el CSS :host-context(.theme-active) se escribe dentro del shadow root.

Selector CSS · Dentro del shadow rootEspecificidad(0, 2, 0)
:host-context(.theme-active)
HTML de la página
<div class="theme-active">
  <baseline-shadow-card>
  </baseline-shadow-card>
</div>

Recibe el estilo: Host del custom element

Errores frecuentes
  • Escribir :host-context() en la hoja de la página.
  • Interpretar la coincidencia como permiso para seleccionar cualquier nodo interno desde fuera.
Soporte y compatibilidad

Su disponibilidad todavía es limitada. Abre el enlace de la tarjeta para ver el detalle por navegador y prueba el efecto de theme-active en el playground.

Consultar la definición completaAbre en una pestaña nueva

Slots y partes

Un slot proyecta hijos de la página; :has-slotted comprueba si recibió contenido. Una parte expone una superficie interna para que la página la estilice.

Elemento asignado

slot[name="content"]::slotted(.item)

Selecciona el elemento .item asignado directamente al slot content.

Especificidad(0, 2, 2)
Comportamiento
  • La regla se escribe dentro del shadow root, junto al slot.
  • El sujeto es un hijo de luz asignado; el segundo bloque HTML muestra el slot interno.
Ejemplos

El span pertenece al HTML de la página y se distribuye al slot content del shadow root.

Selector CSS · Dentro del shadow rootEspecificidad(0, 2, 2)
slot[name="content"]::slotted(.item)
HTML de la página
<baseline-shadow-card>
  <span slot="content" class="item">Contenido asignado</span>
</baseline-shadow-card>
HTML dentro del shadow root
<div part="panel">
  <slot name="content"></slot>
</div>

Recibe el estilo: Elemento asignado al slot

Errores frecuentes
  • Esperar que ::slotted(.item) alcance todos los descendientes del elemento asignado.
  • Contar coincidencias desde document.querySelectorAll() como si evaluara la regla interna.
Consultar la definición completaAbre en una pestaña nueva

Slot con contenido

slot[name="content"]:has-slotted

Selecciona el slot content cuando tiene nodos asignados.

Especificidad(0, 2, 1)
Comportamiento
  • La regla vive dentro del shadow root y estiliza el propio slot.
  • También pueden satisfacer la condición nodos de texto asignados; esta demo usa un span.
Ejemplos

El span de la página se asigna al slot interno; al quitarlo, el slot deja de cumplir :has-slotted.

Selector CSS · Dentro del shadow rootEspecificidad(0, 2, 1)
slot[name="content"]:has-slotted
HTML de la página
<baseline-shadow-card>
  <span slot="content" class="item">Contenido asignado</span>
</baseline-shadow-card>
HTML dentro del shadow root
<div part="panel">
  <slot name="content"></slot>
</div>

Recibe el estilo: Slot dentro del shadow root

Errores frecuentes
  • Confundir :has-slotted con ::slotted(.item): seleccionan sujetos distintos.
  • Suponer que la regla selecciona el span asignado en lugar del slot.
Soporte y compatibilidad

Su disponibilidad todavía es limitada. Abre el enlace de la tarjeta para ver el detalle por navegador y prueba el slot con y sin contenido asignado.

Consultar la definición completaAbre en una pestaña nueva

Parte expuesta

baseline-shadow-card::part(panel)

Estiliza desde la página el panel interno expuesto como part="panel".

Especificidad(0, 0, 2)
Comportamiento
  • El componente declara explícitamente qué elemento interno expone.
  • La regla de página se aplica a esa parte, sin abrir el resto de su árbol interno.
Ejemplos

El panel con part="panel" vive dentro del shadow root; la regla ::part(panel) vive en la página.

Selector CSS · En la páginaEspecificidad(0, 0, 2)
baseline-shadow-card::part(panel)
HTML de la página
<baseline-shadow-card>
</baseline-shadow-card>
HTML dentro del shadow root
<div part="panel">
  <slot name="content"></slot>
</div>

Recibe el estilo: Panel interno con part="panel"

Errores frecuentes
  • Suponer que ::part(panel) deja seleccionar descendientes internos arbitrarios.
  • Esperar que una parte anidada atraviese varios componentes sin exportparts.
Consultar la definición completaAbre en una pestaña nueva

Límites y soporte

El resultado comprueba que la regla se acepta, que funciona en una prueba positiva independiente y que su propiedad testigo aparece en el sujeto actual. No es un recuento de querySelectorAll().

exportparts

exportparts sirve para reenviar una parte a través de otro componente anidado. Este escenario tiene un solo shadow root y no lo necesita.

querySelectorAll()

Una consulta desde la página no atraviesa el shadow root. ::slotted() y ::part() representan estilos en un contexto CSS; sus consultas DOM no ofrecen un recuento comparable.