Skip to content

Commit 32764dd

Browse files
committed
Add dark mode to site
1 parent cf78668 commit 32764dd

12 files changed

Lines changed: 209 additions & 22 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ cython_debug/
172172
/tests/fixtures/fake_music_library
173173
/docs/flow
174174
.pytest-bootstrap-safety/
175+
Gemfile
176+
Gemfile.lock
177+
/_site
178+
/_site-check
175179

176180
# macOS
177181
.DS_Store

docs/_layouts/default.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77
{% capture software_id %}{{ site_home_url }}#softwareapplication{% endcapture %}
88
{% capture webpage_id %}{{ canonical_url }}#webpage{% endcapture %}
99
{% capture faq_id %}{{ canonical_url }}#faq{% endcapture %}
10+
<script>
11+
(() => {
12+
try {
13+
const theme = localStorage.getItem("iopenpod-theme");
14+
if (theme === "light" || theme === "dark") {
15+
document.documentElement.dataset.theme = theme;
16+
}
17+
} catch (error) {
18+
void error;
19+
}
20+
})();
21+
</script>
1022
<meta charset="UTF-8">
1123
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
1224
<title>{{ page.title | default: site.title }}</title>
@@ -39,6 +51,7 @@
3951
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
4052
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,700&amp;family=Geist:wght@400;700&amp;family=Geist+Mono:wght@400;700&amp;display=swap">
4153
<link rel="stylesheet" href="{{ '/site.min.css' | relative_url }}">
54+
<link rel="stylesheet" href="{{ '/theme.min.css' | relative_url }}">
4255

4356
<script type="application/ld+json">
4457
{
@@ -112,5 +125,6 @@
112125
<body class="{{ page.body_class | default: '' }}">
113126
{{ content }}
114127
<script src="{{ '/site.min.js' | relative_url }}" defer></script>
128+
<script src="{{ '/theme.min.js' | relative_url }}" defer></script>
115129
</body>
116130
</html>
File renamed without changes.
File renamed without changes.

docs/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<a href="install-help/">Help</a>
2020
<a href="https://discord.gg/9Yy499Tf5d">Discord</a>
2121
<a href="https://ko-fi.com/johngibbons">Donate</a>
22+
<button type="button" class="site-nav__action theme-toggle" data-theme-toggle aria-pressed="false" aria-label="Switch to dark mode">Dark mode</button>
2223
</div>
2324
</div>
2425
</nav>

docs/install-help.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
<a href="../">Home</a>
108108
<a href="https://discord.gg/9Yy499Tf5d">Discord</a>
109109
<a href="https://ko-fi.com/johngibbons">Donate</a>
110+
<button type="button" class="site-nav__action theme-toggle" data-theme-toggle aria-pressed="false" aria-label="Switch to dark mode">Dark mode</button>
110111
</div>
111112
</div>
112113
</nav>
@@ -227,23 +228,23 @@ <h3 id="macos-system-settings">Allow it in System Settings</h3>
227228

228229
<div class="step-gallery" aria-label="macOS security approval screenshots">
229230
<figure class="step-card wide">
230-
<img src="MacOS_Install_IMGS/macOS_Step1.webp" width="1840" height="1368" loading="lazy" alt="Finder window showing iOpenPod.app selected before first launch">
231+
<img src="{{ '/MacOS_Install_IMGS/macOS_Step1.webp' | relative_url }}" width="1840" height="1368" loading="lazy" alt="Finder window showing iOpenPod.app selected before first launch">
231232
<figcaption>1. Open <code>iOpenPod.app</code> from Finder.</figcaption>
232233
</figure>
233234
<figure class="step-card">
234-
<img src="MacOS_Install_IMGS/macOS_Step2.webp" width="520" height="598" loading="lazy" alt="macOS warning dialog saying iOpenPod.app was not opened and showing Done">
235+
<img src="{{ '/MacOS_Install_IMGS/macOS_Step2.webp' | relative_url }}" width="520" height="598" loading="lazy" alt="macOS warning dialog saying iOpenPod.app was not opened and showing Done">
235236
<figcaption>2. Choose <strong>Done</strong>.</figcaption>
236237
</figure>
237238
<figure class="step-card wide">
238-
<img src="MacOS_Install_IMGS/macOS_Step3.webp" width="1446" height="1632" loading="lazy" alt="Privacy and Security settings showing iOpenPod was blocked and an Open Anyway button">
239+
<img src="{{ '/MacOS_Install_IMGS/macOS_Step3.webp' | relative_url }}" width="1446" height="1632" loading="lazy" alt="Privacy and Security settings showing iOpenPod was blocked and an Open Anyway button">
239240
<figcaption>3. Choose <strong>Open Anyway</strong> in Privacy &amp; Security.</figcaption>
240241
</figure>
241242
<figure class="step-card">
242-
<img src="MacOS_Install_IMGS/macOS_Step4.webp" width="520" height="700" loading="lazy" alt="macOS confirmation dialog asking whether to open iOpenPod.app and showing Open Anyway">
243+
<img src="{{ '/MacOS_Install_IMGS/macOS_Step4.webp' | relative_url }}" width="520" height="700" loading="lazy" alt="macOS confirmation dialog asking whether to open iOpenPod.app and showing Open Anyway">
243244
<figcaption>4. Confirm with <strong>Open Anyway</strong>.</figcaption>
244245
</figure>
245246
<figure class="step-card">
246-
<img src="MacOS_Install_IMGS/macOS_Step5.webp" width="520" height="692" loading="lazy" alt="macOS authentication dialog asking for Touch ID or administrator password">
247+
<img src="{{ '/MacOS_Install_IMGS/macOS_Step5.webp' | relative_url }}" width="520" height="692" loading="lazy" alt="macOS authentication dialog asking for Touch ID or administrator password">
247248
<figcaption>5. Use Touch ID or enter an admin password.</figcaption>
248249
</figure>
249250
</div>

docs/theme.css

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
html {
2+
color-scheme: light;
3+
}
4+
5+
html[data-theme="light"] {
6+
color-scheme: light;
7+
}
8+
9+
html[data-theme="dark"] {
10+
color-scheme: dark;
11+
--color-paper: #1a1a2e;
12+
--color-paper-2: #1e1e32;
13+
--color-paper-3: #212135;
14+
--color-rule: #353546;
15+
--color-rule-2: #27273a;
16+
--color-neutral: #a1a1a9;
17+
--color-muted: #74747f;
18+
--color-ink-2: #a1a1a9;
19+
--color-ink: #e9e9eb;
20+
--color-accent: #409cff;
21+
--color-accent-ink: #ffffff;
22+
--color-focus: #409cff;
23+
--color-success: #51cf66;
24+
--color-warning: #fcc419;
25+
--color-error: #ff6b6b;
26+
--color-overlay: rgb(26 26 46 / 76%);
27+
--color-shadow: rgb(0 0 0 / 42%);
28+
}
29+
30+
@media (prefers-color-scheme: dark) {
31+
html:not([data-theme="light"]) {
32+
color-scheme: dark;
33+
--color-paper: #1a1a2e;
34+
--color-paper-2: #1e1e32;
35+
--color-paper-3: #212135;
36+
--color-rule: #353546;
37+
--color-rule-2: #27273a;
38+
--color-neutral: #a1a1a9;
39+
--color-muted: #74747f;
40+
--color-ink-2: #a1a1a9;
41+
--color-ink: #e9e9eb;
42+
--color-accent: #409cff;
43+
--color-accent-ink: #ffffff;
44+
--color-focus: #409cff;
45+
--color-success: #51cf66;
46+
--color-warning: #fcc419;
47+
--color-error: #ff6b6b;
48+
--color-overlay: rgb(26 26 46 / 76%);
49+
--color-shadow: rgb(0 0 0 / 42%);
50+
}
51+
}
52+
53+
.theme-toggle {
54+
min-width: 3rem;
55+
cursor: pointer;
56+
border-color: var(--color-rule-2);
57+
background-color: var(--color-paper-2);
58+
color: var(--color-ink-2);
59+
}
60+
61+
.theme-toggle[data-theme-current="dark"] {
62+
color: var(--color-ink-2);
63+
}
64+
65+
.theme-toggle[data-theme-current="light"] {
66+
color: var(--color-ink-2);
67+
}
68+
69+
@media (hover: hover) and (pointer: fine) {
70+
.theme-toggle:hover {
71+
text-decoration: none;
72+
}
73+
}

docs/theme.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
(() => {
2+
const storageKey = "iopenpod-theme";
3+
const root = document.documentElement;
4+
const toggle = document.querySelector("[data-theme-toggle]");
5+
6+
if (!toggle) {
7+
return;
8+
}
9+
10+
const media = window.matchMedia("(prefers-color-scheme: dark)");
11+
let savedTheme = null;
12+
13+
const readStoredTheme = () => {
14+
try {
15+
const value = localStorage.getItem(storageKey);
16+
if (value === "light" || value === "dark") {
17+
return value;
18+
}
19+
} catch (error) {
20+
void error;
21+
}
22+
23+
return null;
24+
};
25+
26+
const writeStoredTheme = (theme) => {
27+
try {
28+
if (theme) {
29+
localStorage.setItem(storageKey, theme);
30+
} else {
31+
localStorage.removeItem(storageKey);
32+
}
33+
} catch (error) {
34+
void error;
35+
}
36+
};
37+
38+
const systemTheme = () => (media.matches ? "dark" : "light");
39+
40+
const activeTheme = () => savedTheme || systemTheme();
41+
42+
const updateToggle = () => {
43+
const currentTheme = activeTheme();
44+
const nextTheme = currentTheme === "dark" ? "light" : "dark";
45+
46+
toggle.textContent = nextTheme === "dark" ? "☾" : "☀";
47+
toggle.setAttribute("aria-label", `Switch to ${nextTheme} mode`);
48+
toggle.setAttribute("aria-pressed", currentTheme === "dark" ? "true" : "false");
49+
toggle.dataset.themeCurrent = currentTheme;
50+
51+
if (!savedTheme) {
52+
root.removeAttribute("data-theme");
53+
}
54+
};
55+
56+
const applyTheme = (theme, persist = true) => {
57+
if (theme === "light" || theme === "dark") {
58+
savedTheme = theme;
59+
root.dataset.theme = theme;
60+
if (persist) {
61+
writeStoredTheme(theme);
62+
}
63+
} else {
64+
savedTheme = null;
65+
root.removeAttribute("data-theme");
66+
if (persist) {
67+
writeStoredTheme(null);
68+
}
69+
}
70+
71+
updateToggle();
72+
};
73+
74+
savedTheme = readStoredTheme();
75+
if (savedTheme) {
76+
root.dataset.theme = savedTheme;
77+
} else {
78+
root.removeAttribute("data-theme");
79+
}
80+
81+
updateToggle();
82+
83+
toggle.addEventListener("click", () => {
84+
applyTheme(activeTheme() === "dark" ? "light" : "dark");
85+
});
86+
87+
media.addEventListener("change", () => {
88+
if (!savedTheme) {
89+
updateToggle();
90+
}
91+
});
92+
})();

docs/theme.min.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)