Files
cadence-ui/apps/docs/src/style-matrix.stories.tsx
T

265 lines
9.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
motionPackDetails,
motionPackNames,
type MotionPackName
} from "@ai-ui/tokens";
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
Input,
Skeleton,
Switch,
skinDetails,
skinNames,
type SkinName
} from "@ai-ui/ui";
import type { Meta, StoryObj } from "@storybook/react";
const motionAccessibilityModes = [
{
label: "System accessibility",
value: "system"
},
{
label: "Reduced accessibility",
value: "reduced"
}
] as const;
type MotionAccessibilityMode = (typeof motionAccessibilityModes)[number]["value"];
function RuntimePill({ children }: { children: React.ReactNode }) {
return (
<span className="rounded-full border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-1 text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
{children}
</span>
);
}
function PanelPreview() {
return (
<div
className="grid gap-3 border p-4"
style={{
background: "var(--ui-panel-bg)",
borderColor: "var(--ui-panel-border)",
borderRadius: "var(--ui-panel-radius)",
borderWidth: "var(--ui-panel-border-width)",
boxShadow: "var(--ui-panel-shadow)",
backdropFilter: "blur(var(--ui-panel-backdrop-blur))"
}}
>
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-sm font-semibold text-[var(--color-foreground)]">
Dialog panel contract
</p>
<p className="mt-1 text-sm leading-6 text-[var(--color-muted-foreground)]">
Panel vars preview the dialog surface without opening an overlay in every
matrix cell.
</p>
</div>
<Button aria-hidden="true" size="icon" tabIndex={-1} variant="ghost">
×
</Button>
</div>
<div className="grid gap-2">
<Skeleton shape="line" />
<Skeleton shape="block" />
</div>
</div>
);
}
function ComparisonCell({
motionAccessibility,
motionPack,
skin
}: {
motionAccessibility: MotionAccessibilityMode;
motionPack: MotionPackName;
skin: SkinName;
}) {
return (
<section
className="grid gap-4 rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-4 shadow-[var(--shadow-sm)]"
data-motion={motionAccessibility === "reduced" ? "reduced" : undefined}
data-motion-pack={motionPack}
data-skin={skin}
>
<div className="flex flex-wrap gap-2">
<RuntimePill>{motionPackDetails[motionPack].label}</RuntimePill>
<RuntimePill>{skinDetails[skin].label}</RuntimePill>
<RuntimePill>{motionAccessibility}</RuntimePill>
</div>
<Card interactive tone="default">
<CardHeader>
<CardTitle>Release routing</CardTitle>
<CardDescription>
The same component tree should now pick up distinct skin treatments.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-3">
<Input defaultValue="Launch notes approved" readOnly />
<div className="flex items-center justify-between gap-3">
<span className="text-sm text-[var(--color-muted-foreground)]">
Quiet notifications
</span>
<Switch checked />
</div>
<div className="flex flex-wrap gap-3">
<Button>Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="subtle">Subtle</Button>
</div>
</CardContent>
</Card>
<PanelPreview />
</section>
);
}
function MatrixDialogSandbox() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="secondary">Open live dialog preview</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog validation sandbox</DialogTitle>
<DialogDescription>
Use the Storybook toolbar to validate the real overlay under the active theme,
skin, and motion settings.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="ghost">Back</Button>
<Button>Approve</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
function StyleMatrixShowcase() {
return (
<div className="min-h-screen bg-[var(--color-background)] px-6 py-10 text-[var(--color-foreground)] sm:px-10">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-8">
<header className="max-w-4xl space-y-4">
<p className="text-sm uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
AI UI / Phase 3
</p>
<h1
className="font-semibold tracking-[var(--tracking-tight)]"
style={{
fontFamily: "var(--font-display)",
fontSize: "var(--text-4xl)",
lineHeight: "var(--leading-tight)"
}}
>
Style matrix compares the same product surface across skin and motion scopes.
</h1>
<p className="max-w-3xl text-[var(--text-lg)] leading-[var(--leading-loose)] text-[var(--color-muted-foreground)]">
This page is the screenshot-friendly regression target for the pilot skin work.
The grid uses nested `data-skin`, `data-motion-pack`, and
`data-motion=&quot;reduced&quot;` scopes so the same building blocks can be
reviewed side by side.
</p>
</header>
<section className="grid gap-4">
{motionPackNames.map((motionPack) => (
<div key={motionPack} className="grid gap-4">
<div>
<h2 className="text-2xl font-semibold">{motionPackDetails[motionPack].label}</h2>
<p className="mt-1 max-w-3xl text-sm leading-6 text-[var(--color-muted-foreground)]">
{motionPackDetails[motionPack].note}
</p>
</div>
{motionAccessibilityModes.map((motionAccessibilityMode) => (
<div key={`${motionPack}-${motionAccessibilityMode.value}`} className="grid gap-3">
<div className="flex items-center justify-between gap-4">
<div>
<h3 className="text-xl font-semibold">
{motionAccessibilityMode.label}
</h3>
<p className="mt-1 text-sm text-[var(--color-muted-foreground)]">
{motionAccessibilityMode.value === "reduced"
? '`data-motion="reduced"`'
: "System preference"}
{" "}with{" "}
<code className="text-[var(--color-foreground)]">
{`data-motion-pack="${motionPack}"`}
</code>
.
</p>
</div>
</div>
<div className="grid gap-4 xl:grid-cols-3">
{skinNames.map((skin) => (
<ComparisonCell
key={`${motionPack}-${motionAccessibilityMode.value}-${skin}`}
motionAccessibility={motionAccessibilityMode.value}
motionPack={motionPack}
skin={skin}
/>
))}
</div>
</div>
))}
</div>
))}
</section>
<section className="grid gap-4 rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)] lg:grid-cols-[minmax(0,1fr)_auto] lg:items-center">
<div>
<h2 className="text-2xl font-semibold">Live overlay validation</h2>
<p className="mt-2 max-w-3xl text-sm leading-6 text-[var(--color-muted-foreground)]">
Dialog still portals to the document root, so compare its real overlay and
panel treatment with the Storybook toolbar. The matrix above covers scoped
inline regression across packs and reduced-motion overlay. The control below
covers the live overlay behavior.
</p>
</div>
<div className="flex justify-start lg:justify-end">
<MatrixDialogSandbox />
</div>
</section>
</div>
</div>
);
}
const meta = {
title: "Foundation/Style Matrix",
component: StyleMatrixShowcase,
parameters: {
docs: {
description: {
component:
"Phase 3 adds the regression-oriented comparison surface. Use this page for screenshots and visual review, then use the live dialog sandbox below to validate portal-driven overlays under the active toolbar settings."
}
}
}
} satisfies Meta<typeof StyleMatrixShowcase>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Overview: Story = {};