--- name: design-system description: StepLeague design system, theme configuration, CSS variables, badge styling, and UI patterns. Use when working with colors, themes, styling, badges, buttons, cards, or any visual UI changes. Keywords: CSS, theme, colors, dark mode, light mode, variables, styling, UI, design. compatibility: Antigravity, Claude Code, Cursor metadata: version: "1.1" project: "stepleague" --- # Design System Skill ## Core Rule **NEVER use hardcoded Tailwind colors. ALWAYS use semantic CSS variables.** ```tsx // ❌ WRONG
// ✅ CORRECT
``` --- ## Full Documentation **MUST READ:** [docs/THEME_SYSTEM.md](file:///docs/THEME_SYSTEM.md) This skill is a quick reference. The full theme documentation contains: - Complete CSS variable reference - Light/dark mode implementation - Accessibility requirements - Common pitfalls and solutions --- ## Quick Reference: CSS Variables ### Backgrounds | Variable | Usage | |----------|-------| | `bg-background` | Page background | | `bg-card` | Card/container background | | `bg-muted` | Muted/subtle background | | `bg-primary` | Primary action background | | `bg-secondary` | Secondary background | ### Text | Variable | Usage | |----------|-------| | `text-foreground` | Primary text | | `text-muted-foreground` | Secondary/muted text | | `text-primary` | Emphasised text (brand color) | ### Status Colors ```tsx // Use HSL syntax for status colors className="text-[hsl(var(--success))]" // Green className="text-[hsl(var(--warning))]" // Amber className="text-[hsl(var(--info))]" // Blue className="text-destructive" // Red ``` ### With Opacity ```tsx className="bg-primary/90" // 90% opacity className="bg-[hsl(var(--success)/0.1)]" // 10% opacity ``` --- ## Badge Components ### Two Distinct Systems | Component | Import | Use Case | |-----------|--------|----------| | `Badge` | `@/components/ui/badge` | General UI (variant prop) | | `SystemBadge` | `@/components/ui/SystemBadge` | Category-based (Kanban, Roadmap) | ### shadcn Badge ```tsx import { Badge } from "@/components/ui/badge"; Default Secondary Error Outline ``` ### SystemBadge ```tsx import { SystemBadge } from "@/components/ui/SystemBadge"; ``` ### Central Badge Configuration All badge colors are in `src/lib/badges.ts`: ```typescript import { getBadgeClass, getBadgeConfig } from '@/lib/badges'; const statusClass = getBadgeClass('status', 'verified'); const badgeConfig = getBadgeConfig('type', 'bug'); ``` --- ## Critical Patterns ### Text Truncation **CRITICAL:** `truncate` won't work without `min-w-0`! ```tsx // ✅ CORRECT
// Prevent icon shrinking
// Enable truncation
Long text...
// ❌ WRONG - truncate won't work
Long text...
``` ### Hover States ```tsx className="hover:bg-secondary" className="hover:border-primary/50" className="hover:bg-[hsl(var(--success)/0.1)]" ``` ### Focus States ```tsx className="focus:outline-none focus:ring-1 focus:ring-primary" ``` --- ## ⚠️ Light/Dark Mode (CRITICAL) ### The Rule **Every color MUST work in BOTH light AND dark mode. No exceptions.** ### How It Works | Property | Value | |----------|-------| | Framework | `next-themes` | | Attribute | `data-theme="light"` (NOT class-based) | | Default | Dark mode | | Toggle | User preference via settings | ### CSS Variable Pattern **ALWAYS define BOTH variants:** ```css /* In globals.css */ :root { /* Dark mode (default) */ --custom-color: 200 50% 50%; } [data-theme="light"] { /* Light mode - MUST be defined! */ --custom-color: 200 60% 30%; /* Darker for light bg */ } ``` ### Common Mistakes ```tsx // ❌ WRONG - Hardcoded, breaks in one theme
// ❌ WRONG - Only works in dark mode
// ❌ WRONG - Custom inline color
// ✅ CORRECT - Uses semantic variables
// ✅ CORRECT - With opacity
``` ### Testing Requirement > **ALWAYS test in BOTH themes before considering work complete.** > Toggle to light mode via user settings or browser dev tools. --- ## ⚠️ Contrast Requirements (CRITICAL) ### WCAG 2.1 AA Standard | Text Type | Minimum Ratio | |-----------|---------------| | Body text | **4.5:1** | | Large text (18px+ or 14px bold) | **3:1** | | UI components | **3:1** | ### How to Check 1. Use browser DevTools color picker 2. Or use [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) ### Common Problem: Muted Text ```tsx // ❌ RISKY - muted-foreground may have low contrast

Small muted text

// ✅ SAFER - use for secondary info only, not critical content

Secondary description

Important content here

``` ### Status Colors Must Have Contrast ```tsx // ✅ Status on dark background - use appropriate contrast Verified Error occurred // ✅ Status as background - ensure text contrast Verified // Uses pre-defined contrast ``` --- ## Hardcoded Colors = FORBIDDEN ### Zero Tolerance Policy | Forbidden | Why | |-----------|-----| | `bg-slate-*` | Breaks light mode | | `text-gray-*` | Inconsistent theming | | `border-zinc-*` | Not semantic | | `#1e293b`, `rgb(...)` | Hardcoded | | `dark:` prefix | Use `data-theme` instead | ### Allowed Patterns | Pattern | Example | |---------|---------| | Semantic variables | `bg-card`, `text-foreground` | | Status colors (HSL) | `text-[hsl(var(--success))]` | | With opacity | `bg-primary/90` | | Custom vars in globals.css | After defining light/dark variants | ### Finding Violations Search for hardcoded colors in codebase: ```bash # Find Tailwind color classes (potential violations) grep -r "bg-slate\|bg-gray\|text-slate\|text-gray" src/ grep -r "border-zinc\|bg-zinc" src/ ``` --- ## Accessibility ### Screen Reader Support ```tsx // Always add aria-label for icon-only buttons ``` ### Focus Indicators All interactive elements must have visible focus: ```tsx className="focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2" ``` --- ## shadcn/ui Components ### Installed Components | Component | Location | |-----------|----------| | `toast`, `toaster` | Notifications | | `dialog` | Modals | | `dropdown-menu` | Dropdowns | | `input`, `label`, `textarea` | Form fields | | `select`, `checkbox` | Form controls | | `tooltip` | Tooltips | | `confirm-dialog` | Confirmation prompts | ### Usage ```tsx // Toasts import { toast } from "@/hooks/use-toast"; toast({ title: "Success!", description: "Saved" }); toast({ title: "Error", variant: "destructive" }); // Confirmation import { ConfirmDialog } from "@/components/ui/confirm-dialog"; ``` --- ## Reference Files | File | Purpose | |------|---------| | `docs/THEME_SYSTEM.md` | **Full documentation** | | `src/app/globals.css` | CSS variable definitions | | `src/lib/badges.ts` | Badge color configuration | | `/admin/design-system` | Live component examples | --- ## Checklist Before completing any UI work: - [ ] Using semantic CSS variables (not hardcoded colors) - [ ] Tested in BOTH light and dark themes - [ ] Used `min-w-0` for flex containers needing truncation - [ ] Used `flex-shrink-0` on icons in flex layouts - [ ] Added focus indicators to interactive elements - [ ] Text contrast meets 4.5:1 ratio - [ ] Updated design system page if adding new patterns --- ## Related Skills - `form-components` - Form field styling - `architecture-philosophy` - Use shadcn over custom components