← back to Rentv 2026

public/daynight.js

107 lines

/* RENTV day/night toggle — a simple, user-facing light⇄dark switch with a
 * sun/moon icon, available to EVERY visitor on EVERY page (injected site-wide
 * by server.js, mirroring toggle.js). $0/local.
 *
 * It speaks the SAME contract as theme.js: it sets the CSS custom properties
 * (--red/--ink/--sub/--line/--bg/--wash/--serif/--sans) on <html> and toggles
 * body.theme-dark, so it renders correctly on any page whether or not the
 * 10-theme power picker (theme.js) is also loaded. Two clean states only:
 *   DAY   = RENTV Classic (light, the site default)
 *   NIGHT = Midnight Capital (a polished navy/gold dark theme)
 *
 * FOUC note: the pre-apply that runs before paint lives inline in <head>
 * (injected by server.js). This file just mounts the button + wires clicks. */
(function () {
  if (window.__rentvDayNight) return; window.__rentvDayNight = 1;

  var DAY = {
    dark:false, accent:"#c8102e", ink:"#14171a", sub:"#5b636b", line:"#e4e7ea",
    bg:"#ffffff", wash:"#f5f6f8",
    serif:"'Playfair Display', Georgia, serif", sans:"'Inter', system-ui, sans-serif"
  };
  var NIGHT = {
    dark:true, accent:"#c9a84c", ink:"#f0f2f5", sub:"#8fa3b8", line:"#1e2d40",
    bg:"#0d1b2a", wash:"#132336",
    serif:"'DM Serif Display', Georgia, serif", sans:"'Archivo', system-ui, sans-serif"
  };
  var KEY = 'rentv-daynight';

  // Dark-surface overrides — same fix theme.js applies: the design system overloads
  // var(--ink) as both text AND dark-surface bg (util bar / footer / subscribe box).
  // In night mode --ink is light, so re-point those surfaces to --wash and their text
  // to --sub/--ink. Guarded by an id so it isn't duplicated if theme.js is also present.
  if (!document.getElementById('rentv-dark-surfaces')) {
    var st = document.createElement('style'); st.id = 'rentv-dark-surfaces';
    st.textContent =
      "body.theme-dark .util,body.theme-dark footer,body.theme-dark .ns,body.theme-dark .mast{background:var(--wash)!important}" +
      "body.theme-dark #rentv-footer{background:var(--wash)!important}" +
      "body.theme-dark .util,body.theme-dark .util a,body.theme-dark footer,body.theme-dark footer a,body.theme-dark footer p{color:var(--sub)!important}" +
      "body.theme-dark footer .logo,body.theme-dark footer h5,body.theme-dark #rentv-footer .rf-brand{color:var(--ink)!important}" +
      "body.theme-dark .ns{color:var(--ink)!important}body.theme-dark .ns p{color:var(--sub)!important}" +
      "body.theme-dark .card .img,body.theme-dark .lead .img{filter:brightness(.85)}";
    document.head.appendChild(st);
  }

  // Button chrome. Fixed bottom-RIGHT so it never collides with the admin
  // Front/Backend toggle (bottom-left, admin-only).
  var css = document.createElement('style');
  css.textContent =
    '#rentv-daynight{position:fixed;right:16px;bottom:16px;z-index:2147482000;' +
    'width:42px;height:42px;display:flex;align-items:center;justify-content:center;' +
    'border-radius:999px;cursor:pointer;border:1px solid var(--line,#e4e7ea);' +
    'background:var(--bg,#fff);color:var(--ink,#14171a);' +
    'box-shadow:0 4px 16px rgba(0,0,0,.18);transition:transform .15s,box-shadow .15s,background .2s,color .2s;' +
    '-webkit-tap-highlight-color:transparent;}' +
    '#rentv-daynight:hover{transform:translateY(-2px);box-shadow:0 7px 22px rgba(0,0,0,.26);}' +
    '#rentv-daynight:active{transform:translateY(0);}' +
    '#rentv-daynight svg{width:20px;height:20px;display:block;}' +
    '@media(max-width:640px){#rentv-daynight{width:38px;height:38px;right:12px;bottom:12px;}#rentv-daynight svg{width:18px;height:18px;}}';
  document.head.appendChild(css);

  var SUN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.2"/><path d="M12 2v2.2M12 19.8V22M4.2 4.2l1.6 1.6M18.2 18.2l1.6 1.6M2 12h2.2M19.8 12H22M4.2 19.8l1.6-1.6M18.2 5.8l1.6-1.6"/></svg>';
  var MOON = '<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M20.5 14.2A8.3 8.3 0 0 1 9.8 3.5a.7.7 0 0 0-.94-.86A9.7 9.7 0 1 0 21.4 15.1a.7.7 0 0 0-.9-.9z"/></svg>';

  function isNight() {
    try {
      var v = localStorage.getItem(KEY);
      if (v === 'night') return true;
      if (v === 'day') return false;
    } catch (e) {}
    // First visit with no stored preference → follow the OS setting.
    return !!(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
  }

  function apply(night) {
    var t = night ? NIGHT : DAY, r = document.documentElement;
    r.style.setProperty('--red', t.accent);  r.style.setProperty('--ink', t.ink);
    r.style.setProperty('--sub', t.sub);     r.style.setProperty('--line', t.line);
    r.style.setProperty('--bg', t.bg);       r.style.setProperty('--wash', t.wash);
    r.style.setProperty('--serif', t.serif); r.style.setProperty('--sans', t.sans);
    document.body.classList.toggle('theme-dark', night);
    var btn = document.getElementById('rentv-daynight');
    if (btn) {
      btn.innerHTML = night ? SUN : MOON;                 // show the action, not the state
      btn.setAttribute('aria-label', night ? 'Switch to day mode' : 'Switch to night mode');
      btn.setAttribute('title', night ? 'Day mode' : 'Night mode');
      btn.setAttribute('aria-pressed', night ? 'true' : 'false');
    }
  }

  function mount() {
    if (document.getElementById('rentv-daynight')) return;
    var btn = document.createElement('button');
    btn.id = 'rentv-daynight'; btn.type = 'button';
    document.body.appendChild(btn);
    var night = isNight();
    apply(night);
    btn.addEventListener('click', function () {
      night = !document.body.classList.contains('theme-dark');
      apply(night);
      try { localStorage.setItem(KEY, night ? 'night' : 'day'); } catch (e) {}
    });
  }

  if (document.body) mount();
  else document.addEventListener('DOMContentLoaded', mount);
})();