← 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);
})();