4.2 KiB
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:
- Install
tailwindcss @tailwindcss/vite - Add plugin to
vite.config.ts - Create
index.csswith@import "tailwindcss" - Import
index.cssinmain.tsxAlternatives 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:
clsxalone: 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 controlscomponents/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).