Dashboard Studio 1.12.1: car card redesigned from scratch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 15:46:25 +01:00
parent 7abf7379d8
commit 2ea437af31
6 changed files with 164 additions and 104 deletions
+80 -55
View File
@@ -974,63 +974,88 @@ body.kiosk #tabnav { top: 6px; max-height: calc(100vh - 20px); }
/* pill button with icon and label (media "Turn on") */
.car-act { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px; border-radius: 12px; background: var(--d-fill); font-size: 14px; font-weight: 500; }
.car-act .i { font-size: 18px; }
/* car: a round gauge with the range in the middle and the car in front */
.car3 { --car-top: color-mix(in srgb, var(--d-accent) 55%, var(--d-text)); --car-bottom: color-mix(in srgb, var(--d-accent) 70%, #000);
display: grid; grid-template-areas: "top" "gauge" "info"; grid-template-rows: auto minmax(0, 1fr) auto; gap: 8px; height: 100%; min-height: 0; }
.car3-top { grid-area: top; display: flex; align-items: center; gap: 8px; min-width: 0; }
.car3-top .name { font-size: 17px; flex: 1; min-width: 0; }
.car3-status { font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 99px; background: var(--d-fill); color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 62%; }
.car3-status.charging { color: var(--ok); background: color-mix(in srgb, var(--ok) 16%, transparent); }
.cg { grid-area: gauge; position: relative; min-height: 0; height: 100%; max-height: 260px; max-width: 100%; justify-self: center; align-self: center; aspect-ratio: 1 / .92; }
.cg > svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.cg > svg path { fill: none; stroke-width: 12; stroke-linecap: round; }
.cg-track { stroke: var(--d-fill); }
.cg-fill { stroke: var(--d-text); }
.cg.charging .cg-fill { stroke: var(--ok); animation: cg-pulse 2.4s ease-in-out infinite; }
.cg.low .cg-fill { stroke: var(--crit); }
@keyframes cg-pulse { 50% { opacity: .65; } }
.cg-limit { stroke: var(--d-text-2); stroke-width: 3; stroke-linecap: round; }
.cg-center { position: absolute; left: 0; right: 0; top: 22%; display: flex; flex-direction: column; align-items: center; }
.cg-range { font-size: clamp(26px, 11cqh, 44px); font-weight: 700; letter-spacing: -0.02em; line-height: 1; }
.cg-range small { font-size: .42em; font-weight: 600; color: var(--d-text-2); margin-left: 3px; letter-spacing: 0; }
.cg-pct { margin-top: 4px; font-size: 14px; font-weight: 600; color: var(--d-text-2); }
.cg.charging .cg-pct { color: var(--ok); }
.cg-car { position: absolute; left: 6%; right: 6%; bottom: 2%; display: flex; justify-content: center; }
.cg-car img { max-width: 100%; max-height: 72px; object-fit: contain; filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .3)); }
.car-art2 { width: 100%; max-height: 74px; }
.car-shadow { fill: rgba(0, 0, 0, .28); }
.car-body2 { fill: url(#carBody); }
.car-glass2 { fill: url(#carGlass); }
.car-line { stroke: rgba(255, 255, 255, .25); stroke-width: 1; }
.car-light { fill: #fff6d6; }
.car-tire { fill: #17181b; } .car-rim { fill: #8e9097; }
.car3.is-charging .cg-car::after { content: ""; position: absolute; left: 20%; right: 20%; bottom: 0; height: 40%; border-radius: 50%; background: radial-gradient(closest-side, color-mix(in srgb, var(--ok) 45%, transparent), transparent); z-index: -1; }
.car3-info { grid-area: info; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.cchips { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.cchip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 99px; background: var(--d-fill); white-space: nowrap; }
.cchip .i { font-size: 14px; color: var(--d-text-2); }
.cbs { display: flex; gap: 6px; }
.cb { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 8px 4px; border-radius: 14px; background: var(--d-fill); }
.cb-ico { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; font-size: 18px; transition: background-color .2s; }
.cb[aria-pressed="true"] .cb-ico { background: var(--d-text); color: var(--d-tile); }
.cb-label { font-size: 12px; font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb-sub { font-size: 11px; color: var(--d-text-2); white-space: nowrap; }
/* short: gauge small on the left, numbers and buttons beside it */
@container (max-height: 250px) {
.car3 { grid-template-areas: "top top" "gauge info"; grid-template-columns: auto 1fr; grid-template-rows: auto 1fr; column-gap: 14px; }
.cg { width: auto; height: 100%; max-width: none; aspect-ratio: 1; }
.cg-car { display: none; }
.cg-center { top: 30%; }
.car3-info { justify-content: center; }
.cchips { justify-content: flex-start; }
/* car: the car itself fills up with the charge level */
.cv { --cv-c: var(--d-accent); display: grid; grid-template-areas: "head" "hero" "art" "stats" "ctrls"; grid-template-rows: auto auto minmax(0, 1fr) auto auto; row-gap: 10px; height: 100%; min-height: 0; min-width: 0; }
.cv.charging { --cv-c: var(--ok); }
.cv.low { --cv-c: var(--crit); }
.cv.fuel:not(.low) { --cv-c: var(--warn); }
.cv-head { grid-area: head; display: flex; align-items: center; gap: 8px; min-width: 0; }
.cv-name { flex: 1; min-width: 0; font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 64%; }
.cv-status i { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.cv-status.ok { color: var(--ok); } .cv-status.crit { color: var(--crit); } .cv-status.accent { color: var(--d-accent); }
.cv-status.ok i { animation: cv-blink 1.6s ease-in-out infinite; }
@keyframes cv-blink { 50% { opacity: .25; } }
.cv-hero { grid-area: hero; display: flex; align-items: flex-end; gap: 14px; min-width: 0; }
.cv-big { font-size: clamp(40px, 19cqw, 68px); font-weight: 700; letter-spacing: -0.045em; line-height: .86; }
.cv-big span { font-size: .42em; font-weight: 600; letter-spacing: 0; color: var(--d-text-2); margin-left: 2px; }
.cv-side { min-width: 0; padding-bottom: 2px; }
.cv-range { font-size: clamp(17px, 7cqw, 24px); font-weight: 650; letter-spacing: -0.02em; line-height: 1.05; white-space: nowrap; }
.cv-range small { font-size: .62em; font-weight: 600; color: var(--d-text-2); margin-left: 3px; letter-spacing: 0; }
.cv-sub { margin-top: 3px; font-size: 12px; font-weight: 500; color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv.charging .cv-sub { color: var(--ok); }
.cv-art { grid-area: art; min-height: 0; display: flex; flex-direction: column; justify-content: center; gap: 10px; }
.cv-svg { display: block; width: 100%; height: 100%; max-height: 150px; min-height: 0; color: var(--cv-c); overflow: visible; }
.cv-shadow { fill: var(--d-text); opacity: .12; }
.cv.charging .cv-shadow { fill: var(--ok); opacity: .35; }
.cv-shell { fill: color-mix(in srgb, var(--d-text) 9%, transparent); }
.cv-edge { fill: none; stroke: color-mix(in srgb, var(--d-text) 28%, transparent); stroke-width: 1.2; stroke-linejoin: round; }
.cv-glass { fill: color-mix(in srgb, var(--d-tile) 62%, transparent); stroke: color-mix(in srgb, var(--d-text) 22%, transparent); stroke-width: 1; stroke-linejoin: round; }
.cv-crease { stroke: color-mix(in srgb, var(--d-text) 14%, transparent); stroke-width: 1; fill: none; }
.cv-lamp { stroke: color-mix(in srgb, var(--d-text) 55%, transparent); stroke-width: 2.4; stroke-linecap: round; }
.cv-tail { stroke: var(--crit); stroke-width: 2.4; stroke-linecap: round; opacity: .8; }
.cv-limit { stroke: var(--d-text); stroke-opacity: .55; stroke-width: 1.6; stroke-dasharray: 3 2.5; }
.cv-port { fill: var(--cv-c); stroke: var(--d-tile); stroke-width: 1.5; }
.cv.charging .cv-port { animation: cv-blink 1.6s ease-in-out infinite; }
.cv-tyre { fill: color-mix(in srgb, var(--d-text) 82%, var(--d-tile)); }
.cv-rim { fill: color-mix(in srgb, var(--d-text) 30%, var(--d-tile)); }
.cv-hub { fill: color-mix(in srgb, var(--d-text) 70%, var(--d-tile)); }
.cv-sweep { fill: #fff; opacity: .35; animation: cv-sweep 2.2s ease-in-out infinite; }
@keyframes cv-sweep { from { transform: translateX(0); } to { transform: translateX(310px); } }
.cv-img { min-height: 0; flex: 1; display: flex; align-items: center; justify-content: center; }
.cv-img img { max-width: 100%; max-height: 100%; object-fit: contain; filter: drop-shadow(0 8px 12px rgba(0, 0, 0, .25)); }
/* the slim battery bar: shown with a photo, and instead of the drawing on short cards */
.cv-bar { position: relative; height: 8px; border-radius: 99px; background: color-mix(in srgb, var(--d-text) 10%, transparent); display: none; flex: none; }
.cv-bar i { position: absolute; inset: 0 auto 0 0; border-radius: inherit; background: var(--cv-c); }
.cv-bar b { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; border-radius: 1px; background: var(--d-text-2); }
.cv.has-img .cv-bar { display: block; }
.cv-stats { grid-area: stats; display: none; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 6px; }
.cv-stat { display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; border-radius: 12px; background: var(--d-fill); min-width: 0; }
.cv-stat span { font-size: 11px; color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-stat b { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-ctrls { grid-area: ctrls; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 6px; }
.cv-ctrl { display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; padding: 4px 2px; border-radius: 12px; }
.cv-ico { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; font-size: 19px; background: var(--d-fill); transition: background-color .2s, color .2s; }
.cv-ctrl.on .cv-ico { background: var(--cv-c); color: #fff; }
.cv-ctrl.warn .cv-ico { background: color-mix(in srgb, var(--warn) 22%, transparent); color: var(--warn); }
.cv-lbl { font-size: 11px; font-weight: 600; color: var(--d-text-2); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .cv-sweep { display: none; } .cv-status.ok i, .cv.charging .cv-port { animation: none; } }
/* tall: room for a row of stats */
@container (min-height: 420px) { .cv-stats { display: grid; } }
/* short: the drawing gives way to a slim bar */
@container (max-height: 260px) {
.cv { row-gap: 8px; grid-template-rows: auto auto auto minmax(0, 1fr) auto; }
.cv-svg, .cv-img { display: none; }
.cv-bar { display: block; }
.cv-ctrls { align-self: end; }
.cv-ico { width: 34px; height: 34px; font-size: 17px; }
}
@container (max-height: 160px) { .cbs { display: none; } }
/* wide: gauge on the left, the rest on the right */
@container (min-width: 480px) and (min-height: 251px) {
.car3 { grid-template-areas: "top top" "gauge info"; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: auto 1fr; column-gap: 20px; }
.car3-info { justify-content: center; }
@container (max-height: 160px) { .cv-ctrls { display: none; } .cv-big { font-size: 40px; } }
/* wide: the car on the left, numbers and controls on the right */
@container (min-width: 460px) and (min-height: 261px) {
.cv { grid-template-areas: "art head" "art hero" "art stats" "art ctrls"; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); grid-template-rows: auto auto 1fr auto; column-gap: 22px; }
.cv-hero { align-self: start; }
.cv-svg { max-height: 190px; }
.cv-stats { display: grid; align-self: center; }
}
@container (max-width: 300px) { .cb-label, .cb-sub { display: none; } .cb { padding: 6px 2px; } }
@container (max-width: 230px) {
.cv-lbl { display: none; }
.cv-hero { flex-direction: column; align-items: flex-start; gap: 6px; }
.cv-ctrls { gap: 4px; }
.cv-ico { width: 34px; height: 34px; font-size: 16px; }
.cv-status { max-width: 58%; }
}
@container (max-width: 170px) { .cv-ico { width: 28px; height: 28px; font-size: 14px; } .cv-status { font-size: 0; gap: 0; } .cv-status i { width: 8px; height: 8px; } }
/* ============================================================ phone dashboards */
body.phone-ui:not(.peek) #topbar { display: none; }