Files
initiative/specs/027-ui-polish/research.md

4.8 KiB
Raw Blame History

Research: Combatant Row UI Polish

Feature: 027-ui-polish | Date: 2026-03-10

R1: CSS Shield Shape for AC

Decision: Use an inline SVG background with the AC number centered inside, rendered as a dedicated AcShield component.

Rationale: CSS clip-path clips the element itself (including borders and backgrounds) but makes it hard to get a clean outlined shield. An inline SVG shield path with the number as a <text> or overlaid HTML gives full control over stroke, fill, and sizing. This matches how D&D Beyond and character sheet PDFs render AC.

Alternatives considered:

  • clip-path: polygon(...) — clips the element shape but can't produce a stroke outline easily. Would need a pseudo-element hack.
  • Background image SVG — works but harder to make the stroke color respond to CSS custom properties (theme-aware).
  • Lucide Shield icon with number overlaid via absolute positioning — fragile alignment, icon stroke competes with the number visually.

Approach: Create a small SVG shield outline (viewBox-based) as a React component. The AC number is rendered as a centered <span> overlaid on the SVG using relative/absolute positioning. The SVG uses currentColor for the stroke so it inherits theme colors. Size: approximately 28×32px to comfortably fit 1-3 digit numbers.

R2: Row Click Stat Block — Event Delegation

Decision: Attach onClick handler to the row container, use event.stopPropagation() on all interactive child elements to prevent bubbling.

Rationale: This is the standard pattern for "click anywhere except interactive elements." Each interactive element (initiative, HP, AC, conditions, "+", "×", concentration) already has its own click handler — adding stopPropagation() to each ensures the row-level handler only fires for non-interactive areas.

Alternatives considered:

  • Checking event.target against a list of interactive selectors — fragile and hard to maintain.
  • Wrapping non-interactive areas in a separate clickable element — would complicate the grid layout.

R3: Hover-Only Elements — Touch Device Accessibility

Decision: Use CSS opacity-0 group-hover:opacity-100 for hide/show, combined with focus-within:opacity-100 for keyboard accessibility. On touch devices, the elements are accessible via tap (the first tap reveals, second tap activates — standard mobile pattern with opacity transitions).

Rationale: The concentration button already uses this exact pattern (opacity-0 group-hover:opacity-50). Extending it to the remove button and "+" condition button is consistent.

Alternatives considered:

  • display: none / display: block — causes layout shifts (violates FR-004).
  • visibility: hidden / visible — works but doesn't allow opacity transitions.

R4: Inline Conditions Layout

Decision: Move ConditionTags and the "+" button into the name column's flex container (the 1fr column), after the name text. The conditions and "+" sit inline with the name, wrapping if needed.

Rationale: The name column is already a flex container (flex items-center gap-1). Adding condition icons here is natural. The truncate class on the name will need adjustment — the name should shrink (min-w-0 truncate on just the name text) while conditions fill remaining space.

Alternatives considered:

  • Dedicated column for conditions — adds complexity to the grid and uses horizontal space poorly.
  • Conditions as a second flex row within the name cell — still uses flex-wrap but explicitly creates a two-row layout when many conditions exist.

R5: D20 Icon Sizing

Decision: Increase from h-5 w-5 (20px) to h-7 w-7 (28px). The initiative column is 3rem (48px) wide, so 28px fits comfortably with padding.

Rationale: At 20px the internal geometry lines of the d20 are too dense to read as a die shape. At 28px the icosahedron silhouette becomes recognizable. The column has room — the d20 button is already h-7 w-full so only the icon within needs to grow.

Alternatives considered:

  • h-6 w-6 (24px) — marginal improvement, still a bit small.
  • h-8 w-8 (32px) — might feel oversized relative to the initiative numbers in adjacent rows.

R6: Concentration Click Target

Decision: Expand the concentration button from 1.25rem width to fill the full gutter. Change the grid column from 1.25rem to 2rem (or use padding on the button to extend its hit area to the row edge). The brain icon stays centered visually.

Rationale: The left border of the row already has px-3 padding. The concentration column at 1.25rem (20px) with a 16px icon leaves very little extra hit area. Widening the column or extending the button's padding makes tapping much easier.

Alternatives considered:

  • Negative margin on the button — hacky, could affect layout.
  • Separate invisible overlay — unnecessary complexity.