---
title: "Styling"
description: "How outputs draw. The rule that keeps a wrun indicator honest: outputs are numbers, some numbers are decisions, and declarations map decisions to looks.…"
order: 34
section: "presentation"
---

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

# Styling

How outputs draw. The rule that keeps a wrun indicator honest: outputs are
numbers, some numbers are decisions, and declarations map decisions to
looks. Styling lives in the `output(...)` options and in the `range()`,
`box()` and `segment()` declarations at the top of the file, never in the
module's code: the chart reads the declaration and draws it, and **Run**
records it in the sheet it derives. A static look is one option per
output; a look that changes per bar is a ladder, a data-only output whose
floored value indexes a palette. What the user may change comes from the
same declarations: a `param.color` bound to an output by name, and
[the Style page](../settings/style-page.md) the dialog derives from the
outputs. This page is the wrun vocabulary for each look (size, tooltips,
z-order, palettes, gradients), and which looks have no form.

Every part a wrun indicator draws can be styled; for a part-by-part walk
through one real indicator, start with [Style anything](style-anything.md)
and come back here for the vocabulary.

## Placement, per output

- `plot`: `line`, `bar`, `area`, `histogram`, `candle`, `shape`, `scatter`,
  or `none` for a data-only output: computed, never drawn, the building
  block for gates and palettes below. On the chart `bar` and `histogram`
  both draw as columns and `shape` and `scatter` as marks; `candle` takes
  four consecutive outputs (open, high, low, close) that draw as one
  candle ([Plotting](plotting.md)).
- `panel`: `overlay` (the price chart) or `lower` (a pane below it). Put
  small-magnitude series (probabilities, oscillators) in `lower`; an
  odds-scale line on the price axis hugs the axis floor. The `lower`
  outputs of an indicator that also draws on price share one pane below
  the chart unless `pane: "<name>"` sends them to a pane declared with
  `pane(name, { title, place, height_frac, scale, invert, padding, min,
  max, format })`: up to four named panes, below the chart or, with
  `place: "price"`, over the candles on a hidden scale of their own
  ([Panes](plotting.md#panes)).
- `z`: the paint order inside a pane, an integer -10..10 on an output, a
  `range`, a `fill` or a `box`. Absent, the engine's layer applies: tints
  0, fills 1, lines, columns, bands, marks and boxes 2, text 3; ties keep
  declaration order.
- Numbers: `format` names how the output's value prints in the legend,
  the hover card and its axis tag: `price` (the chart's price digits, or
  a lower pane's own step digits on its axis), `%` (the value with a
  percent sign), `si` (K, M, B, T from 1,000), `int`, `0`, `0.0`, `0.00`,
  `0.000` (fixed digits), `usd` (`$` with two decimals under 1,000, else
  `$1.2K`, `$1.2M`, `$1.2B`, `$1.2T` at one decimal, the sign before the
  `$`) and `auto` (six significant digits with `,` thousands). `decimals`
  (0..8) overrides the word's digits, `signed: true` puts a `+` on
  positives, and `unit` prints after the value once `format` is declared
  (an output's `unit` is not bounded, as it never was; keep it short, a
  host may cut a long one); `decimals` and `signed` without `format` are
  refused ("decimals needs format"). A `unit` on its own is recorded in
  the sheet as before and never printed. Without `format` the
  chart formats numbers itself: a lower pane prints K, M and B from 1,000
  and its own precision below that, a value on the price pane prints at
  the chart's price precision. A pane's axis ticks take the same four
  words on `pane(...)`.

## Static style, per output

One option per output, read from the declaration:

```typescript
output("overbought", line, lower, { color: "#ff0000", width: 1, line_style: "dashed", description: "The 70 level, written on every bar" });
output("delta", histogram, lower, { color_by: "delta_sign", colors: ["#ef5350", "#26a69a"], description: "Fast minus slow as columns, colored by sign" });
// Red while the close is under the average, green (and three times as wide) while it is over.
output("mid", line, overlay, { color_by: "regime", colors: ["#ef4444", "#22c55e"], width_by: "regime", widths: [1, 3] });
// The band between the two edges: dotted edge lines and a slate interior.
range("band_hi", "band_lo", { color: "#94a3b8", edge_width: 1, edge_line_style: "dotted" });
// A curved line split around zero, shaded below it; a fill between the two edges with no edge lines of its own.
output("osc", line, lower, { smooth: true, split: "level", up_color: "theme.up", down_color: "theme.down", split_fill: true });
fill("band_hi", "band_lo", { color: "theme.accent", opacity: 0.15 });
// The 70 level as a guide: one full-width line, labelled on the line, out of the legend.
output("upper", line, lower, { role: "guide", align: "right", color: "theme.muted" });
```

| Look | Option | What it draws |
| --- | --- | --- |
| a color | `color`, or a `colors` palette | the line, column or mark color |
| a halo | `glow` | a soft halo in pixels on a line, an area, columns, candles, marks and scatter dots, and on a `render.shape` mark |
| rounded columns | `corner` | rounded column corners on a histogram, in pixels |
| transparency | `opacity` | 0..1 on an output, fading every colour it draws: a ladder's rungs, a packed colour, a column's sign pair, a candle's body, border and wick, marks and gradient stops; per box, per fill, per handle and per card too |
| thickness | `width` | the line width; `widths` the per-bar ladder, 1..10 entries, each 0.5..20; on columns a share of the bar slot, `widths` entries from 0.05 (1 = the columns touch) |
| a dash | `line_style` | `solid`, `dashed`, `dotted`; lines and areas take the dash, columns do not |
| curves | `smooth` | a line, or an area's edge, bent into curves |
| steps | `step` | a line as horizontal then vertical segments (a trailing stop, a funding step); never beside `smooth` |
| a stroke shaded by height | `gradient` on a line | 2 to 8 colours from the top of the pane to the bottom; never beside `color_by` or `split` |
| two colours around a level or by slope | `split`, `up_color`, `down_color`, `split_level`, `split_fill`, `split_base` | `"level"`: `up_color` above `split_level` (default 0) and `down_color` below, `split_fill` shading down to `split_base`; `"slope"`: `up_color` while rising, `down_color` while falling |
| an area's fill | `fill_color`, `fill_opacity`, `fill_gradient`, `gradient_mode`, `fill_color_by` plus `fill_colors`, `fill_color_packed_by` | the fill's own colour (default the line's) at `fill_opacity` (0.4 flat, 1 on stops), or stops top to bottom over the pane, the fill or each column, or a colour per bar from its own ladder |
| a fill between two lines | `fill(a, b, { color, opacity, opacity_by, color_by, colors, color_packed_by, z })` | the interior only, no edge lines, between two drawn outputs on one pane, its opacity per bar from an output with `opacity_by` |
| a band's edges | `edge_width`, `edge_line_style` on a `range()` | both edge lines at an integer width 0..10 (`0` = no edge lines), solid, dashed or dotted |
| a faded band | `gradient` on a `range()` | 2 to 8 colors, listed top to bottom, shading the interior vertically instead of a flat tint |
| where the stops lie | `gradientMode` | `"pane"` (the pane height, the default), `"fill"` (the band's own vertical extent) or `"line"` (per column, from `upper` down to `lower`) |
| a band's legend row | `legend`, `label` on a `range()` | `legend: false` drops the row; `label` names it |
| columns from a level | `base` | columns grow from `base` instead of zero, `colors[0]` at or above it and `colors[1]` below |
| columns faded by size | `grading` | `"linear"` or `"square"`: each column's alpha follows its magnitude within the visible range |
| stacked columns | `stack` | bars sharing a stack name stack in declaration order, positives above zero and negatives below |
| a candle's style | `candle_style`, `border_colors`, `wick_colors`, `border_width` on a candle group's first output | `"candle"`, `"hollow"`, `"ohlc"` or `"high_low"`; border and wick colours as `[both]` or `[up, down]`; the border width 0..10 |
| the mark | `shape`, `location`, `char`, `font_family`, `fill`, `fill_opacity` | the shape (ten words, `char` one character in `font_family`); `"absolute"`, `"above_bar"`, `"below_bar"`, `"top"` or `"bottom"`; the outline only, or a faded interior |
| a level | `role: "guide"`, `align`, `pill_style`, `font_size`, `axis_label` | one full-width line at the output's last value, out of the legend; a label on the line (`"left"`, `"center"`, `"right"`; `"filled"` or `"outlined"`; 6..64 px) or on the axis |
| paint order | `z` | an integer -10..10 on an output, a range, a fill or a box; absent, the engine's layer (tints, fills, lines and columns and bands and marks and boxes, text) in declaration order |
| a theme colour | `"theme.up"`, `"theme.down"`, `"theme.text"`, `"theme.muted"`, `"theme.bg"`, `"theme.grid"`, `"theme.accent"` | the chart's own colour, resolved when it paints and again on a theme switch ([Theme colours](#theme-colours)) |
| a color per bar | `color_by` plus `colors`, or `color_packed_by` | each bar's value indexes the palette, or carries a packed colour |
| a width per bar | `width_by` plus `widths` | the same ladder for line width |
| an inset strip's colours | `color`, `colors`, `color_by`, `opacity` on `out.inset` | `colors` without `color_by` is a sign pair (up at or above zero, down below); with `color_by` the bar's ladder colour |
| a HUD's look | `look` on `render.hud` | one of twelve presets, every word of which a declared word overrides ([HUD cards](hud-and-hover-cards.md#looks)) |

`color` (or a `colors` palette), `width`, `opacity` (0..1), `line_style`
(`solid`, `dashed`, `dotted`; lines and areas take the dash, columns
do not), `glow` (a soft halo in pixels on a line, an area, columns,
candles, marks and scatter dots), `corner` (rounded column corners, in
pixels), and `description`, which documents the output in the sheet. A
colour is `"#rrggbb"`, `"#rrggbbaa"` (the alpha in the last byte), a CSS
colour name as before, or a theme token (below); `opacity` multiplies
every colour the output draws, a ladder's rungs, a packed colour, a
column's sign pair, a candle's border and wick and a gradient's stops
included. The legend's and the Style page's words for an
output come from `label` (the name read as words when absent) and its
numbers from `format`, `decimals`, `signed` and `unit` (above);
`legend: false` keeps an output out of the legend, `visible: false`
starts it hidden, `price_line: true` draws a dotted line across the pane
at its last value, `axis_label: true` tags that value on the price axis
and `axis_name: true` adds the indicator's name to the tag
([Plotting](plotting.md)). The looks a plot kind takes beyond these
(`step`, `smooth`, `gradient` and `split` on lines; `fill_color`,
`fill_opacity`, `fill_gradient` and the fill ladder on areas; `base`,
`grading`, `stack` and `width` as a share of the bar slot on columns;
`candle_style`, `border_colors`, `wick_colors` and `border_width` on a
candle group; `shape`, `location`, `char`, `fill` and `fill_opacity` on
marks; `role: "guide"` for a level) are the table on
[Plotting](plotting.md#lines-areas-columns-dots-marks). The per-look
reading of this vocabulary, with the decision rule, is
[Decisions to looks](#decisions-to-looks).

## Theme colours

Every colour word accepts a theme token beside the hex forms:
`theme.up` and `theme.down` (the chart's candle colours), `theme.text`
(the axis text), `theme.muted` (that text at 55 percent), `theme.bg`
(the background), `theme.grid` (the grid lines) and `theme.accent` (the
app's accent). The chart resolves a token when it paints and again when
the theme switches, with no rerun, so a `"theme.text"` label reads on a
dark and a light chart alike. A token rides every colour key of this
page (outputs, ranges, fills, boxes, segments, renderers, drawings,
handles, cards, HUDs, docked levels and panels), the colour strings
inside a frame, and a packed colour the module writes
(`toPacked(theme.UP)` from `./sdk/color`,
[Colors kit](../functions/colors-kit.md#theme-colours)). A `param.color` default or
preset stays a hex colour ("a colour param takes #rrggbb or #rrggbbaa;
put the token on the colour key"), and a `theme.` word outside the seven
is refused by name. Two chart-level flags ride beside the tokens:
`chart.contrast_guard(false)` keeps the author's colours as written on a
light chart (the chart otherwise darkens colours that would vanish
there), and `chart.stack_handles(true)` stacks this indicator's
corner-anchored handle groups below other indicators' groups instead of
overprinting them ([Drawing objects](drawing-objects.md)).

## The Style page

Every output you draw gets a row on the dialog's **Style** page without a
declaration, and a spot the file binds to a setting (`color:
"@basis_color"`, `line_style: "@style"`) is that setting's row instead,
so a look never has two controls. The rows, the bindings and the
browser-or-cloud rule are on [The Style page](../settings/style-page.md).

## Decisions to looks

- Per-bar coloring: emit the decision as a data-only output (`none`),
  then on the styled output set `color_by: "<that output>"` plus a
  `colors` palette (at least 2 entries). Each bar's floored value indexes
  the palette; a finite value outside the palette takes entry 0, and a
  non-finite one draws the output's static color (entry 0 when the output
  declares no `color`). An output cannot color itself: the index
  expression is its own output.
- Per-bar width: the color ladder's sibling. On the styled output set
  `width_by: "<data-only output>"` plus a `widths` ladder (1..10 entries,
  each 0.5..20); each bar's value indexes the ladder (floor; a finite
  out-of-range index takes entry 0). A non-finite value on either ladder
  draws that bar in the static style. Both halves or neither, each refused
  by name without the other. An output cannot set its own width.
- Gated markers: a `shape` output with `shape_where: "<gate output>"`
  renders only where the gate is nonzero. An output cannot gate itself.
- Banded ranges: `range(upper, lower, options?)` names two DIFFERENT
  drawn outputs (by name, as strings; a `none` output is refused: "range
  references '<name>', which is not a rendered output"). The chart draws a
  band: both edge lines at `edge_width` (an integer 0..10; `0` draws the
  band with no edge lines) and `edge_line_style` (`solid`, `dashed` or
  `dotted`), with a tinted interior in `color`. A `colors` palette plus
  `color_by` (a data-only output) tints the band per bar (floor; a finite
  out-of-range value takes entry 0); `color_by` requires `colors`.
  `colors` WITHOUT `color_by` is the band's sign palette (a momentum
  fill): entry 0 where `upper` plots above `lower`, entry 1 where it plots
  below. `gradient` (2 to 8 colors, listed top to bottom) shades the
  interior vertically instead of a flat tint, laid over `gradientMode`:
  `"pane"` (the pane height, the default), `"fill"` (the band's own
  vertical extent) or `"line"` (per column, from `upper` down to
  `lower`). `smooth` is a boolean smoothing hint. `legend: false` drops
  the band's own legend row and `label` names it (default: the upper
  edge's label); `z` sets its paint order. Declare as many ranges as
  needed; the same pair may repeat with different options.
- Fills: `fill(a, b, options?)` shades the interior between two drawn
  outputs on one pane and draws no edge lines, so each line keeps its own
  look. Options: `color`, `opacity` (0..1, default 0.25), the `color_by`
  plus `colors` ladder or `color_packed_by` for a colour per bar (a
  non-finite rung draws no fill that bar), `opacity_by` for an opacity
  per bar, and `z`. `opacity_by` names a declared output whose value on
  each bar is that bar's opacity (0..1), with any colour mode; a
  non-finite value keeps `opacity`. The two sides must be different drawn
  outputs on the same pane; `colors` without `color_by`, `color_by`
  without `colors`, `color_packed_by` beside `color_by`, and an
  `opacity_by` that names no declared output are refused.

A setting reaches a fill's opacity through `opacity_by`: the file writes
the setting, scaled to 0..1, into a data-only output on every bar, and the
fill reads it there.

```typescript
param.int("cloudOpacity", 40, { min: 0, max: 100, label: "Cloud opacity" });
output("cloud_alpha", none); // the cloud's opacity on each bar, 0..1
fill("span_a", "span_b", { color_by: "cloud_side", colors: ["#22c55e", "#ef4444"], opacity_by: "cloud_alpha" });
// in onBar(): out_cloud_alpha(p_cloudOpacity() / 100.0);
```

A packed colour skips the palette: the module writes a colour per bar
into a data-only output (`toPacked` from the colour kit,
[Colors](../functions/colors-kit.md)) and the drawn output names it with
`color_packed_by`; an area's fill reads the same number through
`fill_color_packed_by`, so a heat that shifts smoothly needs no rungs.

```typescript
// A heat area painted per bar from a colour the module computes: the edge and the fill both read it.
output("heat", area, lower, { color_packed_by: "heat_color", fill_color_packed_by: "heat_color", fill_opacity: 0.35, width: 2 });
output("heat_color", none, lower, { description: "A packed colour per bar, written with toPacked(...) in onBar()" });
```

A per-bar box takes the same two ladders for its fill and its border,
under camelCase keys in the declaration (`colorBy`, `borderColorBy`,
`borderColorPackedBy`, `borderStyle`), with output handles for the `By`
keys ([Boxes](drawing-objects.md#boxes)): a zone can follow the regime
with a dashed border while a second box, drawn only where the band
squeezes, takes its fill and border from one packed heat colour.

```typescript
const hi = output("band_hi", line, overlay);
const lo = output("band_lo", line, overlay);
const regime = output("regime", none, overlay);
const heat = output("heat_color", none, overlay);
const squeezed = output("squeezed", none, overlay);
// The zone behind the last three bars: fill and border by the regime ladder, dashed, painted behind the lines.
box("zone", { top: hi, bottom: lo, from: -3, to: 0, colorBy: regime, colors: ["#ef444433", "#22c55e33"], borderColorBy: regime, borderColors: ["#ef4444", "#22c55e"], borderStyle: "dashed", z: -1 });
// The squeeze box: fill and border from one packed colour, where the gate is nonzero.
box("squeeze", { top: hi, bottom: lo, when: squeezed, colorPackedBy: heat, borderColorPackedBy: heat, opacity: 0.3, borderWidth: 2 });
```

Every reference is checked as you type and when you press **Run**:
`color_by`, `width_by`, `shape_where`, and each `range()` side must name
a declared (and, where required, drawn) output, and none may name its own
output. A refusal points at the declaration in the editor's Console and
names the field (for example "Output 'mid' (color_by): ...").

Styling dresses per-output SERIES. When the indicator should also put
text, tables, shaped marks, or free-standing objects (lines, boxes,
polylines, labels) on the chart, that is the renderer and drawing
vocabulary: `render.*` and `draw.*` declarations over named outputs and
string slots ([Plotting](plotting.md), [Drawing objects](drawing-objects.md)).
Shapes that repeat on EVERY bar (a zone behind the last few bars, a
projection ray from each bar) are the `box()` and `segment()`
declarations over output handles
([Boxes](drawing-objects.md#boxes), [Segments](drawing-objects.md#segments)).

## Tags, the legend and the candle tint

Four looks belong to the indicator as a whole: whether it keeps a price
axis of its own, which plots tag their value on the price axis, the order
its plots join the legend, and the pane it lives in. One top-level
`display({ ... })` declares them, at most once:

| Key | What the chart does | Absent |
| --- | --- | --- |
| `axis: false` | no price axis of its own: an indicator on the price pane rides the chart's scale | its own axis |
| `price_display: "per_output"` | every plot tags its value on the price axis unless its `show_price_display` is `false`; a mark tags only when its `show_price_display` is `true` | the first output alone tags |
| `overlay: "offchart"` | the indicator lives in its own pane below the chart; `pane("lower", { format: "0.00" })` formats that pane's axis | the outputs' panels decide |
| `mount_order: "first_value"` | plots join the legend, and paint within one layer, in the order they first draw a value | declaration order, every range after every output |

Five words sit on the plots themselves:

| Word | On | What it does |
| --- | --- | --- |
| `show_price_display` | an output, a `range`, a `render.shape` | the plot's tag on the price axis, read under `price_display: "per_output"`: `false` drops it, `true` forces one |
| `show_price_display_by` | an output | a declared output whose value on the last bar switches the tag: nonzero on, zero off, `NaN` keeps `show_price_display` |
| `omit_if_empty` | an output, a `range` | `true`: a run that never drew it leaves it out, with no legend row and no tag (a plot drawn under an `if`) |
| `fill: false` | a `range` | the two edge lines and no interior |
| `barcolor: true` | a `none` output | the candles take its colour: each bar's value (floored) picks an entry of `colors`, or `color_packed_by` names a packed colour; a `NaN` or out-of-range value leaves the candle as it is; one per indicator |

Every colour bound to a setting also takes it at an alpha:
`"@<param>/0.45"` paints the setting's colour at 45 percent, and a
recolour keeps the 45; a panel series colour binds without one
([The Style page](../settings/style-page.md#a-setting-at-an-alpha)).

```typescript sample=fn-chart-look
// No price axis of its own, a tag per plot, and plots listed in the order they first draw a value.
display({ axis: false, price_display: "per_output", mount_order: "first_value" });
param.int("length", 20, { min: 2, max: 200, label: "Length" });
param.color("bull", "#22c55e", { label: "Bull" });
param.color("bear", "#ef4444", { label: "Bear" });
param.bool("tag_basis", true, { label: "Tag the basis" });
// The basis tags its value while the setting is on; the edges never tag.
output("basis", line, overlay, { color: "@bull", show_price_display_by: "basis_tag" });
output("upper", line, overlay, { color: "@bull/0.5", show_price_display: false });
output("lower", line, overlay, { color: "@bear/0.5", show_price_display: false });
output("basis_tag", none);
// A mark on each breakout: a run with none leaves it out of the legend.
output("breakout", shape, overlay, { shape: "triangle_up", location: "below_bar", color: "@bull", omit_if_empty: true });
// The two edges as lines with no interior, untagged.
range("upper", "lower", { fill: false, show_price_display: false });
// The candles in the trend's colour at 45 percent: 0 below the basis, 1 above.
output("tint", none, overlay, { barcolor: true, colors: ["@bear/0.45", "@bull/0.45"] });

let sma = new Sma(20);
let stdev = new Stdev(20);
let tag: f64 = 1.0;

function onStart(): void {
  sma = new Sma(i32(p_length()));
  stdev = new Stdev(i32(p_length()));
  tag = pb_tag_basis() ? 1.0 : 0.0;
}

function onBar(): void {
  const close = bar.close();
  const mid = sma.update(close);
  const sd = stdev.update(close);
  if (isNaN(mid) || isNaN(sd)) return;
  const hi = mid + 2.0 * sd;
  out_basis(mid);
  out_upper(hi);
  out_lower(mid - 2.0 * sd);
  out_basis_tag(tag);
  if (close > hi) out_breakout(bar.low());
  out_tint(close > mid ? 1.0 : 0.0);
}
```

Refused by name: a tag word on a `none` output ("show_price_display needs a
drawn output"), `omit_if_empty` on one ("omit_if_empty needs an output
that draws a value"), `barcolor` on a drawn output, without `colors` or
`color_packed_by`, beside `color_by` (its own value is the index) or on a
second output, a `range` with `fill: false` and a `gradient`, a
`show_price_display_by` that names no output, a second `display(...)` and
a word `display` does not take (`overlay: "onchart"`).

## Text size, tooltips, alignment, z-order, palettes

The styling vocabulary, item by item:

| You want | wrun form |
| --- | --- |
| text size | `size`, an integer pixel count 6..64, on `render.text` and `render.label`, or `size_by` for a size per bar from an output; `font_size` on a guide's label, a box's text and a card |
| type | `font_weight` (`normal`, `medium`, `bold`) and `font_family` (`ui`, the app font; `mono`; `serif`; `rounded`, each a system stack, nothing downloads) on renderer text, labels, box text, cards, HUDs and docked levels |
| a tooltip on a plot | `tooltip` on the output, a template over its own value and any output or string slot: `"{{label}} {{value:price}} · {{state}}"`; shown on the legend entry and when the cursor nears the line ([Labels, tooltips, badges](labels-and-tooltips.md)) |
| a tooltip on a cell, label, mark or drawing | `tooltip` on `render.text`, `render.label`, `render.shape`, `draw.line`, `draw.box`, `draw.polyline` and `draw.label` (one template per declaration, shown on its marks or object); a handle's `tooltip(send)` setter; a `hover` block list on an output or a label for a full card ([HUD and hover cards](hud-and-hover-cards.md#hover-cards)); a table cell's `tooltip` in its `styles` entry, a string slot ([Styled tables](cards-frames-panels.md#styled-tables)) |
| price tags, callouts, pills | `style` on `render.text` and `render.label`, eight words on both: `plain`, `price_label` (the price tag on the axis), `pill` (a rounded chip at the value), `callout` (a leader line to its bar), `badge` (a dot in the label's color), `box`, `knockout` (a box in the background colour) and `emblem` (a mark before the text); a tag look takes `label_position`, `background_color`, `border_color` and `corner_radius` ([Labels, tooltips, badges](labels-and-tooltips.md)) |
| text alignment | `align` (`left`, `center`, `right`) and `valign` (`top`, `middle`, `bottom`) on plain `render.text` and `render.label`, on a handle or declared label, and on a box's text; the nine anchors (`top_left` ... `bottom_right`) are a table's, a HUD's or a corner `render.label`'s `position` ([Drawing objects](drawing-objects.md)) |
| z-order | `z` (-10..10) on an output, a range, a fill or a box, else the engine's layer in declaration order; a handle's `zorder` setter on lines, boxes, labels and polylines |
| a palette | a `colors` literal array on the output, box, range, fill, bgcolor, barcolor, stats row or shape renderer; bucket the driving value into its index ([Colors](../functions/colors-kit.md)); an entry may be a `param.color` by name; `color_packed_by` names an output that carries a packed colour per bar instead |
| per-element glow, opacity, gradients | `glow` per output, per renderer mark and per handle (`glow_color` picks its colour); `opacity` per output (every colour it draws), per box and per handle; a `color_by` ladder per bar; a line's `gradient` by height, an area's `fill_gradient`, a `range()` band's vertical `gradient`, a box handle's `gradient`; `corner` rounds histogram columns, `corner_radius` the corners of labels, boxes, cards and HUDs |
| a setting behind a look | `"@<param>"` in place of a colour, a `colors` or `gradient` entry, or a line style on an output, a `range`, a `fill`, a `box`, a `segment`, a legend entry or a docked `plot.levels` key; `"@<param>/<alpha>"` for the setting's colour at an alpha; a `param.color` on every colour word of a panel ([Panel words](cards-frames-panels.md#panel-words)); a `param.choice` over the looks on a HUD card's `look` ([Looks](hud-and-hover-cards.md#looks)) ([The Style page](../settings/style-page.md)) |
| a table's fill, frame, lines and widths | the look keys on `render.table`: fills with opacity and gradients, a frame, the lines between cells, column widths, merged cells ([Styled tables](cards-frames-panels.md#styled-tables)) |

Value-driven styling, computing the style from the data per element,
is the ladder: normalize the driving value,
floor it into a bucket, write the bucket to a `none` output, and index a
palette with it. The palette is finite, so the gradient is stepped, and
the decision stays a number you can read at the Console prompt or hand to
a declared alert.

## A worked example

Bollinger-style bands with a regime-colored (and regime-widened)
midline and a banded range. The declarations carry every look; the module
computes four numbers per bar and never mentions a color:

```typescript sample=fn-regime-color
param("period", 20, { min: 2, max: 400 });
param("band_width", 2, { min: 0.5, max: 4, description: "Stdev multiples" });
// Red while the close is under the average, green (and three times as wide) while it is over.
output("mid", line, overlay, { color_by: "regime", colors: ["#ef4444", "#22c55e"], width_by: "regime", widths: [1, 3] });
output("band_hi", line, overlay, { color: "#94a3b8", opacity: 0.6 });
output("band_lo", line, overlay, { color: "#94a3b8", opacity: 0.6 });
output("regime", none, overlay, { description: "0 below the average, 1 above: the palette and width index" });
// The band between the two edges: dotted edge lines and a slate interior.
range("band_hi", "band_lo", { color: "#94a3b8", edge_width: 1, edge_line_style: "dotted" });

let sma = new Sma(20);
let stdev = new Stdev(20);
let mult: f64 = 2.0;

function onStart(): void {
  const period = i32(p_period());
  sma = new Sma(period);
  stdev = new Stdev(period);
  mult = p_band_width();
}

function onBar(): void {
  const close = bar.close();
  const mid = sma.update(close);
  const sd = stdev.update(close);
  const regime = close > mid ? 1.0 : 0.0;
  if (isNaN(mid) || isNaN(sd)) return;
  out_mid(mid);
  out_band_hi(mid + mult * sd);
  out_band_lo(mid - mult * sd);
  out_regime(regime);
}
```

The `regime` output computes on every bar but never draws; it exists so
the palette (and the `widths` ladder) on `mid` has a decision to index.
The band is one `range()` over the two drawn edges, so the chart shades
it without a box per bar. The two params are number fields in the
overlay's settings dialog (a `param.int` would step by whole numbers,
and `param.number` with a `step` by that step), and changing one reruns
the compiled module without recompiling it; the three lines each get a
Style row.

The `output(...)` options carry `color`, `colors`, `width`, `opacity`,
`line_style`, `glow`, `corner`, `z`, `color_by`, `color_packed_by`,
`shape_where`, `width_by`, `widths`, `displacement_bars`,
`displacement_bars_by`, `unit`, `description`, the plot-kind looks of
[Plotting](plotting.md#lines-areas-columns-dots-marks) (`step`,
`smooth`, `gradient`, `split` and its four companions, `fill_color`,
`fill_opacity`, `fill_gradient`, `gradient_mode`, `fill_color_by`,
`fill_colors`, `fill_color_packed_by`, `base`, `grading`, `stack`,
`candle_style`, `border_colors`, `wick_colors`, `border_width`, `shape`,
`location`, `char`, `font_family`, `fill`, `role`, `align`, `pill_style`,
`font_size`, `pane`), the presentation keys `label`, `format`,
`decimals`, `signed`, `legend`, `visible`, `price_line`, `axis_label`,
`axis_name`, `tooltip`, `hover`, `badges`, `hint`, and the chart words
`show_price_display`, `show_price_display_by`, `omit_if_empty` and
`barcolor` ([Tags, the legend and the candle tint](#tags-the-legend-and-the-candle-tint)); ranges declare as
`range(upper, lower, options?)`, fills as `fill(a, b, options?)`, panes
as `pane(name, options?)`, boxes and segments as `box(name, options)` /
`segment(name, options)` over output handles, and declaration order sets
the indexes ([Declarations and the sheet](../reference/declarations.md)).

## Where the rules live

Every styling reference is validated as you type and when you press
**Run**, and every refusal names the declaration and the field in the
editor's Console; the messages are in
[Common errors](../faq/common-errors.md). The module never draws: the
chart reads the declarations, so the look is exactly what they describe,
whatever the module computes.
