Skip to main content
Version: 3.0
Beta

App Studio is in release 3.0 as Beta: a paid add-on that no edition includes by default. A licence or a trial licence that includes the app_studio feature switches it on. The module is on by default, so the licence decides. This reference may change while App Studio is in Beta; the SDK changelog records each change.

Generated

Generated from the SDK sources by scripts/appstudio-sdk-docs.cjs for runtime-v1. Do not edit; run npm run appstudio:docs:sdk.

@maestrohub/app-ui

Beta: look up the App Studio UI kit components, such as cards, tables, charts and form fields, that take their colours from the platform theme.

Runtime
runtime-v1
Exports
68 (49 values, 19 types)

The App Studio UI kit: layout, display, input, table and chart components (Beta in 3.0).

Runtime: runtime-v1. 68 exports (49 values, 19 types). Import with:

import { ... } from '@maestrohub/app-ui';

Hooks​

usePalette​

function usePalette(): ResolvedPalette

usePalette returns the kit colours for the current theme: the host's own background / surface / ink / muted / border / subtle / primary / danger when the shell sent them, the kit's palette otherwise; the status colours are always the kit's.

useTheme​

function useTheme(): ThemeMode

useTheme returns the live theme mode the shell set (light when unset).

Components​

Attachments​

function Attachments({ collection, recordKey, label, accept, hint, removable, readOnly, }: { collection: Collection; recordKey: string | null; label?: string; accept?: string; hint?: string; removable?: boolean; readOnly?: boolean; }): JSX.Element

Attachments shows one stored record's files and attaches more: photos render as thumbnails, any other file as a named tile; a click opens the file in a new tab. recordKey null means the record is not saved yet, and the picker says so instead of failing on the first file. A refused file (wrong type, over the size limit, the app's total reached) shows the platform's reason. removable adds a Remove button (with a confirm step) under each file; readOnly hides the picker.

Avatar​

function Avatar({ person, size }: { person: Person; size?: number; }): JSX.Element

A person's picture, or their initials (a group shows its initial on a square). size in px, default 28.

Badge​

function Badge({ tone, children }: { tone?: "ok" | "warn" | "critical" | "neutral"; children?: ReactNode; }): JSX.Element

A small rounded pill coloured by tone (default neutral) from the palette's status colours (status[tone], the same text colour toneColor returns). A toned badge's text must name the status ("Faulted", "Torque high"), not only a number or a name: colour alone is not enough for colour-blind users.

BarChart​

function BarChart({ items, height, unit, max }: { items: { label: string; value: number; tone?: Tone; }[]; height?: number; unit?: string; max?: number; }): JSX.Element

A responsive SVG bar chart of items (label, value, optional tone). The scale tops out at max or the largest value; negative values are drawn as zero-height bars, labels longer than 12 characters are truncated. A bar with a tone carries the tone's shape under its label; the chart's accessible description reads each bar's value (with unit) and tone word, the first 20 and a count of the rest. Renders "No data." for an empty list.

Button​

function Button({ intent, disabled, reason, confirm, onClick, children }: ButtonProps): JSX.Element

The kit's action button. When disabled and reason are both set the reason renders as the button's title and as a caption beneath it, so a dead control always says why. With confirm, the first click swaps the label for the confirm text and shows a Cancel button; only the second click fires onClick.

Card​

function Card({ title, subtitle, actions, children }: { title?: string; subtitle?: string; actions?: ReactNode; children?: ReactNode; }): JSX.Element

A bordered, themed panel: an optional title heading (with a muted subtitle under it and actions on the right) above children. A card on its own keeps a bottom margin so stacked cards space themselves; inside a kit layout (Page, Section, Stack, Grid, Row) the layout's gap spaces it instead (the base stylesheet's .mh-card rule), so the gaps between cards are even.

CheckboxField​

function CheckboxField({ label, checked, onChange, disabled }: { label: string; checked: boolean; onChange: (v: boolean) => void; disabled?: boolean; }): JSX.Element

A checkbox with its label inline; reports onChange(boolean). Unlike the other fields it has no hint.

ConnectionNotice​

function ConnectionNotice({ hidePending }: ConnectionNoticeProps): JSX.Element | null

ConnectionNotice shows, above an app's content, that live values are paused (since when, in the app's clock) and how many records are waiting to be sent, with a "Send now" button. Renders nothing while the connection is live and the outbox is empty.

Example

<Page title="Line 2"><ConnectionNotice /> ...</Page>

DateField​

function DateField({ label, value, onChange, hint, disabled, withTime }: { label: string; value: string; onChange: (iso: string) => void; hint?: string; disabled?: boolean; withTime?: boolean; }): JSX.Element

DateField / DateTimeField take and return ISO strings (empty = unset).

EmptyState​

function EmptyState({ title, description, action }: { title: string; description?: string; action?: ReactNode; }): JSX.Element

A dashed-border placeholder for "nothing here yet": bold title, muted description, and an optional action slot below.

ErrorNotice​

function ErrorNotice({ error, title, onRetry, retryLabel }: ErrorNoticeProps): JSX.Element | null

Renders a caught error as an alert: the title (or the platform code when no title is given), the reason, the remediation when the platform supplied one, and a Retry button when onRetry is set. Understands ApiError, LiveError, shell refusals and plain errors via describeError; renders nothing for null/undefined.

Example

const t = useTopic('plant/line1/oee');
return t.error ? <ErrorNotice error={t.error} /> : <MetricCard label="OEE" value={t.value} />;

FeedStatus​

function FeedStatus({ status }: { status: LiveStatus; }): JSX.Element

One live subscription's state (useTopic(...).status) as a toned badge in plain words: Live, Connecting, Feed error, Disconnected. Pair it with <ErrorNotice error={topic.error} />, which says why.

FileField​

function FileField({ label, onFiles, accept, multiple, hint, disabled, busy }: { label: string; onFiles: (files: File[]) => void; accept?: string; multiple?: boolean; hint?: string; disabled?: boolean; busy?: boolean; }): JSX.Element

A labelled file picker that also takes files dropped on it. Reports the picked files as an array through onFiles (empty picks are not reported) and clears itself, so picking the same file again reports again. accept and multiple pass through to the input; busy swaps the label for "Uploading…" and refuses new files meanwhile. The control never checks types or sizes: the platform refuses what it will not store and says why.

FilterBar​

function FilterBar({ children, actions }: { children?: ReactNode; actions?: ReactNode; }): JSX.Element

FilterBar is the horizontal strip that holds a report's parameters.

Gauge​

function Gauge({ value, max, threshold, unit }: { value: number; max?: number; threshold?: number; unit?: string; }): JSX.Element

A large numeric readout (value + unit, default %) over a horizontal fill bar sized by value / max (default max 100, clamped to 0 to 100 %). When threshold is set and value is below it, the number and the bar turn the critical status colour and a critical ToneMark precedes the number; otherwise the bar is ok.

Grid​

function Grid({ columns, gap, minColumnWidth, children }: { columns?: number; gap?: number; minColumnWidth?: number; children?: ReactNode; }): JSX.Element

Grid lays children out in columns equal columns (1 on narrow screens).

KeyValueList​

function KeyValueList({ items }: { items: { key: string; value: ReactNode; }[]; }): JSX.Element

A two-column definition list (dl) of items; each item's key is the muted term and value the description. Keys must be unique; they are the React keys.

Loading​

function Loading({ label }: { label?: string; }): JSX.Element

A muted one-line status text (role="status"), default "Loading…". No spinner.

MetricCard​

function MetricCard({ label, value, unit, delta, tone, hint }: { label: string; value: ReactNode; unit?: string; delta?: number; tone?: Tone; hint?: string; }): JSX.Element

A KPI tile: uppercase label, a large value with optional unit, and an optional hint line. tone colours the value (ok / warn / critical) and puts the tone's ToneMark before it, so the status is not colour alone; delta renders a badge with an up/down arrow and one padded decimal (▲ 2.0) whose tone is derived from the sign of the delta as shown, not from tone.

NumberField​

function NumberField({ label, value, onChange, min, max, step, unit, hint, disabled }: { label: string; value: number | null; onChange: (v: number | null) => void; min?: number; max?: number; step?: number; unit?: string; hint?: string; disabled?: boolean; }): JSX.Element

A labelled type="number" input. value is number | null; an empty field reports null, anything else Number(text). unit is appended to the label as "(unit)"; min/max/step pass through to the input.

Page​

function Page({ title, subtitle, actions, fill, children }: { title?: string; subtitle?: string; actions?: ReactNode; fill?: boolean; children?: ReactNode; }): JSX.Element

The top-level app frame: a centred column (max 1400 px) with the kit font and ink colour; the document's base stylesheet pads it from the frame's edges. Renders a header with title (h1), subtitle and right-aligned actions only when a title or actions are given. fill makes the page the whole frame instead (no column, no header), for a canvas whose panels float over it.

PersonField​

function PersonField({ label, value, onChange, kind, placeholder, hint, disabled }: { label: string; value: Person | null; onChange: (person: Person | null) => void; kind?: "user" | "group"; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element

A labelled picker over the app's people: type part of a name, pick one. Controlled: value (a Person or null) in, onChange out. kind limits it to people ('user') or groups ('group'). Pass the pick to notifications.notify({ to: [subjectOf(person)] }) or store its id.

ProgressBar​

function ProgressBar({ value, max, tone, label }: { value: number; max?: number; tone?: Tone; label?: ReactNode; }): JSX.Element

A horizontal bar filled to value / max (clamped to 0 to 100 %), in the tone's colour (default ok), with an optional label above it on the left and the percentage on the right: work-order progress, a tank level, a shift target.

Row​

function Row({ gap, align, wrap, children }: { gap?: number; align?: CSSProperties["alignItems"]; wrap?: boolean; children?: ReactNode; }): JSX.Element

A horizontal flex row of children; gap (12), align (center) and wrap (true) map to the flex properties.

Section​

function Section({ title, description, actions, children }: { title?: string; description?: string; actions?: ReactNode; children?: ReactNode; }): JSX.Element

A titled block inside a Page: an h2 title, muted description, and actions on the right, above children. The header row is omitted when neither title nor actions are set.

SelectField​

function SelectField({ label, value, onChange, options, placeholder, hint, disabled }: { label: string; value: string; onChange: (v: string) => void; options: SelectOption[]; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element

A labelled select over options. When placeholder is set it is rendered as a leading option with an empty value, so choosing it reports ''.

Sparkline​

function Sparkline({ values, width, height, tone }: { values: number[]; width?: number; height?: number; tone?: Tone | "primary"; }): JSX.Element

An inline, unlabelled SVG line of values scaled to its own min/max (width 120 × height 28 by default). Non-finite values are dropped; fewer than two remaining renders an em dash instead. A status tone is named in the sparkline's accessible name and title.

Stack​

function Stack({ children, gap }: { children?: ReactNode; gap?: number; }): JSX.Element

A vertical flex column of children separated by gap pixels (default 12).

StatusDot​

function StatusDot({ tone, label }: { tone?: Tone; label?: string; }): JSX.Element

A 10 px ToneMark for tone (default neutral): the tone's colour and shape, named by the tone's word, followed by an optional label. A label that is the tone's own word ("Critical") is not repeated, and a dot with no tone (a default nobody chose) is not announced.

Table​

function Table({ columns, rows, rowKey, pageSize, filterable, sort: initialSort, exportName, emptyText, onRowClick, caption }: TableProps): JSX.Element

The data table: client-side text filter (filterable), click-to-sort headers (numeric when both values parse as numbers, else locale string compare), pagination by pageSize, and CSV export of every filtered and sorted row via the shell when exportName is set. An export the shell or the size cap refuses is shown inline as an ErrorNotice. Cells render through formatCell.

Tabs​

function Tabs({ items, initial }: { items: TabItem[]; initial?: string; }): JSX.Element

A tab strip over a single panel. Holds the active id in local state, starting at initial or the first item; only the active item's content is rendered. Uses tablist/tab/tabpanel roles.

Text​

function Text({ tone, size, children }: { tone?: "body" | "muted" | "strong"; size?: "sm" | "md" | "lg"; children?: ReactNode; }): JSX.Element

Text is the kit's paragraph: the one place body copy is styled.

TextArea​

function TextArea({ label, value, onChange, rows, placeholder, hint, disabled }: { label: string; value: string; onChange: (v: string) => void; rows?: number; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element

A labelled multi-line text input (rows default 3, vertically resizable). Controlled via value / onChange(string).

TextField​

function TextField({ label, value, onChange, placeholder, hint, disabled }: { label: string; value: string; onChange: (v: string) => void; placeholder?: string; hint?: string; disabled?: boolean; }): JSX.Element

A labelled single-line text input. Controlled: value in, onChange(string) out; optional placeholder, hint caption and disabled.

TimeRangeField​

function TimeRangeField({ label, value, onChange }: { label?: string; value: TimeRange; onChange: (r: TimeRange) => void; }): JSX.Element

TimeRangeField: the "last 24 h / last 7 d / custom" control every report needs.

TimeSeriesChart​

function TimeSeriesChart({ series, bands, height, yLabel, yMin, yMax, unit }: { series: Series[]; bands?: Band[]; height?: number; yLabel?: string; yMin?: number; yMax?: number; unit?: string; }): JSX.Element

A responsive SVG line chart of one or more Series with optional shaded bands, four y grid lines, a legend, and the first and last x values as axis labels (formatted as times when any x is not a number). The y range auto-fits the data plus the bands unless yMin/yMax are given; point markers are drawn only for series with 60 points or fewer. Its accessible description names each series and band with its tone's word (the first 20, then a count of the rest). Renders "No data." when there are no points.

Timeline​

function Timeline({ items }: { items: { at: string; title: ReactNode; detail?: ReactNode; tone?: Tone; }[]; }): JSX.Element

Timeline lists dated events newest first: the log/handover/alarm history shape every operations app ends up drawing. Each entry's time reads in the app's clock (formatDateTime); its tone (default neutral) is a ToneMark, shape and colour; an entry without a tone gets the neutral ring, not announced.

ToneMark​

function ToneMark({ tone, size, label, decorative }: { tone: Tone; size?: number; label?: string; decorative?: boolean; }): JSX.Element

ToneMark draws tone as its shape in its colour, size px (default 10), named for assistive technology and on hover by the tone's word (label overrides it). With decorative it is hidden from assistive technology: for a mark whose tone the adjacent text already says, or a default tone nobody chose (an untoned Timeline entry). StatusDot and Timeline draw their dot with it; use it beside toneColor in a status widget of your own.

Functions​

contrastRatio​

function contrastRatio(fg: string, bg: string): number

contrastRatio is the WCAG 2 contrast between two colours (1 to 21). A translucent fg is composited over bg first. NaN when either colour cannot be read.

describeError​

function describeError(error: unknown): ErrorDescription | null

describeError turns any caught value into {reason, remediation?, code?}. Exported so an app (or a test) can read the explanation without the DOM.

formatCell​

function formatCell(value: unknown, column: Column, row: TableRow, context: { columnValues: number[]; }, dark: boolean): { text: string; node?: ReactNode; style?: React.CSSProperties; }

Applies a column's CellFormatter to one value and returns the plain text (used for filtering, sorting and CSV), an optional node to render instead of the text (badges, progress bars), and an optional cell style (heatmap background). context.columnValues supplies the column's numeric values for the percentile heatmap; dark picks the heat scale. Non-numeric input to a numeric formatter renders "—".

Example

formatCell(0.8734, { key: 'oee', label: 'OEE', format: { kind: 'number', digits: 1, unit: '%' } }, row, { columnValues: [] }, false)
// → { text: '0.9 %' }

heatColor​

function heatColor(t: number, dark: boolean): string

heatColor maps 0..1 to a cool→warm scale readable on both themes.

mixColors​

function mixColors(a: string, b: string, t: number): string

mixColors blends a toward b by t (0 = a, 1 = b) and returns opaque hex, a form every consumer takes (inline styles, SVG, three.js). An unreadable input returns the other colour as given.

percentileOf​

function percentileOf(v: number, all: number[]): number

percentileOf returns the fraction of all that is ≤ v (0..1).

toCSV​

function toCSV(columns: Column[], rows: TableRow[], format: (value: unknown, column: Column, row: TableRow) => string): string

toCSV renders rows with the columns' formatted text (RFC 4180 quoting).

toneColor​

function toneColor(p: Palette, tone: Tone): string

toneColor is the one map from a Tone to its colour, for every component that draws a status as a colour (a value, a dot, a bar, a line): p.status[tone].text, the same colour a Badge of that tone writes its text in. Colour is never the only cue: pair it with ToneMark (a shape per tone) or a word.

Constants​

TONE_WORD​

const TONE_WORD: Record<Tone, string>

The word each tone is announced and titled with.

Members

  • critical: string
  • neutral: string
  • ok: string
  • warn: string

Types​

Band​

interface Band

A horizontal shaded band on a TimeSeriesChart (a target range, an alarm limit).

Members

  • from: number: Lower y bound.
  • label?: string | undefined: Text drawn at the band's top-right.
  • to: number: Upper y bound.
  • tone?: Tone | undefined: Fill colour (default ok); the band's title names the tone.

ButtonProps​

interface ButtonProps

Props for Button.

Members

  • children?: React.ReactNode: Button label.
  • confirm?: string | undefined: confirm asks before firing: the G3 rung-2 affordance.
  • disabled?: boolean | undefined: Disables the control; pair with reason so the button explains itself.
  • intent?: "primary" | "danger" | "neutral" | undefined: Colour intent: primary (accent), danger, or neutral (subtle fill). Default primary.
  • onClick?: (() => void) | undefined: Fired on click, after the confirm step when confirm is set; never while disabled.
  • reason?: string | undefined: reason renders the disabled explanation (FR-42's honest-UI half): a disabled control always says why (house rule 36).

CellFormatter​

type CellFormatter = {
kind: 'number';
digits?: number;
unit?: string;
} | {
kind: 'time';
} | {
kind: 'badge';
map?: Record<string, Tone>;
} | {
kind: 'percentile-heatmap';
invert?: boolean;
} | {
kind: 'threshold';
bands: {
max: number;
tone: Tone;
}[];
above?: Tone;
} | {
kind: 'category-color';
map: Record<string, Tone>;
} | {
kind: 'compare';
baseline: string;
percent?: boolean;
digits?: number;
} | {
kind: 'progress-bar';
max: number;
tone?: Tone;
};

The formatter catalogue: closed, schema-shaped, pure.

Column​

interface Column

One column of a Table.

Members

  • align?: "left" | "right" | undefined: Cell alignment; when omitted numeric cells align right, others left.
  • format?: CellFormatter | undefined: Declarative cell formatter. Omitted, a number reads as shownNumber writes it (at most two decimals) and anything else as String(value).
  • key: string: The row property to read; also the sort key.
  • label: string: Header text.
  • sortable?: boolean | undefined: Set to false to make the header inert; every other value keeps click-to-sort on.
  • width?: string | number | undefined: Header width, passed to the th style.

ConnectionNoticeProps​

interface ConnectionNoticeProps

Props for ConnectionNotice.

Members

  • hidePending?: boolean | undefined: Say nothing about pending records (an app that writes none).

ErrorDescription​

interface ErrorDescription

The rendered explanation of an error, independent of the DOM.

Members

  • code?: string | undefined: The platform's code (APP_RATE_LIMITED, ACTION_NOT_DECLARED, …).
  • reason: string: The headline: what was refused or what failed.
  • remediation?: string | undefined: What to do next, when the platform said.
  • retryAfterSeconds?: number | undefined: Seconds the platform asked the app to wait (429 only).

ErrorNoticeProps​

interface ErrorNoticeProps

Props for ErrorNotice.

Members

  • error: unknown: Whatever was caught; null/undefined renders nothing.
  • onRetry?: (() => void) | undefined: Offers a "Retry" button; the notice does not retry by itself.
  • retryLabel?: string | undefined: Label for the retry button (default "Retry").
  • title?: string | undefined: Optional heading above the explanation.

Palette​

interface Palette

A colour set for one ThemeMode. usePalette returns a ResolvedPalette (every field set); the fields added in 2026-10 (background, dangerInk, status) are optional here so an app's own palette literal written before them still type-checks, and toneColor still answers for it.

Members

  • background?: string | undefined: Page background: the host's --background when tokens were sent.
  • badge: Record<Tone, CSSProperties>: Background + text pair per Tone, as Badge renders them: status as CSS.
  • border: string: Borders and rules.
  • chart: string: Data accent for charts and sparklines: the kit's own, never the host's button colour (a shell whose primary is near-white on dark would draw every series in white).
  • danger: string: The destructive FILL (a danger button's background; the host's --destructive when tokens were sent). Not a status colour: a critical value, dot or message reads status.critical, which is the same in every host theme. The shell's dark themes use a light red with a dark label (dangerInk), so never hardcode white on it.
  • dangerInk?: string | undefined: Text ON danger: the host's --destructive-foreground, else the readable one of white / near-black.
  • ink: string: Primary text.
  • muted: string: Secondary text.
  • ok: string: Positive colour: status.ok.text.
  • primary: string: Accent (primary buttons, active tab). The host's button colour when tokens were sent.
  • primaryInk: string: Text ON the accent: the host's --primary-foreground when tokens were sent. Never hardcode white on primary: the shell's default dark theme uses an inverted-mono accent (near-white primary, dark navy text), so white-on-primary renders an unreadable button.
  • status?: Record<Tone, StatusColour> | undefined: The one status palette (the kit's own, in every host theme): what toneColor returns and what Badge paints, so the two cannot differ. Each text reads at 4.5:1 or more on surface and subtle in all six shell themes and the kit's fallback, and on its own background (app-ui-tones.test.tsx stamps every theme).
  • subtle: string: Subtle fill (table header, neutral button, track backgrounds).
  • surface: string: Card / control background.
  • warn: string: Warning colour (a value past its limit, a stale feed): status.warn.text.

Point​

interface Point

One sample in a Series.

Members

  • x: string | number | Date: A number, or a date / date string (parsed with new Date); mixing kinds makes the chart treat the axis as time, labelled in the app's clock.
  • y: number | null: The value; null leaves a gap in the line.

ResolvedPalette​

type ResolvedPalette = Palette & {
background: string;
dangerInk: string;
status: Record<Tone, StatusColour>;
};

The palette usePalette returns: every field set, status included.

SelectOption​

interface SelectOption

One choice in a SelectField: the value reported by onChange and the label shown.

Members

  • label: string: Text shown in the dropdown.
  • value: string: Reported by onChange when chosen; also the React key, so values must be unique.

Series​

interface Series

One line in a TimeSeriesChart.

Members

  • dashed?: boolean | undefined: Draw the line dashed.
  • name: string: Legend label; also the React key, so names must be unique.
  • points: Point[]: The samples, in x order.
  • tone?: "primary" | Tone | undefined: Line colour (default primary). A status tone also marks the points and the legend with the tone's shape.

StatusColour​

interface StatusColour

One status colour: the text / mark colour and the fill a Badge sits it on.

Members

  • background: string: The badge background behind text.
  • text: string: Text, dots, bars and lines in this tone.

TabItem​

interface TabItem

One tab for Tabs: a stable id, the button label, and the panel content.

Members

  • content: React.ReactNode: Rendered in the panel while the tab is active.
  • id: string: Identifies the tab; Tabs.initial refers to it.
  • label: string: Text on the tab button.

TableProps​

interface TableProps

Props for Table.

Members

  • caption?: React.ReactNode: Muted text in the toolbar, left of the filter.
  • columns: Column[]: Column definitions, in display order.
  • emptyText?: string | undefined: Shown in the body when no rows are visible (default "No rows.").
  • exportName?: string | undefined: Export: the file name (CSV of ALL rows after filter+sort, not the page).
  • filterable?: boolean | undefined: Text filter over every displayed cell.
  • onRowClick?: ((row: TableRow) => void) | undefined: Makes rows clickable and receives the clicked row. Cells hold no buttons: a row's own action (print its label, open its detail) is a Button beside the table for the row this selects.
  • pageSize?: number | undefined: Rows per page (default 25); a pager appears when there is more than one page.
  • rowKey?: string | undefined: Stable row id: a column key (default: the row index).
  • rows: TableRow[]: All rows; the table filters, sorts and pages them itself.
  • sort?: { key: string; dir: "asc" | "desc"; } | undefined: Initial sort.

TableRow​

type TableRow = Record<string, unknown>;

One data row: a plain object keyed by Column.key. Cell values are formatted by formatCell.

ThemeMode​

type ThemeMode = 'light' | 'dark';

The two colour modes; read from html[data-theme], which the SDK runtime stamps from the shell.

TimeRange​

interface TimeRange

An ISO-8601 from / to pair, as TimeRangeField reads and reports it.

Members

  • from: string: Start instant, ISO-8601.
  • to: string: End instant, ISO-8601.

Tone​

type Tone = 'ok' | 'warn' | 'critical' | 'neutral';

The status a component shows (ok, warn, critical, neutral): a colour from Palette.status plus a ToneMark shape.