feat(tour): Welcome-Banner + Tour auf Startseite, Logo-Klick zur Startseite

Drei zusammenhängende UI-Bausteine:

1) Tour-Engine ist jetzt page-agnostisch — sie liest die Stationen aus
   ``window.GWOE_TOUR_STEPS`` (pro Page hinterlegt), nicht mehr aus einem
   eingebauten Konstanten. Tour-Komponente wird per ``{% include %}``
   eingehängt; das Page-Template definiert vorher seine eigenen Steps.
   Antrag-Detail-Tour wurde entsprechend in das eigene Template gezogen.

2) Startseite (v2/screens/durchsuchen.html): „Du bist neu hier?"-Banner
   oben mit zwei Buttons — „🧭 Tour starten" und „Nein, danke". Banner
   bleibt sichtbar, bis explizit weggeklickt wird (localStorage-Flag),
   oder die Tour gestartet wird. Fünf Stationen für die Startseite:
   Marken-Block, Suche, Score-Filter + Sortierung, Antrags-Liste,
   linke Navigation.

3) Logo-Klick führt jetzt zur Startseite — sowohl in v2/base.html als
   auch in components/appshell.html. ``v2-brand`` und ``v2-brand-sub``
   sind in einen ``<a href="/">`` mit Hover-Highlight gewickelt
   (``.v2-brand-link``).

Phase 2 (ElevenLabs-Voice) ist der nächste Schritt — bisher läuft das
Audio über die Web Speech API.
This commit is contained in:
Dotty Dotter 2026-05-09 02:47:04 +02:00
parent 1c74cb8801
commit e31ee1ad07
6 changed files with 149 additions and 43 deletions

View File

@ -134,6 +134,16 @@ body.v2 :focus-visible {
height: 100vh; height: 100vh;
} }
.v2-brand-link {
display: block;
text-decoration: none;
color: inherit;
}
.v2-brand-link:hover .v2-brand,
.v2-brand-link:focus-visible .v2-brand {
color: var(--ecg-teal);
}
.v2-brand { .v2-brand {
font-family: var(--font-sans); font-family: var(--font-sans);
font-weight: 900; font-weight: 900;
@ -142,6 +152,7 @@ body.v2 :focus-visible {
color: var(--ecg-dark); color: var(--ecg-dark);
letter-spacing: 0; letter-spacing: 0;
line-height: 1.05; line-height: 1.05;
transition: color 0.12s ease;
} }
.v2-brand .grn { color: var(--ecg-green); } .v2-brand .grn { color: var(--ecg-green); }

View File

@ -25,10 +25,12 @@
<div class="v2-shell"> <div class="v2-shell">
<aside id="v2-sidebar" class="v2-sidebar"> <aside id="v2-sidebar" class="v2-sidebar">
<div class="v2-brand"> <a href="/" class="v2-brand-link" aria-label="GWÖ-Antragsprüfer · Startseite">
GWÖ-<span class="grn">ANTRAGS</span><span class="blu">PRÜFER</span> <div class="v2-brand">
</div> GWÖ-<span class="grn">ANTRAGS</span><span class="blu">PRÜFER</span>
<div class="v2-brand-sub">Matrix 2.0 · Gemeinden</div> </div>
<div class="v2-brand-sub">Matrix 2.0 · Gemeinden</div>
</a>
<nav aria-label="Hauptnavigation"> <nav aria-label="Hauptnavigation">
<div class="v2-nav-group"> <div class="v2-nav-group">

View File

@ -16,10 +16,12 @@
{# ── Sidebar ──────────────────────────────────────────────────── #} {# ── Sidebar ──────────────────────────────────────────────────── #}
<aside id="v2-sidebar" class="v2-sidebar"> <aside id="v2-sidebar" class="v2-sidebar">
<div class="v2-brand"> <a href="/" class="v2-brand-link" aria-label="GWÖ-Antragsprüfer · Startseite">
GWÖ-<span class="grn">ANTRAGS</span><span class="blu">PRÜFER</span> <div class="v2-brand">
</div> GWÖ-<span class="grn">ANTRAGS</span><span class="blu">PRÜFER</span>
<div class="v2-brand-sub">Matrix 2.0 · Gemeinden</div> </div>
<div class="v2-brand-sub">Matrix 2.0 · Gemeinden</div>
</a>
<nav aria-label="Hauptnavigation"> <nav aria-label="Hauptnavigation">

View File

@ -9,6 +9,20 @@
{% block main %} {% block main %}
{# ── Welcome-Banner: Tour-Einstieg für Erst-Besucher:innen (#185) ── #}
<div id="gwoe-welcome-banner" class="v2-kasten outline-blue gwoe-welcome-banner" hidden>
<div class="gwoe-welcome-text">
<strong>Du bist neu hier?</strong>
Soll ich dir die Seite erklären? Eine geführte Tour mit Sprachausgabe — du kannst sie jederzeit abbrechen.
</div>
<div class="gwoe-welcome-actions">
<button type="button" onclick="gwoeTourStart()" class="v2-chip primary"
id="gwoe-welcome-start">🧭 Tour starten</button>
<button type="button" onclick="gwoeWelcomeDismiss()" class="v2-chip"
id="gwoe-welcome-dismiss">Nein, danke</button>
</div>
</div>
{# ── Toolbar: Suche ──────────────────────────────────────────────── #} {# ── Toolbar: Suche ──────────────────────────────────────────────── #}
{# BL-Filter läuft jetzt über den globalen Selector in der Topbar. #} {# BL-Filter läuft jetzt über den globalen Selector in der Topbar. #}
<div class="v2-toolbar" id="v2-toolbar" role="toolbar" aria-label="Filter und Suche"> <div class="v2-toolbar" id="v2-toolbar" role="toolbar" aria-label="Filter und Suche">
@ -69,6 +83,28 @@
</p> </p>
</div> </div>
{# ── Tour-Stationen für die Startseite + Engine-Include ──────────── #}
<script>
window.GWOE_TOUR_STEPS = [
{ selector: '.v2-brand-link, .v2-brand',
title: 'Willkommen beim GWÖ-Antragsprüfer',
text: 'Diese Seite bewertet Anträge aus deutschen Parlamenten nach der Gemeinwohl-Matrix. Was sie auszeichnet: Sie schaut nicht nur, ob ein Antrag „gut klingt", sondern wie sehr er fünf Werten dient — Würde, Solidarität, Nachhaltigkeit, Gerechtigkeit und Demokratie.' },
{ selector: '#v2-search-input',
title: 'Anträge durchsuchen',
text: 'Hier findest du Anträge nach Stichwort, Drucksachen-Nummer oder Antragstellerin. Die Liste filtert sich beim Tippen mit.' },
{ selector: '.v2-toolbar:nth-of-type(2), [data-band]',
title: 'Filter nach Note',
text: 'Mit diesen Knöpfen filterst du nach Gemeinwohl-Note. Acht bis Zehn sind vorbildlich, fünf bis sieben durchwachsen, null bis vier problematisch. Daneben kannst du nach Datum, Score oder Titel sortieren.' },
{ selector: '#v2-results .v2-result-row, #v2-results',
title: 'Die Antrags-Liste',
text: 'Jede Karte zeigt einen Antrag mit seiner Note. Klick auf eine Karte öffnet die ausführliche Bewertung — dort wirst du dort dann auch noch eine eigene Tour finden, die das Detail erklärt.' },
{ selector: '.v2-nav-group, #v2-sidebar nav',
title: 'Navigation links',
text: 'Links findest du weitere Sichten. „Auswertungen" zeigt Aggregate über alle Anträge, „Stimmverhalten" die Konsistenz jeder Fraktion zwischen Wahlprogramm und tatsächlicher Stimme, „Quellen" alle indizierten Wahl- und Grundsatzprogramme — semantisch durchsuchbar.' },
];
</script>
{% include "v3/components/tour.html" %}
{% endblock %} {% endblock %}
{% block body_scripts %} {% block body_scripts %}
@ -319,5 +355,57 @@ kbd {
padding: 1px 5px; padding: 1px 5px;
color: var(--ecg-dark); color: var(--ecg-dark);
} }
.gwoe-welcome-banner {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.gwoe-welcome-banner[hidden] { display: none !important; }
.gwoe-welcome-text {
flex: 1 1 320px;
font-size: 13px;
line-height: 1.5;
color: var(--ecg-dark);
}
.gwoe-welcome-actions {
display: flex;
gap: 8px;
align-items: center;
}
.v2-chip.primary {
background: var(--ecg-teal);
color: #fff;
border-color: var(--ecg-teal);
}
.v2-chip.primary:hover { opacity: 0.92; }
</style> </style>
<script>
/* Welcome-Banner sichtbar bis Tour gestartet oder „Nein, danke".
localStorage: 'gwoe_welcome_dismissed' = '1' merkt den Schließ-Klick. */
(function () {
var KEY = 'gwoe_welcome_dismissed';
var banner = document.getElementById('gwoe-welcome-banner');
if (!banner) return;
if (localStorage.getItem(KEY) !== '1') {
banner.hidden = false;
}
window.gwoeWelcomeDismiss = function () {
try { localStorage.setItem(KEY, '1'); } catch (_) {}
banner.hidden = true;
};
// Wenn Tour gestartet wird, Banner auch wegblenden + dismissed merken.
var origStart = window.gwoeTourStart;
if (typeof origStart === 'function') {
window.gwoeTourStart = function () {
try { localStorage.setItem(KEY, '1'); } catch (_) {}
banner.hidden = true;
return origStart.apply(this, arguments);
};
}
})();
</script>
{% endblock %} {% endblock %}

View File

@ -1,16 +1,20 @@
{# ───────────────────────────────────────────────────────────────────── {# ─────────────────────────────────────────────────────────────────────
Geführte Tour durch das Antrag-Detail (#185). Geführte Tour-Engine (#185).
Was diese Komponente liefert: Was diese Komponente liefert:
- "Tour"-Button rendert in der userrow (siehe antrag_detail.html) - Spotlight-Overlay + Erklärblase + Vor/Zurück/Schließen + Mute
- Vier Stationen: Score-Hero, Matrix, Programm-Treue, Stimmverhalten - Audio via Web Speech API (de-DE, möglichst weiblich)
- Spotlight-Overlay (Klick außerhalb → Tour-Ende) - Engine: liest pro Page hinterlegte ``window.GWOE_TOUR_STEPS``
- Erklärblase mit Vor / Zurück / Schließen (Liste mit ``{selector, title, text}``), zeigt nur Stationen
- Audio: Web Speech API (Browser-eingebaute Stimme), de-DE, deren Element wirklich da ist.
stoppbar bei Schritt-Wechsel und Schließen.
Stand: Phase 1 — Browser-TTS. Phase 2 (ElevenLabs) kommt separat Pro Page:
und tauscht nur das Audio-Backend; das Tour-Skript bleibt gleich. <script>window.GWOE_TOUR_STEPS = [{selector: '...', title: '...', text: '...'}];</script>
{% include "v3/components/tour.html" %}
<button onclick="gwoeTourStart()">Tour starten</button>
Phase 1: Browser-TTS. Phase 2 (ElevenLabs) tauscht nur das
Audio-Backend; das Tour-Skript bleibt gleich.
───────────────────────────────────────────────────────────────────── #} ───────────────────────────────────────────────────────────────────── #}
<div id="gwoe-tour-overlay" class="gwoe-tour-overlay" hidden aria-hidden="true"> <div id="gwoe-tour-overlay" class="gwoe-tour-overlay" hidden aria-hidden="true">
<div id="gwoe-tour-spotlight" class="gwoe-tour-spotlight"></div> <div id="gwoe-tour-spotlight" class="gwoe-tour-spotlight"></div>
@ -136,31 +140,13 @@
<script> <script>
(function () { (function () {
// Tour-Skript: 4 Stationen. Selectoren werden zur Laufzeit aufgelöst — // Tour-Stationen kommen pro Page via window.GWOE_TOUR_STEPS.
// wenn ein Element fehlt (z.B. keine Plenum-Votes), überspringt die // Jeder Eintrag: {selector, title, text}. Selectoren werden zur Laufzeit
// Tour den Schritt automatisch. // aufgelöst — fehlt das Element (z.B. keine Plenum-Votes auf einer
const STEPS = [ // bestimmten Drucksache), überspringt die Tour den Schritt automatisch.
{ function getSteps() {
selector: '.v3-bewertung', return Array.isArray(window.GWOE_TOUR_STEPS) ? window.GWOE_TOUR_STEPS : [];
title: 'Die Gemeinwohl-Note', }
text: 'Diese große Zahl ist die Gemeinwohl-Note. Null ist destruktiv, Zehn vorbildlich. Sie zeigt, wie sehr der Antrag dem Allgemeinwohl dient. Daneben steht die Empfehlung — Unterstützen, Überarbeiten oder Ablehnen.',
},
{
selector: '.v3-matrix-grid, .v3-matrix, [class*="matrix"]',
title: 'Die Gemeinwohl-Matrix',
text: 'Dieses Raster prüft fünf Werte: Würde, Solidarität, Nachhaltigkeit, Gerechtigkeit und Demokratie. Pro Wert wird geschaut, wie der Antrag fünf Berührungsgruppen betrifft — von Lieferanten bis zur Gesellschaft als Ganzes. Grün heißt: der Antrag fördert diesen Wert. Rot: er widerspricht ihm.',
},
{
selector: '.v3-fraktionen',
title: 'Programm-Treue pro Fraktion',
text: 'Hier sehen Sie, wie gut der Antrag zum Wahl- und Parteiprogramm jeder Fraktion passt. Die Zahl rechts ist der Programm-Score von Null bis Zehn. Ein Klick darauf zeigt die Begründung mit Zitaten aus dem Programm.',
},
{
selector: '.v3-vote-section',
title: 'Stimmverhalten und Marker',
text: 'Hier sehen Sie, wie die Fraktionen tatsächlich abgestimmt haben. Das Warnschild neben einer Fraktion bedeutet Heuchelei: Sie stimmt mit Nein, obwohl der Antrag exakt zu ihrem eigenen Wahlprogramm passt. Das Ausrufezeichen markiert Opportunismus: Ja-Stimme bei einem Antrag, der dem eigenen Programm widerspricht.',
},
];
let _tourIdx = 0; let _tourIdx = 0;
let _resolvedSteps = []; // STEPS gefiltert auf vorhandene Elemente let _resolvedSteps = []; // STEPS gefiltert auf vorhandene Elemente
@ -197,7 +183,7 @@
function resolveSteps() { function resolveSteps() {
_resolvedSteps = []; _resolvedSteps = [];
for (const s of STEPS) { for (const s of getSteps()) {
// Erstes Match aus comma-separated Selector-Liste. // Erstes Match aus comma-separated Selector-Liste.
const parts = s.selector.split(',').map(x => x.trim()); const parts = s.selector.split(',').map(x => x.trim());
let el = null; let el = null;

View File

@ -548,6 +548,23 @@
</div> </div>
</div> </div>
{# Tour-Stationen für Antrag-Detail (Engine im include unten) #}
<script>
window.GWOE_TOUR_STEPS = [
{ selector: '.v3-bewertung',
title: 'Die Gemeinwohl-Note',
text: 'Diese große Zahl ist die Gemeinwohl-Note. Null ist destruktiv, Zehn vorbildlich. Sie zeigt, wie sehr der Antrag dem Allgemeinwohl dient. Daneben steht die Empfehlung — Unterstützen, Überarbeiten oder Ablehnen.' },
{ selector: '.v3-matrix-grid, .v3-matrix, [class*="matrix"]',
title: 'Die Gemeinwohl-Matrix',
text: 'Dieses Raster prüft fünf Werte: Würde, Solidarität, Nachhaltigkeit, Gerechtigkeit und Demokratie. Pro Wert wird geschaut, wie der Antrag fünf Berührungsgruppen betrifft — von Lieferanten bis zur Gesellschaft als Ganzes. Grün heißt: der Antrag fördert diesen Wert. Rot: er widerspricht ihm.' },
{ selector: '.v3-fraktionen',
title: 'Programm-Treue pro Fraktion',
text: 'Hier sehen Sie, wie gut der Antrag zum Wahl- und Parteiprogramm jeder Fraktion passt. Die Zahl rechts ist der Programm-Score von Null bis Zehn. Ein Klick darauf zeigt die Begründung mit Zitaten aus dem Programm.' },
{ selector: '.v3-vote-section',
title: 'Stimmverhalten und Marker',
text: 'Hier sehen Sie, wie die Fraktionen tatsächlich abgestimmt haben. Das Warnschild neben einer Fraktion bedeutet Heuchelei: Sie stimmt mit Nein, obwohl der Antrag exakt zu ihrem eigenen Wahlprogramm passt. Das Ausrufezeichen markiert Opportunismus: Ja-Stimme bei einem Antrag, der dem eigenen Programm widerspricht.' },
];
</script>
{% include "v3/components/tour.html" %} {% include "v3/components/tour.html" %}
</div>{# .v3-page #} </div>{# .v3-page #}