Script Valley
Building Your Developer Portfolio
Adding Interactivity with JavaScriptLesson 3.2

How to implement dark mode toggle in a portfolio

dark mode, CSS custom properties, data-theme attribute, localStorage, prefers-color-scheme, classList toggle, system preference detection

Dark Mode Without a Framework

Dark mode is implemented at the CSS layer using custom properties and a data-theme attribute on the html element. JavaScript only toggles that attribute and persists the preference.

CSS: Two Theme Definitions

:root {
  --bg: #ffffff;
  --text: #111827;
  --border: #e5e7eb;
}

[data-theme="dark"] {
  --bg: #0f172a;
  --text: #f1f5f9;
  --border: #334155;
}

body {
  background-color: var(--bg);
  color: var(--text);
  transition: background-color 0.2s ease, color 0.2s ease;
}

JavaScript: Toggle and Persist

const toggle = document.querySelector('#theme-toggle');
const root = document.documentElement;

// Respect system preference on first load
if (localStorage.getItem('theme') === 'dark' ||
    (!localStorage.getItem('theme') &&
     window.matchMedia('(prefers-color-scheme: dark)').matches)) {
  root.setAttribute('data-theme', 'dark');
}

toggle.addEventListener('click', () => {
  const isDark = root.getAttribute('data-theme') === 'dark';
  root.setAttribute('data-theme', isDark ? 'light' : 'dark');
  localStorage.setItem('theme', isDark ? 'light' : 'dark');
});

Check localStorage first, then fall back to prefers-color-scheme. This means first-time visitors get their system preference automatically, and returning visitors get their manual choice. Add the toggle button to your nav with a sun/moon icon or simple text label.

Up next

How to add scroll-triggered animations to a portfolio using IntersectionObserver

Sign in to track progress