Rewrite components/ui/* (Button, Card, Badge, PageHeader, EmptyState, Skeleton) against the chunk-1 tokens instead of hardcoded gray-9xx classes, and drop the remaining font-mono from label/heading text. Add the three colour-blindness-validated encoding components from UI_REDESIGN.md §2.3: - components/marks/SeverityMark.tsx — glyph (filled triangle / outline triangle / outline circle) + optional spine + optional label, from the four-level severity ladder. Colour is never the only channel. - components/marks/TypeGlyph.tsx — five stroked SVG glyphs (fire, police, ems, collision, other) in currentColor. Incident type is now shape, not hue, since five hues can't clear an all-pairs CVD gate. - components/marks/NodeMark.tsx — diamond at four weights (filled+ring / filled / hollow / hollow-dashed). Green is gone from node state entirely. lib/severity.tsx now renders through SeverityMark; SEVERITY_COLORS reads the validated sev-moderate/sev-major tokens with routine/minor neutral. IncidentBadges.tsx's TypeBadge is reimplemented on TypeGlyph instead of a coloured pill. Per UI_REDESIGN.md chunk 2.
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
import type { HTMLAttributes, ReactNode } from "react";
|
|
|
|
interface CardProps extends HTMLAttributes<HTMLDivElement> {
|
|
children: ReactNode;
|
|
hover?: boolean;
|
|
padding?: "none" | "sm" | "md" | "lg";
|
|
highlighted?: boolean;
|
|
}
|
|
|
|
const PADDING: Record<NonNullable<CardProps["padding"]>, string> = {
|
|
none: "",
|
|
sm: "p-4",
|
|
md: "p-5",
|
|
lg: "p-8",
|
|
};
|
|
|
|
/** Standard surface card — the base container used across app + settings + marketing. */
|
|
export function Card({ children, hover, padding = "md", highlighted, className, ...rest }: CardProps) {
|
|
return (
|
|
<div
|
|
className={[
|
|
"bg-surface border rounded-xl",
|
|
highlighted ? "border-accent/40 shadow-glow" : "border-line",
|
|
hover ? "transition-colors hover:border-line-strong" : "",
|
|
PADDING[padding],
|
|
className ?? "",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ")}
|
|
{...rest}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function CardHeader({ title, subtitle, action }: { title: ReactNode; subtitle?: ReactNode; action?: ReactNode }) {
|
|
return (
|
|
<div className="flex items-start justify-between gap-4 mb-4">
|
|
<div className="min-w-0">
|
|
<h3 className="text-ink font-semibold text-sm">{title}</h3>
|
|
{subtitle && <p className="text-ink-muted text-xs mt-0.5 leading-snug">{subtitle}</p>}
|
|
</div>
|
|
{action && <div className="shrink-0">{action}</div>}
|
|
</div>
|
|
);
|
|
}
|