Styling in Astro

Chapter 6
Styling in Astro
Scoped by default, is:global to opt out, define:vars, and Sass support

A <style> block inside an .astro component is scoped to that component automatically — no explicit keyword needed, matching Svelte's own zero-config default rather than Vue's opt-in scoped attribute.

Scoped by Default

// src/components/Card.astro <div class="card">Card content</div> <style> .card { background: #161b22; padding: 1rem; } </style>

Astro adds a unique attribute to this component's own elements at build time, so a different component's own .card rule never collides with this one — the identical technique, and the identical zero-runtime-cost result, Svelte's own scoped styles already used.

Opting Out: is:global

<style is:global> h1 { color: #FF5D01; } </style>

is:global on a specific <style> block disables scoping for just that block, letting its rules apply site-wide from wherever the component happens to be used.

A Real Site-Wide Stylesheet

// src/layouts/BaseLayout.astro --- import '../styles/global.css'; ---

For genuinely site-wide styling — this project's own established dark theme, for instance — a plain imported .css file in a shared layout's frontmatter is the natural choice, applying unscoped to the whole page.

define:vars: Frontmatter Values Inside CSS

--- const accentColor = '#FF5D01'; --- <h2>Styled Heading</h2> <style define:vars={{ accentColor }}> h2 { color: var(--accentColor); } </style>
A genuine Astro-specific convenience
define:vars binds a frontmatter value directly into a real CSS custom property, usable anywhere inside that component's own scoped <style> block — no inline style attribute and no separate CSS-in-JS library required to bridge computed values into CSS.

Sass Support, Out of the Box

npm install sass
<style lang="scss"> $accent: #FF5D01; .card { border: 1px solid $accent; } </style>

Installing the sass package is the only setup required — no bundler configuration to write. lang="scss" on any <style> block enables real Sass syntax, still scoped by default the same as plain CSS.

Scoped Styles, Compared

FrameworkScoped by Default?
ReactNo — needs CSS Modules or a separate styling library
VueOpt-in via <style scoped>
SvelteYes — automatic, zero configuration
AstroYes — automatic, zero configuration, same mechanism as Svelte

Coding Challenges

Challenge 1

Build two components that each style an h3 tag differently in their own scoped <style> block, and confirm both render with their own distinct styling when placed on the same page.

📄 View solution
Challenge 2

Use define:vars to bind a frontmatter color variable into a CSS custom property, applied inside that component's own scoped style block.

📄 View solution
Challenge 3

Add a global.css stylesheet imported from a shared layout applying a site-wide dark background, and confirm a component's own scoped styles still layer correctly on top of it.

📄 View solution

Chapter 6 Quick Reference

  • Scoped by default<style> is automatically scoped, same mechanism as Svelte, unlike Vue's opt-in scoped
  • is:global — opts a specific style block out of scoping
  • Imported global stylesheet — the right tool for genuinely site-wide styling
  • define:vars — binds a frontmatter value into a real CSS custom property, no CSS-in-JS library needed
  • Sassnpm install sass plus lang="scss", no bundler config required
  • Next chapter: Islands Architecture In Depth