How to Add a Dark Mode Toggle to Your Shopify Theme: A Detailed Guide

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

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

  1. Why Support Dark Mode?
  2. Planning Your Dark Mode Palette
    • 2.1. Choosing Semantic CSS Variables
    • 2.2. Examples of Light vs. Dark Variables
  3. Defining CSS Variables in Your Theme
    • 3.1. Modifying Your Main CSS File
    • 3.2. Applying Variables to Common Elements
  4. Adding the Toggle Button Markup
    • 4.1. Placing the Button in Your Header Section
    • 4.2. Ensuring Responsive Placement
  5. Styling the Toggle for Consistency
    • 5.1. Core Styles and Transitions
    • 5.2. Dark/Light Icon Swapping
  6. 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
  7. Accessibility and Fallback Strategies
    • 7.1. ARIA Attributes and Visible Focus States
    • 7.2. No-JS Graceful Degradation
    • 7.3. Contrast Ratio Validation
  8. Testing Across Devices and Browsers
  9. 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
  10. Conclusion
  11. 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

VariableLight Mode ValueDark 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.

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts