Skip to content

Commit 9deeb9c

Browse files
authored
Switch shared chrome to dark-default theme with a light toggle (#1)
Flips the org design tokens to a dark palette by default, adds a light override under [data-theme=light], and ships a small toggle button + theme-toggle.js that persists the visitor's choice in localStorage. An inline head snippet applies the stored preference before paint to avoid a flash of the wrong theme.
1 parent 8300d10 commit 9deeb9c

3 files changed

Lines changed: 177 additions & 13 deletions

File tree

assets/css/site.css

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,47 @@
77
* cards, footer, typography. Project-specific / demo-runtime styles belong
88
* in a local stylesheet loaded after this one.
99
*
10+
* Theme: dark by default; `<html data-theme="light">` switches to the light
11+
* palette. Pair with assets/js/theme-toggle.js, which flips the attribute
12+
* and persists the choice in localStorage. Pages that render the toggle
13+
* button should apply the visitor's stored preference in an inline <head>
14+
* script (before this stylesheet paints) to avoid a flash of the wrong
15+
* theme — see any page's <head> for the one-line snippet.
16+
*
1017
* Canonical copy: https://pydevices.github.io/assets/css/site.css
1118
* Repos vendor a copy under web/vendor/pydevices-chrome/site.css for
1219
* offline dev + CI; keep vendored copies in sync with this file.
1320
*/
1421

1522
:root {
23+
--bg: #100e0b;
24+
--bg-soft: #17130f;
25+
--panel: #1c1712;
26+
--panel-2: #241e17;
27+
--border: #342a1d;
28+
--border-soft: #2a2117;
29+
--ink: #f3ecdf;
30+
--ink-soft: #c9bfae;
31+
--muted: #8f8474;
32+
--accent: #f54e00;
33+
--accent-soft: #ff7a3d;
34+
--accent-strong: #ff8a52;
35+
--accent-ink: #1a0d05;
36+
--indigo: #8b93ff;
37+
--good: #3ecf95;
38+
--warn: #e8b34e;
39+
--bad: #ef5c4e;
40+
--radius: 16px;
41+
--radius-sm: 10px;
42+
--shadow: 0 18px 44px rgba(0, 0, 0, 0.45);
43+
--shadow-soft: 0 6px 18px rgba(0, 0, 0, 0.3);
44+
--hover-border: #4a3c28;
45+
--font-display: "Space Grotesk", ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif;
46+
--font: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
47+
--mono: "SF Mono", "JetBrains Mono", "Fira Code", ui-monospace, Menlo, Consolas, monospace;
48+
}
49+
50+
:root[data-theme="light"] {
1651
--bg: #fbf9f5;
1752
--bg-soft: #f3eee6;
1853
--panel: #ffffff;
@@ -22,21 +57,15 @@
2257
--ink: #201c16;
2358
--ink-soft: #5b5347;
2459
--muted: #8a8172;
25-
--accent: #f54e00;
26-
--accent-soft: #ff7a3d;
2760
--accent-strong: #c33f00;
2861
--accent-ink: #fff7f1;
2962
--indigo: #5a4fd6;
3063
--good: #1f8f6b;
3164
--warn: #b5790a;
3265
--bad: #d1382b;
33-
--radius: 16px;
34-
--radius-sm: 10px;
3566
--shadow: 0 18px 44px rgba(32, 24, 12, 0.1);
3667
--shadow-soft: 0 6px 18px rgba(32, 24, 12, 0.06);
37-
--font-display: "Space Grotesk", ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif;
38-
--font: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
39-
--mono: "SF Mono", "JetBrains Mono", "Fira Code", ui-monospace, Menlo, Consolas, monospace;
68+
--hover-border: #d9cfba;
4069
}
4170

4271
* {
@@ -54,14 +83,22 @@ body {
5483
color: var(--ink);
5584
line-height: 1.6;
5685
background:
57-
radial-gradient(1100px 560px at 14% -12%, rgba(245, 78, 0, 0.09), transparent 60%),
58-
radial-gradient(900px 520px at 100% 0%, rgba(90, 79, 214, 0.06), transparent 55%),
86+
radial-gradient(1100px 560px at 14% -12%, rgba(245, 78, 0, 0.16), transparent 60%),
87+
radial-gradient(900px 520px at 100% 0%, rgba(139, 147, 255, 0.1), transparent 55%),
5988
var(--bg);
6089
background-attachment: fixed;
6190
display: flex;
6291
flex-direction: column;
6392
}
6493

94+
:root[data-theme="light"] body {
95+
background:
96+
radial-gradient(1100px 560px at 14% -12%, rgba(245, 78, 0, 0.09), transparent 60%),
97+
radial-gradient(900px 520px at 100% 0%, rgba(90, 79, 214, 0.06), transparent 55%),
98+
var(--bg);
99+
background-attachment: fixed;
100+
}
101+
65102
h1, h2, h3, h4 {
66103
font-family: var(--font-display);
67104
font-weight: 600;
@@ -126,10 +163,14 @@ pre code {
126163
top: 0;
127164
z-index: 20;
128165
backdrop-filter: blur(10px);
129-
background: rgba(251, 249, 245, 0.82);
166+
background: rgba(16, 14, 11, 0.82);
130167
border-bottom: 1px solid var(--border-soft);
131168
}
132169

170+
:root[data-theme="light"] .site-header {
171+
background: rgba(251, 249, 245, 0.82);
172+
}
173+
133174
.site-header .wrap {
134175
display: flex;
135176
align-items: center;
@@ -191,6 +232,52 @@ pre code {
191232
text-decoration: none;
192233
}
193234

235+
/* ---------- Theme toggle ---------- */
236+
237+
.theme-toggle {
238+
flex: none;
239+
margin-left: 6px;
240+
display: inline-flex;
241+
align-items: center;
242+
justify-content: center;
243+
width: 34px;
244+
height: 34px;
245+
border-radius: 999px;
246+
border: 1px solid var(--border);
247+
background: var(--panel);
248+
color: var(--ink-soft);
249+
cursor: pointer;
250+
padding: 0;
251+
transition: border-color 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
252+
}
253+
254+
.theme-toggle:hover {
255+
border-color: var(--accent-strong);
256+
color: var(--accent-strong);
257+
box-shadow: var(--shadow-soft);
258+
}
259+
260+
.theme-toggle svg {
261+
width: 17px;
262+
height: 17px;
263+
}
264+
265+
.theme-toggle .icon-moon {
266+
display: none;
267+
}
268+
269+
.theme-toggle .icon-sun {
270+
display: block;
271+
}
272+
273+
:root[data-theme="light"] .theme-toggle .icon-sun {
274+
display: none;
275+
}
276+
277+
:root[data-theme="light"] .theme-toggle .icon-moon {
278+
display: block;
279+
}
280+
194281
/* ---------- Hero ---------- */
195282

196283
.hero {
@@ -206,6 +293,7 @@ pre code {
206293
overflow: hidden;
207294
display: grid;
208295
place-items: center;
296+
background: linear-gradient(135deg, var(--accent-soft), var(--accent));
209297
box-shadow: 0 12px 28px rgba(245, 78, 0, 0.28);
210298
}
211299

@@ -291,7 +379,7 @@ pre code {
291379
}
292380

293381
.btn:hover {
294-
border-color: #d9cfba;
382+
border-color: var(--hover-border);
295383
box-shadow: var(--shadow-soft);
296384
text-decoration: none;
297385
}
@@ -384,7 +472,7 @@ pre code {
384472

385473
.card:hover {
386474
transform: translateY(-3px);
387-
border-color: #d9cfba;
475+
border-color: var(--hover-border);
388476
box-shadow: var(--shadow);
389477
text-decoration: none;
390478
}
@@ -461,18 +549,36 @@ pre code {
461549

462550
.tag.async, .tag.featured {
463551
color: var(--indigo);
552+
border-color: rgba(139, 147, 255, 0.3);
553+
background: rgba(139, 147, 255, 0.1);
554+
}
555+
556+
:root[data-theme="light"] .tag.async,
557+
:root[data-theme="light"] .tag.featured {
464558
border-color: rgba(90, 79, 214, 0.3);
465559
background: rgba(90, 79, 214, 0.08);
466560
}
467561

468562
.tag.all, .tag.tool {
469563
color: var(--good);
564+
border-color: rgba(62, 207, 149, 0.3);
565+
background: rgba(62, 207, 149, 0.1);
566+
}
567+
568+
:root[data-theme="light"] .tag.all,
569+
:root[data-theme="light"] .tag.tool {
470570
border-color: rgba(31, 143, 107, 0.3);
471571
background: rgba(31, 143, 107, 0.08);
472572
}
473573

474574
.tag.loops, .tag.warn {
475575
color: var(--warn);
576+
border-color: rgba(232, 179, 78, 0.3);
577+
background: rgba(232, 179, 78, 0.1);
578+
}
579+
580+
:root[data-theme="light"] .tag.loops,
581+
:root[data-theme="light"] .tag.warn {
476582
border-color: rgba(181, 121, 10, 0.3);
477583
background: rgba(181, 121, 10, 0.08);
478584
}
@@ -508,7 +614,7 @@ pre code {
508614
}
509615

510616
.linklist a:hover {
511-
border-color: #d9cfba;
617+
border-color: var(--hover-border);
512618
color: var(--ink);
513619
text-decoration: none;
514620
}

assets/js/theme-toggle.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* PyDevices — shared dark/light theme toggle
3+
*
4+
* Pairs with the `data-theme` attribute in assets/css/site.css. Dark is the
5+
* default (no attribute); "light" is opt-in and persisted in localStorage.
6+
*
7+
* Pages that render a `#theme-toggle` button should also inline this
8+
* snippet at the very top of <head>, before any stylesheet, so a returning
9+
* light-theme visitor doesn't see a flash of dark on load:
10+
*
11+
* <script>try{if(localStorage.getItem('pydevices-theme')==='light'){document.documentElement.setAttribute('data-theme','light');}}catch(e){}</script>
12+
*
13+
* Canonical copy: https://pydevices.github.io/assets/js/theme-toggle.js
14+
*/
15+
(function () {
16+
var STORAGE_KEY = "pydevices-theme";
17+
var root = document.documentElement;
18+
19+
function currentTheme() {
20+
return root.getAttribute("data-theme") === "light" ? "light" : "dark";
21+
}
22+
23+
function applyTheme(theme) {
24+
if (theme === "light") {
25+
root.setAttribute("data-theme", "light");
26+
} else {
27+
root.removeAttribute("data-theme");
28+
}
29+
}
30+
31+
function init() {
32+
var toggle = document.getElementById("theme-toggle");
33+
if (!toggle) {
34+
return;
35+
}
36+
toggle.addEventListener("click", function () {
37+
var next = currentTheme() === "light" ? "dark" : "light";
38+
applyTheme(next);
39+
try {
40+
localStorage.setItem(STORAGE_KEY, next);
41+
} catch (e) {
42+
/* localStorage unavailable (private mode, etc.) — toggle still works this visit */
43+
}
44+
});
45+
}
46+
47+
if (document.readyState === "loading") {
48+
document.addEventListener("DOMContentLoaded", init);
49+
} else {
50+
init();
51+
}
52+
})();

index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8">
5+
<script>try{if(localStorage.getItem('pydevices-theme')==='light'){document.documentElement.setAttribute('data-theme','light');}}catch(e){}</script>
56
<meta name="viewport" content="width=device-width, initial-scale=1">
67
<title>PyDevices</title>
78
<meta name="description" content="A cross-platform Python stack for displays, input, and graphics — running the same code on MicroPython, CircuitPython, and CPython.">
@@ -14,6 +15,7 @@
1415
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1516
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap">
1617
<link rel="stylesheet" href="assets/css/site.css">
18+
<script src="assets/js/theme-toggle.js" defer></script>
1719
</head>
1820
<body>
1921
<header class="site-header">
@@ -28,6 +30,10 @@
2830
<a href="https://pydevices.github.io/cmods/">cmods</a>
2931
<a href="https://github.com/PyDevices">GitHub</a>
3032
</nav>
33+
<button type="button" class="theme-toggle" id="theme-toggle" aria-label="Toggle color theme" title="Toggle color theme">
34+
<svg class="icon-sun" 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"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
35+
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
36+
</button>
3137
</div>
3238
</header>
3339

0 commit comments

Comments
 (0)