10 CSS Dark Mode Toggles05 / 10

CSS + JSMIT licensed

LocalStorage Anti-Flash Template

An interactive simulation of the anti-FOUC pattern: an inline blocking IIFE reads localStorage before CSS loads and applies a data-dm05 attribute to prevent any flash of white on dark pages.

Published

Live Demo
Try it

The code

<div class="dm-05" id="dm-05-root">
  <div class="dm-05__main">
    <div class="dm-05__topbar">
      <span class="dm-05__topbar-title">Anti-Flash Engine Active</span>
      <div class="dm-05__indicator"><div class="dm-05__led"></div>FOUC Protected</div>
    </div>
    <div class="dm-05__body">
      <span class="dm-05__eyebrow">⚡ localStorage</span>
      <h2 class="dm-05__title">Anti-Flash<br>Dark Mode</h2>
      <p class="dm-05__sub">An inline blocking IIFE in <code>&lt;head&gt;</code> reads localStorage <em>before</em> CSS loads, preventing any flash of unstyled white content. Simulate a page reload to see it work.</p>
      <div class="dm-05__console">
        <div class="dm-05__console-line">document.head &larr; inline script executes</div>
        <div class="dm-05__console-line">localStorage.getItem('theme') &rarr; <span id="dm-05-stored">null</span></div>
        <div class="dm-05__console-line dim">// CSS loads AFTER this block &rarr; no flash</div>
      </div>
      <div class="dm-05__refresh-row">
        <button class="dm-05__refresh-btn" id="dm-05-refresh">
          <i class="dm-05__refresh-icon">↺</i>Simulate Refresh
        </button>
        <button class="dm-05__toggle-btn" id="dm-05-toggle">
          <span id="dm-05-toggle-icon">🌙</span>
          <span id="dm-05-toggle-label">Switch to Dark</span>
        </button>
      </div>
    </div>
    <div class="dm-05__log">
      <div class="dm-05__log-title">Event log</div>
      <div class="dm-05__log-list" id="dm-05-log">
        <div class="dm-05__log-entry"><span class="ts">[init]</span>Page loaded — reading localStorage…</div>
      </div>
    </div>
  </div>
</div>
.dm-05,
.dm-05 *,
.dm-05 *::before,
.dm-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.dm-05 ::selection {
  background: #10b981;
  color: #000;
}

[data-dm05="dark"] .dm-05 {
  --bg: #030d08;
  --surface: #071a0f;
  --border: #0f3a1f;
  --text: #d1fae5;
  --muted: #4d7a5e;
  --accent: #10b981;
  --accent2: #34d399;
  --glow: rgba(16,185,129,0.3);
  --tag-bg: rgba(16,185,129,0.12);
  --console-bg: #010b05;
  --console-border: #0a2a12;
  --console-text: #34d399;
}

.dm-05 {
  --bg: #f0fdf8;
  --surface: #ffffff;
  --border: #d1fae5;
  --text: #064e3b;
  --muted: #6b8e7a;
  --accent: #059669;
  --accent2: #10b981;
  --glow: rgba(5,150,105,0.15);
  --tag-bg: rgba(5,150,105,0.1);
  --console-bg: #f0fdf4;
  --console-border: #bbf7d0;
  --console-text: #059669;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  gap: 24px;
  color: var(--text);
  transition: background 0.4s ease,color 0.4s ease;
}

.dm-05__main {
  width: min(520px,100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  overflow: hidden;
  box-shadow: 0 4px 40px rgba(0,0,0,0.06);
  transition: background 0.4s ease,border-color 0.4s ease,box-shadow 0.4s ease;
}

[data-dm05="dark"] .dm-05__main {
  box-shadow: 0 4px 40px var(--glow);
}

.dm-05__topbar {
  background: var(--accent);
  padding: 10px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.dm-05__topbar-title {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
}

.dm-05__indicator {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.8);
}

.dm-05__led {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
  animation: dm-05-pulse 2s ease-in-out infinite;
}

@keyframes dm-05-pulse {
  0%,
    100% {
    opacity: 1;
  }

  50% {
    opacity: 0.3;
  }
}

.dm-05__body {
  padding: 32px 28px;
}

.dm-05__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 99px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--tag-bg);
  color: var(--accent);
  border: 1px solid rgba(5,150,105,0.2);
  margin-bottom: 12px;
  transition: background 0.4s ease,color 0.4s ease;
}

.dm-05__title {
  font-size: clamp(1.3rem,3.5vw,1.8rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  color: var(--text);
  margin-bottom: 10px;
  transition: color 0.4s ease;
  line-height: 1.15;
}

.dm-05__sub {
  font-size: 0.85rem;
  line-height: 1.65;
  color: var(--muted);
  margin-bottom: 24px;
  transition: color 0.4s ease;
}
/* Console box */

.dm-05__console {
  background: var(--console-bg);
  border: 1px solid var(--console-border);
  border-radius: 12px;
  padding: 16px;
  font-family: monospace;
  margin-bottom: 20px;
  transition: background 0.4s ease,border-color 0.4s ease;
}

.dm-05__console-line {
  font-size: 0.75rem;
  line-height: 1.8;
  color: var(--console-text);
  transition: color 0.4s ease;
}

.dm-05__console-line::before {
  content: '> ';
  opacity: 0.5;
}

.dm-05__console-line.dim {
  opacity: 0.4;
}
/* Simulate refresh button */

.dm-05__refresh-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.dm-05__refresh-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 12px;
  border: none;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  box-shadow: 0 3px 12px var(--glow);
  transition: background 0.3s ease,transform 0.2s ease,box-shadow 0.3s ease;
}

.dm-05__refresh-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 5px 20px var(--glow);
}

.dm-05__refresh-btn:active {
  transform: translateY(0);
}

.dm-05__refresh-btn.spinning .dm-05__refresh-icon {
  animation: dm-05-spin 0.6s linear;
}

@keyframes dm-05-spin {
  to {
    transform: rotate(360deg);
  }
}

.dm-05__refresh-icon {
  display: inline-block;
  font-style: normal;
}

.dm-05__toggle-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 11px 18px;
  border-radius: 12px;
  border: 1.5px solid var(--border);
  cursor: pointer;
  background: transparent;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  transition: border-color 0.3s ease,color 0.3s ease,background 0.3s ease;
}

.dm-05__toggle-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--tag-bg);
}
/* History log */

.dm-05__log {
  border-top: 1px solid var(--border);
  padding: 16px 28px;
  transition: border-color 0.4s ease;
}

.dm-05__log-title {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
  transition: color 0.4s ease;
}

.dm-05__log-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.dm-05__log-entry {
  font-size: 0.72rem;
  color: var(--muted);
  font-family: monospace;
  transition: color 0.4s ease;
  opacity: 0;
  animation: dm-05-fadein 0.3s forwards;
}

@keyframes dm-05-fadein {
  to {
    opacity: 1;
  }
}

.dm-05__log-entry .ts {
  color: var(--accent);
  margin-right: 8px;
  opacity: 0.7;
}

@media (prefers-reduced-motion: reduce) {
  .dm-05,
    .dm-05 * {
    transition: none!important;
    animation: none!important;
  }
}
(()=>{
  const root=document.getElementById('dm-05-root');
  const storedSpan=document.getElementById('dm-05-stored');
  const toggleBtn=document.getElementById('dm-05-toggle');
  const toggleIcon=document.getElementById('dm-05-toggle-icon');
  const toggleLabel=document.getElementById('dm-05-toggle-label');
  const refreshBtn=document.getElementById('dm-05-refresh');
  const log=document.getElementById('dm-05-log');
  let dark=document.documentElement.getAttribute('data-dm05')==='dark';
  function addLog(msg){
    const el=document.createElement('div');
    el.className='dm-05__log-entry';
    const ts=new Date().toLocaleTimeString('en',{hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'});
    el.innerHTML=`<span class="ts">[${ts}]</span>${msg}`;
    log.appendChild(el);
    if(log.children.length>6)log.removeChild(log.firstChild);
  }
  function applyTheme(isDark){
    dark=isDark;
    root.classList.toggle('is-dark',isDark);
    if(isDark){document.documentElement.setAttribute('data-dm05','dark');}
    else{document.documentElement.removeAttribute('data-dm05');}
    try{
      if(isDark)localStorage.setItem('dm05-theme','dark');
      else localStorage.removeItem('dm05-theme');
    }catch(e){}
    storedSpan.textContent=isDark?'"dark"':'null';
    toggleIcon.textContent=isDark?'☀️':'🌙';
    toggleLabel.textContent=isDark?'Switch to Light':'Switch to Dark';
  }
  toggleBtn.addEventListener('click',()=>{
    applyTheme(!dark);
    addLog(`Theme set to <strong>${dark?'dark':'light'}</strong> → saved to localStorage`);
  });
  refreshBtn.addEventListener('click',()=>{
    refreshBtn.classList.add('spinning');
    addLog('Simulating page reload…');
    setTimeout(()=>{
      const stored=dark?'dark':null;
      addLog(`localStorage['dm05-theme'] = ${stored?'"'+stored+'"':'null'} — applied BEFORE paint`);
      refreshBtn.classList.remove('spinning');
      addLog(`No FOUC — page painted in <strong>${dark?'dark':'light'}</strong> mode instantly ✓`);
    },600);
  });
  // Initial state
  storedSpan.textContent=dark?'"dark"':'null';
  toggleIcon.textContent=dark?'☀️':'🌙';
  toggleLabel.textContent=dark?'Switch to Light':'Switch to Dark';
  if(dark)addLog('Dark mode detected in localStorage — applied instantly');
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Dark Mode Toggle from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: LocalStorage Anti-Flash Template
Source: https://codefronts.com/snippets/css-dark-mode-toggle/localstorage-anti-flash-template/

An interactive simulation of the anti-FOUC pattern: an inline blocking IIFE reads localStorage before CSS loads and applies a data-dm05 attribute to prevent any flash of white on dark pages.
## HTML
```html
<div class="dm-05" id="dm-05-root">
  <div class="dm-05__main">
    <div class="dm-05__topbar">
      <span class="dm-05__topbar-title">Anti-Flash Engine Active</span>
      <div class="dm-05__indicator"><div class="dm-05__led"></div>FOUC Protected</div>
    </div>
    <div class="dm-05__body">
      <span class="dm-05__eyebrow">⚡ localStorage</span>
      <h2 class="dm-05__title">Anti-Flash<br>Dark Mode</h2>
      <p class="dm-05__sub">An inline blocking IIFE in <code>&lt;head&gt;</code> reads localStorage <em>before</em> CSS loads, preventing any flash of unstyled white content. Simulate a page reload to see it work.</p>
      <div class="dm-05__console">
        <div class="dm-05__console-line">document.head &larr; inline script executes</div>
        <div class="dm-05__console-line">localStorage.getItem('theme') &rarr; <span id="dm-05-stored">null</span></div>
        <div class="dm-05__console-line dim">// CSS loads AFTER this block &rarr; no flash</div>
      </div>
      <div class="dm-05__refresh-row">
        <button class="dm-05__refresh-btn" id="dm-05-refresh">
          <i class="dm-05__refresh-icon">↺</i>Simulate Refresh
        </button>
        <button class="dm-05__toggle-btn" id="dm-05-toggle">
          <span id="dm-05-toggle-icon">🌙</span>
          <span id="dm-05-toggle-label">Switch to Dark</span>
        </button>
      </div>
    </div>
    <div class="dm-05__log">
      <div class="dm-05__log-title">Event log</div>
      <div class="dm-05__log-list" id="dm-05-log">
        <div class="dm-05__log-entry"><span class="ts">[init]</span>Page loaded — reading localStorage…</div>
      </div>
    </div>
  </div>
</div>
```
## CSS
```css
.dm-05,
.dm-05 *,
.dm-05 *::before,
.dm-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.dm-05 ::selection {
  background: #10b981;
  color: #000;
}

[data-dm05="dark"] .dm-05 {
  --bg: #030d08;
  --surface: #071a0f;
  --border: #0f3a1f;
  --text: #d1fae5;
  --muted: #4d7a5e;
  --accent: #10b981;
  --accent2: #34d399;
  --glow: rgba(16,185,129,0.3);
  --tag-bg: rgba(16,185,129,0.12);
  --console-bg: #010b05;
  --console-border: #0a2a12;
  --console-text: #34d399;
}

.dm-05 {
  --bg: #f0fdf8;
  --surface: #ffffff;
  --border: #d1fae5;
  --text: #064e3b;
  --muted: #6b8e7a;
  --accent: #059669;
  --accent2: #10b981;
  --glow: rgba(5,150,105,0.15);
  --tag-bg: rgba(5,150,105,0.1);
  --console-bg: #f0fdf4;
  --console-border: #bbf7d0;
  --console-text: #059669;
  font-family: 'Segoe UI',system-ui,sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  gap: 24px;
  color: var(--text);
  transition: background 0.4s ease,color 0.4s ease;
}

.dm-05__main {
  width: min(520px,100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  overflow: hidden;
  box-shadow: 0 4px 40px rgba(0,0,0,0.06);
  transition: background 0.4s ease,border-color 0.4s ease,box-shadow 0.4s ease;
}

[data-dm05="dark"] .dm-05__main {
  box-shadow: 0 4px 40px var(--glow);
}

.dm-05__topbar {
  background: var(--accent);
  padding: 10px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.dm-05__topbar-title {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
}

.dm-05__indicator {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.8);
}

.dm-05__led {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
  animation: dm-05-pulse 2s ease-in-out infinite;
}

@keyframes dm-05-pulse {
  0%,
    100% {
    opacity: 1;
  }

  50% {
    opacity: 0.3;
  }
}

.dm-05__body {
  padding: 32px 28px;
}

.dm-05__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 99px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--tag-bg);
  color: var(--accent);
  border: 1px solid rgba(5,150,105,0.2);
  margin-bottom: 12px;
  transition: background 0.4s ease,color 0.4s ease;
}

.dm-05__title {
  font-size: clamp(1.3rem,3.5vw,1.8rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  color: var(--text);
  margin-bottom: 10px;
  transition: color 0.4s ease;
  line-height: 1.15;
}

.dm-05__sub {
  font-size: 0.85rem;
  line-height: 1.65;
  color: var(--muted);
  margin-bottom: 24px;
  transition: color 0.4s ease;
}
/* Console box */

.dm-05__console {
  background: var(--console-bg);
  border: 1px solid var(--console-border);
  border-radius: 12px;
  padding: 16px;
  font-family: monospace;
  margin-bottom: 20px;
  transition: background 0.4s ease,border-color 0.4s ease;
}

.dm-05__console-line {
  font-size: 0.75rem;
  line-height: 1.8;
  color: var(--console-text);
  transition: color 0.4s ease;
}

.dm-05__console-line::before {
  content: '> ';
  opacity: 0.5;
}

.dm-05__console-line.dim {
  opacity: 0.4;
}
/* Simulate refresh button */

.dm-05__refresh-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.dm-05__refresh-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 12px;
  border: none;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  box-shadow: 0 3px 12px var(--glow);
  transition: background 0.3s ease,transform 0.2s ease,box-shadow 0.3s ease;
}

.dm-05__refresh-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 5px 20px var(--glow);
}

.dm-05__refresh-btn:active {
  transform: translateY(0);
}

.dm-05__refresh-btn.spinning .dm-05__refresh-icon {
  animation: dm-05-spin 0.6s linear;
}

@keyframes dm-05-spin {
  to {
    transform: rotate(360deg);
  }
}

.dm-05__refresh-icon {
  display: inline-block;
  font-style: normal;
}

.dm-05__toggle-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 11px 18px;
  border-radius: 12px;
  border: 1.5px solid var(--border);
  cursor: pointer;
  background: transparent;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  transition: border-color 0.3s ease,color 0.3s ease,background 0.3s ease;
}

.dm-05__toggle-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--tag-bg);
}
/* History log */

.dm-05__log {
  border-top: 1px solid var(--border);
  padding: 16px 28px;
  transition: border-color 0.4s ease;
}

.dm-05__log-title {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
  transition: color 0.4s ease;
}

.dm-05__log-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.dm-05__log-entry {
  font-size: 0.72rem;
  color: var(--muted);
  font-family: monospace;
  transition: color 0.4s ease;
  opacity: 0;
  animation: dm-05-fadein 0.3s forwards;
}

@keyframes dm-05-fadein {
  to {
    opacity: 1;
  }
}

.dm-05__log-entry .ts {
  color: var(--accent);
  margin-right: 8px;
  opacity: 0.7;
}

@media (prefers-reduced-motion: reduce) {
  .dm-05,
    .dm-05 * {
    transition: none!important;
    animation: none!important;
  }
}
```

## JavaScript
```js
(()=>{
  const root=document.getElementById('dm-05-root');
  const storedSpan=document.getElementById('dm-05-stored');
  const toggleBtn=document.getElementById('dm-05-toggle');
  const toggleIcon=document.getElementById('dm-05-toggle-icon');
  const toggleLabel=document.getElementById('dm-05-toggle-label');
  const refreshBtn=document.getElementById('dm-05-refresh');
  const log=document.getElementById('dm-05-log');
  let dark=document.documentElement.getAttribute('data-dm05')==='dark';
  function addLog(msg){
    const el=document.createElement('div');
    el.className='dm-05__log-entry';
    const ts=new Date().toLocaleTimeString('en',{hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'});
    el.innerHTML=`<span class="ts">[${ts}]</span>${msg}`;
    log.appendChild(el);
    if(log.children.length>6)log.removeChild(log.firstChild);
  }
  function applyTheme(isDark){
    dark=isDark;
    root.classList.toggle('is-dark',isDark);
    if(isDark){document.documentElement.setAttribute('data-dm05','dark');}
    else{document.documentElement.removeAttribute('data-dm05');}
    try{
      if(isDark)localStorage.setItem('dm05-theme','dark');
      else localStorage.removeItem('dm05-theme');
    }catch(e){}
    storedSpan.textContent=isDark?'"dark"':'null';
    toggleIcon.textContent=isDark?'☀️':'🌙';
    toggleLabel.textContent=isDark?'Switch to Light':'Switch to Dark';
  }
  toggleBtn.addEventListener('click',()=>{
    applyTheme(!dark);
    addLog(`Theme set to <strong>${dark?'dark':'light'}</strong> → saved to localStorage`);
  });
  refreshBtn.addEventListener('click',()=>{
    refreshBtn.classList.add('spinning');
    addLog('Simulating page reload…');
    setTimeout(()=>{
      const stored=dark?'dark':null;
      addLog(`localStorage['dm05-theme'] = ${stored?'"'+stored+'"':'null'} — applied BEFORE paint`);
      refreshBtn.classList.remove('spinning');
      addLog(`No FOUC — page painted in <strong>${dark?'dark':'light'}</strong> mode instantly ✓`);
    },600);
  });
  // Initial state
  storedSpan.textContent=dark?'"dark"':'null';
  toggleIcon.textContent=dark?'☀️':'🌙';
  toggleLabel.textContent=dark?'Switch to Light':'Switch to Dark';
  if(dark)addLog('Dark mode detected in localStorage — applied instantly');
})();
```

How this works

The critical snippet is a synchronous, non-deferred <script> tag in <head> before any stylesheets. It runs an IIFE that reads localStorage.getItem(dm05-theme) and, if the value is dark, immediately calls document.documentElement.setAttribute(data-dm05, dark). Because this script executes before the browser paints a single pixel, the CSS that follows targets [data-dm05=dark] .dm-05 and the page is painted dark without any flash of unstyled content.

The interactive log panel demonstrates the event sequence: the inline IIFE fires at document.head load time, CSS parses after, and the visual result is already dark when DOMContentLoaded fires. A simulated refresh button walks through the timeline via setTimeout to make the ordering visible. Toggling calls localStorage.setItem or removeItem and the stored value display updates in real time.

Make it yours

  • Replace data-dm05 with a CSS class on :root — use document.documentElement.classList.add(dark) instead for Tailwind or class-based frameworks.
  • Extend the IIFE to read system preference as a fallback: after checking localStorage, check window.matchMedia(prefers-color-scheme: dark).matches and apply dark if true.
  • Wrap the IIFE in a try-catch (already done) to silently handle Safari Private Browsing or browsers with storage disabled — the page gracefully defaults to light.
  • Add an expiry timestamp by storing JSON.stringify({ theme: dark, expires: Date.now() + 86400000 }) and checking the timestamp in the IIFE before applying.
  • For Next.js or Remix, inject the IIFE via a custom <Script strategy=beforeInteractive> tag or a _document.js render method to maintain the correct load order.

Gotchas — read before shipping

  • The IIFE must be in <head> before any <link rel=stylesheet> — placing it after styles means the browser has already queued a light-mode paint frame.
  • Never add defer or async to the anti-flash script — both attributes move execution after HTML parsing, eliminating the flash-prevention benefit.
  • Safari Private Browsing throws a SecurityError on localStorage access — always wrap in a try-catch to prevent the IIFE from crashing and blocking page render.

Browser support

ChromeSafariFirefoxEdge
49+10+44+49+

localStorage is universally supported; the pattern works in all modern browsers including mobile.

Techniques used in this demo

Search CodeFronts

Loading…