diff --git a/.claude/commands/archon/archon-ui-consistency-review.md b/.claude/commands/archon/archon-ui-consistency-review.md index 091d813d..f44e88ea 100644 --- a/.claude/commands/archon/archon-ui-consistency-review.md +++ b/.claude/commands/archon/archon-ui-consistency-review.md @@ -1,7 +1,7 @@ --- description: Analyze UI components for reusability, Radix usage, primitives, and styling consistency argument-hint: -allowed-tools: Read, Grep, Glob, Write +allowed-tools: Read, Grep, Glob, Write, Bash thinking: auto --- @@ -9,261 +9,51 @@ thinking: auto **Review scope**: $ARGUMENTS -I'll analyze the UI components and generate a detailed report on consistency, reusability, and adherence to the Archon design system. +## Process -## Review Process +### Step 1: Load Standards +Read `PRPs/ai_docs/UI_STANDARDS.md` - This is the single source of truth for all rules, patterns, and scans. -### Step 1: Load Archon Design System Standards - -Read the following documentation for design system standards: -- `PRPs/ai_docs/UI_STANDARDS.md` - Complete UI design standards (Tailwind v4, Radix, responsive patterns) -- `CLAUDE.md` - General development standards and patterns -- `archon-ui-main/src/features/ui/primitives/` - Available primitive components -- Existing codebase patterns as reference - -The UI_STANDARDS.md document contains: -- Section 0: Project-wide Conventions -- Section 1: Radix Primitives -- Section 2: Tailwind CSS (v4) - includes anti-patterns for dynamic classes -- Section 3: Responsive Layout - includes horizontal scroll patterns -- Section 4: Light/Dark Themes -- Section 5: Component Reusability & Shared Primitives -- Section 6: Tailwind Tokens -- Section 7: Pre-Flight Checklist -- Section 8: Automated Scanning Patterns -- Section 9: Quick Reference - -### Step 2: Scan Components - -Scan the provided path for: -- React components (`.tsx` files) -- Component usage patterns -- Imports from primitives vs manual styling - -### Step 3: Compare Against Standards - -For each component, check against Archon design standards: -- Radix primitive usage (vs native HTML) -- Tailwind class patterns (no dynamic class construction) -- Responsive layout patterns (mobile-first with breakpoints) -- Component reusability (primitives vs custom implementations) -- Dark mode support -- Glassmorphism styling consistency - -### Step 4: Automated Scans - -Run automated pattern detection to find common violations: - -```bash -# Dynamic Tailwind class construction (BREAKING) -grep -r "\`bg-\${.*}\|\`text-\${.*}\|\`border-\${.*}" [path] --include="*.tsx" - -# Unconstrained horizontal scroll (BREAKING) -grep -r "overflow-x-auto" [path] --include="*.tsx" | grep -v "w-full" - -# Non-responsive grids (BREAKING) -grep -r "grid-cols-[2-9]" [path] --include="*.tsx" | grep -v "md:\|lg:\|sm:\|xl:" - -# Native HTML form elements (should use Radix) -grep -r "`) that centralize class lists over duplicating long utility strings across the app. -- Maintain a **z‑index scale** (e.g., base=0, dropdown=30, overlay=40, modal=50, toast=60) and stick to it. -- **Desktop-primary with responsive adaptations**: Optimize for desktop by default and add breakpoint rules for tablets/phones. -- Use **Radix UI primitives** for all interactive elements (Select, Checkbox, RadioGroup, Dialog, etc.) - never native HTML form elements. -- Use **shared component primitives** (Card, PillNavigation, etc.) from `@/features/ui/primitives/` instead of hardcoding repeated patterns. - -**Anti‑Patterns** -- Arbitrary values like `bg-[#12abef]`, `text-[17px]`, `w-[913px]` in markup (unless promoted to tokens or used as static one-offs). -- Global layout hacks (e.g., `body { overflow: hidden }`) to "fix" scroll; fix the root cause instead. -- Competing styles on the same element (e.g., `w-full w-1/2`, `px-4 px-8`). -- Native HTML form elements (``, ``) - use Radix primitives. -- Hardcoding glassmorphism, edge-lit effects, or pill navigation - use shared primitives. - ---- - -## 1) Radix Primitives - -**Rules** -- **Compose, don’t wrap**: use `asChild` to attach Radix behavior to your button/link components. -- **Use the documented structure** for each primitive (e.g., `Root/Trigger/Portal/Overlay/Content/Close` for Dialog). -- **Style via your classes and Radix data attributes** (e.g., `[data-state="open"]`, `[data-disabled]`) instead of targeting internal DOM. -- **Layering**: use `Portal` and your z‑index scale for overlays/popovers; avoid random `z-[9999]`. -- **A11y**: never remove focus rings or ARIA attributes that primitives rely on. - -**Anti‑Patterns** -- Deep styling of internal nodes (e.g., selecting Radix’s internal class names or structure). -- Manually toggling open/close state without Radix APIs. -- Mixing multiple portaled surfaces with arbitrary z‑indices (dialogs behind popovers, etc.). - -**Good Examples** -```tsx -// Compose a Trigger with your Button using asChild - - - - - - - - Title - - Description - -
- - -
-
-
-
-``` - -```css -/* State-driven styling via data attributes */ -.popover-content[data-state="open"] { /* animations, borders, etc. */ } -``` - ---- - -## 2) Tailwind CSS (v4) - -**Rules** -- **CRITICAL: Define custom dark variant FIRST** - Required for `dark:` utilities to work in v4. -- **Define tokens properly**: Variables in `:root` and `.dark` with **bare HSL values** (NO hsl() wrapper), then map to Tailwind with `@theme inline`. -```css -@import "tailwindcss"; - -@custom-variant dark (&:where(.dark, .dark *)); /* REQUIRED for dark: utilities */ - -:root { - /* Bare HSL values - Tailwind adds hsl() wrapper automatically */ - --background: 0 0% 98%; - --foreground: 240 10% 3.9%; - --border: 240 5.9% 90%; - --radius: 0.5rem; - color-scheme: light; -} - -.dark { - /* Bare HSL values - redefine same variables */ - --background: 0 0% 0%; - --foreground: 0 0% 100%; - --border: 240 3.7% 15.9%; - color-scheme: dark; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-border: var(--border); - --radius-lg: var(--radius); - --radius-md: calc(var(--radius) - 2px); -} -``` - -- **CRITICAL: NO dynamic class name construction**. Tailwind processes source code as **plain text at BUILD time**, not runtime. It cannot understand variables, string concatenation, or template literals. -- **CSS variables ARE allowed in arbitrary values** as long as the utility name is static (e.g., `bg-[var(--accent)]`, `border-[color:var(--accent)]`). -- **Use static class lookup objects** for discrete variants (e.g., `const colorClasses = { cyan: "bg-cyan-500 text-cyan-700", ... }`). -- **Use CSS variables pattern** for flexible/continuous values (colors, alphas, glows, gradients). -- **Conditional classes must be complete strings** (use `cn()` helper with full class names, not interpolated fragments). -- **Arbitrary values are allowed** if written as complete static strings in source code (e.g., `shadow-[0_0_30px_rgba(34,211,238,0.4)]`). -- **Inline style allowed ONLY for CSS variables** (e.g., `style={{ "--accent": token }}`), never for direct visual CSS. -- **`@layer` and `@apply` are still valid** in v4 - use them for base styles and component classes. For custom utilities, register with `@utility` first. -- **Dark mode**: use `dark:` variants consistently for colors, borders, shadows (and add `.dark` on ``). -- **No arbitrary values** unless promoted to tokens or used as static one-offs; if you need a value repeatedly, add it to `@theme`. -- **Reference tokens** for any custom CSS (e.g., `border-radius: var(--radius-md)`). -- **If referencing vars inside vars**, use `@theme inline` to avoid resolution pitfalls. - -**Anti‑Patterns** -- **Dynamic class construction**: `bg-${color}-500`, `` `text-${textColor}-700` ``, `className={isActive ? \`bg-${activeColor}-500\` : ...}` - CSS WILL NOT BE GENERATED. -- **String interpolation for class names**: Template literals with variables, computed class names. -- `style="..."` for visual styling (except setting local CSS vars). -- Mixing utility classes that conflict or duplicate a property. -- Introducing new colors/sizes ad‑hoc instead of defining in `:root`/`.dark` and mapping via `@theme inline`. - -**Good Examples** - -### Pattern A: Lookup Map (discrete variants) -```tsx -// ✅ CORRECT - Static class lookup object for finite set of variants -const colorClasses = { - cyan: "bg-cyan-500 text-cyan-700 border-cyan-500/50", - purple: "bg-purple-500 text-purple-700 border-purple-500/50", - blue: "bg-blue-500 text-blue-700 border-blue-500/50", -}; -
- -// ✅ CORRECT - Conditional with complete class names -
- -// ✅ CORRECT - Use cn() helper with complete class strings -
-``` - -### Pattern B: CSS Variables (flexible values) -```tsx -// ✅ CORRECT - CSS variables in arbitrary values (utility name is static) -// In CSS: :root { --accent: oklch(0.75 0.12 210); } -
- -// ✅ CORRECT - Set variable via inline style, use in static utilities -
- -// ✅ CORRECT - Named accent classes that set CSS variables -// In CSS: .accent-cyan { --accent: var(--color-cyan-500); } -const accentClass = { - cyan: "accent-cyan", - orange: "accent-orange", -} as const; -
-``` +### Rules +- **NO dynamic class construction** - Tailwind scans source code as plain text at build time + - NEVER: `` `bg-${color}-500` ``, `` `ring-${color}-500` ``, `` `shadow-${size}` `` + - Use static lookup objects instead +- **Bare HSL values in CSS variables** - NO `hsl()` wrapper + - Correct: `--background: 0 0% 98%;` + - Wrong: `--background: hsl(0 0% 98%);` +- **CSS variables allowed in arbitrary values** - Utility name must be static + - Correct: `bg-[var(--accent)]` + - Wrong: `` `bg-[var(--${colorName})]` `` +- **Use @theme inline** to map CSS vars to Tailwind utilities +- **Define @custom-variant dark** - Required for `dark:` to work in v4 ### Anti-Patterns ```tsx -// ❌ BROKEN - Dynamic class construction (CSS NOT GENERATED) +// ❌ Dynamic classes (NO CSS GENERATED) const color = "cyan"; -
// NO CSS OUTPUT -
// NO CSS OUTPUT +
+
// Common miss! -// ❌ BROKEN - Inline style for visual CSS (use variables instead) -
// Should be className or CSS variable - -// ✅ CORRECT alternative - Use CSS variable -
+// ❌ Inline styles for visual CSS +
``` +### Good Examples ```tsx -// Centralized Button component (preferred over scattered long class strings) -export function Button({ variant = "primary", className = "", ...props }) { - const base = - "inline-flex items-center gap-2 rounded-[var(--radius-md)] px-3 py-2 text-sm font-medium " + - "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 " + - "transition-colors"; - const variants = { - primary: - "bg-brand-500 text-white hover:brightness-110 dark:text-white " + - "focus-visible:outline-brand-500", - ghost: - "bg-transparent text-fg dark:text-fg-dark hover:bg-black/5 dark:hover:bg-white/10" - }; - return - ))} -
-
- -// ❌ BROKEN - Native HTML elements - ``` +### Automated Scans +```bash +# All dynamic class construction patterns +grep -rn "className.*\`.*\${.*}\`" [path] --include="*.tsx" +grep -rn "bg-\${.*}\|text-\${.*}\|border-\${.*}" [path] --include="*.tsx" +grep -rn "ring-\${.*}\|shadow-\${.*}\|outline-\${.*}\|opacity-\${.*}" [path] --include="*.tsx" + +# Inline visual styles (not CSS vars) +grep -rn "style={{.*backgroundColor\|color:\|padding:" [path] --include="*.tsx" +``` + +**Fix Pattern**: Add all properties to static variant object (checked, glow, focusRing, hover) + --- -## 6) Tailwind Tokens Quickstart (promote values to tokens) +## 2. LAYOUT & RESPONSIVE -```css -@import "tailwindcss"; +### Rules +- **Responsive grids** - NEVER fixed columns without breakpoints + - Use: `grid-cols-1 md:grid-cols-2 lg:grid-cols-4` +- **Constrain horizontal scroll** - Parent must have `w-full` or `max-w-*` +- **Add scrollbar-hide** to all `overflow-x-auto` containers +- **min-w-0 on flex parents** containing scroll containers (prevents page expansion) +- **Text truncation** - Always use `truncate`, `line-clamp-N`, or `break-words` +- **Desktop-primary** - Optimize for desktop, add responsive breakpoints down -:root { - --background: hsl(0 0% 98%); - --foreground: hsl(240 10% 3.9%); - --border: hsl(240 5.9% 90%); - --brand-500: oklch(0.70 0.15 220); - --radius: 0.5rem; - color-scheme: light; -} +### Anti-Patterns +```tsx +// ❌ Fixed grid (breaks mobile) +
-.dark { - --background: hsl(0 0% 0%); - --foreground: hsl(0 0% 100%); - --border: hsl(240 3.7% 15.9%); - color-scheme: dark; -} +// ❌ Unconstrained scroll (page becomes horizontally scrollable) +
+
-@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-border: var(--border); - --color-brand-500: var(--brand-500); - --radius-lg: var(--radius); - --radius-md: calc(var(--radius) - 2px); -} +// ❌ Flex parent without min-w-0 (page expansion) +
+
{/* MISSING min-w-0! */} +
``` -Then use generated utilities (e.g., `bg-brand-500`, `border-border`) or token references (`bg-[var(--accent)]`). +### Good Examples +```tsx +// ✅ Responsive grid +
---- +// ✅ Constrained horizontal scroll +
+
+
-## 7) UI Review Pre-Flight Checklist - -Before committing any UI component, verify ALL of these: - -### Tailwind v4 Rules -- [ ] **@custom-variant dark defined** (`@custom-variant dark (&:where(.dark, .dark *));` in index.css) -- [ ] **No dynamic class construction** (`bg-${color}-500`, template literals with variables) -- [ ] **CSS variables allowed in arbitrary values** (static utility names: `bg-[var(--accent)]`) -- [ ] **Static class lookup objects** for discrete variants (e.g., `const colorClasses = { cyan: "...", ... }`) -- [ ] **CSS variables pattern** for flexible/continuous values (colors, alphas, glows) -- [ ] **Conditional classes are complete strings** (use `cn()` with full class names) -- [ ] **Arbitrary values are static** (written as complete strings in source code) -- [ ] **Inline style ONLY for CSS variables** (`style={{ "--accent": token }}`), never direct visual CSS -- [ ] **Variables in `:root` and `.dark` with bare HSL values** (NO hsl() wrapper) -- [ ] **Variables mapped with `@theme inline`** -- [ ] **`@layer` and `@apply` used properly** (base styles and component classes) - -### Responsive Layout Rules -- [ ] **Desktop-primary with responsive adaptations** (optimize for desktop, add breakpoints for smaller screens) -- [ ] **Horizontal scroll containers have `w-full` parent** (CRITICAL - prevents page-width expansion) -- [ ] **Horizontal scroll containers use `scrollbar-hide`** (cleaner UI without visible scrollbar) -- [ ] **Flex parent containing scroll container has `min-w-0`** (CRITICAL - prevents page expansion) -- [ ] **Responsive grid columns** (`grid-cols-1 md:grid-cols-2 lg:grid-cols-3` etc.) -- [ ] **Flexbox adapts at breakpoints** (`flex-col md:flex-row`) -- [ ] **Text has truncation/wrapping** (`truncate`, `line-clamp-N`, or `break-words max-w-full`) -- [ ] **Fixed widths have `max-w-*` constraints** (no bare `w-[500px]`) -- [ ] **Add `min-w-0` to flex children** that contain text/images (allows shrinking) -- [ ] **Layout toggle buttons always visible** (not absolutely positioned off-screen) -- [ ] **Use `min-h-[100dvh]`** instead of rigid `h-screen` on mobile -- [ ] **Remove layout-level `overflow-hidden`** (clips popovers/toasts/dialogs) - -### Component Reusability Rules -- [ ] **Use Card primitive** for glassmorphism/edge-lit effects (no hardcoded patterns) -- [ ] **Use PillNavigation component** for tab navigation (no custom implementations) -- [ ] **Use Radix UI primitives** for all interactive elements (Select, Checkbox, RadioGroup, Dialog, etc.) -- [ ] **Import from styles.ts** for shared class objects when appropriate -- [ ] **No duplicated styling patterns** (DRY - create wrapper components if repeated 3+ times) - -### Radix Primitives Rules -- [ ] **Use `asChild` for custom triggers** (compose, don't wrap) -- [ ] **Follow documented structure** (Root/Trigger/Portal/Overlay/Content/Close) -- [ ] **Style via data attributes** (`[data-state="open"]`, `[data-disabled]`) -- [ ] **Use Portal and z-index scale** for overlays (no random `z-[9999]`) -- [ ] **Never remove focus rings or ARIA** attributes - -### Light/Dark Theme Rules -- [ ] **Every color/border/shadow has `dark:` variant** where visible -- [ ] **Structure identical between themes** (only colors/opacity/shadows change) -- [ ] **Use tokens for both themes** (`--color-bg` and `--color-bg-dark`) - -### Testing Requirements -- [ ] **Tested at 375px width** (mobile - iPhone SE) -- [ ] **Tested at 768px width** (tablet) -- [ ] **Tested at 1024px width** (laptop) -- [ ] **Tested at 1440px+ width** (desktop) -- [ ] **All UI controls accessible** at all sizes -- [ ] **No unintended horizontal page scroll** (only intentional container scroll) -- [ ] **Images scale responsively** -- [ ] **Modals/dialogs fit within viewport** -- [ ] **Touch targets minimum 44x44px** on mobile - -### Common Violations to Fix Immediately -- ❌ `bg-${color}-500` → ✅ `colorClasses[color]` -- ❌ `grid-cols-4` → ✅ `grid-cols-1 md:grid-cols-2 lg:grid-cols-4` (Symptom: page scrolls horizontally, controls off-screen) -- ❌ Hardcoded glassmorphism → ✅ `` -- ❌ `` from Radix -- ❌ Custom pill navigation → ✅ `` -- ❌ `overflow-x-auto` without `w-full` parent → ✅ Wrap in `
` -- ❌ Long text with no truncation → ✅ Add `truncate` or `line-clamp-N` -- ❌ Fixed `w-[500px]` → ✅ `w-full max-w-[500px]` -- ❌ Absolute positioned controls → ✅ Use flexbox layout -- ❌ No dark mode variant → ✅ Add `dark:` classes - ---- - -## 8) Automated Scanning Patterns - -Use these patterns to programmatically detect violations in `.tsx` files. - -### Critical Violations (Breaking Changes) - -**Missing @custom-variant dark (DARK MODE WON'T WORK)** -```bash -grep -n "@custom-variant dark" [path]/index.css +// ✅ Flex parent with scroll container +
+
{/* min-w-0 CRITICAL */} ``` -**Rule**: Section 2 - @custom-variant dark REQUIRED for Tailwind v4 -**Symptom**: dark: utilities don't apply, theme toggle appears to work but nothing changes visually -**Fix**: Add `@custom-variant dark (&:where(.dark, .dark *));` immediately after `@import "tailwindcss";` -**Dynamic Tailwind Class Construction (NO CSS GENERATED)** +### Automated Scans ```bash -grep -rn "className={\`.*\${.*}.*\`}" [path] --include="*.tsx" -grep -rn "bg-\${.*}\|text-\${.*}\|border-\${.*}\|shadow-\${.*}" [path] --include="*.tsx" -``` -**Rule**: Section 2 - NO dynamic class name construction -**Fix**: Use static class lookup objects (Pattern A from Section 2) +# Non-responsive grids +grep -rn "grid-cols-[2-9]" [path] --include="*.tsx" | grep -v "md:\|lg:\|xl:" -**Unconstrained Horizontal Scroll (BREAKS LAYOUT)** -```bash -# Find overflow-x-auto without w-full parent +# Unconstrained scroll grep -rn "overflow-x-auto" [path] --include="*.tsx" -# Then manually verify parent has w-full or max-w-* -``` -**Rule**: Section 3 - Constrain horizontal scroll containers -**Fix**: Wrap in `
` (Example in Section 3) +# Then manually verify parent has w-full -**Non-Responsive Grid Columns (BREAKS MOBILE)** -```bash -grep -rn "grid-cols-[2-9]" [path] --include="*.tsx" | grep -v "md:\|lg:\|sm:\|xl:" -``` -**Rule**: Section 3 - Responsive grid columns mandatory -**Symptom**: Page scrolls horizontally, UI controls (grid/list toggles, navigation buttons) shift off-screen and become inaccessible -**Fix**: Add responsive breakpoints: `grid-cols-1 md:grid-cols-2 lg:grid-cols-4` (Section 3) +# Missing text truncation +grep -rn "" [path] --include="*.tsx" -grep -rn "