diff --git a/api/src/static/chart-utils.js b/api/src/static/chart-utils.js index a8eced2..4220b70 100644 --- a/api/src/static/chart-utils.js +++ b/api/src/static/chart-utils.js @@ -36,18 +36,22 @@ export function fmtHours(seconds) { } // Accessibility: versteckte Datentabelle je Diagramm (WCAG 1.1.1). -// Das aria-label des Canvas fasst nur zusammen — diese sr-only- macht -// jeden Einzel-Messpunkt fuer Screenreader zugaenglich. Canvas verweist via -// aria-describedby auf die Tabelle. Idempotent: ersetzt bei Re-Render. +// Das aria-label des Canvas fasst nur zusammen — diese Tabelle macht jeden +// Einzel-Messpunkt fuer Screenreader zugaenglich. Ein sr-only-Wrapper begrenzt +// ihre intrinsische Breite. Canvas verweist via aria-describedby auf die +// Tabelle. Idempotent: ersetzt bei Re-Render. // createElement statt innerHTML (kein XSS mit dynamischen Werten). export function buildChartDataTable(canvas, labels, datasets, captionText = '') { if (!canvas?.id) return; const tableId = `${canvas.id}-table`; - document.getElementById(tableId)?.remove(); + const wrapperId = `${tableId}-wrapper`; + document.getElementById(wrapperId)?.remove(); + const wrapper = document.createElement('div'); + wrapper.id = wrapperId; + wrapper.className = 'sr-only'; const table = document.createElement('table'); table.id = tableId; - table.className = 'sr-only'; if (captionText) { const caption = document.createElement('caption'); @@ -84,6 +88,7 @@ export function buildChartDataTable(canvas, labels, datasets, captionText = '') }); table.appendChild(tbody); - canvas.insertAdjacentElement('afterend', table); + wrapper.appendChild(table); + canvas.insertAdjacentElement('afterend', wrapper); canvas.setAttribute('aria-describedby', tableId); } diff --git a/api/src/static/style.css b/api/src/static/style.css index a88b85c..7592883 100644 --- a/api/src/static/style.css +++ b/api/src/static/style.css @@ -71,6 +71,34 @@ html:not(.dark) { border: 1px solid var(--card-border); box-shadow: var(--card-shadow); } +.card-hero { + border-color: color-mix(in srgb, var(--accent) 18%, var(--card-border)); +} +.card-metric { + box-shadow: 0 1px 3px rgba(0,0,0,.18), 0 3px 12px rgba(0,0,0,.1); +} +.card-collection, +.card-status, +.card-insight { + box-shadow: none; +} +.card-collection { + background: var(--surface); +} +.card-form { + box-shadow: 0 1px 3px rgba(0,0,0,.14); +} +.card-insight { + line-height: 1.6; +} +.prose-layout { + display: flex; + flex-direction: column; + gap: var(--sp-6); + color: var(--muted); + font-size: var(--text-sm); + line-height: 1.625; +} .card h2 { font-size: var(--text-xs); color: var(--muted); @@ -187,6 +215,16 @@ html:not(.dark) .tab-btn.active { color: #047857; border-bottom-color: #047857; .topbar-btn:hover { background: var(--surface-2); color: var(--text); } .topbar-btn-danger:hover { color: var(--red); background: color-mix(in srgb, var(--red) 12%, transparent); } .topbar-divider { width: 1px; height: 20px; background: var(--card-border); margin: 0 var(--sp-2); } +@media (max-width: 640px) { + .dash-topbar .dash-shell { flex-wrap: wrap; } + .dash-topbar nav[aria-label="Hauptnavigation"] { + flex: 1 0 100%; + min-width: 0; + flex-wrap: wrap; + } + .dash-topbar .topbar-btn { padding: 5px 8px; } + .dash-topbar .topbar-divider { display: none; } +} /* ── View Toolbar + Segmented Time-Range ───────────────────────────────── */ .view-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; } @@ -861,12 +899,12 @@ html:not(.dark) .recommendation-block { background: rgba(16,185,129,.07); } from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } } -.hero-card-wrap > .card, -.tab-panel > .card { animation: card-in var(--dur-base) var(--ease-out) backwards; } -.tab-panel > .card:nth-child(1) { animation-delay: 0ms; } -.tab-panel > .card:nth-child(2) { animation-delay: 40ms; } -.tab-panel > .card:nth-child(3) { animation-delay: 80ms; } -.tab-panel > .card:nth-child(4) { animation-delay: 120ms; } +.hero-card-wrap > .card-hero, +.tab-panel > .card-metric { animation: card-in var(--dur-base) var(--ease-out) backwards; } +.tab-panel > .card-metric:nth-child(1) { animation-delay: 0ms; } +.tab-panel > .card-metric:nth-child(2) { animation-delay: 40ms; } +.tab-panel > .card-metric:nth-child(3) { animation-delay: 80ms; } +.tab-panel > .card-metric:nth-child(4) { animation-delay: 120ms; } /* ── Skeleton-Screens — perceived performance, 300-ms-Regel ───────────────────── Wrapper bleibt 300 ms unsichtbar; kommt der echte Inhalt vorher, wird das Markup diff --git a/api/src/templates/accessibility.html b/api/src/templates/accessibility.html index dbca8b0..c6e8210 100644 --- a/api/src/templates/accessibility.html +++ b/api/src/templates/accessibility.html @@ -2,7 +2,7 @@ {% block title %}PulseBase — Barrierefreiheitserklärung{% endblock %} {% block content %}
-
+ -
+

1. Geltungsbereich

diff --git a/api/src/templates/account_delete_confirm.html b/api/src/templates/account_delete_confirm.html index a80a4d8..fe834a7 100644 --- a/api/src/templates/account_delete_confirm.html +++ b/api/src/templates/account_delete_confirm.html @@ -6,7 +6,7 @@

PulseBase

Kontolöschung bestätigen

-
+
{% if error %}
{{ error }}

diff --git a/api/src/templates/account_delete_pending.html b/api/src/templates/account_delete_pending.html index 740f87b..ee5e459 100644 --- a/api/src/templates/account_delete_pending.html +++ b/api/src/templates/account_delete_pending.html @@ -6,7 +6,7 @@

PulseBase

Kontolöschung

-
+
{% if error %}
{{ error }}
{% else %} diff --git a/api/src/templates/activity.html b/api/src/templates/activity.html index 9c3bda8..649b120 100644 --- a/api/src/templates/activity.html +++ b/api/src/templates/activity.html @@ -7,7 +7,7 @@ {% endblock %} {% block content %} -
+
← Dashboard

@@ -18,7 +18,7 @@

-