Menus — the title card, the how-to-play card, the pause card and the two end
cards of THE EIGHTH KETYL.
Every tree here is a pure function of state: hand it the game's state and the
focused item and it returns a widget tree. Nothing in it reads input, runs the
game or holds state of its own — the Screens component owns all of that.
LAYOUT RULE. Every number is derived from the LIVE ui.screenSize() through
metrics() below — the content width is a fraction of the real frame and the
type is a fraction of the shorter edge (Still.sz). There is no pixel constant:
the filmed shape is 1080x1440, and the same card has to land on a phone held
either way, a laptop and a television. The one thing that reflows rather than
scaling is the relationship between the drawn column and the words beside it,
which Kit.portrait() decides.
A flex column that centres its children makes them hug their content, so a
paragraph inside one collapses to a glyph per line. Everything here therefore
sits in a column of a KNOWN width and centres its text with textAlign.
wordWidth( ) → number
The widest a string of `n` characters gets at a given size, near enough to
pick a type size that fits the frame instead of running off the side of it.
The width of the column the words stand in: the whole card on a tall screen,
half of it on a wide one, where the still drawn in elevation takes the other
half. Every card measures its type, its rows and its rule against this rather
than against the card, or the words overflow the column they sit in.
noteSize(note: string, fitWidth: number?, u: number) → number
The monospaced note under a menu row. A fixed-pitch face is close to 0.62 em
a character, so this one measures honestly; it only shrinks when the column
is too narrow for the sentence.
| arg | type | description |
|---|
| note | string | |
| fitWidth | number? | |
| u | number | |
fitSize(text: string, width: number, ideal: number, tracking: number) → number
| arg | type | description |
|---|
| text | string | |
| width | number | |
| ideal | number | |
| tracking | number | |
eyebrow(text: string) →
The small-caps rule that heads every card — an apothecary label's top edge.
| arg | type | description |
|---|
| text | string | What the rule says. |
item(id: string, text: string, note: string?, tone: string?, fitWidth: number?) →
One line of a menu. It is the focus target, the click target and the touch target at once.
| arg | type | description |
|---|
| id | string | The item id, matched against Focus.id(). |
| text | string | What it says. |
| note | string? | A dim second line under it, or nil. |
| tone | string? | The accent it lights up in. |
| fitWidth | number? | |
itemList(rows: { any }, width: number) →
A stack of menu items, spaced and sized as one block.
| arg | type | description |
|---|
| rows | { any } | The item nodes. |
| width | number | The width of the block, in the caller's own metrics. |
familiesFooter(confirmLegend: string, width: number) →
The footer that says the game answers to all three families at once.
| arg | type | description |
|---|
| confirmLegend | string | The connected pad's legend for the south face button. |
| width | number | The width of the block. |
root(tint: string?, m: Metrics, children: { any }, scrim: number?) → void
THE CARDS
| arg | type | description |
|---|
| tint | string? | |
| m | Metrics | |
| children | { any } | |
| scrim | number? | |
tallArtShare(m: Metrics, base: number) → number
The drawn column, sized so it always fits the frame it is standing in: the
art is 240x620, so its height decides its width and neither can overflow.
How much of the height the still in elevation may take on a TALL card. The
type on these cards is sized off the shorter screen edge, so a screen barely
taller than it is wide sets it as large as a much taller screen would while
having less room to put it in, and the tail of the card — the last row, the
footer — runs off the bottom. The art gives that room back: it keeps its full
share on a 3:4 screen and shrinks towards square.
| arg | type | description |
|---|
| m | Metrics | |
| base | number | |
columnPlate(markup: string, m: Metrics, share: number) → void
| arg | type | description |
|---|
| markup | string | |
| m | Metrics | |
| share | number | |
title(opts: { confirmLegend: string }) →
The title card. The column stands in the middle of it; the game's name sits under it.
| arg | type | description |
|---|
| opts | { confirmLegend: string } | { confirmLegend } |
controlRow(width: number, name: string, kbm: { any }, pad: { any }, touch: { any }) → void
One row of the control table: what the action is, then the same action on a
keyboard, on a pad and under a thumb.
| arg | type | description |
|---|
| width | number | |
| name | string | |
| kbm | { any } | |
| pad | { any } | |
| touch | { any } | |
howto(opts: { confirmLegend: string, padLegends: { [string]: string } }) →
The how-to-play card: what you are, what one plate asks of you, and every control on all three device families.
| arg | type | description |
|---|
| opts | { confirmLegend: string, padLegends: { [string]: string } } | { confirmLegend, padLegends } — the legends the CONNECTED pad prints, resolved rather than assumed. |
pause(opts: { confirmLegend: string, plate: number, growths: number }) →
The pause card. The still is held; the run is not over.
| arg | type | description |
|---|
| opts | { confirmLegend: string, plate: number, growths: number } | { confirmLegend, plate, growths } |
stat(caption: string, value: string, tint: string, width: number) → void
A stat cell takes a stated width rather than a flex share: three cells
sharing one row by flex did not divide it evenly here, and the third landed
outside the card.
| arg | type | description |
|---|
| caption | string | |
| value | string | |
| tint | string | |
| width | number | |
statWidth( ) → number
The width one of three stat cells gets on the card it is standing on.
clock(seconds: number) → string
| arg | type | description |
|---|
| seconds | number | |
sealed(opts: { confirmLegend: string, runTime: number }) →
The win card: plate eight purged, the still sealed, the solvent gone deep blue.
| arg | type | description |
|---|
| opts | { confirmLegend: string, runTime: number } | { confirmLegend, runTime } |
quenched(opts: { confirmLegend: string, plate: number, runTime: number }) →
The loss card: wetness reached maximum and the laboratory begins the ninth.
| arg | type | description |
|---|
| opts | { confirmLegend: string, plate: number, runTime: number } | { confirmLegend, plate, runTime } |