diff --git a/CHANGELOG.md b/CHANGELOG.md index 7444c31..5a35365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.40.0 — 2026-10-28 — Navigation : section Administration + bouton Dark/Light (#51, #52) + +- **Accordéon (#51)** : la section Administration ne fermait jamais — le JS posait `nav-closed-admin`, mais `style.css` ne définissait les règles de fermeture que pour les 4 autres sections. Les règles admin sont ajoutées (fermeture et rotation du chevron) : l'accordéon redevient exclusif. +- **Bouton Dark/Light (#52)** : bouton « ☀️ Mode clair / 🌙 Mode sombre » dans l'en-tête applicatif, qui bascule `data-theme` sur `` et mémorise le choix (localStorage, appliqué avant le rendu par l'amorce de `_header.html`). Thème clair sur les jetons draco (fond #f2f2f0, surfaces blanches/grises, accent rose conservé, second ton assombri pour les liens), barre Fusion et panneau Spark qui suivent. Défaut inchangé : sombre. `nav.js` est désormais versionné (`?v=`), comme dashboard.js depuis la 0.38.1. + ## 0.39.0 — 2026-10-28 — Thème du site : look agent-dev / barre Fusion (#49) - **Re-thème complet** : `style.css` passe sur les jetons draco-barre (Console v7.2) — fond #101211, surfaces #1a1c1a / #242624 / #20221f, bordure #2e302c, textes #eceee9 / #a6aba2 / #8b9089, accent rose Spark #c2437e (second ton #e078a8). Fond plat, plus de halos ni de dégradés violet→bleu, y compris les valeurs de repli et l'écran de connexion. diff --git a/app/config.py b/app/config.py index dc3ed59..2a756a6 100644 --- a/app/config.py +++ b/app/config.py @@ -177,7 +177,7 @@ from urllib.parse import urlparse BASE_DIR = Path(__file__).resolve().parent.parent -APP_VERSION = "0.39.0" +APP_VERSION = "0.40.0" def _env_bool(name: str, default: bool) -> bool: diff --git a/app/templates/_header.html b/app/templates/_header.html index 77b64c9..8910a5d 100644 --- a/app/templates/_header.html +++ b/app/templates/_header.html @@ -1,3 +1,15 @@ + {% include "_draco_bar.html" %} {# ── Cadre applicatif partagé (issue #16, refonte 0.16.0) ───────── Barre latérale unique pour TOUTES les pages applicatives : @@ -141,6 +153,7 @@ 🔗 Gitea ↗ {% endif %} + - + diff --git a/static/css/style.css b/static/css/style.css index fc75eeb..ce2e32b 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1298,11 +1298,13 @@ body.sidebar-collapsed .side-search { margin-left: 0; } body.nav-closed-principal [data-group="principal"] .side-acc, body.nav-closed-rituels [data-group="rituels"] .side-acc, body.nav-closed-pilotage [data-group="pilotage"] .side-acc, -body.nav-closed-outils [data-group="outils"] .side-acc { grid-template-rows: 0fr; } +body.nav-closed-outils [data-group="outils"] .side-acc, +body.nav-closed-admin [data-group="admin"] .side-acc { grid-template-rows: 0fr; } body.nav-closed-principal [data-group="principal"] .side-chevron, body.nav-closed-rituels [data-group="rituels"] .side-chevron, body.nav-closed-pilotage [data-group="pilotage"] .side-chevron, -body.nav-closed-outils [data-group="outils"] .side-chevron { transform: rotate(-90deg); } +body.nav-closed-outils [data-group="outils"] .side-chevron, +body.nav-closed-admin [data-group="admin"] .side-chevron { transform: rotate(-90deg); } @media (min-width: 901px) { /* Rail replié : pas d'accordéon, toutes les icônes visibles. */ body.sidebar-collapsed .side-acc { grid-template-rows: 1fr !important; } @@ -1791,3 +1793,36 @@ body.sidebar-collapsed .ws-switch { display: none; } .ms-form input:focus, .ms-form textarea:focus { outline: none; border-color: var(--accent); } .ms-hint { color: var(--muted, #a6aba2); font-size: 0.78rem; } .ms-form-actions { display: flex; gap: 8px; } + +/* ── Thème clair (#52) : jetons draco-barre (clair par défaut). + html[data-theme="light"] prime sur le :root sombre et sur le + prefers-color-scheme de la barre Fusion. Le sombre reste le + défaut (les gabarits portent data-theme="dark", le JS du + thème ne bascule que sur choix mémorisé). */ +html[data-theme="light"] { + --bg: #f2f2f0; --surface: #ffffff; --surface-2: #e9eae6; --surface-3: #f6f6f3; + --border: #dedfd9; --border-strong: #c9cbc2; + --text: #1b1d1a; --text-2: #5c615a; --text-3: #787d75; + --accent: #c2437e; --accent-2: #a92e63; + --shadow: 0 8px 28px rgba(0, 0, 0, 0.12); + --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.08); + color-scheme: light; +} +html[data-theme="light"] .sidebar { background: rgba(255, 255, 255, 0.96); } +html[data-theme="light"] .app-header { background: rgba(255, 255, 255, 0.85); } +/* Lien actif de la barre latérale : fond rose translucide sur + fond clair — le texte blanc du thème sombre y serait illisible. */ +html[data-theme="light"] .side-link.active { color: #1b1d1a; } +/* Menu ⋯ des tuiles : jetons clairs explicites, pour primer sur + le prefers-color-scheme sombre du système. */ +html[data-theme="light"] .card-menu { + --menu-bg: #ffffff; --menu-bg-2: #e9eae6; --menu-ink: #1b1d1a; + --menu-ink-2: #5c615a; --menu-line: #dedfd9; +} +/* Barre Fusion : jetons clairs explicites, pour primer sur son + @media (prefers-color-scheme: dark). Le panneau Spark suit + via son MutationObserver sur data-theme (draco-panneau.js). */ +html[data-theme="light"] .draco-bar { + --bg: #f2f2f0; --surface: #ffffff; --surface-2: #e9eae6; --surface-3: #f6f6f3; + --ink: #1b1d1a; --ink-2: #5c615a; --ink-3: #787d75; --line: #dedfd9; +} diff --git a/static/js/nav.js b/static/js/nav.js index 29e2622..59bc0d7 100644 --- a/static/js/nav.js +++ b/static/js/nav.js @@ -34,6 +34,26 @@ if (window.innerWidth > 900) closeDrawer(); }); + /* Bascule Dark/Light (#52) : le choix est mémorisé et + l'amorce de _header.html l'a déjà appliqué avant le rendu. + Ici, on ne fait que synchroniser le libellé et le clic. */ + var themeBtn = document.getElementById('theme-toggle'); + function syncThemeBtn(theme) { + if (!themeBtn) return; + var light = theme === 'light'; + themeBtn.textContent = light ? '🌙 Mode sombre' : '☀️ Mode clair'; + themeBtn.setAttribute('aria-pressed', light ? 'true' : 'false'); + } + if (themeBtn) { + syncThemeBtn(document.documentElement.getAttribute('data-theme') || 'dark'); + themeBtn.addEventListener('click', function () { + var next = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light'; + document.documentElement.setAttribute('data-theme', next); + try { localStorage.setItem('kanbante-theme', next); } catch (e) { /* non-fatal */ } + syncThemeBtn(next); + }); + } + if (collapseBtn) { collapseBtn.addEventListener('click', function () { var collapsed = document.body.classList.toggle('sidebar-collapsed'); diff --git a/tests/test_nav_theme.py b/tests/test_nav_theme.py new file mode 100644 index 0000000..1c4c29e --- /dev/null +++ b/tests/test_nav_theme.py @@ -0,0 +1,37 @@ +"""Navigation (#51) et bascule Dark/Light (#52). + +L'accordéon de la barre latérale omettait la section admin : +le JS posait nav-closed-admin, mais aucun style ne la suivait. +Le thème clair, lui, doit être atteignable par un bouton et +mémorisé, avec les jetons draco clairs. +""" + + +def test_admin_accordion_closes(client, fake_gitea): + css = client.get("/static/css/style.css").text + assert 'body.nav-closed-admin [data-group="admin"] .side-acc' in css + assert 'body.nav-closed-admin [data-group="admin"] .side-chevron' in css + + +def test_theme_toggle_present_and_versioned(client, fake_gitea): + page = client.get("/") + assert 'id="theme-toggle"' in page.text + assert "Mode clair" in page.text + assert "/static/js/nav.js?v=" in page.text + + +def test_theme_js_contract(client, fake_gitea): + js = client.get("/static/js/nav.js").text + assert "kanbante-theme" in js + assert "theme-toggle" in js + assert "data-theme" in js + + +def test_light_theme_tokens(client, fake_gitea): + css = client.get("/static/css/style.css").text + assert 'html[data-theme="light"]' in css + assert "--bg: #f2f2f0;" in css + assert "--surface: #ffffff;" in css + assert "--accent-2: #a92e63;" in css + assert 'html[data-theme="light"] .draco-bar' in css + assert 'html[data-theme="light"] .card-menu' in css