Skip to content

Commit f64f21f

Browse files
committed
make dark mode / light mode toggle responsive to user preferences
1 parent 68ed0fe commit f64f21f

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
lines changed

_includes/head/custom.html

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,305 @@
99
<meta name="application-name" content="Zarr">
1010
<meta name="msapplication-TileColor" content="#ffffff">
1111
<meta name="theme-color" content="#ffffff">
12+
13+
<!-- Dark/Light Mode CSS Variables and Styles -->
14+
<style>
15+
:root {
16+
/* Light mode colors (default) */
17+
--bg-color: #ffffff;
18+
--text-color: #494e52;
19+
--text-muted: #7a8288;
20+
--link-color: #52adc8;
21+
--link-hover-color: #367d93;
22+
--primary-color: #fff;
23+
--secondary-color: #f3f3f3;
24+
--border-color: #f2f3f3;
25+
--code-bg: #fafafa;
26+
--masthead-bg: #fff;
27+
--footer-bg: #f3f3f3;
28+
--notice-bg: #f8f9fa;
29+
--notice-info-bg: #d1ecf1;
30+
--notice-warning-bg: #fff3cd;
31+
--notice-danger-bg: #f8d7da;
32+
--notice-success-bg: #d4edda;
33+
}
34+
35+
[data-theme="dark"] {
36+
/* Dark mode colors */
37+
--bg-color: #1a1a1a;
38+
--text-color: #eaeaea;
39+
--text-muted: #a8a8a8;
40+
--link-color: #79b8cc;
41+
--link-hover-color: #a4cdd9;
42+
--primary-color: #1a1a1a;
43+
--secondary-color: #2d2d2d;
44+
--border-color: #404040;
45+
--code-bg: #2d2d2d;
46+
--masthead-bg: #1a1a1a;
47+
--footer-bg: #2d2d2d;
48+
--notice-bg: #2d2d2d;
49+
--notice-info-bg: #1e3a4a;
50+
--notice-warning-bg: #4a3e1e;
51+
--notice-danger-bg: #4a1e1e;
52+
--notice-success-bg: #1e4a2e;
53+
}
54+
55+
/* Apply CSS variables to override theme colors */
56+
html, body {
57+
background-color: var(--bg-color) !important;
58+
color: var(--text-color) !important;
59+
}
60+
61+
.masthead {
62+
background: var(--masthead-bg) !important;
63+
border-bottom: 1px solid var(--border-color) !important;
64+
}
65+
66+
.masthead__inner-wrap {
67+
background: var(--masthead-bg) !important;
68+
}
69+
70+
.masthead__menu {
71+
background: var(--masthead-bg) !important;
72+
}
73+
74+
.greedy-nav {
75+
background: var(--masthead-bg) !important;
76+
}
77+
78+
.greedy-nav a,
79+
.site-title,
80+
.masthead__menu-item a,
81+
.greedy-nav .visible-links a {
82+
color: var(--text-color) !important;
83+
}
84+
85+
.greedy-nav a:hover,
86+
.site-title:hover,
87+
.masthead__menu-item a:hover,
88+
.greedy-nav .visible-links a:hover {
89+
color: var(--link-hover-color) !important;
90+
}
91+
92+
.greedy-nav .hidden-links {
93+
background: var(--masthead-bg) !important;
94+
border: 1px solid var(--border-color) !important;
95+
}
96+
97+
.greedy-nav .hidden-links a {
98+
color: var(--text-color) !important;
99+
border-bottom: 1px solid var(--border-color) !important;
100+
}
101+
102+
.greedy-nav .hidden-links a:hover {
103+
background: var(--secondary-color) !important;
104+
color: var(--link-hover-color) !important;
105+
}
106+
107+
.greedy-nav__toggle {
108+
color: var(--text-color) !important;
109+
}
110+
111+
.greedy-nav__toggle:hover {
112+
color: var(--link-hover-color) !important;
113+
}
114+
115+
.search__toggle {
116+
color: var(--text-color) !important;
117+
}
118+
119+
.search__toggle:hover {
120+
color: var(--link-hover-color) !important;
121+
}
122+
123+
.page__footer {
124+
background: var(--footer-bg) !important;
125+
color: var(--text-muted) !important;
126+
}
127+
128+
.page__content {
129+
background: var(--bg-color) !important;
130+
}
131+
132+
.sidebar {
133+
background: var(--bg-color) !important;
134+
}
135+
136+
.notice, .notice--primary {
137+
background-color: var(--notice-bg) !important;
138+
border: 1px solid var(--border-color) !important;
139+
color: var(--text-color) !important;
140+
}
141+
142+
.notice--info {
143+
background-color: var(--notice-info-bg) !important;
144+
}
145+
146+
.notice--warning {
147+
background-color: var(--notice-warning-bg) !important;
148+
}
149+
150+
.notice--danger {
151+
background-color: var(--notice-danger-bg) !important;
152+
}
153+
154+
.notice--success {
155+
background-color: var(--notice-success-bg) !important;
156+
}
157+
158+
code, pre {
159+
background: var(--code-bg) !important;
160+
color: var(--text-color) !important;
161+
}
162+
163+
a {
164+
color: var(--link-color) !important;
165+
}
166+
167+
a:hover {
168+
color: var(--link-hover-color) !important;
169+
}
170+
171+
/* Theme toggle button styles */
172+
.theme-toggle {
173+
position: relative;
174+
display: inline-flex;
175+
align-items: center;
176+
padding: 0.5rem;
177+
background: none;
178+
border: 1px solid var(--border-color);
179+
border-radius: 4px;
180+
cursor: pointer;
181+
font-size: 1rem;
182+
color: var(--text-color);
183+
transition: all 0.3s ease;
184+
margin-left: 1rem;
185+
}
186+
187+
.theme-toggle:hover {
188+
background: var(--secondary-color);
189+
border-color: var(--link-color);
190+
}
191+
192+
.theme-toggle-icon {
193+
width: 1.2em;
194+
height: 1.2em;
195+
margin-right: 0.5rem;
196+
}
197+
198+
@media (max-width: 768px) {
199+
.theme-toggle {
200+
margin-left: 0;
201+
margin-top: 0.5rem;
202+
}
203+
}
204+
</style>
205+
206+
<!-- Dark/Light Mode JavaScript -->
207+
<script>
208+
(function() {
209+
// Theme management
210+
const THEME_KEY = 'zarr-theme';
211+
const THEME_DARK = 'dark';
212+
const THEME_LIGHT = 'light';
213+
214+
// Get saved theme or detect browser preference
215+
function getInitialTheme() {
216+
// Check browser preference first
217+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
218+
return THEME_DARK;
219+
}
220+
221+
return THEME_LIGHT;
222+
}
223+
224+
// Get theme with user preference override
225+
function getCurrentTheme() {
226+
const savedTheme = localStorage.getItem(THEME_KEY);
227+
if (savedTheme) {
228+
return savedTheme;
229+
}
230+
return getInitialTheme();
231+
}
232+
233+
// Apply theme to document
234+
function applyTheme(theme) {
235+
if (theme === THEME_DARK) {
236+
document.documentElement.setAttribute('data-theme', 'dark');
237+
} else {
238+
document.documentElement.removeAttribute('data-theme');
239+
}
240+
localStorage.setItem(THEME_KEY, theme);
241+
}
242+
243+
// Toggle between themes
244+
function toggleTheme() {
245+
const currentTheme = getCurrentTheme();
246+
const newTheme = currentTheme === THEME_DARK ? THEME_LIGHT : THEME_DARK;
247+
applyTheme(newTheme);
248+
updateToggleButton();
249+
}
250+
251+
// Update toggle button appearance
252+
function updateToggleButton() {
253+
const button = document.querySelector('.theme-toggle');
254+
if (!button) return;
255+
256+
const currentTheme = getCurrentTheme();
257+
const icon = button.querySelector('.theme-toggle-icon');
258+
const text = button.querySelector('.theme-toggle-text');
259+
260+
if (currentTheme === THEME_DARK) {
261+
icon.innerHTML = '☀️';
262+
text.textContent = 'Light';
263+
button.setAttribute('aria-label', 'Switch to light mode');
264+
} else {
265+
icon.innerHTML = '🌙';
266+
text.textContent = 'Dark';
267+
button.setAttribute('aria-label', 'Switch to dark mode');
268+
}
269+
}
270+
271+
// Initialize theme on page load
272+
function initTheme() {
273+
const initialTheme = getCurrentTheme();
274+
applyTheme(initialTheme);
275+
276+
// Wait for DOM to be ready, then update button
277+
if (document.readyState === 'loading') {
278+
document.addEventListener('DOMContentLoaded', updateToggleButton);
279+
} else {
280+
updateToggleButton();
281+
}
282+
}
283+
284+
// Listen for system theme changes
285+
if (window.matchMedia) {
286+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
287+
// Only respond to system changes if user hasn't set a preference
288+
if (!localStorage.getItem(THEME_KEY)) {
289+
const newTheme = e.matches ? THEME_DARK : THEME_LIGHT;
290+
applyTheme(newTheme);
291+
updateToggleButton();
292+
}
293+
});
294+
}
295+
296+
// Debug function - clear saved preference to test system detection
297+
function clearThemePreference() {
298+
localStorage.removeItem(THEME_KEY);
299+
const systemTheme = getInitialTheme();
300+
applyTheme(systemTheme);
301+
updateToggleButton();
302+
console.log('Theme preference cleared. Using system preference:', systemTheme);
303+
}
304+
305+
// Make functions globally available
306+
window.toggleTheme = toggleTheme;
307+
window.updateToggleButton = updateToggleButton;
308+
window.clearThemePreference = clearThemePreference;
309+
310+
// Initialize immediately
311+
initTheme();
312+
})();
313+
</script>

_includes/masthead.html

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{% capture logo_path %}{{ site.logo }}{% endcapture %}
2+
3+
<div class="masthead">
4+
<div class="masthead__inner-wrap">
5+
<div class="masthead__menu">
6+
<nav id="site-nav" class="greedy-nav">
7+
{% unless logo_path == empty %}
8+
<a class="site-logo" href="{{ '/' | relative_url }}"><img src="{{ logo_path | relative_url }}" alt="{{ site.masthead_title | default: site.title }}"></a>
9+
{% endunless %}
10+
<a class="site-title" href="{{ '/' | relative_url }}">
11+
{{ site.masthead_title | default: site.title }}
12+
{% if site.subtitle %}<span class="site-subtitle">{{ site.subtitle }}</span>{% endif %}
13+
</a>
14+
<ul class="visible-links">
15+
{%- for link in site.data.navigation.main -%}
16+
<li class="masthead__menu-item">
17+
<a href="{{ link.url | relative_url }}"{% if link.description %} title="{{ link.description }}"{% endif %}>{{ link.title }}</a>
18+
</li>
19+
{%- endfor -%}
20+
</ul>
21+
{% if site.search == true %}
22+
<button class="search__toggle" type="button">
23+
<span class="visually-hidden">{{ site.data.ui-text[site.locale].search_label | default: "Toggle search" }}</span>
24+
<i class="fas fa-search"></i>
25+
</button>
26+
{% endif %}
27+
<!-- Theme Toggle Button -->
28+
<button class="theme-toggle" type="button" onclick="toggleTheme()" aria-label="Toggle theme">
29+
<span class="theme-toggle-icon">🌙</span>
30+
<span class="theme-toggle-text">Dark</span>
31+
</button>
32+
<button class="greedy-nav__toggle hidden" type="button">
33+
<span class="visually-hidden">{{ site.data.ui-text[site.locale].menu_label | default: "Toggle menu" }}</span>
34+
<div class="navicon"></div>
35+
</button>
36+
<ul class="hidden-links hidden"></ul>
37+
</nav>
38+
</div>
39+
</div>
40+
</div>

0 commit comments

Comments
 (0)