Update theme switcher

This commit is contained in:
Flare Starfall 2026-03-09 10:42:17 +01:00
parent 42fbd6eeea
commit 026f6ab02e
11 changed files with 165 additions and 155 deletions

View File

@ -1 +1 @@
0.1.0-alpha.2
0.1.0-alpha.3

View File

@ -6,16 +6,16 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Starfall 0.1.0-alpha.2\n"
"Project-Id-Version: Starfall 0.1.0-alpha.3\n"
"Report-Msgid-Bugs-To: flare@theflare.at\n"
"POT-Creation-Date: 2026-03-09 01:32+0100\n"
"POT-Creation-Date: 2026-03-09 09:57+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
"Generated-By: Babel 2.18.0\n"
#: web/templates/base.jinja:6
msgid "base.meta.title.empty"
@ -41,19 +41,23 @@ msgstr ""
msgid "base.label.toggle_nav"
msgstr ""
#: web/templates/base.jinja:50
#: web/templates/base.jinja:47
msgid "base.label.theme.btn"
msgstr ""
#: web/templates/base.jinja:52
msgid "base.label.theme.light"
msgstr ""
#: web/templates/base.jinja:53
#: web/templates/base.jinja:55
msgid "base.label.theme.dark"
msgstr ""
#: web/templates/base.jinja:56
#: web/templates/base.jinja:58
msgid "base.label.theme.auto"
msgstr ""
#: web/templates/base.jinja:82
#: web/templates/base.jinja:84
msgid "base.label.copy"
msgstr ""
@ -113,7 +117,7 @@ msgstr ""
msgid "error.404.title"
msgstr ""
#: web/templates/errors/not_found.jinja:10
#: web/templates/errors/not_found.jinja:9
msgid "error.404.body"
msgstr ""

View File

@ -5,7 +5,7 @@ from inspect import isclass
from pkgutil import iter_modules
from typing import final
from flask import Blueprint, Flask, g, request
from flask import Blueprint, Flask, request
from flask_assets import Bundle, Environment
from flask_babel import Babel
from livereload import Server

View File

@ -7,17 +7,16 @@ msgid ""
msgstr ""
"Project-Id-Version: Starfall 0.1.0-alpha.2\n"
"Report-Msgid-Bugs-To: flare@theflare.at\n"
"POT-Creation-Date: 2026-03-09 01:32+0100\n"
"POT-Creation-Date: 2026-03-09 09:57+0100\n"
"PO-Revision-Date: 2026-03-09 01:34+0100\n"
"Last-Translator: Flare Starfall <flare@theflare.at>\n"
"Language-Team: en <LL@li.org>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Generated-By: Babel 2.10.3\n"
"X-Generator: Poedit 3.8\n"
"Generated-By: Babel 2.18.0\n"
#: web/templates/base.jinja:6
msgid "base.meta.title.empty"
@ -43,19 +42,23 @@ msgstr "Starfall - Logo"
msgid "base.label.toggle_nav"
msgstr "Open menu"
#: web/templates/base.jinja:50
#: web/templates/base.jinja:47
msgid "base.label.theme.btn"
msgstr "Theme"
#: web/templates/base.jinja:52
msgid "base.label.theme.light"
msgstr "Light"
#: web/templates/base.jinja:53
#: web/templates/base.jinja:55
msgid "base.label.theme.dark"
msgstr "Dark"
#: web/templates/base.jinja:56
#: web/templates/base.jinja:58
msgid "base.label.theme.auto"
msgstr "Auto"
#: web/templates/base.jinja:82
#: web/templates/base.jinja:84
msgid "base.label.copy"
msgstr "&copy; Team Starfall"
@ -126,8 +129,8 @@ msgstr ""
"<p>The following privacy policy is intended to inform you in particular "
"about the type, scope, purpose, duration, and legal basis for the "
"processing of such data either under our own control or in conjunction "
"with others. We also inform you below about the third-party components "
"we use to optimize our website and improve the user experience which may "
"with others. We also inform you below about the third-party components we"
" use to optimize our website and improve the user experience which may "
"result in said third parties also processing data they collect and "
"control.</p>\n"
"<p>Our privacy policy is structured as follows:</p>\n"
@ -138,9 +141,9 @@ msgstr ""
#: web/templates/imprint.jinja:36
msgid "page.imprint.sec2.body"
msgstr ""
"<p>The party responsible for this website (the „controller“) for "
"purposes of data protection law is:<br>Robert Bäs-Fischlmair a.k.a. "
"Flare Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a "
"<p>The party responsible for this website (the „controller“) for purposes"
" of data protection law is:<br>Robert Bäs-Fischlmair a.k.a. Flare "
"Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a "
"href=\"mailto:flare@theflare.at\">flare@theflare.at</a></p>"
#: web/templates/imprint.jinja:37
@ -150,20 +153,20 @@ msgstr ""
"below, users and data subjects have the right</p>\n"
"<ul>\n"
"<li>to confirmation of whether data concerning them is being processed, "
"information about the data being processed, further information about "
"the nature of the data processing, and copies of the data (cf. also Art. "
"15 GDPR);</li>\n"
"<li>to correct or complete incorrect or incomplete data (cf. also Art. "
"16 GDPR);</li>\n"
"information about the data being processed, further information about the"
" nature of the data processing, and copies of the data (cf. also Art. 15 "
"GDPR);</li>\n"
"<li>to correct or complete incorrect or incomplete data (cf. also Art. 16"
" GDPR);</li>\n"
"<li>to the immediate deletion of data concerning them (cf. also Art. 17 "
"DSGVO), or, alternatively, if further processing is necessary as "
"stipulated in Art. 17 Para. 3 GDPR, to restrict said processing per Art. "
"18 GDPR; </li> <li>to receive copies of the data concerning them and/or "
"provided by them and to have the same transmitted to other providers/"
"controllers (cf. also Art. 20 GDPR);</li> <li>to file complaints with "
"the supervisory authority if they believe that data concerning them is "
"being processed by the controller in breach of data protection "
"provisions (see also Art. 77 GDPR).</li>\n"
"provided by them and to have the same transmitted to other "
"providers/controllers (cf. also Art. 20 GDPR);</li> <li>to file "
"complaints with the supervisory authority if they believe that data "
"concerning them is being processed by the controller in breach of data "
"protection provisions (see also Art. 77 GDPR).</li>\n"
"</ul>\n"
"<p>In addition, the controller is obliged to inform all recipients to "
"whom it discloses data of any such corrections, deletions, or "
@ -171,10 +174,10 @@ msgstr ""
"GDPR. However, this obligation does not apply if such notification is "
"impossible or involves a disproportionate effort. Nevertheless, users "
"have a right to information about these recipients.</p>\n"
"<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have "
"the right to object to the controller&apos;s future processing of their "
"data pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an "
"objection to data processing for the purpose of direct advertising is "
"<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the"
" right to object to the controller&apos;s future processing of their data"
" pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an objection to "
"data processing for the purpose of direct advertising is "
"permissible.</strong></p>"
#: web/templates/imprint.jinja:38
@ -190,8 +193,8 @@ msgstr ""
"ensure a secure and stable website: These server log files record the "
"type and version of your browser, operating system, the webpages on our "
"site visited, and the date and time of your visit. The IP address from "
"which you visited our site is additionally recorded if an error occurs.</"
"p>\n"
"which you visited our site is additionally recorded if an error "
"occurs.</p>\n"
"<p>The data thus collected will be temporarily stored, but not in "
"association with any other of your data.</p>\n"
"<p>The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. Our "
@ -206,8 +209,9 @@ msgstr ""
msgid "error.404.title"
msgstr "404 Not Found"
#: web/templates/errors/not_found.jinja:10
#: web/templates/errors/not_found.jinja:9
msgid "error.404.body"
msgstr ""
"<p>Whatever you were looking for cannot be found here.<br>You may want "
"to <a href=\"/\">start over</a>.</p>"
"<p>Whatever you were looking for cannot be found here.<br>You may want to"
" <a href=\"/\">start over</a>.</p>"

View File

@ -1,80 +0,0 @@
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2025 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
*/
(() => {
'use strict'
const getStoredTheme = () => localStorage.getItem('theme')
const setStoredTheme = theme => localStorage.setItem('theme', theme)
const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
const setTheme = theme => {
if (theme === 'auto') {
document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
} else {
document.documentElement.setAttribute('data-bs-theme', theme)
}
}
setTheme(getPreferredTheme())
const showActiveTheme = (theme, focus = false) => {
const themeSwitcher = document.querySelector('#bd-theme')
if (!themeSwitcher) {
return
}
const themeSwitcherText = document.querySelector('#bd-theme-text')
const activeThemeIcon = document.querySelector('.theme-icon-active use')
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
element.classList.remove('active')
element.setAttribute('aria-pressed', 'false')
})
btnToActive.classList.add('active')
btnToActive.setAttribute('aria-pressed', 'true')
activeThemeIcon.setAttribute('href', svgOfActiveBtn)
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
themeSwitcher.setAttribute('aria-label', 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)
})
})
})
})()

View File

@ -0,0 +1,89 @@
"use strict";
class S_ThemeSwitcher {
static attach() {
window.S_ThemeSwitcher = new S_ThemeSwitcher();
}
constructor() {
this.setTheme(this.getStoredTheme() || this.getPreferredTheme());
this.showActiveTheme();
document.querySelectorAll("[data-bs-theme-value]").forEach(function (button) {
button.addEventListener("click", () => this.manageThemeClick(button, true));
}, this);
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
const storedTheme = this.getStoredTheme();
if (storedTheme !== "light" && storedTheme !== "dark") {
const theme = this.getColorTheme();
const themeButton = document.querySelector("[data-bs-theme-value=\"" + theme + "\"]");
this.manageThemeClick(themeButton);
}
});
const breakpointMd = window.getComputedStyle(document.body).getPropertyValue("--bs-breakpoint-md");
const matchMd = window.matchMedia("(min-width: " + breakpointMd + ")");
this.manageThemeText(matchMd.matches);
matchMd.addEventListener("change", (ev) => this.manageThemeText(ev.matches));
}
getStoredTheme() {
return localStorage.getItem("theme");
}
setStoredTheme(theme) {
localStorage.setItem("theme", theme);
}
getPreferredTheme() {
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
getColorTheme() {
const theme = this.getStoredTheme();
return ('auto' === theme) ? this.getPreferredTheme() : theme;
}
setTheme(theme) {
document.documentElement.setAttribute("data-bs-theme", ("auto" === theme) ? this.getPreferredTheme() : theme);
}
showActiveTheme(focus = false) {
const themeSwitcher = document.querySelector("#theme");
if (!themeSwitcher) { return; }
const themeSwitcherIcon = themeSwitcher.querySelector("i");
const activeTheme = this.getColorTheme();
const activeThemeButton = document.querySelector("[data-bs-theme-value=\"" + activeTheme + "\"]");
const activeThemeButtonIcon = activeThemeButton.querySelector("i");
themeSwitcherIcon.classList = activeThemeButtonIcon.classList;
if (focus) {
themeSwitcher.focus();
}
}
manageThemeClick(button, focus = false) {
const theme = button.getAttribute("data-bs-theme-value");
this.setStoredTheme(theme);
this.setTheme(theme);
this.showActiveTheme(focus);
}
manageThemeText(matches) {
const themeSwitcher = document.querySelector("#theme");
if (!themeSwitcher) { return; }
const themeSwitcherText = themeSwitcher.querySelector("span");
themeSwitcherText.classList.toggle("visually-hidden", matches);
}
}
if (document.readyState !== "loading") {
S_ThemeSwitcher.attach();
} else {
document.addEventListener("DOMContentLoaded", S_ThemeSwitcher.attach);
}

View File

@ -1,12 +1,2 @@
$font-family-sans-serif: 'Overpass', system-ui, -apple-system, sans-serif;
$font-size-base: 18px;
[data-bs-theme="light"] {
--site-background: linear-gradient(to bottom right, rgb(80, 160, 220), rgb(114, 165, 255));
--nav-background: rgba(211, 164, 255, 0.3);
}
[data-bs-theme="dark"] {
--site-background: linear-gradient(to bottom right, rgb(70, 81, 110), rgb(16, 20, 60));
--nav-background: rgba(28, 29, 54, 0.4);
}
$font-size-base: 18px;

View File

@ -1,3 +1,13 @@
[data-bs-theme="light"] {
--site-background: linear-gradient(to bottom right, rgb(220, 230, 255), rgb(210, 240, 255));
--nav-background: rgba(0, 0, 0, 0.2);
}
[data-bs-theme="dark"] {
--site-background: linear-gradient(to bottom right, rgb(70, 81, 110), rgb(16, 20, 60));
--nav-background: rgba(28, 29, 54, 0.4);
}
@mixin site-backgrounds(){
background: var(--site-background);
background-repeat: no-repeat;

View File

@ -22,29 +22,30 @@
<header class="sticky-top">
<nav class="navbar navbar-expand-md">
<div class="container-fluid">
<div class="container-fluid mx-2">
<a class="navbar-brand" href="/">
<img src="{{url_for('static', filename='img/logo.png')}}" alt="{{_('base.label.logo_alt')}}" />
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
<button class="me-auto navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="{{_('base.label.toggle_nav')}}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<div class="collapse navbar-collapse d-md-flex flex-row justify-content-between" id="navbarSupportedContent">
<ul class="navbar-nav">
{# %block title: Page title. #}
{%block navLinks%}
{%include 'components/nav_public.jinja'%}
{%endblock%}
</ul>
<ul class="navbar-nav">
<li class="nav-menu dropdown">
<button id="theme" class="btn btn-link nav-link dropdown-toggle" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-circle-half"></i>
<span class="visually-hidden">{{_('base.label.theme.btn')}}</span>
</button>
<ul class="dropdown-menu">
<ul class="dropdown-menu dropdown-menu-end">
<li>
<button class="dropdown-item" type="button" data-bs-theme-value="light">
<i class="bi bi-brightness-high-fill"></i>&nbsp;{{_('base.label.theme.light')}}
@ -78,17 +79,17 @@
<footer class="fixed-bottom">
<nav class="navbar">
<div class="container-fluid">
<small>{{_('base.label.copy')}} {{bp.date.year}}</small>
<ul class="navbar-nav">
<div class="container-fluid mx-2 row">
<small class="col">{{_('base.label.copy')}} {{bp.date.year}}</small>
<ul class="col navbar-nav d-flex flex-row justify-content-end">
{%include 'components/nav_foot.jinja'%}
</ul>
</div>
</nav>
</footer>
<script type="text/javascript" src="{{url_for('static', filename='js/bootstrap.bundle.min.js')}}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/color-switcher.js')}}"></script>
<script type="text/javascript" src="{{url_for('static', filename='js/bootstrap.bundle.min.js')}}" defer></script>
<script type="text/javascript" src="{{url_for('static', filename='js/theme-switcher.js')}}" defer></script>
</body>
</html>

View File

@ -1,9 +1 @@
{%extends 'base.jinja'%}
{%block body%}
<div class="container" id="content">
<div class="d-flex flex-column">
<h1>Starfall</h1>
</div>
</div>
{%endblock%}
{%extends 'base.jinja'%}