reopt designreopt design
DocsExploreToolsPricingBuilder
Start
Overview
Start
Next.js 설치
Private install
Core Concepts
아키텍처
Composition Patterns
Accessibility
Keyboard Patterns
Styling
Theme System
Advanced Patterns
Build & Operate
Skills
AI Integration
CLI (opt surface add)
Dependency Graph
Tools
Canvas Catalog
Theme Builder
Form Builder
Templates
Templates
Releases
Release Notes
Oopt-ui
reopt designreopt design

A design system for the AI era

  • Docs
  • Pricing
  • Releases
  • GitHub
  • Terms of Service
  • Privacy Policy

© 2026 reopt-ai. All rights reserved.

Core Concepts
  1. Docs
  2. /
  3. Core Concepts
  4. /
  5. Keyboard Patterns

Keyboard patterns

A detailed walkthrough of Roving Tabindex, Spatial Navigation, and per-component keyboard interactions.

reopt design · Updated Jun 26, 2026

Roving Tabindex in detail

Roving Tabindex is a pattern that keeps a single Tab Stop inside a composite widget while letting arrow keys navigate between items. The Composite layer manages this automatically.

How it works

  1. All items in the container start with tabindex="-1".
  2. Only the currently active item is set to tabindex="0".
  3. Pressing Tab to enter the container focuses the active item.
  4. When you move with arrow keys, the previous item becomes tabindex="-1" and the new item becomes tabindex="0".
  5. Pressing Tab to leave the container moves to the next Tab Stop.
tsx
// CompositeZone은 built-in roving tabindex를 활용합니다
import { CompositeZone, CompositeRow, CompositeItem } from "@reopt-ai/opt-ui";

<CompositeZone
  aria-label="대시보드 그리드"
  focusLoop    // 마지막 항목에서 첫 항목으로 순환
  focusWrap    // 행 끝에서 다음 행으로 자동 이동
>
  <CompositeRow>
    <CompositeItem>카드 1</CompositeItem>
    <CompositeItem>카드 2</CompositeItem>
  </CompositeRow>
  <CompositeRow>
    <CompositeItem>카드 3</CompositeItem>
    <CompositeItem>카드 4</CompositeItem>
  </CompositeRow>
</CompositeZone>

focusLoop vs focusWrap

PropBehaviorUse case
focusLoopArrow keys wrap from the last item back to the firstToolbar, TabList, sidebar
focusWrapIn a Grid, reaching the end of a row moves to the next row automatically2D Grid (DashboardGrid, example lists)

orientation configuration

Use CompositeZone's orientation prop to control which axis arrow keys respond to.

orientationActive keysExample
"vertical"↑ ↓Sidebar, accordion, vertical menu
"horizontal"← →Toolbar, TabList, Menubar
omitted (both)↑ ↓ ← →Grid (DashboardGrid)
tsx
// 수직 방향만 반응하는 사이드바
<CompositeZone orientation="vertical" focusLoop>
  <CompositeItem>홈</CompositeItem>
  <CompositeItem>대시보드</CompositeItem>
  <CompositeItem>설정</CompositeItem>
</CompositeZone>

// 수평 방향만 반응하는 탭 목록
<TabList orientation="horizontal">
  <Tab>개요</Tab>
  <Tab>설정</Tab>
  <Tab>로그</Tab>
</TabList>

Spatial Navigation in detail

Spatial Navigation is the system that moves focus to the nearest focusable element using arrow keys outside of a Composite.

Distance score calculation

It calculates a score for each candidate in the direction of travel and moves to the element with the lowest score.

ts
// 점수 계산 공식
score = distance + (alignment_offset * penalty)

// distance: 두 요소 중심 간 직선 거리
// alignment_offset: 이동 방향의 수직축 오프셋
// penalty: 정렬 페널티 계수 (기본 0.5)

// 예시: 오른쪽으로 이동할 때
// - 완벽히 정렬된 요소 (Y축 차이 0): score = 100 + 0 = 100
// - 약간 어긋난 요소 (Y축 차이 50): score = 100 + 25 = 125
// → 정렬된 요소가 선택됨

Priority

  1. Composite handlers run first (defaultPrevented)
  2. Skip directions protected by input fields
  3. Skip inside Trapped Contexts (Dialog, Menu)
  4. If the above pass, Spatial Navigation kicks in

Per-component keyboard interactions

ComponentKeyboardNotes
CommandPaletteCmd+K to open, ↑↓ to navigate items, Enter to run, Esc to closeType to filter
DashboardGridTab to enter, ↑↓←→ for 4-way navigation, Enter to selectfocusLoop + focusWrap
ContentTabs←→ to switch tabs, Tab to enter panel, Home/EndAutomatic activation
FaqAccordion↑↓ to navigate items, Enter/Space to toggleVertical Composite
StatusSelectEnter to open, ↑↓ to navigate, Enter to select, Esc to closeListbox pattern
SearchComboboxType to filter, ↑↓ to navigate, Enter to selectInput-protected (←→)
SettingsFormTab to move between fields, Space for checkboxes, ←→ for radiosStandard form elements
EnvPanel↑↓ to navigate items, Enter/Space to toggleDisclosure-based

Keyboard checklist

A keyboard accessibility checklist to run through when building a new component.

  • Is the Tab order logical? (matches visual order)
  • Does it need an arrow-key navigation context (CompositeZone)?
  • Do elements that close with Esc restore focus correctly?
  • Is every interaction reachable via Enter/Space?
  • Is the focus indicator clear? (data-[focus-visible] styles)
  • Have aria-labels been applied to containers that need them?

Global shortcuts

ShortcutActionImplementation
Cmd+K / Ctrl+KToggle the command paletteapp/layout.tsx
Ctrl+Shift+FToggle the opt-devtool overlayapp/layout.tsx
↑ ↓ ← →Spatial Navigationspatial-nav.ts (global)
PreviousAccessibilitySpatial Navigation, 포커스 관리, WAI-ARIA 역할, 키보드 단축키Core Concepts
Go to Accessibility
NextStylingdata-attribute 셀렉터, className 오버라이드, 다크모드, 애니메이션Core Concepts