Styling
Rezi styling is designed to be:
Rezi styling is designed to be:
- explicit: styles are passed as props
- deterministic: the same inputs produce the same frames
- composable: styles inherit through containers
Text attributes
TextStyle supports these boolean text attributes:
bolddimitalicunderlineinversestrikethroughoverlineblink
Extended underline fields:
underlineStyle?: "none" | "straight" | "double" | "curly" | "dotted" | "dashed"underlineColor?: string | ThemeColor
New attribute SGR target mappings:
strikethrough-> SGR9overline-> SGR53blink-> SGR5
These codes are the terminal mapping used by the backend emitter. Drawlist encoding carries all three attrs, and backend emission now supports strikethrough, overline, and blink end-to-end (terminal rendering still depends on terminal support). Underline variants and underline color use extended style fields on compatible drawlist versions.
Inline styles
Most visual widgets accept a style prop:
import { ui, rgb } from "@rezi-ui/core";
ui.text("Warning", { style: { fg: rgb(255, 180, 0), bold: true } });
ui.box({ border: "rounded", p: 1, style: { bg: rgb(20, 20, 24) } }, [
ui.text("Panel content"),
]);When a container (row, column, box) has a style, that style is inherited by its children and can be overridden per-widget.
Theme-based styling
Themes provide consistent defaults (background/foreground, widget chrome, etc.) and are applied at the app level:
import { ui, darkTheme } from "@rezi-ui/core";
import { createNodeApp } from "@rezi-ui/node";
const app = createNodeApp({ initialState: {}, theme: darkTheme });
app.view(() => ui.text("Hello"));
await app.start();Switching themes at runtime:
app.setTheme(darkTheme);Runtime guarantees for setTheme:
- it can be called before
start()and while running - it throws if called during render/commit
- it is a no-op when the effective theme identity is unchanged
- a theme change triggers a full redraw path
Theme validation and extension
Theme hardening APIs are available from @rezi-ui/core:
validateTheme(theme)for strict token validationextendTheme(base, overrides)for deep-merge inheritance + validationcontrastRatio(fg, bg)for WCAG contrast calculations
Theme tokens include a diagnostic palette:
diagnostic.errordiagnostic.warningdiagnostic.infodiagnostic.hint
Example:
import { darkTheme, extendTheme, validateTheme } from "@rezi-ui/core";
const brandTheme = extendTheme(darkTheme, {
colors: { accent: { primary: { r: 255, g: 180, b: 84 } } },
});
validateTheme(brandTheme);Scoped theme overrides
box, row, and column accept a scoped theme override prop:
import { ui } from "@rezi-ui/core";
ui.column({}, [
ui.text("parent"),
ui.box({ theme: { colors: { primary: { r: 90, g: 200, b: 140 } } } }, [ui.text("scoped")]),
ui.text("parent restored"),
]);Behavior:
- nested scopes compose (inner override wins)
- exiting a scoped subtree restores parent theme
- partial overrides inherit unspecified parent tokens
See: Theme.
Decision guide
Use inline styles when:
- you need one-off emphasis (errors, highlights)
- a widget needs a custom color not tied to semantics
Use themes when:
- you want consistent styling across many widgets
- you support light/dark/high-contrast variants
- you want to centralize visual decisions
In practice, most apps use both: a theme for defaults + inline styles for local emphasis.
Style inheritance
Style is merged from parent → child:
- containers pass their resolved style to children
- leaf widgets merge their own
styleon top - boolean attrs use tri-state semantics:
undefinedinherits,falsedisables,trueenables box/row/columncan also apply scopedthemeoverrides to descendants- when container
style.bgis set, that container rect is filled
Example:
import { ui, rgb } from "@rezi-ui/core";
ui.box({ p: 1, style: { fg: rgb(200, 200, 255) } }, [
ui.text("Inherits fg"),
ui.text("Overrides", { style: { fg: rgb(255, 200, 120), bold: true } }),
]);Dynamic styles
Compute styles from state, but keep view(state) pure (no timers, no I/O):
import { ui, rgb } from "@rezi-ui/core";
ui.text(state.connected ? "Online" : "Offline", {
style: { fg: state.connected ? rgb(80, 220, 120) : rgb(255, 100, 100) },
});Related
- Style props -
TextStyle, spacing props, helpers - Theme - Theme structure and built-ins
- Icons - Icon registry and fallback rules
- Focus styles - Focus and disabled visuals
- Text style internals - Drawlist bit layout and merge/cache internals
Next: Performance.