Add light/dark theme toggle to all pages
- Flash-prevention script in <head> reads radical_theme from localStorage and sets data-theme on <html> before first paint — no FOUC - Fixed ☀️/🌙 button (top-right, z-index 9999) on index, admin, and login using CSS ::before so the icon is driven by data-theme with no JS initialization - Light mode: #F5F3EF warm off-white body, #E8E6E2 cards, #CCC borders, #111 text - Pink accent (#ff0099) unchanged in both modes - Signed-in users: theme saved to radical_user localStorage object and applied on load so preference follows their session Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ad058c1978
commit
942da4b6a5
4 changed files with 202 additions and 0 deletions
34
admin.html
34
admin.html
|
|
@ -105,9 +105,37 @@ body{font-family:'Roboto Mono',monospace;background:var(--bg);color:var(--text);
|
|||
.recent-time{font-size:11px;color:var(--muted);white-space:nowrap}
|
||||
|
||||
.loading{text-align:center;padding:40px;color:var(--muted)}
|
||||
/* ── Theme toggle ── */
|
||||
#theme-toggle{position:fixed;top:14px;right:14px;z-index:9999;background:rgba(0,0,0,.55);border:1px solid #444;border-radius:50%;width:38px;height:38px;font-size:0;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .2s,border-color .2s;padding:0}
|
||||
#theme-toggle::before{content:"☀️";font-size:17px;line-height:1}
|
||||
#theme-toggle:hover{background:rgba(255,0,153,.18);border-color:#ff0099}
|
||||
/* ── Light mode ── */
|
||||
html[data-theme="light"]{--bg:#F5F3EF;--card:#E8E6E2;--border:#CCCCCC;--text:#333;--muted:#777}
|
||||
html[data-theme="light"] body{background:var(--bg);color:var(--text)}
|
||||
html[data-theme="light"] #theme-toggle{background:rgba(245,243,239,.88);border-color:#BBB}
|
||||
html[data-theme="light"] #theme-toggle::before{content:"🌙"}
|
||||
html[data-theme="light"] #theme-toggle:hover{background:rgba(255,0,153,.1);border-color:#ff0099}
|
||||
html[data-theme="light"] .top-bar{background:#E0DED9}
|
||||
html[data-theme="light"] .stat-value{color:#111}
|
||||
html[data-theme="light"] .aec-track{background:#CCC}
|
||||
html[data-theme="light"] .state-track{background:#CCC}
|
||||
html[data-theme="light"] .data-table th{background:#DDD;color:#555}
|
||||
html[data-theme="light"] .data-table td{border-bottom-color:#DDD}
|
||||
html[data-theme="light"] .data-table tr:hover td{background:#DEDAD6}
|
||||
html[data-theme="light"] .data-table .name-cell{color:#111}
|
||||
html[data-theme="light"] #pw-input{background:#FFF;color:#111;border-color:#CCC}
|
||||
html[data-theme="light"] .search-box{background:#FFF;color:#111;border-color:#CCC}
|
||||
html[data-theme="light"] .state-filter{background:#FFF;color:#111;border-color:#CCC}
|
||||
html[data-theme="light"] .petition-row{border-bottom-color:#DDD}
|
||||
html[data-theme="light"] .petition-row:hover{background:#DEDAD6}
|
||||
html[data-theme="light"] .recent-row{border-bottom-color:#DDD}
|
||||
html[data-theme="light"] .recent-row:hover{background:#DEDAD6}
|
||||
html[data-theme="light"] .recent-name{color:#111}
|
||||
</style>
|
||||
<script>(function(){try{if(localStorage.getItem('radical_theme')==='light')document.documentElement.setAttribute('data-theme','light')}catch(e){}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="theme-toggle" onclick="toggleTheme()" aria-label="Toggle light/dark mode"></button>
|
||||
|
||||
<!-- Auth gate -->
|
||||
<div id="auth-gate">
|
||||
|
|
@ -394,6 +422,12 @@ function fmtDate(ts) {
|
|||
function esc(s) {
|
||||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const next = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
|
||||
document.documentElement.setAttribute('data-theme', next);
|
||||
try { localStorage.setItem('radical_theme', next); } catch(e) {}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
69
index.html
69
index.html
|
|
@ -250,6 +250,59 @@
|
|||
.postcode-badge span{color:#ff0099}
|
||||
/* ── Inline milestone strip on proposal cards ── */
|
||||
.card-milestone{margin-top:8px}
|
||||
/* ── LIGHT MODE: index.html-specific overrides ── */
|
||||
html[data-theme="light"] .stats-bar{background:#E8E6E2;border-color:#CCC;color:#555}
|
||||
html[data-theme="light"] .stat-divider{color:#BBB}
|
||||
html[data-theme="light"] .leaderboard-section{border-color:#ff0099}
|
||||
html[data-theme="light"] .leaderboard-item{border-bottom-color:#DDD}
|
||||
html[data-theme="light"] .leaderboard-item:hover{background:#DEDAD6}
|
||||
html[data-theme="light"] .leaderboard-text{color:#111}
|
||||
html[data-theme="light"] .leaderboard-rank{color:#777}
|
||||
html[data-theme="light"] .leaderboard-postcode{color:#777}
|
||||
html[data-theme="light"] .milestone-bar-track{background:#CCC}
|
||||
html[data-theme="light"] .milestone-label{color:#666}
|
||||
html[data-theme="light"] .membership-modal-overlay{background:rgba(0,0,0,.55)}
|
||||
html[data-theme="light"] .membership-modal{background:#F5F3EF}
|
||||
html[data-theme="light"] .membership-modal-intro{color:#555}
|
||||
html[data-theme="light"] .mm-input{background:#FFF;border-color:#CCC;color:#111}
|
||||
html[data-theme="light"] .mm-input option{background:#FFF;color:#111}
|
||||
html[data-theme="light"] .mm-field label{color:#555}
|
||||
html[data-theme="light"] .mm-check label{color:#555}
|
||||
html[data-theme="light"] .mm-later{color:#777}
|
||||
html[data-theme="light"] .mm-later:hover{color:#444}
|
||||
html[data-theme="light"] .email-modal-overlay{background:rgba(0,0,0,.55)}
|
||||
html[data-theme="light"] .email-modal{background:#F5F3EF}
|
||||
html[data-theme="light"] .email-modal p{color:#555}
|
||||
html[data-theme="light"] .email-modal-input{background:#FFF;border-color:#CCC;color:#111}
|
||||
html[data-theme="light"] .email-modal-skip{color:#777}
|
||||
html[data-theme="light"] .email-modal-skip:hover{color:#333}
|
||||
html[data-theme="light"] .constitution-section{background:#EBEBEA;border-color:#CCC}
|
||||
html[data-theme="light"] .constitution-header{background:#E0DED9}
|
||||
html[data-theme="light"] .constitution-header:hover{background:#D8D6D2}
|
||||
html[data-theme="light"] .constitution-title{color:#111}
|
||||
html[data-theme="light"] .constitution-meta{color:#777}
|
||||
html[data-theme="light"] .constitution-body{background:#F5F3EF}
|
||||
html[data-theme="light"] .constitution-draft-notice{background:#FFF8EE;border-color:#E8CFA0;color:#555}
|
||||
html[data-theme="light"] .constitution-adopted{color:#777}
|
||||
html[data-theme="light"] .const-section{border-bottom-color:#DDD}
|
||||
html[data-theme="light"] .const-clause{color:#333}
|
||||
html[data-theme="light"] .const-clause strong{color:#111}
|
||||
html[data-theme="light"] .const-clause em{color:#555}
|
||||
html[data-theme="light"] .const-sub{color:#555}
|
||||
html[data-theme="light"] .const-end{color:#777;border-top-color:#DDD}
|
||||
html[data-theme="light"] .about-section{background:#EBEBEA;border-color:#CCC}
|
||||
html[data-theme="light"] .about-body{color:#333}
|
||||
html[data-theme="light"] .about-body strong{color:#111}
|
||||
html[data-theme="light"] .about-lead{color:#222}
|
||||
html[data-theme="light"] .about-divider{border-top-color:#DDD}
|
||||
html[data-theme="light"] .about-switzerland{background:#E0DED9;border-color:#CCC}
|
||||
html[data-theme="light"] .about-swiss-heading{color:#111}
|
||||
html[data-theme="light"] .about-step{background:#E0DED9}
|
||||
html[data-theme="light"] .about-footer-note{color:#777;border-top-color:#DDD}
|
||||
html[data-theme="light"] .join-input{background:#FFF;border-color:#CCC;color:#111}
|
||||
html[data-theme="light"] .join-input option{background:#FFF;color:#111}
|
||||
html[data-theme="light"] .join-field label{color:#555}
|
||||
html[data-theme="light"] .join-check label{color:#555}
|
||||
</style>
|
||||
|
||||
<!-- Crawling -->
|
||||
|
|
@ -332,8 +385,10 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<script>(function(){try{if(localStorage.getItem('radical_theme')==='light')document.documentElement.setAttribute('data-theme','light')}catch(e){}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="theme-toggle" onclick="toggleTheme()" aria-label="Toggle light/dark mode"></button>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>RADICAL</h1>
|
||||
|
|
@ -765,9 +820,23 @@ function getMediaURL(path) {
|
|||
});
|
||||
|
||||
// Initialize application
|
||||
function applyTheme(t) {
|
||||
document.documentElement.setAttribute('data-theme', t);
|
||||
try { localStorage.setItem('radical_theme', t); } catch(e) {}
|
||||
}
|
||||
function toggleTheme() {
|
||||
const next = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
|
||||
applyTheme(next);
|
||||
if (typeof currentUser !== 'undefined' && currentUser?.id) {
|
||||
currentUser.theme = next;
|
||||
try { localStorage.setItem('radical_user', JSON.stringify(currentUser)); } catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
async function initializeApp() {
|
||||
updateUIText();
|
||||
await loadUserData();
|
||||
if (currentUser?.theme) applyTheme(currentUser.theme);
|
||||
loadVerifiedUsers();
|
||||
setupEventListeners();
|
||||
await Promise.allSettled([fetchProposals(), fetchLeaderboard(), fetchStats()]);
|
||||
|
|
|
|||
34
login.html
34
login.html
|
|
@ -583,9 +583,37 @@
|
|||
color: #444;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* ── Theme toggle ── */
|
||||
#theme-toggle{position:fixed;top:14px;right:14px;z-index:9999;background:rgba(0,0,0,.55);border:1px solid #444;border-radius:50%;width:38px;height:38px;font-size:0;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .2s,border-color .2s;padding:0}
|
||||
#theme-toggle::before{content:"☀️";font-size:17px;line-height:1}
|
||||
#theme-toggle:hover{background:rgba(255,0,153,.18);border-color:#ff0099}
|
||||
/* ── Light mode ── */
|
||||
html[data-theme="light"]{--light:#111;--border:#CCC;--dark:#F5F3EF}
|
||||
html[data-theme="light"] body{background:#F5F3EF;color:#111}
|
||||
html[data-theme="light"] #theme-toggle{background:rgba(245,243,239,.88);border-color:#BBB}
|
||||
html[data-theme="light"] #theme-toggle::before{content:"🌙"}
|
||||
html[data-theme="light"] #theme-toggle:hover{background:rgba(255,0,153,.1);border-color:#ff0099}
|
||||
html[data-theme="light"] .login-container{background-color:rgba(235,234,232,0.92)}
|
||||
html[data-theme="light"] .emoji-grid{background-color:rgba(255,255,255,0.9)}
|
||||
html[data-theme="light"] .emoji-item:hover{background-color:rgba(0,0,0,.06)}
|
||||
html[data-theme="light"] .dictator-card{background-color:rgba(255,255,255,0.7)}
|
||||
html[data-theme="light"] .target-card{background-color:rgba(255,255,255,0.7)}
|
||||
html[data-theme="light"] .target-card:hover{background-color:rgba(235,234,232,0.9)}
|
||||
html[data-theme="light"] .target-card.selected{background-color:rgba(255,0,153,.08)}
|
||||
html[data-theme="light"] .search-input{background-color:rgba(255,255,255,0.9);color:#111}
|
||||
html[data-theme="light"] .how-it-works{background:#EBEBEA;border-color:#CCC}
|
||||
html[data-theme="light"] .hiw-step{color:#555}
|
||||
html[data-theme="light"] .hiw-step strong{color:#111}
|
||||
html[data-theme="light"] .anon-option{background:#EBEBEA;border-color:#CCC}
|
||||
html[data-theme="light"] .anon-note{color:#777}
|
||||
html[data-theme="light"] .tagline{color:#333}
|
||||
html[data-theme="light"] .tooltip{background-color:rgba(245,243,239,0.97);color:#111}
|
||||
</style>
|
||||
<script>(function(){try{if(localStorage.getItem('radical_theme')==='light')document.documentElement.setAttribute('data-theme','light')}catch(e){}})();</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="theme-toggle" onclick="toggleTheme()" aria-label="Toggle light/dark mode"></button>
|
||||
<div class="container animate-fade-in">
|
||||
<header>
|
||||
<h1>RADICAL</h1>
|
||||
|
|
@ -1349,6 +1377,12 @@ function attemptLogin() {
|
|||
setTimeout(() => { window.location.href = '/'; }, 2500);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const next = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
|
||||
document.documentElement.setAttribute('data-theme', next);
|
||||
try { localStorage.setItem('radical_theme', next); } catch(e) {}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
65
styles.css
65
styles.css
|
|
@ -1219,3 +1219,68 @@ a {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* ─── THEME TOGGLE BUTTON ──────────────────────────────────────────────── */
|
||||
#theme-toggle {
|
||||
position: fixed;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
z-index: 9999;
|
||||
background: rgba(0,0,0,0.55);
|
||||
border: 1px solid #444;
|
||||
border-radius: 50%;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
font-size: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background .2s, border-color .2s;
|
||||
padding: 0;
|
||||
}
|
||||
#theme-toggle::before {
|
||||
content: "☀️";
|
||||
font-size: 17px;
|
||||
line-height: 1;
|
||||
}
|
||||
#theme-toggle:hover {
|
||||
background: rgba(255,0,153,0.18);
|
||||
border-color: #ff0099;
|
||||
}
|
||||
|
||||
/* ─── LIGHT MODE ───────────────────────────────────────────────────────── */
|
||||
html[data-theme="light"] {
|
||||
--secondary: #111111;
|
||||
--darker: #E8E6E2;
|
||||
--light: #111111;
|
||||
--border: #CCCCCC;
|
||||
--text: #111111;
|
||||
--text-muted: #666666;
|
||||
--card-bg: rgba(235,234,232,0.9);
|
||||
}
|
||||
html[data-theme="light"] body { background-color: #F5F3EF; }
|
||||
html[data-theme="light"] #theme-toggle { background: rgba(245,243,239,0.88); border-color: #BBB; }
|
||||
html[data-theme="light"] #theme-toggle::before { content: "🌙"; }
|
||||
html[data-theme="light"] #theme-toggle:hover { background: rgba(255,0,153,0.1); border-color: #ff0099; }
|
||||
html[data-theme="light"] .proposal-input { background-color: #FFFFFF; box-shadow: inset 0 0 5px rgba(0,0,0,.06); }
|
||||
html[data-theme="light"] .sort-select { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .form-input { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .share-link-field { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .comment-input { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .meme-upload { background-color: rgba(255,255,255,.5); }
|
||||
html[data-theme="light"] .meme-upload-label { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .meme-preview { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .proposal-meme { background-color: rgba(255,255,255,.4); }
|
||||
html[data-theme="light"] .meme-remove { background-color: rgba(255,255,255,.9); color: #111; }
|
||||
html[data-theme="light"] .meme-remove:hover { background-color: #ff0099; color: #000; }
|
||||
html[data-theme="light"] .modal-overlay { background-color: rgba(0,0,0,.55); }
|
||||
html[data-theme="light"] .modal-container { background: #EBEBEA; }
|
||||
html[data-theme="light"] .post-button:disabled { background-color: #CCC; color: #999; }
|
||||
html[data-theme="light"] .petition-submit-btn:disabled { background-color: #CCC; color: #999; }
|
||||
html[data-theme="light"] .comment-submit:disabled { background-color: #CCC; color: #999; }
|
||||
html[data-theme="light"] .comment-item { border-bottom-color: #DDD; }
|
||||
html[data-theme="light"] .social-share-button { background-color: #FFFFFF; }
|
||||
html[data-theme="light"] .share-button { background: #FFFFFF; }
|
||||
html[data-theme="light"] .music-controls { background: rgba(235,234,232,.95); }
|
||||
html[data-theme="light"] .trending-badge { background-color: #333; color: #F5F3EF; }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue