Skip to content

Focus Zone

Groups focusable widgets into a logical unit for Tab traversal. Focus zones help organize complex UIs by creating focus "islands" that users navigate between with Tab.

Usage

ui.focusZone(
  { id: "toolbar", tabIndex: 0 },
  [
    ui.button({ id: "new", label: "New" }),
    ui.button({ id: "open", label: "Open" }),
    ui.button({ id: "save", label: "Save" }),
  ]
)

Props

Prop Type Default Description
id string required Unique identifier for the zone
tabIndex number 0 Order in which zones receive focus (lower = earlier)
navigation "linear" \| "grid" \| "none" "linear" How focus moves within the zone
columns number 1 Number of columns for grid navigation
wrapAround boolean true Wrap from last to first item
onEnter () => void - Callback when zone receives focus
onExit () => void - Callback when focus leaves zone
key string - Reconciliation key

Linear (Default)

Arrow keys move through items in document order:

ui.focusZone({ id: "list", navigation: "linear" }, [
  ui.button({ id: "a", label: "A" }),
  ui.button({ id: "b", label: "B" }),
  ui.button({ id: "c", label: "C" }),
])
// Up/Down or Left/Right moves A -> B -> C

Grid

Arrow keys navigate a 2D grid layout:

ui.focusZone({ id: "grid", navigation: "grid", columns: 3 }, [
  ui.button({ id: "1", label: "1" }),
  ui.button({ id: "2", label: "2" }),
  ui.button({ id: "3", label: "3" }),
  ui.button({ id: "4", label: "4" }),
  ui.button({ id: "5", label: "5" }),
  ui.button({ id: "6", label: "6" }),
])
// Left/Right moves horizontally
// Up/Down moves between rows

None

Disables internal arrow key navigation (Tab still works):

ui.focusZone({ id: "custom", navigation: "none" }, [...])

Zone Ordering

Use tabIndex to control the order zones receive focus:

ui.column({}, [
  // User presses Tab: sidebar -> main -> footer
  ui.focusZone({ id: "sidebar", tabIndex: 0 }, [...]),
  ui.focusZone({ id: "main", tabIndex: 1 }, [...]),
  ui.focusZone({ id: "footer", tabIndex: 2 }, [...]),
])

Enter/Exit Callbacks

Track when focus enters or leaves a zone:

ui.focusZone({
  id: "search",
  onEnter: () => showSearchHint(),
  onExit: () => hideSearchHint(),
}, [
  ui.input({ id: "query", value: state.query }),
  ui.button({ id: "search", label: "Search" }),
])

Form Example

Organize a form into logical sections:

ui.column({ gap: 2 }, [
  ui.focusZone({ id: "credentials", tabIndex: 0 }, [
    ui.field({ label: "Username", children:
      ui.input({ id: "user", value: state.user })
    }),
    ui.field({ label: "Password", children:
      ui.input({ id: "pass", value: state.pass })
    }),
  ]),

  ui.focusZone({ id: "actions", tabIndex: 1 }, [
    ui.button({ id: "login", label: "Login" }),
    ui.button({ id: "cancel", label: "Cancel" }),
  ]),
])
  • Focus Trap - Constrain focus within a region
  • Modal - Modal dialog with focus trap
  • Button - Focusable button