3.0 KiB
3.0 KiB
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 entityisActive: boolean— whether this is the active turnonRename: (id, newName) => voidonSetInitiative: (id, value) => voidonRemove: (id) => voidonSetHp: (id, maxHp) => voidonAdjustHp: (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) => voidonAdvanceTurn: () => 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