Styling
Widgets are styled with a single style prop: a plain object of properties,
inline like React’s style but typed to what a terminal cell can actually show.
There are no stylesheets to wire up for the common case — set properties and
they apply.
import { Label } from "@huyz0/ztui/react";
<Label style={{ color: "$primary", bold: true, background: "$surface" }}> Hello</Label>;Colors
Section titled “Colors”color (foreground) and background accept:
- Theme tokens —
"$primary","$surface","$error", … Resolve against the active theme, so they adapt when the theme changes. Prefer these — see Theming for the full token list. - Hex —
"#4daafc"(truecolor; degraded automatically on 256-color terminals). - Named colors — standard CSS/ANSI names like
"red","cyan","gray". "transparent"— let the layer below show through.
<VBox style={{ background: "$panel" }}> <Label style={{ color: "$success" }}>ok</Label> <Label style={{ color: "#e06c75" }}>literal hex</Label></VBox>Theme tokens ($name)
Section titled “Theme tokens ($name)”Any string value may embed $name (or the CSS form var(--name)); it’s replaced
with the active theme’s color before painting. This is the portable way to color
things — the same widget tree looks right under every theme and on both backends.
Tokens resolve per-widget, so a widget’s theme override
flows to its descendants.
Text attributes
Section titled “Text attributes”Booleans toggle terminal cell attributes:
| Property | Effect |
|---|---|
bold | bold / bright text |
italic | italic (where the terminal supports it) |
underline | underline |
strikethrough | struck-through |
dim | reduced intensity |
reverse | swap fg/bg |
<Label style={{ bold: true, underline: true }}>emphasis</Label>link: "https://…" marks the text as a hyperlink (OSC 8) on terminals that
support it.
Mouse pointer shape
Section titled “Mouse pointer shape”cursor sets the mouse-pointer shape shown while the pointer is over a widget,
using the CSS cursor names (pointer, text, grab, grabbing, not-allowed,
progress, wait, crosshair, move, zoom-in, and the *-resize family —
note the OSC 22 names like ew-resize/ns-resize, not col-resize):
<Box style={{ cursor: "pointer" }} onClick={…}>Open</Box><Box style={{ cursor: "text" }}>Editable</Box><Box style={{ cursor: "ew-resize" }}>│ drag to resize</Box>The shape is inherited from the nearest ancestor that sets cursor, so wrapping
a clickable region is enough — children don’t each need it. It relies on the
terminal’s OSC 22 support (capabilities.pointerShapes: kitty, foot, recent
xterm; opt-in on Alacritty) and is ignored elsewhere, with the pointer reset to
the default arrow over empty space and on exit.
Built-in widgets already pick a sensible default, so you usually don’t set
cursor yourself: interactive controls (buttons, checkboxes, switches, radios,
toggle buttons, selects, sliders, menus, tabs, collapsibles, lists/trees) and
any widget with an onClick show pointer; text inputs, textareas, and the
chat composer show text; and a disabled control shows not-allowed. Set
cursor explicitly to override.
Box properties
Section titled “Box properties”Sizing, spacing, borders, alignment, docking, and overflow are all set through
the same style object and are covered in Layout:
width/height, padding/margin, border/borderColor, align, dock,
overflowX/overflowY, and so on.
Defaults and precedence
Section titled “Defaults and precedence”Every widget has a defaultStyle (its built-in look) that your style props
override key-by-key. So you only specify what differs:
<Button style={{ background: "$success" }}>Save</Button> // keeps the rest of Button's lookInteractive widgets (Button, Input, Select, …) manage their own
focus and hover visuals — a focused control gets a gently animated focus
ring, hovered controls highlight — so you don’t style those states by hand. For
custom :hover / :focus rules on your own widgets you can load a CSS-like
stylesheet with pseudo-selectors, but inline style plus theme tokens covers
nearly everything.
How a value becomes a cell
Section titled “How a value becomes a cell”When a frame renders, each widget’s style is resolved into a concrete
computedStyle: $tokens are looked up against the active theme, focus/hover
state is folded in, and the result is written into the ScreenBuffer
as per-cell character + colors + attributes. Colors that the terminal can’t show
natively are degraded (truecolor → 256 → 16) by the driver, so you author once in
hex or tokens and let the backend match the device.