---
title: "HUD and hover cards"
description: "Two surfaces read the indicator's newest values and are drawn by the chart from declarations alone: a HUD card of typed tiles pinned at one of the nine chart…"
order: 40
section: "presentation"
---

<!-- source: docs/indicators/presentation/hud-and-hover-cards.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# HUD and hover cards

Two surfaces read the indicator's newest values and are drawn by the
chart from declarations alone: a HUD card of typed tiles pinned at one of
the nine chart anchors, never moving with price, and the hover card that
opens when the cursor rests on a legend entry, a HUD tile, a line, a text
mark or a label. Both are built from one kit of blocks, `tile.*` on the
HUD card and `block.*` on the hover card, and both are drawn where the
indicator runs in the browser, on the desktop chart. The third surface,
the legend entry, is on [Legend](legend.md); the three together, with a
complete module, on [What the chart shows](overview.md). The status
card, the meter, the feed, the ladder and the stats row share the HUD's
words; their looks close the page ([The other widgets](#the-other-widgets)).

## HUD cards

`render.hud(name, { position, title?, columns?, tiles, look?, ... })` is
a card at one of the nine anchors, one or two columns, its tiles from
the block kit under the `tile.*` name. It is the home for text that sits
at a viewport anchor and does not move with price: a corner readout is a
HUD card with one tile (or a `render.label` with a `position`, or a
one-cell `render.table` at the same anchor,
[Plotting](plotting.md#fixed-position-text)), a dashboard a card of
several. Its surface, type and frame are words on the declaration, and
`look` picks a whole set of them at once.

```typescript
const basis = output("basis", line, overlay, { color: "#2962ff", width: 2, label: "Basis", format: "price", tooltip: "{{label}} {{value:price}}" });
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
output("basis_chg", none);
output("state", none);
string("regime", { max_bytes: 16 });
render.hud("board", { position: "top_right", title: "Basis", columns: 2, tiles: [tile.value("Basis", "basis", { format: "price", delta: "basis_chg" }), tile.spark("RSI", "rsi", { bars: 32 }), tile.gauge("RSI level", "rsi", { min: 0, max: 100, format: "int" }), tile.pill("Regime", "regime", { color_by: "state", colors: ["#ef5350", "#26a69a"] })] });
```

- `position`: one of the nine anchors, `top_left`, `top_center`,
  `top_right`, `middle_left`, `middle_center`, `middle_right`,
  `bottom_left`, `bottom_center`, `bottom_right`; a position outside
  them is refused.
- `title`: the card's heading. `columns`: 1 or 2.
- `tiles`: the card's tiles, any of the eight kinds
  ([Blocks and tiles](#blocks-and-tiles)); a presentation name used twice
  is refused.

Every tile takes `color` or a `color_by` plus `colors` ladder (colour
words, theme tokens included, never both); absent, the tile keeps its
ink (marks in the output colour, text in the card text), or the look's
default when the card declares a `look`.

![a card of four tiles in the top-right corner: a value with its change, a sparkline, a dial and a chip](/wrun/images/wrun-hud.svg)

The card in the corner shows the basis with its change, a sparkline, a
dial and the regime chip, and follows every live tick. Resting the
cursor on a tile shows that tile again as a hover card, and a wheel over
the card still zooms the chart. The card keeps clear of the pane chrome:
the right anchors sit clear of the price axis, the bottom anchors clear
of the time axis, and `safe_area: true` keeps the top anchors clear of
the legend and the pane action bar too. It disappears with its indicator
when the indicator is hidden or removed.

### Moving a card

A viewer can move any card on their chart: drag it from anywhere on the
card (on a phone, hold its grip, then drag). Near one of the nine places
it snaps in and a label names the place ("Top right, under Market");
anywhere else it stays where it is let go. Option (Alt) drops it without
the snap, Esc cancels, and a double-click sends a moved card back. The
move is the viewer's, saved with the overlay on that chart; `position`
and `offset` stay the card's default, and the Position row on the
indicator's Style page ([The Style page](../settings/style-page.md#the-position-row))
says where the card is and puts it back. A card moved into a place that
already holds cards joins the far end of its stack.

### Surface, type and frame

Every word is optional and absent keeps today's card. Colours are
`"#rrggbb"`, `"#rrggbbaa"` or a theme token (`"theme.bg"`,
[Styling](styling.md#theme-colours)).

```typescript
output("basis", line, overlay, { color: "#2962ff", width: 2, label: "Basis", format: "price" });
render.hud("board", { position: "top_right", title: "Market", columns: 2, width: 260, offset: [12, 8], safe_area: true, background_color: "theme.bg", background_opacity: 0.85, border_color: "theme.grid", corner_radius: 10, font_family: "mono", accent_color: "theme.up", chrome: "brackets", tiles: [tile.value("Basis", "basis", { format: "price", headline: true })] });
```

- Size and place: `width` (80..1200 px; default 300, 180 at one
  column), `offset: [x, y]` (each -4096..4096 px; x moves inward from a
  left or right edge and y inward from the top or bottom, signed on the
  centre and middle anchors), `z` (an integer; cards in one anchor stack
  by it, lower nearer the anchor), `safe_area: true` (a top-left card
  starts under the legend, top-centre and top-right cards under the pane
  action bar, right-hand cards clear of the price-axis tags; `offset`
  applies from the cleared edge), `mobile: true` (the card shows on
  phones too, with pointer-free tiles and at most the larger of 140 px
  and 60 percent of its own width; absent, phones keep the chart as it
  is).
- Surface: `background_color`, `background_opacity` (0..1, default
  0.92), `background_gradient` (2 to 8 colours) with `gradient_direction`
  (`"vertical"`, the default, or `"horizontal"`), `border_color`,
  `border_width` (0..10, default 1), `border_style` (`"solid"`,
  `"dashed"`, `"dotted"`), `corner_radius` (0..32, default 12), `padding`
  (0..24), `shadow` (default true), `opacity` (0..1, the whole card).
- Type: `text_color`, `title_text_color` (default the muted ink),
  `font_family` (`"ui"`, the app font; `"mono"`; `"serif"`; `"rounded"`;
  each a system stack, nothing downloads), `title_case` and `label_case`
  (`"none"`, `"upper"`, `"lower"`, `"small_caps"`; the title is upper and
  labels are as written by default; values and numbers never change
  case), `text_glow` (0..8 px, a static halo in the text colour).
- Accent: `accent_color` (default `"theme.accent"`) inks every gauge,
  meter fill, spark and headline that declares no colour of its own;
  `accent_color_by` plus `accent_colors` (2 to 8) make the whole card
  follow an output per bar (green rising, red falling), and the pair
  excludes `accent_color`.
- Frame: `chrome` is `"card"` (today's box), `"none"` (no box, text over
  the chart; pair it with a translucent background), `"brackets"` (four
  corner marks in the accent), `"rules"` (a thick and a thin rule above
  the title), `"tag"` (a small accent block at the top left),
  `"title_bar"` (the title on a full-width bar in the accent),
  `"window"` (bevelled edges and a two-colour title bar, its buttons
  decoration only) or `"cover"` (a square tile left of the title holding
  the chart's base asset over the accent gradient); `texture` is
  `"none"` or `"scanlines"` (static 1 px lines every 3 px).

A flat card that takes its colour from the data: a dashed hairline
border, no drop shadow, the title in the full text ink, and every gauge,
meter fill, spark and headline inked by the sign of the bar's basis
change, red falling and green rising. Reach for it when the card should
read as part of the chart, not as a window over it.

```typescript
output("basis", line, overlay, { color: "#2962ff", width: 2, label: "Basis", format: "price" });
output("basis_chg", none);
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
// 0 on a bar where the basis fell, 1 where it rose: the whole card's accent follows it.
output("basis_dir", none);
render.hud("flat", {
  position: "top_right",
  title: "Basis",
  tiles: [tile.value("Basis", "basis", { format: "price", delta: "basis_chg", headline: true }), tile.gauge("RSI", "rsi", { min: 0, max: 100, format: "int" })],
  accent_color_by: "basis_dir",
  accent_colors: ["theme.down", "theme.up"],
  border_style: "dashed",
  border_width: 1,
  padding: 14,
  shadow: false,
  title_text_color: "theme.text",
});
```

An instrument readout: mono type, the title and labels in capitals, a
soft halo on the text, scanlines over the surface and bracket corners
instead of a box. Reach for it on a dark chart where the card should look
like a gauge cluster rather than a dialog.

```typescript
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
render.hud("instrument", {
  position: "bottom_left",
  title: "RSI",
  tiles: [tile.gauge("Level", "rsi", { min: 0, max: 100, format: "int", draw: "ticks" }), tile.spark("Trend", "rsi", { bars: 32 })],
  text_color: "#7DF9FF",
  font_family: "mono",
  title_case: "upper",
  label_case: "upper",
  text_glow: 4,
  texture: "scanlines",
  chrome: "brackets",
});
```

A pocket card that shows on phones too: one column, a fixed 160 px
width, a small inset, no shadow, and `safe_area` so it starts under the
legend. Absent `mobile`, a phone keeps the chart as it is and the card
stays a desktop readout.

```typescript
output("basis", line, overlay, { color: "#2962ff", width: 2, label: "Basis", format: "price" });
output("basis_chg", none);
render.hud("pocket", {
  position: "top_left",
  columns: 1,
  tiles: [tile.value("Basis", "basis", { format: "price", delta: "basis_chg" })],
  mobile: true,
  safe_area: true,
  width: 160,
  padding: 8,
  shadow: false,
});
```

Nothing on a card animates, blurs or loads from outside the chart, and
no script ever writes CSS: a shared indicator cannot paint over the chart
with anything the words above do not describe.

### Looks

`look` stands for a whole row of the table below; any word declared
beside it wins, so a look is a starting point, not a cage. Absent, the
card keeps today's look untouched. The viewer can switch the look on the
indicator's Style page ([The Style page](../settings/style-page.md)),
where every HUD gets a Look row with "As made" first, unless the file
bound the look to a setting (below).

| look | surface | type | draws | chrome | palette |
| --- | --- | --- | --- | --- | --- |
| `default` | the defaults above | the defaults above | arc, line, bar, capsule | card | adaptive |
| `glass` | background `#1c1c1ee6` (`#f2f2f7e6` on a light chart), border `#ffffff29` 1 px, corner radius 22, padding 16, shadow | `rounded`, no title, headline 34 | gauge ring, spark area, meter bar, pill dot | card | adaptive |
| `stage` | background gradient `#2c6b46`, `#1b3a28`, `#121212`, `#121212` vertical, corner radius 10, padding 14 | `ui`, headline 30 bold, accent `#1ed760` | gauge bar, meter split, spark line, pill text | cover | home |
| `signal` | background `#000000`, border `#1d1d1d` 1 px, corner radius 14 | `ui`, headline 38, no title | spark line (height 78, a zero line), meter split | card | home; the whole card's accent follows `accent_color_by` |
| `terminal` | background `#000000`, border `#3a3a3a` 1 px, corner radius 0, padding 6 | `mono`, title and labels upper, label ink `#F7A21B`, text `#ffffff` | gauge text, spark text, meter text, pill text | title_bar (bar `#F7A21B`, title ink `#000000`) | home |
| `broadsheet` | background `#F6F1E7`, text `#141414`, corner radius 0, padding 14 | `serif`, title small_caps, headline 20 | spark dots, meter bar, pill text, rows with dot leaders | rules | home |
| `chart_desk` | background `#ffffff`, text `#121212`, corner radius 0, padding 14 | `ui`, no title | spark bars (height 96, the baseline at the meter mark), pill text | tag (`#E3120B`) | home |
| `dial` | background `#EBE8E1`, text `#1d1d1b`, corner radius 16, padding 14, accent `#E05A1E` | `ui`, no title | gauge dial, meter scale, pill led | card | home |
| `grid` | background `#F2F1EC`, text `#111111`, corner radius 0, padding 14, accent `#E30613` | `ui`, headline 44 bold | spark line (ink), meter split (ink and accent), pill text | rules | home |
| `cockpit` | background `#030e12c2`, corner radius 0, padding 12, text `#7DF9FF`, accent `#FFB000` | `mono`, title and labels upper, text glow 6 | gauge ticks, meter segments, spark line, pill text | brackets | home |
| `phosphor` | background `#05160B`, text `#3BFF73`, corner radius 12, padding 12 | `mono`, text glow 5 | gauge text, spark text, meter text, pill text | none | home; texture scanlines |
| `classic` | background `#C0C0C0`, text `#000000`, corner radius 0, padding 8 | `ui` | gauge blocks, meter blocks, spark line, pill dot | window (bevels, a title bar from `#000080` to `#1084D0`) | home |

A "home" look keeps its paper on a dark and a light chart alike (a
printed object does not invert); an "adaptive" look resolves its theme
tokens per theme. The "draws" column is each tile kind's `draw` default
under that look ([Blocks and tiles](#blocks-and-tiles)); a tile that
declares its own `draw` keeps it.

To let the trader pick among looks you chose, bind `look` to a setting:
`look: "@card_look"` names a `param.choice` whose every choice is one of
the twelve looks. The build writes the setting's default look on the
card, and the card takes the picked look the moment the setting changes.
That setting's row is the look's one control: the Style page draws no
Look row for a bound card (its Position row stays), and any word declared
beside `look` still wins over the picked look.

```typescript
param.choice("card_look", ["phosphor", "glass", "terminal"], "phosphor", { label: "Card look" });
render.hud("desk", { position: "top_right", title: "RSI", look: "@card_look", tiles: [tile.gauge("Level", "rsi", { min: 0, max: 100 })] });
```

Start from a look and change only what you must. `phosphor` with the
scanlines off, a softer glow, a card frame around it and an upper-case
title keeps its green-on-black type and its text-drawn tiles; every word
beside `look` wins.

```typescript
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
string("regime", { max_bytes: 16 });
render.hud("desk", {
  position: "top_right",
  title: "RSI",
  tiles: [tile.gauge("Level", "rsi", { min: 0, max: 100, format: "int" }), tile.pill("Regime", "regime")],
  look: "phosphor",
  texture: "none",
  text_glow: 2,
  chrome: "card",
  title_case: "upper",
});
```

The rules, each refused by name when broken:

- `columns` takes 1 or 2; a `position` outside the nine anchors is
  refused.
- `look`, `chrome`, `texture`, `title_case`, `label_case`, `font_family`
  and a tile's `draw` outside their words are refused by name, with the
  list; so are `width` outside 80..1200, `offset` outside -4096..4096,
  `corner_radius` outside 0..32 and `text_glow` outside 0..8.
- `look: "@name"` naming no setting, or a setting that is not a
  `param.choice` over looks only, is refused by name (`render.hud 'desk'
  look references "@ink", which is not a param.choice over
  default|glass|...`); `look` is the card's only word a setting can
  drive, so a card colour stays a literal.
- `accent_color` beside `accent_color_by` is refused, and the ladder
  needs its `accent_colors`.
- One headline tile per card: a second `headline: true` is refused.
- A corner readout is a HUD card, a `render.label` with a `position`, or
  a one-cell table; a label placed by `x` and `y` sits at its price.
- A presentation name used twice is refused.
- The HUD is drawn where the indicator runs in the browser; a phone
  shows a card only when it declares `mobile: true`.

## Hover cards

`hover: [...]` on an output, `render.text` or `render.label`, or
`hover(handle, [...])` at the top level, is the card the cursor opens,
from the block kit under the `block.*` name: one card per output, either
form. Without one the surface shows the output's `tooltip`, else its
label and value ([Labels, tooltips, badges](labels-and-tooltips.md)).
`badges` names another output, a ladder whose labels become chips on the
card.

```typescript
const basis = output("basis", line, overlay, { color: "#2962ff", width: 2, label: "Basis", format: "price", tooltip: "{{label}} {{value:price}}" });
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
output("basis_chg", none);
string("regime", { max_bytes: 16 });
hover(basis, [block.value("Basis", "basis", { format: "price", delta: "basis_chg" }), block.rows([["RSI", "rsi", "0.0"], ["Regime", "regime"]]), block.meter("RSI", "rsi", { min: 0, max: 100, marks: [30, 70] }), block.chips("regime")]);
```

![the card a tile opens under the cursor: a value block with its delta](/wrun/images/wrun-hover-card.svg)

Where the card appears:

- a line: the output whose value at the cursor's bar sits nearest the
  cursor. On the price pane the line has to be within 12 pixels of the
  cursor; in an indicator's own lower pane the card follows the cursor's
  bar anywhere in the pane (a histogram answers over its whole column);
- a legend entry: the value under the cursor names its output;
- a HUD tile: the tile is shown again as a hover card;
- a text mark or a label: the `hover` list on the `render.text` or
  `render.label` that drew it
  ([Plotting](plotting.md#text-labels-tables-strips-tints)).

Around the blocks: `hint` on the output, or on a value block, adds its
words to the card; `badges` adds the chips of the ladder it names; the
card's edge takes the output's color, the current rung of its `color_by`
ladder when it has one, else its static `color`.

The rules, each refused by name when broken:

- `hover` takes an output handle first (bind a handle with a top-level
  `const h = output(...)` and pass `h`), not a string literal.
- One hover per output: inline or top-level, never both.
- A hover needs at least one block.
- `badges` takes ANOTHER output; an output badging itself is refused.
- A table or a drawing shows no card.
- In a `tooltip` or a `value` template an unknown name stays as written.

## Blocks and tiles

The block kit is one vocabulary with two names: `tile.*` builds the
tiles of a `render.hud(...)` card, `block.*` the blocks of a
`hover(...)` card, the same constructors under their other name. A
constructor lives inside the card's literal only and is erased before the
compiler. `value`, `spark`, `gauge` and `meter` read a numeric output,
`pill` a string slot, `chips` either, `rows` a list of
`[label, output or slot, format?]` lines, and `rings` one to three
outputs each with its own range. Every block reads the newest
row, so a data-only output written per bar is the way to carry a delta or
a regime onto either card.

| Constructor | Draws | Options |
| --- | --- | --- |
| `value(label, output, { format?, delta?, tooltip?, hint?, color?, color_by?, colors?, headline?, font_size? })` | the output's newest value under its label, a signed delta from the `delta` output beside it; `headline: true` spans every column of a HUD card at `font_size` (default 22) with a hairline under it | `format` one of the ten; `delta` an output; `tooltip` and `hint` words; `font_size` 6..64 (default 16) |
| `spark(label, output, { bars?, color?, color_by?, colors?, draw?, height? })` | a sparkline of the output's last bars (64 at most) | `bars` a positive integer; `draw` `line` (the default), `area`, `dots`, `bars` or `text`; `height` 16..120 px (default 28) |
| `gauge(label, output, { min, max, format?, color?, color_by?, colors?, draw? })` | a dial between `min` and `max` | `min` and `max` required, `min` below `max`; `format` one of the ten; `draw` `arc` (the default), `ring`, `dial`, `ticks`, `bar`, `blocks` or `text` |
| `meter(label, output, { min, max, marks?, color?, color_by?, colors?, draw?, height? })` | a bar between `min` and `max` with marks | `min` and `max` required, `min` below `max`; `marks` a list of numbers; `draw` `bar` (the default), `split`, `blocks`, `segments`, `scale` or `text`; `height` 16..120 px (default 4) |
| `pill(label, slot, { color?, color_by?, colors?, draw?, headline?, font_size? })` | the slot's words as a chip, colored by a ladder | `draw` `capsule` (the default), `dot`, `led` or `text`; `headline` and `font_size` as on `value` (default 10, 22 as a headline) |
| `rows(label?, [[label, output or slot, format?], ...], { color?, color_by?, colors?, leader? })` | rows of label and value | the caption may be left out: the rows array alone; the options object after it is optional too; `leader` `none` (the default) or `dots`, a dotted rule between each key and its value |
| `chips(slot or ladder output)` | the slot's words, or the ladder's labels, as chips | none |
| `rings(label?, [[label, output, { min, max, color? }], ...], { height? })` | one to three concentric rings, outermost first, each the share of its own `min..max` its output holds, with a legend beside them (dot, label, value) | 1..3 entries, `min` below `max`; `color` a colour word (default the accent at full, 70 and 45 percent strength); `height` 16..120 px (default 86) |

The ten formats are `price`, `%`, `si`, `int`, `0`, `0.0`, `0.00`,
`0.000`, `usd` (`$1.2M`) and `auto` (six significant digits with `,`
thousands) ([Styling](styling.md#placement-per-output)).

Colour: every block takes `color` (a colour word: `"#rrggbb"`,
`"#rrggbbaa"` or a theme token such as `"theme.up"`) or a `color_by`
output plus a `colors` ladder; with both, the ladder wins and `color` is
the fallback where the index is not finite. Absent, the block keeps its
ink: marks in the output's colour, text in the card's text colour, or the
look's default on a HUD card that declares a `look`. A `rows` block's
colour inks every value in it.

What the `draw` words mean: `ring` is one circular track; `dial` a
printed dial with ticks every 5 and a needle over 270 degrees; `ticks` a
half arc with a tick every 10 and the value in the middle; `bar` a track
with a knob; `blocks` a segmented progress bar; `text` block characters
in the card's font (a gauge or meter as `████▌·····` over 10 cells, a
spark as `▁▂▃▄▅▆▇█` over at most 16 cells); `area` the line plus a faint
fill and an end dot; `dots` a stippled line; `bars` one column per bar
from the first meter mark or zero; `split` the fill in the accent and the
rest in `theme.down`, with labels at both ends; `segments` 20 cells;
`scale` a tuning scale with a pointer; `capsule` today's tinted pill;
`dot` the word in its colour after a small dot; `led` a lit lamp before
the word; and `text` on a pill the word alone in its colour. A hover card
keeps today's drawings unless the block declares `draw`.

A reading column on a HUD card: an area spark taller than the default, a
split meter with its marks, and rows with dotted leaders between each
label and its value. Reach for the taller spark when the card has the
room, and for the leaders when a label sits far from its value.

```typescript
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
string("regime", { max_bytes: 16 });
render.hud("column", {
  position: "middle_right",
  columns: 1,
  tiles: [
    tile.spark("RSI", "rsi", { bars: 48, draw: "area", height: 48 }),
    tile.meter("Level", "rsi", { min: 0, max: 100, marks: [30, 70], draw: "split", height: 16 }),
    tile.rows("Readout", [["RSI", "rsi", "0.0"], ["Regime", "regime"]], { leader: "dots" }),
  ],
});
```

An options-desk tile: three rings, outermost first, the calls' share of
open interest, the puts' share and the IV rank, each over its own range
and in its own ink, with the legend beside them. Reach for rings when
three shares belong together and a bar each would read as three unrelated
meters.

```typescript
// Each share is 0 to 1 and the IV rank 0 to 100: a ring fills by its output's place in its own range.
output("calls_share", none);
output("puts_share", none);
output("iv_rank", none);
render.hud("options_desk", {
  position: "top_right",
  title: "Options desk",
  columns: 1,
  tiles: [
    tile.rings("Positioning", [
      ["Calls", "calls_share", { min: 0, max: 1, color: "theme.up" }],
      ["Puts", "puts_share", { min: 0, max: 1, color: "theme.down" }],
      ["IV rank", "iv_rank", { min: 0, max: 100, color: "theme.accent" }],
    ], { height: 96 }),
  ],
});
```

Outputs and slots are named by a bound handle or by their name as a
string: `tile.value("Basis", basis, ...)` with `const basis =
output("basis", ...)` above it, or `tile.value("Basis", "basis", ...)`.
The sheet records the name either way.

The rules, each refused by name when broken:

- A card takes block constructors (`block.<kind>(...)` or
  `tile.<kind>(...)`), kinds `value`, `spark`, `gauge`, `pill`, `rows`,
  `meter`, `chips`, `rings`, and needs at least one.
- `gauge` and `meter` are refused without `min` and `max`, and need
  `min` below `max`; so does each ring.
- `bars` takes a positive integer literal; `colors` needs at least one
  color.
- A `pill` reads a string slot, `chips` a slot or a ladder output, the
  numeric kinds an output; a block whose output or slot does not exist is
  refused.
- `draw` on a `value`, `rows` or `chips` block, or a word outside the
  kind's own list, is refused by name ("spark draw must be one of
  ..."); `headline` on a kind other than `value` or `pill`, two headline
  tiles in one card, and `height` on a kind other than `spark`, `meter`
  or `rings` are refused too.
- A spark's `color` or a pill's `colors` is a literal (a theme token
  counts): a setting paints an output, a renderer or a legend entry only,
  never a block.
- A block has no size, alignment or color of its own beyond these
  options: the chart draws it.

## HUD starters

Six complete cards to start from, under **HUDs** in the editor's template
picker, each in one of the [looks](#looks). Each answers one question first,
as its headline, and reads the numbers behind it underneath:

- [Market HUD](../cookbook/market-hud.md), glass: the trend, then RSI, the
  taker buy share and the last closed bar's volume on three rings, ATR
  and the open-interest change in coins.
- [Decision board](../cookbook/decision-board.md), broadsheet: a headline
  counting the six factors, the net score as a stippled line and the
  factors in rows with dotted leaders.
- [Order flow HUD](../cookbook/order-flow-hud.md), chart desk: who is
  pushing price, each bar's buy share as bars from the 50 mark, the
  window's buy share, the net delta and the liquidations.
- [Order book HUD](../cookbook/order-book-hud.md), cockpit: the heavier side
  of the book, the bid share in segments, the dollars resting by distance
  from the mid and the biggest walls.
- [Session HUD](../cookbook/session-hud.md), stage: the session trading
  now, how much of it has passed, the session up next, the day's range used
  and the distance from the VWAP and the prior day's high and low.
- [Gamma map](../cookbook/gamma-map.md), terminal: dealer gamma at spot,
  net GEX, spot against the gamma flip and the four levels as numbered
  rows.

## The other widgets

The HUD card is one of six widgets that share a vocabulary. The status
card, the feed and the meter (`draw.card`, `draw.feed`, `draw.meter`)
take the HUD's surface words plus a chrome of their own, the ladder
(`draw.ladder`) its own short list, and the stats row under the price
pane (`render.stats_row`) a colour ladder and a priority. Their
contracts, with the full tables, are on
[Cards, frames and panels](cards-frames-panels.md#status-cards) and
[Plotting](plotting.md#text-labels-tables-strips-tints); this section
shows them as looks. Every word is optional and absent keeps today's
widget.

A status card without the state chrome: no state word, no stripe, no
hide or collapse glyphs until the viewer hovers it, painted over your drawings, a hairline in the
grid colour between the headline and the rows, a small medium-weight
title and muted labels. Reach for it when the card is a dashboard, not an
alarm.

```typescript
output("pnl", none);
output("trades", none);
output("win_rate", none);
draw.card("session", {
  title: "{{symbol}} session",
  anchor: "top_left",
  headline: { output: "pnl", format: "usd", signed: true },
  rows: [
    { label: "Trades", value: { output: "trades", format: "int" } },
    { label: "Win rate", value: { output: "win_rate", format: "%", decimals: 1 } },
  ],
  show_state: false,
  stripe: false,
  controls: "none",
  above_drawings: true,
  rule_color: "theme.grid",
  title_font_size: 11,
  title_font_weight: "medium",
  label_text_color: "theme.muted",
});
```

An alarm card: the state word and the stripe on, the four state inks
declared, the hide and collapse glyphs always visible, a hairline under
the headline, a clock row and a countdown to the next close. Reach for it
when the card is the indicator's alarm panel.

```typescript
output("spread", none);
// 0 ok, 1 armed, 2 fired, 3 error: the card's state word and inks follow it.
output("alert_state", none);
output("close_at_ms", none);
draw.card("alarm", {
  title: "Spread alarm",
  anchor: "top_right",
  state_by: "alert_state",
  headline: { output: "spread", format: "0.00" },
  rows: [
    { label: "Now", clock: true },
    { label: "Next close", countdown_to: { output: "close_at_ms" } },
  ],
  show_state: true,
  stripe: true,
  controls: "always",
  state_colors: ["theme.muted", "theme.accent", "theme.up", "theme.down"],
  rule: true,
});
```

A meter with a thicker bar on a grey track, and a feed that prints each
line's clock time, both showing their hide and collapse glyphs on hover
only. Reach for the thicker bar when the meter stands alone in a corner;
`time_format: "none"` drops the time column when the times add nothing.

```typescript
output("rsi_fraction", none);
string("rsi_text", { max_bytes: 8 });
const events = frame("events", { max_bytes: 2048 });
draw.meter({ name: "rsi_meter", label: "RSI", fraction: { output: "rsi_fraction" }, ramp: ["#3b82f6", "#64748b", "#ef4444"], text: { slot: "rsi_text" }, anchor: "top_left", bar_height: 8, track_color: "theme.grid", controls: "hover" });
draw.feed({ name: "rsi_events", frame: events, anchor: "bottom_left", title: "Crossings", time_format: "HH:mm", controls: "hover", stripe: false });
```

The feed's frame as the script writes it: one to fifty `lines` of a time
in milliseconds, a text of at most 80 characters and an optional colour,
newest first, and a per-run `title` that replaces the declared one.

```json
{
  "title": "Crossings",
  "lines": [
    [1759536000000, "RSI crossed above 70", "#ef4444"],
    [1759532400000, "RSI crossed below 30", "#3b82f6"]
  ]
}
```

A slim mono ladder with its row labels on and the divider line in the
accent colour. The divider itself comes with the frame (its `divider`, a
label and a price); `divider_color` inks it.

```typescript
output("close_line", line, overlay, { color: "#94a3b8", label: "Close" });
const rows = frame("rows", { max_bytes: 2048 });
draw.ladder({ name: "volume_by_price", frame: rows, side: "right", width_frac: 0.12, labels: true, font_family: "mono", font_size: 10, color: "#64748b", divider_color: "theme.accent" });
```

The ladder's frame: one to 256 `rows` of a price, a size, the bar's
length from 0 to 1 and an optional colour, and a `divider` that marks one
price. A divider in the frame replaces a declared one.

```json
{
  "rows": [
    [63950, 1820, 0.62, "#64748b"],
    [64000, 2940, 1, "#f59e0b"],
    [64050, 1210, 0.41, "#64748b"]
  ],
  "divider": { "label": "Last", "price": 64010 }
}
```

Two rows in the statistics strip: a diverging row whose bull and bear
inks are the `colors` pair, kept readable longest as bars narrow
(`priority: 1`), and a row inked per bar by a packed colour the script
writes (`color_packed_by`, [Colors](../functions/colors-kit.md)), the
first to lose its text (`priority: 3`).

```typescript
output("basis_chg", none);
output("rsi", line, lower, { color: "#8b5cf6", label: "RSI", format: "0.0" });
// A packed colour per bar, written with toPacked(...) from the colors kit.
output("rsi_ink", none);
render.stats_row("basis_row", { output: "basis_chg", title: "Basis chg", format: "price", polarity: "diverging", colors: ["theme.up", "theme.down"], priority: 1 });
render.stats_row("rsi_row", { output: "rsi", title: "RSI", format: "0.0", color_packed_by: "rsi_ink", priority: 3 });
```

## Related

- [Cards, frames and panels](cards-frames-panels.md#status-cards): the
  status card, the ladder, the feed and the meter in full
- [What the chart shows](overview.md): the three surfaces together, with
  a complete module
- [Legend](legend.md): the legend entry, and `color_by` on it
- [Labels, tooltips, badges](labels-and-tooltips.md): the `tooltip`
  template a card falls back to, and `badges`
- [Plotting](plotting.md): the outputs, string slots and renderers a card
  reads
