Foundations
Game menus
Every menu the player opens in the city — a shop, a garage, a barber, a stranger’s questions — is built one way. The rules are strict so that a new screen looks like the others on the first try.
Anatomy
A real
MenuScreen at 1080p, scaled to this page the way the NUI scales it. Six slots, always in the same place.
1
Tabs · Top left
TabsList variant="menu" with Q / E bumpers. The screen’s sections.
2
Wallet · Top right
PriceTags: cash, then bank. Only on screens that cost money.
3
Title + list · Left column, 480px
The screen’s name, a subtitle with where-am-I, then one MenuList.
4
The world · Centre
Empty. The character, the car, the room — what the menu changes.
5
Checkout · Bottom right, 448px
CheckoutBar, with a MenuChoice for the payment above it if needed.
6
Key hints · Foot, right
KeyHintBar separated, at most four hints a row, two rows at most.
Layout
- 1Every in-game menu is a
MenuScreen. Nothing is positioned by hand; a menu that needs something else is missing a slot — add the slot to the registry, not an absolute div to the page. - 2The centre of the screen stays empty. The world is the point of the menu.
- 3A person or a thing offering a few choices (up to nine) is
layout="prompt": numbered rows low on the left, no tabs, no wallet, no checkout. - 4One list per screen. A second level replaces the first, with a
backrow at its top — never two lists side by side.
Type
- 1Menus speak only in
font-menu— Barlow Condensed, bold, italic, capitals — at the menu sizes:menu-title64,menu-heading40,menu-figure28,menu-tab22,menu-item20,menu-hint15. No other size. - 2Write the copy in sentence case in the locale file;
font-menusets the capitals. Setlang="tr"so i becomes İ. - 3A row label fits on one line at 480px: twenty-two characters is the budget.
- 4All text over the world carries
text-hud. The components do it; hand-written text must too.
Colour
- 1Blocks are
menu-block: the dark at 78%. Never a panel, never glass, never a blur inside a menu. - 2White means chosen or focused. Nothing else on a menu is white-filled.
- 3The accent fills two things only: the wallet and the total. Never a row, a tab or a button.
- 4Neon colours appear only as data: swatches, meters, a rarity glow. Never as a block’s fill.
Shape and space
- 1Menu blocks are cut at
rounded-menu(2px). Norounded-lgin a menu; pills only for toggles. - 2Blocks stack
gap-menu-gap(6px) apart. Rows areh-menu-row(56px). The screen inset ismenu-inset(48px). - 3No borders around blocks. A row’s only line is the rule on its left.
Keyboard first
- 1A menu is played, not clicked. It opens with a row already chosen (
MenuList autoFocus) and every action in it is a key. The menu takes the keyboard without a cursor — the game’sSetNuiFocus(true, false). - 2The cursor is focus, and there is only one. Hovering a row moves that same cursor; a menu never shows a hover highlight and a selection at once.
- 3The pointer appears only where pointing is the task: the map, dragging items between inventories, turning a camera with the mouse, placing or drawing in an editor. Any other screen that shows a cursor is a bug.
- 4The keys are fixed: ↑ ↓ walk rows, ← → change a stepper, slider, toggle or choice, ENTER selects, BACKSPACE steps back, Q / E switch tabs from anywhere, TAB jumps to checkout and back, ESC closes the whole menu. A numbered row is pressed by its number. A screen-specific action takes a letter (H: Korna) and says so.
- 5Tabs never take focus. The keyboard stays in the list, where the work is.
- 6Every action shows its key. If it can be done, there is a hint for it; if there is a hint, it works. Mouse hints use the glyphs (
LMB,RMB,WHEEL,MOUSE), never the words. - 7No native inputs: no
<select>, notype="range", no checkbox. In the FiveM NUI they take the keyboard from the game.
Motion and transitions
- 1Opening, the world goes first: it blurs and dims over 380ms. Then each piece comes in from its own edge — tabs from the top, the list from the left, the aside and checkout from the right, the hints from the bottom — and the rows follow one another 40ms apart.
MenuScreenandMenuListdo all of it. - 2Closing is quicker than opening: everything leaves together in 180ms (
MenuPresence). A game drops its menu at once. - 3Every change of view says which way it went. The next tab or a step in slides in from the right; the previous tab or a step back from the left. Wrap the title and the list in
MenuViewwith the directionMenuTabshands you, or ±1 for a drill-down. The travel is 6% of the view over 200ms. - 4The cursor is the only thing that moves on a selection: the white fill wipes in from the left in 120ms and the row juts out. The tabs’ white block glides to the new tab.
- 5Values roll: a stepper’s value slides out the way it changed and the new one in behind it. A number never just swaps.
- 6Every move makes a sound — the cursor ticks, a value clicks, a tab swaps, ENTER confirms, BACKSPACE drops back, the menu opens and closes — through
UiSoundsProvider, mounted once at the app root. The components do it; see Sound for the twelve and their rules. A silent move feels broken in a game. - 7Only a chosen swatch or tile pops. Nothing bounces, loops or pulses in a menu. Under reduced motion the views only fade.
Before a menu ships
A pull request that adds or changes a menu answers yes to every line.
- Built from
MenuScreenand the menu components only — noabsolutein the page. - Every text node is
font-menuat a menu size, or comes from a component. - Accent appears on the wallet and the total, nowhere else.
- Opens with a row chosen and works with the mouse unplugged — unless it is a map, a drag-and-drop or an editor.
- Every key it listens to has a hint; every view change passes a direction; every move plays a sound; no label selects.
- Checked in the gallery in NUI 1080p and NUI 1440p: nothing clips, nothing stays small.
- Copy in both
tranden, sentence case, row labels within 22 characters.
The game’s screens, rebuilt
Five screens from the game’s webview, rebuilt from the rules alone. Full screen and keyboard only: ESC closes a menu, E (F1 for the dashboard) opens it again — watch it come and go.
Building one
Start from the clothing demo’s source: aMenuScreen, its slots filled with the menu components, nothing else.<MenuScreen
onClose={close}
tabs={<MenuTabs items={TABS} value={tab} onValueChange={(value, step) => { setDirection(step); setTab(value); }} />}
wallet={<PriceTag icon={<Wallet />}>$1.200</PriceTag>}
title={<MenuView viewKey={tab} direction={direction}>Kıyafet</MenuView>}
checkout={<CheckoutBar total="$200" note="+ vergi" confirmLabel="Satın al" />}
hints={<KeyHintBar separated><KeyHint voice="menu" keys="ESC" label="Çık" /></KeyHintBar>}
>
<MenuView viewKey={tab} direction={direction}>
<MenuList autoFocus>
<MenuItem icon={<Shirt />} active>Üst giyim</MenuItem>
<OptionStepper label="Model" value={model} count={148} onChange={setModel} />
</MenuList>
</MenuView>
</MenuScreen>

