254 lines
12 KiB
TypeScript
254 lines
12 KiB
TypeScript
import {
|
|
authoringChecklist,
|
|
commonSlotNames,
|
|
commonStateNames,
|
|
cvaConventions
|
|
} from "@ai-ui/ui";
|
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
|
|
const docsCoverage = [
|
|
{
|
|
name: "Playground",
|
|
note: "One opinionated default example that shows the component in its most typical role."
|
|
},
|
|
{
|
|
name: "States",
|
|
note: "Only when the component has meaningful disabled, invalid, checked, selected, or open-state behavior worth comparing."
|
|
},
|
|
{
|
|
name: "Anatomy",
|
|
note: "Name the stable slots and public data attributes that designers and engineers can style against."
|
|
},
|
|
{
|
|
name: "Accessibility or Motion",
|
|
note: "Choose the dimension that is most likely to be misunderstood by contributors and consumers."
|
|
}
|
|
] as const;
|
|
|
|
const docsWritingRules = [
|
|
"Use `docs.description.component` to explain when the component should be chosen, not to restate its name.",
|
|
"Show real product language instead of lorem ipsum so states and hierarchy feel intentional.",
|
|
"Keep examples narrow. One good scenario teaches more than six generic permutations.",
|
|
"If a story exists only to explain slots or motion, say that directly in the story description."
|
|
] as const;
|
|
|
|
function ComponentAuthoringGuide() {
|
|
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-6xl flex-col gap-8">
|
|
<header className="max-w-4xl space-y-3">
|
|
<p className="text-sm uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
|
AI UI / Docs Guide
|
|
</p>
|
|
<h1
|
|
className="font-semibold tracking-[var(--tracking-tight)]"
|
|
style={{
|
|
fontFamily: "var(--font-display)",
|
|
fontSize: "var(--text-4xl)",
|
|
lineHeight: "var(--leading-tight)"
|
|
}}
|
|
>
|
|
Component docs should explain usage, structure, and behavior without drifting
|
|
away from the actual source contract.
|
|
</h1>
|
|
<p className="text-[var(--text-lg)] leading-[var(--leading-loose)] text-[var(--color-muted-foreground)]">
|
|
This page turns the repo's component contract into a repeatable Storybook
|
|
recipe. The goal is not maximum story count. The goal is a small set of stories
|
|
that makes API, anatomy, and behavioral intent obvious to the next contributor.
|
|
</p>
|
|
</header>
|
|
|
|
<section className="grid gap-4 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]">
|
|
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">Minimum Story Recipe</h2>
|
|
<div className="mt-5 grid gap-3">
|
|
{docsCoverage.map((item) => (
|
|
<div
|
|
key={item.name}
|
|
className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3"
|
|
>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<p className="text-sm font-medium">{item.name}</p>
|
|
<span className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
|
docs lane
|
|
</span>
|
|
</div>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
{item.note}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</article>
|
|
|
|
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">Writing Rules</h2>
|
|
<div className="mt-5 grid gap-3">
|
|
{docsWritingRules.map((rule) => (
|
|
<div
|
|
key={rule}
|
|
className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3"
|
|
>
|
|
<p className="text-sm leading-6 text-[var(--color-foreground)]">{rule}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section className="grid gap-4 lg:grid-cols-2">
|
|
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">Component Contract Inputs</h2>
|
|
<p className="mt-1 text-sm text-[var(--color-muted-foreground)]">
|
|
The docs should mirror the same public hooks that the components expose in
|
|
source.
|
|
</p>
|
|
<div className="mt-5 grid gap-3">
|
|
{authoringChecklist.map((item) => (
|
|
<div
|
|
key={item}
|
|
className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3"
|
|
>
|
|
<p className="text-sm leading-6 text-[var(--color-foreground)]">{item}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</article>
|
|
|
|
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">CVA Guardrails</h2>
|
|
<p className="mt-1 text-sm text-[var(--color-muted-foreground)]">
|
|
Docs should reinforce the variant surface that engineering already considers
|
|
stable.
|
|
</p>
|
|
<div className="mt-5 grid gap-3">
|
|
{cvaConventions.map((item) => (
|
|
<div
|
|
key={item}
|
|
className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3"
|
|
>
|
|
<p className="text-sm leading-6 text-[var(--color-foreground)]">{item}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section className="grid gap-4 lg:grid-cols-2">
|
|
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">Slots Worth Calling Out</h2>
|
|
<p className="mt-1 text-sm text-[var(--color-muted-foreground)]">
|
|
Anatomy stories should name only the slots a consumer can reasonably style or
|
|
inspect in tests.
|
|
</p>
|
|
<div className="mt-5 grid gap-3">
|
|
{commonSlotNames.map((item) => (
|
|
<div
|
|
key={item.slot}
|
|
className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3"
|
|
>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<code className="text-sm font-medium">{`data-slot="${item.slot}"`}</code>
|
|
<span className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
|
slot
|
|
</span>
|
|
</div>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
{item.guidance}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</article>
|
|
|
|
<article className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">States Worth Explaining</h2>
|
|
<p className="mt-1 text-sm text-[var(--color-muted-foreground)]">
|
|
State stories should match the durable `data-*` surface, not one-off visual
|
|
tweaks.
|
|
</p>
|
|
<div className="mt-5 grid gap-3">
|
|
{commonStateNames.map((item) => (
|
|
<div
|
|
key={item.state}
|
|
className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3"
|
|
>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<code className="text-sm font-medium">{`data-${item.state}`}</code>
|
|
<span className="text-xs uppercase tracking-[var(--tracking-caps)] text-[var(--color-muted-foreground)]">
|
|
state
|
|
</span>
|
|
</div>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
{item.guidance}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-card)] p-6 shadow-[var(--shadow-sm)]">
|
|
<h2 className="text-2xl font-semibold">Before Opening The PR</h2>
|
|
<div className="mt-5 grid gap-3 md:grid-cols-2">
|
|
<div className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3">
|
|
<p className="text-sm font-medium text-[var(--color-foreground)]">
|
|
Ask if the docs tell a consumer when to choose the component.
|
|
</p>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
If the answer is "not really", the component description is still too
|
|
generic.
|
|
</p>
|
|
</div>
|
|
<div className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3">
|
|
<p className="text-sm font-medium text-[var(--color-foreground)]">
|
|
Ask if the anatomy story names the real public styling hooks.
|
|
</p>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
If the story only explains visuals, it is not yet useful to another engineer.
|
|
</p>
|
|
</div>
|
|
<div className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3">
|
|
<p className="text-sm font-medium text-[var(--color-foreground)]">
|
|
Ask if the most failure-prone behavior is explicitly documented.
|
|
</p>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
For overlays that usually means accessibility. For animated surfaces it may
|
|
be motion.
|
|
</p>
|
|
</div>
|
|
<div className="rounded-[var(--radius-sm)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3">
|
|
<p className="text-sm font-medium text-[var(--color-foreground)]">
|
|
Ask if the story count stayed disciplined.
|
|
</p>
|
|
<p className="mt-2 text-sm leading-6 text-[var(--color-muted-foreground)]">
|
|
The docs should feel intentional, not encyclopedic.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const meta = {
|
|
title: "Foundation/Component Authoring",
|
|
component: ComponentAuthoringGuide,
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
component:
|
|
"Use this page as the Storybook-side companion to the repo's component contract. It defines the minimum documentation recipe for a new component and keeps docs work aligned with slots, states, variants, and motion semantics that already exist in source."
|
|
}
|
|
},
|
|
layout: "fullscreen"
|
|
}
|
|
} satisfies Meta<typeof ComponentAuthoringGuide>;
|
|
|
|
export default meta;
|
|
|
|
type Story = StoryObj<typeof meta>;
|
|
|
|
export const Overview: Story = {};
|