Dark mode on Astro Adding dark mode to your Astro site.
Just like in regular Svelte, we use the class
strategy from Tailwind CSS to support dark mode toggling. See the Tailwind CSS documentation for more information.
How you add the dark
class to the html
element is up to you. In this guide, we'll take a look at enabling dark mode toggling with mode-watcher .
Usage Create an inline theme script This script will, in part, keep and track the dark mode value in localStorage
and prevent FUOC .
src/pages/index.astro ---
import "$lib/styles/app.css" ;
---
< script is:inline >
const isBrowser = typeof localStorage !== 'undefined' ;
const getThemePreference = () => {
if ( isBrowser && localStorage . getItem ( 'theme' )) {
return localStorage . getItem ( 'theme' );
}
return window . matchMedia ( '(prefers-color-scheme: dark)' ). matches
? 'dark' : 'light' ;
};
const isDark = getThemePreference () === 'dark' ;
document . documentElement . classList [ isDark ? 'add' : 'remove' ]( 'dark' );
if ( isBrowser ) {
const observer = new MutationObserver (() => {
const isDark = document . documentElement . classList . contains ( 'dark' );
localStorage . setItem ( 'theme' , isDark ? 'dark' : 'light' );
});
observer . observe ( document . documentElement , {
attributes : true,
attributeFilter : [ 'class' ]
});
}
</ script >
< html lang = "en" >
< body >
< h1 > Astro </ h1 >
</ body >
</ html >
</ script >
Copy Install mode-watcher npm install mode-watcher
select package manager npm Copy Add the ModeWatcher component Import the ModeWatcher
component and use it in your page with the client:load
directive:
src/pages/index.astro ---
import "$lib/styles/app.css" ;
import { ModeWatcher } from "mode-watcher" ;
---
<!-- inline-script -->
< html lang = "en" >
< body >
< h1 > Astro </ h1 >
< ModeWatcher client:load />
</ body >
</ html >
Copy Create a mode toggle Create a mode toggle on your site to toggle between light and dark mode:
Light switch < script lang = "ts" >
import Sun from "svelte-radix/Sun.svelte" ;
import Moon from "svelte-radix/Moon.svelte" ;
import { toggleMode } from "mode-watcher" ;
import { Button } from "$lib/components/ui/button/index.js" ;
</ script >
< Button on: click ={ toggleMode } variant = "outline" size = "icon" >
< Sun
class = "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
/>
< Moon
class = "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
/>
< span class = "sr-only" > Toggle theme </ span >
</ Button >
Copy < script lang = "ts" >
import Sun from "lucide-svelte/icons/sun" ;
import Moon from "lucide-svelte/icons/moon" ;
import { toggleMode } from "mode-watcher" ;
import { Button } from "$lib/components/ui/button/index.js" ;
</ script >
< Button on: click ={ toggleMode } variant = "outline" size = "icon" >
< Sun
class = "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
/>
< Moon
class = "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
/>
< span class = "sr-only" > Toggle theme </ span >
</ Button >
Copy
< script lang = "ts" >
import Sun from "svelte-radix/Sun.svelte" ;
import Moon from "svelte-radix/Moon.svelte" ;
import { resetMode , setMode } from "mode-watcher" ;
import { Button } from "$lib/components/ui/button/index.js" ;
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js" ;
</ script >
< DropdownMenu.Root >
< DropdownMenu.Trigger asChild let: builder >
< Button builders ={[ builder ]} variant = "outline" size = "icon" >
< Sun
class = "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
/>
< Moon
class = "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
/>
< span class = "sr-only" > Toggle theme </ span >
</ Button >
</ DropdownMenu.Trigger >
< DropdownMenu.Content align = "end" >
< DropdownMenu.Item on: click ={() => setMode ( "light" )}
> Light </ DropdownMenu.Item
>
< DropdownMenu.Item on: click ={() => setMode ( "dark" )}> Dark </ DropdownMenu.Item >
< DropdownMenu.Item on: click ={() => resetMode ()}> System </ DropdownMenu.Item >
</ DropdownMenu.Content >
</ DropdownMenu.Root >
Copy < script lang = "ts" >
import Sun from "lucide-svelte/icons/sun" ;
import Moon from "lucide-svelte/icons/moon" ;
import { resetMode , setMode } from "mode-watcher" ;
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js" ;
import { Button } from "$lib/components/ui/button/index.js" ;
</ script >
< DropdownMenu.Root >
< DropdownMenu.Trigger asChild let: builder >
< Button builders ={[ builder ]} variant = "outline" size = "icon" >
< Sun
class = "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
/>
< Moon
class = "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
/>
< span class = "sr-only" > Toggle theme </ span >
</ Button >
</ DropdownMenu.Trigger >
< DropdownMenu.Content align = "end" >
< DropdownMenu.Item on: click ={() => setMode ( "light" )}
> Light </ DropdownMenu.Item
>
< DropdownMenu.Item on: click ={() => setMode ( "dark" )}> Dark </ DropdownMenu.Item >
< DropdownMenu.Item on: click ={() => resetMode ()}> System </ DropdownMenu.Item >
</ DropdownMenu.Content >
</ DropdownMenu.Root >
Copy
Add mode toggle to page Add the mode toggle to the page (also with the client:load
directive):
src/pages/index.astro ---
import "$lib/styles/app.css" ;
import { ModeWatcher } from "mode-watcher" ;
import ModeToggle from "$lib/components/mode-toggle.svelte" ;
---
<!-- inline-script -->
< html lang = "en" >
< body >
< h1 > Astro </ h1 >
< ModeWatcher client:load />
< ModeToggle client:load />
</ body >
</ html >
Copy