--- date: "2026-03-13T15:35:07.699570+00:00" git_commit: bd398080008349b47726d0016f4b03587f453833 branch: main topic: "CSS class usage, button categorization, and hover effects across all components" tags: [research, codebase, css, tailwind, buttons, hover, ui] status: complete --- # Research: CSS Class Usage, Button Categorization, and Hover Effects ## Research Question How are CSS classes used across all components? How are buttons categorized — are there primary and secondary buttons? What hover effects exist, and are they unified? ## Summary The project uses **Tailwind CSS v4** with a custom dark theme defined in `index.css` via `@theme`. All class merging goes through a `cn()` utility (clsx + tailwind-merge). Buttons are built on a shared `Button` component using **class-variance-authority (CVA)** with three variants: **default** (primary), **outline**, and **ghost**. Hover effects are partially unified through semantic color tokens (`hover-neutral`, `hover-action`, `hover-destructive`) defined in the theme, but several components use **one-off hardcoded hover colors** that bypass the token system. ## Detailed Findings ### Theme System (`index.css`) All colors are defined as CSS custom properties via Tailwind v4's `@theme` directive (`index.css:3-26`): | Token | Value | Purpose | |---|---|---| | `--color-background` | `#0f172a` | Page background | | `--color-foreground` | `#e2e8f0` | Default text | | `--color-muted` | `#64748b` | Subdued elements | | `--color-muted-foreground` | `#94a3b8` | Secondary text | | `--color-card` | `#1e293b` | Card/panel surfaces | | `--color-border` | `#334155` | Borders | | `--color-primary` | `#3b82f6` | Primary actions (blue) | | `--color-accent` | `#3b82f6` | Accent (same as primary) | | `--color-destructive` | `#ef4444` | Destructive actions (red) | **Hover tokens** (semantic layer for hover states): | Token | Resolves to | Usage | |---|---|---| | `hover-neutral` | `primary` (blue) | Text color on neutral hover | | `hover-action` | `primary` (blue) | Text color on action hover | | `hover-destructive` | `destructive` (red) | Text color on destructive hover | | `hover-neutral-bg` | `card` (slate) | Background on neutral hover | | `hover-action-bg` | `muted` | Background on action hover | | `hover-destructive-bg` | `transparent` | Background on destructive hover | ### Button Component (`components/ui/button.tsx`) Uses CVA with three variants and three sizes: **Variants:** | Variant | Base styles | Hover | |---|---|---| | `default` (primary) | `bg-primary text-primary-foreground` | `hover:bg-primary/90` | | `outline` | `border border-border bg-transparent` | `hover:bg-hover-neutral-bg hover:text-hover-neutral` | | `ghost` | (no background/border) | `hover:bg-hover-neutral-bg hover:text-hover-neutral` | **Sizes:** | Size | Classes | |---|---| | `default` | `h-9 px-4 py-2` | | `sm` | `h-8 px-3 text-xs` | | `icon` | `h-8 w-8` | All variants share: `rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:pointer-events-none disabled:opacity-50`. There is **no "secondary" variant** — the outline variant is the closest equivalent. ### Composite Button Components **ConfirmButton** (`components/ui/confirm-button.tsx`): - Wraps `Button variant="ghost" size="icon"` - Default state: `hover:text-hover-destructive` (uses token) - Confirming state: `bg-destructive text-primary-foreground animate-confirm-pulse hover:bg-destructive hover:text-primary-foreground` **OverflowMenu** (`components/ui/overflow-menu.tsx`): - Trigger: `Button variant="ghost" size="icon"` with `text-muted-foreground hover:text-hover-neutral` - Menu items: raw `