Skip to content

How to Build: Menu Panels

The HUD gives us buttons. Now we need the panels those buttons open. There are five of them: Inventory, Equipment, Skills, Vendor, and Checkpoint Map. Each one shows detailed information about a player system, and they all share the same shell — a dark overlay with a content area that slides up from the bottom.

Only one panel can be open at a time. Tapping outside the panel or hitting the same menu bar button closes it. Panels are accessible from anywhere: mid-combat, while walking, whenever.

Shared Panel Wrapper

Before building five different panels, let's build the one thing they all have in common. The MenuPanel wrapper provides the overlay backdrop, close-on-tap-outside behavior, and consistent positioning:

typescript
function MenuPanel(props: {
  children: React.ReactNode
  onClose: () => void
}): JSX.Element

The wrapper renders a full-screen semi-transparent overlay. Clicking the overlay calls onClose. The content area stops click propagation so tapping inside the panel doesn't close it. Position the content area above the menu bar so they don't overlap.

State Management

Panel state lives in React, consistent with the HUD:

typescript
const [openMenu, setOpenMenu] = useState<MenuType | null>(null)

type MenuType = 'inventory' | 'equipment' | 'skills' | 'vendor' | 'checkpoints'

The HudOverlay component owns this state and passes it down. Setting openMenu to null closes whatever is open. Setting it to a MenuType opens that panel (and implicitly closes the previous one since only one value can be active).

Inventory Panel

The simplest panel. It shows what the player has:

typescript
function InventoryMenu(props: { player: Entity }): JSX.Element

Read Gold and Inventory traits using useTrait. Display:

  • Gold count
  • HP potion count
  • List of owned but unequipped gear (name and stat bonuses)

That's it. No actions, no buttons — just information. The player equips gear from the Equipment panel, not here. Keeping inventory as a read-only view avoids confusion about where to perform actions.

Equipment Panel

This one is interactive. The player can swap their equipped gear:

typescript
function EquipmentMenu(props: { player: Entity }): JSX.Element

Read Equipment and Inventory traits. Display:

  • Weapon slot: currently equipped weapon (name, stat bonuses) or "None"
  • Armor slot: currently equipped armor (name, stat bonuses) or "None"
  • Available gear list: all owned, unequipped items with an "Equip" button

When the player taps "Equip" on an item, call the equipGear action. The old item goes back to inventory, the new item goes into the slot. Both traits update, and useTrait triggers a re-render automatically.

Notice how there's no confirmation dialog. Equipping is instant and reversible — the old gear isn't destroyed, it just moves back to inventory. This keeps the interaction snappy for mobile.

Skills Panel

This panel has the most information density. It shows progression across all four skills and the auto-potion settings:

typescript
function SkillsMenu(props: { player: Entity }): JSX.Element

Read Skills and ConsumableSettings traits. For each of the four skills (Attack, Strength, Defence, Hitpoints):

  • Display the current level
  • Show an experience points progress bar toward the next level (using your xpForLevel formula)

Below the skill rows, add the auto-potion settings:

  • A checkbox toggle for enabling/disabling auto-potion use
  • A slider for the HP threshold (10% to 90%) — "use a potion when HP drops below X%"

Both settings write directly to the ConsumableSettings trait via player.set().

Vendor and Checkpoint Map Panels

These are covered in their own guides:

They both use the same MenuPanel wrapper and follow the same patterns described here.

File Structure

src/features/ui/menus/
├── MenuPanel.tsx          # Shared panel wrapper (overlay, close behavior)
├── InventoryMenu.tsx      # Gold, potions, owned items
├── EquipmentMenu.tsx      # Equipped gear, swap interface
└── SkillsMenu.tsx         # Skill levels, XP bars, consumable settings

What This System Depends On

  • Player Entity — reads Gold, Equipment, Inventory, Skills, ConsumableSettings traits
  • Inventory and GoldequipGear action for equipment swaps
  • HUD — menu bar buttons trigger panel open/close

We now have a complete UI layer: a minimal HUD with five detailed panels the player can pop open at any time. But we still need two more gameplay systems to round out the prototype — checkpoints for fast travel and saves for persistence.