Files
initiative/specs/010-ui-baseline/contracts/ui-components.md

79 lines
3.0 KiB
Markdown

# UI Component Contracts: UI Baseline
**Feature**: 010-ui-baseline | **Date**: 2026-03-05
## Layout Contract
The encounter screen follows a single-column layout with three zones:
```
┌─────────────────────────────────┐
│ EncounterHeader │
│ Title + Round/Turn status │
├─────────────────────────────────┤
│ CombatantList │
│ ┌─ CombatantRow (active) ───┐ │
│ │ Init │ Name │ HP │ Actions│ │
│ └───────────────────────────┘ │
│ ┌─ CombatantRow ────────────┐ │
│ │ Init │ Name │ HP │ Actions│ │
│ └───────────────────────────┘ │
│ ... (scrollable if overflow) │
│ │
│ [EmptyState if no combatants] │
├─────────────────────────────────┤
│ ActionBar │
│ [Name input] [Add] [Next Turn] │
└─────────────────────────────────┘
```
## CombatantRow Contract
**Props**:
- `combatant: Combatant` — domain entity
- `isActive: boolean` — whether this is the active turn
- `onRename: (id, newName) => void`
- `onSetInitiative: (id, value) => void`
- `onRemove: (id) => void`
- `onSetHp: (id, maxHp) => void`
- `onAdjustHp: (id, delta) => void`
**Visual contract**:
- Row uses consistent column widths across all combatant rows
- Active row has visually distinct highlight (accent background or left border)
- Name column truncates with ellipsis at max width
- Remove action is an icon button (no text label)
- All inputs use design system styling (no browser defaults)
**Interaction contract**:
- Click name → enter inline edit mode
- Enter/blur in edit mode → commit change
- Escape in edit mode → cancel
- Initiative input is always visible (not click-to-edit), direct typing only
- HP inputs are direct-entry text fields with numeric keyboard (`inputmode="numeric"`)
- All numeric inputs: no browser spinners, ch-based widths (`6ch`), tabular numerals, centered text
## ActionBar Contract
**Props**:
- `onAddCombatant: (name: string) => void`
- `onAdvanceTurn: () => void`
**Visual contract**:
- Visually separated from combatant list (spacing, background, or border)
- Add form: text input + submit button in a row
- Next Turn: distinct button, visually secondary to Add
**Interaction contract**:
- Enter in name input → add combatant + clear input
- Empty name → no action (button may be disabled or form simply ignores)
## EmptyState Contract
**Displayed when**: `encounter.combatants.length === 0`
**Visual contract**:
- Centered message in the combatant list area
- Muted/secondary text color
- Suggests adding a combatant