Epgrade to Tailwind 4

This commit is contained in:
sean-eskerium
2025-10-09 09:31:47 -04:00
parent 70b6e70a95
commit 80992ca975
14 changed files with 1711 additions and 1910 deletions

View File

@@ -1,489 +0,0 @@
# Tailwind CSS & Responsive Design Best Practices
## Critical Tailwind CSS Rules
### Rule 1: NO Dynamic Class Name Construction
Tailwind processes your source code as **plain text at BUILD time**, NOT at runtime. It cannot understand variables, string concatenation, or template literals.
#### BROKEN Patterns (Will Not Work)
```tsx
// BROKEN - Tailwind won't generate these classes
const color = "cyan";
<div className={`bg-${color}-500`} /> // CSS NOT GENERATED
// BROKEN - Template literal concatenation
<div className={`text-${textColor}-700 border-${borderColor}-500`} />
// BROKEN - String interpolation
const glow = `shadow-[0_0_30px_rgba(${rgba},0.4)]`;
// BROKEN - Computed class names
className={isActive ? `bg-${activeColor}-500` : `bg-${inactiveColor}-500`}
```
**Why These Fail:**
- Tailwind scans code as text looking for class tokens
- `bg-cyan-500` exists as a token → CSS generated
- `bg-${color}-500` does NOT exist as a token → NO CSS generated
- At runtime, browser receives class name with no matching CSS
#### CORRECT Patterns (Use These)
```tsx
// CORRECT - Static class lookup object
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",
};
<div className={colorClasses[color]} />
// CORRECT - Conditional with complete class names
<div className={isActive ? "bg-cyan-500 text-white" : "bg-gray-500 text-gray-300"} />
// CORRECT - Use cn() helper with complete class strings
<div className={cn(
"base-classes",
isActive && "bg-cyan-500 text-white",
isPinned && "border-purple-500 shadow-lg"
)} />
// CORRECT - Static arbitrary values (scanned at build time)
<div className="shadow-[0_0_30px_rgba(34,211,238,0.4)]" />
// CORRECT - Pre-defined classes from styles.ts
import { glassCard } from '@/features/ui/primitives/styles';
className={cn(glassCard.base, glassCard.variants.cyan.glow)}
```
### Rule 2: Use Static Class Mappings
When you need variant styling based on props or state:
```tsx
// CORRECT Pattern
interface ButtonProps {
variant: "primary" | "secondary" | "danger";
}
const Button = ({ variant }: ButtonProps) => {
const variantClasses = {
primary: "bg-blue-500 hover:bg-blue-600 text-white",
secondary: "bg-gray-200 hover:bg-gray-300 text-gray-900",
danger: "bg-red-500 hover:bg-red-600 text-white",
};
return <button className={variantClasses[variant]}>Click me</button>;
};
```
### Rule 3: Arbitrary Values for One-Off Styles
When you need a specific value not in your theme:
```tsx
// CORRECT - Arbitrary values are scanned at build time
<div className="w-[347px]" />
<div className="shadow-[0_0_30px_rgba(34,211,238,0.4)]" />
<div className="bg-[#316ff6]" />
// These work because the complete string exists in source code
```
## Responsive Design Best Practices
### Rule 1: Mobile-First Approach
Always design for mobile first, then layer styles for larger screens.
```tsx
// CORRECT - Mobile first, then larger screens
<div className="w-full md:w-1/2 lg:w-1/3">
// WRONG - Desktop first (harder to override)
<div className="w-1/3 sm:w-full">
```
### Rule 2: Responsive Grid Columns
Never use fixed grid columns without responsive breakpoints.
```tsx
// WRONG - Fixed 4 columns breaks on mobile
<div className="grid grid-cols-4 gap-4">
// CORRECT - Responsive columns
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
```
### Rule 3: Constrain Horizontal Scroll Containers
**CRITICAL:** When creating horizontally scrollable content, you MUST constrain the parent container.
```tsx
// WRONG - Entire page becomes scrollable
<div className="overflow-x-auto">
<div className="flex gap-4 min-w-max">
{/* Wide cards */}
</div>
</div>
// CORRECT - Scroll isolated to specific container
<div className="w-full">
<div className="overflow-x-auto -mx-6 px-6">
<div className="flex gap-4 min-w-max">
{/* Only cards scroll, page stays within viewport */}
</div>
</div>
</div>
```
**Why This Matters:**
- Without `w-full` parent, `min-w-max` forces entire page width to expand
- UI controls shift off-screen
- Layout breaks at all viewport sizes
- User cannot access navigation or buttons
### Rule 4: Responsive Flexbox
Use flex-wrap and responsive flex directions:
```tsx
// WRONG - Overflows on small screens
<div className="flex gap-4">
<div className="w-64">Sidebar</div>
<div className="w-96">Content</div>
</div>
// CORRECT - Wraps on mobile, horizontal on desktop
<div className="flex flex-col md:flex-row gap-4">
<div className="w-full md:w-64">Sidebar</div>
<div className="flex-1">Content</div>
</div>
```
### Rule 5: Text Truncation & Overflow
Always handle long text content:
```tsx
// WRONG - Long titles break layout
<h3 className="font-medium">{veryLongTitle}</h3>
// CORRECT - Single line truncate
<h3 className="font-medium truncate">{veryLongTitle}</h3>
// CORRECT - Multi-line clamp
<h3 className="font-medium line-clamp-2">{veryLongTitle}</h3>
// CORRECT - Wrap with proper width constraint
<h3 className="font-medium break-words max-w-full">{veryLongTitle}</h3>
```
## Common Layout Anti-Patterns
### Anti-Pattern 1: Fixed Widths Without Constraints
```tsx
// WRONG - Fixed width can overflow viewport
<div className="w-96 p-8">
// CORRECT - Max width with full width fallback
<div className="w-full max-w-96 p-8">
```
### Anti-Pattern 2: Absolute Positioning for UI Controls
```tsx
// WRONG - Controls shift off-screen when content overflows
<div className="absolute top-8 right-8">
<Button>Toggle View</Button>
</div>
// CORRECT - Use flexbox layout
<div className="flex justify-end mb-4">
<Button>Toggle View</Button>
</div>
```
### Anti-Pattern 3: Non-Responsive Tables
```tsx
// WRONG - Table breaks on mobile
<table className="w-full">
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
</tr>
</thead>
</table>
// CORRECT - Wrap in overflow container
<div className="overflow-x-auto w-full">
<table className="w-full">
{/* Table can scroll horizontally on mobile */}
</table>
</div>
```
### Anti-Pattern 4: Padding on Scroll Container
```tsx
// WRONG - Padding prevents content from reaching edge
<div className="overflow-x-auto px-8 py-8">
<div className="flex gap-4 min-w-max">
// CORRECT - Use negative margin to compensate
<div className="w-full">
<div className="overflow-x-auto -mx-6 px-6">
<div className="flex gap-4 min-w-max">
```
### Anti-Pattern 5: Missing Width Constraints on Scrollable Parents
```tsx
// WRONG - Parent has no width, child forces expansion
<div>
<div className="overflow-x-auto">
<div className="flex gap-4 min-w-max">
{/* Forces parent to expand beyond viewport */}
</div>
</div>
</div>
// CORRECT - Explicit width constraint
<div className="w-full max-w-7xl mx-auto">
<div className="overflow-x-auto">
<div className="flex gap-4 min-w-max">
{/* Scrolls within constrained parent */}
</div>
</div>
</div>
```
## Radix UI Integration Best Practices
### Use Radix Primitives for All Interactive Elements
```tsx
// WRONG - Native HTML elements
<select>
<option value="1">Option 1</option>
</select>
<input type="checkbox" />
<input type="radio" />
// CORRECT - Radix primitives
import { Select, SelectTrigger, SelectContent, SelectItem } from '@/features/ui/primitives/select';
import { Checkbox } from '@/features/ui/primitives/checkbox';
import { RadioGroup, RadioGroupItem } from '@/features/ui/primitives/radio-group';
```
### Modal/Dialog Responsive Patterns
```tsx
// CORRECT - Responsive dialog sizes
<DialogContent className="w-full max-w-md md:max-w-2xl lg:max-w-4xl">
{/* Adapts to screen size */}
</DialogContent>
```
## Component Reusability Patterns
### Pattern 1: Card Primitives Over Hardcoded Styles
```tsx
// WRONG - Hardcoding glassmorphism and edge-lit effects
<div className="relative rounded-xl overflow-hidden">
<div className="absolute inset-x-0 top-0 h-[2px] bg-cyan-500" />
<div className="absolute inset-x-0 top-0 h-16 bg-gradient-to-b from-cyan-500/40 to-transparent blur-lg" />
<div className="backdrop-blur-md bg-white/10 border border-gray-200 p-4">
{children}
</div>
</div>
// CORRECT - Use Card primitive with props
<Card edgePosition="top" edgeColor="cyan" blur="lg" transparency="light">
{children}
</Card>
```
### Pattern 2: Static Class Objects from styles.ts
```tsx
// WRONG - Hardcoding repeated glass effects
<div className="backdrop-blur-md bg-white/10 border border-gray-200 rounded-lg">
// CORRECT - Use pre-defined classes
import { glassCard } from '@/features/ui/primitives/styles';
<div className={cn(glassCard.base, glassCard.blur.md, glassCard.transparency.light)}>
```
### Pattern 3: Shared Navigation Components
```tsx
// WRONG - Custom pill navigation each time
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border rounded-full p-1">
<div className="flex gap-1">
{items.map(item => (
<button className="px-6 py-2.5 rounded-full text-sm font-medium">
{item.label}
</button>
))}
</div>
</div>
// CORRECT - Use PillNavigation component
<PillNavigation
items={items}
activeSection={activeSection}
onSectionClick={setActiveSection}
colorVariant="orange"
size="small"
/>
```
## Testing Responsive Layouts
### Required Viewport Tests
Test every layout at these breakpoints:
- **375px** - iPhone SE (smallest modern phone)
- **768px** - Tablet portrait
- **1024px** - Tablet landscape / small laptop
- **1440px** - Desktop
- **1920px** - Large desktop
### Responsive Checklist
For every component:
- [ ] All UI controls visible at all breakpoints
- [ ] No horizontal page scroll (only intentional container scroll)
- [ ] Text truncates or wraps appropriately
- [ ] Images scale responsively
- [ ] Grid adapts to screen size
- [ ] Flexbox wraps or changes direction on mobile
- [ ] Modals/dialogs fit within viewport
- [ ] Touch targets are minimum 44x44px on mobile
## Common Mistakes Summary
### 1. Dynamic Tailwind Classes
**Problem:** String interpolation like `bg-${color}-500`
**Solution:** Static class object lookup
### 2. Unconstrained Scroll Containers
**Problem:** `min-w-max` without `w-full` parent
**Solution:** Always constrain parent width
### 3. Fixed Grid Columns
**Problem:** `grid-cols-4` on all screen sizes
**Solution:** `grid-cols-1 md:grid-cols-2 lg:grid-cols-4`
### 4. Missing Text Truncation
**Problem:** Long text breaks layout
**Solution:** Add `truncate` or `line-clamp-*`
### 5. Hardcoded Component Styles
**Problem:** Duplicating glassmorphism/edge-lit patterns
**Solution:** Use Card/DataCard primitives
### 6. Absolute Positioned Controls
**Problem:** Fixed position elements shift off-screen
**Solution:** Use flexbox for layout positioning
### 7. Native HTML Form Elements
**Problem:** Using `<select>`, `<input type="checkbox">`, etc.
**Solution:** Use Radix UI primitives
## Archon-Specific Patterns
### Overflow Scroll Pattern
```tsx
// Standard pattern for horizontally scrollable cards
<div className="w-full">
<div className="overflow-x-auto -mx-6 px-6 py-8">
<div className="flex gap-4 min-w-max">
{items.map(item => <Card key={item.id} />)}
</div>
</div>
</div>
```
### Responsive Grid Pattern
```tsx
// Standard grid for card layouts
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{items.map(item => <Card key={item.id} />)}
</div>
```
### Responsive Flex Layout
```tsx
// Sidebar + main content pattern
<div className="flex flex-col lg:flex-row gap-6">
<aside className="w-full lg:w-64 flex-shrink-0">
{/* Sidebar */}
</aside>
<main className="flex-1 min-w-0">
{/* Main content - min-w-0 allows truncation */}
</main>
</div>
```
### Card Primitive Usage
```tsx
// Use Card primitive props instead of hardcoding
<Card
blur="lg" // sm, md, lg, xl, 2xl, 3xl
transparency="light" // clear, light, medium, frosted, solid
edgePosition="top" // none, top, left, right, bottom
edgeColor="cyan" // purple, blue, cyan, green, orange, pink, red
glowColor="cyan" // For glow effects
glowType="outer" // inner or outer
>
{children}
</Card>
```
## Pre-Flight Checklist
Before committing any UI component:
### Tailwind Rules
- [ ] No dynamic class name construction (`bg-${var}-500`)
- [ ] No template literal class building
- [ ] All variant classes defined in static objects
- [ ] Arbitrary values written as complete static strings
### Responsive Rules
- [ ] Horizontal scroll containers have `w-full` parent wrapper
- [ ] Grids use responsive column breakpoints
- [ ] Flex layouts adapt at appropriate breakpoints
- [ ] Text content has truncation or wrapping
- [ ] Fixed widths have `max-w-*` constraints
- [ ] Layout toggle buttons always visible
### Component Rules
- [ ] Using Card primitive for glassmorphism
- [ ] Using Radix primitives for all interactive elements
- [ ] Using shared components (PillNavigation, etc.)
- [ ] No duplicated styling patterns
### Testing
- [ ] Tested at 375px width (mobile)
- [ ] 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

View File

@@ -0,0 +1,935 @@
# UI_STANDARDS.md
**Audience**: AI agents performing automated UI audits and refactors.
**Scope**: Radix Primitives + Tailwind CSS (v4) + Responsive (Flexbox-first) with **Light** and **Dark** themes.
**How to use**: Treat **Rules** as MUST, **AntiPatterns** as MUST NOT. Prefer the **Good Examples** verbatim unless there's a documented exception.
---
## 0) Projectwide Conventions (applies to all sections)
**Rules**
- Use **no inline styles** (`style=""`) except when setting CSS variables (e.g., `style={{ "--accent": token }}`). Everything else must use Tailwind utilities or predeclared component classes.
- Use a **single source of truth** for design tokens via Tailwind v4 `@theme` variables (colors, spacing, radii, shadows, breakpoints). Never invent adhoc hex values or pixel sizes.
- Keep markup **semantic and accessible**. Preserve focus outlines and keyboard navigation.
- Prefer **wrapper components** (e.g., `<Button variant="primary" />`) that centralize class lists over duplicating long utility strings across the app.
- Maintain a **zindex 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.
**AntiPatterns**
- 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 (`<select>`, `<input type="checkbox">`, `<input type="radio">`) - use Radix primitives.
- Hardcoding glassmorphism, edge-lit effects, or pill navigation - use shared primitives.
---
## 1) Radix Primitives
**Rules**
- **Compose, dont 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 zindex scale for overlays/popovers; avoid random `z-[9999]`.
- **A11y**: never remove focus rings or ARIA attributes that primitives rely on.
**AntiPatterns**
- Deep styling of internal nodes (e.g., selecting Radixs internal class names or structure).
- Manually toggling open/close state without Radix APIs.
- Mixing multiple portaled surfaces with arbitrary zindices (dialogs behind popovers, etc.).
**Good Examples**
```tsx
// Compose a Trigger with your Button using asChild
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="primary">Open</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/40 backdrop-blur-sm z-40" />
<Dialog.Content
className="fixed left-1/2 top-1/2 z-50 w-[min(90vw,38rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg
bg-surface/95 dark:bg-surface-dark/95 shadow-xl p-6 focus:outline-none">
<Dialog.Title className="text-lg font-semibold">Title</Dialog.Title>
<Dialog.Description className="mt-1 text-muted-foreground">
Description
</Dialog.Description>
<div className="mt-6 flex justify-end gap-2">
<Dialog.Close asChild><Button variant="ghost">Cancel</Button></Dialog.Close>
<Button variant="primary">Confirm</Button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
```
```css
/* State-driven styling via data attributes */
.popover-content[data-state="open"] { /* animations, borders, etc. */ }
```
---
## 2) Tailwind CSS (v4)
**Rules**
- **Define tokens properly**: Variables in `:root` and `.dark`, then map to Tailwind with `@theme inline`.
```css
@import "tailwindcss";
:root {
--background: hsl(0 0% 98%);
--foreground: hsl(240 10% 3.9%);
--border: hsl(240 5.9% 90%);
--radius: 0.5rem;
color-scheme: light;
}
.dark {
--background: hsl(0 0% 0%);
--foreground: hsl(0 0% 100%);
--border: hsl(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 `<html>`).
- **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.
**AntiPatterns**
- **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 adhoc 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",
};
<div className={colorClasses[color]} />
// ✅ CORRECT - Conditional with complete class names
<div className={isActive ? "bg-cyan-500 text-white" : "bg-gray-500 text-gray-300"} />
// ✅ CORRECT - Use cn() helper with complete class strings
<div className={cn(
"base-classes",
isActive && "bg-cyan-500 text-white",
isPinned && "border-purple-500 shadow-lg"
)} />
```
### 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); }
<div className="bg-[var(--accent)] border-[color:var(--accent)]" />
// ✅ CORRECT - Set variable via inline style, use in static utilities
<div
className="bg-[var(--glass-bg)] border-[color:var(--accent)] shadow-[0_0_24px_color-mix(in_oklab,var(--accent)_35%,transparent)]"
style={{ "--accent": "oklch(0.75 0.12 210)" } as React.CSSProperties}
/>
// ✅ 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;
<div className={cn("bg-[var(--accent)]", accentClass[color])} />
```
### Anti-Patterns
```tsx
// ❌ BROKEN - Dynamic class construction (CSS NOT GENERATED)
const color = "cyan";
<div className={`bg-${color}-500`} /> // NO CSS OUTPUT
<div className={`text-${textColor}-700 border-${borderColor}-500`} /> // NO CSS OUTPUT
// ❌ BROKEN - Inline style for visual CSS (use variables instead)
<div style={{ backgroundColor: "#12abef" }} /> // Should be className or CSS variable
// ✅ CORRECT alternative - Use CSS variable
<div className="bg-[var(--accent)]" style={{ "--accent": "#12abef" } as React.CSSProperties} />
```
```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 <button className={[base, variants[variant], className].join(' ')} {...props} />;
}
```
```css
/* Local component CSS using tokens (no arbitrary values) */
.card {
border-radius: var(--radius-md);
box-shadow: var(--shadow-card);
background: color-mix(in oklab, var(--color-bg) 92%, transparent);
}
.dark .card {
background: color-mix(in oklab, var(--color-bg-dark) 92%, transparent);
}
```
---
## 3) Responsive Layout (Flexboxfirst)
**Rules**
- **Desktop-primary with responsive adaptations**: Optimize for desktop by default and add breakpoint rules (`md:`, `lg:`, `xl:`) for tablets/phones.
- Prefer **fluid sizing**: `w-full`, percentages, or flex fractions; constrain with `max-w-*` where needed.
- Use **Flexbox** for 1D layouts; enable `flex-wrap` or responsive `flex-col md:flex-row` when layouts must adapt.
- Use **`min-w-0` (or `min-h-0` for column layouts)** on flex children that contain text/images to allow proper shrinking without overflow.
- **CRITICAL: Constrain horizontal scroll containers**. When using `overflow-x-auto`, parent MUST have `w-full` or `max-w-*` to prevent forcing entire page width expansion.
- Use **`overflow-x-auto`** only on intended horizontal scroll regions; give scroll items `shrink-0`.
- **Hide scrollbars on horizontal scroll**: Add `scrollbar-hide` class to `overflow-x-auto` containers for cleaner UI (scrollbar-hide utility defined in index.css).
- **Responsive grid columns**: NEVER use fixed grid columns (e.g., `grid-cols-4`) without responsive breakpoints. Use `grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4`.
- **Text truncation**: Always handle long text with `truncate` (single line), `line-clamp-N` (multi-line), or `break-words max-w-full` (wrap).
- For fullheight sections, prefer **`min-h-[100dvh]`** (or allow page scroll) instead of rigid `h-screen` on mobile.
- Keep **spacing with gaps** (`gap-*`) rather than adhoc margins between siblings.
- **Breakpoint reference**: `sm`=640px, `md`=768px, `lg`=1024px, `xl`=1280px, `2xl`=1536px.
- **Test at**: 375px (mobile), 768px (tablet), 1024px (laptop), 1440px+ (desktop).
**AntiPatterns**
- Fixed pixel widths on layout containers (e.g., `w-[500px]`) without `max-w-*` constraint.
- Blanket `overflow-hidden` on large containers/pages (clips popovers, toasts, dialogs).
- **Unconstrained scroll containers**: Using `min-w-max` inside `overflow-x-auto` without parent `w-full` - forces entire page to expand horizontally.
- **CRITICAL: Flex parent without min-w-0 containing scroll container**. **SYMPTOM**: Scroll container forces ENTIRE PAGE to expand horizontally, UI controls (toggles, buttons) shift off-screen. The flex child refuses to shrink below its content's natural width, causing page-wide horizontal scroll.
- **Fixed grid columns**: `grid-cols-4` on all screen sizes breaks mobile. **SYMPTOM**: Page scrolls horizontally, UI controls (grid/list toggles, buttons) shift off-screen and become inaccessible.
- **Missing text truncation**: Long text that breaks layout without `truncate` or `line-clamp-*`.
- **Absolute positioning for UI controls**: Fixed position elements shift off-screen when content overflows.
- 100vh "traps" on mobile that hide content behind browser UI; avoid locking scroll inside viewheight panels.
- Mixing contradictory sizing on a single element (e.g., `flex-1` with `w-64`).
**Good Examples**
```tsx
// ✅ CORRECT - Responsive two-column layout
<div className="mx-auto max-w-6xl px-4">
<div className="flex flex-col md:flex-row gap-6">
<aside className="w-full md:w-64 shrink-0">sidebar</aside>
<main className="flex-1 min-w-0">content that can shrink without overflow</main>
</div>
</div>
// ✅ CORRECT - Responsive grid with proper breakpoints
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{items.map(item => <Card key={item.id} />)}
</div>
// ✅ CORRECT - Constrained horizontal scroll (Archon pattern)
<div className="w-full">
<div className="overflow-x-auto -mx-6 px-6 py-8 scrollbar-hide">
<div className="flex gap-4 min-w-max">
{items.map(item => <Card key={item.id} className="w-72 shrink-0" />)}
</div>
</div>
</div>
// ✅ CORRECT - Flex parent containing scroll container MUST have min-w-0
<div className="flex gap-6">
<aside className="w-64 shrink-0">Sidebar</aside>
<main className="flex-1 min-w-0"> {/* min-w-0 is CRITICAL here */}
{/* This content includes horizontal scroll containers */}
<div className="w-full">
<div className="overflow-x-auto scrollbar-hide">
<div className="flex gap-4 min-w-max">
{items.map(item => <Card key={item.id} className="w-72 shrink-0" />)}
</div>
</div>
</div>
</main>
</div>
// ✅ CORRECT - Text truncation patterns
<h3 className="font-medium truncate">{veryLongTitle}</h3> {/* Single line */}
<h3 className="font-medium line-clamp-2">{description}</h3> {/* Multi-line */}
<p className="break-words max-w-full">{wrappableContent}</p> {/* Wrap */}
// ✅ CORRECT - Responsive flexbox with wrapping
<div className="flex flex-col md:flex-row gap-4 flex-wrap">
<div className="w-full md:w-64">Sidebar</div>
<div className="flex-1 min-w-0">Content</div>
</div>
// ❌ BROKEN - Unconstrained scroll (entire page becomes horizontally scrollable)
<div className="overflow-x-auto">
<div className="flex gap-4 min-w-max">
{/* Page width forced beyond viewport */}
</div>
</div>
// ❌ CRITICAL BROKEN - Flex parent without min-w-0 containing scroll container
// SYMPTOM: Scroll container forces ENTIRE PAGE to expand, UI controls shift off-screen
<div className="flex gap-6">
<aside className="w-64 shrink-0">Sidebar</aside>
<main className="flex-1 max-w-6xl"> {/* MISSING min-w-0! */}
<div className="w-full">
<div className="overflow-x-auto">
<div className="flex gap-4 min-w-max">
<Card className="w-72 shrink-0" /> {/* Forces flex parent to expand */}
</div>
</div>
</div>
{/* Grid/list toggle buttons are now OFF-SCREEN → */}
</main>
</div>
// ❌ BROKEN - Fixed grid columns (breaks mobile, forces page-width expansion)
// SYMPTOM: UI controls (toggles, buttons) shift off-screen, page scrolls horizontally
<div className="grid grid-cols-4 gap-4">
<Card className="w-72" /> {/* Fixed width × 4 cols = 1152px minimum */}
{/* User cannot access grid/list toggle buttons - they're off-screen */}
</div>
// ❌ ALSO BROKEN - Responsive grid BUT fixed-width children
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card className="w-72" /> {/* w-72 prevents cards from adapting to grid cell */}
{/* On md breakpoint: 2 × 288px = 576px + gaps → can still overflow */}
</div>
// ❌ BROKEN - No text truncation
<h3 className="font-medium">{veryLongTitle}</h3> {/* Overflows layout */}
```
```css
/* Full-height panel that still works on mobile */
.section {
min-height: 100dvh; /* allows for dynamic mobile toolbars */
}
```
---
## 4) Light/Dark Themes
**Rules**
- Implement dark mode with the **`dark:` variant** and an `.dark` class on `<html>`.
- Keep structure identical; only colors/opacity/shadows change between themes.
- Use tokens for both themes (e.g., `--color-bg` and `--color-bg-dark`) and switch via `.dark`.
**AntiPatterns**
- Hardcoding light colors without a dark counterpart.
- Completely different component visuals between themes (beyond color/contrast).
- Using global filters/invert to “fake” dark mode.
**Good Example**
```html
<article class="bg-[var(--color-bg)] text-[var(--color-fg)]
dark:bg-[var(--color-bg-dark)] dark:text-[var(--color-fg-dark)]">
</article>
```
---
## 5) Component Reusability & Shared Primitives
**Rules**
- **Use Card primitives** from `@/features/ui/primitives/` for all glassmorphism, edge-lit, and card effects - NEVER hardcode these patterns.
- **Use PillNavigation component** for tab navigation - NEVER create custom pill navigation.
- **Use Radix UI primitives** for all interactive elements (Select, Checkbox, RadioGroup, Dialog, Popover, etc.).
- **Import from styles.ts** when you need pre-defined class objects - this is the design system's foundation.
- **Centralize repeated patterns** - if you find yourself copying the same class strings 3+ times, create a wrapper component.
### The styles.ts Design System
**Location**: `archon-ui-main/src/features/ui/primitives/styles.ts`
This file is the **single source of truth** for Archon's Tron-inspired glassmorphism design system. It provides pre-built static class strings that ensure consistency across the entire application.
**What styles.ts Provides:**
1. **`glassmorphism`** - Core glass effect utilities
- `background.*` - Transparency levels (subtle, strong, card, colored)
- `border.*` - Border styles (default, colored, focus, hover)
- `interactive.*` - Interaction states (base, hover, active, selected, disabled)
- `animation.*` - Animation presets (fadeIn, slideIn, etc.)
- `shadow.*` - Shadow effects including neon glows
- `priority.*` - Task priority colors (critical, high, medium, low)
2. **`glassCard`** - Complete card system
- `base` - Base card structure
- `blur.*` - Blur intensity levels (none, sm, md, lg, xl, 2xl, 3xl)
- `transparency.*` - Glass opacity levels (clear, light, medium, frosted, solid)
- `tints.*` - Colored glass tints (purple, blue, cyan, green, orange, pink, red)
- `variants.*` - Pre-built color variants with border + glow
- `edgeLit.*` - Edge-lit effects (position + color + glow)
- `sizes.*` - Padding variants (none, sm, md, lg, xl)
- `outerGlowSizes.*` - Outer glow by size and color
- `innerGlowSizes.*` - Inner glow by size and color
3. **`compoundStyles`** - Common composition patterns
- `interactiveElement` - Standard button/menu item styling
- `floatingPanel` - Dropdown/popover/tooltip styling
- `formControl` - Input/select styling
- `card` - Basic card styling
4. **`cn()`** - Utility function to combine class strings
**When to Use styles.ts:**
**ALWAYS use for:**
- Glassmorphism backgrounds (`glassmorphism.background.*`)
- Card variants (`glassCard.variants.*`)
- Edge-lit effects (`glassCard.edgeLit.*`)
- Glow effects (`glassCard.outerGlowSizes.*`, `glassCard.innerGlowSizes.*`)
- Interactive states (`glassmorphism.interactive.*`)
- Animations (`glassmorphism.animation.*`)
**DON'T use for:**
- Layout (use Tailwind utilities: `flex`, `grid`, `w-full`, etc.)
- Spacing (use Tailwind: `p-4`, `m-2`, `gap-4`, etc.)
- Typography (use Tailwind: `text-lg`, `font-medium`, etc.)
- One-off custom styling (use inline Tailwind classes)
**How to Use:**
```tsx
import { glassmorphism, glassCard, cn } from '@/features/ui/primitives/styles';
// Example 1: Using glassmorphism utilities
<div className={cn(
glassmorphism.background.strong,
glassmorphism.border.default,
glassmorphism.shadow.lg,
"p-6 rounded-lg" // Layout/spacing from Tailwind
)}>
Content
</div>
// Example 2: Using glassCard with variants
<div className={cn(
glassCard.base,
glassCard.blur.lg,
glassCard.transparency.light,
glassCard.variants.cyan.border,
glassCard.variants.cyan.glow,
"p-6" // Padding from Tailwind
)}>
Card content
</div>
// Example 3: Using edge-lit effects
<div className={cn(
glassCard.base,
glassCard.blur.xl,
glassCard.transparency.medium,
glassCard.edgeLit.position.top,
glassCard.edgeLit.color.purple.line,
glassCard.edgeLit.color.purple.glow,
"p-8"
)}>
Edge-lit card
</div>
```
**Why styles.ts Matters:**
1. **Consistency** - Same glass effects across all components
2. **Maintainability** - Change design system in one place
3. **Performance** - Static class strings, no runtime computation
4. **Type Safety** - TypeScript autocomplete for all variants
5. **Design Tokens** - All values reference @theme tokens
6. **DRY Principle** - No duplication of complex class strings
**AntiPatterns**
- Hardcoding glassmorphism effects: `backdrop-blur-md bg-white/10 border border-gray-200`.
- Creating custom pill navigation each time instead of using `<PillNavigation />`.
- Using native HTML form elements (`<select>`, `<input type="checkbox">`, `<input type="radio">`) instead of Radix primitives.
- Duplicating edge-lit glow effects across multiple components.
**Good Examples**
```tsx
// ✅ CORRECT - Use Card primitive with props
import { Card } from '@/features/ui/primitives/card';
<Card
blur="lg" // sm, md, lg, xl, 2xl, 3xl
transparency="light" // clear, light, medium, frosted, solid
edgePosition="top" // none, top, left, right, bottom
edgeColor="cyan" // purple, blue, cyan, green, orange, pink, red
glowColor="cyan" // For glow effects
glowType="outer" // inner or outer
>
{children}
</Card>
// ✅ CORRECT - Use PillNavigation component
import { PillNavigation } from '@/features/ui/primitives/pill-navigation';
<PillNavigation
items={[
{ id: 'overview', label: 'Overview' },
{ id: 'tasks', label: 'Tasks' },
]}
activeSection={activeSection}
onSectionClick={setActiveSection}
colorVariant="orange"
size="small"
/>
// ✅ CORRECT - Use Radix primitives
import { Select, SelectTrigger, SelectContent, SelectItem } from '@/features/ui/primitives/select';
import { Checkbox } from '@/features/ui/primitives/checkbox';
import { RadioGroup, RadioGroupItem } from '@/features/ui/primitives/radio-group';
<Select value={value} onValueChange={setValue}>
<SelectTrigger>Choose option</SelectTrigger>
<SelectContent>
<SelectItem value="1">Option 1</SelectItem>
</SelectContent>
</Select>
// ✅ CORRECT - Use styles.ts for shared class objects
import { glassCard } from '@/features/ui/primitives/styles';
<div className={cn(glassCard.base, glassCard.blur.md, glassCard.transparency.light)}>
{children}
</div>
// ❌ BROKEN - Hardcoding glassmorphism
<div className="backdrop-blur-md bg-white/10 border border-gray-200 rounded-lg">
{children}
</div>
// ❌ BROKEN - Custom pill navigation
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border rounded-full p-1">
<div className="flex gap-1">
{items.map(item => (
<button className="px-6 py-2.5 rounded-full text-sm font-medium">
{item.label}
</button>
))}
</div>
</div>
// ❌ BROKEN - Native HTML elements
<select>
<option value="1">Option 1</option>
</select>
```
---
## 6) Tailwind Tokens Quickstart (promote values to tokens)
```css
@import "tailwindcss";
: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;
}
.dark {
--background: hsl(0 0% 0%);
--foreground: hsl(0 0% 100%);
--border: hsl(240 3.7% 15.9%);
color-scheme: dark;
}
@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);
}
```
Then use generated utilities (e.g., `bg-brand-500`, `border-border`) or token references (`bg-[var(--accent)]`).
---
## 7) UI Review Pre-Flight Checklist
Before committing any UI component, verify ALL of these:
### Tailwind v4 Rules
- [ ] **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`**, 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 → ✅ `<Card blur="lg" transparency="light">`
-`<select>` → ✅ `<Select>` from Radix
- ❌ Custom pill navigation → ✅ `<PillNavigation>`
-`overflow-x-auto` without `w-full` parent → ✅ Wrap in `<div className="w-full">`
- ❌ 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)
**Dynamic Tailwind Class Construction (NO CSS GENERATED)**
```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)
**Unconstrained Horizontal Scroll (BREAKS LAYOUT)**
```bash
# Find overflow-x-auto without w-full parent
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 `<div className="w-full">` (Example in Section 3)
**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)
**Min-w-max Without Constraint**
```bash
grep -rn "min-w-max" [path] --include="*.tsx"
```
**Rule**: Section 3 - min-w-max must have constrained parent
**Fix**: Ensure parent has `w-full` or `max-w-*`
### High Priority Violations
**Native HTML Form Elements (Should Use Radix)**
```bash
grep -rn "<select>" [path] --include="*.tsx"
grep -rn "<option>" [path] --include="*.tsx"
grep -rn "type=\"checkbox\"" [path] --include="*.tsx"
grep -rn "type=\"radio\"" [path] --include="*.tsx"
```
**Rule**: Section 0, Section 1 - Use Radix UI primitives
**Fix**: Import from `@/features/ui/primitives/` (Section 5)
**Hardcoded Glassmorphism (Should Use Card Primitive)**
```bash
grep -rn "backdrop-blur.*bg-white/.*border.*rounded" [path] --include="*.tsx"
grep -rn "absolute inset-x-0 top-0.*bg-gradient-to-b" [path] --include="*.tsx"
```
**Rule**: Section 5 - Use Card primitive for glassmorphism
**Fix**: `<Card blur="lg" transparency="light">` (Section 5)
**Hardcoded Pill Navigation (Should Use Component)**
```bash
grep -rn "rounded-full.*flex gap-" [path] --include="*.tsx"
grep -rn "backdrop-blur.*rounded-full.*px-6 py-2" [path] --include="*.tsx"
```
**Rule**: Section 5 - Use PillNavigation component
**Fix**: `<PillNavigation items={...} />` (Section 5)
**Missing Text Truncation**
```bash
grep -rn "<h[1-6].*className" [path] --include="*.tsx" | grep -v "truncate\|line-clamp"
```
**Rule**: Section 3 - Always handle long text
**Fix**: Add `truncate` or `line-clamp-N` (Section 3)
**Fixed Widths Without Constraints**
```bash
grep -rn "w-\[0-9\]" [path] --include="*.tsx" | grep -v "max-w-"
grep -rn "w-96\|w-80\|w-72\|w-64" [path] --include="*.tsx" | grep -v "max-w-"
```
**Rule**: Section 3 - Fixed widths need max-w-* constraints
**Fix**: Change to `w-full max-w-[value]`
**Inline Style for Visual CSS (Not Variables)**
```bash
grep -rn "style={{.*backgroundColor\|color:\|padding:\|margin:" [path] --include="*.tsx"
```
**Rule**: Section 0, Section 2 - Inline style ONLY for CSS variables
**Fix**: Use className or CSS variables (Section 2 Pattern B)
**Missing Dark Mode Variants**
```bash
# Find colors without dark: variant (manual review needed)
grep -rn "bg-.*-[0-9]" [path] --include="*.tsx" | grep -v "dark:"
```
**Rule**: Section 4 - Every visible color needs dark: variant
**Fix**: Add `dark:` classes (Section 4)
### Medium Priority Violations
**Not Using Shared Primitives**
```bash
# Manually check imports - should import from @/features/ui/primitives/
grep -rn "import.*from.*primitives" [path] --include="*.tsx" -L
```
**Rule**: Section 0, Section 5 - Use shared component primitives
**Fix**: Import and use Card, PillNavigation, Radix primitives
**Missing min-w-0 on Flex Children (CRITICAL for scroll containers)**
```bash
# Find flex-1 without min-w-0 (manual review for children containing scroll containers)
grep -rn "flex-1" [path] --include="*.tsx" | grep -v "min-w-0"
```
**Rule**: Section 3 - Flex parent containing scroll container MUST have min-w-0
**Symptom**: Scroll container forces ENTIRE PAGE to expand, UI controls shift off-screen
**Fix**: Add `min-w-0` to flex parent: `className="flex-1 min-w-0"` (Section 3)
### Scoring Guide
For each component, calculate violation score:
- **0 critical violations**: 9-10/10
- **1 critical violation**: 7-8/10
- **2-3 critical violations**: 5-6/10
- **4+ critical violations**: 1-4/10
Adjust based on high/medium priority violations.
---
## 9) Quick Reference
### Archon-Specific Component Patterns
```tsx
// Horizontal Scroll Pattern (constrained, hidden scrollbar)
<div className="w-full">
<div className="overflow-x-auto -mx-6 px-6 py-8 scrollbar-hide">
<div className="flex gap-4 min-w-max">
{items.map(item => <Card key={item.id} className="w-72 shrink-0" />)}
</div>
</div>
</div>
// Note: scrollbar-hide utility defined in index.css:
// .scrollbar-hide {
// -ms-overflow-style: none;
// scrollbar-width: none;
// }
// .scrollbar-hide::-webkit-scrollbar {
// display: none;
// }
// Responsive Grid Pattern
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{items.map(item => <Card key={item.id} />)}
</div>
// Sidebar + Main Content Pattern
<div className="flex flex-col lg:flex-row gap-6">
<aside className="w-full lg:w-64 flex-shrink-0">
{/* Sidebar content */}
</aside>
<main className="flex-1 min-w-0">
{/* Main content - min-w-0 allows truncation */}
</main>
</div>
// Card Primitive Usage
<Card
blur="lg"
transparency="light"
edgePosition="top"
edgeColor="cyan"
glowColor="cyan"
glowType="outer"
>
{children}
</Card>
// Radix Dialog Pattern
<Dialog.Root>
<Dialog.Trigger asChild>
<Button variant="primary">Open</Button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/40 backdrop-blur-sm z-40" />
<Dialog.Content className="fixed left-1/2 top-1/2 z-50 w-full max-w-md md:max-w-2xl
-translate-x-1/2 -translate-y-1/2 rounded-lg
bg-surface/95 dark:bg-surface-dark/95 shadow-xl p-6">
<Dialog.Title>Title</Dialog.Title>
<Dialog.Description>Description</Dialog.Description>
{/* Content */}
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
```
### Color Variant Patterns
**Pattern A: Lookup Map (discrete variants)**
```tsx
// Static class object for finite set of brand colors
const colorClasses = {
cyan: "bg-cyan-500 text-cyan-900 border-cyan-500/50 shadow-cyan-500/20",
purple: "bg-purple-500 text-purple-900 border-purple-500/50 shadow-purple-500/20",
blue: "bg-blue-500 text-blue-900 border-blue-500/50 shadow-blue-500/20",
orange: "bg-orange-500 text-orange-900 border-orange-500/50 shadow-orange-500/20",
};
<div className={cn("base-styles", colorClasses[colorVariant])} />
```
**Pattern B: CSS Variables (flexible values)**
```css
/* In index.css */
:root {
--cyan-500: oklch(0.75 0.12 210);
--orange-500: oklch(0.75 0.16 70);
--accent: var(--cyan-500);
}
@theme inline {
--color-cyan-500: var(--cyan-500);
--color-orange-500: var(--orange-500);
}
/* Named accent classes */
.accent-cyan { --accent: var(--cyan-500); }
.accent-orange { --accent: var(--orange-500); }
```
```tsx
// Component using CSS variables
const accentClass = {
cyan: "accent-cyan",
orange: "accent-orange",
} as const;
export function GlassCard({ color = "cyan", customAccent }: Props) {
return (
<div
className={cn(
"rounded-lg backdrop-blur-md",
"bg-[var(--glass-bg)] border-[color:var(--accent)]",
"shadow-[0_0_24px_color-mix(in_oklab,var(--accent)_35%,transparent)]",
accentClass[color]
)}
// Optional: set custom accent dynamically via variable
style={customAccent ? ({ "--accent": customAccent } as React.CSSProperties) : undefined}
/>
);
}
```
### Breakpoint Reference
- **sm**: 640px (small tablets)
- **md**: 768px (tablets)
- **lg**: 1024px (laptops)
- **xl**: 1280px (desktops)
- **2xl**: 1536px (large desktops)
### Critical Reminders
1. **NO dynamic class construction** - `bg-${color}-500` will NOT generate CSS
2. **CSS variables ARE allowed** - `bg-[var(--accent)]` works (utility name is static)
3. **Inline style ONLY for CSS variables** - `style={{ "--accent": token }}`, never direct visual CSS
4. **Proper @theme pattern** - Variables in `:root`/`.dark`, map with `@theme inline`
5. **`@layer` and `@apply` still work** - Valid in v4 for base/component styles
6. **Constrain scroll containers** - `overflow-x-auto` parent needs `w-full`
7. **Responsive grids always** - Never fixed `grid-cols-4` without breakpoints
8. **Use primitives** - Card, PillNavigation, Radix UI components
9. **Desktop-primary** - Optimize for desktop, add responsive breakpoints for smaller screens
10. **Text truncation** - Always handle long text explicitly
11. **Dark mode** - Every visible color needs `dark:` variant