Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "suaveplan-web-artifacts",
|
||||
"version": "1.0.0",
|
||||
"description": "Build production-grade React artifacts using Suaveplan component library"
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
---
|
||||
name: web-artifacts-builder
|
||||
description: Build production-ready React 19 + TypeScript + Tailwind CSS components and pages using Suaveplan's UI library, Vite bundling, and shadcn/ui primitives
|
||||
---
|
||||
|
||||
# Web Artifacts Builder for Suaveplan
|
||||
|
||||
Build React 19 components, pages, and interactive artifacts within the Suaveplan monorepo. All artifacts use TypeScript, Tailwind CSS, shadcn/ui from `@suaveplan/react-ui`, and Vite for bundling. No external UI libraries when Suaveplan equivalents exist.
|
||||
|
||||
## Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|-----------|
|
||||
| Framework | React 19 (Server Components, Actions, useActionState) |
|
||||
| Language | TypeScript 5.x (strict mode) |
|
||||
| Styling | Tailwind CSS with `cn()` from `@suaveplan/react-ui` |
|
||||
| UI Primitives | shadcn/ui via `@suaveplan/react-ui` |
|
||||
| Bundler | Vite (NOT Parcel, NOT webpack) |
|
||||
| Testing | Vitest (unit) + Playwright (E2E) |
|
||||
| Linting | Biome (NOT ESLint) |
|
||||
| Stories | Storybook 8 |
|
||||
| Package Manager | Bun with workspace protocol |
|
||||
|
||||
## Available Suaveplan Packages
|
||||
|
||||
Always use these instead of external alternatives:
|
||||
|
||||
| Need | Suaveplan Package | Do NOT Use |
|
||||
|------|------------------|------------|
|
||||
| Buttons, Cards, Dialogs, Inputs | `@suaveplan/react-ui` | Material UI, Chakra UI, Ant Design |
|
||||
| Forms with validation | `@suaveplan/react-forms` | React Hook Form (standalone) |
|
||||
| Data grids/tables | `@suaveplan/react-data-table` | AG Grid, TanStack Table (standalone) |
|
||||
| Notifications/toasts | `@suaveplan/react-notifications` | react-hot-toast, sonner |
|
||||
| Timeline/Gantt views | `@suaveplan/react-timeline` | vis-timeline |
|
||||
| Authentication UI | `@suaveplan/react-auth` | custom auth forms |
|
||||
| Media players | `@suaveplan/react-media` | react-player |
|
||||
| Internationalization | `@suaveplan/react-translate` | react-i18next (standalone) |
|
||||
| CSS class merging | `cn()` from `@suaveplan/react-ui` | clsx, classnames (standalone) |
|
||||
| Charts/visualization | `@suaveplan/react-charts` | recharts (standalone) |
|
||||
| Drive/file sharing UI | `@suaveplan/drive-react-sharing` | custom file dialogs |
|
||||
| Virtualized lists | `@suaveplan/react-virtual` | react-window, react-virtuoso |
|
||||
|
||||
## Component File Structure
|
||||
|
||||
Every component follows this five-file pattern inside its directory:
|
||||
|
||||
```text
|
||||
src/components/<component-name>/
|
||||
<component-name>.tsx # Implementation (max 250 lines)
|
||||
<component-name>.test.tsx # Unit tests (max 500 lines, 95%+ coverage)
|
||||
<component-name>.stories.tsx # Storybook stories
|
||||
<component-name>.types.ts # TypeScript interfaces and types
|
||||
<component-name>.md # Documentation (200+ words, simple + advanced examples)
|
||||
```
|
||||
|
||||
### Implementation File Pattern
|
||||
|
||||
```typescript
|
||||
// src/components/status-badge/status-badge.tsx
|
||||
import * as React from 'react';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { cn } from '@suaveplan/react-ui';
|
||||
import type { StatusBadgeProps } from './status-badge.types';
|
||||
|
||||
const statusBadgeVariants = cva(
|
||||
'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors',
|
||||
{
|
||||
variants: {
|
||||
status: {
|
||||
active: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
|
||||
pending: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
|
||||
inactive: 'bg-muted text-muted-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: { status: 'active' },
|
||||
}
|
||||
);
|
||||
|
||||
export const StatusBadge = React.forwardRef<HTMLSpanElement, StatusBadgeProps>(
|
||||
({ className, status, label, ...props }, ref) => {
|
||||
return (
|
||||
<span
|
||||
ref={ref}
|
||||
data-testid="sp-status-badge"
|
||||
role="status"
|
||||
aria-label={`Status: ${label ?? status}`}
|
||||
className={cn(statusBadgeVariants({ status }), className)}
|
||||
{...props}
|
||||
>
|
||||
{label ?? status}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
StatusBadge.displayName = 'StatusBadge';
|
||||
```
|
||||
|
||||
### Types File Pattern
|
||||
|
||||
```typescript
|
||||
// src/components/status-badge/status-badge.types.ts
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
import type { statusBadgeVariants } from './status-badge';
|
||||
|
||||
export interface StatusBadgeProps
|
||||
extends React.HTMLAttributes<HTMLSpanElement>,
|
||||
VariantProps<typeof statusBadgeVariants> {
|
||||
/** Display label (defaults to status name) */
|
||||
label?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Stories File Pattern
|
||||
|
||||
```typescript
|
||||
// src/components/status-badge/status-badge.stories.tsx
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { StatusBadge } from './status-badge';
|
||||
|
||||
const meta: Meta<typeof StatusBadge> = {
|
||||
title: 'Components/StatusBadge',
|
||||
component: StatusBadge,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
status: {
|
||||
control: 'select',
|
||||
options: ['active', 'pending', 'inactive'],
|
||||
},
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof StatusBadge>;
|
||||
|
||||
export const Active: Story = { args: { status: 'active' } };
|
||||
export const Pending: Story = { args: { status: 'pending', label: 'Awaiting Review' } };
|
||||
export const Inactive: Story = { args: { status: 'inactive' } };
|
||||
|
||||
export const AllStatuses: Story = {
|
||||
render: () => (
|
||||
<div className="flex gap-2">
|
||||
<StatusBadge status="active" />
|
||||
<StatusBadge status="pending" />
|
||||
<StatusBadge status="inactive" />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
```
|
||||
|
||||
### Test File Pattern
|
||||
|
||||
```typescript
|
||||
// src/components/status-badge/status-badge.test.tsx
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { StatusBadge } from './status-badge';
|
||||
|
||||
describe('StatusBadge', () => {
|
||||
it('renders with active status', () => {
|
||||
render(<StatusBadge status="active" />);
|
||||
expect(screen.getByTestId('sp-status-badge')).toBeInTheDocument();
|
||||
expect(screen.getByRole('status')).toHaveTextContent('active');
|
||||
});
|
||||
|
||||
it('uses custom label when provided', () => {
|
||||
render(<StatusBadge status="pending" label="In Review" />);
|
||||
expect(screen.getByRole('status')).toHaveTextContent('In Review');
|
||||
});
|
||||
|
||||
it('applies custom className via cn()', () => {
|
||||
render(<StatusBadge status="active" className="ml-4" />);
|
||||
expect(screen.getByTestId('sp-status-badge')).toHaveClass('ml-4');
|
||||
});
|
||||
|
||||
it('forwards ref', () => {
|
||||
const ref = { current: null as HTMLSpanElement | null };
|
||||
render(<StatusBadge ref={ref} status="active" />);
|
||||
expect(ref.current).toBeInstanceOf(HTMLSpanElement);
|
||||
});
|
||||
|
||||
it('has accessible aria-label', () => {
|
||||
render(<StatusBadge status="active" />);
|
||||
expect(screen.getByRole('status')).toHaveAttribute('aria-label', 'Status: active');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Documentation File Pattern
|
||||
|
||||
Follow `.claude/templates/module-docs.md` template with these requirements:
|
||||
|
||||
- Minimum 200 words
|
||||
- Include a "Basic Example" section (simple import + render)
|
||||
- Include an "Advanced Example" section (composition, custom styling, controlled state)
|
||||
- Document all props in an API Reference section
|
||||
- Explain design decisions
|
||||
|
||||
```markdown
|
||||
# StatusBadge
|
||||
|
||||
## Purpose
|
||||
A visual indicator component that displays the current status of an entity...
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Example
|
||||
\`\`\`typescript
|
||||
import { StatusBadge } from '@suaveplan/react-<package>';
|
||||
<StatusBadge status="active" />
|
||||
\`\`\`
|
||||
|
||||
### Advanced Example
|
||||
\`\`\`typescript
|
||||
import { StatusBadge } from '@suaveplan/react-<package>';
|
||||
import { cn } from '@suaveplan/react-ui';
|
||||
// Composed with conditional logic and custom styling...
|
||||
\`\`\`
|
||||
|
||||
## API Reference
|
||||
...
|
||||
```
|
||||
|
||||
## Styling Rules
|
||||
|
||||
### Tailwind CSS + cn() Utility
|
||||
|
||||
Always use `cn()` from `@suaveplan/react-ui` for conditional class merging:
|
||||
|
||||
```typescript
|
||||
import { cn } from '@suaveplan/react-ui';
|
||||
|
||||
// Correct: cn() for merging
|
||||
<div className={cn('flex items-center', isActive && 'bg-primary', className)} />
|
||||
|
||||
// Wrong: template literals for conditional classes
|
||||
<div className={`flex items-center ${isActive ? 'bg-primary' : ''}`} />
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
|
||||
Use Tailwind's `dark:` variant. Shadcn/ui CSS variables automatically adapt:
|
||||
|
||||
```typescript
|
||||
<div className="bg-background text-foreground dark:bg-background dark:text-foreground">
|
||||
<p className="text-muted-foreground">Uses CSS variables, works in both modes</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Shadcn/UI CSS Variables
|
||||
|
||||
Reference these semantic tokens instead of raw Tailwind colors:
|
||||
|
||||
- `background` / `foreground` -- page background and default text
|
||||
- `primary` / `primary-foreground` -- primary actions
|
||||
- `secondary` / `secondary-foreground` -- secondary actions
|
||||
- `muted` / `muted-foreground` -- subdued UI elements
|
||||
- `accent` / `accent-foreground` -- highlights and hover states
|
||||
- `destructive` / `destructive-foreground` -- errors and deletions
|
||||
- `border` -- default border color
|
||||
- `ring` -- focus ring color
|
||||
- `card` / `card-foreground` -- card surfaces
|
||||
|
||||
## Build Configuration
|
||||
|
||||
Suaveplan browser packages use Vite for building:
|
||||
|
||||
```typescript
|
||||
// vite.config.ts
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), dts({ rollupTypes: true })],
|
||||
build: {
|
||||
lib: {
|
||||
entry: 'src/index.ts',
|
||||
formats: ['es'],
|
||||
fileName: 'index',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['react', 'react-dom', 'react/jsx-runtime'],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Package.json Requirements
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@suaveplan/react-<name>",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@suaveplan/react-ui": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Max 250 lines** per implementation file -- split into sub-components if larger
|
||||
- **Max 500 lines** per test file -- split by feature area
|
||||
- **95% minimum test coverage** -- verified via `bun run test:coverage`
|
||||
- **No external UI libraries** when a Suaveplan package covers the need
|
||||
- **No Parcel** -- Vite is the bundler for all browser packages
|
||||
- **No tsc compilation** -- Vite handles transforms, TypeScript is for type-checking only
|
||||
- **No mocks** -- test with real implementations
|
||||
- **Biome linting** -- not ESLint
|
||||
- **React 19** -- use modern patterns (useActionState, useOptimistic, Server Components where applicable)
|
||||
- **Accessibility** -- ARIA labels, keyboard navigation, focus management on all interactive elements
|
||||
- **`data-testid`** attributes must use `sp-` prefix on all Suaveplan components
|
||||
- **`forwardRef`** -- all leaf components must forward refs
|
||||
- **Documentation** -- every `.tsx` implementation file must have a matching `.md` file
|
||||
Reference in New Issue
Block a user