e1d570992e
Drei zusammengehörige Teile in einem Repository: - homeassistant/ Das fertige, im Einsatz befindliche Home-Assistant-Panel (panel_custom Custom Element + pyscript-Backend). Echte Fahrzeug- und Personendaten (fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl, Tankbelege) bleiben per .gitignore außen vor; die anonymisierte Vorlage fahrzeugprofil.example.json ist mit dabei. - design-system/ Eigenständige React-Komponentenbibliothek (@audi-dash/ui), die die visuelle Sprache des Panels nachbildet - ohne Audi-Markenzeichen und ohne die lizenzierte Hausschrift. Dient als Grundlage für Claude Design. War bis hierher ein eigenes Repository und ist in dieses eingeschmolzen worden. - companion-app/ Datenschicht der neuen App DataMetric360 (iOS/Android via Capacitor, zusätzlich als Iframe im HA-Dashboard). Noch ohne Oberfläche: REST- und WebSocket-Zugriff auf Home Assistant plus Warteschlange für Änderungen ohne Netz. Ersetzt das eingespritzte hass-Objekt, das nur innerhalb des HA-Frontends existiert. Dazu die Projektdokumentation: SPECIFICATION.md (Ist-Stand des Panels), COMPANION_APP_ARCHITECTURE.md (Architekturentscheidungen der neuen App), AUDIT_2026-08-10.md, DESIGN_BRIEF_DATAMETRIC360.md und der ursprüngliche Bauauftrag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
117 lines
11 KiB
Markdown
117 lines
11 KiB
Markdown
# Audi Dashboard — Full Audit (Code · UI/UX · Cross-Platform)
|
||
|
||
**Date:** 2026-08-10
|
||
**Scope:** `homeassistant/www/audi-dashboard-app.js` (3130 lines), `audi-dashboard.css` (715 lines), `audi-dashboard-panel.js`, as currently deployed (post Design-Audit-merge, post scroll-bug fix).
|
||
**Method:** static review (Grep/Read across the full source), computed WCAG contrast checks, live verification of prior findings in the `audi_ha_test` Docker container. Not a full manual screen-reader pass (no VoiceOver/TalkBack device in this environment) — flagged explicitly below where that matters.
|
||
|
||
---
|
||
|
||
## 1. Platform verdict — can it run on iPhone, Android, Home Assistant?
|
||
|
||
| Environment | Verdict | Why |
|
||
|---|---|---|
|
||
| **Home Assistant** (browser, sidebar panel) | ✅ Works — this is its native environment | Built as a `panel_custom` custom element; `hass` is injected directly by HA's frontend. |
|
||
| **iPhone / Android, via the official Home Assistant Companion App** | ✅ Works today, no changes needed | The Companion App is a WebView wrapper around the exact same HA frontend — it opens this panel the identical way a desktop browser does, `hass` injection included. The app is already tuned for this (safe-area insets, iOS-style pull-to-refresh, 44×44px tap targets, `prefers-reduced-motion` support). This is very likely how "iPhone" was meant in the original brief (§4.2 already assumes the Companion App). |
|
||
| **iPhone / Android, as a standalone app (Capacitor-wrapped or plain browser tab pointed at a public URL)** | ❌ Does not work, and can't with this file as-is | The entire data layer is `hass.states[...]` / `hass.callService(...)`. There is no REST call, no WebSocket client, no token/auth flow, and no `Capacitor.*` usage anywhere in the file — outside of an HA frontend context, `hass` is simply `undefined` and the app never renders past the loading tile. This is exactly the gap the separately-discussed companion-app architecture (FMM003 → Traccar → HA → REST/WebSocket API client) is meant to fill; that work is a different, not-yet-built app, not a mode of this file. |
|
||
| **Generic iframe embed** (e.g. in a non-HA page) | ❌ Same reason | Needs `hass` injected the `panel_custom` way, not a generic postMessage/REST contract. |
|
||
|
||
**One-line answer:** yes on Home Assistant and yes on iPhone/Android *through the HA Companion App*; no as an independent native/standalone app — that requires the separate REST/WebSocket-backed companion app already discussed, not a change to this file.
|
||
|
||
---
|
||
|
||
## 2. Findings
|
||
|
||
Severity: 🔴 real bug/breakage · 🟠 should fix · 🟡 minor/polish
|
||
|
||
### ✅ FIXED — 🔴 iOS Safari/WKWebView auto-zoom on form inputs
|
||
**Where:** `audi-dashboard.css:462` — `.feld input, .feld select, .notiz { font-size: 15px; }`
|
||
**Issue:** iOS Safari (and the Companion App's WKWebView) auto-zooms the viewport when a focused input's computed font-size is under 16px. Every text/number field in the app (trip forms, fill-up forms, settings, service-book entries) is 15px — so tapping any field on an iPhone zooms the page in, and the user has to manually zoom back out after every field.
|
||
**Fix:** raise to `font-size: 16px` (or `max(16px, …)` if the 15px look matters, add a `transform: scale(0.9375)` trick — not recommended, adds complexity for no real benefit). Simplest: just bump to 16px; the visual difference is negligible at this size.
|
||
|
||
### ❌ RETRACTED (false positive) — Dark-theme label/eyebrow contrast
|
||
**Originally claimed:** `--fg3: #657081` on `--canvas: #161b23` ≈ 3.45:1, failing WCAG AA.
|
||
**Why it was wrong:** that value came from `SPECIFICATION.md`'s §2 color table, not from the actual CSS.
|
||
The shipped value is **`--fg3: #8a94a3`** — the Design Audit's own finding **A2** already fixed exactly
|
||
this, and `audi-dashboard.css` documents it inline: *"A2: von #657081 (3,0:1 auf --tile) auf 4,6:1
|
||
angehoben."* Recomputed against the real value: **5.6:1 on `--canvas`, 4.9:1 on `--tile`** — passes AA
|
||
on both surfaces. Nothing to fix in the CSS.
|
||
**The real defect this exposed:** `SPECIFICATION.md`'s color table was stale (documenting pre-merge
|
||
values) — the same doc-staleness class as the theming finding below. **Fixed:** the `--fg3` row in
|
||
`SPECIFICATION.md` §2 now carries the shipped value.
|
||
**Lesson for future audits of this project:** read `audi-dashboard.css` directly for token values;
|
||
`SPECIFICATION.md` predates the Design-Audit merge in several places.
|
||
|
||
### ✅ FIXED — 🟠 No visible keyboard-focus indicator outside form fields
|
||
**Where:** `audi-dashboard.css:472` is the *only* `:focus` rule in the file, and it's scoped to `input/select/.notiz`.
|
||
**Issue:** every button, tile, tab, and swipe-row is focusable (native `<button>`/interactive elements) but has no `:focus`/`:focus-visible` style at all — a keyboard or switch-control user (Bluetooth keyboard + iPad/desktop browser via Tailscale, or iOS Full Keyboard Access) gets no visual indication of where focus is. This is invisible on a touch-only phone, which is presumably why it wasn't caught, but the app is also reachable from a browser tab (desktop, Tailscale).
|
||
**Fix:** add a generic `:focus-visible` rule (e.g. `outline: 2px solid var(--red); outline-offset: 2px;`) on `button, .tile, .tab, [tabindex]` — keeps mouse/touch users unaffected (`:focus-visible` only fires for keyboard focus in modern browsers) while fixing keyboard access.
|
||
|
||
### 🟠 Swipe-to-delete has no non-gesture affordance
|
||
**Where:** `audi-dashboard-app.js:1872` (`.swipe-delete`/`.swipe-loeschen`), used for trip and fill-up rows.
|
||
**Issue:** the delete action only becomes reachable (as an actual, labeled `<button>Löschen</button>`) *after* a horizontal Pointer Event drag reveals it. A screen-reader user (VoiceOver swipe-navigation, not a raw drag gesture) or anyone using switch control has no way to trigger that reveal, so trip/fill-up rows are **not deletable** for them at all. Service-book rows already use a plain always-visible delete button (per SPECIFICATION.md §3) — the same pattern doesn't exist for trips/fills.
|
||
**Fix:** smallest viable fix is a long-press or a small always-present "…" affordance per row that reveals the same action sheet already built for other destructive actions (`sheet`/`bestaetigen()`), rather than requiring the drag gesture as the only path.
|
||
|
||
### 🟡 Popup dismiss-by-tapping-outside isn't keyboard-reachable
|
||
**Where:** `audi-dashboard-app.js:1460, 1477, 1576` — `.bildmenu-catcher` / overlay-close divs.
|
||
**Issue:** these are plain `<div>`s with click handlers, not buttons — fine for touch/mouse, invisible to keyboard/AT navigation. Low severity since these are popups tied to photo management, a small surface, and likely have another (or should have another) explicit close control — worth a quick manual check rather than assumed broken.
|
||
|
||
### 🟡 Leaflet map requires public internet, independent of the Tailscale tunnel
|
||
**Where:** SPECIFICATION.md §3 confirms — Leaflet is loaded from a CDN at runtime, not bundled.
|
||
**Issue:** on a phone connected only via Tailscale with no other route to the public internet (e.g. cellular data off, only the VPN interface up), the trip-detail map silently fails to load tiles. Not a bug in the code as such, but worth knowing as a real-world failure mode on iPhone/Android specifically (desktop Tailscale setups usually have a normal internet route too).
|
||
**Fix (optional):** self-host the Leaflet JS/CSS bundle under `/local/`, keep tiles CDN-based (tiles are unavoidably remote) — removes the JS/CSS-loading failure mode at least.
|
||
|
||
### ✅ FIXED — 🟡 Stale doc: SPECIFICATION.md says theme has "no persistence"
|
||
**Where:** `SPECIFICATION.md:136` — "**No persistence** — theme resets to 'nacht' on every full reload; no `localStorage` use was found."
|
||
**Issue:** this is now false — the Design-Audit merge added `localStorage`-backed theme persistence (`THEME_KEY`, `audi-dashboard-app.js:2157-2163`) and tab-label persistence (`SCHRIFT_KEY`, line 1377/2856). Not a code bug — just a doc that predates the merge and will mislead the next person/session reading it.
|
||
**Fix:** update SPECIFICATION.md §3 "Theming" to reflect the current `localStorage` behavior.
|
||
|
||
### 🟡 Touch targets — no issue found (noted for completeness)
|
||
Checked `.themebtn`, `.profilbtn`, `.zahnrad` (all 44×44px, Apple HIG minimum) and the tab bar (icon + label, generous padding). This was clearly already addressed by the Design Audit — nothing to fix here.
|
||
|
||
### 🟡 Icon-only buttons — no issue found (noted for completeness)
|
||
`#back`, `.profilbtn`, and every `.zahnrad` settings-gear button carry a correct, specific `aria-label`; decorative SVGs inside them are `aria-hidden`. Tab bar buttons pair the icon with a visible text `<span>`. This is solid — better than average for a project this size.
|
||
|
||
---
|
||
|
||
## 3. What was done (2026-08-10)
|
||
|
||
**Applied and verified live in the `audi_ha_test` container** (CSS version `1786377886`):
|
||
|
||
1. **Form-input font-size 15px → 16px** (`audi-dashboard.css`, `.feld input, .feld select, .notiz`).
|
||
Verified: all 14 inputs on the Settings/Einrichten form compute to `16px`, none below the iOS
|
||
auto-zoom threshold.
|
||
2. **Global `:focus-visible` rule added** — `outline: 2px solid var(--red); outline-offset: 2px;`.
|
||
Verified: the rule parses into the shadow-root stylesheet (confirmed via CSSOM, not just file
|
||
content), a focused button reports `matches(":focus-visible") === true`, and the computed outline
|
||
resolves to `solid 2px rgb(245, 5, 55)` at a 2px offset. Using `:focus-visible` rather than `:focus`
|
||
keeps mouse/touch interaction visually unchanged.
|
||
|
||
Both carry inline German comments explaining *why*, matching the file's existing convention.
|
||
|
||
**Verification caveat:** the browser screenshot tool failed throughout this session ("Browser pane is
|
||
not displayed"), so confirmation is structural (computed styles, CSSOM, live DOM) rather than visual.
|
||
The structural evidence is conclusive for both fixes, but nobody has actually *looked* at the focus
|
||
ring yet — worth a glance next time the pane cooperates.
|
||
|
||
**Also fixed (documentation):** `SPECIFICATION.md`'s stale `--fg3` color-table value and its stale
|
||
"no persistence" theming paragraph — both now match the shipped code.
|
||
|
||
## 4. Remaining / not done
|
||
|
||
1. **Swipe-to-delete accessible fallback** (🟠) — the only real feature work in this audit, not a
|
||
one-liner. Left open deliberately: it needs a decision on *which* affordance (long-press, a
|
||
persistent "…" button per row, or routing through the existing Action Sheet).
|
||
2. **Popup close-by-tap-outside `<div>`s** (🟡) — needs a quick manual check before deciding whether
|
||
it's genuinely broken for keyboard/AT users.
|
||
3. **Self-hosting Leaflet** (🟡) — optional; removes a Tailscale-only-network failure mode.
|
||
|
||
Note on how much further to invest here: `COMPANION_APP_ARCHITECTURE.md` records the decision that this
|
||
panel is eventually superseded by the responsive companion app. These three items may be better carried
|
||
into that app's component work than retrofitted into this file.
|
||
|
||
## 5. Platform targets — unchanged
|
||
|
||
No changes needed for the Home-Assistant or HA-Companion-App targets; both already work. The
|
||
"standalone iPhone/Android app" question is answered by `COMPANION_APP_ARCHITECTURE.md`, not by
|
||
anything fixable in this file.
|