From 026f6ab02e180d5768f1bf8c5ed2436c56ca9b92 Mon Sep 17 00:00:00 2001 From: Flare Starfall Date: Mon, 9 Mar 2026 10:42:17 +0100 Subject: [PATCH] Update theme switcher --- VERSION.md | 2 +- messages.pot | 20 ++-- starfall/web/__init__.py | 2 +- translations/en/LC_MESSAGES/messages.mo | Bin 7478 -> 7390 bytes translations/en/LC_MESSAGES/messages.po | 70 +++++++------- web/static/js/color-switcher.js | 80 ---------------- web/static/js/theme-switcher.js | 89 ++++++++++++++++++ .../scss/starfall/_bootstrap-overrides.scss | 12 +-- .../scss/starfall/_site-backgrounds.scss | 10 ++ web/templates/base.jinja | 25 ++--- web/templates/home.jinja | 10 +- 11 files changed, 165 insertions(+), 155 deletions(-) delete mode 100644 web/static/js/color-switcher.js create mode 100644 web/static/js/theme-switcher.js diff --git a/VERSION.md b/VERSION.md index 3c5942d..8de0802 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -0.1.0-alpha.2 \ No newline at end of file +0.1.0-alpha.3 \ No newline at end of file diff --git a/messages.pot b/messages.pot index 270e762..a5b4cfd 100644 --- a/messages.pot +++ b/messages.pot @@ -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 \n" "Language-Team: LANGUAGE \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 "" diff --git a/starfall/web/__init__.py b/starfall/web/__init__.py index 71075ee..846a3d0 100644 --- a/starfall/web/__init__.py +++ b/starfall/web/__init__.py @@ -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 diff --git a/translations/en/LC_MESSAGES/messages.mo b/translations/en/LC_MESSAGES/messages.mo index 87d6cf118255f798cb289b5e5fc3a12104b5a683..23c0344172c0e4d63d85de81ee037043eb35ceda 100644 GIT binary patch delta 581 zcmYk(IY`4$9LMoz+Y}WqJh6%f2Tvlg#iP;511BBS$;p;Ju%a!L4k}2|MR5{>gM*Xk zB8pIPadZ){#Z6EL7bl@8ii`Mt_y!+IKKbPx`M>1Mc?xHq%6;2{R>xV-`JuwUc9SPW zDft#F@DT%;!zwKD2vLbmsB$k>0=eAcS-%jm6?eh+vXfR6I?W3Slrv6O+UueG&hGL2MN< zbTLT`LQIwt6N!kWvEg%`yNH{d_j%`@d(ZuzbMnLIv2?!9cPOl8tQC91y0Dyw4{O#d zQVr)|J&Z#=y9ERA!0;Fb@pHz%H+(hxh7H66MIu$O2a-1}!~E#QN!SjTUAN2ClUA\n" -"Language-Team: en \n" "Language: en\n" +"Language-Team: en \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 "© Team Starfall" @@ -126,8 +129,8 @@ msgstr "" "

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.

\n" "

Our privacy policy is structured as follows:

\n" @@ -138,9 +141,9 @@ msgstr "" #: web/templates/imprint.jinja:36 msgid "page.imprint.sec2.body" msgstr "" -"

The party responsible for this website (the „controller“) for " -"purposes of data protection law is:
Robert Bäs-Fischlmair a.k.a. " -"Flare Starfall
Phone: +43 (0) 677 62890651
Email: The party responsible for this website (the „controller“) for purposes" +" of data protection law is:
Robert Bäs-Fischlmair a.k.a. Flare " +"Starfall
Phone: +43 (0) 677 62890651
Email:
flare@theflare.at

" #: web/templates/imprint.jinja:37 @@ -150,20 +153,20 @@ msgstr "" "below, users and data subjects have the right

\n" "
    \n" "
  • 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);
  • \n" -"
  • to correct or complete incorrect or incomplete data (cf. also Art. " -"16 GDPR);
  • \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);\n" +"
  • to correct or complete incorrect or incomplete data (cf. also Art. 16" +" GDPR);
  • \n" "
  • 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;
  • 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);
  • 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).
  • \n" +"provided by them and to have the same transmitted to other " +"providers/controllers (cf. also Art. 20 GDPR);
  • 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).
  • \n" "
\n" "

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.

\n" -"

Likewise, under Art. 21 GDPR, users and data subjects have " -"the right to object to the controller'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 " +"

Likewise, under Art. 21 GDPR, users and data subjects have the" +" right to object to the controller'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.

" #: 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.\n" +"which you visited our site is additionally recorded if an error " +"occurs.

\n" "

The data thus collected will be temporarily stored, but not in " "association with any other of your data.

\n" "

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 "" -"

Whatever you were looking for cannot be found here.
You may want " -"to start over.

" +"

Whatever you were looking for cannot be found here.
You may want to" +" start over.

" + diff --git a/web/static/js/color-switcher.js b/web/static/js/color-switcher.js deleted file mode 100644 index e7aa709..0000000 --- a/web/static/js/color-switcher.js +++ /dev/null @@ -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) - }) - }) - }) -})() diff --git a/web/static/js/theme-switcher.js b/web/static/js/theme-switcher.js new file mode 100644 index 0000000..d167705 --- /dev/null +++ b/web/static/js/theme-switcher.js @@ -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); +} \ No newline at end of file diff --git a/web/static/scss/starfall/_bootstrap-overrides.scss b/web/static/scss/starfall/_bootstrap-overrides.scss index e51630f..14331f1 100644 --- a/web/static/scss/starfall/_bootstrap-overrides.scss +++ b/web/static/scss/starfall/_bootstrap-overrides.scss @@ -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); -} \ No newline at end of file +$font-size-base: 18px; \ No newline at end of file diff --git a/web/static/scss/starfall/_site-backgrounds.scss b/web/static/scss/starfall/_site-backgrounds.scss index 66cc271..8f785e0 100644 --- a/web/static/scss/starfall/_site-backgrounds.scss +++ b/web/static/scss/starfall/_site-backgrounds.scss @@ -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; diff --git a/web/templates/base.jinja b/web/templates/base.jinja index af93d46..1896a81 100644 --- a/web/templates/base.jinja +++ b/web/templates/base.jinja @@ -22,29 +22,30 @@