Adding a Theme to Your Astro Project

When building an Astro project, you have two distinct theming needs: starting with a prebuilt Astro starter template that provides structure and design, or implementing runtime UI theming like dark and light mode switching. This guide covers both approaches, from Astro theme installation to advanced customization techniques.
Key Takeaways
- Install Astro themes quickly using the CLI with prebuilt templates from the official catalog
- Implement runtime theme switching using CSS custom properties and minimal JavaScript
- Prevent flash of unstyled content with inline scripts that check user preferences
- Follow accessibility best practices including WCAG contrast ratios and ARIA labels
Installing Astro Starter Templates
The quickest way to add a theme to your Astro project is using prebuilt templates from the Astro Themes Catalog. These Astro themes range from blog layouts to documentation sites, each offering different features and design approaches.
Using the Astro CLI
The recommended method for adding a theme to Astro is through the CLI:
npm create astro@latest -- --template theme-name
# or for a specific GitHub template:
npm create astro@latest -- --template username/repo-name
This command scaffolds a complete project with the theme’s structure, components, and configurations. Popular options include:
- Blog themes with built-in Astro dark mode support
- Documentation templates with sidebar navigation
- Portfolio themes with project galleries
- Landing page templates optimized for conversions
Forking and Customizing
For more control over Astro theme customization, fork the theme repository directly:
git clone https://github.com/your-fork/theme-name.git
cd theme-name
npm install
npm run dev
This approach makes updating and tracking changes easier, especially when you need to maintain custom modifications alongside theme updates.
Understanding Theme Structure
Most Astro starter templates follow a consistent structure:
src/
├── components/ # Reusable UI components
├── layouts/ # Page templates
├── pages/ # Route files
├── styles/ # Global and component styles
└── config.ts # Theme configuration
Key files for customization include:
astro.config.mjs
- Core Astro settings and integrationssrc/styles/global.css
- Global styles and CSS custom propertiessrc/layouts/BaseLayout.astro
- Main layout wrapper
Discover how at OpenReplay.com.
Implementing Runtime UI Theming
Beyond starter templates, Astro UI theming often means adding dark/light mode switching. Here’s a robust implementation using CSS custom properties:
Setting Up Theme Variables
First, define your color schemes using CSS custom properties:
/* src/styles/global.css */
:root {
color-scheme: light dark;
/* Light mode colors */
--color-bg: #ffffff;
--color-text: #1a1a1a;
--color-primary: #0066cc;
}
[data-theme="dark"] {
--color-bg: #1a1a1a;
--color-text: #e8e8e8;
--color-primary: #4da3ff;
}
The color-scheme
property helps browsers render form controls and scrollbars correctly for each theme.
Preventing Flash of Unstyled Content
Add this inline script to your document head to prevent FOUC:
<!-- src/layouts/BaseLayout.astro -->
<script is:inline>
const theme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
</script>
Creating a Theme Toggle
Build a toggle component for Astro light mode and Astro dark mode switching:
<!-- src/components/ThemeToggle.astro -->
<button id="theme-toggle" aria-label="Toggle theme">
<span class="sun">☀️</span>
<span class="moon">🌙</span>
</button>
<script>
const toggle = document.getElementById('theme-toggle');
toggle?.addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
});
</script>
Preserving Theme Across View Transitions
When using Astro’s View Transitions, preserve theme state with:
document.addEventListener('astro:after-swap', () => {
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
});
Best Practices for Theme Implementation
Accessibility
- Ensure WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text)
- Include proper ARIA labels on theme toggles
- Respect
prefers-color-scheme
for initial theme selection
Performance
- Use CSS custom properties instead of JavaScript style manipulation
- Minimize theme-switching JavaScript to under 1KB
- Leverage the astro-themes package for simplified toggle logic if needed
Maintainability
- Keep theme customizations in separate files from the original theme source
- Document your color tokens and design decisions
- Use semantic variable names (
--color-primary
not--blue-500
)
Conclusion
Whether you’re starting with Astro starter templates or implementing custom Astro UI theming, the framework provides flexible approaches for both needs. Prebuilt themes offer rapid development with professional designs, while runtime theming with CSS custom properties and minimal JavaScript ensures a smooth user experience. Focus on accessibility, performance, and clear separation between theme sources and customizations to build maintainable, user-friendly Astro projects.
FAQs
While technically possible, combining multiple themes requires significant manual work. Each theme has its own structure and dependencies. Instead, choose one base theme and customize it or extract specific components you need from other themes.
Create a separate branch for your customizations and regularly pull updates from the original repository into your main branch. Then merge main into your custom branch, resolving conflicts as needed. This keeps your changes isolated while staying current.
When implemented correctly with CSS custom properties and minimal JavaScript, the impact is negligible. The theme toggle script should be under 1KB and CSS variables have no runtime cost. Avoid JavaScript-based style manipulation which can cause layout thrashing.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.