Minimum Story Recipe
+{item.name}
+ + docs lane + ++ {item.note} +
+diff --git a/.gitignore b/.gitignore index 0509f57..fbc903e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ coverage .pnpm-store /.storybook .home +.tmp-home +playwright-report +test-results .DS_Store diff --git a/apps/docs/.storybook/preview.ts b/apps/docs/.storybook/preview.ts index e1a57e2..6948ffd 100644 --- a/apps/docs/.storybook/preview.ts +++ b/apps/docs/.storybook/preview.ts @@ -42,7 +42,7 @@ const preview: Preview = { }, parameters: { a11y: { - test: "todo" + test: "error" }, backgrounds: { default: "canvas", diff --git a/apps/docs/src/component-authoring.stories.tsx b/apps/docs/src/component-authoring.stories.tsx new file mode 100644 index 0000000..6155fb7 --- /dev/null +++ b/apps/docs/src/component-authoring.stories.tsx @@ -0,0 +1,253 @@ +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 ( +
+ AI UI / Docs Guide +
++ 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. +
+{item.name}
+ + docs lane + ++ {item.note} +
+{rule}
++ The docs should mirror the same public hooks that the components expose in + source. +
+{item}
++ Docs should reinforce the variant surface that engineering already considers + stable. +
+{item}
++ Anatomy stories should name only the slots a consumer can reasonably style or + inspect in tests. +
+{`data-slot="${item.slot}"`}
+
+ slot
+
+ + {item.guidance} +
++ State stories should match the durable `data-*` surface, not one-off visual + tweaks. +
+{`data-${item.state}`}
+
+ state
+
+ + {item.guidance} +
++ Ask if the docs tell a consumer when to choose the component. +
++ If the answer is "not really", the component description is still too + generic. +
++ Ask if the anatomy story names the real public styling hooks. +
++ If the story only explains visuals, it is not yet useful to another engineer. +
++ Ask if the most failure-prone behavior is explicitly documented. +
++ For overlays that usually means accessibility. For animated surfaces it may + be motion. +
++ Ask if the story count stayed disciplined. +
++ The docs should feel intentional, not encyclopedic. +
++ Dialog anatomy +
+
+ data-slot="overlay" sits
+ behind the surface and carries the backdrop motion.
+
+ data-slot="content" wraps
+ the modal panel and exposes data-size.
+
+ data-slot="header",{" "}
+ data-slot="footer",{" "}
+ data-slot="label", and{" "}
+ data-slot="description"
+ provide stable hooks for structure and docs.
+
+ The close button is built into DialogContent,
+ so every dialog gets a dismiss affordance even when the footer stays minimal.
+
Keep the title outcome-oriented so assistive tech announces the decision clearly.
++ Use the description for the consequence or next step, not decorative copy. +
++ Keep the trigger specific. "Open approval dialog" is more useful than a generic + "Open". +
++ Reserve dialogs for blocking work. If the content should not trap focus, prefer + a popover instead. +
++ Dropdown menu anatomy +
+
+ data-slot="content" frames
+ the floating panel and exposes sizing for denser menus.
+
+ data-slot="item",{" "}
+ data-slot="trigger", and{" "}
+ data-slot="shortcut" map the
+ action rows, nested trigger, and keyboard hint.
+
+ data-slot="label",{" "}
+ data-slot="separator", and{" "}
+ data-slot="icon" support
+ grouping, dividers, and selection markers.
+
Use labels and separators to group commands into short scannable clusters.
++ Keep checkbox and radio items in menus only when the state change is immediate + and local to the current context. +
++ Prefer concise labels. Long explanatory copy belongs in a dialog or popover, + not in a menu row. +
+Release health
++ 12 checks passed, 2 reviewers pending, and rollout is limited to 10% of + traffic. +
+Release health
-- 12 checks passed, 2 reviewers pending, and rollout is limited to 10% of traffic. -
-+ Popover anatomy +
+
+ data-slot="content" wraps
+ the floating surface and exposes data-size.
+
+ data-slot="arrow" visually
+ connects the surface back to its trigger.
+
+ Use the trigger for entry, keep the content concise, and add an explicit close + action only when the popover contains multi-step or form-like controls. +
++ Reviewer routing +
++ Keep contextual edits in a side panel instead of interrupting the page with a full + modal flow. +
++ Rollout note +
++ Sheets reuse dialog semantics while shifting the visual emphasis toward adjacent, + in-flow work. +
++ Sheet anatomy +
+
+ data-slot="overlay" on the
+ backdrop layer.
+
+ data-slot="content" on the
+ sheet panel itself.
+
+ data-slot="header",{" "}
+ data-slot="footer",{" "}
+ data-slot="label", and{" "}
+ data-slot="description"{" "}
+ mirror the dialog contract.
+
+ data-side and{" "}
+ data-size expose layout intent
+ for styling and docs inspection.
+
+ Tabs anatomy +
+
+ data-slot="root" carries{" "}
+ data-orientation so layout
+ styling can react to horizontal or vertical usage.
+
+ data-slot="list" groups the
+ triggers, while data-slot="trigger"{" "}
+ exposes active and disabled state styling.
+
+ data-slot="content" frames
+ the active panel and enters with the same motion language as the rest of the system.
+
Use tabs for sibling content, not for unrelated navigation destinations.
++ Keep trigger labels brief so keyboard users can scan the set quickly. +
++ Disabled tabs should represent unavailable views, not hidden primary steps. +
++ Toast anatomy +
+
+ data-slot="viewport" owns
+ the screen edge stacking region for multiple toasts.
+
+ data-slot="root" exposes{" "}
+ data-variant for semantic
+ feedback styling.
+
+ data-slot="label",{" "}
+ data-slot="description",{" "}
+ data-slot="action", and{" "}
+ data-slot="close" structure
+ the content and affordances.
+
Trigger a toast, then dismiss it with the close button or a horizontal swipe.
++ The viewport stacks from the lower edge on small screens and tightens into the + lower-right corner on larger canvases. +
++ Keep toast actions secondary and fast. Anything that needs a full decision should + escalate into a dialog instead. +
++ Tooltip anatomy +
+
+ data-slot="content" holds
+ the compact explanatory copy and exposes the size token.
+
+ data-slot="arrow" anchors
+ the floating label back to its trigger.
+
+ Tooltip copy should stay short enough to scan in one glance. It is a hint, not a + mini panel. +
+Always keep the trigger label meaningful even without the tooltip.
++ Use tooltips to clarify or shorten, not to hide critical onboarding copy. +
++ Avoid links, buttons, or form controls inside tooltip content. Those belong in a + popover. +
+