desktop-new: delete the proving-ground dashboard

It was meant to stress the design system by putting many decisions next
to each other. It did surface one real finding - the neutral hover step
is tuned for hovering a filled component, not a bare row, so the same
token is too weak for a quiet button and too strong for a bare row - but
the page was ~85% bespoke CSS routed around the shared components, so
most of what it showed was its own scaffolding rather than the system.

The app itself is the better proving ground. Removing this rather than
carrying a mockup whose defects have to be triaged separately from the
product's.

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
This commit is contained in:
morgmart
2026-09-06 22:23:48 -07:00
parent 4100246b75
commit ce94bb1599
6 changed files with 0 additions and 1097 deletions

View File

@@ -1,6 +0,0 @@
import { createFileRoute } from "@tanstack/react-router";
import { ProvingGroundPage } from "@/features/proving-ground/ui/ProvingGroundPage";
export const Route = createFileRoute("/proving-ground")({
component: ProvingGroundPage,
});

View File

@@ -1,187 +0,0 @@
/**
* Fixture data for the proving ground.
*
* A fictional product — a fleet-monitoring dashboard — deliberately unrelated to
* Buzz. The point is to exercise the design system at the density and variety a
* real page has, without any of it becoming a claim about what Buzz looks like.
* Nothing here is a product decision; it is load for the tokens to carry.
*/
export type Health = "healthy" | "degraded" | "failing" | "paused";
export type Region = {
id: string;
name: string;
code: string;
health: Health;
/** Requests per second, for the sparkline and the headline figure. */
throughput: number;
/** Percentage, one decimal. */
successRate: number;
latencyMs: number;
/** Twelve readings, oldest first. */
history: number[];
};
export type Incident = {
id: string;
title: string;
region: string;
severity: "critical" | "warning" | "info";
openedAt: string;
assignee?: { name: string; initial: string };
acknowledged: boolean;
};
export type Deploy = {
id: string;
service: string;
version: string;
status: "live" | "rolling" | "held" | "rolled-back";
author: string;
at: string;
};
export const REGIONS: Region[] = [
{
id: "use1",
name: "US East",
code: "use1",
health: "healthy",
throughput: 18420,
successRate: 99.98,
latencyMs: 42,
history: [12, 14, 13, 16, 18, 17, 19, 21, 20, 22, 24, 23],
},
{
id: "usw2",
name: "US West",
code: "usw2",
health: "healthy",
throughput: 11260,
successRate: 99.94,
latencyMs: 51,
history: [9, 10, 11, 10, 12, 13, 12, 14, 13, 15, 14, 16],
},
{
id: "euw1",
name: "EU West",
code: "euw1",
health: "degraded",
throughput: 8940,
successRate: 97.21,
latencyMs: 189,
history: [14, 13, 15, 14, 12, 11, 9, 8, 10, 9, 7, 8],
},
{
id: "apse1",
name: "AP Southeast",
code: "apse1",
health: "failing",
throughput: 1180,
successRate: 71.44,
latencyMs: 842,
history: [11, 12, 10, 11, 9, 7, 5, 3, 2, 2, 1, 1],
},
{
id: "sae1",
name: "SA East",
code: "sae1",
health: "paused",
throughput: 0,
successRate: 0,
latencyMs: 0,
history: [6, 6, 5, 6, 4, 3, 0, 0, 0, 0, 0, 0],
},
];
export const INCIDENTS: Incident[] = [
{
id: "INC-4821",
title: "Elevated 5xx from the ingest gateway",
region: "apse1",
severity: "critical",
openedAt: "14 min ago",
assignee: { name: "Priya Raman", initial: "P" },
acknowledged: true,
},
{
id: "INC-4820",
title: "Replica lag above threshold on the primary cluster",
region: "euw1",
severity: "warning",
openedAt: "38 min ago",
assignee: { name: "Tomas Lindqvist", initial: "T" },
acknowledged: true,
},
{
id: "INC-4819",
title: "Certificate expires in 6 days",
region: "usw2",
severity: "info",
openedAt: "2 hr ago",
acknowledged: false,
},
{
id: "INC-4817",
title: "Queue depth climbing without a matching request increase",
region: "euw1",
severity: "warning",
openedAt: "5 hr ago",
assignee: { name: "Dani Okonkwo", initial: "D" },
acknowledged: false,
},
];
export const DEPLOYS: Deploy[] = [
{
id: "d-9931",
service: "ingest-gateway",
version: "2026.9.4",
status: "rolling",
author: "Priya Raman",
at: "6 min ago",
},
{
id: "d-9930",
service: "index-writer",
version: "2026.9.3",
status: "live",
author: "Marco Bellini",
at: "1 hr ago",
},
{
id: "d-9929",
service: "query-planner",
version: "2026.9.3",
status: "held",
author: "Dani Okonkwo",
at: "3 hr ago",
},
{
id: "d-9927",
service: "ingest-gateway",
version: "2026.9.2",
status: "rolled-back",
author: "Tomas Lindqvist",
at: "yesterday",
},
];
/** Maps a health state to the status family that carries its meaning. */
export const HEALTH_TONE: Record<
Health,
"success" | "warning" | "danger" | "neutral"
> = {
healthy: "success",
degraded: "warning",
failing: "danger",
paused: "neutral",
};
export const HEALTH_LABEL: Record<Health, string> = {
healthy: "Healthy",
degraded: "Degraded",
failing: "Failing",
paused: "Paused",
};

View File

@@ -1,401 +0,0 @@
import {
IconActivity,
IconAlertTriangle,
IconChartDots,
IconChevronRight,
IconCloud,
IconMoon,
IconRocket,
IconSun,
} from "@tabler/icons-react";
import { useState } from "react";
import { useColorScheme } from "@/shared/theme/useColorScheme";
import { Avatar } from "@/shared/ui/Avatar";
import { Button } from "@/shared/ui/Button";
import { IconButton } from "@/shared/ui/IconButton";
import { SearchField } from "@/shared/ui/SearchField";
import { SegmentedNavigation } from "@/shared/ui/SegmentedNavigation";
import { DEPLOYS, HEALTH_TONE, INCIDENTS, REGIONS } from "../data";
import {
HealthPill,
KeyValue,
Metric,
Panel,
Row,
Sparkline,
StatusPill,
} from "./parts";
/**
* The proving ground: a fictional fleet-monitoring dashboard.
*
* Deliberately not Buzz. Its job is to load the design system the way a real
* product does — many decisions adjacent to each other, at density, in both
* modes — and to be the surface a generated theme is judged on. A specimen page
* shows one component at a time on a backdrop the designer chose; this shows
* forty at once, which is where a missing role or a wrong neutral step appears.
*
* Built only from role tokens. If something here needs a value the system does
* not have, that is the finding.
*/
const RANGES = [
{ value: "1h", label: "1 hour" },
{ value: "24h", label: "24 hours" },
{ value: "7d", label: "7 days" },
] as const;
const SEVERITY_TONE = {
critical: "danger",
warning: "warning",
info: "info",
} as const;
const DEPLOY_TONE = {
live: "success",
rolling: "accent",
held: "warning",
"rolled-back": "danger",
} as const;
const DEPLOY_LABEL = {
live: "Live",
rolling: "Rolling out",
held: "Held",
"rolled-back": "Rolled back",
} as const;
export function ProvingGroundPage() {
const { scheme, toggle } = useColorScheme();
const [range, setRange] = useState<string>("24h");
const [section, setSection] = useState("overview");
const [query, setQuery] = useState("");
const openIncidents = INCIDENTS.length;
const failing = REGIONS.filter((r) => r.health === "failing").length;
return (
<div className="dash-shell">
<nav className="dash-nav" aria-label="Sections">
<div className="dash-nav-group">
<span className="dash-nav-label text-body-sm">Monitor</span>
<NavItem
icon={<IconActivity size={16} />}
label="Overview"
id="overview"
current={section}
onSelect={setSection}
/>
<NavItem
icon={<IconAlertTriangle size={16} />}
label="Incidents"
id="incidents"
count={openIncidents}
current={section}
onSelect={setSection}
/>
<NavItem
icon={<IconCloud size={16} />}
label="Regions"
id="regions"
count={REGIONS.length}
current={section}
onSelect={setSection}
/>
</div>
<div className="dash-nav-group">
<span className="dash-nav-label text-body-sm">Ship</span>
<NavItem
icon={<IconRocket size={16} />}
label="Deploys"
id="deploys"
current={section}
onSelect={setSection}
/>
<NavItem
icon={<IconChartDots size={16} />}
label="Reports"
id="reports"
current={section}
onSelect={setSection}
/>
</div>
</nav>
<main className="dash-main">
<header className="dash-topbar">
<div className="flex min-w-0 flex-col gap-1">
<h1 className="text-title text-primary">Fleet</h1>
<p className="text-body text-secondary">
Five regions, {openIncidents} open incidents
{failing > 0 ? `, ${failing} failing` : ""}.
</p>
</div>
<div className="flex flex-wrap items-center gap-2">
<SearchField
value={query}
onValueChange={setQuery}
placeholder="Search regions and services"
/>
<SegmentedNavigation
label="Time range"
items={RANGES}
value={range}
onValueChange={setRange}
/>
<IconButton
aria-label={
scheme === "dark" ? "Use light mode" : "Use dark mode"
}
onClick={toggle}
icon={
scheme === "dark" ? (
<IconSun size={16} />
) : (
<IconMoon size={16} />
)
}
/>
<Button variant="primary">New check</Button>
</div>
</header>
<div className="dash-metric-grid">
<Metric
label="Requests"
value="39.8"
unit="k/s"
change="+4.2% vs yesterday"
direction="up"
/>
<Metric
label="Success rate"
value="97.4"
unit="%"
change="−1.9 points"
direction="down"
/>
<Metric
label="p95 latency"
value="188"
unit="ms"
change="+61 ms"
direction="down"
/>
<Metric
label="Open incidents"
value="4"
change="2 acknowledged"
direction="flat"
/>
</div>
<div className="dash-columns">
<div className="flex min-w-0 flex-col gap-6">
<Panel
title="Regions"
description="Throughput over the selected window."
>
<div className="dash-region-grid">
{REGIONS.map((region) => (
<article key={region.id} className="dash-region">
<div className="flex items-start justify-between gap-2">
<div className="flex min-w-0 flex-col">
<span className="text-body text-primary">
{region.name}
</span>
<span className="text-mono-sm text-tertiary">
{region.code}
</span>
</div>
<HealthPill health={region.health} />
</div>
<Sparkline
values={region.history}
tone={HEALTH_TONE[region.health]}
/>
<div className="flex flex-col gap-1">
<KeyValue
label="Requests"
value={
region.throughput === 0
? "—"
: `${(region.throughput / 1000).toFixed(1)}k/s`
}
/>
<KeyValue
label="Success"
value={
region.successRate === 0
? "—"
: `${region.successRate.toFixed(2)}%`
}
/>
<KeyValue
label="p95"
value={
region.latencyMs === 0
? "—"
: `${region.latencyMs} ms`
}
/>
</div>
</article>
))}
</div>
</Panel>
<Panel
title="Open incidents"
description="Newest first."
padded={false}
action={
<Button variant="quiet" size="compact">
View all
</Button>
}
>
{INCIDENTS.map((incident) => (
<Row key={incident.id} interactive>
<StatusPill tone={SEVERITY_TONE[incident.severity]}>
{incident.severity}
</StatusPill>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-body text-primary">
{incident.title}
</span>
<span className="text-body-sm text-tertiary">
<span className="text-mono-sm">{incident.id}</span> ·{" "}
{incident.region} · {incident.openedAt}
</span>
</div>
{incident.assignee ? (
<Avatar
size="small"
alt={`Assigned to ${incident.assignee.name}`}
fallback={incident.assignee.initial}
/>
) : (
<span className="text-body-sm text-disabled">
Unassigned
</span>
)}
<IconChevronRight
size={16}
className="text-tertiary"
aria-hidden="true"
/>
</Row>
))}
</Panel>
</div>
<div className="flex min-w-0 flex-col gap-6">
<Panel title="Recent deploys" padded={false}>
{DEPLOYS.map((deploy) => (
<Row key={deploy.id}>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-body text-primary">
{deploy.service}
</span>
<span className="text-body-sm text-tertiary">
<span className="text-mono-sm">{deploy.version}</span> ·{" "}
{deploy.author} · {deploy.at}
</span>
</div>
<StatusPill tone={DEPLOY_TONE[deploy.status]}>
{DEPLOY_LABEL[deploy.status]}
</StatusPill>
</Row>
))}
</Panel>
<Panel
title="Rollout"
description="ingest-gateway 2026.9.4 is going out region by region."
>
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="flex items-baseline justify-between">
<span className="text-body-sm text-secondary">
Progress
</span>
<span className="text-body-sm text-primary tabular-nums">
2 of 5 regions
</span>
</div>
{/* A determinate bar: inset track, accent fill. */}
<div
className="h-2 overflow-hidden rounded-pill bg-inset"
role="progressbar"
aria-valuenow={40}
aria-valuemin={0}
aria-valuemax={100}
aria-label="Rollout progress"
>
<div className="h-full w-2/5 rounded-pill bg-accent" />
</div>
</div>
<div className="flex flex-col gap-1">
<KeyValue label="Started" value="6 min ago" />
<KeyValue label="Error budget" value="98.2% remaining" />
<KeyValue label="Next region" value="euw1" />
</div>
<div className="flex flex-wrap gap-2">
<Button variant="primary" size="compact">
Continue rollout
</Button>
<Button variant="quiet" size="compact">
Hold
</Button>
<Button variant="ghost" size="compact" disabled>
Roll back
</Button>
</div>
</div>
</Panel>
</div>
</div>
</main>
</div>
);
}
function NavItem({
icon,
label,
id,
count,
current,
onSelect,
}: {
icon: React.ReactNode;
label: string;
id: string;
count?: number;
current: string;
onSelect: (id: string) => void;
}) {
const isCurrent = current === id;
return (
<button
type="button"
className="dash-nav-item text-body"
aria-current={isCurrent ? "page" : undefined}
onClick={() => onSelect(id)}
>
<span aria-hidden="true" className="flex shrink-0">
{icon}
</span>
{label}
{count !== undefined ? (
<span className="dash-nav-count text-body-sm">{count}</span>
) : null}
</button>
);
}

View File

@@ -1,187 +0,0 @@
import type { ReactNode } from "react";
import { type Health, HEALTH_LABEL, HEALTH_TONE } from "../data";
/**
* Pieces the proving-ground dashboard is assembled from.
*
* These are page-local on purpose. The shared layer earns a component when the
* product repeats one; this page exists to find out *which* things repeat, so
* putting its parts in `shared/ui` first would be guessing. Anything that turns
* out to be load-bearing graduates afterwards.
*/
type Tone = "success" | "warning" | "danger" | "neutral" | "accent" | "info";
/**
* A status pill. Tint fill, coloured text — the tint arrangement from
* DESIGN.md, where a colour means something rather than carrying an action.
*/
export function StatusPill({
tone,
children,
}: {
tone: Tone;
children: ReactNode;
}) {
return (
<span className="status-pill" data-tone={tone}>
{children}
</span>
);
}
export function HealthPill({ health }: { health: Health }) {
return (
<StatusPill tone={HEALTH_TONE[health]}>{HEALTH_LABEL[health]}</StatusPill>
);
}
/** A page region. Panel surface, generous inset, no card-per-row inside. */
export function Panel({
title,
description,
action,
children,
padded = true,
}: {
title: string;
description?: string;
action?: ReactNode;
children: ReactNode;
/** False when the content is edge-to-edge rows that supply their own inset. */
padded?: boolean;
}) {
return (
<section className="dash-panel">
<header className="dash-panel-header">
<div className="flex min-w-0 flex-col gap-1">
<h2 className="text-heading text-primary">{title}</h2>
{description ? (
<p className="text-body-sm text-secondary">{description}</p>
) : null}
</div>
{action}
</header>
<div data-padded={padded || undefined} className="dash-panel-body">
{children}
</div>
</section>
);
}
/**
* A headline figure with its label and change.
*
* Hierarchy comes from size, not colour: the number is `text-display`, the label
* is small and secondary. Only the change direction earns a colour, because a
* direction is a signal.
*/
export function Metric({
label,
value,
unit,
change,
direction,
}: {
label: string;
value: string;
unit?: string;
change?: string;
direction?: "up" | "down" | "flat";
}) {
return (
<div className="dash-metric">
<span className="text-body-sm text-secondary">{label}</span>
<div className="flex items-baseline gap-1.5">
<span className="text-display text-primary tabular-nums">{value}</span>
{unit ? (
<span className="text-body-sm text-tertiary">{unit}</span>
) : null}
</div>
{change ? (
<span
className="text-body-sm tabular-nums"
data-direction={direction}
// A rise is not automatically good, so direction is coloured by what
// it means on this metric rather than by which way it points.
style={{
color:
direction === "up"
? "var(--text-success)"
: direction === "down"
? "var(--text-danger)"
: "var(--text-tertiary)",
}}
>
{change}
</span>
) : null}
</div>
);
}
/**
* A bar sparkline, drawn with divs rather than SVG.
*
* Deliberate: it inherits `currentColor` and the rem-based type scale, so it
* tracks the theme and the font-size preference without a redraw. Decorative —
* the figure beside it carries the information, so this is hidden from
* assistive technology rather than described.
*/
export function Sparkline({
values,
tone = "accent",
}: {
values: number[];
tone?: Tone;
}) {
const peak = Math.max(...values, 1);
// A reading's position in the window is part of its identity — the oldest
// sample is a different thing from the newest — so the key carries both.
const bars = values.map((value, index) => ({
id: `${index}:${value}`,
height: `${Math.max((value / peak) * 100, 4)}%`,
}));
return (
<div className="dash-sparkline" data-tone={tone} aria-hidden="true">
{bars.map((bar) => (
<span
key={bar.id}
className="dash-sparkline-bar"
style={{ height: bar.height }}
/>
))}
</div>
);
}
/** A dense table row. Dividers, edge to edge — never a card per row. */
export function Row({
children,
interactive = false,
}: {
children: ReactNode;
interactive?: boolean;
}) {
return (
<div className="dash-row" data-interactive={interactive || undefined}>
{children}
</div>
);
}
export function KeyValue({
label,
value,
}: {
label: string;
value: ReactNode;
}) {
return (
<div className="flex items-baseline justify-between gap-4">
<span className="text-body-sm text-secondary">{label}</span>
<span className="text-body-sm text-primary tabular-nums">{value}</span>
</div>
);
}

View File

@@ -3,7 +3,6 @@
@import "./typography.css";
@import "./components.css";
@import "./chips.css";
@import "./proving-ground.css";
@import "./design-components.css";
@import "./product.css";
@import "./agents.css";

View File

@@ -1,315 +0,0 @@
@layer components {
/**
* Proving-ground dashboard.
*
* A fictional product built only from role tokens, to find out whether the
* system holds up on a real page rather than on a specimen page. Specimen
* pages are honest but sympathetic — they show one component at a time, on a
* generous backdrop, at a size the designer chose. A dashboard puts forty
* decisions next to each other and is where a missing role shows up.
*/
.dash-shell {
display: grid;
min-height: 100vh;
grid-template-columns: var(--size-navigator) minmax(0, 1fr);
background: var(--bg-app);
}
/* Narrow: the navigator becomes a top strip rather than a drawer. There is no
drawer machinery here yet, and a horizontal strip is honest about that. */
@media (max-width: 60rem) {
.dash-shell {
grid-template-columns: minmax(0, 1fr);
}
}
.dash-nav {
display: flex;
flex-direction: column;
gap: var(--space-6);
padding: var(--space-panel-inset);
border-right: 1px solid var(--border-tertiary);
}
@media (max-width: 60rem) {
.dash-nav {
flex-direction: row;
align-items: center;
gap: var(--space-4);
overflow-x: auto;
border-right: 0;
border-bottom: 1px solid var(--border-tertiary);
padding-block: var(--space-3);
}
}
.dash-nav-group {
display: flex;
min-width: 0;
flex-direction: column;
gap: var(--space-1);
}
@media (max-width: 60rem) {
.dash-nav-group {
flex-direction: row;
align-items: center;
gap: var(--space-1);
}
/* The section labels are useful in a column and noise in a strip. */
.dash-nav-label {
display: none;
}
}
.dash-nav-label {
padding-inline: var(--space-2);
padding-block: var(--space-1);
color: var(--text-tertiary);
}
.dash-nav-item {
display: flex;
align-items: center;
gap: var(--space-row-gap);
border: 0;
border-radius: var(--radius-row);
padding: var(--space-2) var(--space-2);
background: transparent;
color: var(--text-secondary);
font: inherit;
text-align: start;
white-space: nowrap;
cursor: pointer;
transition:
background-color var(--duration-state) var(--easing-state),
color var(--duration-state) var(--easing-state);
}
.dash-nav-item:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
/* Selected is a tint plus coloured text — the tint arrangement. Weight is
never used to say "selected"; that is reserved for structure. */
.dash-nav-item[aria-current="page"] {
background: var(--bg-accent-tint);
color: var(--text-accent);
}
.dash-nav-item:focus-visible {
outline: 2px solid var(--ring-focus);
outline-offset: 2px;
}
.dash-nav-count {
margin-inline-start: auto;
color: var(--text-tertiary);
font-variant-numeric: tabular-nums;
}
@media (max-width: 60rem) {
.dash-nav-count {
margin-inline-start: var(--space-1);
}
}
.dash-main {
display: flex;
min-width: 0;
flex-direction: column;
gap: var(--space-6);
padding: var(--space-panel-inset);
}
.dash-topbar {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
}
.dash-metric-grid {
display: grid;
gap: var(--space-4);
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.dash-metric {
display: flex;
flex-direction: column;
gap: var(--space-1);
min-width: 0;
}
.dash-columns {
display: grid;
gap: var(--space-6);
grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
align-items: start;
}
@media (max-width: 72rem) {
.dash-columns {
grid-template-columns: minmax(0, 1fr);
}
}
.dash-panel {
display: flex;
min-width: 0;
flex-direction: column;
overflow: hidden;
border: 1px solid var(--border-tertiary);
border-radius: var(--radius-panel);
background: var(--bg-panel);
box-shadow: var(--shadow-xs);
}
.dash-panel-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
padding: var(--space-5) var(--space-panel-inset);
}
.dash-panel-body[data-padded] {
padding: 0 var(--space-panel-inset) var(--space-panel-inset);
}
/* Dense rows, edge to edge, separated by a line rather than by cards. */
.dash-row {
display: flex;
align-items: center;
gap: var(--space-4);
padding: var(--space-3) var(--space-panel-inset);
border-top: 1px solid var(--border-tertiary);
}
.dash-row[data-interactive] {
cursor: pointer;
transition: background-color var(--duration-state) var(--easing-state);
}
.dash-row[data-interactive]:hover {
background: var(--bg-hover);
}
.status-pill {
display: inline-flex;
align-items: center;
gap: var(--space-1);
border-radius: var(--radius-pill);
padding: 0.125rem var(--space-2);
white-space: nowrap;
}
.status-pill[data-tone="success"] {
background: var(--bg-success-tint);
color: var(--text-success);
}
.status-pill[data-tone="warning"] {
background: var(--bg-warning-tint);
color: var(--text-warning);
}
.status-pill[data-tone="danger"] {
background: var(--bg-danger-tint);
color: var(--text-danger);
}
.status-pill[data-tone="info"] {
background: var(--bg-info-tint);
color: var(--text-info);
}
.status-pill[data-tone="accent"] {
background: var(--bg-accent-tint);
color: var(--text-accent);
}
/* Paused is not a status colour: nothing is wrong, so a colour would teach
people to distrust the coloured ones. */
.status-pill[data-tone="neutral"] {
background: var(--bg-inset);
color: var(--text-secondary);
}
.dash-sparkline {
display: flex;
align-items: flex-end;
gap: 2px;
height: 2rem;
min-width: 4rem;
flex: 1 1 auto;
}
.dash-sparkline-bar {
flex: 1 1 0;
min-width: 2px;
border-radius: 1px;
background: currentColor;
}
.dash-sparkline[data-tone="success"] {
color: var(--bg-success);
}
.dash-sparkline[data-tone="warning"] {
color: var(--bg-warning);
}
.dash-sparkline[data-tone="danger"] {
color: var(--bg-danger);
}
.dash-sparkline[data-tone="accent"] {
color: var(--bg-accent);
}
.dash-sparkline[data-tone="neutral"] {
color: var(--border-secondary);
}
.dash-region-grid {
display: grid;
gap: var(--space-4);
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
/* A card carries no default fill. It sits on the panel and is grouped by a
hairline, so fill is free to mean one thing only: you are pointing at this.
Add `data-interactive` when a card is actually clickable. */
.dash-region {
display: flex;
min-width: 0;
flex-direction: column;
gap: var(--space-3);
border: 1px solid var(--border-tertiary);
border-radius: var(--radius-control);
padding: var(--space-4);
}
.dash-region[data-interactive] {
cursor: pointer;
transition: background-color var(--duration-state) var(--easing-state);
}
.dash-region[data-interactive]:hover {
background: var(--bg-hover);
}
@media (prefers-reduced-motion: reduce) {
.dash-nav-item,
.dash-row[data-interactive],
.dash-region[data-interactive] {
transition-duration: 0ms;
}
}
}