import { authoringChecklist, commonSlotNames, commonStateNames, cvaConventions, getMotionRecipeClassNames, motionRecipes } from "@ai-ui/ui"; import type { Meta, StoryObj } from "@storybook/react"; const componentRecipeExample = `const buttonVariants = cva( [ "inline-flex items-center justify-center gap-2", "rounded-[var(--radius-sm)] font-medium", "motion-pressable motion-ring" ], { variants: { variant: { primary: "bg-[var(--color-primary)] text-[var(--color-primary-foreground)]", secondary: "bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)]" }, size: { sm: "h-9 px-3 text-sm", md: "h-10 px-4 text-sm", lg: "h-12 px-5 text-base" } }, defaultVariants: { variant: "primary", size: "md" } } );`; const stateRecipeExample = `const rootProps = withRootProps( { className: cn(buttonVariants({ variant, size }), className) }, { slot: "root", states: { disabled, loading, state: open ? "open" : "closed" } } );`; function ContractsOverview() { return (

AI UI / Phase 2

Component authoring now follows one repeatable contract.

Phase 2 now ships real components and codifies the shared state, slot, variant, and motion conventions they follow.

Authoring Checklist

{authoringChecklist.map((item) => (

{item}

))}

CVA Conventions

{cvaConventions.map((item) => (

{item}

))}

State Naming

Public styling state should flow through stable `data-*` attributes.

{commonStateNames.map((item) => (
{`data-${item.state}`} convention

{item.guidance}

))}

Slot Naming

Slots create stable styling hooks for component internals and docs.

{commonSlotNames.map((item) => (
{`data-slot="${item.slot}"`} slot

{item.guidance}

))}

Variant Base Example

Variants should start from a strong base string, then branch only where appearance semantics actually change.

              {componentRecipeExample}
            

State Helper Example

Shared helpers standardize slot names and `data-*` state attributes.

              {stateRecipeExample}
            

Motion Recipe Helpers

These class names map directly to the recipe layer defined in `motion.css`.

{getMotionRecipeClassNames("transition", "ring", "pressable")}
{Object.entries(motionRecipes).map(([name, className]) => (

{name}

{className}
))}
); } const meta = { title: "Foundation/Contracts", component: ContractsOverview, parameters: { layout: "fullscreen" } } satisfies Meta; export default meta; type Story = StoryObj; export const Overview: Story = {};