Rezi Design System
The Rezi design system is the semantic styling layer on top of ThemeDefinition, ColorTokens, and recipe.*.
The Rezi design system is the semantic styling layer on top of ThemeDefinition,
ColorTokens, and recipe.*.
Its goals are:
- consistent defaults across core widgets
- semantic token authoring instead of ad-hoc RGB literals
- predictable override behavior
- stable renderer output for tests and snapshots
Architecture
The public model is:
ThemeDefinitionprovides semantic theme tokens.- Renderer/widget code reads those tokens through shared helpers.
recipe.*turns tokens into widget-level styles.- Widget-specific manual props merge on top of recipe output.
Advanced widget surfaces use dedicated widget token families:
widget.syntaxwidget.diffwidget.logswidget.toastwidget.chart
Renderer-backed defaults
Recipe styling is enabled by default for the core design-system-backed widgets, including:
- buttons
- inputs and textareas
- checkboxes and radio groups
- selects
- sliders
- tables
- progress
- badges
- callouts
- tabs
- accordion
- breadcrumb
- pagination
- kbd
- dropdown
- tree
- modal
This does not mean every visual primitive has a standalone ui.* wrapper.
Important distinctions:
recipe.surface(...)exists, but there is no standaloneui.surface(...).recipe.text(...)exists, but plainui.text(...)is not globally recipe-driven.recipe.scrollbar(...)exists, but overflow scrollbars are not universally rendered through that recipe path yet.ui.divider(...)is theme-aware, but it is not part of the same “full recipe widget coverage” story as buttons/inputs/selects.
Overrides
Manual widget props do not disable the design system.
Common merge order:
- resolve theme tokens
- compute recipe defaults
- merge widget-level manual overrides such as
style,pressedStyle,selectionStyle,trackStyle,px, and similar props
This keeps defaults stable while still allowing targeted changes.
Scoped theme overrides
Use ui.themed(...) or a container theme prop to override a subtree.
import { rgb, ui } from "@rezi-ui/core";
ui.row({ gap: 1 }, [
ui.themed(
{
colors: {
accent: {
primary: rgb(255, 140, 90),
},
},
spacing: {
md: 3,
},
},
[ui.box({ p: 1 }, [ui.text("Scoped subtree")])],
),
ui.box({ flex: 1, p: 1 }, [ui.text("Parent theme")]),
]);Scoped overrides:
- inherit unspecified values from the parent theme
- can override
colors,spacing,focusIndicator, andwidgetpalettes - compose predictably when nested
Focus system
Focus styling is token-driven:
colors.focus.ringcontrols focus accent colorcolors.focus.bgprovides subtle focused-surface tint where supportedfocusIndicator.boldandfocusIndicator.underlinedefine default text focus treatment
Widgets may also accept focusConfig to change or suppress their local focus
presentation without changing keyboard focus behavior.
Spacing scale
Theme spacing is semantic and required:
| Token | Cells |
|---|---|
xs | 1 |
sm | 1 |
md | 2 |
lg | 3 |
xl | 4 |
2xl | 6 |
Recipe sizing maps directly to that scale:
sm->{ px: spacing.sm, py: 0 }md->{ px: spacing.md, py: 0 }lg->{ px: spacing.lg, py: 1 }
Theme transitions
AppConfig.themeTransitionFrames controls theme interpolation during
app.setTheme(...).
0: instant swap> 0: interpolate colors across the configured number of frames
Spacing and focus-indicator structure are not tweened per cell; the transition is primarily a color interpolation path.
Direct recipe use
Use recipe.* when building custom widgets with defineWidget(...).
import { defineWidget, recipe, ui } from "@rezi-ui/core";
const MetricTile = defineWidget\<{ label: string; value: string; key?: string }>((props, ctx) => {
const tokens = ctx.useTheme();
const surface = recipe.surface(tokens, { elevation: 1 });
const labelStyle = recipe.text(tokens, { role: "caption" });
const valueStyle = recipe.text(tokens, { role: "title" });
return ui.box({ border: surface.border, style: surface.bg, p: 1 }, [
ui.text(props.label, { style: labelStyle }),
ui.text(props.value, { style: valueStyle }),
]);
});Theme authoring
Use createThemeDefinition(...) for new themes and extendTheme(...) for
variants.
import { createThemeDefinition, rgb } from "@rezi-ui/core";
export const myTheme = createThemeDefinition(
"my-theme",
{
bg: {
base: rgb(10, 14, 20),
elevated: rgb(15, 20, 28),
overlay: rgb(24, 30, 40),
subtle: rgb(20, 25, 34),
},
fg: {
primary: rgb(231, 236, 242),
secondary: rgb(142, 155, 170),
muted: rgb(96, 107, 121),
inverse: rgb(10, 14, 20),
},
accent: {
primary: rgb(255, 180, 84),
secondary: rgb(89, 194, 255),
tertiary: rgb(149, 230, 203),
},
success: rgb(170, 217, 76),
warning: rgb(255, 180, 84),
error: rgb(240, 113, 120),
info: rgb(89, 194, 255),
focus: { ring: rgb(255, 180, 84), bg: rgb(26, 31, 38) },
selected: { bg: rgb(39, 55, 71), fg: rgb(231, 236, 242) },
disabled: { fg: rgb(96, 107, 121), bg: rgb(15, 20, 28) },
diagnostic: {
error: rgb(240, 113, 120),
warning: rgb(255, 180, 84),
info: rgb(89, 194, 255),
hint: rgb(149, 230, 203),
},
border: {
subtle: rgb(26, 31, 38),
default: rgb(96, 107, 121),
strong: rgb(142, 155, 170),
},
},
);createThemeDefinition(...) fills default spacing, focusIndicator, and
widget palettes when they are not provided explicitly.
Verification
For design-system work, verify:
- recipe unit tests
- renderer integration tests
- golden fixture updates when renderer bytes change
- at least one live PTY spot-check in a built-in theme