# Research: UI Baseline **Feature**: 010-ui-baseline | **Date**: 2026-03-05 ## R1: Tailwind CSS Version Choice **Decision**: Use Tailwind CSS v4 (latest stable) **Rationale**: Tailwind v4 uses a CSS-first configuration approach with `@import "tailwindcss"` and CSS-based theme customization via `@theme`. This simplifies setup — no `tailwind.config.ts` is strictly required for basic usage. Vite has first-class support via `@tailwindcss/vite` plugin. **Alternatives considered**: - Tailwind v3: Mature but requires JS config file and PostCSS plugin. v4 is stable and recommended for new projects. ## R2: shadcn/ui Integration Approach **Decision**: Use shadcn/ui CLI to scaffold components into `apps/web/src/components/ui/` **Rationale**: shadcn/ui is not a package dependency — it copies component source code into the project. This gives full control over styling and avoids version lock-in. Components use Tailwind classes + Radix UI primitives. **Alternatives considered**: - Radix UI directly: More low-level, requires writing all styles manually. shadcn/ui provides pre-styled Tailwind components. - Material UI / Chakra UI: Heavier runtime, opinionated styling that conflicts with Tailwind approach. ## R3: Tailwind v4 + Vite Setup **Decision**: Use `@tailwindcss/vite` plugin instead of PostCSS **Rationale**: Tailwind v4 provides a dedicated Vite plugin that is faster than the PostCSS approach. Setup is: 1. Install `tailwindcss @tailwindcss/vite` 2. Add plugin to `vite.config.ts` 3. Create `index.css` with `@import "tailwindcss"` 4. Import `index.css` in `main.tsx` **Alternatives considered**: - PostCSS plugin: Works but slower; Vite plugin is recommended for Vite projects. ## R4: Icon Library **Decision**: Use Lucide React for icons (remove button, etc.) **Rationale**: Lucide is the default icon set for shadcn/ui. Tree-shakeable, consistent style, and already expected by shadcn/ui component templates. **Alternatives considered**: - Heroicons: Good quality but not the shadcn/ui default. - Inline SVG: Too manual for maintenance. ## R5: CSS Utility Helper (cn function) **Decision**: Use `clsx` + `tailwind-merge` via a `cn()` utility function **Rationale**: Standard shadcn/ui pattern. `clsx` handles conditional classes, `tailwind-merge` deduplicates conflicting Tailwind classes. The `cn()` helper is placed in `lib/utils.ts`. **Alternatives considered**: - `clsx` alone: Doesn't deduplicate conflicting Tailwind classes (e.g., `p-2 p-4`). ## R6: Component Decomposition **Decision**: Extract App.tsx into focused components while keeping them in a single file or minimal files **Rationale**: The current App.tsx (~280 lines) has inline components (EditableName, MaxHpInput, CurrentHpInput). For the UI baseline, we'll restructure into: - `App.tsx` — layout shell (header, combatant list, action bar) - `components/combatant-row.tsx` — single combatant row with all controls - `components/ui/` — shadcn/ui primitives (Button, Input, Card) This keeps the change focused while establishing a scalable component structure. **Alternatives considered**: - Keep everything in App.tsx: Gets unwieldy with Tailwind classes added. - Full atomic decomposition: Over-engineered for current scope. ## R7: verbatimModuleSyntax Compatibility **Decision**: shadcn/ui components work with `verbatimModuleSyntax` since they use standard ESM imports **Rationale**: shadcn/ui generates standard TypeScript files with explicit type imports. The `cn` utility and Radix imports use value imports. No special handling needed. ## R8: Biome 2.0 Compatibility **Decision**: No conflicts expected; Tailwind class strings are just strings **Rationale**: Biome formats/lints TypeScript and JSX. Tailwind classes in `className` props are plain strings, which Biome ignores content-wise. The shadcn/ui generated code follows standard formatting conventions. May need to run `pnpm format` after generating components. ## R9: Knip Compatibility **Decision**: May need to configure Knip to recognize shadcn/ui component exports **Rationale**: shadcn/ui components are copied into the project. If not all are immediately used, Knip may flag them as unused. Solution: only install the shadcn/ui components we actually need (Button, Input, Card/container).