Skip to content
Astro as scripts 4 min read

Global Styles

By default, Astro scopes every <style> tag to the component it lives in, which is great for avoiding leaks but unhelpful when you need site-wide rules like resets, typography defaults, or CSS custom properties. Global styles solve this: they apply to the whole document regardless of which component renders them. Astro gives you several ways to ship global CSS, including the is:global directive, the :global() selector, and plain CSS imports. This page explains each approach and how to structure your base and reset stylesheets so they stay maintainable as your project grows.

Scoped is the default

When you write a <style> block inside an .astro file, Astro adds a hashed data-astro-cid-* attribute to your markup and rewrites your selectors to match only that component. That isolation is exactly what you want for component-specific look-and-feel, but it means a rule like body { margin: 0 } written in one component will not style the real <body> element. To affect elements outside the current component, you must opt into global behavior.

Reach for global styles only for genuinely document-wide concerns: resets, design tokens, base typography, and element defaults. Keep everything else scoped to preserve encapsulation.

Using the is:global directive

The simplest way to make an entire style block global is to add the is:global directive to the <style> tag. Astro then leaves every selector inside untouched and injects the CSS into the page as-is.

---
// src/layouts/BaseLayout.astro
---
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <slot name="head" />
  </head>
  <body>
    <slot />
  </body>
</html>

<style is:global>
  :root {
    --color-bg: #ffffff;
    --color-text: #1a1a1a;
    --font-sans: system-ui, sans-serif;
  }

  body {
    margin: 0;
    font-family: var(--font-sans);
    background: var(--color-bg);
    color: var(--color-text);
  }
</style>

Because this lives in a layout that wraps every page, the body and :root rules now apply across the whole site with zero client-side JavaScript.

The :global() selector

Sometimes you want most of a style block scoped, with only one or two escape hatches. The :global() pseudo-selector lets you mark individual selectors as global inside an otherwise-scoped block.

---
// src/components/Prose.astro
---
<article class="prose">
  <slot />
</article>

<style>
  .prose {
    max-width: 65ch;
    margin-inline: auto;
  }

  /* Style slotted/markdown children that aren't part of this component's markup */
  .prose :global(h2) {
    margin-top: 2rem;
    font-weight: 700;
  }

  .prose :global(a) {
    color: var(--color-link);
    text-decoration: underline;
  }
</style>

Here .prose stays scoped to this component, but the nested :global(h2) and :global(a) rules will match slotted content such as rendered Markdown, which Astro would otherwise not touch.

Importing CSS files

For shared, reusable styles the cleanest approach is a plain .css file imported from the frontmatter of an .astro component (or from a script). Astro bundles, processes, and links the stylesheet automatically, and CSS imported this way is always global.

/* src/styles/global.css */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  line-height: 1.5;
}

img,
picture,
video {
  display: block;
  max-width: 100%;
}
---
// src/layouts/BaseLayout.astro
import "../styles/reset.css";
import "../styles/global.css";
---
<html lang="en">
  <body>
    <slot />
  </body>
</html>

Imports are resolved at build time, so the bundled CSS is fingerprinted and cacheable. You can also import CSS from the global src/styles directory using a path alias if you configure one in tsconfig.json.

Structuring base and reset stylesheets

A predictable structure keeps global CSS from sprawling. A common layering looks like this:

FilePurpose
reset.cssNormalize browser defaults (margins, box-sizing, media elements)
tokens.cssDefine design tokens as CSS custom properties on :root
base.cssElement defaults: body, headings, links, form controls
utilities.cssOptional small utility classes used across components

Import them in dependency order from a single layout so the cascade is deterministic:

---
import "../styles/reset.css";
import "../styles/tokens.css";
import "../styles/base.css";
import "../styles/utilities.css";
---

To guard against unpredictable ordering when files grow, lean on native CSS cascade layers:

/* src/styles/base.css */
@layer reset, tokens, base, utilities;

@layer base {
  h1 { font-size: clamp(2rem, 5vw, 3rem); }
  a:hover { text-decoration: none; }
}

Watch out for ordering: imported global CSS and is:global blocks land in the bundle in import/render order. Using @layer makes precedence explicit instead of depending on source position.

Best practices

  • Keep a single source of truth: import your reset, tokens, and base styles once in a top-level layout rather than scattering them across components.
  • Prefer imported .css files over large is:global blocks for anything reusable; they are easier to lint, share, and cache.
  • Reserve :global() for the narrow cases (slotted content, rendered Markdown) where scoping genuinely gets in your way.
  • Define design tokens as CSS custom properties on :root so scoped components can consume them without re-importing styles.
  • Use @layer to make cascade precedence explicit and avoid import-order surprises.
  • Remember global styles ship as CSS only, preserving Astro’s zero-JS-by-default model for styling.
Last updated June 14, 2026
Was this helpful?