Skip to content

Commit 777f206

Browse files
committed
feat: add dark mode support
closes cookiecutter#5629
1 parent bf2a3c5 commit 777f206

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*!
2+
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
3+
* Copyright 2011-2024 The Bootstrap Authors
4+
* Licensed under the Creative Commons Attribution 3.0 Unported License.
5+
*/
6+
7+
(() => {
8+
'use strict'
9+
10+
const getStoredTheme = () => localStorage.getItem('theme')
11+
const setStoredTheme = theme => localStorage.setItem('theme', theme)
12+
13+
const getPreferredTheme = () => {
14+
const storedTheme = getStoredTheme()
15+
if (storedTheme) {
16+
return storedTheme
17+
}
18+
19+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
20+
}
21+
22+
const setTheme = theme => {
23+
if (theme === 'auto') {
24+
document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
25+
} else {
26+
document.documentElement.setAttribute('data-bs-theme', theme)
27+
}
28+
}
29+
30+
setTheme(getPreferredTheme())
31+
32+
const showActiveTheme = (theme, focus = false) => {
33+
const themeSwitcher = document.querySelector('#bd-theme')
34+
35+
if (!themeSwitcher) {
36+
return
37+
}
38+
39+
const themeSwitcherText = document.querySelector('#bd-theme-text')
40+
const activeThemeIcon = document.querySelector('.theme-icon-active use')
41+
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
42+
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
43+
44+
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
45+
element.classList.remove('active')
46+
element.setAttribute('aria-pressed', 'false')
47+
})
48+
49+
btnToActive.classList.add('active')
50+
btnToActive.setAttribute('aria-pressed', 'true')
51+
activeThemeIcon.setAttribute('href', svgOfActiveBtn)
52+
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
53+
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
54+
55+
if (focus) {
56+
themeSwitcher.focus()
57+
}
58+
}
59+
60+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
61+
const storedTheme = getStoredTheme()
62+
if (storedTheme !== 'light' && storedTheme !== 'dark') {
63+
setTheme(getPreferredTheme())
64+
}
65+
})
66+
67+
window.addEventListener('DOMContentLoaded', () => {
68+
showActiveTheme(getPreferredTheme())
69+
70+
document.querySelectorAll('[data-bs-theme-value]')
71+
.forEach(toggle => {
72+
toggle.addEventListener('click', () => {
73+
const theme = toggle.getAttribute('data-bs-theme-value')
74+
setStoredTheme(theme)
75+
setTheme(theme)
76+
showActiveTheme(theme, true)
77+
})
78+
})
79+
})
80+
})()
81+
82+

Diff for: {{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}/templates/base.html

+32-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{% endraw %}
99
{%- endif %}{% raw %}<!DOCTYPE html>
1010
{% get_current_language as LANGUAGE_CODE %}
11-
<html lang="{{ LANGUAGE_CODE }}">
11+
<html lang="{{ LANGUAGE_CODE }}" data-bs-theme="light">
1212
<head>
1313
<meta charset="utf-8" />
1414
<meta http-equiv="x-ua-compatible" content="ie=edge" />
@@ -92,16 +92,19 @@
9292
{% if cookiecutter.frontend_pipeline == 'None' %}
9393
{% raw %}
9494
<script defer src="{% static 'js/project.js' %}"></script>
95+
<script defer src="{% static 'js/theme-toggler.js' %}"></script>
9596
{%- endraw %}
9697
{% elif cookiecutter.frontend_pipeline == 'Django Compressor' %}
9798
{% raw %}
9899
{% compress js %}
99100
<script defer src="{% static 'js/project.js' %}"></script>
101+
<script defer src="{% static 'js/theme-toggler.js' %}"></script>
100102
{% endcompress %}
101103
{%- endraw %}
102104
{% elif cookiecutter.frontend_pipeline == 'Gulp' %}
103105
{% raw %}
104106
<script defer src="{% static 'js/project.min.js' %}"></script>
107+
<script defer src="{% static 'js/theme-toggler.js' %}"></script>
105108
{%- endraw %}
106109
{% elif cookiecutter.frontend_pipeline == "Webpack" %}
107110
{% raw %}
@@ -155,6 +158,34 @@
155158
<a id="log-in-link" class="nav-link" href="{% url 'account_login' %}">{% translate "Sign In" %}</a>
156159
</li>
157160
{% endif %}
161+
<li class="nav-item dropdown">
162+
<a class="nav-link dropdown-toggle"
163+
href="#"
164+
id="themeDropdown"
165+
role="button"
166+
data-bs-toggle="dropdown"
167+
aria-expanded="false">
168+
<i class="bi bi-palette"></i>Palette
169+
</a>
170+
<ul class="dropdown-menu dropdown-menu-end"
171+
aria-labelledby="themeDropdown">
172+
<li>
173+
<button class="dropdown-item" type="button" data-bs-theme-value="light">
174+
<i class="bi bi-sun-fill me-2"></i>Light
175+
</button>
176+
</li>
177+
<li>
178+
<button class="dropdown-item" type="button" data-bs-theme-value="dark">
179+
<i class="bi bi-moon-stars-fill me-2"></i>Dark
180+
</button>
181+
</li>
182+
<li>
183+
<button class="dropdown-item" type="button" data-bs-theme-value="auto">
184+
<i class="bi bi-circle-half me-2"></i>Auto
185+
</button>
186+
</li>
187+
</ul>
188+
</li>
158189
</ul>
159190
</div>
160191
</div>

0 commit comments

Comments
 (0)