Use Dark Mode to make use of either a base or dark: variant for your utility class styles. By default, Tailwind uses the prefers-color-scheme media query to determine and match the user’s operating system settings. However, if you wish to provide your users manual control, you’ll need to adjust the Dark Mode strategy for Tailwind, as well as provide the toggle interface (aka a light switch). This guide will show you how to fulfill both requirements.
Adjust the Dark Mode Strategy
Open your global stylesheet and set the following variant:
@custom-variant dark (&:where([data-mode="dark"], [data-mode="dark"] *));Then set the following data attribute on your application’s <html> element for light mode:
<html data-mode="light"></html>Or for dark mode:
<html data-mode="dark"></html>Create the Component
We’ll create a implementation of the Switch component that can toggle the mode on demand.
import { Switch } from '@skeletonlabs/skeleton-react';
import { useEffect, useState } from 'react';
export default function Lightswitch() {
const [checked, setChecked] = useState(false);
useEffect(() => {
const mode = localStorage.getItem('mode') || 'light';
setChecked(mode === 'dark');
}, []);
const onCheckedChange = (event: { checked: boolean }) => {
const mode = event.checked ? 'dark' : 'light';
document.documentElement.setAttribute('data-mode', mode);
localStorage.setItem('mode', mode);
setChecked(event.checked);
};
return (
<>
<script
dangerouslySetInnerHTML={{
__html: `
const mode = localStorage.getItem('mode') || 'light';
document.documentElement.setAttribute('data-mode', mode);
`,
}}
/>
<Switch checked={checked} onCheckedChange={onCheckedChange}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.Label>Label</Switch.Label>
<Switch.HiddenInput />
</Switch>
</>
);
}Note: For Next.js users, you will need to set suppressHydrationWarning to
trueon the root<html>element. This will suppress hydration warnings for thedata-modeattribute.
<script lang="ts">
import { Switch } from '@skeletonlabs/skeleton-svelte';
let checked = $state(false);
$effect(() => {
const mode = localStorage.getItem('mode') || 'light';
checked = mode === 'dark';
});
const onCheckedChange = (event: { checked: boolean }) => {
const mode = event.checked ? 'dark' : 'light';
document.documentElement.setAttribute('data-mode', mode);
localStorage.setItem('mode', mode);
checked = event.checked;
};
</script>
<svelte:head>
<script>
const mode = localStorage.getItem('mode') || 'light';
document.documentElement.setAttribute('data-mode', mode);
</script>
</svelte:head>
<Switch {checked} {onCheckedChange}>
<Switch.Control>
<Switch.Thumb />
</Switch.Control>
<Switch.HiddenInput />
</Switch>Import the Component
We’ll then add the component to our app. Make sure to set the correct path.
import Lightswitch from './path/to/Lightswitch';
export default function Page() {
return <Lightswitch />;
}<script lang="ts">
import Lightswitch from './path/to/Lightswitch.svelte';
</script>
<Lightswitch />User Interface
While we utilize a primitive Switch for the minimal example above, feel free to adjust the logic and interface to your preference. We provide a more detailed Switch example .