One of Astro’s strongest points is how it manages CSS Scoping, the cascade, and specificity.
Astro implements Scoped CSS by default, while also giving us tools to break that scope when necessary.
CSS Scoped styling means that a component’s CSS only applies to that component itself and not to the entire website.
Styling management in Astro is native and requires no configuration. You don’t need to install preprocessors or complex CSS-in-JS libraries to get started.
Scoped CSS
Imagine you have two components: Hero.astro and Footer.astro. Both have an <h1> inside.
In traditional development, if you do this in your CSS:
h1 { color: red; }
All the titles on your website will turn red. In Astro, this doesn’t happen.
When you write a <style> tag inside a .astro component, those styles only apply to that component.
<h1>Hello, I'm the Hero</h1>
<style>
/* This ONLY affects the h1 inside Hero.astro */
h1 {
color: blue;
font-size: 3rem;
}
</style>
You can have another Footer.astro component with another h1 and a different color, and they will never conflict.
Global Styles
Sometimes we want styles to be global. For example, for a CSS reset, to define base fonts, or for utility styles.
In Astro, we have two ways to do this.
If you add the is:global attribute to the style tag, Astro will stop processing those selectors with the scoped hash. It will behave like “normal” CSS.
<style is:global>
/* This will affect ALL h1 elements in the application */
h1 {
font-family: 'Comic Sans MS', sans-serif; /* Please, don't do this */
}
body {
background-color: #f0f0f0;
}
</style>
Use is:global with caution. If you overuse it, you lose the advantages of the islands architecture and go back to the maintenance problems of traditional CSS.
Sometimes you need a mix: you want most styles to be local, but you need to access a child element you don’t control (for example, HTML injected from a CMS or Markdown).
For that, we use the :global() pseudo-selector.
<style>
/* Local style for the container */
.articulo-blog {
padding: 20px;
}
/* Global style ONLY for children of .articulo-blog */
.articulo-blog :global(p) {
font-size: 1.2rem;
line-height: 1.6;
}
</style>
This is extremely useful when styling content that comes from a <slot />.
Importing external CSS files
You are not forced to write CSS inside the .astro file. If you prefer to have your styles in separate files (or use a library), you can import them.
---
// Direct import in the frontmatter
import '../styles/mi-tema.css';
import 'bootstrap/dist/css/bootstrap.min.css';
---
Astro will detect these imports, process them (minification, bundling) and inject them into the page automatically.
CSS Variables and define:vars
We can pass JavaScript variables (from the frontmatter) directly to our CSS. Astro takes care of creating the necessary Custom Properties (CSS variables).
This is one of Astro’s most powerful and least known features.
Imagine a component that accepts a background color as a Prop:
---
interface Props {
colorFondo: string;
imagenUrl: string;
}
const { colorFondo, imagenUrl } = Astro.props;
---
<div class="tarjeta">
<h2>Dynamic Card</h2>
</div>
<style define:vars={{ colorFondo, url: `url(${imagenUrl})` }}>
.tarjeta {
background-color: var(--colorFondo);
background-image: var(--url);
padding: 20px;
border-radius: 8px;
}
</style>
The define:vars={{ variable }} directive does the following:
- Takes the JS value.
- Injects it as an inline style on the component’s parent element (
style="--colorFondo: #ff0000; ..."). - Makes it available inside the
<style>block using the standardvar(--variable)syntax.
This eliminates the need for CSS-in-JS libraries (like Styled Components or Emotion) for 90% of dynamic styling use cases.
