Skip to content

Focus Styles

Rezi is keyboard-first. Focus visuals are designed to be:

  • deterministic (no timing-based animation required)
  • readable in common terminal themes
  • non-color-only (so focus is visible in monochrome/high-contrast)

Focus appearance

For the built-in focusable widgets (e.g. button, input, select, checkbox, radioGroup), Rezi applies a consistent focus indicator:

  • underline + bold when focused

This is applied by the renderer and merged with any user-provided style prop.

All interactive widgets route focus styling through shared renderer helpers (resolveFocusIndicatorStyle() and resolveFocusedContentStyle()) to keep focus rendering consistent across widget kinds.

Focus ring

In a terminal, “focus ring” usually means an outline or a high-contrast marker. In Rezi v1, the default focus indicator is text underline + bold on the focused widget’s content.

Theme path behavior:

  • With ThemeDefinition semantics available, focus uses focus.ring token color in addition to underline + bold.
  • With legacy Theme only, focus falls back to underline + bold (no token-color focus accent).

focus.bg may be used for subtle focused-surface emphasis where widget renderers support a background hint.

If you want a more explicit “ring” around a region (for example, around an input group), you can draw it structurally:

import { ui } from "@rezi-ui/core";

ui.box({ border: "double", p: 1, title: "Focused Section" }, [
  ui.input({ id: "name", value: "Ada" }),
]);

For higher-level, app-specific focus rings, prefer explicit layout and borders over per-frame style mutations.

Disabled styling

When disabled: true is set on interactive widgets, Rezi makes the widget non-focusable and applies a deterministic gray foreground override.

Example:

import { ui } from "@rezi-ui/core";

ui.button({ id: "submit", label: "Submit", disabled: true });
ui.input({ id: "email", value: "test@example.com", disabled: true });

Per-Widget Focus Control with focusConfig

Interactive widgets accept an optional focusConfig prop that controls how focus visuals are rendered. This is useful when a widget is focusable (for keyboard/scroll routing) but should not display a focus indicator.

import type { FocusConfig } from "@rezi-ui/core";

Suppressing focus visuals

Set focusConfig: { indicator: "none" } to suppress the default focus highlight while keeping the widget focusable:

// Code editor receives focus for scroll routing, but no focus overlay
ui.codeEditor({
  id: "editor",
  language: "typescript",
  value: sourceCode,
  focusConfig: { indicator: "none" },
});

// File tree with no row-level focus highlight
ui.fileTreeExplorer({
  id: "files",
  data: fileTree,
  expanded: expandedPaths,
  focusConfig: { indicator: "none" },
  onChange: handleToggle,
  onSelect: handleSelect,
  onPress: handleActivate,
});

focusConfig.style overrides still work and are merged after DS/renderer focus style resolution.

Supported widgets

The following widgets accept focusConfig:

Widget Effect of indicator: "none"
button Suppresses focused label style (underline + bold)
input Suppresses focused input decoration
textarea Suppresses focused textarea decoration
select Suppresses focused select decoration
slider Suppresses focused slider highlight
virtualList Suppresses focused item highlight
table Suppresses focused row highlight
commandPalette Suppresses focused item highlight
filePicker Suppresses focused row highlight
fileTreeExplorer Suppresses focused node row highlight
codeEditor Suppresses active cursor cell highlight
diffViewer Suppresses focus decoration
logsConsole Suppresses focus decoration

When to use focusConfig

Use focusConfig: { indicator: "none" } when:

  • A widget must be focusable for event routing (scroll, keyboard) but has its own visual selection mechanism
  • The widget is embedded inside a pane with its own focus chrome (e.g., a bordered box with active/inactive border colors)
  • The default focus highlight conflicts with the widget’s own rendering (e.g., syntax highlighting in a code editor)

Customization

You can customize appearance without breaking focus visibility:

  • prefer fg/bg changes and keep underline available for focus
  • keep focusable widgets’ id stable so focus doesn’t jump
  • use the app theme for global consistency and inline styles for local emphasis
import { ui, rgb } from "@rezi-ui/core";

ui.button({
  id: "danger",
  label: "Delete",
  style: { fg: rgb(255, 110, 110), bold: true },
});

Accessibility

Practical guidelines for terminal UIs:

  • Do not rely on color alone; use underline/bold/labels to convey state.
  • Provide keyboard shortcuts for primary actions and document them with ui.kbd(...).
  • Ensure disabled controls are clearly indicated and not focusable.
  • Consider offering a high-contrast theme variant for low-vision users.
  • Input & Focus - Focus model and navigation
  • Icons - Visual cues that work in plain terminals
  • Theme - Global color decisions