Saltar al contenido

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.

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.

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.

ASTRO
<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:

ASTRO
<style is:global>  @import "open-props/style";  @font-face {    font-family: 'Inter';    src: url('/fuentes/inter.woff2') format('woff2');  }</style>

: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.

ASTRO
---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/.

ASTRO
---// Layout.astroimport '../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.

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:

ASTRO
<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.