Skip to main content

Overview

Astro provides multiple approaches to styling your components and pages. Choose the method that best fits your project needs and team preferences.

Scoped Styles

Astro components support scoped CSS out of the box. Styles defined in a <style> tag are automatically scoped to that component.

Basic Scoped Styles

src/components/Button.astro
<button class="primary">Click me</button>

<style>
  .primary {
    background: #2337ff;
    color: white;
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: 0.375rem;
    font-weight: 600;
    cursor: pointer;
  }

  .primary:hover {
    background: #000d8a;
  }
</style>
The .primary class is scoped to this component and won’t affect other components with the same class name.

How Scoping Works

Astro adds unique data attributes to your elements and styles:
<!-- Output -->
<button class="primary" data-astro-cid-abc123>Click me</button>

<style>
  .primary[data-astro-cid-abc123] {
    background: #2337ff;
    /* ... */
  }
</style>

Global Styles

Use global styles for site-wide CSS like resets, typography, and design tokens.

Creating Global Styles

1

Create a global CSS file

src/styles/global.css
:root {
  --accent: #2337ff;
  --accent-dark: #000d8a;
  --black: 15, 18, 25;
  --gray: 96, 115, 159;
}

body {
  font-family: system-ui, sans-serif;
  margin: 0;
  padding: 0;
  color: rgb(var(--black));
  line-height: 1.7;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 0.5rem 0;
  line-height: 1.2;
}
2

Import in a layout or component

src/components/BaseHead.astro
---
import '../styles/global.css';
---

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />

Global Modifier

Use :global() to create unscoped styles within a scoped style block:
src/layouts/BlogPost.astro
<article class="prose">
  <slot />
</article>

<style>
  .prose {
    max-width: 720px;
    margin: 0 auto;
  }

  /* Style all links within .prose globally */
  .prose :global(a) {
    color: var(--accent);
    text-decoration: none;
  }

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

CSS Variables

Define CSS custom properties for consistent theming:
src/layouts/Layout.astro
---
const { theme = 'light' } = Astro.props;
---

<div class={`theme-${theme}`}>
  <slot />
</div>

<style>
  .theme-light {
    --bg-color: #ffffff;
    --text-color: #1a1a1a;
    --accent: #2337ff;
  }

  .theme-dark {
    --bg-color: #1a1a1a;
    --text-color: #ffffff;
    --accent: #4d7fff;
  }

  .theme-light,
  .theme-dark {
    background: var(--bg-color);
    color: var(--text-color);
  }
</style>

CSS Preprocessors

Astro supports CSS preprocessors like Sass and Less out of the box.
Install Sass:
npm install sass
Use in your components:
src/components/Card.astro
<div class="card">
  <slot />
</div>

<style lang="scss">
  $border-radius: 0.5rem;
  $shadow-color: rgba(0, 0, 0, 0.1);

  .card {
    border-radius: $border-radius;
    box-shadow: 0 2px 8px $shadow-color;
    padding: 1.5rem;

    &:hover {
      box-shadow: 0 4px 16px $shadow-color;
    }
  }
</style>
---
import '../styles/components.scss';
---
src/styles/components.scss
@mixin button-variant($bg, $color) {
  background: $bg;
  color: $color;
  padding: 0.75rem 1.5rem;
  border-radius: 0.375rem;
}

.btn-primary {
  @include button-variant(#2337ff, white);
}

.btn-secondary {
  @include button-variant(#6c757d, white);
}

Tailwind CSS

Tailwind CSS is a popular utility-first CSS framework that works seamlessly with Astro.

Installation

npm install @tailwindcss/vite

Configuration

astro.config.mjs
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  vite: {
    plugins: [tailwindcss()],
  },
});

Usage

src/components/Hero.astro
<section class="bg-gradient-to-r from-blue-500 to-purple-600 text-white py-20">
  <div class="container mx-auto px-4">
    <h1 class="text-5xl font-bold mb-4">Welcome to My Site</h1>
    <p class="text-xl mb-8">Build faster with Astro and Tailwind CSS</p>
    <button class="bg-white text-blue-600 px-6 py-3 rounded-lg font-semibold hover:bg-gray-100 transition">
      Get Started
    </button>
  </div>
</section>

CSS Modules

CSS Modules provide scoped styles with explicit imports.
src/components/Card.module.css
.card {
  background: white;
  border-radius: 0.5rem;
  padding: 1.5rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.title {
  font-size: 1.5rem;
  font-weight: bold;
  margin-bottom: 0.5rem;
}
src/components/Card.astro
---
import styles from './Card.module.css';
---

<div class={styles.card}>
  <h2 class={styles.title}>
    <slot name="title" />
  </h2>
  <div>
    <slot />
  </div>
</div>

Styled Components (CSS-in-JS)

Use CSS-in-JS libraries with framework components.
npm install styled-components
npm install @astrojs/react
src/components/StyledButton.tsx
import styled from 'styled-components';

const Button = styled.button`
  background: #2337ff;
  color: white;
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 0.375rem;
  font-weight: 600;
  cursor: pointer;

  &:hover {
    background: #000d8a;
  }
`;

export default function StyledButton({ children }) {
  return <Button>{children}</Button>;
}
src/pages/index.astro
---
import StyledButton from '../components/StyledButton';
---

<StyledButton client:load>
  Click me
</StyledButton>

PostCSS

Astro includes PostCSS support for autoprefixing and other transformations.
postcss.config.cjs
module.exports = {
  plugins: [
    require('autoprefixer'),
    require('postcss-nested'),
  ],
};

Styling Strategies

Component-First

Use scoped styles for component-specific styling. Best for isolated, reusable components.

Utility-First

Use Tailwind CSS for rapid development with utility classes. Best for prototyping and consistent design systems.

Semantic CSS

Use global CSS or preprocessors for traditional semantic class names. Best for content-heavy sites.

Hybrid Approach

Combine multiple methods: global styles for resets, scoped styles for components, and utilities for layout.

Advanced Patterns

Conditional Styles

src/components/Alert.astro
---
const { variant = 'info' } = Astro.props;
---

<div class={`alert alert-${variant}`}>
  <slot />
</div>

<style>
  .alert {
    padding: 1rem;
    border-radius: 0.375rem;
    border: 1px solid;
  }

  .alert-info {
    background: #e3f2fd;
    border-color: #2196f3;
    color: #0d47a1;
  }

  .alert-success {
    background: #e8f5e9;
    border-color: #4caf50;
    color: #1b5e20;
  }

  .alert-error {
    background: #ffebee;
    border-color: #f44336;
    color: #b71c1c;
  }
</style>

Media Queries

src/components/Grid.astro
<div class="grid">
  <slot />
</div>

<style>
  .grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr;
  }

  @media (min-width: 640px) {
    .grid {
      grid-template-columns: repeat(2, 1fr);
    }
  }

  @media (min-width: 1024px) {
    .grid {
      grid-template-columns: repeat(3, 1fr);
    }
  }
</style>

Font Loading

src/components/BaseHead.astro
<head>
  <!-- Preload fonts for better performance -->
  <link 
    rel="preload" 
    href="/fonts/inter-var.woff2" 
    as="font" 
    type="font/woff2" 
    crossorigin 
  />
</head>

<style is:global>
  @font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-var.woff2') format('woff2');
    font-weight: 100 900;
    font-display: swap;
  }

  body {
    font-family: 'Inter', system-ui, sans-serif;
  }
</style>

Best Practices

1

Prefer Scoped Styles

Use scoped styles by default for component isolation and avoiding naming conflicts.
2

Use CSS Variables for Theming

Define design tokens as CSS custom properties for consistent, maintainable themes.
3

Minimize Global Styles

Keep global CSS limited to resets, typography, and truly global styles.
4

Optimize for Performance

Preload critical fonts, use font-display: swap, and minimize unused CSS.

Build docs developers (and LLMs) love