wandres.dev
ESTILOS · scoped, global, CSS

Estilos globales y la ruptura del ámbito

Las tres puertas deliberadas al ámbito global en Astro: el bloque is:global para desactivar el scope, la función :global para excepciones quirúrgicas dentro de un bloque scoped, y la importación de un global.css desde un layout, con las implicaciones de orden y cascada que conviene dominar.

⏱ 15 min

El scoping por defecto es una bendición para los componentes, pero no todo estilo pertenece a un componente. Un reinicio de estilos, una tipografía base, un conjunto de tokens de diseño o el retoque de un widget de terceros son, por naturaleza, globales. Astro no te obliga a elegir entre aislamiento o alcance total: te da tres puertas explícitas al ámbito global, cada una con su radio de acción, para que rompas el scope justo donde lo necesitas y en ninguna otra parte.

🎯 Al terminar esta lección sabrás
  • Reconocer qué familias de estilo pertenecen legítimamente al ámbito global.
  • Desactivar el scoping de un bloque completo con <style is:global>.
  • Abrir excepciones quirúrgicas dentro de un bloque scoped con la función :global(...).
  • Importar un global.css desde un layout y razonar el orden de cascada resultante.

Qué estilos piden ser globales

Antes de romper el ámbito conviene saber cuándo hacerlo. La regla práctica es simple: lo global es la capa base, aquello que sostiene a todos los componentes sin pertenecer a ninguno. Cuatro familias caen ahí de forma natural, y reconocerlas evita el impulso de globalizar por comodidad lo que debería seguir acotado.

♻️

Reinicios y normalización

El box-sizing, los márgenes por defecto del navegador, la normalización tipográfica. Afectan a todo el documento por definición.

🎨

Tokens de diseño

Las variables CSS en :root —colores, espaciados, tipografías— que los componentes consumen. Son el vocabulario compartido.

📰

Contenido inyectado

El HTML que llega por set:html o desde Markdown no lleva marca de ámbito; estilarlo exige salir del scope de forma controlada.

Todo lo demás —la apariencia concreta de una tarjeta, un botón, una cabecera— debería seguir viviendo en el <style> scoped de su componente. La frontera es clara: global para lo que es de todos, scoped para lo que es de uno. Cada vez que dudes, pregúntate si el estilo tiene un dueño único; si lo tiene, no es global.

Hay una cuarta familia que conviene nombrar aparte: los estilos de un widget o librería de terceros que renderiza su propio marcado. Ese HTML no nace de tus componentes, así que no lleva marca de ámbito y el scoping no lo alcanza; ajustarlo obliga, por fuerza, a operar en el terreno global. Reconocer de antemano estos casos evita la frustración de escribir reglas scoped que, misteriosamente, no surten efecto.

is:global: apagar el scope de un bloque

La primera puerta es la más amplia. Al añadir la directiva is:global a un bloque <style>, le dices a Astro que no lo acote: sus reglas se emiten tal cual, sin la marca de ámbito, y aplican a todo el documento. Es el lugar idóneo para el reinicio y los tokens, casi siempre en un layout raíz que envuelve todas las páginas.

<style is:global>
  :root { --color-acento: #7c3aed; --espacio: 1rem; }
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; font-family: system-ui, sans-serif; }
</style>

Nada de lo que escribas aquí queda confinado, así que la responsabilidad vuelve a tus manos: un is:global es CSS global de toda la vida, con su poder y sus riesgos. Úsalo para la capa base y resiste la tentación de meter en él estilos de componente que el scoping resolvería sin colisiones.

Ese bloque es también el lugar natural para traer CSS externo con una regla @import o para declarar una fuente con @font-face. Todo lo que escribas dentro se emite sin acotar y aplica a todo el documento:

<style is:global>
  @import "open-props/style";
  @font-face {
    font-family: 'Inter';
    src: url('/fuentes/inter.woff2') format('woff2');
  }
</style>
⚠️
is:global reabre la puerta a las colisiones

En el instante en que un bloque deja de estar acotado, recuperas todos los problemas que el scoping te ahorraba: nombres que chocan, especificidad que se descontrola, cambios de alcance impredecible. Reserva is:global para lo que de verdad es transversal y mantén los selectores lo más genéricos posible. Un is:global lleno de clases específicas de componentes es una señal de que algo debería haberse quedado scoped.

:global(): la excepción quirúrgica

La segunda puerta es un bisturí, no un hacha. Dentro de un bloque scoped normal puedes marcar selectores concretos como globales envolviéndolos en la función :global(...). El resto del bloque sigue acotado; solo la parte envuelta escapa. Es la herramienta perfecta para estilar, desde el propio componente, el HTML que este inyecta y que —lo vimos— no lleva la marca de ámbito.

---
const { html } = Astro.props;
---
<div class="prosa" set:html={html} />

<style>
  .prosa { max-width: 65ch; line-height: 1.7; }
  /* alcanza el contenido inyectado, que no lleva marca de ambito */
  .prosa :global(a) { color: var(--color-acento); }
  .prosa :global(h2) { margin-top: 2rem; }
</style>

Fíjate en el patrón: .prosa permanece scoped y ancla la excepción a su subárbol, mientras :global(a) alcanza los enlaces que Astro no habría marcado. Así consigues alcance global localizado: la regla sale del ámbito, pero solo bajo un contenedor que tú controlas, sin contaminar el resto del documento. Es la síntesis entre las dos necesidades: la potencia de lo global y la contención de lo scoped.

El mismo patrón resuelve otro caso frecuente: estilar el marcado de un componente hijo o de un widget de terceros desde el componente padre. En vez de globalizar la regla entera, la anclas bajo un contenedor propio y envuelves en :global(...) solo el selector que debe cruzar la frontera. La excepción queda contenida por el contexto y nunca escapa más allá de donde tú la sitúas, que es exactamente la garantía que hace que :global() sea seguro de usar.

Importar un global.css y el orden de cascada

La tercera puerta separa los estilos del componente. Escribes tu CSS global en un fichero aparte y lo importas desde el frontmatter, normalmente en un layout para que se cargue una vez y valga para todo el sitio. También puedes importar la hoja de un paquete de npm de la misma manera, o enlazar con <link> un fichero servido desde public/.

---
// Layout.astro
import '../styles/global.css';
import 'una-libreria/dist/estilos.css';
---
<html lang="es">
  <head><slot name="head" /></head>
  <body><slot /></body>
</html>

Queda la pregunta del orden. ¿Gana el global importado o el scoped del componente? Astro está diseñado para que, ante especificidad de autor equivalente, prevalezca el estilo scoped del componente sobre el global importado; a ello contribuye el peldaño de especificidad que estudiamos en la lección anterior. La consecuencia práctica es tranquilizadora: los globales ponen la base y los componentes la refinan sin que tengas que pelear la cascada. Por eso conviene que tu global.css sea genérico —selectores de elemento, tokens, baja especificidad— y deje las decisiones finas a cada componente.

flowchart TD
Q[estilo que quiero aplicar] --> UNO[pertenece a un componente]
UNO --> SCOPED[bloque style scoped por defecto]
Q --> BASE[reset token o tipografia base]
BASE --> GLOBAL[global.css importado o is global]
Q --> EXC[excepcion dentro de un scope]
EXC --> LOCAL[envolver el selector en global]
style Q fill:#89b4fa,color:#11111b
style SCOPED fill:#a6e3a1,color:#11111b
style GLOBAL fill:#a6e3a1,color:#11111b

No todo estilo global tiene que pasar por el bundler. Un fichero servido desde public/ puede enlazarse con una etiqueta <link> en la cabecera del layout, útil para hojas que prefieres cargar tal cual, sin procesar:

<head>
  <link rel="stylesheet" href="/estilos/impresion.css" media="print" />
  <slot name="head" />
</head>

La diferencia práctica es de pipeline: lo importado en el frontmatter entra en el proceso de build —se agrupa, se optimiza, se versiona con un hash de caché—, mientras que lo enlazado con <link> se sirve como un activo estático más, sin tocar. Elige lo primero para el CSS propio del proyecto y lo segundo para hojas externas que no quieras que Astro transforme.

📝
El @import de CSS va siempre arriba

Si usas la regla @import de CSS dentro de un bloque global, recuerda que la especificación exige que aparezca antes que cualquier otra regla del bloque; colocarla después hace que el navegador la ignore en silencio. Es un tropiezo clásico que no tiene que ver con Astro, pero que aflora en cuanto empiezas a componer tu capa global a mano.

💡
Un solo punto de entrada para lo global

Centraliza la importación del global.css en tu layout raíz, no en cada página. Así garantizas un único punto de verdad, evitas cargar la hoja varias veces y controlas el orden desde un solo sitio. Cuando un estilo global se cuela repartido por media docena de componentes, el orden de cascada se vuelve un misterio; concentrarlo lo convierte en algo que puedes leer de un vistazo.

Lo global no es lo contrario del scope: es su complemento deliberado

Es tentador leer los estilos globales de Astro como una vía de escape, como si el framework, incapaz de sostener siempre su promesa de aislamiento, ofreciera una puerta trasera para los casos difíciles. Esa lectura yerra el sentido del diseño. El scope y lo global no son adversarios sino las dos mitades de una misma arquitectura de estilos, y Astro las expone con una gradación que es, en sí misma, una tesis sobre cómo debe organizarse el CSS. is:global desactiva el ámbito para un bloque entero: la herramienta de la capa base, donde el alcance total es la intención, no el accidente. La importación de un global.css lleva esa misma idea a su lugar natural, un fichero separado y un único punto de entrada, dejando claro que lo transversal vive fuera de los componentes. Y :global() completa el espectro por el otro extremo: no globaliza un bloque, sino un selector, y siempre anclado a un contenedor scoped, de modo que la excepción nunca se vuelve regla. Leídas juntas, las tres puertas dibujan una jerarquía de intención: por defecto, todo está contenido; para romperlo, tienes que decirlo, y cuanto más amplio es el permiso que pides, más explícito ha de ser el gesto. Esa es la marca de un buen sistema de estilos: no que impida lo global, sino que lo haga costar exactamente lo justo, para que el aislamiento siga siendo el camino de menor resistencia y la ruptura, una decisión consciente y localizada.

⚔️ Construye tu capa global
  1. Crea un src/styles/global.css con un reinicio, unos tokens en :root y una tipografía base; impórtalo desde tu layout raíz.
  2. Convierte un bloque de tokens en <style is:global> dentro del layout y compara cuándo prefieres el fichero externo y cuándo el bloque en línea.
  3. Renderiza contenido con set:html y estílalo desde el componente usando :global(...) bajo un contenedor con clase propia.
  4. Provoca a propósito un choque entre una regla global genérica y una scoped equivalente, e identifica cuál gana y por qué.