Dashboard Studio 1.13.0: Tesla-style car card with square and wide designs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+116
-79
@@ -974,88 +974,125 @@ 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: 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; }
|
||||
/* car: in the style of the Tesla app. Square cards show the car from the front, wide ones from the side. */
|
||||
.tz { --tz-c: var(--d-text); --tz-paint: color-mix(in srgb, var(--d-text) 22%, #b9bec6);
|
||||
display: grid; grid-template-areas: "head" "art" "charge" "ctrls" "rows"; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto auto auto; row-gap: 10px; height: 100%; min-height: 0; min-width: 0; }
|
||||
.tz.charging { --tz-c: var(--ok); }
|
||||
.tz.low { --tz-c: var(--crit); }
|
||||
.tz.fuel:not(.low) { --tz-c: var(--warn); }
|
||||
.tz-head { grid-area: head; display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
|
||||
.tz-title { flex: 1; min-width: 0; }
|
||||
.tz-name { font-size: clamp(17px, 7.5cqw, 24px); font-weight: 650; letter-spacing: -0.02em; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.tz-status { margin-top: 2px; font-size: 13px; color: var(--d-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.tz.charging .tz-status { color: var(--ok); }
|
||||
.tz-batt { display: grid; grid-template-columns: auto auto; align-items: center; column-gap: 6px; font-size: 14px; font-weight: 600; flex: none; padding-top: 3px; }
|
||||
.tz-brange { grid-column: 1 / -1; justify-self: end; font-size: 12px; font-weight: 500; color: var(--d-text-2); }
|
||||
.tz-bicon { width: 28px; height: 14px; display: block; }
|
||||
.tz-bshell { fill: none; stroke: var(--d-text-2); stroke-width: 1.5; }
|
||||
.tz-blevel { fill: var(--tz-c); }
|
||||
.tz-bnub { stroke: var(--d-text-2); stroke-width: 2; stroke-linecap: round; }
|
||||
.tz-bbolt { fill: var(--d-tile); stroke: var(--d-tile); stroke-width: .6; stroke-linejoin: round; }
|
||||
.tz-big { grid-area: big; display: none; flex-direction: column; gap: 2px; }
|
||||
.tz-pct { font-size: clamp(40px, 10cqw, 64px); font-weight: 700; letter-spacing: -0.045em; line-height: .9; }
|
||||
.tz-pct small { font-size: .42em; font-weight: 600; color: var(--d-text-2); margin-left: 2px; letter-spacing: 0; }
|
||||
.tz-rng { font-size: 15px; font-weight: 500; color: var(--d-text-2); }
|
||||
.tz-art { grid-area: art; min-height: 0; display: flex; align-items: center; justify-content: center; }
|
||||
.tz-front, .tz-side { width: 100%; height: 100%; min-height: 0; }
|
||||
.tz-front { max-height: 190px; } .tz-side { display: none; max-height: 200px; }
|
||||
.tz-img { max-width: 100%; max-height: 100%; object-fit: contain; filter: drop-shadow(0 10px 14px rgba(0, 0, 0, .3)); }
|
||||
.tz-paint { fill: var(--tz-paint); }
|
||||
.tz-shadow { fill: #000; opacity: .3; }
|
||||
.tz.charging .tz-shadow { fill: var(--ok); opacity: .4; }
|
||||
.tz-tyre { fill: #16171a; }
|
||||
.tz-rim { fill: #3b3e45; } .tz-spokes { stroke: #6d717a; stroke-width: 1.6; } .tz-hub { fill: #9a9ea6; }
|
||||
.tz-crease { fill: none; stroke: #000; stroke-opacity: .16; stroke-width: 1; }
|
||||
.tz-handle { stroke: #000; stroke-opacity: .25; stroke-width: 1.6; stroke-linecap: round; }
|
||||
.tz-lamp { fill: #f3f6fb; } .tz-lamp-s { stroke: #f3f6fb; stroke-width: 2.4; stroke-linecap: round; }
|
||||
.tz-tail { stroke: #e0353b; stroke-width: 2.4; stroke-linecap: round; }
|
||||
.tz-intake { fill: #15161a; opacity: .85; }
|
||||
.tz-port { fill: var(--ok); stroke: var(--d-tile); stroke-width: 1.5; }
|
||||
.tz.charging .tz-port, .tz.charging .tz-bar i { animation: tz-pulse 2s ease-in-out infinite; }
|
||||
@keyframes tz-pulse { 50% { opacity: .55; } }
|
||||
.tz-charge { grid-area: charge; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
||||
.tz-charge.tz-idle { display: none; }
|
||||
.tz-cl, .tz-cs { display: flex; justify-content: space-between; gap: 8px; min-width: 0; }
|
||||
.tz-ct { font-size: 13px; font-weight: 600; white-space: nowrap; flex: none; }
|
||||
.tz.charging .tz-ct { color: var(--ok); }
|
||||
.tz-ct .num { color: var(--d-text-2); font-weight: 500; margin-left: 4px; }
|
||||
.tz-cr { min-width: 0; font-size: 13px; color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.tz-cs { font-size: 11px; color: var(--d-text-2); }
|
||||
.tz-bar { position: relative; height: 6px; border-radius: 99px; background: color-mix(in srgb, var(--d-text) 12%, transparent); }
|
||||
.tz-bar i { position: absolute; inset: 0 auto 0 0; border-radius: inherit; background: var(--tz-c); }
|
||||
.tz-bar b { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; border-radius: 1px; background: var(--d-text-2); }
|
||||
.tz-ctrls { grid-area: ctrls; display: flex; justify-content: space-around; gap: 4px; }
|
||||
.tz-btn { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; font-size: 22px; color: var(--d-text-2); transition: color .2s, background-color .2s; }
|
||||
.tz-btn:hover { background: var(--d-fill); }
|
||||
.tz-btn.on { color: var(--d-accent); }
|
||||
.tz-btn.ok { color: var(--ok); }
|
||||
.tz-btn.warn { color: var(--warn); }
|
||||
.tz-rows { grid-area: rows; display: none; flex-direction: column; border-top: 1px solid var(--d-separator); }
|
||||
.tz-row { display: flex; align-items: center; gap: 12px; min-height: 46px; padding: 0 2px; border-bottom: 1px solid var(--d-separator); text-align: left; width: 100%; }
|
||||
.tz-row:last-child { border-bottom: 0; }
|
||||
.tz-row > .i:first-child { font-size: 19px; color: var(--d-text-2); }
|
||||
.tz-rt { flex: 1; font-size: 15px; font-weight: 500; }
|
||||
.tz-rs { font-size: 13px; color: var(--d-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 55%; }
|
||||
.tz-row > .i:last-child { font-size: 14px; color: var(--d-text-2); }
|
||||
@media (prefers-reduced-motion: reduce) { .tz.charging .tz-port, .tz.charging .tz-bar i { animation: none; } }
|
||||
/* a standard square card keeps its room for the car */
|
||||
@container (max-height: 380px) { .tz.tz-sq .tz-cs { display: none; } }
|
||||
@container (max-height: 380px) and (max-aspect-ratio: 8 / 5) { .tz.tz-auto .tz-cs { display: none; } }
|
||||
/* tall square cards list the details, like the Tesla app's menu */
|
||||
@container (min-height: 470px) { .tz:not(.tz-wide) .tz-rows { display: flex; } }
|
||||
/* short: no drawing, just the numbers and the bar */
|
||||
@container (max-height: 280px) {
|
||||
.tz.tz-sq .tz-front { display: none; }
|
||||
.tz.tz-sq .tz-side { display: block; }
|
||||
.tz-btn { width: 40px; height: 40px; font-size: 19px; }
|
||||
}
|
||||
@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-height: 280px) and (max-aspect-ratio: 8 / 5) {
|
||||
.tz.tz-auto .tz-front { display: none; }
|
||||
.tz.tz-auto .tz-side { display: block; }
|
||||
}
|
||||
@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: 210px) {
|
||||
.tz-head { flex-direction: column; gap: 4px; }
|
||||
.tz-batt { display: flex; flex-wrap: wrap; padding-top: 0; }
|
||||
.tz-brange { flex-basis: 100%; }
|
||||
.tz-cs, .tz-ct .num { display: none; }
|
||||
.tz-btn { width: 36px; height: 36px; font-size: 18px; }
|
||||
}
|
||||
@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; } }
|
||||
/* wide: the car from the side on the right, the numbers on the left */
|
||||
.tz.tz-wide { grid-template-areas: "head art" "big art" "charge ctrls"; grid-template-columns: minmax(170px, .8fr) minmax(0, 1.2fr); grid-template-rows: auto minmax(0, 1fr) auto; column-gap: 24px; }
|
||||
.tz.tz-wide .tz-batt { display: none; }
|
||||
.tz.tz-wide .tz-big { display: flex; }
|
||||
.tz.tz-wide .tz-front { display: none; }
|
||||
.tz.tz-wide .tz-side { display: block; }
|
||||
.tz.tz-wide .tz-charge { align-self: end; }
|
||||
.tz.tz-wide .tz-ctrls { justify-content: center; align-self: end; }
|
||||
.tz.tz-wide .tz-rows { display: none; }
|
||||
.tz.tz-wide .tz-art { align-self: stretch; }
|
||||
.tz.tz-wide .tz-big { align-self: start; }
|
||||
@container (min-aspect-ratio: 8 / 5) {
|
||||
.tz.tz-auto { grid-template-areas: "head art" "big art" "charge ctrls"; grid-template-columns: minmax(170px, .8fr) minmax(0, 1.2fr); grid-template-rows: auto minmax(0, 1fr) auto; column-gap: 24px; }
|
||||
.tz.tz-auto .tz-batt { display: none; }
|
||||
.tz.tz-auto .tz-big { display: flex; }
|
||||
.tz.tz-auto .tz-front { display: none; }
|
||||
.tz.tz-auto .tz-side { display: block; }
|
||||
.tz.tz-auto .tz-charge { align-self: end; }
|
||||
.tz.tz-auto .tz-ctrls { justify-content: center; align-self: end; }
|
||||
.tz.tz-auto .tz-rows { display: none; }
|
||||
.tz.tz-auto .tz-art { align-self: stretch; }
|
||||
.tz.tz-auto .tz-big { align-self: start; }
|
||||
}
|
||||
|
||||
/* very short, whatever the width: name, battery and the bar */
|
||||
@container (max-height: 170px) {
|
||||
.tz.tz { grid-template-areas: "head" "charge" "ctrls"; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr auto; }
|
||||
.tz.tz .tz-art, .tz.tz .tz-big, .tz.tz .tz-cs, .tz.tz .tz-rows { display: none; }
|
||||
.tz.tz .tz-batt { display: grid; }
|
||||
.tz.tz .tz-charge, .tz.tz .tz-charge.tz-idle { display: flex; align-self: center; }
|
||||
}
|
||||
@container (max-height: 120px) { .tz.tz .tz-ctrls { display: none; } }
|
||||
|
||||
/* ============================================================ phone dashboards */
|
||||
body.phone-ui:not(.peek) #topbar { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user