← back to Ventura Corridor

public/theme.js

53 lines

/**
 * Theme toggle — sun/moon, persists via localStorage.
 *
 * Inverts the noir-on-metal palette: --noir↔--ink, --noir-rise↔--noir-rise (compute), etc.
 * Applied via a single body class `light` that swaps CSS vars at the :root level.
 *
 * Anti-flash: this script runs synchronously inline in <head>, so the class is set
 * before first paint.
 *
 * Usage: drop <script src="/theme.js" defer></script> in <head> AFTER an inline
 * antiflash snippet, then call attachThemeToggle(parentEl) from page JS.
 */
(function() {
  const KEY = 'vc-theme';
  const stored = (function () { try { return localStorage.getItem(KEY); } catch { return null; } })();
  const initial = stored === 'light' || stored === 'dark' ? stored : 'dark';
  document.documentElement.dataset.theme = initial;

  function setTheme(t) {
    document.documentElement.dataset.theme = t;
    try { localStorage.setItem(KEY, t); } catch {}
    document.querySelectorAll('.theme-toggle').forEach(btn => {
      btn.textContent = t === 'light' ? '☾' : '☀';
      btn.title = t === 'light' ? 'Switch to dark' : 'Switch to light';
    });
  }

  window.toggleTheme = function() {
    const cur = document.documentElement.dataset.theme || 'dark';
    setTheme(cur === 'dark' ? 'light' : 'dark');
  };

  window.attachThemeToggle = function(parent) {
    if (!parent) return;
    if (parent.querySelector('.theme-toggle')) return;
    const btn = document.createElement('button');
    btn.className = 'theme-toggle';
    btn.type = 'button';
    btn.setAttribute('aria-label', 'Toggle dark / light theme');
    btn.style.cssText = 'background:transparent;border:1px solid var(--rule);color:var(--ink-mute);font-size:13px;padding:4px 10px;cursor:pointer;letter-spacing:.1em;font-family:inherit;line-height:1.2;height:28px;';
    btn.addEventListener('click', toggleTheme);
    btn.addEventListener('mouseenter', () => { btn.style.borderColor = 'var(--metal)'; btn.style.color = 'var(--metal)'; });
    btn.addEventListener('mouseleave', () => { btn.style.borderColor = 'var(--rule)'; btn.style.color = 'var(--ink-mute)'; });
    parent.appendChild(btn);
    setTheme(document.documentElement.dataset.theme || 'dark');
  };

  // Auto-attach to any element with class "theme-toggle-host"
  document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll('.theme-toggle-host').forEach(host => attachThemeToggle(host));
  });
})();