Paritaetsrunden 2 und 3, Designpruefung umgesetzt (2026.8.30.8)

Drei zusammenhaengende Runden, alle live bei 375x812 gegen audi_ha_test
geprueft und in beiden Codebasen angewandt.

Paritaetsrunde 2: der gemeinsame Rahmen war das eigentliche Problem -
Seitenrand, Kachelabstaende, Kopfleiste, Tableiste und vier Bausteine des
Design-Systems trugen noch den Stand vor der iOS-Entscheidung. Dazu sieben
Bildschirme neu aufgebaut. Zwei Panel-Fehler dabei mitbehoben: teaser()
zeigte unter "Letzte Fahrt" den aeltesten Datensatz, und "Daten bearbeiten"
war ein toter Knopf.

Paritaetsrunde 3: das Panel hat nie Audi Type gerendert. @font-face in einem
Shadow Root wird ignoriert - Schriftschnitte registriert der Browser pro
Dokument, nie pro Shadow Tree. Die drei Schnitte liegen jetzt in
audi-dashboard-schriften.css und werden ins Dokument gehaengt. Damit erledigt
sich eine ganze Reihe von "die Schrift sieht anders aus"-Eindruecken: die
beiden Anwendungen zeigten tatsaechlich verschiedene Schriften.
Ausserdem "Daten bearbeiten" im Panel gebaut und portiert, das Zeilenmenue
entfernt und die letzten neun Bildschirme angeglichen.

Designpruefung: die fuenf Punkte der Reihenfolge. Beim vierten hat das Messen
den Befund veraendert - gezaehlt waren die deklarierten Groessen, wirksam war
laengst eine saubere Sieben-Schritt-Skala mit 27 Ausreissern; die sind jetzt
auf den naechsten Schritt gezogen, keiner verschiebt sich um mehr als 1px.

Zuletzt: die Standortvorschau zeichnete die falsche Nadel (das Panel wechselt
den Icon-Satz ab 34px, die App nahm immer den grossen), und der Kopfabstand
der App ist auf den sicheren Bereich reduziert - die 56px des Panels liegen
dort unter der Kopfleiste von Home Assistant, in der nativen Huelle steht
darueber nichts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 18:31:41 +02:00
parent 25c0d073c4
commit 4a58021670
64 changed files with 4130 additions and 1432 deletions
@@ -1,41 +1,64 @@
/* Maße live am Panel gemessen (.acc-h in audi-dashboard.css + iOS-Auflage):
13px Polster, 56px Mindesthöhe, 12px Abstand, Titel 16px, Wert 12,5px und
einzeilig. */
.ads-acc-h {
display: flex;
align-items: center;
gap: var(--sp-2);
gap: var(--sp-3);
width: 100%;
min-height: 56px;
background: none;
border: none;
font-family: inherit;
text-align: left;
padding: 12px 0;
padding: 13px 0;
cursor: pointer;
border-bottom: 1px solid var(--line);
transition: background .15s;
}
.ads-acc-h:active {
background: var(--tile-2);
}
.ads-acc-h__k {
flex: 1;
font-size: 15px;
font-size: 16px;
color: var(--fg);
}
/* Ohne eigene Regel erbt <small> den UA-Wert "smaller" (0,833x) - im
Akkordeonkopf sind das 10,4px und damit unter der 11px-Untergrenze, die
dieses Projekt aus den Apple-Richtlinien uebernommen hat. */
.ads-acc-h__v small { font-size: 11px; }
.ads-acc-h__v {
font-size: 11.5px;
flex: 0 0 auto;
font-size: 12.5px;
color: var(--fg2);
text-align: right;
white-space: nowrap;
}
/* Quadratisch, nicht 6x10: die Technik aus zwei Rahmenkanten plus 45°-Drehung
ergibt nur bei gleich langen Kanten einen sauberen Pfeil — sonst sieht sie
aus wie ein Häkchen. Genau dieser Fehler wurde im Panel am 2026-08-13
behoben und war hier stehen geblieben. */
.ads-mark {
width: 6px;
height: 10px;
width: 8px;
height: 8px;
flex: 0 0 auto;
border-right: 1.4px solid var(--fg3);
border-bottom: 1.4px solid var(--fg3);
transform: rotate(-45deg) scaleY(1);
transform: rotate(-45deg);
transition: transform .15s, border-color .15s;
}
/* Ein Chevron, eine Farbe — nur die Drehung zeigt den Zustand. Rot bleibt
Akzent/destruktiv, nie Navigationsfarbe (dieselbe Regel wie bei der
Menüleiste und dem Zurück-Pfeil). */
.ads-acc-h[aria-expanded="true"] .ads-mark {
border-color: var(--red);
transform: rotate(45deg) scaleY(1);
transform: rotate(45deg);
}
.ads-acc-b {
@@ -39,10 +39,12 @@ export function Accordion({
return (
<div className={classes.join(" ")}>
{/* Der Pfeil steht links vor dem Titel, wie `.acc-h` im Panel
(`<span class="mark"></span><span class="k">…`). */}
<button type="button" className="ads-acc-h" onClick={toggle} aria-expanded={isOpen}>
<span className="ads-mark" aria-hidden />
<span className="ads-acc-h__k">{title}</span>
{summary !== undefined && <span className="ads-acc-h__v">{summary}</span>}
<span className="ads-mark" aria-hidden />
</button>
<div className={isOpen ? "ads-acc-b ads-acc-b--on" : "ads-acc-b"}>{children}</div>
</div>
@@ -18,7 +18,7 @@ button.ads-leaf {
}
.ads-leaf__k {
font-size: 13.5px;
font-size: 13px;
color: var(--fg);
display: flex;
flex-direction: column;
@@ -1,20 +1,39 @@
/* Werte 1:1 aus der iOS-Auflage des Panels (`.aktion` in
audi-dashboard-ios.css): gefüllte Fläche ohne Rahmen, 16px normale Schrift,
keine Großschreibung, 14px Radius. Hier stand noch die Optik von vor der
iOS-Entscheidung vom 2026-08-13 — 11px, gesperrt, GROSSGESCHRIEBEN, nur
Umriss, roter Rahmen beim Überfahren. Das betraf jeden Knopf der App
("SPEICHERN", "+ POSITION HINZUFÜGEN", ...). */
.ads-action {
display: block;
width: 100%;
font-family: inherit;
font-size: 11px;
letter-spacing: .14em;
text-transform: uppercase;
font-size: 16px;
font-weight: 400;
letter-spacing: 0;
text-transform: none;
color: var(--fg);
background: none;
border: 1px solid var(--line-strong);
border-radius: var(--r-pill);
padding: 14px;
background: var(--ios-fill);
border: 0;
border-radius: 14px;
padding: 15px;
min-height: 48px;
margin-top: 18px;
cursor: pointer;
transition: background .15s, filter .15s, transform .1s, opacity .15s;
}
/* `.aktion + .aktion` im Panel: zwei Knöpfe untereinander rücken enger. */
.ads-action + .ads-action {
margin-top: 10px;
}
.ads-action:hover:not(:disabled) {
border-color: var(--red);
background: var(--tile-2);
}
.ads-action:active:not(:disabled) {
transform: scale(.985);
}
.ads-action:disabled {
@@ -22,13 +41,25 @@
cursor: default;
}
/* Hauptaktion: graue Fläche, kein Rot — Rot bleibt destruktiven Aktionen
vorbehalten (Nutzerentscheidung 2026-08-16). */
.ads-action--primary {
background: var(--fg);
color: var(--canvas);
}
.ads-action--primary:hover:not(:disabled) {
background: var(--fg);
filter: brightness(1.08);
}
.ads-action--destructive {
background: var(--ios-fill);
color: var(--bad);
border-color: var(--bad);
}
.ads-action--destructive:hover:not(:disabled) {
background: var(--bad);
color: var(--fg);
border-color: var(--bad);
color: #fff;
filter: none;
}
@@ -3,7 +3,8 @@ import * as React from "react";
export interface ActionButtonProps {
children: React.ReactNode;
onClick?: () => void;
variant?: "default" | "destructive";
/** "primary" is the source app's `.aktion.primaer` - a filled main action. */
variant?: "default" | "primary" | "destructive";
disabled?: boolean;
type?: "button" | "submit";
className?: string;
@@ -18,6 +19,7 @@ export function ActionButton({
className,
}: ActionButtonProps) {
const classes = ["ads-action"];
if (variant === "primary") classes.push("ads-action--primary");
if (variant === "destructive") classes.push("ads-action--destructive");
if (className) classes.push(className);
return (
+4 -1
View File
@@ -11,7 +11,10 @@
}
.ads-fig__unit {
font-size: .32em;
/* Relativ zur Zahl, aber nie unter der Lesbarkeitsgrenze — wie `.fig .unit`
im Panel. Ohne die Untergrenze rutschte die Einheit an der 20px-Zahl des
Kilometerstands auf 6,4px (live gemessen), im Panel sind es dort 11px. */
font-size: max(11px, .32em);
font-weight: 400;
letter-spacing: normal;
color: var(--fg2);
@@ -24,6 +24,12 @@
border-radius: 50%;
}
/* Der Platzhalter im Panel ist eine gefüllte Fläche ohne Rahmen
(`.bildbox.bild-fehlt .platzhalter{border:0;background:var(--ios-fill);
border-radius:var(--r-tile)}` in der iOS-Auflage), mit 13px-Bezeichnung in
--fg2 und 12px-Zusatz in --fg3. Hier stand noch ein gestrichelter Rahmen mit
sehr kleiner, gesperrter Beschriftung und einem Zusatz in Schreibmaschinen-
schrift. */
.ads-platzhalter {
width: 100%;
height: 100%;
@@ -31,12 +37,13 @@
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
border: 1px dashed var(--line-strong);
gap: 7px;
background: var(--ios-fill);
border: 0;
border-radius: inherit;
color: var(--fg3);
text-align: center;
padding: var(--sp-2);
padding: var(--sp-3);
}
.ads-platzhalter__icon svg {
@@ -46,13 +53,12 @@
}
.ads-platzhalter__label {
font-size: 10.5px;
letter-spacing: .02em;
color: var(--fg3);
font-size: 13px;
letter-spacing: normal;
color: var(--fg2);
}
.ads-platzhalter__hint {
font-family: ui-monospace, monospace;
font-size: 9px;
font-size: 12.5px;
color: var(--fg3);
}
@@ -18,7 +18,7 @@ const DEFAULT_ICON = (
<path
fill="currentColor"
fillRule="evenodd"
d="M4 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V5Zm2 0v14h12V5H6Zm3 3a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm-1 8 3-3 2 2 3-4 3 5H8Z"
d="m23.35 1.35-22 22-.7-.7 22-22zM1 3h17.5v1H2v16H1zm21 11.85V4h1v17H6v-1h16v-3.77l-3.24-3.08-4.57 4.48-2.85-2.84.7-.7 2.15 2.14 4.56-4.47 3.25 3.1ZM4 8.5a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0M6.5 7a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3"
clipRule="evenodd"
/>
</svg>
+9 -5
View File
@@ -1,13 +1,17 @@
/* `.pill` in der iOS-Auflage des Panels: gefüllte Fläche ohne Rahmen, 12px
normale Schrift, keine Großschreibung. Hier stand noch die Optik von vor der
iOS-Entscheidung (10px, gesperrt, GROSSGESCHRIEBEN, nur Umriss). */
.ads-pill {
display: inline-flex;
align-items: center;
font-size: 10px;
letter-spacing: .14em;
text-transform: uppercase;
font-size: 12.5px;
letter-spacing: .01em;
text-transform: none;
color: var(--fg2);
border: 1px solid var(--line-strong);
background: var(--ios-fill);
border: 0;
border-radius: var(--r-pill);
padding: 6px 12px;
padding: 5px 11px;
}
.ads-pill--work {
+1 -1
View File
@@ -32,7 +32,7 @@
border: none;
color: var(--fg);
font-family: inherit;
font-size: 13.5px;
font-size: 13px;
padding: 10px 4px;
cursor: pointer;
}
@@ -1,7 +1,11 @@
/* Live am Panel gemessen: die iOS-Auflage hebt den Balken von 4px auf 6px und
färbt die Spur mit --ios-fill statt --line (`.balken{height:6px;
background:var(--ios-fill)}` in audi-dashboard-ios.css). Hier stand noch der
Wert aus dem Basis-Stylesheet, das im Panel nie zum Zug kommt. */
.ads-balken {
height: 4px;
height: 6px;
border-radius: var(--r-pill);
background: var(--line);
background: var(--ios-fill);
overflow: hidden;
}
@@ -1,14 +1,32 @@
/* Maße 1:1 vom Panel (.status in audi-dashboard.css/-ios.css: min-height 44px,
padding 14px 0 2px, Titel 16px, Punkt 10px). Sie waren hier auf 0/14px/11px
geblieben - die Zeile stand damit sichtbar enger und kleiner als im Panel. */
.ads-status {
display: flex;
align-items: center;
gap: var(--sp-3);
width: 100%;
min-height: 44px;
background: none;
border: none;
padding: 0;
padding: 14px 0 2px;
text-align: left;
font-family: inherit;
cursor: default;
color: inherit;
transition: opacity .15s;
}
button.ads-status:active {
opacity: .55;
}
.ads-status__go {
width: 6px;
height: 10px;
margin-left: auto;
flex: 0 0 auto;
color: var(--fg3);
}
button.ads-status {
@@ -16,8 +34,8 @@ button.ads-status {
}
.ads-dot {
width: 11px;
height: 11px;
width: 10px;
height: 10px;
border-radius: 50%;
flex: 0 0 auto;
}
@@ -32,7 +50,7 @@ button.ads-status {
}
.ads-status__t {
font-size: 14px;
font-size: 16px;
color: var(--fg);
}
@@ -4,11 +4,13 @@ export interface StatusRowProps {
status: "ok" | "warn" | "bad";
title: React.ReactNode;
subtitle?: React.ReactNode;
/** Trailing chevron, marking the row as a link to a detail view. */
chevron?: boolean;
onClick?: () => void;
className?: string;
}
export function StatusRow({ status, title, subtitle, onClick, className }: StatusRowProps) {
export function StatusRow({ status, title, subtitle, chevron, onClick, className }: StatusRowProps) {
const classes = ["ads-status"];
if (className) classes.push(className);
const Tag: React.ElementType = onClick ? "button" : "div";
@@ -23,6 +25,18 @@ export function StatusRow({ status, title, subtitle, onClick, className }: Statu
<span className="ads-status__t">{title}</span>
{subtitle !== undefined && <span className="ads-status__s">{subtitle}</span>}
</span>
{chevron && (
<svg className="ads-status__go" viewBox="0 0 6 10" aria-hidden>
<polyline
points="1,1 5,5 1,9"
fill="none"
stroke="currentColor"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)}
</Tag>
);
}
@@ -3,6 +3,15 @@
overflow: hidden;
}
/* 13px in normaler Schreibweise wie `.swipe-loeschen` im Panel — hier stand
noch 11px GROSSGESCHRIEBEN mit Sperrung (derselbe Vor-iOS-Stil wie bei
Knöpfen und Pillen).
`visibility:hidden`, bis wirklich gewischt wird: Zeilenhöhen sind gebrochen
(z. B. 66,28px), dadurch fällt die Kante des Inhalts nicht aufs
Gerätepixelraster und der rote Knopf blitzt sonst als dünne Linie an den
Zeilenrändern durch — im Panel als "roter Teilrahmen um jede Listenzeile"
gemeldet und dort genau so behoben. */
.ads-swipe-delete {
position: absolute;
inset: 0 0 0 auto;
@@ -11,10 +20,19 @@
color: #fff;
border: none;
font-family: inherit;
font-size: 11px;
letter-spacing: .08em;
text-transform: uppercase;
font-size: 13px;
letter-spacing: normal;
text-transform: none;
cursor: pointer;
visibility: hidden;
}
.ads-swipe-wrap:has(.ads-swiped) .ads-swipe-delete {
visibility: visible;
}
.ads-swipe-delete:active {
filter: brightness(.88);
}
.ads-swipe-content {
+48 -13
View File
@@ -3,12 +3,16 @@
border-top: 1px solid var(--line);
}
/* Maße 1:1 vom Panel (.tab in audi-dashboard-ios.css: padding 7px 2px 6px,
gap 3px). Sie standen hier noch auf den Werten vor der iOS-Auflage von
2026-08-13 - dieselbe Ursache wie beim roten Aktiv-Rand, der 2026-08-30
ersetzt wurde. */
.ads-tab {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 10px 0 8px;
gap: 3px;
padding: 7px 2px 6px;
background: none;
border: none;
color: var(--fg3);
@@ -24,26 +28,57 @@
ab (--shade ist .05/.04, hier .14/.07), deshalb hier direkt benannt, wie im
Original. */
.ads-tab--on {
background: rgba(255, 255, 255, 0.14);
border-radius: 14px;
color: var(--fg);
}
[data-theme="tag"] .ads-tab--on {
.ads-tabbar:not(.ads-tabbar--ohne) .ads-tab--on {
background: rgba(255, 255, 255, 0.14);
border-radius: 14px;
}
[data-theme="tag"] .ads-tabbar:not(.ads-tabbar--ohne) .ads-tab--on {
background: rgba(0, 0, 0, 0.07);
}
.ads-tab__icon svg {
width: 21px;
height: 21px;
/* Die Hülle um das Symbol ist die .tabpille des Panels: feste 63x39, damit
ihre Größe unabhängig vom Symbol ist. Ohne sie saß die Beschriftung im
Vergleich zum Panel deutlich zu dicht unter dem Symbol. Im reinen
Symbolmodus trägt sie selbst die Pille, sonst der ganze Knopf (oben). */
.ads-tab__icon {
display: flex;
align-items: center;
justify-content: center;
width: 63px;
height: 39px;
max-width: 100%;
border-radius: 20px;
}
.ads-tabbar--ohne .ads-tab__icon svg {
.ads-tabbar--ohne .ads-tab--on .ads-tab__icon {
background: rgba(255, 255, 255, 0.14);
}
[data-theme="tag"] .ads-tabbar--ohne .ads-tab--on .ads-tab__icon {
background: rgba(0, 0, 0, 0.07);
}
/* .tab svg im Panel ist in beiden Modi 26px - nur das Polster wechselt
(.tabbar.ohne .tab{padding:11px 2px 10px}). Die 21px hier ließen die
Symbole der Menüleiste sichtbar kleiner wirken als im Panel. */
.ads-tab__icon svg {
width: 26px;
height: 26px;
}
.ads-tab__label {
font-size: 9px;
letter-spacing: .08em;
text-transform: uppercase;
.ads-tabbar--ohne .ads-tab {
padding: 11px 2px 10px;
}
/* Live am Panel gemessen (.tab span): 11px, keine Großschreibung, .01em
Laufweite. Die 9px/uppercase/.08em hier waren derselbe Vor-iOS-Stil, den
.ads-eyebrow noch trägt - dort bewusst behalten und lokal überschrieben,
weil es eine allgemeine Textklasse des Kits ist; die Beschriftung der
Menüleiste ist dagegen ein Bauteil-Detail und folgt dem Panel direkt. */
.ads-tab__label {
font-size: 11px;
letter-spacing: .01em;
text-transform: none;
}
@@ -26,8 +26,13 @@ button.ads-tile--button:hover {
background: var(--tile-2);
}
/* min-width:0 hebt die automatische Mindestbreite eines Flex-Kindes auf.
Ohne sie wuchs der Inhalt an einer langen, unbrechbaren Zeichenkette (der
Fahrgestellnummer) über das Innenmaß der Kachel hinaus und lief rechts aus
dem Bild — im Panel bleibt die Zeile innerhalb der Kachel. */
.ads-tile__content {
flex: 1;
min-width: 0;
}
.ads-tile__go {
+10 -2
View File
@@ -51,11 +51,19 @@
--canvas: #F2F2F7;
--tile: #FFFFFF;
--tile-2: #EFEFF4;
--line: rgba(60, 60, 67, .13);
/* Live gemessen: --ios-sep im Tagmodus des Panels ist .16, nicht .13 (im
Nachtmodus stimmen beide überein). Betraf jede Trennlinie im Tagmodus. */
--line: rgba(60, 60, 67, .16);
--line-strong: rgba(60, 60, 67, .24);
--fg: #000000;
--fg2: #3C3C43;
--fg3: #8E8E93;
/* --fg3 traegt jede Kachelueberschrift, jede Einheit und jeden Zusatz.
Auf hellem Grund lag #8E8E93 bei 2,92:1 gegen die Seitenflaeche und
3,26:1 gegen die Kachel - fuer einen kurzen Blick im Auto bei
Sonnenlicht zu wenig (Designpruefung 2026-08-30). #6B6B70 bringt beide
auf 4,75:1 bzw. 5,30:1, ohne den ruhigen Gesamteindruck zu aendern.
Der Nachtwert bleibt #8E8E93 - dort sind es ohnehin 5,86:1. */
--fg3: #6B6B70;
--ok: #34C759;
--warn: #FF9F0A;
--bad: #FF3B30;