Files
initiative/specs/028-semantic-hover-tokens/research.md

7.0 KiB

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-neutraltext-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.