From d13f7d166e3dc8076b49d25a0bae13fc668e4801 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 6 Apr 2026 14:55:35 +0200 Subject: [PATCH] Add Plan 5: UI System implementation plan --- .../plans/2026-04-06-plan-5-ui-system.md | 610 ++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-06-plan-5-ui-system.md diff --git a/docs/superpowers/plans/2026-04-06-plan-5-ui-system.md b/docs/superpowers/plans/2026-04-06-plan-5-ui-system.md new file mode 100644 index 0000000..53c8944 --- /dev/null +++ b/docs/superpowers/plans/2026-04-06-plan-5-ui-system.md @@ -0,0 +1,610 @@ +# Plan 5: UI System — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Set up `@repo/ui` with Atomic Design folder structure, Tailwind CSS v4, shadcn/ui, base components (atoms + a molecule), and `apps/storybook` with Storybook 8. + +**Architecture:** `@repo/ui` uses Atomic Design (atoms/molecules/organisms/templates). Tailwind v4 uses CSS-first config (`@import "tailwindcss"` + `@theme`). shadcn/ui components land in `atoms/` by default. Storybook runs as a separate app pulling stories from the UI package via `@storybook/react-vite` with `@tailwindcss/vite` plugin. + +**Tech Stack:** Tailwind CSS v4, shadcn/ui, clsx, tailwind-merge, Storybook 8, @storybook/react-vite + +--- + +### Task 1: Set up @repo/ui with Tailwind v4 + Atomic Design structure + +**Files:** +- Modify: `packages/ui/package.json` +- Modify: `packages/ui/tsconfig.json` +- Create: `packages/ui/src/styles/globals.css` +- Create: `packages/ui/src/lib/utils.ts` +- Create: `packages/ui/src/atoms/index.ts` +- Create: `packages/ui/src/molecules/index.ts` +- Create: `packages/ui/src/organisms/index.ts` +- Create: `packages/ui/src/templates/index.ts` +- Modify: `packages/ui/src/index.ts` + +- [ ] **Step 1: Update packages/ui/package.json** + +```json +{ + "name": "@repo/ui", + "private": true, + "version": "0.0.0", + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "build": "echo 'typechecked by consuming app bundler'", + "lint": "eslint .", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "clsx": "^2.1.0", + "tailwind-merge": "^3.0.0", + "react": "^19.0.0" + }, + "devDependencies": { + "@repo/eslint-config": "workspace:*", + "@repo/typescript-config": "workspace:*", + "@types/react": "^19.0.0", + "tailwindcss": "^4.1.0" + } +} +``` + +- [ ] **Step 2: Create src/styles/globals.css** + +Tailwind v4 CSS-first config — no tailwind.config.ts needed. + +```css +@import "tailwindcss"; + +@theme { + --color-background: hsl(0 0% 100%); + --color-foreground: hsl(240 10% 3.9%); + --color-card: hsl(0 0% 100%); + --color-card-foreground: hsl(240 10% 3.9%); + --color-popover: hsl(0 0% 100%); + --color-popover-foreground: hsl(240 10% 3.9%); + --color-primary: hsl(240 5.9% 10%); + --color-primary-foreground: hsl(0 0% 98%); + --color-secondary: hsl(240 4.8% 95.9%); + --color-secondary-foreground: hsl(240 5.9% 10%); + --color-muted: hsl(240 4.8% 95.9%); + --color-muted-foreground: hsl(240 3.8% 46.1%); + --color-accent: hsl(240 4.8% 95.9%); + --color-accent-foreground: hsl(240 5.9% 10%); + --color-destructive: hsl(0 84.2% 60.2%); + --color-destructive-foreground: hsl(0 0% 98%); + --color-border: hsl(240 5.9% 90%); + --color-input: hsl(240 5.9% 90%); + --color-ring: hsl(240 5.9% 10%); + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; +} +``` + +- [ ] **Step 3: Create src/lib/utils.ts** + +```typescript +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} +``` + +- [ ] **Step 4: Create atomic design barrel files** + +```typescript +// packages/ui/src/atoms/index.ts +// Atom components are exported from here +export {}; +``` + +```typescript +// packages/ui/src/molecules/index.ts +// Molecule components are exported from here +export {}; +``` + +```typescript +// packages/ui/src/organisms/index.ts +// Organism components are exported from here +export {}; +``` + +```typescript +// packages/ui/src/templates/index.ts +// Template components are exported from here +export {}; +``` + +- [ ] **Step 5: Update src/index.ts** + +```typescript +export { cn } from "./lib/utils.js"; +export * from "./atoms/index.js"; +export * from "./molecules/index.js"; +export * from "./organisms/index.js"; +export * from "./templates/index.js"; +``` + +- [ ] **Step 6: Run pnpm install and commit** + +```bash +pnpm install +git add packages/ui/ pnpm-lock.yaml +git commit -m "feat(ui): set up Atomic Design structure with Tailwind v4" +``` + +--- + +### Task 2: Add Button atom + +**Files:** +- Create: `packages/ui/src/atoms/button/button.tsx` +- Create: `packages/ui/src/atoms/button/button.stories.tsx` +- Create: `packages/ui/src/atoms/button/index.ts` +- Modify: `packages/ui/src/atoms/index.ts` + +- [ ] **Step 1: Create button.tsx** + +```tsx +import { forwardRef, type ButtonHTMLAttributes } from "react"; +import { cn } from "../../lib/utils.js"; + +export interface ButtonProps extends ButtonHTMLAttributes { + variant?: "default" | "secondary" | "destructive" | "outline" | "ghost"; + size?: "sm" | "default" | "lg"; +} + +const variantStyles: Record, string> = { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + ghost: "hover:bg-accent hover:text-accent-foreground", +}; + +const sizeStyles: Record, string> = { + sm: "h-9 px-3 text-sm", + default: "h-10 px-4 py-2", + lg: "h-11 px-8 text-lg", +}; + +export const Button = forwardRef( + ({ className, variant = "default", size = "default", ...props }, ref) => { + return ( +