Fix wheel-photo squareness bug, native select popups, image-cycle crash

.bildbox.radbild lost to a same-specificity, later-declared .bildbox rule,
so the wheel photo stayed a full-width 4:3 box despite the earlier crop
fix - only which slice showed ever changed. Fixed with a compound selector
and moved "Montiert" to top-left to match the now-correctly-sized photo.

Native <select> popups had no color-scheme hint and rendered in the
browser's default light palette regardless of the app's dark theme -
this is what made "Modell" and other dropdowns flash bright white.

bildWeiter() (Mein Audi image-cycle tap) referenced a CSS class that was
never emitted (.platzhalter-datei vs. the real .platzhalter-aktion),
throwing and aborting before the page-dot indicator update - the photo
advanced but the dots never moved.

Merged the separate "Fahrzeugbilder" upload grid into "Bild der
Übersicht": pick a view from the existing dropdown, tap its preview to
upload/replace/delete - same generic popup plumbing, no new mechanism.
Removed the now-dead BILDER_UPLOAD_SLOTS/.bildgrid/.bildslot/.carfix.mini.

Back arrow changed from red to the neutral headline color, matching every
other navigation-color decision in this project.

Investigated but could not reproduce: user-reported "whole tab bar
disappears" when hiding labels. Traced the exact commit that added the
icon+label highlight and confirmed it's correctly scoped to :not(.ohne);
live DOM/computed-style testing across toggle, navigation, and both
mobile/desktop widths showed the tab bar staying visible throughout.
Left unchanged pending a repro from the user.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 20:40:14 +02:00
parent 367985ba44
commit 17b25d492a
7 changed files with 156 additions and 83 deletions
+64 -1
View File
@@ -17,7 +17,13 @@ generic-receipt-parser gaps; a sixth round the same day fixed the map pins' real
(the CI `poi-car`/`poi` icons are thin outline-only paths, only 13-40% filled — solved with a solid
silhouette layer extracted from each icon's own outer contour, not a redraw), applied a batch of
~20 user-reported UI polish items against Apple's Human Interface Guidelines, some via a guided
Q&A, and lowered the battery-voltage statistic's cutoff from 13.2V to 12.8V). This file is the entry point for every new agent
Q&A, and lowered the battery-voltage statistic's cutoff from 13.2V to 12.8V; a seventh round the
same day fixed a real CSS specificity bug that kept the wheel photo a wide 4:3 box instead of the
intended 96x96 square, moved the "Montiert" pill to match, added `color-scheme` so native `<select>`
popups stop rendering in the browser's default light palette against the dark app, root-caused and
fixed a JS crash in the "Mein Audi" image-cycle click handler, merged the separate "Fahrzeugbilder"
upload grid into "Bild der Übersicht" (pick a view, tap its photo to upload), and turned the back
arrow from red to the neutral headline color — see section C). This file is the entry point for every new agent
session: what this repo is, what is finished, what is missing, and how to work here. Detail lives in
the linked documents — this file points, it does not duplicate.
@@ -926,6 +932,63 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed
Deployed to `audi_ha_test` (version `1786901000`); pin fix visually confirmed live (solid
teardrop pin with white car glyph, clearly over the map's "Obergurgl" label text) and the
tab-bar highlight confirmed covering both icon and "Übersicht" label.
- [x] Seventh round the same day (2026-08-16) — more user-reported layout/interaction bugs:
(1) **Wheel photo (Sommerrad/Winterrad) was never actually a square, despite the fifth round's
crop-direction fix.** Root cause: `bildMitPlatzhalter()` puts the `radbild` class on *both* the
wrapping `.bildbox` div and the `<img>`; the old `.radbild{width:96px;height:96px;...}` rule and
`.bildbox{width:100%;aspect-ratio:4/3;...}` have equal specificity (one class each), and
`.bildbox` is declared later in the file, so it silently won — the box stayed a full-width 4:3
rectangle the whole time, `object-position:right center` just changed which slice of that wide
box showed. Fixed with a `.bildbox.radbild{width:96px;height:96px;aspect-ratio:1/1}` compound
selector (two classes, unambiguously higher specificity than plain `.bildbox`); `.radbild` alone
now only carries `object-position`. Verified live: `.bildbox.radbild` now measures exactly
96×96px.
(2) **"Montiert" moved from top-right to top-left**, per explicit request — this was the visible
symptom of (1): the pill was anchored top-right assuming it overlaid the (accidentally) full-
width photo; once the photo is a real 96px square on the left, top-left is where it actually
needs to sit to stay over the picture.
(3) **Native `<select>` popups now get a `color-scheme` hint.** The closed `<select>` box itself
was already themed correctly, but the *opened* native option list ignores app CSS and falls back
to the browser/OS default palette unless `color-scheme` says otherwise — this is what made
"Modell" and every other dropdown "flash" bright white against the dark app on open. Added
`color-scheme: dark` under `:host([data-theme="nacht"])` and `color-scheme: light` under
`:host([data-theme="tag"])` in `audi-dashboard-ios.css`.
(4) **Fixed a real JS crash in the "Mein Audi" image-cycle click handler.** `bildWeiter()` (fires
on tapping the hero photo) tried `box.querySelector(".platzhalter-datei").textContent = ...`
that class was never emitted by `bildMitPlatzhalter()` (the actual class is `.platzhalter-
aktion`, and in gallery mode it deliberately shows a static "Foto hinzufügen" prompt, not a
filename). `querySelector` returned `null`, the assignment threw, and the exception aborted the
function *before* reaching the line that updates the `.dots` page indicator — so the photo
itself advanced (that line ran first) but the dots never moved, reading as "doesn't work."
Removed the dead line entirely; verified live that the `.dots` `on` class now correctly moves
with each click and no exception is thrown.
(5) **Merged the separate "Fahrzeugbilder" upload grid into "Bild der Übersicht"**, per explicit
request: pick a view from the existing "Ansicht" dropdown, then tap the (now also click-to-
upload) preview below it to upload/replace/delete that specific photo — reusing the same
generic `data-bildklick`/`bildMenuOffen` popup plumbing already used by the wheel-photo tiles,
not a new mechanism. `bildInfo()`'s existing winter-side-view special case (swaps in
`seitenansicht-winter.webp` while winter tires are marked mounted) means the winter variant
stays uploadable through the same control, gated by current tire season instead of a permanently
visible separate slot. The now-unused `BILDER_UPLOAD_SLOTS` constant and the `.bildgrid`/
`.bildslot`/`.bildslot-label`/`.carfix.mini` CSS were removed as dead code; a new
`.bildmenu.ansicht` positions the ersetzen/löschen popup anchored to the tile's *bottom* edge
(not a fixed top offset like the other two menu variants) since this preview's height varies
with the tile's own content instead of being a fixed small thumbnail.
(6) **Back arrow changed from red (`--ios-tint`) to the neutral headline color (`--fg`)** in
`audi-dashboard-ios.css`, matching every other navigation-color decision made this project (red
stays reserved for destructive actions per Apple's HIG).
Investigated but could not reproduce: user reported that hiding tab labels ("Beschriftung in der
Menüleiste" off) makes the *entire* tab bar disappear. Traced the full CSS cascade and the exact
commit that added the sixth round's icon+label highlight (`git show` on the relevant lines) —
the new rules are correctly scoped to `:not(.ohne)` and provably never touched the icon-only
path; live DOM/computed-style testing (toggle the switch, click the actual label, navigate away
and back, both mobile and desktop container widths) showed the tab bar staying visible, correctly
sized, and correctly classed in every case tried in the in-app Chromium browser. Left unchanged
pending a repro (screenshot or the exact device/browser) from the user, since no defect could be
located to fix.
Deployed to `audi_ha_test`; wheel-photo squareness, Montiert position, back-arrow color, and the
image-cycle dots all confirmed live via direct DOM/computed-style checks (no visual screenshot
tool available this session — see running note below).
- [ ] Fix remaining documentation drift (statistics claim, README gaps, obsolete TODO comment) —
text-only changes; INSTALL.md's WLAN/TommiG1 drift and stale variable names were fixed
2026-08-12 (see section B); `DESIGN_REVIEW_2026-08-13.md` and `REVIEW_main_2026-08-13.md`
@@ -207,10 +207,6 @@ const BILDER = [
];
let bild = 0;
const SEITE_WINTER = { label: "Auto Seite mit Winterrädern", datei: "seitenansicht-winter.webp" };
// Alle einzeln hochladbaren Bild-Plätze (§ Fahrzeugbilder-Upload in
// Einstellungen) - dieselben festen Dateinamen wie BILDER, plus die separat
// geführte Winter-Variante der Seitenansicht.
const BILDER_UPLOAD_SLOTS = [...BILDER, { name: "Seitenansicht (Winter)", label: SEITE_WINTER.label, datei: SEITE_WINTER.datei }];
// Bei jedem Hochladen/Löschen eines Fahrzeugfotos hochgezählt und als
// Query-Parameter an alle bilder/-URLs gehängt (siehe bildInfo()
// unten) - ohne das würde der Browser nach einem Upload weiter das alte Bild
@@ -225,8 +221,9 @@ let bildVersion = Date.now();
Klassen erhalten, siehe carfix/carfix.klein/radbild in audi-dashboard.css). */
/* A5: Der Platzhalter nennt das Motiv und die nächste Handlung - der
Dateiname ist Entwicklersprache und steht nur noch in den Einstellungen
(siehe BILDER_UPLOAD_SLOTS). Die Maße bleiben über die CSS-Klassen erhalten,
das Layout springt beim Nachliefern eines Fotos nicht. */
(Bild der Übersicht: Ansicht wählen, dann auf das Bild tippen). Die Maße
bleiben über die CSS-Klassen erhalten, das Layout springt beim Nachliefern
eines Fotos nicht. */
function bildMitPlatzhalter(src, datei, label, klasse, imgId, dateiZeigen = false) {
const idTeil = imgId ? ` id="${imgId}"` : "";
return `<div class="bildbox ${klasse}" data-bildbox title="${esc(label)}">
@@ -294,7 +291,6 @@ function bildWeiter() {
if (box) {
box.classList.remove("bild-fehlt"); // neues Bild kann laden, auch wenn das vorherige fehlte
box.querySelector(".platzhalter-label").textContent = info.label;
box.querySelector(".platzhalter-datei").textContent = info.datei;
}
const sz = ROOT.getElementById("szene");
if (sz) sz.className = "szene " + szeneKlasse(bild);
@@ -2004,28 +2000,24 @@ function vEinst() {
<div class="tile"><span class="label">Bild der Übersicht</span>
<div class="feld" style="border-bottom:0"><label for="startbild">Ansicht</label>
<select id="startbild" data-startbild>${BILDER.map((b) => `<option ${b.name === CONFIG.startbild ? "selected" : ""}>${b.name}</option>`).join("")}</select></div>
<div style="margin-top:16px">${(() => { const info = bildInfo(startIndex()); return bildMitPlatzhalter(info.src, info.datei, info.label, "carfix"); })()}</div>
</div>
<div class="tile"><span class="label">Fahrzeugbilder</span>
<span class="label" style="margin-top:10px;color:var(--fg2);letter-spacing:0;
text-transform:none;font-size:12.5px;line-height:1.6">
Auf ein Bild tippen, um ein eigenes Foto hochzuladen oder zu ersetzen.</span>
<div class="bildgrid">
${BILDER_UPLOAD_SLOTS.map((s) => `
<div class="bildslot">
<div data-bildklick="${esc(s.datei)}">
${bildMitPlatzhalter(`/local/bilder/${s.datei}?v=${bildVersion}`, s.datei, s.label, "carfix mini", null, true)}
</div>
<input type="file" accept="image/*" hidden data-bildupload="${esc(s.datei)}">
<span class="bildslot-label">${esc(s.name)}</span>
${bildMenuOffen === s.datei ? `
<div class="bildmenu-catcher" data-bildmenu-schliessen></div>
<div class="bildmenu slot">
<button type="button" data-bildersetzen="${esc(s.datei)}">Foto ersetzen</button>
<button type="button" class="loeschen" data-bildloeschen="${esc(s.datei)}">Löschen</button>
</div>` : ""}
</div>`).join("")}
</div>
Auf das Bild tippen, um für diese Ansicht ein eigenes Foto hochzuladen oder zu ersetzen.</span>
${(() => {
const info = bildInfo(startIndex());
return `<div style="margin-top:12px">
<div data-bildklick="${esc(info.datei)}">
${bildMitPlatzhalter(info.src, info.datei, info.label, "carfix", null, true)}
</div>
<input type="file" accept="image/*" hidden data-bildupload="${esc(info.datei)}">
${bildMenuOffen === info.datei ? `
<div class="bildmenu-catcher" data-bildmenu-schliessen></div>
<div class="bildmenu ansicht">
<button type="button" data-bildersetzen="${esc(info.datei)}">Foto ersetzen</button>
<button type="button" class="loeschen" data-bildloeschen="${esc(info.datei)}">Löschen</button>
</div>` : ""}
</div>`;
})()}
</div>
<div class="tile"><span class="label">Kraftstoff-Rabatt</span>
<div class="feld"><label for="sdAktiv" style="display:flex;align-items:center;gap:6px">
@@ -3563,7 +3555,7 @@ function ereignisseVerdrahten() {
if (bildKlick) {
const dateiname = bildKlick.dataset.bildklick;
const box = bildKlick.querySelector(".bildbox");
const container = bildKlick.closest(".tile, .bildslot");
const container = bildKlick.closest(".tile");
if (box && box.classList.contains("bild-fehlt")) {
const input = container.querySelector(`[data-bildupload="${dateiname}"]`);
if (input) input.click();
@@ -3575,7 +3567,7 @@ function ereignisseVerdrahten() {
if (e.target.closest("[data-bildmenu-schliessen]")) { bildMenuOffen = null; render(); return; }
const bildErsetzen = e.target.closest("[data-bildersetzen]");
if (bildErsetzen) {
const container = bildErsetzen.closest(".tile, .bildslot");
const container = bildErsetzen.closest(".tile");
const input = container.querySelector(`[data-bildupload="${bildErsetzen.dataset.bildersetzen}"]`);
if (input) input.click();
return;
@@ -26,6 +26,11 @@
--line-strong:rgba(255,255,255,.24);
--fg:#FFFFFF; --fg2:#BFC4CC; --fg3:#8E8E93;
--ok:#30D158; --warn:#FFD60A; --bad:#FF453A;
/* Ohne diese Angabe rendert der Browser native Steuerelemente (Dropdown-
Aufklapp-Liste bei <select>, Scrollbars) in seiner Standard-Hellansicht,
egal wie dunkel die App selbst ist - das liess z. B. "Modell" beim
Aufklappen grell-weiss aufblitzen. Betrifft alle <select>-Felder. */
color-scheme: dark;
}
:host([data-theme="tag"]){
--canvas:#F2F2F7;
@@ -38,6 +43,7 @@
--ios-sep:rgba(60,60,67,.16);
--ios-fill:rgba(120,120,128,.16);
--tile-deckend:#FFFFFF;
color-scheme: light;
}
body{padding:0;background:var(--canvas)}
@@ -67,7 +73,10 @@ html,body{height:100%}
.eyebrow{display:none}
.title{font-size:22px;font-weight:400;letter-spacing:-.01em;line-height:1.2}
.back{width:34px;height:34px;margin-left:-8px;justify-content:center}
.back svg{width:22px;height:22px;color:var(--ios-tint)}
/* Rot bleibt Akzent/destruktiv, nicht Navigationsfarbe (dieselbe Regel wie
bei den Aktions-Buttons und dem aktiven Menü-Icon) - derselbe neutrale Ton
wie die Kopfzeile selbst. */
.back svg{width:22px;height:22px;color:var(--fg)}
.sync{margin-top:3px;font-size:12.5px;color:var(--fg3)}
.sync svg{width:13px;height:13px}
.themebtn{width:36px;height:36px;border:0;background:var(--ios-fill);color:var(--fg2)}
@@ -442,11 +442,15 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.carbox { position: relative; }
.carfix { width: 100%; aspect-ratio: 16 / 9; }
.carfix.klein { aspect-ratio: 16 / 8; }
.carfix.mini { width: 78px; height: 78px; aspect-ratio: auto; }
/* Reifenfoto ist oft eine breite Aufnahme; statt beide Ränder symmetrisch
wegzuschneiden (object-fit:cover-Standard), bleibt bewusst der rechte Rand
des Originalfotos erhalten - dort sitzt das Rad meist im Bild. */
.radbild { width: 96px; height: 96px; aspect-ratio: auto; border-radius: 50%; object-position: right center; }
.radbild { object-position: right center; }
/* .bildbox.radbild statt .radbild allein: .bildbox unten setzt width/aspect-
ratio auf derselben Spezifitaet (eine Klasse) und gewinnt als spaeter
notierte Regel sonst automatisch - die Box blieb dadurch bislang die volle
Kachelbreite im 4:3-Format statt eines echten 96x96-Quadrats. */
.bildbox.radbild { width: 96px; height: 96px; aspect-ratio: 1 / 1; }
.bildbox { position: relative; width: 100%; aspect-ratio: 4 / 3; border-radius: 12px; overflow: hidden; }
.bildbox img { display: block; width: 100%; height: 100%; object-fit: cover; }
@@ -458,7 +462,6 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
App. Mit Foto gilt wieder das volle Seitenverhältnis. */
.bildbox.bild-fehlt.carfix { aspect-ratio: auto; height: 132px; }
.bildbox.bild-fehlt.carfix.klein { height: 120px; }
.bildbox.bild-fehlt.carfix.mini { height: 78px; }
.szene.bleed:has(.bild-fehlt)::before { display: none; }
.szene.bleed:has(.bild-fehlt) { margin-left: 0; margin-right: 0; }
.szene.bleed:has(.bild-fehlt) .carbox { padding: 0; }
@@ -840,9 +843,6 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.standort-pille[disabled] { opacity: .4; pointer-events: none; }
/* ------------------------------------------------------- Bilderverwaltung */
.bildgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); margin-top: var(--sp-4); }
.bildslot { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.bildslot-label { font-size: 11.5px; letter-spacing: normal; color: var(--fg2); text-align: center; }
.bildmenu-catcher { position: fixed; inset: 0; z-index: 1000; }
.bildmenu {
position: absolute; z-index: 1001;
@@ -852,8 +852,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
display: flex; flex-direction: column;
min-width: 160px;
}
.bildmenu.slot { top: 60px; left: 50%; transform: translateX(-50%); }
.bildmenu.rad { top: 90px; left: 22px; }
/* "Bild der Übersicht": das Vorschaubild füllt die ganze Kachelbreite, seine
Unterkante liegt daher immer nah an der Kachel-Unterkante - das Menü von
dort aus verankern statt mit einem festen top-Wert, der bei jedem
Bildseitenverhältnis neu nachgemessen werden müsste. */
.bildmenu.ansicht { bottom: var(--sp-5); left: 50%; transform: translateX(-50%); }
.bildmenu button {
display: block; width: 100%; text-align: left;
background: none; border: none;
@@ -874,12 +878,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.radkopf { display: flex; flex-direction: column; margin-top: var(--sp-4); }
/* Kompakte Pille statt voller Breite (Apple-HIG-Pull-down-Maß): oben rechts
in der Kachel, obere Kante auf Höhe des Radfotos, rechter Abstand
identisch zum linken Texteinzug (beides var(--sp-5), die Kachel-Innen-
/* Kompakte Pille statt voller Breite (Apple-HIG-Pull-down-Maß): oben links
in der Kachel, obere Kante auf Höhe des Radfotos, linker Abstand
identisch zum Texteinzug (beides var(--sp-5), die Kachel-Innen-
polsterung). */
.montiert {
position: absolute; top: var(--sp-5); right: var(--sp-5);
position: absolute; top: var(--sp-5); left: var(--sp-5);
display: inline-flex; align-items: center; justify-content: center;
font-family: inherit; font-size: 12px; letter-spacing: normal;
color: var(--fg2);
+21 -29
View File
@@ -207,10 +207,6 @@ const BILDER = [
];
let bild = 0;
const SEITE_WINTER = { label: "Auto Seite mit Winterrädern", datei: "seitenansicht-winter.webp" };
// Alle einzeln hochladbaren Bild-Plätze (§ Fahrzeugbilder-Upload in
// Einstellungen) - dieselben festen Dateinamen wie BILDER, plus die separat
// geführte Winter-Variante der Seitenansicht.
const BILDER_UPLOAD_SLOTS = [...BILDER, { name: "Seitenansicht (Winter)", label: SEITE_WINTER.label, datei: SEITE_WINTER.datei }];
// Bei jedem Hochladen/Löschen eines Fahrzeugfotos hochgezählt und als
// Query-Parameter an alle bilder/-URLs gehängt (siehe bildInfo()
// unten) - ohne das würde der Browser nach einem Upload weiter das alte Bild
@@ -225,8 +221,9 @@ let bildVersion = Date.now();
Klassen erhalten, siehe carfix/carfix.klein/radbild in audi-dashboard.css). */
/* A5: Der Platzhalter nennt das Motiv und die nächste Handlung - der
Dateiname ist Entwicklersprache und steht nur noch in den Einstellungen
(siehe BILDER_UPLOAD_SLOTS). Die Maße bleiben über die CSS-Klassen erhalten,
das Layout springt beim Nachliefern eines Fotos nicht. */
(Bild der Übersicht: Ansicht wählen, dann auf das Bild tippen). Die Maße
bleiben über die CSS-Klassen erhalten, das Layout springt beim Nachliefern
eines Fotos nicht. */
function bildMitPlatzhalter(src, datei, label, klasse, imgId, dateiZeigen = false) {
const idTeil = imgId ? ` id="${imgId}"` : "";
return `<div class="bildbox ${klasse}" data-bildbox title="${esc(label)}">
@@ -294,7 +291,6 @@ function bildWeiter() {
if (box) {
box.classList.remove("bild-fehlt"); // neues Bild kann laden, auch wenn das vorherige fehlte
box.querySelector(".platzhalter-label").textContent = info.label;
box.querySelector(".platzhalter-datei").textContent = info.datei;
}
const sz = ROOT.getElementById("szene");
if (sz) sz.className = "szene " + szeneKlasse(bild);
@@ -2004,28 +2000,24 @@ function vEinst() {
<div class="tile"><span class="label">Bild der Übersicht</span>
<div class="feld" style="border-bottom:0"><label for="startbild">Ansicht</label>
<select id="startbild" data-startbild>${BILDER.map((b) => `<option ${b.name === CONFIG.startbild ? "selected" : ""}>${b.name}</option>`).join("")}</select></div>
<div style="margin-top:16px">${(() => { const info = bildInfo(startIndex()); return bildMitPlatzhalter(info.src, info.datei, info.label, "carfix"); })()}</div>
</div>
<div class="tile"><span class="label">Fahrzeugbilder</span>
<span class="label" style="margin-top:10px;color:var(--fg2);letter-spacing:0;
text-transform:none;font-size:12.5px;line-height:1.6">
Auf ein Bild tippen, um ein eigenes Foto hochzuladen oder zu ersetzen.</span>
<div class="bildgrid">
${BILDER_UPLOAD_SLOTS.map((s) => `
<div class="bildslot">
<div data-bildklick="${esc(s.datei)}">
${bildMitPlatzhalter(`/local/bilder/${s.datei}?v=${bildVersion}`, s.datei, s.label, "carfix mini", null, true)}
</div>
<input type="file" accept="image/*" hidden data-bildupload="${esc(s.datei)}">
<span class="bildslot-label">${esc(s.name)}</span>
${bildMenuOffen === s.datei ? `
<div class="bildmenu-catcher" data-bildmenu-schliessen></div>
<div class="bildmenu slot">
<button type="button" data-bildersetzen="${esc(s.datei)}">Foto ersetzen</button>
<button type="button" class="loeschen" data-bildloeschen="${esc(s.datei)}">Löschen</button>
</div>` : ""}
</div>`).join("")}
</div>
Auf das Bild tippen, um für diese Ansicht ein eigenes Foto hochzuladen oder zu ersetzen.</span>
${(() => {
const info = bildInfo(startIndex());
return `<div style="margin-top:12px">
<div data-bildklick="${esc(info.datei)}">
${bildMitPlatzhalter(info.src, info.datei, info.label, "carfix", null, true)}
</div>
<input type="file" accept="image/*" hidden data-bildupload="${esc(info.datei)}">
${bildMenuOffen === info.datei ? `
<div class="bildmenu-catcher" data-bildmenu-schliessen></div>
<div class="bildmenu ansicht">
<button type="button" data-bildersetzen="${esc(info.datei)}">Foto ersetzen</button>
<button type="button" class="loeschen" data-bildloeschen="${esc(info.datei)}">Löschen</button>
</div>` : ""}
</div>`;
})()}
</div>
<div class="tile"><span class="label">Kraftstoff-Rabatt</span>
<div class="feld"><label for="sdAktiv" style="display:flex;align-items:center;gap:6px">
@@ -3563,7 +3555,7 @@ function ereignisseVerdrahten() {
if (bildKlick) {
const dateiname = bildKlick.dataset.bildklick;
const box = bildKlick.querySelector(".bildbox");
const container = bildKlick.closest(".tile, .bildslot");
const container = bildKlick.closest(".tile");
if (box && box.classList.contains("bild-fehlt")) {
const input = container.querySelector(`[data-bildupload="${dateiname}"]`);
if (input) input.click();
@@ -3575,7 +3567,7 @@ function ereignisseVerdrahten() {
if (e.target.closest("[data-bildmenu-schliessen]")) { bildMenuOffen = null; render(); return; }
const bildErsetzen = e.target.closest("[data-bildersetzen]");
if (bildErsetzen) {
const container = bildErsetzen.closest(".tile, .bildslot");
const container = bildErsetzen.closest(".tile");
const input = container.querySelector(`[data-bildupload="${bildErsetzen.dataset.bildersetzen}"]`);
if (input) input.click();
return;
+10 -1
View File
@@ -26,6 +26,11 @@
--line-strong:rgba(255,255,255,.24);
--fg:#FFFFFF; --fg2:#BFC4CC; --fg3:#8E8E93;
--ok:#30D158; --warn:#FFD60A; --bad:#FF453A;
/* Ohne diese Angabe rendert der Browser native Steuerelemente (Dropdown-
Aufklapp-Liste bei <select>, Scrollbars) in seiner Standard-Hellansicht,
egal wie dunkel die App selbst ist - das liess z. B. "Modell" beim
Aufklappen grell-weiss aufblitzen. Betrifft alle <select>-Felder. */
color-scheme: dark;
}
:host([data-theme="tag"]){
--canvas:#F2F2F7;
@@ -38,6 +43,7 @@
--ios-sep:rgba(60,60,67,.16);
--ios-fill:rgba(120,120,128,.16);
--tile-deckend:#FFFFFF;
color-scheme: light;
}
body{padding:0;background:var(--canvas)}
@@ -67,7 +73,10 @@ html,body{height:100%}
.eyebrow{display:none}
.title{font-size:22px;font-weight:400;letter-spacing:-.01em;line-height:1.2}
.back{width:34px;height:34px;margin-left:-8px;justify-content:center}
.back svg{width:22px;height:22px;color:var(--ios-tint)}
/* Rot bleibt Akzent/destruktiv, nicht Navigationsfarbe (dieselbe Regel wie
bei den Aktions-Buttons und dem aktiven Menü-Icon) - derselbe neutrale Ton
wie die Kopfzeile selbst. */
.back svg{width:22px;height:22px;color:var(--fg)}
.sync{margin-top:3px;font-size:12.5px;color:var(--fg3)}
.sync svg{width:13px;height:13px}
.themebtn{width:36px;height:36px;border:0;background:var(--ios-fill);color:var(--fg2)}
+15 -11
View File
@@ -442,11 +442,15 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.carbox { position: relative; }
.carfix { width: 100%; aspect-ratio: 16 / 9; }
.carfix.klein { aspect-ratio: 16 / 8; }
.carfix.mini { width: 78px; height: 78px; aspect-ratio: auto; }
/* Reifenfoto ist oft eine breite Aufnahme; statt beide Ränder symmetrisch
wegzuschneiden (object-fit:cover-Standard), bleibt bewusst der rechte Rand
des Originalfotos erhalten - dort sitzt das Rad meist im Bild. */
.radbild { width: 96px; height: 96px; aspect-ratio: auto; border-radius: 50%; object-position: right center; }
.radbild { object-position: right center; }
/* .bildbox.radbild statt .radbild allein: .bildbox unten setzt width/aspect-
ratio auf derselben Spezifitaet (eine Klasse) und gewinnt als spaeter
notierte Regel sonst automatisch - die Box blieb dadurch bislang die volle
Kachelbreite im 4:3-Format statt eines echten 96x96-Quadrats. */
.bildbox.radbild { width: 96px; height: 96px; aspect-ratio: 1 / 1; }
.bildbox { position: relative; width: 100%; aspect-ratio: 4 / 3; border-radius: 12px; overflow: hidden; }
.bildbox img { display: block; width: 100%; height: 100%; object-fit: cover; }
@@ -458,7 +462,6 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
App. Mit Foto gilt wieder das volle Seitenverhältnis. */
.bildbox.bild-fehlt.carfix { aspect-ratio: auto; height: 132px; }
.bildbox.bild-fehlt.carfix.klein { height: 120px; }
.bildbox.bild-fehlt.carfix.mini { height: 78px; }
.szene.bleed:has(.bild-fehlt)::before { display: none; }
.szene.bleed:has(.bild-fehlt) { margin-left: 0; margin-right: 0; }
.szene.bleed:has(.bild-fehlt) .carbox { padding: 0; }
@@ -840,9 +843,6 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.standort-pille[disabled] { opacity: .4; pointer-events: none; }
/* ------------------------------------------------------- Bilderverwaltung */
.bildgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); margin-top: var(--sp-4); }
.bildslot { position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.bildslot-label { font-size: 11.5px; letter-spacing: normal; color: var(--fg2); text-align: center; }
.bildmenu-catcher { position: fixed; inset: 0; z-index: 1000; }
.bildmenu {
position: absolute; z-index: 1001;
@@ -852,8 +852,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
display: flex; flex-direction: column;
min-width: 160px;
}
.bildmenu.slot { top: 60px; left: 50%; transform: translateX(-50%); }
.bildmenu.rad { top: 90px; left: 22px; }
/* "Bild der Übersicht": das Vorschaubild füllt die ganze Kachelbreite, seine
Unterkante liegt daher immer nah an der Kachel-Unterkante - das Menü von
dort aus verankern statt mit einem festen top-Wert, der bei jedem
Bildseitenverhältnis neu nachgemessen werden müsste. */
.bildmenu.ansicht { bottom: var(--sp-5); left: 50%; transform: translateX(-50%); }
.bildmenu button {
display: block; width: 100%; text-align: left;
background: none; border: none;
@@ -874,12 +878,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
.radkopf { display: flex; flex-direction: column; margin-top: var(--sp-4); }
/* Kompakte Pille statt voller Breite (Apple-HIG-Pull-down-Maß): oben rechts
in der Kachel, obere Kante auf Höhe des Radfotos, rechter Abstand
identisch zum linken Texteinzug (beides var(--sp-5), die Kachel-Innen-
/* Kompakte Pille statt voller Breite (Apple-HIG-Pull-down-Maß): oben links
in der Kachel, obere Kante auf Höhe des Radfotos, linker Abstand
identisch zum Texteinzug (beides var(--sp-5), die Kachel-Innen-
polsterung). */
.montiert {
position: absolute; top: var(--sp-5); right: var(--sp-5);
position: absolute; top: var(--sp-5); left: var(--sp-5);
display: inline-flex; align-items: center; justify-content: center;
font-family: inherit; font-size: 12px; letter-spacing: normal;
color: var(--fg2);