Dark mode has become a staple in modern UI design—not only does it look sleek, it reduces eye strain in low-light environments and can extend battery life on OLED devices. By giving your customers the choice between light and dark appearances, you enhance their shopping experience and demonstrate attention to detail. In this comprehensive guide, we’ll walk through every step of integrating a dark mode toggle into your Shopify theme—from defining color palettes and writing CSS variables to wiring up JavaScript logic, handling user preferences, and ensuring accessibility. Whether you’re working with Shopify’s Dawn theme or a custom Liquid build, you’ll have everything you need to deliver a polished dark mode feature in under an hour.

Table of Contents
- Why Support Dark Mode?
- Planning Your Dark Mode Palette
- 2.1. Choosing Semantic CSS Variables
- 2.2. Examples of Light vs. Dark Variables
- Defining CSS Variables in Your Theme
- 3.1. Modifying Your Main CSS File
- 3.2. Applying Variables to Common Elements
- Adding the Toggle Button Markup
- 4.1. Placing the Button in Your Header Section
- 4.2. Ensuring Responsive Placement
- Styling the Toggle for Consistency
- 5.1. Core Styles and Transitions
- 5.2. Dark/Light Icon Swapping
- JavaScript for Theme Switching
- 6.1. Reading and Persisting User Preference
- 6.2. Detecting OS-Level Preferences (
prefers-color-scheme
) - 6.3. Full Script with Comments
- Accessibility and Fallback Strategies
- 7.1. ARIA Attributes and Visible Focus States
- 7.2. No-JS Graceful Degradation
- 7.3. Contrast Ratio Validation
- Testing Across Devices and Browsers
- Advanced Enhancements
- 9.1. Animating the Theme Transition
- 9.2. Saving Theme Preference in Cookies for Server-Side Rendering
- 9.3. Integrating with Shopify Sections and Theme Settings
- Conclusion
- SEO Metadata
Why Support Dark Mode?
- User Comfort: Dark backgrounds reduce glare in low-light conditions, easing eye fatigue for evening shoppers.
- Battery Efficiency: On OLED and AMOLED screens, black pixels consume no power, extending device battery life.
- Brand Perception: Offering dark mode signals that your brand keeps pace with modern UI trends and user expectations.
- Accessibility: Some users with light sensitivity rely on dark themes to browse comfortably.
Planning Your Dark Mode Palette
2.1. Choosing Semantic CSS Variables
Use descriptive variables that convey intent, not just color values:

--color-background
--color-surface
(cards, modals)--color-text-primary
--color-text-secondary
--color-accent
--color-border
This naming scheme makes it easy to expand your theme later (e.g., adding success, warning colors).
2.2. Examples of Light vs. Dark Variables
Variable | Light Mode Value | Dark Mode Value |
---|---|---|
--color-background | #ffffff | #121212 |
--color-surface | #f5f5f5 | #1e1e1e |
--color-text-primary | #212121 | #e0e0e0 |
--color-text-secondary | #616161 | #b0b0b0 |
--color-accent | #0071e3 | #4a90e2 |
--color-border | #e0e0e0 | #2c2c2c |
Defining CSS Variables in Your Theme
3.1. Modifying Your Main CSS File
Locate your main stylesheet—often assets/theme.css
or assets/base.css
. At the top, define your variables:
cssCopyEdit/* Light mode (default) */
:root {
--color-background: #ffffff;
--color-surface: #f5f5f5;
--color-text-primary: #212121;
--color-text-secondary: #616161;
--color-accent: #0071e3;
--color-border: #e0e0e0;
}
/* Dark mode */
[data-theme="dark"] {
--color-background: #121212;
--color-surface: #1e1e1e;
--color-text-primary: #e0e0e0;
--color-text-secondary: #b0b0b0;
--color-accent: #4a90e2;
--color-border: #2c2c2c;
}
3.2. Applying Variables to Common Elements
Replace static color definitions with your new variables:
cssCopyEditbody {
background-color: var(--color-background);
color: var(--color-text-primary);
}
.header, .footer {
background-color: var(--color-surface);
border-bottom: 1px solid var(--color-border);
}
a, .btn-primary {
color: var(--color-accent);
}
.card {
background-color: var(--color-surface);
border: 1px solid var(--color-border);
}
p, .text-secondary {
color: var(--color-text-secondary);
}
Adding the Toggle Button Markup
4.1. Placing the Button in Your Header Section

Edit sections/header.liquid
(Dawn) or snippets/header.liquid
(other themes). Insert this snippet where you want the toggle to appear—commonly near the cart icon:
liquidCopyEdit<button id="theme-toggle" class="theme-toggle" aria-label="Toggle dark mode">
<span id="theme-icon">🌙</span>
</button>
4.2. Ensuring Responsive Placement
Wrap it in a container that adapts to mobile:
liquidCopyEdit<div class="header__extras">
{{ cart_icon }}
{{ account_icon }}
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle dark mode">
<span id="theme-icon">🌙</span>
</button>
</div>
Adjust your theme’s CSS to accommodate the new element across breakpoints.
Styling the Toggle for Consistency
5.1. Core Styles and Transitions
Add to your CSS:
cssCopyEdit.theme-toggle {
background: none;
border: none;
cursor: pointer;
font-size: 1.5rem;
margin-left: 1rem;
color: var(--color-text-primary);
transition: color 0.3s;
}
.theme-toggle:hover,
.theme-toggle:focus {
color: var(--color-accent);
}
.theme-toggle:focus {
outline: 2px dashed var(--color-accent);
outline-offset: 2px;
}
5.2. Dark/Light Icon Swapping
You’ll swap the icon via JavaScript—no extra CSS needed, but ensure the icon <span>
uses var(--color-text-primary)
for visibility in both modes.
JavaScript for Theme Switching
Create assets/theme-toggle.js
:

jsCopyEdit// assets/theme-toggle.js
(function() {
const toggleButton = document.getElementById('theme-toggle');
const themeIcon = document.getElementById('theme-icon');
const root = document.documentElement;
// Determine initial theme: saved preference or OS setting
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const initialTheme = savedTheme || (prefersDark ? 'dark' : 'light');
// Apply the initial theme
root.setAttribute('data-theme', initialTheme);
themeIcon.textContent = initialTheme === 'dark' ? '☀️' : '🌙';
// Toggle event listener
toggleButton.addEventListener('click', () => {
const current = root.getAttribute('data-theme');
const next = current === 'light' ? 'dark' : 'light';
root.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
themeIcon.textContent = next === 'dark' ? '☀️' : '🌙';
});
})();
Finally, include this script in layout/theme.liquid
before the closing </body>
tag:
liquidCopyEdit<script src="{{ 'theme-toggle.js' | asset_url }}"></script>
Accessibility and Fallback Strategies
7.1. ARIA Attributes and Visible Focus States
- Use
aria-label="Toggle dark mode"
to describe the button’s purpose. - Ensure visible focus outlines for keyboard users, as shown in the CSS snippet above.
7.2. No-JS Graceful Degradation
If JavaScript is disabled, your site remains in light mode, preserving readability and navigation.
7.3. Contrast Ratio Validation
Run both themes through a contrast checker (e.g., WebAIM) to verify a minimum 4.5:1 ratio for normal text and 3:1 for large text.
Testing Across Devices and Browsers
- Desktop: Chrome, Firefox, Safari, Edge—toggle and reload to confirm saved preferences.
- Mobile: Test on iOS and Android; ensure the button remains tappable and icons render correctly.
- Dark/Light OS Settings: On first visit (with no saved preference), your site should adopt the user’s system theme.
Advanced Enhancements

9.1. Animating the Theme Transition
cssCopyEdithtml {
transition: background-color 0.3s, color 0.3s;
}
9.2. Server-Side Rendering with Cookies
For apps using Shopify’s Hydrogen or SSR, read a theme
cookie on the server and inject data-theme
into the served HTML.
9.3. Integrating with Theme Settings
Expose a setting in config/settings_schema.json
so merchants can enable or disable dark mode support, tailoring the feature to their brand’s needs.
Conclusion
Implementing a dark mode toggle in your Shopify theme elevates user experience, aligns with modern design patterns, and shows customers you care about comfort and accessibility. By defining semantic CSS variables, inserting a concise toggle button, writing unobtrusive JavaScript to handle preferences, and validating accessibility, you’ll deliver a robust, production-ready solution that delights shoppers—day or night.