# Research: Semantic Hover Tokens ## R-001: Tailwind v4 Theme Token Mechanism **Decision**: Define new CSS custom properties in the `@theme` block of `index.css`. Tailwind v4 automatically makes `--color-*` variables available as utility classes (e.g., `--color-hover-neutral` → `text-hover-neutral`, `bg-hover-neutral`). **Rationale**: The project already uses this pattern for `--color-primary`, `--color-destructive`, etc. Adding new tokens follows the established convention and requires zero config changes. **Alternatives considered**: - Tailwind plugin extending theme: Unnecessary — Tailwind v4 CSS-native theming handles this. - CSS-only approach without Tailwind utilities: Would lose the `hover:text-*` class syntax that all components already use. ## R-002: Scope of Affected Components **Decision**: The spec lists five components, but the audit found hover colors in additional files: `hp-adjust-popover.tsx`, `stat-block-panel.tsx`, `bestiary-search.tsx`, `ac-shield.tsx`, and `ui/button.tsx`. The plan will address the five specified components plus `ac-shield.tsx` (already uses `hover:text-primary` for an editable field). The HP adjust popover's semantic damage/healing colors are **out of scope** — they represent domain-semantic colors (damage=red, healing=green) rather than interaction-tier hover colors. The `button.tsx` CVA variants were initially out of scope but were brought in during implementation (see R-007). **Rationale**: The HP popover buttons communicate game semantics (damage vs. healing), not interaction intent. Forcing them into the three-tier system would reduce clarity. `stat-block-panel.tsx` and `bestiary-search.tsx` use `hover:text-foreground` which already matches the neutral-interactive intent (dim→bright) — these can adopt the neutral token. **Alternatives considered**: - Include all hover colors across all components: Over-scoped; HP popover serves a different purpose. - Strictly limit to five listed files: Would miss `ac-shield.tsx` which is clearly neutral-interactive. ## R-003: Token Naming Convention **Decision**: Use `--color-hover-neutral`, `--color-hover-action`, `--color-hover-destructive` as the CSS custom property names. This yields Tailwind classes like `hover:text-hover-neutral`, `hover:bg-hover-action`, etc. **Rationale**: The `hover-` prefix groups them visually in the theme. The tier names (neutral/action/destructive) are concise and parallel the existing `--color-primary`/`--color-destructive` naming. **Alternatives considered**: - `--color-interactive-*`: Longer, and "interactive" is redundant since hover already implies interaction. - `--color-hover-primary` for the action tier: Conflicts conceptually with existing `--color-primary` (which is used for non-hover purposes too). ## R-004: Background Hover Variants **Decision**: Define three additional background tokens: `--color-hover-neutral-bg`, `--color-hover-action-bg`, `--color-hover-destructive-bg`. Components that use `hover:bg-*` will reference these. **Rationale**: Several components use background hovers (condition-picker `hover:bg-card`, navigation buttons `hover:bg-muted`, bestiary items `hover:bg-accent/10`). A single text-only token won't cover these cases. **Alternatives considered**: - Reuse existing `--color-card`/`--color-muted` for background hovers: Breaks the single-point-of-change goal — changing the neutral hover background would also change all card backgrounds. - No background tokens (text-only): Would leave background hovers hardcoded, defeating the purpose. ## R-005: Default Token Values **Decision**: Map defaults to preserve current visual appearance: - `--color-hover-neutral: var(--color-primary)` (#3b82f6 — blue, matching previous `hover:text-primary` behavior) - `--color-hover-action: var(--color-primary)` (#3b82f6 — blue) - `--color-hover-destructive: var(--color-destructive)` (#ef4444 — red) - `--color-hover-neutral-bg: var(--color-card)` (#1e293b) - `--color-hover-action-bg: var(--color-muted)` (#64748b — defined but not used by nav buttons, see R-008) - `--color-hover-destructive-bg: transparent` (current destructive hovers are text-only in scope) **Rationale**: Using `var()` references means the hover tokens inherit from the base theme by default, reducing duplication. The neutral token was initially `var(--color-foreground)` but changed to `var(--color-primary)` during implementation because elements already at `text-foreground` (name, initiative, HP) had no visible hover change — the hover target color was identical to the resting color. Using `var(--color-primary)` preserves the original blue hover feedback while still centralizing control. **Alternatives considered**: - Hardcode hex values: Would break the cascade if base theme colors change. - Fewer background tokens: The action and neutral backgrounds serve different purposes currently. - `--color-hover-neutral: var(--color-foreground)`: No visible hover for elements already at foreground color — rejected during implementation. ## R-006: Components Using hover:text-foreground **Decision**: `stat-block-panel.tsx` and `bestiary-search.tsx` close buttons use `hover:text-foreground` (dim→bright). These will migrate to `hover:text-hover-neutral` since the default value of `--color-hover-neutral` is `var(--color-primary)` — providing a blue hover instead of the previous dim→bright. **Rationale**: Consistent with the neutral-interactive tier. These are close/dismiss actions, not destructive (they don't delete data). ## R-007: Button Component (ui/button.tsx) Brought Into Scope **Decision**: Migrate the `outline` and `ghost` CVA variant hover styles in `ui/button.tsx` from hardcoded `hover:bg-card hover:text-foreground` to `hover:bg-hover-neutral-bg hover:text-hover-neutral`. **Rationale**: Initially excluded as a shared UI primitive (R-002). During implementation, the ghost variant's `hover:text-foreground` produced no visible hover on the search button (same issue as R-005 — foreground→foreground). Additionally, the outline variant's `hover:bg-card` conflicted with inline `hover:bg-hover-action-bg` on the nav buttons, creating a double-background hover artifact. Migrating the Button variants to semantic tokens resolved both issues. **Alternatives considered**: - Keep button.tsx out of scope: Would leave the search button without visible hover and nav buttons with conflicting hover backgrounds. ## R-008: Nav Button Background Hover Removed **Decision**: The Previous/Next outline buttons in `turn-navigation.tsx` use `hover:bg-transparent` instead of `hover:bg-hover-action-bg`. The blue text + blue border provide sufficient hover feedback without a background fill. **Rationale**: The `--color-hover-action-bg` default (`var(--color-muted)` = `#64748b`) created a heavy filled-box appearance on the small outline icon buttons. Combined with the blue border and icon, the solid grey background was visually overwhelming. Removing it produces a cleaner hover state. **Alternatives considered**: - Keep `hover:bg-hover-action-bg`: Visually heavy on small outline buttons. - Use a semi-transparent background: Adds complexity for marginal benefit.