mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
ui: Implement a toggle menu for setting dark mode
- Add a toggle menu for selecting the color scheme. JS code largely taken from Bootstrap documentation and slightly customized. - Use local storage to store the setting for dark/light/auto. Default to auto which means browser level preference is picked up (which could be system level preference). Tests: - Appearance of the toggle menu is consistent. Check box is shown on the currently selected value. - Deleting the local storage value reverts the preference to browser set value. - Menu is collapsed at smaller screen sizes. Appearance and functionality as expected. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
0419eb02cf
commit
00a69108dd
5
debian/copyright
vendored
5
debian/copyright
vendored
@ -38,6 +38,11 @@ Copyright: Marie Van den Broeck (https://thenounproject.com/marie49/)
|
||||
Comment: https://thenounproject.com/icon/162372/
|
||||
License: CC-BY-SA-3.0
|
||||
|
||||
Files: static/themes/default/js/color-modes.js
|
||||
Copyright: 2011-2025 The Bootstrap Authors
|
||||
Comment: https://getbootstrap.com/docs/5.3/customize/color-modes/
|
||||
License: CC-BY-3.0
|
||||
|
||||
Files: plinth/modules/bepasty/static/icons/bepasty.svg
|
||||
Copyright: (c) 2014 by the Bepasty Team, see the AUTHORS file.
|
||||
Comment: https://github.com/bepasty/bepasty-server/blob/master/src/bepasty/static/app/bepasty.svg
|
||||
|
||||
@ -58,6 +58,9 @@
|
||||
<link rel="stylesheet" href="{% static user_css %}"/>
|
||||
{% endif %}
|
||||
|
||||
<!-- This script is not loaded in defer mode because it needs to run before
|
||||
page is rendered -->
|
||||
<script type="text/javascript" src="{% static 'theme/js/color-modes.js' %}"></script>
|
||||
<!-- Local link to system Bootstrap JS -->
|
||||
<script type="text/javascript" src="{% static '/javascript/popperjs2/popper.min.js' %}" defer></script>
|
||||
<script type="text/javascript" src="{% static '/javascript/bootstrap5/js/bootstrap.bundle.min.js' %}" defer></script>
|
||||
@ -148,6 +151,8 @@
|
||||
|
||||
{% include "notifications-dropdown.html" %}
|
||||
|
||||
{% include "theme-menu.html" %}
|
||||
|
||||
{% include "help-menu.html" %}
|
||||
|
||||
<li id="id_user_menu" class="nav-item dropdown">
|
||||
|
||||
46
plinth/templates/theme-menu.html
Normal file
46
plinth/templates/theme-menu.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<li id="id_theme_menu" class="dropdown nav-item">
|
||||
<a href="#" title="{% trans "Toggle theme (auto)" %}"
|
||||
class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
|
||||
role="button" aria-expanded="false" aria-haspopup="true"
|
||||
id="id_theme_menu_link">
|
||||
<span class="fa fa-adjust nav-icon" id="id_active_theme_icon"></span>
|
||||
<span class="nav-text d-md-none" id="id_toggle_theme_text">
|
||||
{% trans "Toggle theme" %}
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="id_theme_menu_link">
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="light" data-bs-icon-value="fa-sun"
|
||||
aria-pressed="false">
|
||||
<span class="fa fa-sun nav-icon me-2"></span>
|
||||
{% trans "Light" %}
|
||||
<span class="fa fa-check nav-icon ms-auto d-none"></span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="dark" data-bs-icon-value="fa-moon"
|
||||
aria-pressed="false">
|
||||
<span class="fa fa-moon nav-icon me-2"></span>
|
||||
{% trans "Dark" %}
|
||||
<span class="fa fa-check nav-icon ms-auto d-none"></span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="auto" data-bs-icon-value="fa-adjust"
|
||||
aria-pressed="true">
|
||||
<span class="fa fa-adjust nav-icon me-2"></span>
|
||||
{% trans "Auto" %}
|
||||
<span class="fa fa-check nav-icon ms-auto d-none"></span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@ -522,6 +522,10 @@ footer {
|
||||
border-bottom: var(--freedombox-navbar-color) 3px solid;
|
||||
}
|
||||
|
||||
.main-header .dropdown-menu .active .fa-check {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Breadcrumbs */
|
||||
.breadcrumb-item {
|
||||
--bs-breadcrumb-divider: ">";
|
||||
|
||||
96
static/themes/default/js/color-modes.js
Normal file
96
static/themes/default/js/color-modes.js
Normal file
@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: CC-BY-3.0
|
||||
/*
|
||||
This file is part of FreedomBox. Color mode toggler for Bootstrap's docs
|
||||
(https://getbootstrap.com/). Copyright 2011-2025 The Bootstrap Authors.
|
||||
|
||||
@licstart The following is the entire license notice for the
|
||||
JavaScript code in this page.
|
||||
|
||||
Licensed under the Creative Commons Attribution 3.0 Unported License.
|
||||
|
||||
@licend The above is the entire license notice
|
||||
for the JavaScript code in this page.
|
||||
*/
|
||||
|
||||
(() => {
|
||||
'use strict';
|
||||
|
||||
const getStoredTheme = () => localStorage.getItem('theme');
|
||||
const setStoredTheme = theme => localStorage.setItem('theme', theme);
|
||||
|
||||
const getBrowserTheme = () => {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)')
|
||||
.matches ? 'dark' : 'light';
|
||||
};
|
||||
|
||||
const getPreferredTheme = () => {
|
||||
const storedTheme = getStoredTheme();
|
||||
if (storedTheme) {
|
||||
return storedTheme;
|
||||
}
|
||||
|
||||
return getBrowserTheme();
|
||||
};
|
||||
|
||||
const setTheme = (theme) => {
|
||||
if (theme === 'auto') {
|
||||
theme = getBrowserTheme();
|
||||
}
|
||||
document.documentElement.setAttribute('data-bs-theme', theme);
|
||||
};
|
||||
|
||||
setTheme(getPreferredTheme());
|
||||
|
||||
const showActiveTheme = (theme, focus = false) => {
|
||||
const themeSwitcher = document.querySelector('#id_theme_menu_link');
|
||||
|
||||
if (!themeSwitcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
const themeSwitcherText = document.querySelector('#id_toggle_theme_text');
|
||||
const activeThemeIcon = document.querySelector('#id_active_theme_icon');
|
||||
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`);
|
||||
const iconOfActiveBtn = btnToActive.dataset.bsIconValue;
|
||||
|
||||
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
|
||||
element.classList.remove('active');
|
||||
element.setAttribute('aria-pressed', 'false');
|
||||
const iconOfBtn = element.dataset.bsIconValue;
|
||||
if (activeThemeIcon.classList.contains(iconOfBtn)) {
|
||||
activeThemeIcon.classList.remove(iconOfBtn);
|
||||
}
|
||||
});
|
||||
|
||||
btnToActive.classList.add('active');
|
||||
btnToActive.setAttribute('aria-pressed', 'true');
|
||||
activeThemeIcon.classList.add(iconOfActiveBtn);
|
||||
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`;
|
||||
themeSwitcher.setAttribute('title', themeSwitcherLabel);
|
||||
|
||||
if (focus) {
|
||||
themeSwitcher.focus();
|
||||
};
|
||||
};
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
const storedTheme = getStoredTheme();
|
||||
if (storedTheme !== 'light' && storedTheme !== 'dark') {
|
||||
setTheme(getPreferredTheme());
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
showActiveTheme(getPreferredTheme());
|
||||
|
||||
document.querySelectorAll('[data-bs-theme-value]')
|
||||
.forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
const theme = toggle.getAttribute('data-bs-theme-value');
|
||||
setStoredTheme(theme);
|
||||
setTheme(theme);
|
||||
showActiveTheme(theme, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
Loading…
x
Reference in New Issue
Block a user