ui/art — the look of SLAG SAINT, in one place.
Every screen in the game is built out of these: the palette translated into
CSS strings once, the type ramp bound to real faces, the small style helpers
that give the whole UI one spacing rhythm, and the inline SVG that puts art
on a screen instead of words alone.
The frame is 1080 x 1440 — PORTRAIT. Everything here stacks down the tall
axis: nothing is a row of things spanning the width, and the only thing
allowed to reach edge to edge is a hairline.
local Art = require("ui.art")
Art.label("EMBERWARD", Art.T.label(Art.C.boneDim))
rgba(hex: string, alpha: number) → string
`rgba(r,g,b,a)` from a `#rrggbb` string. Used everywhere a colour needs to
sit at partial strength — a scrim, a glow, a hairline.
| arg | type | description |
|---|
| hex | string | |
| alpha | number | |
mix(a: string, b: string, t: number) → string
Linear blend between two `#rrggbb` strings. The phase grade travels the
UI by mixing the phase colour into the chrome.
| arg | type | description |
|---|
| a | string | |
| b | string | |
| t | number | |
display(color: string?, size: number?) → any
| arg | type | description |
|---|
| color | string? | |
| size | number? | |
heading(color: string?, size: number?) → any
| arg | type | description |
|---|
| color | string? | |
| size | number? | |
prose(color: string?, size: number?) → any
| arg | type | description |
|---|
| color | string? | |
| size | number? | |
quote(color: string?, size: number?) → any
| arg | type | description |
|---|
| color | string? | |
| size | number? | |
label(color: string?, size: number?) → any
| arg | type | description |
|---|
| color | string? | |
| size | number? | |
readout(color: string?, size: number?) → any
| arg | type | description |
|---|
| color | string? | |
| size | number? | |
merge(base: any, extra: any?) → any
Node helpers
| arg | type | description |
|---|
| base | any | |
| extra | any? | |
label(text: string, style: any?) → any
| arg | type | description |
|---|
| text | string | |
| style | any? | |
para(text: string, style: any?, width: any?) → any
A wrapping paragraph. The WIDTH belongs on the container, never on the
label — a label carrying both `wrap` and a width collapses to one glyph
per line.
| arg | type | description |
|---|
| text | string | |
| style | any? | |
| width | any? | |
lines(list: { string }, style: any?, gap: number?) → any
A block of centred prose, one node per line. A wrapping label lays its
lines out from the left whatever `textAlign` says, so anything that has to
read as centred is broken into lines here and centred by the column.
| arg | type | description |
|---|
| list | { string } | |
| style | any? | |
| gap | number? | |
svg(markup: string, style: any?) → any
| arg | type | description |
|---|
| markup | string | |
| style | any? | |
col(style: any?, children: any) → any
| arg | type | description |
|---|
| style | any? | |
| children | any | |
row(style: any?, children: any) → any
| arg | type | description |
|---|
| style | any? | |
| children | any | |
abs(l: any, t: any, w: any, h: any, extra: any?) → any
An absolutely-positioned box. `l`/`t` may be nil to pin from the other
edge with `extra.right` / `extra.bottom`.
| arg | type | description |
|---|
| l | any | |
| t | any | |
| w | any | |
| h | any | |
| extra | any? | |
setStill(value: boolean) → void
Build trees without animation (the clock is stopped) or with it.
| arg | type | description |
|---|
| value | boolean | |
enter(from: any, duration: number?, delay: number?) → any
One-shot entrance. `fill = "both"` holds the first frame through `delay`,
so staggering delays across siblings makes them arrive in sequence.
| arg | type | description |
|---|
| from | any | |
| duration | number? | |
| delay | number? | |
rise(delay: number?, distance: number?) → any
The house entrance: rise and fade in.
| arg | type | description |
|---|
| delay | number? | |
| distance | number? | |
settle(delay: number?) → any
The house entrance for a display line: settle down out of a slight scale.
| arg | type | description |
|---|
| delay | number? | |
loop(keyframes: any, duration: number, easing: string?, extra: any?) → any
An infinite idle loop.
| arg | type | description |
|---|
| keyframes | any | |
| duration | number | |
| easing | string? | |
| extra | any? | |
breathe(low: number, high: number, duration: number, delay: number?) → any
A slow breath on opacity — for a prompt that must be noticed and not
shouted at.
| arg | type | description |
|---|
| low | number | |
| high | number | |
| duration | number | |
| delay | number? | |
plate(extra: any?) → any
Surfaces
A raised plate. On a near-black UI depth comes from a step in lightness
and a lit top edge, not from a black drop shadow.
| arg | type | description |
|---|
| extra | any? | |
rule(color: string?, alpha: number?, extra: any?) → any
A hairline. The one element allowed to span the frame.
| arg | type | description |
|---|
| color | string? | |
| alpha | number? | |
| extra | any? | |
keyCap(glyph: string, wide: boolean?) → any
A key cap. `wide` for SPACE / SHIFT.
| arg | type | description |
|---|
| glyph | string | |
| wide | boolean? | |
meter(w: any, h: number, frac: number, fill: string, opts: any?) → any
A horizontal meter. `w` wide, `h` px tall, `frac` 0..1 filled in `fill`,
with an optional slower `ghost` fraction behind it (the damage chip that
drains a moment after a hit), tick marks, and an optional outer glow.
| arg | type | description |
|---|
| w | any | |
| h | number | |
| frac | number | |
| fill | string | |
| opts | any? | |
paint(markup: string, vars: { [string]: string }) → string
Art — inline SVG. Everything below is drawn, not typed.
Written as long strings with `$name` slots so the markup stays readable;
`paint` fills the slots. (An interpolated `\`...\`` string in Luau cannot
carry a raw newline, which is why these are not written that way.)
| arg | type | description |
|---|
| markup | string | |
| vars | { [string]: string } | |
bell(stroke: string, fill: string?) → string
The saint's head: a cathedral bell with a clapper. The game's mark.
| arg | type | description |
|---|
| stroke | string | |
| fill | string? | |
chain(stroke: string) → string
A chain-link pair, used as a divider mark and a section rule.
| arg | type | description |
|---|
| stroke | string | |
sigil(stroke: string, accent: string) → string
The order's sigil: a quenching droplet inside a ring.
| arg | type | description |
|---|
| stroke | string | |
| accent | string | |
blade(metal: string, wrap: string) → string
The Emberward's quenching blade, point down.
| arg | type | description |
|---|
| metal | string | |
| wrap | string | |
saint(body: string, seam: string, rim: string?) → string
The saint, kneeling: the silhouette the title screen is built on. Bell
head, shuttered furnace chest, an anvil on a chain for a right arm.
| arg | type | description |
|---|
| body | string | |
| seam | string | |
| rim | string? | |
mouse(stroke: string, hot: string, side: string) → string
A mouse, for the how-to card. `side` highlights "left" or "right".
| arg | type | description |
|---|
| stroke | string | |
| hot | string | |
| side | string | |
pip(color: string, state: string) → string
A phase pip: hollow before, solid now, struck through once passed.
| arg | type | description |
|---|
| color | string | |
| state | string | |
motes(color: string, seed: number) → string
A drifting field of motes — steam early, embers late. Cheap depth.
| arg | type | description |
|---|
| color | string | |
| seed | number | |
menuItem(id: string, text: string, note: string, delay: number, tone: string?) → any
Composed pieces used on more than one screen
A menu choice. The whole plate is the click target — no invisible button
laid over a box.
| arg | type | description |
|---|
| id | string | |
| text | string | |
| note | string | |
| delay | number | |
| tone | string? | |
escChip(id: string, text: string, tone: string?, nodeId: string?) → any
The menu affordance. A REAL button, because a button is the widget that
carries a keyboard `shortcut` — so the same chip answers a click and a
key, without an input map this slice does not own.
The chip carries a `shortcut` of P, but the CLICK is the affordance the
label promises, because the key is the half that could not be proved here:
a simulated key press reaches the engine's own input (Escape flips
`engine.paused`) and never reaches a widget — a focused `input` takes no
text from one either — so a keyboard shortcut cannot be verified from
inside the engine. Escape is spent on the engine's gameplay pause in any
case. The chip therefore says MENU and means the plate.
| arg | type | description |
|---|
| id | string | |
| text | string | |
| tone | string? | |
| nodeId | string? | |
eyebrow(text: string, color: string?, width: any?) → any
An eyebrow: a tiny letterspaced line with a rule running off it. Sets a
section without spending a heading.
| arg | type | description |
|---|
| text | string | |
| color | string? | |
| width | any? | |
chainRule(width: any, color: string?) → any
A rule broken in the middle by a chain link — the order's mark, used as a
divider under a title.
| arg | type | description |
|---|
| width | any | |
| color | string? | |
vignette(strength: number?) → any
The vignette that closes every screen's corners.
| arg | type | description |
|---|
| strength | number? | |
moteField(color: string, opacity: number, seed: number, duration: number) → any
A drifting mote field, full-bleed, behind everything on a card.
| arg | type | description |
|---|
| color | string | |
| opacity | number | |
| seed | number | |
| duration | number | |