UNPKG

@loke/design-system

Version:

A design system with individually importable components

252 lines (237 loc) 11.3 kB
module.exports=`<!-- Parent: ../AGENTS.md --> <!-- Generated: 2026-04-07 | Updated: 2026-04-07 --> <h1>components</h1> <h2>Purpose</h2> <p>This directory contains 35 individually styled React components that form the core of the @loke/design-system. Each component is self-contained with its own implementation, stories, tests, and documentation. Components are designed for maximum reusability and support granular imports for tree-shaking.</p> <h2>Key Components</h2> <table> <thead> <tr><th>Component</th><th>Purpose</th></tr> </thead> <tbody> <tr><td><strong>Interactive</strong></td><td>button, checkbox, radio-group, switch, input, textarea, select, dropdown-menu, popover</td></tr> <tr><td><strong>Feedback</strong></td><td>alert, alert-dialog, toast, tooltip, spinner, skeleton</td></tr> <tr><td><strong>Layout</strong></td><td>card, tabs, accordion, collapsible, separator, sidebar, sheet</td></tr> <tr><td><strong>Forms</strong></td><td>label, form (Radix-based), date-picker, calendar, command</td></tr> <tr><td><strong>Display</strong></td><td>badge, avatar, heading, text, pagination, table</td></tr> <tr><td><strong>Complex</strong></td><td>dialog, date-picker (popover-based)</td></tr> </tbody> </table> <h2>Directory Structure</h2> <p>Each component follows this exact structure:</p> <pre><code>src/components/ComponentName/ ├── component-name.tsx # Implementation with CVA variants ├── component-name.stories.tsx # Storybook stories (required) ├── component-name.test.ts # Smoke tests (required) ├── index.ts # Named export only └── README.mdx # Documentation with examples </code></pre> <h2>For AI Agents</h2> <h3>File Purposes</h3> <ul> <li> <p><strong>component-name.tsx</strong> — Main implementation file. Exports the component using <code>forwardRef</code>, declares <code>ComponentProps</code> interface extending HTML attributes, and exports <code>componentVariants</code> CVA object. Includes JSDoc comments.</p> </li> <li> <p><strong>component-name.stories.tsx</strong> — Storybook stories for visual development and documentation. Required for Storybook autodocs and smoke tests. Uses Storybook 7 syntax with <code>Meta</code>, <code>StoryObj</code>, and <code>argTypes</code> for variant controls.</p> </li> <li> <p><strong>component-name.test.ts</strong> — Vitest smoke tests. Imports story module and calls <code>createSmokeTests(stories, &quot;ComponentName&quot;)</code> to auto-generate tests for each story. Must exist for CI to pass.</p> </li> <li> <p><strong>index.ts</strong> — Single named export. Example: <code>export { Button } from &quot;./button&quot;</code>. Keep minimal.</p> </li> <li> <p><strong>README.mdx</strong> — Component documentation in MDX format. Rendered in Storybook. Must include usage examples, props table, and implementation notes.</p> </li> </ul> <h3>Common Patterns</h3> <h4>1. CVA Variants Pattern</h4> <pre><code class="language-tsx">import { cva, type VariantProps } from &quot;class-variance-authority&quot;; import { cn } from &quot;@loke/design-system/cn&quot;; const buttonVariants = cva( cn(&quot;base-classes&quot;, &quot;focus-states&quot;, &quot;disabled-states&quot;), { variants: { variant: { default: &quot;bg-primary text-primary-foreground hover:bg-brand-900&quot;, destructive: &quot;bg-destructive text-destructive-foreground&quot;, outline: &quot;border border-input bg-transparent hover:bg-accent&quot;, }, size: { sm: &quot;h-9 px-3&quot;, default: &quot;h-10 px-4&quot;, lg: &quot;h-11 px-8&quot;, }, }, defaultVariants: { variant: &quot;default&quot;, size: &quot;default&quot; }, } ); </code></pre> <h4>2. Component with forwardRef</h4> <pre><code class="language-tsx">import { forwardRef, type ButtonHTMLAttributes } from &quot;react&quot;; export interface ButtonProps extends ButtonHTMLAttributes&lt;HTMLButtonElement&gt;, VariantProps&lt;typeof buttonVariants&gt; { asChild?: boolean; // Slot composition support } const Button = forwardRef&lt;HTMLButtonElement, ButtonProps&gt;( ({ className, variant, size, asChild = false, ...props }, ref) =&gt; { const Comp = asChild ? Slot : &quot;button&quot;; return ( &lt;Comp ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props} /&gt; ); } ); Button.displayName = &quot;Button&quot;; export { Button }; </code></pre> <h4>3. Slot Composition (Polymorphic Components)</h4> <pre><code class="language-tsx">import { createSlot } from &quot;@loke/ui/slot&quot;; const ButtonSlot = createSlot(&quot;Button&quot;); // Allows: &lt;Button asChild&gt;&lt;a href=&quot;...&quot;&gt;Link&lt;/a&gt;&lt;/Button&gt; </code></pre> <h4>4. Client Directive (for interactive components)</h4> <p>Add to top of file if component uses hooks or event handlers:</p> <pre><code class="language-tsx">&quot;use client&quot;; </code></pre> <h4>5. Importing from Sibling Packages</h4> <pre><code class="language-tsx">// Icons import { Plus, ChevronDown } from &quot;@loke/icons&quot;; // Unstyled Radix primitives import { Root, Trigger, Content } from &quot;@loke/ui/dialog&quot;; // Class name merging import { cn } from &quot;@loke/design-system/cn&quot;; </code></pre> <h3>Testing Pattern</h3> <p>Every component <strong>must</strong> have a test file using <code>createSmokeTests</code>:</p> <pre><code class="language-tsx">// button.test.ts import { createSmokeTests } from &quot;@loke/design-system/test&quot;; import * as stories from &quot;./button.stories&quot;; createSmokeTests(stories, &quot;Button&quot;); </code></pre> <p>The <code>createSmokeTests</code> helper automatically generates tests for each story, verifying the component renders without crashing.</p> <h3>Adding a New Component</h3> <ol> <li> <p><strong>Create folder:</strong> <code>src/components/YourComponentName/</code> (PascalCase folder name)</p> </li> <li> <p><strong>Create implementation file</strong> (<code>your-component-name.tsx</code>):</p> <ul> <li>Use CVA for variants</li> <li>Use forwardRef for ref forwarding</li> <li>Extend HTML attributes in props interface</li> <li>Export component and variants</li> <li>Add JSDoc comments</li> </ul> </li> <li> <p><strong>Create stories file</strong> (<code>your-component-name.stories.tsx</code>):</p> <ul> <li>Define Meta with component, title, and argTypes</li> <li>Export Default story with args</li> <li>Export 5-10 additional stories showing variants and states</li> <li>Use render() for composite stories</li> </ul> </li> <li> <p><strong>Create test file</strong> (<code>your-component-name.test.ts</code>):</p> <pre><code class="language-tsx">import { createSmokeTests } from &quot;@loke/design-system/test&quot;; import * as stories from &quot;./your-component-name.stories&quot;; createSmokeTests(stories, &quot;YourComponentName&quot;); </code></pre> </li> <li> <p><strong>Create index file</strong> (<code>index.ts</code>):</p> <pre><code class="language-tsx">export { YourComponentName } from &quot;./your-component-name&quot;; export type { YourComponentNameProps } from &quot;./your-component-name&quot;; </code></pre> </li> <li> <p><strong>Create README</strong> (<code>README.mdx</code>):</p> <ul> <li>MDX format with Meta block for Storybook routing</li> <li>Sections: description, features, installation, usage examples, props table, variants, accessibility, best practices</li> </ul> </li> <li> <p><strong>Update package.json</strong> exports: Add entry to <code>packages/design-system/package.json</code>:</p> <pre><code class="language-json">&quot;./your-component-name&quot;: { &quot;import&quot;: { &quot;default&quot;: &quot;./dist/components/your-component-name/index.mjs&quot;, &quot;types&quot;: &quot;./dist/components/your-component-name/index.d.mts&quot; }, &quot;require&quot;: { &quot;default&quot;: &quot;./dist/components/your-component-name/index.js&quot;, &quot;types&quot;: &quot;./dist/components/your-component-name/index.d.ts&quot; } } </code></pre> </li> <li> <p><strong>Verify:</strong></p> <pre><code class="language-bash">bun run build # Should complete without errors bun run test # All tests should pass bun run storybook # Should display your component </code></pre> </li> </ol> <h3>Storybook Best Practices</h3> <ul> <li><strong>argTypes:</strong> Define control types for all variant props (select, boolean, etc.)</li> <li><strong>tags:</strong> Add <code>tags: [&quot;autodocs&quot;]</code> to Meta to enable automatic documentation</li> <li><strong>Default story:</strong> Always export a <code>Default</code> story as the primary example</li> <li><strong>Stories:</strong> Name exports as PascalCase (e.g., <code>WithIcon</code>, <code>DisabledState</code>, <code>AllVariants</code>)</li> <li><strong>Render functions:</strong> Use <code>render()</code> for composite stories showing multiple variants</li> <li><strong>Icons:</strong> Import from @loke/icons; avoid hardcoding SVGs</li> </ul> <h3>Accessibility Checklist</h3> <ul> <li>Use semantic HTML (<code>&lt;button&gt;</code>, <code>&lt;input&gt;</code>, etc.)</li> <li>Include aria attributes (aria-label, aria-expanded, aria-invalid, etc.)</li> <li>Test keyboard navigation (Tab, Enter, Space, Escape)</li> <li>Include focus-visible states in Tailwind classes</li> <li>Support disabled states with pointer-events-none</li> <li>Use aria-hidden for decorative elements</li> </ul> <h3>Styling with Tailwind v4</h3> <ul> <li>Use <code>cn()</code> to merge conditional Tailwind classes</li> <li>Leverage CSS variables for theme colors (--color-primary, --color-destructive, etc.)</li> <li>Use gap, padding, margin utilities for spacing</li> <li>Apply dark: prefix for dark mode support</li> <li>Use responsive prefixes (sm:, md:, lg:, etc.) sparingly—prefer layout components</li> </ul> <h3>Common Imports Reference</h3> <pre><code class="language-tsx">// React &amp; HTML attributes import { forwardRef, type HTMLAttributes, type ReactNode } from &quot;react&quot;; // Design system utilities import { cn } from &quot;@loke/design-system/cn&quot;; import { Box } from &quot;@loke/design-system/box&quot;; // Variant management import { cva, type VariantProps } from &quot;class-variance-authority&quot;; // Icons import { Plus, ChevronDown } from &quot;@loke/icons&quot;; // Unstyled UI primitives import { Root, Trigger, Content } from &quot;@loke/ui/dialog&quot;; import { createSlot } from &quot;@loke/ui/slot&quot;; // Storybook import type { Meta, StoryObj } from &quot;@storybook/react&quot;; // Testing import { createSmokeTests } from &quot;@loke/design-system/test&quot;; </code></pre> <h3>Debugging Tips</h3> <ul> <li><strong>Build fails:</strong> Run <code>bun run lint</code> and <code>bun run typecheck</code> to catch issues early</li> <li><strong>Story not rendering:</strong> Verify <code>tags: [&quot;autodocs&quot;]</code> in Meta and all stories export correctly</li> <li><strong>Styles not applying:</strong> Check CVA syntax, ensure <code>cn()</code> is imported, verify Tailwind classes exist</li> <li><strong>Test fails:</strong> Ensure every story renders without errors; check console for React warnings</li> </ul> <!-- MANUAL: --> `;