DataMetric360 Board aus Claude Design importieren: iOS/Großbildschirm-Optikauflage
Neuer Vergleichsentwurf (Smartphone + Großbildschirm nebeneinander) direkt per Claude-Design-MCP importiert. Enthaelt die responsive Grossbildschirm-Umsetzung (Tab-Leiste wird zur Seitennavigation ab 860px) als reine CSS-Auflage. audi-dashboard-app.js aus dem Export weicht 641 Zeilen vom produktiven Stand ab (fehlende Vier-Kacheln-Kurzwahl, Balkenfarbe, Resttank-Liter) - deshalb NICHT uebernommen, nur die CSS-Auflage (audi-dashboard-ios.css) nach homeassistant/www/ kopiert, aber noch nicht in die Panel-Ladelogik eingebunden. Entscheidung zur Aktivierung und zur Aufloesung der JS-Abweichung steht noch aus.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="./support.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<x-dc>
|
||||
<helmet>
|
||||
<link rel="stylesheet" href="_ds/audi-dashboard-ui-27d66fd4-ed86-43b2-b8bf-61e281a5a1a5/_ds_bundle.css">
|
||||
<link rel="stylesheet" href="_ds/audi-dashboard-ui-27d66fd4-ed86-43b2-b8bf-61e281a5a1a5/styles.css">
|
||||
<script src="_ds/audi-dashboard-ui-27d66fd4-ed86-43b2-b8bf-61e281a5a1a5/_ds_bundle.js"></script>
|
||||
<style>
|
||||
@font-face { font-family: 'Audi Type'; src: url('assets/fonts/AudiTypeVF.ttf') format('truetype-variations'); font-weight: 100 900; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Audi Type Wide'; src: url('assets/fonts/AudiType-WideLight.ttf') format('truetype'); font-weight: 300; font-style: normal; font-display: swap; }
|
||||
html, body { margin: 0; background: #05070a; }
|
||||
a { color: #F50537; text-decoration: none; }
|
||||
a:hover { color: #fff; }
|
||||
</style>
|
||||
</helmet>
|
||||
<div style="min-height:100vh;background:radial-gradient(120% 80% at 50% 0%, #14181e 0%, #05070a 70%);display:flex;flex-direction:column;align-items:flex-start;gap:26px;padding:44px 24px 60px;width:max-content;min-width:100%;font-family:'Audi Type',Helvetica,Arial,sans-serif">
|
||||
|
||||
<div style="display:flex;flex-wrap:wrap;align-items:flex-end;gap:40px;width:100%">
|
||||
<div style="color:#fff;max-width:560px;padding-left:4px">
|
||||
<div style="font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:#8E8E93">Audi CI · Smartphone und großer Bildschirm</div>
|
||||
<div style="font-size:26px;font-weight:300;margin-top:10px;line-height:1.25;letter-spacing:-.01em;font-family:'Audi Type Wide','Audi Type',Helvetica,Arial,sans-serif">Dieselbe App, zwei Größen</div>
|
||||
</div>
|
||||
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding-bottom:2px">
|
||||
<sc-for list="{{ screens }}" as="s" hint-placeholder-count="5">
|
||||
<button onClick="{{ s.go }}" style="padding:8px 15px;border-radius:999px;border:1px solid {{ s.border }};font:inherit;font-size:12.5px;font-weight:300;cursor:pointer;background:{{ s.bg }};color:{{ s.fg }}">{{ s.label }}</button>
|
||||
</sc-for>
|
||||
<div style="width:1px;height:26px;background:rgba(255,255,255,.16);margin:0 6px"></div>
|
||||
<sc-for list="{{ themen }}" as="t" hint-placeholder-count="2">
|
||||
<button onClick="{{ t.go }}" style="padding:8px 15px;border-radius:999px;border:1px solid {{ t.border }};font:inherit;font-size:12.5px;font-weight:300;cursor:pointer;background:{{ t.bg }};color:{{ t.fg }}">{{ t.label }}</button>
|
||||
</sc-for>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;align-items:flex-start;gap:44px;flex:0 0 auto">
|
||||
<div style="flex:0 0 auto">
|
||||
<div style="display:flex;align-items:baseline;gap:10px;margin-bottom:14px;padding-left:4px">
|
||||
<span style="font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:#8E8E93">Smartphone</span>
|
||||
<span style="font-size:11.5px;font-weight:300;color:#6b7076">402 px · Tab-Leiste unten</span>
|
||||
</div>
|
||||
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx" dark="{{ dunkel }}" width="{{ 402 }}" height="{{ 874 }}" hint-size="402px,874px">
|
||||
<iframe data-dm="phone" src="dm360-host.html" title="DataMetric360 Smartphone" style="width:100%;height:100%;border:0;display:block;background:#0C1014"></iframe>
|
||||
</x-import>
|
||||
</div>
|
||||
|
||||
<div style="flex:0 0 auto">
|
||||
<div style="display:flex;align-items:baseline;gap:10px;margin-bottom:14px;padding-left:4px">
|
||||
<span style="font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:#8E8E93">Großer Bildschirm</span>
|
||||
<span style="font-size:11.5px;font-weight:300;color:#6b7076">1280 px · Navigation seitlich ab 860 px</span>
|
||||
</div>
|
||||
<div style="width:1280px;height:874px;border-radius:18px;overflow:hidden;border:1px solid rgba(255,255,255,.14);box-shadow:0 40px 90px rgba(0,0,0,.55);background:#0C1014;display:flex">
|
||||
<iframe data-dm="wide" src="dm360-host.html" title="DataMetric360 großer Bildschirm" style="width:100%;height:100%;border:0;display:block;background:#0C1014"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</x-dc>
|
||||
<script type="text/x-dc" data-dc-script data-props="{"$preview":{"width":1820,"height":1060}}">
|
||||
const SCREENS = [
|
||||
['home', 'Übersicht'], ['audi', 'Mein Audi'], ['trips', 'Fahrten'], ['stat', 'Statistik'],
|
||||
['fuel', 'Tanken'], ['einst', 'Einstellungen']
|
||||
];
|
||||
|
||||
class Component extends DCLogic {
|
||||
state = { theme: 'nacht', screen: 'home' };
|
||||
|
||||
componentDidMount() {
|
||||
this._onMsg = (e) => { if (e.data && e.data.dm360bereit) this._senden(); };
|
||||
window.addEventListener('message', this._onMsg);
|
||||
this._senden();
|
||||
}
|
||||
componentWillUnmount() { window.removeEventListener('message', this._onMsg); }
|
||||
componentDidUpdate() { this._senden(); }
|
||||
|
||||
_senden() {
|
||||
const nutzlast = { dm360: { theme: this.state.theme, screen: this.state.screen } };
|
||||
document.querySelectorAll('iframe[data-dm]').forEach((f) => {
|
||||
try { f.contentWindow.postMessage(nutzlast, '*'); } catch (e) {}
|
||||
});
|
||||
}
|
||||
|
||||
_stil(aktiv) {
|
||||
return aktiv
|
||||
? { bg: '#F50537', fg: '#fff', border: '#F50537' }
|
||||
: { bg: 'transparent', fg: '#c7c9cc', border: 'rgba(255,255,255,.22)' };
|
||||
}
|
||||
|
||||
renderVals() {
|
||||
const s = this.state;
|
||||
return {
|
||||
dunkel: s.theme === 'nacht',
|
||||
screens: SCREENS.map(([id, label]) => ({
|
||||
key: id, label, go: () => this.setState({ screen: id }), ...this._stil(s.screen === id)
|
||||
})),
|
||||
themen: [['nacht', 'Nacht'], ['tag', 'Tag']].map(([id, label]) => ({
|
||||
key: id, label, go: () => this.setState({ theme: id }), ...this._stil(s.theme === id)
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+18
-2
@@ -13,6 +13,11 @@ Das ist der **Entwurf**, nicht der lauffähige Code. Umgesetzt wird er später i
|
||||
|---|---|
|
||||
| `DataMetric360.dc.html` | Einstiegsdatei des Entwurfs |
|
||||
| `DM360.dc.html` | Die eigentlichen Bildschirme und Zustände |
|
||||
| `DataMetric360 Board.dc.html` | Vergleichsansicht: dieselbe App gleichzeitig als Smartphone- und Großbildschirm-Rahmen nebeneinander, mit Bildschirm-/Theme-Umschalter. Steuert beide Rahmen per `postMessage`. |
|
||||
| `dm360-host.html` | Lädt `audi-dashboard-app.js` in einem eigenen `<audi-dashboard-panel>`, hängt danach `audi-dashboard-ios.css` in dessen Shadow-Root - der eigentliche Träger hinter dem Board. |
|
||||
| `ios-frame.jsx` | iOS-26-Gerätekachel (Statusleiste, Dynamic Island, Home-Indikator) - reine Optik, keine App-Logik. |
|
||||
| `audi-dashboard-ios.css` | **Die eigentliche Neuerung dieses Exports.** Optik-Auflage über `audi-dashboard.css`: iOS-natives Erscheinungsbild (44-px-Zeilen, transluzente Tab-Leiste, iOS-Schalter) plus - wichtig - die responsive Großbildschirm-Umsetzung per `@container (min-width:860px)` (Tab-Leiste wird zur seitlichen Navigation). Nach eigener Aussage der Datei rein additiv, rührt `audi-dashboard.css` und die App-Logik nicht an. **Nicht verifiziert, ob das zutrifft** - noch nicht in `homeassistant/www/` scharf geschaltet. |
|
||||
| `datametric360-ios.css` | Dieselbe Optik-Auflage, aber für die `@audi-dash/ui`-Komponentenklassen (`.ads-*`) statt für die Panel-eigenen Klassen - für später, wenn `companion-app/` eine UI bekommt. |
|
||||
| `support.js` | Hilfsfunktionen des Entwurfs |
|
||||
| `assets/` | Icons, Logos, Typenschilder und Schriften, die der Entwurf verwendet |
|
||||
| `uploads/` | Rohdateien, die ins Design-Projekt hochgeladen wurden (Schriften, Ringe, alle RS-/S-Typenschilder) |
|
||||
@@ -20,8 +25,9 @@ Das ist der **Entwurf**, nicht der lauffähige Code. Umgesetzt wird er später i
|
||||
|
||||
## Stand dieses Exports
|
||||
|
||||
Heruntergeladen am 2026-08-11. Zwei bekannte Abweichungen, die im Design-Projekt bereits als Korrektur
|
||||
beauftragt, in *diesem* Export aber noch nicht enthalten sind:
|
||||
Heruntergeladen am 2026-08-11 direkt per Claude-Design-MCP (nicht per ZIP). Zwei bekannte Abweichungen,
|
||||
die im Design-Projekt bereits als Korrektur beauftragt, in *diesem* Export aber noch nicht enthalten
|
||||
sind:
|
||||
|
||||
1. **Falsches Modell:** zeigt „Audi RS 6 Avant" mit RS-6-Typenschild. Richtig ist der
|
||||
**RS 4 Avant competition** (siehe `../SPECIFICATION.md` §1).
|
||||
@@ -31,6 +37,16 @@ beauftragt, in *diesem* Export aber noch nicht enthalten sind:
|
||||
Kfz-Steuer, Reifen, Fahrt-Detail, Tankvorgang-Detail sowie deutlich umfangreichere Einstellungen)
|
||||
- vollständige Liste in `../SPECIFICATION.md` §3.
|
||||
|
||||
**Drittens, neu und wichtig:** das im Board-Entwurf verwendete `audi-dashboard-app.js` weicht vom
|
||||
tatsächlich in `../homeassistant/www/` laufenden Stand ab (641 Zeilen Unterschied im Diff). Der
|
||||
Design-Entwurf scheint auf einem älteren Stand aufzusetzen und enthält nicht alle seither gemachten
|
||||
Anpassungen - u. a. fehlen die Vier-Kacheln-Kurzwahl auf der Übersicht (Fahrzeugstatus/Service/
|
||||
Reifen/Tanken), die füllstandsabhängige Balkenfarbe und die Resttank-in-Liter-Angabe. **Deshalb wurde
|
||||
nur `audi-dashboard-ios.css` übernommen (reine CSS-Auflage, keine Logik) - `audi-dashboard-app.js`
|
||||
selbst wurde NICHT aus diesem Export übernommen**, um diese bereits fertigen Funktionen nicht zu
|
||||
verlieren. Bevor diese Auflage produktiv geschaltet wird, muss geklärt werden, ob/wie sie mit dem
|
||||
aktuellen `audi-dashboard-app.js` zusammenpasst.
|
||||
|
||||
Dieser Ordner wird beim nächsten Export komplett ersetzt.
|
||||
|
||||
## Markenrechtlicher Hinweis
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* ==================================================================
|
||||
iOS-Auflage für die Design-System-Komponenten (AudiDashUI) im Board.
|
||||
Gleiche Sprache wie audi-dashboard-ios.css für die echte App: Audi CI in
|
||||
iOS-Form — 44-px-Zeilen, gruppierte Kacheln, transluente Tab-Leiste,
|
||||
iOS-Schalter und -Segmente, gefüllte Aktionsknöpfe.
|
||||
Wird nach dem Design-System-Bundle geladen, ändert dieses nicht.
|
||||
================================================================== */
|
||||
|
||||
.ads-root{
|
||||
--ios-tint:#F50537;
|
||||
--ios-grau:#8E8E93;
|
||||
--ios-fill:rgba(120,120,128,.24);
|
||||
--ios-sep:rgba(255,255,255,.13);
|
||||
--r-tile:16px;
|
||||
--canvas:#0C1014;
|
||||
--tile:rgba(255,255,255,.055);
|
||||
--tile-2:rgba(255,255,255,.10);
|
||||
--tile-deckend:#171B21;
|
||||
--line:rgba(255,255,255,.13);
|
||||
--line-strong:rgba(255,255,255,.24);
|
||||
--fg:#FFFFFF; --fg2:#BFC4CC; --fg3:#8E8E93;
|
||||
--ok:#30D158; --warn:#FFD60A; --bad:#FF453A;
|
||||
}
|
||||
.ads-root[data-theme="tag"]{
|
||||
--ios-sep:rgba(60,60,67,.16);
|
||||
--ios-fill:rgba(120,120,128,.16);
|
||||
--canvas:#F2F2F7;
|
||||
--tile:#FFFFFF;
|
||||
--tile-2:#EFEFF4;
|
||||
--tile-deckend:#FFFFFF;
|
||||
--line:rgba(60,60,67,.13);
|
||||
--line-strong:rgba(60,60,67,.24);
|
||||
--fg:#000000; --fg2:#3C3C43; --fg3:#8E8E93;
|
||||
--ok:#34C759; --warn:#FF9F0A; --bad:#FF3B30;
|
||||
}
|
||||
|
||||
/* Beschriftungen: iOS-Gruppenüberschrift statt Sperrschrift */
|
||||
.ads-eyebrow,.ads-label{font-size:13px;letter-spacing:.01em;text-transform:none;color:var(--fg3)}
|
||||
.ads-title{font-size:22px;letter-spacing:-.01em}
|
||||
|
||||
/* Kacheln und Listen */
|
||||
.ads-tile{padding:16px 18px;margin-bottom:14px}
|
||||
.ads-tile--flat{padding:0}
|
||||
.ads-row{min-height:44px;padding:11px 0;border-bottom:1px solid var(--ios-sep)}
|
||||
.ads-row dt,.ads-row__k{font-size:16px;color:var(--fg)}
|
||||
.ads-row dd,.ads-row__v{font-size:16px;color:var(--fg2)}
|
||||
.ads-leaf{border-bottom:1px solid var(--ios-sep)}
|
||||
.ads-leaf__k{font-size:16px}
|
||||
.ads-leaf__v{font-size:15px}
|
||||
.ads-acc-h{border-bottom:1px solid var(--ios-sep)}
|
||||
.ads-acc-h__k{font-size:16px}
|
||||
.ads-acc-h__v{font-size:12.5px}
|
||||
.ads-chev,.ads-tile__go{stroke:var(--ios-grau);stroke-width:1.6}
|
||||
.ads-feld{min-height:44px;border-bottom:1px solid var(--ios-sep)}
|
||||
.ads-feld input,.ads-feld select{background:transparent;border:0;font-size:16px}
|
||||
.ads-feld input:focus,.ads-feld select:focus{background:var(--ios-fill)}
|
||||
/* Die Wischzeile muss das Löschen-Feld darunter verdecken */
|
||||
.ads-swipe-content{background:var(--tile-deckend)}
|
||||
|
||||
/* Bedienelemente */
|
||||
.ads-action{border:0;border-radius:14px;padding:15px;background:var(--ios-tint);color:#fff;
|
||||
font-size:16px;font-weight:400;letter-spacing:0;text-transform:none}
|
||||
.ads-action:hover:not(:disabled){border-color:transparent;filter:brightness(1.08)}
|
||||
.ads-action--destructive{background:var(--ios-fill);color:var(--bad);border:0}
|
||||
.ads-action--destructive:hover:not(:disabled){background:var(--bad);color:#fff;filter:none}
|
||||
|
||||
.ads-seg{gap:0;padding:2px;background:var(--ios-fill);border-radius:9px}
|
||||
.ads-seg button{border:0;border-radius:7px;padding:7px 10px;font-size:13px;letter-spacing:0;
|
||||
text-transform:none;color:var(--fg2)}
|
||||
.ads-seg button.ads-seg__on{background:var(--tile-2);color:var(--fg);border-color:transparent}
|
||||
|
||||
.ads-switch{width:51px;height:31px}
|
||||
.ads-switch__track{background:var(--ios-fill);border:0}
|
||||
.ads-switch__track::before{left:2px;top:2px;width:27px;height:27px}
|
||||
.ads-switch input:checked+.ads-switch__track{background:var(--ios-tint);border-color:transparent}
|
||||
.ads-switch input:checked+.ads-switch__track::before{transform:translateX(20px)}
|
||||
|
||||
.ads-pill{border:0;background:var(--ios-fill);color:var(--fg2);font-size:12px;
|
||||
letter-spacing:.01em;text-transform:none;padding:5px 11px}
|
||||
.ads-pill--work{background:rgba(245,5,55,.16);color:var(--ios-tint);border-color:transparent}
|
||||
|
||||
.ads-dot{width:10px;height:10px}
|
||||
.ads-status__t{font-size:16px}
|
||||
.ads-balken{height:6px;background:var(--ios-fill)}
|
||||
.ads-balken__fill,.ads-balken>i,.ads-balken>div{background:var(--ok)}
|
||||
.ads-quad__l{font-size:11px;letter-spacing:.01em;text-transform:none}
|
||||
|
||||
/* Der Platzhalterrahmen muss in seine Bildfläche passen */
|
||||
.ads-platzhalter{box-sizing:border-box}
|
||||
|
||||
/* Menüs, Popups */
|
||||
.ads-popup{border:0;border-radius:14px;background:rgba(40,44,52,.94);
|
||||
-webkit-backdrop-filter:saturate(180%) blur(22px);backdrop-filter:saturate(180%) blur(22px)}
|
||||
.ads-root[data-theme="tag"] .ads-popup{background:rgba(250,250,252,.94)}
|
||||
.ads-popup-item{font-size:16px;padding:13px 16px;border-bottom:1px solid var(--ios-sep)}
|
||||
|
||||
/* Tab-Leiste */
|
||||
.ads-tabbar{border-top:1px solid var(--ios-sep);padding-bottom:6px;
|
||||
background:rgba(12,16,20,.72);
|
||||
-webkit-backdrop-filter:saturate(180%) blur(24px);backdrop-filter:saturate(180%) blur(24px)}
|
||||
.ads-root[data-theme="tag"] .ads-tabbar{background:rgba(242,242,247,.78)}
|
||||
.ads-tab{border-top:0;padding:9px 2px 7px;gap:3px;color:var(--ios-grau)}
|
||||
.ads-tab svg{width:26px;height:26px}
|
||||
.ads-tab--on{color:var(--ios-tint);border-top-color:transparent}
|
||||
.ads-tab__label{font-size:10px;letter-spacing:0;text-transform:none}
|
||||
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>DataMetric360 Panel</title>
|
||||
<style>
|
||||
html, body { margin: 0; height: 100%; background: #0C1014; overflow: hidden; }
|
||||
audi-dashboard-panel { display: block; width: 100%; height: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="audi-dashboard-app.js"></script>
|
||||
<script>
|
||||
/* Das Panel erst nach dem Skript erzeugen: liegt es schon im HTML, wertet
|
||||
customElements.define() es noch waehrend der Skriptauswertung auf und
|
||||
connectedCallback greift auf MOCK_HASS zu, bevor die Konstante steht. */
|
||||
document.body.appendChild(document.createElement('audi-dashboard-panel'));
|
||||
/* Eigenes Dokument je Rahmen: audi-dashboard-app.js haelt ROOT/HOST/route
|
||||
modulweit, zwei Panels im selben Dokument wuerden sich ueberschreiben.
|
||||
Ein iframe pro Groesse gibt jeder Fassung ihren eigenen Zustand. */
|
||||
const panel = document.querySelector('audi-dashboard-panel');
|
||||
panel.dataset.theme = 'nacht';
|
||||
|
||||
/* iOS/CI-Auflage in den Shadow-Root haengen, nach der eigenen audi-dashboard.css */
|
||||
(function auflage() {
|
||||
if (!panel.shadowRoot) { setTimeout(auflage, 100); return; }
|
||||
if (panel.shadowRoot.querySelector('link[data-ios]')) return;
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.dataset.ios = '1';
|
||||
link.href = 'audi-dashboard-ios.css';
|
||||
panel.shadowRoot.appendChild(link);
|
||||
})();
|
||||
|
||||
function zeige(screen) {
|
||||
const r = panel.shadowRoot;
|
||||
if (!r) return false;
|
||||
if (screen === 'einst') {
|
||||
const b = r.getElementById('ringsbtn');
|
||||
if (!b) return false;
|
||||
b.click();
|
||||
return true;
|
||||
}
|
||||
const tab = r.querySelector('.tab[data-tab="' + screen + '"]');
|
||||
if (!tab) return false;
|
||||
tab.click();
|
||||
return true;
|
||||
}
|
||||
|
||||
let letzterScreen = null;
|
||||
window.addEventListener('message', (e) => {
|
||||
const m = e.data && e.data.dm360;
|
||||
if (!m) return;
|
||||
if (m.theme) panel.dataset.theme = m.theme;
|
||||
if (m.screen && m.screen !== letzterScreen) {
|
||||
let n = 0;
|
||||
const t = setInterval(() => { if (zeige(m.screen) || ++n > 40) clearInterval(t); }, 80);
|
||||
letzterScreen = m.screen;
|
||||
}
|
||||
});
|
||||
|
||||
/* Elternseite anstupsen, sobald das Panel steht */
|
||||
(function bereit(n) {
|
||||
if (panel.shadowRoot && panel.shadowRoot.querySelector('.phone')) {
|
||||
parent.postMessage({ dm360bereit: true }, '*');
|
||||
return;
|
||||
}
|
||||
if (n < 80) setTimeout(() => bereit(n + 1), 100);
|
||||
})(0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,352 @@
|
||||
// @ds-adherence-ignore -- omelette starter scaffold (raw elements/hex/px by design)
|
||||
// Copied omelette starter. Re-running copy_starter_component with this kind overwrites this file with the latest version (page content is unaffected).
|
||||
|
||||
/* BEGIN USAGE */
|
||||
// iOS.jsx — Simplified iOS 26 (Liquid Glass) device frame
|
||||
// Based on the iOS 26 UI Kit + Figma status bar spec. No assets, no deps.
|
||||
// Exports (to window): IOSDevice, IOSStatusBar, IOSNavBar, IOSGlassPill, IOSList, IOSListRow, IOSKeyboard
|
||||
//
|
||||
// Usage — wrap your screen content in <IOSDevice> to get the bezel, status bar
|
||||
// and home indicator (props: title, dark, keyboard):
|
||||
//
|
||||
// <IOSDevice title="Settings">
|
||||
// ...your screen content...
|
||||
// </IOSDevice>
|
||||
// <IOSDevice dark title="Search" keyboard>…</IOSDevice>
|
||||
/* END USAGE */
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Status bar
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSStatusBar({ dark = false, time = '9:41' }) {
|
||||
const c = dark ? '#fff' : '#000';
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', gap: 154, alignItems: 'center', justifyContent: 'center',
|
||||
padding: '21px 24px 19px', boxSizing: 'border-box',
|
||||
position: 'relative', zIndex: 20, width: '100%',
|
||||
}}>
|
||||
<div style={{ flex: 1, height: 22, display: 'flex', alignItems: 'center', justifyContent: 'center', paddingTop: 1.5 }}>
|
||||
<span style={{
|
||||
fontFamily: '-apple-system, "SF Pro", system-ui', fontWeight: 590,
|
||||
fontSize: 17, lineHeight: '22px', color: c,
|
||||
}}>{time}</span>
|
||||
</div>
|
||||
<div style={{ flex: 1, height: 22, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7, paddingTop: 1, paddingRight: 1 }}>
|
||||
<svg width="19" height="12" viewBox="0 0 19 12">
|
||||
<rect x="0" y="7.5" width="3.2" height="4.5" rx="0.7" fill={c}/>
|
||||
<rect x="4.8" y="5" width="3.2" height="7" rx="0.7" fill={c}/>
|
||||
<rect x="9.6" y="2.5" width="3.2" height="9.5" rx="0.7" fill={c}/>
|
||||
<rect x="14.4" y="0" width="3.2" height="12" rx="0.7" fill={c}/>
|
||||
</svg>
|
||||
<svg width="17" height="12" viewBox="0 0 17 12">
|
||||
<path d="M8.5 3.2C10.8 3.2 12.9 4.1 14.4 5.6L15.5 4.5C13.7 2.7 11.2 1.5 8.5 1.5C5.8 1.5 3.3 2.7 1.5 4.5L2.6 5.6C4.1 4.1 6.2 3.2 8.5 3.2Z" fill={c}/>
|
||||
<path d="M8.5 6.8C9.9 6.8 11.1 7.3 12 8.2L13.1 7.1C11.8 5.9 10.2 5.1 8.5 5.1C6.8 5.1 5.2 5.9 3.9 7.1L5 8.2C5.9 7.3 7.1 6.8 8.5 6.8Z" fill={c}/>
|
||||
<circle cx="8.5" cy="10.5" r="1.5" fill={c}/>
|
||||
</svg>
|
||||
<svg width="27" height="13" viewBox="0 0 27 13">
|
||||
<rect x="0.5" y="0.5" width="23" height="12" rx="3.5" stroke={c} strokeOpacity="0.35" fill="none"/>
|
||||
<rect x="2" y="2" width="20" height="9" rx="2" fill={c}/>
|
||||
<path d="M25 4.5V8.5C25.8 8.2 26.5 7.2 26.5 6.5C26.5 5.8 25.8 4.8 25 4.5Z" fill={c} fillOpacity="0.4"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Liquid glass pill — blur + tint + shine
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSGlassPill({ children, dark = false, style = {} }) {
|
||||
return (
|
||||
<div style={{
|
||||
height: 44, minWidth: 44, borderRadius: 9999,
|
||||
position: 'relative', overflow: 'hidden',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
boxShadow: dark
|
||||
? '0 2px 6px rgba(0,0,0,0.35), 0 6px 16px rgba(0,0,0,0.2)'
|
||||
: '0 1px 3px rgba(0,0,0,0.07), 0 3px 10px rgba(0,0,0,0.06)',
|
||||
...style,
|
||||
}}>
|
||||
{/* blur + tint */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 9999,
|
||||
backdropFilter: 'blur(12px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||||
background: dark ? 'rgba(120,120,128,0.28)' : 'rgba(255,255,255,0.5)',
|
||||
}} />
|
||||
{/* shine */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 9999,
|
||||
boxShadow: dark
|
||||
? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.15), inset -1px -1px 1px rgba(255,255,255,0.08)'
|
||||
: 'inset 1.5px 1.5px 1px rgba(255,255,255,0.7), inset -1px -1px 1px rgba(255,255,255,0.4)',
|
||||
border: dark ? '0.5px solid rgba(255,255,255,0.15)' : '0.5px solid rgba(0,0,0,0.06)',
|
||||
}} />
|
||||
<div style={{ position: 'relative', zIndex: 1, display: 'flex', alignItems: 'center', padding: '0 4px' }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Navigation bar — glass pills + large title
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSNavBar({ title = 'Title', dark = false, trailingIcon = true }) {
|
||||
const muted = dark ? 'rgba(255,255,255,0.6)' : '#404040';
|
||||
const text = dark ? '#fff' : '#000';
|
||||
const pillIcon = (content) => (
|
||||
<IOSGlassPill dark={dark}>
|
||||
<div style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
{content}
|
||||
</div>
|
||||
</IOSGlassPill>
|
||||
);
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 10,
|
||||
paddingTop: 62, paddingBottom: 10, position: 'relative', zIndex: 5,
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
padding: '0 16px',
|
||||
}}>
|
||||
{/* back chevron */}
|
||||
{pillIcon(
|
||||
<svg width="12" height="20" viewBox="0 0 12 20" fill="none" style={{ marginLeft: -1 }}>
|
||||
<path d="M10 2L2 10l8 8" stroke={muted} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
)}
|
||||
{/* trailing ellipsis */}
|
||||
{trailingIcon && pillIcon(
|
||||
<svg width="22" height="6" viewBox="0 0 22 6">
|
||||
<circle cx="3" cy="3" r="2.5" fill={muted}/>
|
||||
<circle cx="11" cy="3" r="2.5" fill={muted}/>
|
||||
<circle cx="19" cy="3" r="2.5" fill={muted}/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
{/* large title */}
|
||||
<div style={{
|
||||
padding: '0 16px',
|
||||
fontFamily: '-apple-system, system-ui',
|
||||
fontSize: 34, fontWeight: 700, lineHeight: '41px',
|
||||
color: text, letterSpacing: 0.4,
|
||||
}}>{title}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Grouped list (inset card, r:26) + row (52px)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSListRow({ title, detail, icon, chevron = true, isLast = false, dark = false }) {
|
||||
const text = dark ? '#fff' : '#000';
|
||||
const sec = dark ? 'rgba(235,235,245,0.6)' : 'rgba(60,60,67,0.6)';
|
||||
const ter = dark ? 'rgba(235,235,245,0.3)' : 'rgba(60,60,67,0.3)';
|
||||
const sep = dark ? 'rgba(84,84,88,0.65)' : 'rgba(60,60,67,0.12)';
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', minHeight: 52,
|
||||
padding: '0 16px', position: 'relative',
|
||||
fontFamily: '-apple-system, system-ui', fontSize: 17,
|
||||
letterSpacing: -0.43,
|
||||
}}>
|
||||
{icon && (
|
||||
<div style={{
|
||||
width: 30, height: 30, borderRadius: 7, background: icon,
|
||||
marginRight: 12, flexShrink: 0,
|
||||
}} />
|
||||
)}
|
||||
<div style={{ flex: 1, color: text }}>{title}</div>
|
||||
{detail && <span style={{ color: sec, marginRight: 6 }}>{detail}</span>}
|
||||
{chevron && (
|
||||
<svg width="8" height="14" viewBox="0 0 8 14" style={{ flexShrink: 0 }}>
|
||||
<path d="M1 1l6 6-6 6" stroke={ter} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
)}
|
||||
{!isLast && (
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 0, right: 0,
|
||||
left: icon ? 58 : 16, height: 0.5, background: sep,
|
||||
}} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IOSList({ header, children, dark = false }) {
|
||||
const hc = dark ? 'rgba(235,235,245,0.6)' : 'rgba(60,60,67,0.6)';
|
||||
const bg = dark ? '#1C1C1E' : '#fff';
|
||||
return (
|
||||
<div>
|
||||
{header && (
|
||||
<div style={{
|
||||
fontFamily: '-apple-system, system-ui', fontSize: 13,
|
||||
color: hc, textTransform: 'uppercase',
|
||||
padding: '8px 36px 6px', letterSpacing: -0.08,
|
||||
}}>{header}</div>
|
||||
)}
|
||||
<div style={{
|
||||
background: bg, borderRadius: 26,
|
||||
margin: '0 16px', overflow: 'hidden',
|
||||
}}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Device frame
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSDevice({
|
||||
children, width = 402, height = 874, dark = false,
|
||||
title, keyboard = false,
|
||||
}) {
|
||||
return (
|
||||
// data-om-starter: inert presence marker — Claude Design's starter-usage
|
||||
// probe reads it; it renders nothing. Keep it on this root element.
|
||||
<div data-om-starter="ios-frame" style={{
|
||||
width, height, borderRadius: 48, overflow: 'hidden',
|
||||
position: 'relative', background: dark ? '#000' : '#F2F2F7',
|
||||
boxShadow: '0 40px 80px rgba(0,0,0,0.18), 0 0 0 1px rgba(0,0,0,0.12)',
|
||||
fontFamily: '-apple-system, system-ui, sans-serif',
|
||||
WebkitFontSmoothing: 'antialiased',
|
||||
}}>
|
||||
{/* dynamic island */}
|
||||
<div style={{
|
||||
position: 'absolute', top: 11, left: '50%', transform: 'translateX(-50%)',
|
||||
width: 126, height: 37, borderRadius: 24, background: '#000', zIndex: 50,
|
||||
}} />
|
||||
{/* status bar (absolute) */}
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, zIndex: 10 }}>
|
||||
<IOSStatusBar dark={dark} />
|
||||
</div>
|
||||
{/* nav + content */}
|
||||
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
{title !== undefined && <IOSNavBar title={title} dark={dark} />}
|
||||
<div style={{ flex: 1, overflow: 'auto' }}>{children}</div>
|
||||
{keyboard && <IOSKeyboard dark={dark} />}
|
||||
</div>
|
||||
{/* home indicator — always on top */}
|
||||
<div style={{
|
||||
position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 60,
|
||||
height: 34, display: 'flex', justifyContent: 'center', alignItems: 'flex-end',
|
||||
paddingBottom: 8, pointerEvents: 'none',
|
||||
}}>
|
||||
<div style={{
|
||||
width: 139, height: 5, borderRadius: 100,
|
||||
background: dark ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.25)',
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Keyboard — iOS 26 liquid glass
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
function IOSKeyboard({ dark = false }) {
|
||||
const glyph = dark ? 'rgba(255,255,255,0.7)' : '#595959';
|
||||
const sugg = dark ? 'rgba(255,255,255,0.6)' : '#333';
|
||||
const keyBg = dark ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.85)';
|
||||
|
||||
// special-key icons
|
||||
const icons = {
|
||||
shift: <svg width="19" height="17" viewBox="0 0 19 17"><path d="M9.5 1L1 9.5h4.5V16h8V9.5H18L9.5 1z" fill={glyph}/></svg>,
|
||||
del: <svg width="23" height="17" viewBox="0 0 23 17"><path d="M7 1h13a2 2 0 012 2v11a2 2 0 01-2 2H7l-6-7.5L7 1z" fill="none" stroke={glyph} strokeWidth="1.6" strokeLinejoin="round"/><path d="M10 5l7 7M17 5l-7 7" stroke={glyph} strokeWidth="1.6" strokeLinecap="round"/></svg>,
|
||||
ret: <svg width="20" height="14" viewBox="0 0 20 14"><path d="M18 1v6H4m0 0l4-4M4 7l4 4" fill="none" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
|
||||
};
|
||||
|
||||
const key = (content, { w, flex, ret, fs = 25, k } = {}) => (
|
||||
<div key={k} style={{
|
||||
height: 42, borderRadius: 8.5,
|
||||
flex: flex ? 1 : undefined, width: w, minWidth: 0,
|
||||
background: ret ? '#08f' : keyBg,
|
||||
boxShadow: '0 1px 0 rgba(0,0,0,0.075)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontFamily: '-apple-system, "SF Compact", system-ui',
|
||||
fontSize: fs, fontWeight: 458, color: ret ? '#fff' : glyph,
|
||||
}}>{content}</div>
|
||||
);
|
||||
|
||||
const row = (keys, pad = 0) => (
|
||||
<div style={{ display: 'flex', gap: 6.5, justifyContent: 'center', padding: `0 ${pad}px` }}>
|
||||
{keys.map(l => key(l, { flex: true, k: l }))}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'relative', zIndex: 15, borderRadius: 27, overflow: 'hidden',
|
||||
padding: '11px 0 2px',
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
||||
boxShadow: dark
|
||||
? '0 -2px 20px rgba(0,0,0,0.09)'
|
||||
: '0 -1px 6px rgba(0,0,0,0.018), 0 -3px 20px rgba(0,0,0,0.012)',
|
||||
}}>
|
||||
{/* liquid glass bg — same recipe as nav pills */}
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 27,
|
||||
backdropFilter: 'blur(12px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||||
background: dark ? 'rgba(120,120,128,0.14)' : 'rgba(255,255,255,0.25)',
|
||||
}} />
|
||||
<div style={{
|
||||
position: 'absolute', inset: 0, borderRadius: 27,
|
||||
boxShadow: dark
|
||||
? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.15)'
|
||||
: 'inset 1.5px 1.5px 1px rgba(255,255,255,0.7), inset -1px -1px 1px rgba(255,255,255,0.4)',
|
||||
border: dark ? '0.5px solid rgba(255,255,255,0.15)' : '0.5px solid rgba(0,0,0,0.06)',
|
||||
pointerEvents: 'none',
|
||||
}} />
|
||||
|
||||
{/* autocorrect bar */}
|
||||
<div style={{
|
||||
display: 'flex', gap: 20, alignItems: 'center',
|
||||
padding: '8px 22px 13px', width: '100%', boxSizing: 'border-box',
|
||||
position: 'relative',
|
||||
}}>
|
||||
{['"The"', 'the', 'to'].map((w, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{i > 0 && <div style={{ width: 1, height: 25, background: '#ccc', opacity: 0.3 }} />}
|
||||
<div style={{
|
||||
flex: 1, textAlign: 'center',
|
||||
fontFamily: '-apple-system, system-ui', fontSize: 17,
|
||||
color: sugg, letterSpacing: -0.43, lineHeight: '22px',
|
||||
}}>{w}</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* key layout */}
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 13,
|
||||
padding: '0 6.5px', width: '100%', boxSizing: 'border-box',
|
||||
position: 'relative',
|
||||
}}>
|
||||
{row(['q','w','e','r','t','y','u','i','o','p'])}
|
||||
{row(['a','s','d','f','g','h','j','k','l'], 20)}
|
||||
<div style={{ display: 'flex', gap: 14.25, alignItems: 'center' }}>
|
||||
{key(icons.shift, { w: 45, k: 'shift' })}
|
||||
<div style={{ display: 'flex', gap: 6.5, flex: 1 }}>
|
||||
{['z','x','c','v','b','n','m'].map(l => key(l, { flex: true, k: l }))}
|
||||
</div>
|
||||
{key(icons.del, { w: 45, k: 'del' })}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
||||
{key('ABC', { w: 92.25, fs: 18, k: 'abc' })}
|
||||
{key('', { flex: true, k: 'space' })}
|
||||
{key(icons.ret, { w: 92.25, ret: true, k: 'ret' })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* bottom spacer (emoji+mic area, icons omitted) */}
|
||||
<div style={{ height: 56, width: '100%', position: 'relative' }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(window, {
|
||||
IOSDevice, IOSStatusBar, IOSNavBar, IOSGlassPill, IOSList, IOSListRow, IOSKeyboard,
|
||||
});
|
||||
@@ -0,0 +1,209 @@
|
||||
/* ==================================================================
|
||||
iOS-Auflage über audi-dashboard.css — Audi CI (Rot F50537, Audi Type,
|
||||
dunkle Fläche) in der Formensprache einer modernen iOS-App:
|
||||
Große Titel, gruppierte Listen mit 44-px-Zeilen, transluente Tab-Leiste,
|
||||
iOS-Schalter und -Segmente, gefüllte Aktionsknöpfe.
|
||||
Reine Auflage: audi-dashboard.css und die App-Logik bleiben unberührt.
|
||||
================================================================== */
|
||||
|
||||
:host{
|
||||
display:block;height:100%;container-type:inline-size;
|
||||
font-family:"Audi Type",Helvetica,Arial,sans-serif;
|
||||
--ios-tint:#F50537;
|
||||
--ios-sep:rgba(255,255,255,.13);
|
||||
--ios-grau:#8E8E93;
|
||||
--ios-fill:rgba(120,120,128,.24);
|
||||
--r-tile:16px;
|
||||
/* deckende Kachelfarbe fuer Flaechen, die etwas verdecken muessen
|
||||
(Wischzeile ueber dem Loeschen-Feld, Kartenfeld) */
|
||||
--tile-deckend:#171B21;
|
||||
}
|
||||
:host([data-theme="nacht"]){
|
||||
--canvas:#0C1014;
|
||||
--tile:rgba(255,255,255,.055);
|
||||
--tile-2:rgba(255,255,255,.10);
|
||||
--line:rgba(255,255,255,.13);
|
||||
--line-strong:rgba(255,255,255,.24);
|
||||
--fg:#FFFFFF; --fg2:#BFC4CC; --fg3:#8E8E93;
|
||||
--ok:#30D158; --warn:#FFD60A; --bad:#FF453A;
|
||||
}
|
||||
:host([data-theme="tag"]){
|
||||
--canvas:#F2F2F7;
|
||||
--tile:#FFFFFF;
|
||||
--tile-2:#EFEFF4;
|
||||
--line:rgba(60,60,67,.13);
|
||||
--line-strong:rgba(60,60,67,.24);
|
||||
--fg:#000000; --fg2:#3C3C43; --fg3:#8E8E93;
|
||||
--ok:#34C759; --warn:#FF9F0A; --bad:#FF3B30;
|
||||
--ios-sep:rgba(60,60,67,.16);
|
||||
--ios-fill:rgba(120,120,128,.16);
|
||||
--tile-deckend:#FFFFFF;
|
||||
}
|
||||
|
||||
body{padding:0;background:var(--canvas)}
|
||||
html,body{height:100%}
|
||||
/* Der von _aufbauen() eingefuegte Wrapper zwischen Host und .phone */
|
||||
:host > div{height:100%}
|
||||
.phone{width:100%;max-width:none;height:100%;border-radius:0;
|
||||
font-family:"Audi Type",Helvetica,Arial,sans-serif;
|
||||
-webkit-font-smoothing:antialiased;letter-spacing:0}
|
||||
|
||||
/* ---------------- Navigationsleiste ---------------- */
|
||||
/* Sichere Bereiche des Geräterahmens: Statusleiste oben, Home-Indikator unten */
|
||||
.topbar{align-items:center;gap:4px;padding:56px 20px 4px}
|
||||
.head{display:flex;flex-direction:column}
|
||||
.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)}
|
||||
.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)}
|
||||
.themebtn:hover{background:var(--tile-2);color:var(--fg)}
|
||||
.rings svg{height:17px}
|
||||
/* Unterseite: kompakter, zentrierter Titel wie in einer iOS-Navigationsleiste */
|
||||
.phone:has(.back.on) .topbar{padding:54px 16px 6px;gap:8px}
|
||||
.phone:has(.back.on) .head{align-items:center}
|
||||
.phone:has(.back.on) .title{font-size:17px;font-weight:400;letter-spacing:-.01em;text-align:center}
|
||||
.phone:has(.back.on) .sync{margin-top:2px;font-size:11.5px}
|
||||
|
||||
main{padding:0 16px 30px;scroll-behavior:smooth}
|
||||
|
||||
/* ---------------- Karten und Listen ---------------- */
|
||||
.tile{padding:16px 18px;margin-bottom:14px;border-radius:var(--r-tile)}
|
||||
.tile.flat{padding:0}
|
||||
.tilebtn:hover{background:var(--tile-2)}
|
||||
.tilebtn .go{top:50%;margin-top:-5px;right:18px;stroke:var(--ios-grau);stroke-width:1.6}
|
||||
.label{font-size:13px;letter-spacing:.01em;text-transform:none;color:var(--fg3)}
|
||||
.row{min-height:44px;padding:11px 0;border-bottom:1px solid var(--ios-sep)}
|
||||
.row dt{font-size:16px;color:var(--fg)}
|
||||
.row dd{font-size:16px;color:var(--fg2)}
|
||||
.row dd small{font-size:12.5px;margin-top:2px}
|
||||
.rows-fest dt{flex:0 0 150px}
|
||||
.chev{stroke:var(--ios-grau);stroke-width:1.6}
|
||||
|
||||
.acc-h{padding:13px 0;border-bottom:1px solid var(--ios-sep)}
|
||||
.acc-h .k{font-size:16px}
|
||||
.acc-h .v{font-size:12.5px}
|
||||
.leaf{padding:12px 0 12px 40px;border-bottom:1px solid var(--ios-sep)}
|
||||
.leaf .k{font-size:16px}
|
||||
.leaf .v{font-size:15px}
|
||||
.leaf .k small,.leaf .v small{font-size:12.5px}
|
||||
.mark::before,.mark::after{background:var(--ios-grau)}
|
||||
|
||||
/* ---------------- Bedienelemente ---------------- */
|
||||
.seg{gap:0;padding:2px;background:var(--ios-fill);border-radius:9px;margin-top:14px}
|
||||
.seg button{border:0;border-radius:7px;padding:7px 8px;font-size:13px;font-weight:400;
|
||||
letter-spacing:0;text-transform:none;color:var(--fg2)}
|
||||
.seg button.on{background:var(--tile-2);color:var(--fg);box-shadow:0 1px 3px rgba(0,0,0,.25)}
|
||||
|
||||
.aktion{margin-top:18px;padding:15px;border:0;border-radius:14px;
|
||||
background:var(--ios-tint);color:#fff;font-size:16px;font-weight:400;
|
||||
letter-spacing:0;text-transform:none}
|
||||
.aktion:hover{filter:brightness(1.08)}
|
||||
.aktion.loeschen{background:var(--ios-fill);color:var(--bad);border:0}
|
||||
.aktion.loeschen:hover{background:var(--bad);color:#fff;filter:none}
|
||||
|
||||
.feld{min-height:44px;padding:9px 0;border-bottom:1px solid var(--ios-sep)}
|
||||
.feld label{font-size:16px;color:var(--fg)}
|
||||
.feld input,.feld select{background:transparent;border:0;border-radius:8px;font-size:16px;
|
||||
padding:8px 4px;color:var(--fg2)}
|
||||
.feld input:focus,.feld select:focus{background:var(--ios-fill);color:var(--fg)}
|
||||
.mitEinheit input{background:var(--ios-fill);padding:8px 10px}
|
||||
.notiz{background:var(--ios-fill);border:0;border-radius:12px;font-size:16px}
|
||||
|
||||
.switch{width:51px;height:31px}
|
||||
.switch .track{background:var(--ios-fill);border:0}
|
||||
.switch .track::before{left:2px;top:2px;width:27px;height:27px;box-shadow:0 2px 6px rgba(0,0,0,.3)}
|
||||
.switch input:checked+.track{background:var(--ios-tint)}
|
||||
.switch input:checked+.track::before{transform:translateX(20px)}
|
||||
|
||||
.pill{font-size:12px;letter-spacing:.01em;text-transform:none;padding:5px 11px;
|
||||
border:0;background:var(--ios-fill);color:var(--fg2)}
|
||||
.pill.work{background:rgba(245,5,55,.16);color:var(--ios-tint)}
|
||||
|
||||
.status{padding:14px 0 2px}
|
||||
.status .t{font-size:16px}
|
||||
.dot{width:10px;height:10px}
|
||||
|
||||
/* ---------------- Zahlen (Audi CI bleibt führend) ---------------- */
|
||||
.fig{letter-spacing:-.03em}
|
||||
.unit{color:var(--fg3)}
|
||||
.balken{height:6px;background:var(--ios-fill)}
|
||||
.balken i{background:var(--ok)}
|
||||
.quad .l{font-size:11px;letter-spacing:.01em;text-transform:none}
|
||||
.legend span{font-size:12.5px}
|
||||
|
||||
/* ---------------- Menüs und Popups ---------------- */
|
||||
.bildmenu,.sdpopup{border:0;border-radius:14px;background:rgba(40,44,52,.92);
|
||||
-webkit-backdrop-filter:saturate(180%) blur(22px);backdrop-filter:saturate(180%) blur(22px);
|
||||
box-shadow:0 12px 40px rgba(0,0,0,.5)}
|
||||
:host([data-theme="tag"]) .bildmenu,:host([data-theme="tag"]) .sdpopup{background:rgba(250,250,252,.92)}
|
||||
.bildmenu button{padding:13px 16px;font-size:16px;border-bottom:1px solid var(--ios-sep)}
|
||||
|
||||
/* ---------------- Tab-Leiste ---------------- */
|
||||
.tabbar{border-top:1px solid var(--ios-sep);padding-bottom:26px;
|
||||
background:rgba(12,16,20,.72);
|
||||
-webkit-backdrop-filter:saturate(180%) blur(24px);backdrop-filter:saturate(180%) blur(24px)}
|
||||
:host([data-theme="tag"]) .tabbar{background:rgba(242,242,247,.78)}
|
||||
.tab{border-top:0;margin-top:0;padding:9px 2px 7px;gap:3px;
|
||||
font-size:10px;letter-spacing:0;text-transform:none;color:var(--ios-grau)}
|
||||
/* Beschriftung folgt weiter dem Schalter in den Einstellungen */
|
||||
.tabbar.ohne .tab span{display:none}
|
||||
.tab svg{width:26px;height:26px;stroke-width:1.5}
|
||||
.tab.on{color:var(--ios-tint);border-top-color:transparent}
|
||||
.tab.on svg polygon{fill:var(--ios-tint);stroke:var(--ios-tint)}
|
||||
.tabbar.ohne .tab{padding:14px 2px 12px}
|
||||
|
||||
:focus-visible{outline:2px solid var(--ios-tint);outline-offset:2px}
|
||||
|
||||
/* Die Wischzeile muss das darunterliegende Loeschen-Feld verdecken - das
|
||||
halbtransparente --tile der Auflage liess es durchscheinen. */
|
||||
.swipe-wrap .swipe-content{background:var(--tile-deckend)}
|
||||
.mapbox,.mapbox .leaflet-container{background:var(--tile-deckend)}
|
||||
|
||||
/* ==================================================================
|
||||
Große Bildschirme (Tablet, Desktop): dieselbe App, aber die Tab-Leiste
|
||||
wird zur seitlichen Navigation und der Inhalt bekommt eine ruhige,
|
||||
begrenzte Spalte. Container-Abfrage statt @media, weil die App in einem
|
||||
eigenen Rahmen sitzt und nicht die Fensterbreite messen darf. Schwelle 860 px.
|
||||
================================================================== */
|
||||
@container (min-width:860px){
|
||||
.phone{display:grid;grid-template-columns:264px minmax(0,1fr);
|
||||
grid-template-rows:auto auto minmax(0,1fr)}
|
||||
.topbar{grid-column:2;grid-row:1;padding:38px 44px 8px}
|
||||
.phone:has(.back.on) .topbar{grid-column:2;grid-row:1;padding:34px 44px 8px;justify-content:flex-start}
|
||||
.phone:has(.back.on) .head{align-items:flex-start}
|
||||
.phone:has(.back.on) .title{font-size:26px;text-align:left}
|
||||
.title{font-size:34px}
|
||||
.ptr{grid-column:2;grid-row:2}
|
||||
main{grid-column:2;grid-row:3;padding:8px 44px 48px;max-width:860px}
|
||||
|
||||
.tabbar{grid-column:1;grid-row:1 / 4;display:flex;flex-direction:column;gap:4px;
|
||||
align-content:start;width:264px;padding:38px 14px 20px;
|
||||
border-top:0;border-right:1px solid var(--ios-sep);background:none;
|
||||
-webkit-backdrop-filter:none;backdrop-filter:none}
|
||||
.tab{flex-direction:row;justify-content:flex-start;align-items:center;gap:14px;
|
||||
padding:12px 14px;border-radius:12px;font-size:15px;letter-spacing:0}
|
||||
.tab span{display:block}
|
||||
.tab svg{width:22px;height:22px}
|
||||
.tab:hover{background:var(--tile)}
|
||||
.tab.on{background:rgba(245,5,55,.14);color:var(--ios-tint)}
|
||||
.tabbar.ohne .tab span{display:block}
|
||||
.tabbar.ohne .tab{padding:12px 14px}
|
||||
|
||||
.duo{grid-template-columns:repeat(3,minmax(0,1fr))}
|
||||
.carfix,.bildbox.carfix{height:260px}
|
||||
.carfix.klein,.bildbox.carfix.klein{height:220px}
|
||||
.bildgrid{grid-template-columns:repeat(4,1fr)}
|
||||
.quad{gap:14px}
|
||||
}
|
||||
|
||||
/* Fahrzeugbild randlos wie in einer Fahrzeug-App: der Platzhalter bleibt
|
||||
sichtbar, aber ohne technischen Strichrahmen. */
|
||||
.szene{margin-bottom:20px}
|
||||
.bildbox.bild-fehlt .platzhalter{border:0;background:var(--tile);border-radius:var(--r-tile)}
|
||||
.platzhalter-datei{display:none}
|
||||
|
||||
/* Zeilen mit Wert plus Statuspunkt rechts (Sicherheitscheck) ruhiger setzen */
|
||||
.row dd .dot{width:8px;height:8px}
|
||||
Reference in New Issue
Block a user