@loke/design-system
Version:
A design system with individually importable components
252 lines (237 loc) • 11.3 kB
JavaScript
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, "ComponentName")</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 "./button"</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 "class-variance-authority";
import { cn } from "@loke/design-system/cn";
const buttonVariants = cva(
cn("base-classes", "focus-states", "disabled-states"),
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-brand-900",
destructive: "bg-destructive text-destructive-foreground",
outline: "border border-input bg-transparent hover:bg-accent",
},
size: {
sm: "h-9 px-3",
default: "h-10 px-4",
lg: "h-11 px-8",
},
},
defaultVariants: { variant: "default", size: "default" },
}
);
</code></pre>
<h4>2. Component with forwardRef</h4>
<pre><code class="language-tsx">import { forwardRef, type ButtonHTMLAttributes } from "react";
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean; // Slot composition support
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
ref={ref}
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
);
}
);
Button.displayName = "Button";
export { Button };
</code></pre>
<h4>3. Slot Composition (Polymorphic Components)</h4>
<pre><code class="language-tsx">import { createSlot } from "@loke/ui/slot";
const ButtonSlot = createSlot("Button");
// Allows: <Button asChild><a href="...">Link</a></Button>
</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">"use client";
</code></pre>
<h4>5. Importing from Sibling Packages</h4>
<pre><code class="language-tsx">// Icons
import { Plus, ChevronDown } from "@loke/icons";
// Unstyled Radix primitives
import { Root, Trigger, Content } from "@loke/ui/dialog";
// Class name merging
import { cn } from "@loke/design-system/cn";
</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 "@loke/design-system/test";
import * as stories from "./button.stories";
createSmokeTests(stories, "Button");
</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 "@loke/design-system/test";
import * as stories from "./your-component-name.stories";
createSmokeTests(stories, "YourComponentName");
</code></pre>
</li>
<li>
<p><strong>Create index file</strong> (<code>index.ts</code>):</p>
<pre><code class="language-tsx">export { YourComponentName } from "./your-component-name";
export type { YourComponentNameProps } from "./your-component-name";
</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">"./your-component-name": {
"import": {
"default": "./dist/components/your-component-name/index.mjs",
"types": "./dist/components/your-component-name/index.d.mts"
},
"require": {
"default": "./dist/components/your-component-name/index.js",
"types": "./dist/components/your-component-name/index.d.ts"
}
}
</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: ["autodocs"]</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><button></code>, <code><input></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 & HTML attributes
import { forwardRef, type HTMLAttributes, type ReactNode } from "react";
// Design system utilities
import { cn } from "@loke/design-system/cn";
import { Box } from "@loke/design-system/box";
// Variant management
import { cva, type VariantProps } from "class-variance-authority";
// Icons
import { Plus, ChevronDown } from "@loke/icons";
// Unstyled UI primitives
import { Root, Trigger, Content } from "@loke/ui/dialog";
import { createSlot } from "@loke/ui/slot";
// Storybook
import type { Meta, StoryObj } from "@storybook/react";
// Testing
import { createSmokeTests } from "@loke/design-system/test";
</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: ["autodocs"]</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: -->
`;