130 lines
7.0 KiB
Markdown
130 lines
7.0 KiB
Markdown
# Implementation Plan: Combatant Row UI Polish
|
||
|
||
**Branch**: `027-ui-polish` | **Date**: 2026-03-10 | **Spec**: [spec.md](./spec.md)
|
||
**Input**: Feature specification from `/specs/027-ui-polish/spec.md`
|
||
|
||
## Summary
|
||
|
||
Six presentational improvements to the combatant row: move conditions inline after the creature name, make the row clickable for stat blocks (removing the BookOpen icon), hide the remove button until hover, display AC inside a shield shape, expand the concentration click target, and increase the d20 icon size. All changes are confined to the web adapter layer — no domain or application changes needed.
|
||
|
||
## Technical Context
|
||
|
||
**Language/Version**: TypeScript 5.8 (strict mode, verbatimModuleSyntax)
|
||
**Primary Dependencies**: React 19, Tailwind CSS v4, Lucide React (icons), Vite 6
|
||
**Storage**: N/A (no storage changes — purely presentational)
|
||
**Testing**: Vitest (existing domain tests must continue passing; no new component tests)
|
||
**Target Platform**: Modern browsers (desktop primary, touch secondary)
|
||
**Project Type**: Web application (single-page, local-first)
|
||
**Performance Goals**: No perceptible layout shift on hover; smooth opacity transitions
|
||
**Constraints**: Must not break any existing interactions; all changes in `apps/web/` only
|
||
**Scale/Scope**: ~1 main component file restructured, 1 new small component
|
||
|
||
## Constitution Check
|
||
|
||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||
|
||
| Principle | Status | Notes |
|
||
|-----------|--------|-------|
|
||
| I. Deterministic Domain Core | PASS | No domain changes whatsoever |
|
||
| II. Layered Architecture | PASS | All changes in adapter layer (apps/web). No domain or application imports added. |
|
||
| III. Agent Boundary | N/A | No agent features involved |
|
||
| IV. Clarification-First | PASS | All six changes were discussed and confirmed with user before speccing |
|
||
| V. Escalation Gates | PASS | All changes are within spec scope |
|
||
| VI. MVP Baseline Language | PASS | No permanent bans introduced |
|
||
| VII. No Gameplay Rules | PASS | No gameplay mechanics in this feature |
|
||
|
||
**Post-Phase 1 Re-check**: All gates still pass. No domain or application layer changes introduced during design.
|
||
|
||
## Project Structure
|
||
|
||
### Documentation (this feature)
|
||
|
||
```text
|
||
specs/027-ui-polish/
|
||
├── plan.md # This file
|
||
├── research.md # Phase 0 output
|
||
├── data-model.md # Phase 1 output
|
||
├── quickstart.md # Phase 1 output
|
||
├── checklists/
|
||
│ └── requirements.md # Spec quality checklist
|
||
└── tasks.md # Phase 2 output (via /speckit.tasks)
|
||
```
|
||
|
||
### Source Code (repository root)
|
||
|
||
```text
|
||
apps/web/src/components/
|
||
├── combatant-row.tsx # MODIFY — layout restructure, event handling, hover states
|
||
├── condition-tags.tsx # UNCHANGED — reused inline
|
||
├── condition-picker.tsx # MINOR MODIFY — positioning may need adjustment for inline context
|
||
├── d20-icon.tsx # UNCHANGED — sized via className prop
|
||
└── ac-shield.tsx # NEW — shield-shaped AC display component
|
||
```
|
||
|
||
**Structure Decision**: No new directories or architectural changes. One new presentational component (`ac-shield.tsx`). All modifications within existing `apps/web/src/components/` directory.
|
||
|
||
## Design Decisions
|
||
|
||
### D1: Inline Conditions Layout
|
||
|
||
The name column (`1fr`) already uses `flex items-center gap-1`. Conditions move into this flex container after the name text. The name text gets `min-w-0 truncate` to shrink when conditions need space. The `ConditionTags` component is reused as-is. The "+" button uses `opacity-0 group-hover:opacity-100` to appear only on hover.
|
||
|
||
The separate conditions row below the grid (with `ml-[calc(3rem+0.75rem)]`) is removed entirely.
|
||
|
||
### D2: Row Click Stat Block via Event Delegation
|
||
|
||
The row container gets an `onClick` handler that calls `onShowStatBlock` (for bestiary combatants). All interactive child elements add `event.stopPropagation()` to their existing click handlers so clicks on initiative, HP, AC, conditions, "+", "×", and concentration don't bubble up to the row handler.
|
||
|
||
Bestiary rows get `cursor-pointer` on the row container. Custom combatant rows (no `creatureId`) don't get the handler or cursor.
|
||
|
||
The BookOpen icon button is deleted from the name area.
|
||
|
||
### D3: Hover-Only Remove Button
|
||
|
||
The remove button gets `opacity-0 group-hover:opacity-100 focus:opacity-100` classes. The button remains in the DOM (preserving the `2rem` grid column) so no layout shift occurs. This matches the existing pattern used by the concentration button.
|
||
|
||
### D4: AC Shield Shape Component
|
||
|
||
New `AcShield` component renders:
|
||
- An inline SVG shield outline (stroke-based, `currentColor`) as background
|
||
- The AC number centered inside via absolute positioning
|
||
- Click handler passed through for editing
|
||
- Sized approximately 28×32px to fit 1-3 digit values
|
||
|
||
Replaces the current `Shield` Lucide icon + number in `AcDisplay`. The edit mode input still appears on click, positioned over/replacing the shield.
|
||
|
||
### D5: Concentration Click Target
|
||
|
||
Widen the first grid column from `1.25rem` to `2rem` and make the concentration button fill the full column with padding. The brain icon stays visually centered. The button's click area now spans from the row's left edge (after border) to the initiative column.
|
||
|
||
Grid changes from:
|
||
```
|
||
grid-cols-[1.25rem_3rem_1fr_auto_auto_2rem]
|
||
```
|
||
to:
|
||
```
|
||
grid-cols-[2rem_3rem_1fr_auto_auto_2rem]
|
||
```
|
||
|
||
### D6: D20 Icon Size
|
||
|
||
Change `className="h-5 w-5"` to `className="h-7 w-7"` on the `D20Icon` in `InitiativeDisplay`. The icon grows from 20px to 28px. The initiative column (3rem = 48px) and the button (`h-7 w-full`) accommodate this without overflow.
|
||
|
||
### D7: Dark Scrollbar Styling
|
||
|
||
Apply `scrollbar-color: var(--color-border) transparent` and `scrollbar-width: thin` on `*` in `index.css`. This gives all scrollbars (main page, stat block panel) a slim dark thumb (`#334155`) on a transparent track, matching the dark UI. Supported in Chrome 121+, Firefox 64+, Safari 18+.
|
||
|
||
### D8: Top Bar Icon Redesign
|
||
|
||
Replace the Previous/Next text+chevron buttons with icon-only `StepBack`/`StepForward` buttons using the `outline` variant with `border-foreground` (white border matching the icon color). Utility actions (d20, trash) use `ghost` variant with `text-muted-foreground` — creating a two-tier visual hierarchy: primary navigation (outlined, white) vs secondary utilities (borderless, grey).
|
||
|
||
Layout: `[StepBack]` — center info — `[d20][trash] [StepForward]`. The d20 and trash are tightly grouped (`gap-0`) with `gap-3` separating them from the Next button. Icon sizes: d20 `h-6 w-6`, trash `h-5 w-5`, step buttons `h-5 w-5`. All buttons `h-8 w-8`.
|
||
|
||
### D9: Remove "Initiative Tracker" Heading
|
||
|
||
Remove the `<header>` block containing the `<h1>Initiative Tracker</h1>` from `apps/web/src/App.tsx`. The turn navigation bar already provides context (round number, active combatant name), making the heading redundant. Removing it reclaims ~60px of vertical space for the combatant list.
|
||
|
||
## Complexity Tracking
|
||
|
||
No constitution violations to justify. All changes are straightforward adapter-layer modifications.
|