A social RSS reader built on the AT Protocol. glean.at
glean atproto atmosphere rss feed social app
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement system theme preference and cycle through dark, light, and system modes

+39 -26
+37 -26
internal/tmpl/base.html
··· 6 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 7 <title>Glean</title> 8 8 <script> 9 - (function(){var t=localStorage.getItem('theme')||'dark';document.documentElement.setAttribute('data-theme',t)})(); 9 + (function(){var p=localStorage.getItem('theme')||'system';var r=p==='system'?(window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light'):p;document.documentElement.setAttribute('data-theme',r)})(); 10 10 </script> 11 11 <link rel="stylesheet" href="/static/output.css"> 12 12 <script src="https://unpkg.com/htmx.org@2"></script> ··· 37 37 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> 38 38 </head> 39 39 <body class="bg-spot-bg text-spot-text min-h-screen flex"> 40 - <dialog id="confirm-dialog" class="bg-spot-surface rounded-xl p-6 max-w-sm shadow-lg border border-spot-divider backdrop:bg-black/50" style="color-scheme:dark" onclick="if(event.target===this)this.close()"> 40 + <dialog id="confirm-dialog" class="bg-spot-surface rounded-xl p-6 max-w-sm shadow-lg border border-spot-divider backdrop:bg-black/50" onclick="if(event.target===this)this.close()"> 41 41 <p id="confirm-dialog-msg" class="text-spot-text text-sm mb-6"></p> 42 42 <div class="flex justify-end gap-3"> 43 43 <button onclick="document.getElementById('confirm-dialog').close(false)" class="text-sm text-spot-secondary hover:text-spot-text px-4 py-2 rounded-pill border border-spot-outline transition">Cancel</button> 44 44 <button id="confirm-dialog-ok" class="text-sm text-white bg-spot-red hover:brightness-110 px-4 py-2 rounded-pill font-bold uppercase tracking-button transition">Confirm</button> 45 45 </div> 46 46 </dialog> 47 - <dialog id="shortcuts-dialog" class="bg-spot-surface rounded-xl p-6 max-w-xs shadow-lg border border-spot-divider backdrop:bg-black/50" style="color-scheme:dark" onclick="if(event.target===this)this.close()"> 47 + <dialog id="shortcuts-dialog" class="bg-spot-surface rounded-xl p-6 max-w-xs shadow-lg border border-spot-divider backdrop:bg-black/50" onclick="if(event.target===this)this.close()"> 48 48 <h3 class="text-sm font-bold text-spot-text uppercase tracking-wide mb-4">Keyboard shortcuts</h3> 49 49 <div class="space-y-2 text-sm"> 50 50 <div class="text-spot-secondary font-bold text-xs uppercase tracking-wide">Navigation</div> ··· 193 193 <button onclick="toggleTheme()" class="text-xs text-spot-secondary hover:text-spot-text hover:underline inline-flex gap-1.5 items-center transition text-left"> 194 194 <svg class="w-3.5 h-3.5 shrink-0 theme-icon-dark" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg> 195 195 <svg class="w-3.5 h-3.5 shrink-0 theme-icon-light hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg> 196 - <span class="theme-text-dark">Light mode</span> 197 - <span class="theme-text-light hidden">Dark mode</span> 196 + <svg class="w-3.5 h-3.5 shrink-0 theme-icon-system hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25A2.25 2.25 0 0 1 5.25 3h13.5A2.25 2.25 0 0 1 21 5.25Z"/></svg> 197 + <span class="theme-text-dark">Dark</span> 198 + <span class="theme-text-light hidden">Light</span> 199 + <span class="theme-text-system hidden">System</span> 198 200 </button> 199 201 <button onclick="document.getElementById('shortcuts-dialog').showModal()" class="text-xs text-spot-secondary hover:text-spot-text hover:underline inline-flex gap-1.5 items-center transition text-left"> 200 202 <svg class="w-3.5 h-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z"/></svg> ··· 228 230 <button onclick="toggleTheme()" class="text-xs text-spot-secondary hover:text-spot-text inline-flex gap-1.5 items-center transition"> 229 231 <svg class="w-3.5 h-3.5 theme-icon-dark" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg> 230 232 <svg class="w-3.5 h-3.5 theme-icon-light hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg> 231 - <span class="theme-text-dark">Light mode</span> 232 - <span class="theme-text-light hidden">Dark mode</span> 233 + <svg class="w-3.5 h-3.5 theme-icon-system hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25A2.25 2.25 0 0 1 5.25 3h13.5A2.25 2.25 0 0 1 21 5.25Z"/></svg> 234 + <span class="theme-text-dark">Dark</span> 235 + <span class="theme-text-light hidden">Light</span> 236 + <span class="theme-text-system hidden">System</span> 233 237 </button> 234 238 </div> 235 239 <span class="text-xs text-spot-secondary">&copy; {{now.Format "2006"}} <a href="https://bsky.app/profile/julien.rbrt.fr">julien.rbrt.fr</a> &middot; Made in Europe &#127466;&#127482;</span> ··· 241 245 </main> 242 246 243 247 <script> 244 - function toggleTheme() { 245 - var current = document.documentElement.getAttribute('data-theme'); 246 - var next = current === 'dark' ? 'light' : 'dark'; 247 - document.documentElement.setAttribute('data-theme', next); 248 - localStorage.setItem('theme', next); 249 - updateThemeIcons(next); 248 + var themePref = localStorage.getItem('theme') || 'system'; 249 + var modes = ['dark', 'light', 'system']; 250 + 251 + function resolveTheme(pref) { 252 + return pref === 'system' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : pref; 250 253 } 251 - function updateThemeIcons(theme) { 252 - document.querySelectorAll('.theme-icon-dark').forEach(function(el) { 253 - el.classList.toggle('hidden', theme === 'light'); 254 - }); 255 - document.querySelectorAll('.theme-icon-light').forEach(function(el) { 256 - el.classList.toggle('hidden', theme === 'dark'); 257 - }); 258 - document.querySelectorAll('.theme-text-dark').forEach(function(el) { 259 - el.classList.toggle('hidden', theme === 'light'); 260 - }); 261 - document.querySelectorAll('.theme-text-light').forEach(function(el) { 262 - el.classList.toggle('hidden', theme === 'dark'); 254 + 255 + function applyTheme() { 256 + document.documentElement.setAttribute('data-theme', resolveTheme(themePref)); 257 + var next = modes[(modes.indexOf(themePref) + 1) % 3]; 258 + modes.forEach(function(m) { 259 + var show = m === next; 260 + document.querySelectorAll('.theme-icon-' + m + ', .theme-text-' + m).forEach(function(el) { 261 + el.classList.toggle('hidden', !show); 262 + }); 263 263 }); 264 264 } 265 - updateThemeIcons(document.documentElement.getAttribute('data-theme') || 'dark'); 265 + 266 + function toggleTheme() { 267 + themePref = modes[(modes.indexOf(themePref) + 1) % 3]; 268 + localStorage.setItem('theme', themePref); 269 + applyTheme(); 270 + } 271 + 272 + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() { 273 + if (themePref === 'system') applyTheme(); 274 + }); 275 + 276 + applyTheme(); 266 277 267 278 function closeAnnotate(el) { 268 279 var form = el.closest('.annotate-form');
+2
static/input.css
··· 4 4 5 5 @layer base { 6 6 :root { 7 + color-scheme: dark; 7 8 --spot-bg: #0a1814; 8 9 --spot-surface: #152b24; 9 10 --spot-hover: #1e3c33; ··· 23 24 } 24 25 25 26 [data-theme="light"] { 27 + color-scheme: light; 26 28 --spot-bg: #f2f0eb; 27 29 --spot-surface: #ffffff; 28 30 --spot-hover: #edebe9;