Files
initiative/specs/021-bestiary-statblock/contracts/ui-contracts.md

5.3 KiB

UI Contracts: Bestiary Search & Stat Block Display

Branch: 021-bestiary-statblock | Date: 2026-03-06

Component Contracts

BestiarySearch

Purpose: Search input with autocomplete dropdown for creature selection.

Props:

  • onSelectCreature: (creature: Creature) => void — Called when user selects a creature from results
  • onClose: () => void — Called when search is dismissed (Escape, click outside)

Behavior:

  • Opens focused on the search input
  • Filters creatures by case-insensitive substring match on name
  • Minimum 2 characters before showing results
  • Maximum 10 results shown at a time
  • Keyboard navigation: ArrowUp/ArrowDown to navigate, Enter to select, Escape to close
  • Shows "No creatures found" for zero-match queries
  • Shows source tag next to creature name (e.g., "Goblin (MM 2024)")

StatBlock

Purpose: Renders a complete creature stat block.

Props:

  • creature: Creature — The creature data to display

Sections rendered (in order, omitted if data absent):

  1. Header: name, size, type, alignment
  2. Stats bar: AC, HP (average + formula), Speed
  3. Ability scores: STR/DEX/CON/INT/WIS/CHA with modifiers
  4. Properties: Saving Throws, Skills, Damage Vulnerabilities, Damage Resistances, Damage Immunities, Condition Immunities, Senses, Languages, CR + Proficiency Bonus
  5. Traits
  6. Spellcasting (rendered as a separate section after traits)
  7. Actions
  8. Bonus Actions
  9. Reactions
  10. Legendary Actions (with preamble text)

StatBlockPanel

Purpose: Responsive wrapper — side panel on desktop, drawer on mobile.

Props:

  • creature: Creature | null — Currently displayed creature (null = closed)
  • onClose: () => void — Close the panel/drawer

Behavior:

  • Desktop (>= 1024px): Renders as a right-side panel, tracker shifts left
  • Mobile (< 1024px): Renders as a slide-over drawer from right with backdrop
  • Close button always visible
  • Panel scrolls independently of the main content

ActionBar (modified)

Extended Props:

  • onAddCombatant: (name: string) => void — Existing: add plain combatant
  • onAddFromBestiary: (creature: Creature) => void — New: add creature with stat pre-fill
  • suggestions: Creature[] — Matching creatures for current name input
  • onSearchChange: (query: string) => void — Notify parent of input changes for suggestion filtering

New Elements:

  • Magnifying glass icon button next to input → opens dedicated BestiarySearch
  • Autocomplete suggestion list below input when typing (>= 2 chars and matches exist)

Hook Contracts

useBestiary

Purpose: Provides creature search and lookup from the bundled bestiary.

Returns:

  • search: (query: string) => Creature[] — Filter creatures by name substring
  • getCreature: (id: CreatureId) => Creature | undefined — Look up creature by ID
  • allCreatures: Creature[] — Full list (for potential future use)

Behavior:

  • Loads and normalizes bestiary data once on initialization (lazy, memoized)
  • Search is synchronous (in-memory filter)
  • Returns results sorted alphabetically by name

useEncounter (extended)

Extended return:

  • addFromBestiary: (creature: Creature) => void — New: adds combatant with auto-numbered name, pre-filled HP/AC, and creatureId link

Auto-numbering behavior:

  • Checks existing combatant names for conflicts with the creature's base name
  • If no conflict: uses creature name as-is
  • If one existing match: renames existing to "Name 1", new one becomes "Name 2"
  • If N existing matches: new one becomes "Name N+1"
  • Names remain editable after auto-numbering

Layout Contract

App (modified)

Desktop layout (>= 1024px, stat block open):

+--------------------------------------------------+
| Header                                            |
+-------------------------+------------------------+
| Tracker (flex-1)        | Stat Block (~400px)    |
| - Turn Navigation       | - Scrollable           |
| - Combatant List        | - Close button         |
| - Action Bar            |                        |
+-------------------------+------------------------+

Desktop layout (stat block closed):

+--------------------------------------------------+
| Header                                            |
| Tracker (max-w-2xl, centered)                     |
| - Turn Navigation                                 |
| - Combatant List                                  |
| - Action Bar                                      |
+--------------------------------------------------+

Mobile layout (< 1024px, stat block open):

+--------------------------------------------------+
| Header                                            |
| Tracker (full width)                              |
| - Turn Navigation                                 |
| - Combatant List                                  |
| - Action Bar                                      |
|                                                   |
|  +----------------------------------------------+ |
|  | Drawer (slide from right, 85% width)         | |
|  | - Close button                               | |
|  | - Stat Block (scrollable)                    | |
|  +----------------------------------------------+ |
|  | Backdrop (click to close)                    | |
+--------------------------------------------------+