reopt designreopt design
DocsExploreToolsPricingBuilder
Login
Start
Overview
Start
Next.js 설치
Manual 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
  • About
  • Contact
  • Terms of Service
  • Privacy Policy

reopt Inc.Business registration no. 217-88-02453contact@reopt.ai

© 2026 reopt Inc. All rights reserved.

Core Concepts
  1. Docs
  2. /
  3. Core Concepts
  4. /
  5. 디자인 규칙

디자인 규칙

opt-ui를 사용하는 프로젝트가 지켜야 하는 접근성·스타일링·안티패턴 규칙과, 각 규칙을 실제로 막는 주체를 정리합니다.

reopt design · Updated Aug 30, 2026

개요

규칙은 총 56개입니다. 각 규칙에는 무엇이 이것을 막는지가 명시되어 있습니다 — 11개는 bun run lint가 CI에서 차단하고, 38개는 /opt-ui-guide 스킬이 스캔하며, 6개는 코드 리뷰에서 확인합니다.

이 페이지와 스킬의 레퍼런스 문서는 모두 apps/web/data/design-rules.ts 한 파일에서 나옵니다. 생성기는 렌더링 전에 규칙이 참조하는 Oxlint 룰이 실제로 존재하는지, advisory 표시가 루트 설정의 on/off와 맞는지, 플러그인의 모든 룰이 문서 항목을 갖는지 검증합니다. 따라서 여기 적힌 “무엇이 막는지”는 CI가 실제로 하는 일과 어긋날 수 없습니다.

접근성 (12)

스크린 리더와 키보드 사용자가 화면에 도달할 수 있게 하는 규칙입니다. 대부분 자동 검출이 어려워 스킬 스캔과 리뷰가 담당합니다.

A01

Interactive element must have accessible name

error

/opt-ui-guide 스킬 스캔

Every interactive element (`<button>`, `<a>`, `Button`, `MenuItem`, …) must expose an accessible name via visible text children, `aria-label`, or `aria-labelledby` pointing at a visible element.

지양

<button onClick={handleClick}>
  <IconTrash />
</button>

권장

<button onClick={handleClick} aria-label="Delete item">
  <IconTrash />
</button>

검출 Find `<button`, primitive button wrappers, and `<a ` elements. Check for text children, `aria-label`, or `aria-labelledby`. Icon-only buttons (children is only a component starting with `Icon` or `<svg>`) without `aria-label` trigger this rule.

A02

Images must have alt text or aria-hidden

error

/opt-ui-guide 스킬 스캔

All `<img>` elements must have `alt`. Decorative images use `alt=""` or `aria-hidden="true"`. SVGs used as icons must be `aria-hidden="true"`.

지양

<img src="/logo.png" />
<svg viewBox="0 0 24 24">...</svg>

권장

<img src="/logo.png" alt="Company logo" />
<svg viewBox="0 0 24 24" aria-hidden="true">...</svg>

검출 Find `<img` without `alt`. Find `<svg` without `aria-hidden` that is not inside a labeled button.

A03

Form inputs must have associated label

error

/opt-ui-guide 스킬 스캔

Every `<input>`, `<select>`, `<textarea>`, and opt-ui form control must have a `<label>` with matching `htmlFor`/`id`, an `aria-label`, or an `aria-labelledby`.

지양

<input type="text" placeholder="Search..." />

권장

<label htmlFor="search">Search</label>
<input id="search" type="text" placeholder="Search..." />

검출 Find `<input`, `<select`, `<textarea` elements. Check for a `<label htmlFor=` matching the `id`, or `aria-label`/`aria-labelledby` on the element. An opt-ui `<FormInput>` with a `name` prop that has a corresponding `<FormLabel>` is acceptable.

A04

Error messages must use aria-describedby

error

/opt-ui-guide 스킬 스캔

Error messages tied to a form input must be linked via `aria-describedby` and carry `role="alert"` so they are announced.

지양

<input />
{error && <span className="text-red-500">{error}</span>}

권장

<input aria-invalid={!!error} aria-describedby="email-error" />
{error && (
  <span id="email-error" role="alert">
    {error}
  </span>
)}

검출 Find an error `<span>`/`<p>` rendered near an input where the input has no `aria-describedby` pointing at the error element's `id`.

A05

Semantic roles for custom widgets

error

/opt-ui-guide 스킬 스캔

Custom widgets must declare their role explicitly when HTML semantics do not convey it: toggle → `role="switch"`, tab panel → `role="tabpanel"`, status indicator → `role="status"`, alert → `role="alert"`.

예외 opt-ui primitives with built-in roles are exempt.

검출 Look for components rendering `<div>`/`<span>` with toggle, switch, or status behavior but no `role` attribute.

A06

Focus must be visible

error

Oxlint opt-ui/no-outline-none-without-focus-visible — bun run lint가 차단

All interactive elements need visible focus indication. Use `focus-visible:ring-2 focus-visible:ring-blue-600` or `data-[focus-visible]:outline-2 data-[focus-visible]:outline-blue-600`.

Do NOT use `focus:` — use `focus-visible:` so mobile taps do not flash a focus ring.

지양

<button className="outline-none">Click</button>

권장

<button className="outline-none focus-visible:ring-2 focus-visible:ring-blue-600">
  Click
</button>

검출 Find interactive elements with `outline-none` but no `focus-visible:` or `data-[focus-visible]:` styles. Primitives that receive focus tokens (`OPT_FOCUS`, `OPT_FOCUS_VISIBLE`) via `cn()` are acceptable.

A07

onClick must have keyboard equivalent

warn

/opt-ui-guide 스킬 스캔

Non-button elements with `onClick` must also handle `onKeyDown` for Enter/Space, or use a semantic element (`<button>`, `<a>`, `Button`).

지양

<div onClick={handleClick}>Clickable area</div>

권장

<button onClick={handleClick}>Clickable area</button>

검출 Find `<div`/`<span` with `onClick` and no `onKeyDown`. Overlaps X01, but A07 is specifically about keyboard equivalence.

A08

Color-only status must have text alternative

warn

/opt-ui-guide 스킬 스캔

Status indicators conveyed only by color (green/red/yellow dots) must also provide text or an `aria-label` for colorblind users.

색상만으로 상태를 전달하면 색각 이상 사용자와 스크린 리더 사용자에게 아무 정보도 남지 않습니다.

지양

api-gateway
worker-queue

권장

api-gateway정상
worker-queue중단

지양

<span className="h-2 w-2 rounded-full bg-green-500" />

권장

<span className="h-2 w-2 rounded-full bg-green-500" aria-label="Active" />

검출 Find elements with only dot-style classes (`h-2 w-2 rounded-full bg-{color}-500`) and no `aria-label` or adjacent text.

A09

Dialog/modal must trap focus

warn

/opt-ui-guide 스킬 스캔

Dialogs and modals must trap focus. Use opt-ui's `Dialog`, which handles this automatically.

검출 Find a raw `<div>` rendered as a modal (`fixed inset-0` or a portal) without opt-ui `Dialog`. Hand-rolled focus traps are acceptable but should be flagged as `info`.

A10

Skip link or landmark regions

info

/opt-ui-guide 스킬 스캔

Page-level Blocks should use landmark regions (`<main>`, `<nav>`, `<aside>`, `<header>`, `<footer>`) or provide a skip link.

검출 In Block files, check for at least one landmark element or equivalent `role` (`role="main"`, `role="navigation"`).

A11

Heading hierarchy must be logical

info

/opt-ui-guide 스킬 스캔

Components must not skip heading levels — an `<h1>` followed by `<h3>` with no `<h2>` between them.

레벨을 건너뛰면 스크린 리더의 제목 목록에서 문서 구조가 무너집니다.

지양

h1 · 워크스페이스

h3 · 멤버 목록 (h2 없음)

권장

h1 · 워크스페이스

h2 · 설정

h3 · 멤버 목록

검출 Collect `<h1>`–`<h6>` in the file and check for skipped levels.

A12

Live region for dynamic content

info

/opt-ui-guide 스킬 스캔

Content that updates dynamically (status changes, loading states, notifications) should use `aria-live="polite"` or `aria-live="assertive"`.

검출 Find conditional rendering of status text or notifications where the container has no `aria-live`.

스타일링 (13)

색상·간격·테마가 프로젝트 전반에서 일관되게 유지되도록 하는 규칙입니다.

S01

Use OPT_* tokens instead of legacy token constants

warn

Oxlint opt-ui/no-legacy-tokens — bun run lint가 차단

Legacy token constants are retired. Import the `OPT_*` design tokens instead so themes stay in sync.

S02

No hardcoded zinc colors

warn

Oxlint opt-ui/no-zinc-colors — bun run lint가 차단

Replace hardcoded `zinc-*` colors with opacity-based equivalents.

Zinc patternOpacity replacement
`text-zinc-900``text-black/80`
`text-zinc-700``text-black/80` (or `OPT_TEXT_PRIMARY`)
`text-zinc-500``text-black/45` (or `OPT_TEXT_TERTIARY`)
`text-zinc-400``text-white/35` (dark mode)
`text-zinc-300``text-white/55` (dark mode)
`text-zinc-200`use the `OPT_BORDER` pattern
`text-zinc-100`use `bg-black/[0.04]`
`border-zinc-200``border-black/[0.13]`
`border-zinc-700``border-border`
`bg-zinc-100``bg-black/[0.04]`
`bg-zinc-800``bg-bg-muted`
`bg-zinc-900``bg-surface`

지양

<div className="text-zinc-700 dark:text-zinc-300">

권장

<div className="text-black/80 dark:text-white/80">

검출 Regex for `zinc-\d{2,3}` in className strings and template literals.

S03

Dark mode background must use a semantic surface token

warn

/opt-ui-guide 스킬 스캔

Dark mode backgrounds use `bg-surface`, not a hardcoded HSL or `zinc-900`. The default dark theme defines distinct base, raised, and overlay surface levels, and semantic utilities keep consumers aligned when those values change.

지양

<div className="bg-white dark:bg-zinc-900">

권장

<div className="bg-white dark:bg-surface">

검출 Find `dark:bg-zinc-900` in className strings.

S04

Focus ring must use blue-600

warn

Oxlint opt-ui/prefer-blue-600-focus — bun run lint가 차단

Focus indicators use `blue-600`, not `blue-500`, to match the opt-ui focus color.

지양

focus-visible:ring-blue-500

권장

focus-visible:ring-blue-600

검출 Find `blue-500` in focus-related classes (`ring-blue-500`, `outline-blue-500`).

S05

Border opacity must use black/white alpha

warn

/opt-ui-guide 스킬 스캔

Borders use the opacity-based pattern so they hold up across themes.

지양

border-zinc-200 dark:border-zinc-700

권장

border-border

검출 Find `border-zinc-` patterns in className strings.

S06

Consistent border radius

info

/opt-ui-guide 스킬 스캔

Use a consistent border radius per element type within a file.

RadiusApplies to
`rounded-lg`containers, inputs, buttons
`rounded-md`inner items (menu items, list items)
`rounded-xl`elevated cards and surfaces
`rounded-full`badges, avatars, dots

같은 종류의 요소에 반경이 섞이면 카드 모서리가 미묘하게 어긋나 보입니다.

지양

rounded-lg 항목

rounded-sm 항목

rounded-xl 항목

권장

첫 번째 항목

두 번째 항목

세 번째 항목

검출 Collect all `rounded-*` classes and flag files that mix `rounded-lg` and `rounded-md` for the same kind of element.

S07

Dark mode class must pair with light mode

warn

/opt-ui-guide 스킬 스캔

Every `dark:` utility needs a corresponding light-mode class. An unpaired dark class means the light theme was never styled.

지양 — dark text with no light equivalent

<div className="dark:text-white/80">

권장

<div className="text-black/80 dark:text-white/80">

예외 `dark:ring-offset-*` and `dark:placeholder:*` may stand alone.

검출 Find `dark:` prefixed classes and verify a non-dark equivalent exists on the same element.

S08

Prefer token import over inline repetition

info

/opt-ui-guide 스킬 스캔

If the same Tailwind class combination appears three or more times in a file, extract it to a local constant or import an existing token.

검출 Find className strings that repeat the same 3+ class combination more than twice.

S09

No arbitrary color values outside tokens

info

/opt-ui-guide 스킬 스캔

Avoid arbitrary color values (`bg-[#ff0000]`, `text-[rgb(255,0,0)]`). Use design system colors (blue, red, emerald, amber) or opacity-based tokens.

예외 HSL dark mode backgrounds `hsl(204 4% *)` are acceptable.

검출 Find `[#`, `[rgb`, or `[hsl` in className strings, excluding the standard dark-mode HSL values.

S10

No !important in Tailwind classes

info

/opt-ui-guide 스킬 스캔

Avoid the `!important` modifier. If specificity fights you, restructure the component instead.

지양

<div className="!text-red-500">

권장

<div className="text-red-500">

검출 Find a `!` prefix on Tailwind class names within className strings.

S11

Prefer semantic spacing tokens

warn

Oxlint opt-ui/prefer-semantic-spacing (advisory — CI에서 off, 스킬이 검사)

Use semantic spacing tokens instead of raw numeric values so spacing stays consistent across themes.

CSS variables: `--opt-space-section` (1.5rem), `--opt-space-group` (1rem), `--opt-space-element` (0.5rem).

Raw valueSemantic token
`gap-6`, `space-y-6``gap-section` — between major sections (24px)
`gap-4`, `space-y-4``gap-group` — between related items (16px)
`gap-2`, `space-y-2``gap-element` — between small items (8px)

시맨틱 스페이싱은 섹션·그룹·요소의 위계를 눈에 보이게 만듭니다. 임의 숫자는 위계를 지웁니다.

지양

알림 설정

업데이트를 알림으로 받습니다

보안

2단계 인증을 사용합니다

권장

알림 설정

업데이트를 알림으로 받습니다

보안

2단계 인증을 사용합니다

지양 — raw numeric spacing

<div className="space-y-6">

권장

<div className="flex flex-col gap-section">

검출 Find `gap-4`, `gap-6`, `gap-8`, `space-y-4`, `space-y-6`, `space-y-8` in className strings and suggest semantic equivalents.

S12

No space-y/space-x utilities

warn

Oxlint opt-ui/no-space-y (advisory — CI에서 off, 스킬이 검사)

`space-y-*` and `space-x-*` compile to the `> * + *` selector, which only applies margin between direct sibling children. Wrap the content in a single element and the spacing silently becomes zero.

Always use `flex` plus `gap-*` instead.

래퍼가 하나 끼는 순간 space-y의 `> * + *` 선택자가 형제를 찾지 못해 간격이 0이 됩니다. gap은 영향을 받지 않습니다.

지양

첫 번째 카드

래퍼 안에 있습니다

두 번째 카드

간격이 사라졌습니다

권장

첫 번째 카드

래퍼 안에 있습니다

두 번째 카드

간격이 유지됩니다

지양 — breaks as soon as a wrapper wraps the content

<div className="space-y-6">

권장 — gap works regardless of nesting

<div className="flex flex-col gap-section">

검출 Find `space-y-` or `space-x-` in className strings, template literals, and `cn()` arguments.

S13

No raw color classes

warn

Oxlint opt-ui/no-raw-color (advisory — CI에서 off, 스킬이 검사)

Raw color utilities (`text-white`, `bg-amber-500`, and the rest of the Tailwind palette) bypass theming. Use semantic tokens — `text-accent-fg`, `text-danger-fg`, `text-text-{primary,secondary,tertiary}`, `bg-surface` — or their alpha aliases such as `text-accent-fg/60`.

For composite item active state, drive children from `OPT_ACTIVE_ITEM` (`lib/styles.ts`) with `data-[active-item]:text-accent-fg/{N}` or `bg-accent-fg/{N}`.

Intentional exceptions (`text-white` over a saturated badge background, marquee selection, padding visualisation) need an inline suppression with a reason.

검출 The `dark:` prefix is allowed automatically; other raw palette classes are reported.

안티패턴 (11)

리뷰에서 반복적으로 지적되는 실수들입니다. 절반 이상이 Oxlint로 차단됩니다.

X01

div/span as interactive element

error

Oxlint opt-ui/no-div-onclick — bun run lint가 차단

Never use `<div>` or `<span>` as a clickable element. Non-semantic interactive elements are invisible to screen readers and unreachable by keyboard — use `<button>`, `<a>`, or an accessible component.

지양

<div onClick={handleClick} className="cursor-pointer">
  Click me
</div>

권장

<button onClick={handleClick}>
  Click me
</button>

예외 `<div onClick>` inside a `role="grid"` or composite context where a framework manages keyboard navigation.

검출 Find `<div`/`<span` with an `onClick` prop, excluding elements carrying `role="button"`, `role="option"`, `role="tab"`, or `role="gridcell"`.

X02

cursor-pointer on non-interactive element

warn

/opt-ui-guide 스킬 스캔

`cursor-pointer` on a `<div>` or `<span>` is a code smell: the element should be a button. If it really is non-interactive, drop the class.

지양

<div className="cursor-pointer" onClick={handleClick}>

권장

<button className="cursor-pointer" onClick={handleClick}>

검출 Find `cursor-pointer` in className on `<div`/`<span` elements.

X03

onClick on non-interactive element without role

error

/opt-ui-guide 스킬 스캔

In the rare case `onClick` must live on a non-button element, it needs `role="button"` (or the appropriate role), `tabIndex={0}`, and an `onKeyDown` handler for Enter/Space.

지양

<div onClick={handleClick}>

허용 — prefer `<button>`

<div role="button" tabIndex={0} onClick={handleClick} onKeyDown={handleKeyDown}>

검출 Find `<div`/`<span` with `onClick` and no `role` attribute.

X04

Prefer the opt-ui Form over a raw form element

warn

Oxlint opt-ui/prefer-primitives-form (advisory — CI에서 off, 스킬이 검사)

Use the opt-ui `Form` primitives rather than a raw `<form>`, so validation, labelling, and submission stay consistent.

X05

No legacy and OPT_* tokens in the same file

warn

Oxlint opt-ui/no-mixed-tokens — bun run lint가 차단

Mixing legacy token constants with `OPT_*` tokens in one file produces inconsistent theming. Migrate the file wholesale.

X06

No hardcoded user-facing strings

warn

Oxlint opt-ui/require-labels-no-hardcode (advisory — CI에서 off, 스킬이 검사)

User-facing copy belongs in a `Labels` interface so consumers can translate it.

X07

console.log in production code

warn

Oxlint opt-ui/no-console-log — bun run lint가 차단

Remove `console.log`, `console.debug`, and `console.info`. `console.warn` and `console.error` are fine for genuine warnings and failures.

지양

console.log("render count:", count);

허용

console.warn("Deprecated prop used");
console.error("Failed to load data", error);

검출 Find `console.log(`, `console.debug(`, `console.info(` in non-test files.

X08

any type assertion

warn

/opt-ui-guide 스킬 스캔

Avoid `as any`, `: any`, and `any[]`. Model the type properly, or use `unknown` with a type guard.

지양

const data = response as any;
function process(items: any[]) { ... }

권장

const data = response as ApiResponse;
function process(items: unknown[]) { ... }

검출 Find `as any`, `: any`, `any[]`, `any>`, `<any` in TypeScript code, excluding comments.

X09

oxlint-disable / ts-ignore without explanation

info

Oxlint opt-ui/require-suppress-explanation (advisory — CI에서 off, 스킬이 검사)

Every suppression comment must say why the suppression is needed.

Prefer `@ts-expect-error` over `@ts-ignore` — it errors once the suppression is no longer needed.

지양

// oxlint-disable-next-line
// @ts-ignore

권장

// oxlint-disable-next-line react/exhaustive-deps -- intentionally empty deps
// @ts-expect-error -- type mismatch, fixed in next version

검출 Find `oxlint-disable`, `@ts-ignore`, `@ts-expect-error` comments and check for explanatory text after `--` or on the same line.

X10

Deeply nested ternary expressions

info

Oxlint opt-ui/no-deep-ternary (advisory — CI에서 off, 스킬이 검사)

Ternaries nested three or more levels deep are hard to read. Use early returns, `if`/`else`, or a lookup object.

지양

const color =
  status === "active"
    ? type === "admin"
      ? role === "super"
        ? "blue"
        : "green"
      : "yellow"
    : "gray";

권장

function getColor(status: string, type: string, role: string) {
  if (status !== "active") return "gray";
  if (type !== "admin") return "yellow";
  return role === "super" ? "blue" : "green";
}

검출 Count ternary nesting depth and flag at depth 3 or more.

X11

No deprecated opt-ui components or props

warn

Oxlint opt-ui/no-deprecated-api — bun run lint가 차단

Components and props carrying a `deprecated` notice in `component-catalog.json` must not be used in new code. The notice names the replacement.

The rule is generated from component metadata: adding `deprecated` to a `ComponentMeta` or `PropDef` is the only step needed to start blocking a usage.

지양

<Switch label="알림" description="설명 텍스트" />

권장

<Switch label="알림" hint="설명 텍스트" />

검출 Resolve each JSX element back to the `@reopt-ai/opt-*` export it was imported from, then look the component and its props up in the catalog.

컴포넌트 · 레이어 (14)

모노레포 안에서 컴포넌트를 작성할 때 지켜야 하는 구조 계약입니다. 소비자 프로젝트에는 적용되지 않습니다.

C01

No upward layer imports

error

Oxlint opt-ui/no-upward-imports — bun run lint가 차단

Imports flow Core → Shells → Blocks and never the other way. A Core component may not import from `shells/`.

C02

No cross-package imports from retired packages

error

Oxlint opt-ui/no-cross-package-import — bun run lint가 차단

opt-ui must not import from the retired opt-ui-surface package.

C03

"use client" directive on interactive files

warn

Oxlint opt-ui/require-use-client (advisory — CI에서 off, 스킬이 검사)

Files using React hooks, `forwardRef`, `createContext`, `memo`, `lazy`, inline JSX event handlers, or function props need a `"use client"` directive at the top.

C03b

No unnecessary "use client"

warn

Oxlint opt-ui/no-unnecessary-use-client (advisory — CI에서 off, 스킬이 검사)

Static components stay server-safe so the RSC tree keeps its efficiency. Remove `"use client"` where no interactive pattern is present.

C04

Props interface follows {ComponentName}Props

warn

Oxlint opt-ui/props-naming (advisory — CI에서 off, 스킬이 검사)

A component's props interface is named after the component.

C05

forwardRef components set displayName

warn

Oxlint opt-ui/require-display-name (advisory — CI에서 off, 스킬이 검사)

Without `displayName`, React DevTools shows the component as anonymous.

C06

Shells and Blocks define Labels interfaces

warn

Oxlint opt-ui/require-labels (advisory — CI에서 off, 스킬이 검사)

Every user-facing string is injectable through a `Labels` interface for i18n.

C07

Data components handle empty state

info

Oxlint opt-ui/require-empty-state (advisory — CI에서 off, 스킬이 검사)

A component that renders a collection must render something when it is empty.

C08

Components are exported from index.ts

info

테스트 — packages/opt-ui/src/__tests__/meta-source-files.test.ts

A component not exported from the barrel is unreachable by consumers.

C09

Block root uses BlockLayout

error

Oxlint opt-ui/require-block-layout — bun run lint가 차단

A Block root must use `BlockLayout` (or `createBlock`) rather than applying `space-y-*`/`gap-*` directly.

C10

Blocks accept a loading prop

warn

Oxlint opt-ui/block-loading-prop (advisory — CI에서 off, 스킬이 검사)

A Block owns its loading presentation, so it must accept `loading`.

C11

Blocks accept header/actions slots

warn

Oxlint opt-ui/block-slots (advisory — CI에서 off, 스킬이 검사)

Blocks expose `header` and `actions` slots so pages can compose around them.

D01

Exported API carries JSDoc

warn

Oxlint opt-ui/require-export-jsdoc (advisory — CI에서 off, 스킬이 검사)

Exported functions, interfaces, and types document what they are for.

P02

Components carry a data-opt-id

info

Oxlint opt-ui/require-opt-id (advisory — CI에서 off, 스킬이 검사)

`data-opt-id` identifies a component instance for devtools and E2E targeting. Scheduled for promotion to `error`.

인터랙션 · 애니메이션 (6)

휴리스틱이 강해 룰로 만들면 오탐이 많은 항목들입니다. 리뷰에서 확인합니다.

I01

Disclosure components use the animated prop

warn

코드 리뷰

Expand/collapse should run through `animated` (`data-[enter]`/`data-[leave]`).

I02

Transitions must have both enter and leave states

warn

코드 리뷰

A transition defined only for enter snaps on the way out.

I03

Popovers, menus, and tooltips set gutter

warn

코드 리뷰

Floating surfaces need a 4–8px `gutter` so they do not touch their trigger.

I04

No inline object/array creation in JSX props

info

코드 리뷰

A fresh object literal each render defeats memoisation — hoist it or use `useMemo`.

I05

Static data lives at module level

info

코드 리뷰

Data that never changes should not be rebuilt inside the component body.

I06

Handlers passed to memoized children use useCallback

info

코드 리뷰

An unstable handler identity re-renders the memoized child on every parent render.

PreviousAccessibilitySpatial Navigation, 포커스 관리, WAI-ARIA 역할, 키보드 단축키Core Concepts
Go to Accessibility
NextKeyboard PatternsCompositeZone, roving tabindex, 방향키 탐색, Esc/Enter 패턴을 예제로 정리Core Concepts