Skip to content

Commit f04fca4

Browse files
authored
Merge pull request #154 from d-v-b/feat/dark-mode
dark mode / light mode toggle
2 parents b7ee073 + fdff54a commit f04fca4

File tree

2 files changed

+380
-0
lines changed

2 files changed

+380
-0
lines changed

_includes/head/custom.html

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,343 @@
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+
/* Table styling for light/dark mode */
172+
table {
173+
background: var(--bg-color) !important;
174+
color: var(--text-color) !important;
175+
border-collapse: collapse !important;
176+
}
177+
178+
table th,
179+
table td {
180+
background: var(--bg-color) !important;
181+
color: var(--text-color) !important;
182+
border: 1px solid var(--border-color) !important;
183+
padding: 0.5rem !important;
184+
}
185+
186+
table th {
187+
background: var(--secondary-color) !important;
188+
color: var(--text-color) !important;
189+
font-weight: bold !important;
190+
}
191+
192+
table tbody tr:nth-child(even) {
193+
background: var(--secondary-color) !important;
194+
}
195+
196+
table tbody tr:hover {
197+
background: var(--notice-bg) !important;
198+
}
199+
200+
/* Ensure table links are visible */
201+
table a {
202+
color: var(--link-color) !important;
203+
}
204+
205+
table a:hover {
206+
color: var(--link-hover-color) !important;
207+
}
208+
209+
/* Theme toggle button styles */
210+
.theme-toggle {
211+
position: relative;
212+
display: inline-flex;
213+
align-items: center;
214+
padding: 0.5rem;
215+
background: none;
216+
border: 1px solid var(--border-color);
217+
border-radius: 4px;
218+
cursor: pointer;
219+
font-size: 1rem;
220+
color: var(--text-color);
221+
transition: all 0.3s ease;
222+
margin-left: 1rem;
223+
}
224+
225+
.theme-toggle:hover {
226+
background: var(--secondary-color);
227+
border-color: var(--link-color);
228+
}
229+
230+
.theme-toggle-icon {
231+
width: 1.2em;
232+
height: 1.2em;
233+
margin-right: 0.5rem;
234+
}
235+
236+
@media (max-width: 768px) {
237+
.theme-toggle {
238+
margin-left: 0;
239+
margin-top: 0.5rem;
240+
}
241+
}
242+
</style>
243+
244+
<!-- Dark/Light Mode JavaScript -->
245+
<script>
246+
(function() {
247+
// Theme management
248+
const THEME_KEY = 'zarr-theme';
249+
const THEME_DARK = 'dark';
250+
const THEME_LIGHT = 'light';
251+
252+
// Get saved theme or detect browser preference
253+
function getInitialTheme() {
254+
// Check browser preference first
255+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
256+
return THEME_DARK;
257+
}
258+
259+
return THEME_LIGHT;
260+
}
261+
262+
// Get theme with user preference override
263+
function getCurrentTheme() {
264+
const savedTheme = localStorage.getItem(THEME_KEY);
265+
if (savedTheme) {
266+
return savedTheme;
267+
}
268+
return getInitialTheme();
269+
}
270+
271+
// Apply theme to document
272+
function applyTheme(theme) {
273+
if (theme === THEME_DARK) {
274+
document.documentElement.setAttribute('data-theme', 'dark');
275+
} else {
276+
document.documentElement.removeAttribute('data-theme');
277+
}
278+
localStorage.setItem(THEME_KEY, theme);
279+
}
280+
281+
// Toggle between themes
282+
function toggleTheme() {
283+
const currentTheme = getCurrentTheme();
284+
const newTheme = currentTheme === THEME_DARK ? THEME_LIGHT : THEME_DARK;
285+
applyTheme(newTheme);
286+
updateToggleButton();
287+
}
288+
289+
// Update toggle button appearance
290+
function updateToggleButton() {
291+
const button = document.querySelector('.theme-toggle');
292+
if (!button) return;
293+
294+
const currentTheme = getCurrentTheme();
295+
const icon = button.querySelector('.theme-toggle-icon');
296+
const text = button.querySelector('.theme-toggle-text');
297+
298+
if (currentTheme === THEME_LIGHT) {
299+
icon.innerHTML = '☀️';
300+
text.textContent = 'Light';
301+
button.setAttribute('aria-label', 'Switch to light mode');
302+
} else {
303+
icon.innerHTML = '🌙';
304+
text.textContent = 'Dark';
305+
button.setAttribute('aria-label', 'Switch to dark mode');
306+
}
307+
}
308+
309+
// Initialize theme on page load
310+
function initTheme() {
311+
const initialTheme = getCurrentTheme();
312+
applyTheme(initialTheme);
313+
314+
// Wait for DOM to be ready, then update button
315+
if (document.readyState === 'loading') {
316+
document.addEventListener('DOMContentLoaded', updateToggleButton);
317+
} else {
318+
updateToggleButton();
319+
}
320+
}
321+
322+
// Listen for system theme changes
323+
if (window.matchMedia) {
324+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
325+
// Only respond to system changes if user hasn't set a preference
326+
if (!localStorage.getItem(THEME_KEY)) {
327+
const newTheme = e.matches ? THEME_DARK : THEME_LIGHT;
328+
applyTheme(newTheme);
329+
updateToggleButton();
330+
}
331+
});
332+
}
333+
334+
// Debug function - clear saved preference to test system detection
335+
function clearThemePreference() {
336+
localStorage.removeItem(THEME_KEY);
337+
const systemTheme = getInitialTheme();
338+
applyTheme(systemTheme);
339+
updateToggleButton();
340+
console.log('Theme preference cleared. Using system preference:', systemTheme);
341+
}
342+
343+
// Make functions globally available
344+
window.toggleTheme = toggleTheme;
345+
window.updateToggleButton = updateToggleButton;
346+
window.clearThemePreference = clearThemePreference;
347+
348+
// Initialize immediately
349+
initTheme();
350+
})();
351+
</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)