npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reopt-ai/opt-ui

v1.16.0

Published

접근성 우선 UI 컴포넌트 라이브러리. opt-ui-primitives Core, 비즈니스 Shells, 페이지 Surfaces.

Readme

@reopt-ai/opt-ui

접근성 우선 UI 컴포넌트 라이브러리. opt-ui-primitives 기반 Core, 비즈니스 Shells, Surface/block layout primitives를 제공합니다. 페이지 템플릿 Surface는 @reopt-ai/opt-cli registry로 설치하며, 차트와 데이터 시각화 컴포넌트는 @reopt-ai/opt-charts로 분리되었습니다.

에이전트 스킬로 설정하기 (권장)

소비자 프로젝트 루트에서 전용 스킬을 설치합니다.

npx skills add reopt-ai/reopt-skills/opt-ui-install

설치 후 에이전트에게 opt-ui-install 스킬로 이 프로젝트에 @reopt-ai/opt-ui를 설정하고 검증해줘라고 요청하세요. 스킬은 신규 설치와 업그레이드를 구분하고, AGENTS.md(없으면 CLAUDE.md)의 reopt marker 블록, Tailwind v4, OptThemeProvider, opt doctor, Surface 흐름을 멱등하게 설정합니다. 스킬 소스는 reopt-ai/reopt-skills가 단일 기준입니다.

아래 설치 명령은 스킬을 사용할 수 없거나 모든 단계를 직접 통제할 때의 수동 대안입니다.

설치

bun add @reopt-ai/opt-ui

Peer Dependencies

| 패키지 | 필수 | | ------------------- | ---- | | react ^19.0.0 | Yes | | react-dom ^19.0.0 | Yes |

Optional Peer Dependencies

| 패키지 | 용도 | | ---------------------------- | --------------------------------- | | next >=16.0.0 | SidebarNav, RouteFocusManager | | @codemirror/view | SqlEditor | | @codemirror/state | SqlEditor | | @codemirror/lang-sql | SqlEditor | | @codemirror/theme-one-dark | SqlEditor | | @codemirror/autocomplete | SqlEditor (자동완성) | | @codemirror/commands | SqlEditor (undo/redo) |

컴포넌트 계층

Core (74)  →  Shells (95)  →  Blocks via opt-cli
 primitives     비즈니스 조합     페이지 템플릿 설치

opt-charts (38) → 데이터 시각화와 chart-specific shells

Core (74개)

접근성 프리미티브 + Tailwind 스타일. 단일 책임 primitive 컴포넌트.

Button, Badge, Input, Textarea, Switch, Checkbox, CheckboxGroup, RadioGroup, Radio, Select, Combobox, Dialog, Disclosure, Accordion, Tabs, Menu, Menubar, Toolbar, Tooltip, Form (FormInput, FormTextarea, FormSelect, FormSwitch), Popover, DropdownMenu, Drawer, NumberInput, OtpField, Kbd, StatCard, Avatar, AvatarGroup, Progress, CircularProgress, Spinner, Alert, Skeleton, Separator, Breadcrumb, EmptyState, Card, DateRangePicker, TagInput, Toggle, ColorPicker, IconPicker, StepBuilder, ConditionBuilder, CompositeZone, SurfaceLayout, createSurface, PageHeader, ScrollArea, Container, Section, Resizable (PanelGroup/Panel/Handle), Logo, FieldToken, Facet, Expression, TextTruncate, Highlight, LiveRegion, ColorPalette, InlineEdit, KeyPadMenu, DescriptionList, Identity

Charts / Visuals

차트 컴포넌트는 @reopt-ai/opt-charts에서 직접 import합니다. @reopt-ai/opt-ui/visuals는 기존 소비자를 위한 deprecated compatibility re-export입니다.

대표 컴포넌트:

ChartContainer, ChartTooltip, ChartLegend, LineChart, BarChart, AreaChart, Sparkline, PieChart, ScatterChart, ComparisonChart, GaugeChart, FunnelChart, RetentionHeatmap, SankeyChart, TimeSeriesChart, TimeSeriesTooltipContent, LoadingChart, SmallMultiples

Shells (95개)

Core 조합 + 비즈니스 로직.

대표 컴포넌트:

CommandPalette, CommandPaletteTrigger, DashboardGrid, SearchCombobox, StatusSelect, AppMenubar, SidebarNav, EditorToolbar, ContentTabs, FaqAccordion, SettingsForm, AuthForm, DynamicFieldForm, FileUploadForm, WizardForm, DeploymentTimeline, ProjectSwitcher, EnvPanel, BranchSelect, DomainTable, DataTable, NotificationToast, TaskList, ActivityFeed, EventTimeline, FilterBar, QuickActions, TeamMemberList, EventIcon, ConnectionIndicator, FloatingActionBar, Flyout, TemplatePicker, CodeSnippetViewer, PropertyExplorer, QueryResultsTable, AlertBuilder, SqlEditor, EventMetaEditor, WidgetGrid, ReportBuilder, SegmentBuilder, InsightsPanel, ScoreBreakdown, TimeRangeSelector, TimeRangeControl, TimeSeriesPanel, LogTable, QueryBar, FieldSidebar, ExportButton, LoadingOverlay, SummaryRow, ComparisonSelector, ThemeSwitcher, PromptTemplate, Sidebar, AppShell, DesignGuidePanel

전체 목록은 COMPONENT_CATALOG.md 또는 generated docs의 02-components/03-shells.md를 기준으로 확인합니다.

Surface / Block templates

Shells 조합 페이지 템플릿은 runtime export가 아니라 copy-paste block registry로 제공합니다. 템플릿 내부에서는 SurfaceLayout/createSurface로 구조적 일관성을 유지합니다.

전체 Surface 레지스트리는 npx @reopt-ai/opt-cli surface add <slug> 으로 프로젝트에 설치합니다.

사용법

// 전체 패키지에서 import
import { Button, Badge, Input } from "@reopt-ai/opt-ui";
import { DataTable, FilterBar } from "@reopt-ai/opt-ui";
import { LineChart, BarChart } from "@reopt-ai/opt-charts";

// 또는 계층별 서브패스로 import
import { Button, Badge } from "@reopt-ai/opt-ui/core";
import { LineChart, BarChart } from "@reopt-ai/opt-charts/visuals";
import { DataTable, FilterBar } from "@reopt-ai/opt-ui/shells";

// React가 필요 없는 파서와 시간 계산은 서버 안전 서브패스 사용
import { parseQuery, matchesQueryObject } from "@reopt-ai/opt-ui/query";
import { resolveTimeRange } from "@reopt-ai/opt-charts/time";

기본 예시

import {
  Card,
  CardHeader,
  CardTitle,
  CardContent,
  Button,
  Badge,
} from "@reopt-ai/opt-ui";

function MyComponent() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Dashboard</CardTitle>
      </CardHeader>
      <CardContent>
        <Badge variant="success">Active</Badge>
        <Button onClick={() => console.log("clicked")}>Action</Button>
      </CardContent>
    </Card>
  );
}

차트 예시

import { LineChart, ChartContainer } from "@reopt-ai/opt-charts";

const data = [
  { name: "Jan", revenue: 4000, users: 2400 },
  { name: "Feb", revenue: 3000, users: 1398 },
];

const series = [
  { dataKey: "revenue", name: "Revenue", color: "#8884d8" },
  { dataKey: "users", name: "Users", color: "#82ca9d" },
];

<ChartContainer height={300}>
  <LineChart data={data} series={series} />
</ChartContainer>;

데이터 탐색 primitives

반복되는 분석 UI를 화면마다 다시 만들지 않도록 필드 타입, 값 분포, 조건식, 검색 결과, 긴 식별자, 라이브 알림을 Core primitive로 제공합니다. 각 컴포넌트의 상태와 접근성 동작은 Explore에서 직접 확인할 수 있습니다.

분석 상세 조사 흐름

목록에서 대상을 선택하고, 페이지 문맥을 유지한 채 상세 근거를 조사하는 흐름을 Core와 Shell 조합으로 제공합니다.

import {
  DescriptionList,
  EventTimeline,
  Flyout,
  Identity,
  ScoreBreakdown,
} from "@reopt-ai/opt-ui";

<Identity
  name={customer.name}
  secondary={customer.email}
  id={customer.id}
  state={customer.anonymous ? "anonymous" : "identified"}
/>

<Flyout open={Boolean(customer)} onClose={clearCustomer} title={customer?.name}>
  <DescriptionList items={profileFacts} columns={2} />
  <ScoreBreakdown score={health.score} axes={health.axes} />
  <EventTimeline groups={sessions} showGaps />
</Flyout>;

로그 탐색: 쿼리를 값으로 유지

QueryBar는 문자열을 내부 상태에 숨기지 않고 직렬화 가능한 Query 값을 입출력합니다. 같은 값으로 서버 요청, 현재 브라우저 행, 실시간으로 도착한 행을 동일하게 필터링할 수 있습니다. Route Handler나 서버 코드에서는 React client boundary를 끌어오지 않는 @reopt-ai/opt-ui/query를 사용합니다.

import { matchesQueryObject, parseQuery } from "@reopt-ai/opt-ui/query";

const query = parseQuery("service:api tags:checkout -status:200");

const visibleRows = rows.filter((row) =>
  matchesQueryObject(query, row, { nested: "properties" }),
);

문자열·숫자 같은 단일 값과 tags: ["checkout", "payment"] 같은 배열 필드는 같은 쿼리로 다룰 수 있습니다. tags:checkouttags:*는 배열 전체를 문자열로 바꾸지 않고 각 원소를 비교하며, 빈 배열은 tags:*에 일치하지 않습니다. 필드가 없는 bare term 검색도 문자열·숫자·불리언 배열 원소를 검색합니다.

UI에서는 동일한 QueryQueryBar에 전달하고, LogTable의 cell action을 applyCellAction으로 다시 쿼리에 합칩니다. FieldSidebar의 값 분포 필터도 같은 include/exclude 계약을 사용합니다.

시간 범위: 표현식을 저장하고 나중에 해석

TimeRangeControl은 두 시각을 고정하는 대신 { from: "now-7d", to: "now" } 형태의 date-math 표현식을 유지합니다. URL이나 저장된 대시보드는 계속 “최근 7일”을 뜻하고, 실제 요청을 보낼 때만 기준 시각과 시간대를 넣어 절대 범위로 해석합니다.

import { resolveTimeRange } from "@reopt-ai/opt-charts/time";

const resolved = resolveTimeRange(
  { from: "now-7d/d", to: "now/d" },
  {
    now: Date.now(),
    timeZone: "Asia/Seoul",
    weekStartsOn: 1,
  },
);

if (!resolved) {
  throw new Error("Invalid time range");
}

서버, URL parser, unit test에서는 @reopt-ai/opt-charts/time을 사용하고, 클라이언트 화면에서는 TimeRangeControl 또는 차트까지 묶은 TimeSeriesPanel을 사용합니다.

Block template 설치

npx @reopt-ai/opt-cli surface add data-explorer

테마 프리셋

5개 시그니처 프리셋 CSS를 제공합니다. 각 프리셋은 [data-theme] 셀렉터 기반 CSS 변수를 정의합니다.

/* 원하는 프리셋을 import */
@import "@reopt-ai/opt-ui/theme/presets/default.css";
@import "@reopt-ai/opt-ui/theme/presets/minimal.css";
@import "@reopt-ai/opt-ui/theme/presets/natural.css";
@import "@reopt-ai/opt-ui/theme/presets/pro.css";
@import "@reopt-ai/opt-ui/theme/presets/mono-dark.css";

| 프리셋 | 특징 | | ----------- | -------------------------------------------- | | default | 뉴트럴 블루 계열, 범용 | | minimal | 모노톤, 미니멀, 샤프 엣지 | | natural | Playfair Display + Lora, force-light | | pro | Geist Mono 헤딩, 네온 블루, 고밀도 | | mono-dark | OLED 친화 블랙, 네온 시안, 신택스 하이라이트 |

이전 버전의 corporate/playful 프리셋은 제거되었습니다. localStorage에 남은 값은 default로 자동 마이그레이션됩니다. 기존 시각적 톤을 유지하려면 OptThemeProvider의 generated 모드 또는 ThemeBuilder로 직접 시드를 정의해 재현할 수 있습니다.

OptThemeProviderThemeSwitcher로 런타임 프리셋 + 라이트/다크 모드 전환을 지원합니다.

디자인 토큰

CSS 변수 기반 시맨틱 토큰 — dark: 프리픽스 없이 자동 전환:

| 토큰 | 용도 | | -------------------- | -------------- | | OPT_INPUT | 입력 필드 | | OPT_FOCUS_VISIBLE | 포커스 링 | | OPT_SURFACE | 카드/패널 배경 | | OPT_BORDER | 테두리 | | OPT_TEXT_PRIMARY | 주요 텍스트 | | OPT_TEXT_SECONDARY | 보조 텍스트 |

시맨틱 스페이싱

| CSS 변수 | Tailwind | 값 | | --------------------- | -------------------------- | ------------- | | --opt-space-section | gap-section, p-section | 1.5rem (24px) | | --opt-space-group | gap-group, p-group | 1rem (16px) | | --opt-space-element | gap-element, p-element | 0.5rem (8px) |

Block 루트는 반드시 BlockLayout을 사용합니다. 페이지 콘텐츠 경계를 Block이 직접 맡을 때는 inset="section"을 지정하고, 카드나 Shell이 이미 여백을 제공하는 embedded Block에서는 inset을 생략합니다. PageHeader는 재사용 가능한 콘텐츠 헤더이므로 바깥 여백을 소유하지 않습니다.

접근성

입력의 자동 ID는 컴포넌트 인스턴스마다 고유합니다. 같은 라벨의 입력을 여러 번 렌더해도 각각의 라벨과 오류가 해당 입력에 연결됩니다. 외부 코드가 ID를 참조해야 하면 id를 명시하세요.

폼의 라벨·설명·오류는 필드별 FormField 아래에 조합합니다.

<FormField name="message">
  <FormLabel>메시지</FormLabel>
  <FormTextarea name="message" />
  <FormDescription>요청 내용을 입력하세요.</FormDescription>
  <FormError name="message" />
</FormField>

AuthForm, FileUploadForm, SettingsForm, DynamicFieldForm, WizardFormonSubmitPromise<void>를 반환할 수 있습니다. 요청이 완료될 때까지 제출을 막고, 성공 후에만 완료 안내를 표시합니다. 실패 안내는 labels.submitError로 지정하며 다시 제출할 수 있습니다. AsyncCombobox와 비동기 SearchCombobox는 검색 실패와 재시도를 labels.error·labels.retry로 제공합니다.

InlineEdit의 편집 입력 이름은 labels.input으로 지정합니다. IME 조합 중에는 Enter/Escape 명령을 실행하지 않으며, 저장·취소 후 읽기 버튼으로 포커스를 복원합니다. 다른 컨트롤을 선택해 편집을 마치면 선택한 컨트롤의 포커스를 유지합니다. NumberInputspinbutton 역할과 값·범위 ARIA 속성을 제공합니다.

  • opt-ui-primitives 기반 ARIA 역할/상태 관리
  • 모든 인터랙티브 요소에 포커스 링
  • Roving tabindex (CompositeZone, DataTable, SidebarNav)
  • Focus trap (Dialog)
  • Spatial Navigation 엔진 (SpatialNavProvider)
  • 키보드: Tab, Arrow, Enter, Space, Escape

빌드

# 빌드만
bun run build

# 타입 체크
bun run typecheck

# 린트
bun run lint

배포

이 패키지는 npmjs.org에 public으로 배포됩니다. 유지보수자 배포는 직접 npm publish 또는 bun run release*를 사용하지 않고 design-publish 워크플로우만 사용합니다.

/design-publish opt-ui
  • npm registry publish 권한과 2FA 또는 automation token이 필요합니다.
  • Surface 또는 registry 입력이 바뀌면 @reopt-ai/opt-cli와 함께 배포합니다.
  • @reopt-ai/opt-ui-primitives는 런타임 의존성이므로 publish-safe 버전으로 함께 검증합니다.
  • CI는 publish snapshot과 generated artifact(registry.json, COMPONENT_CATALOG.md) freshness를 확인합니다.

테스트

bun run test
bun run validate:test-coverage
bun run test:coverage

bun run test는 Vitest 실행 후 scripts/validate-test-coverage.ts를 이어서 실행합니다. 이 검증은 src/*/_meta.ts의 169개 ComponentMeta가 모두 testDescribe를 갖고, 해당 이름의 describe(...) 블록이 테스트 파일에 존재하는지 확인합니다.

Design Decisions

  • Layer metaphor: Core → Shells는 opt-ui가 맡고, 페이지 템플릿 Surface는 opt-cli block registry로 설치
  • BlockLayout 강제: block template은 반드시 BlockLayout 또는 createBlock을 사용하고, 페이지 경계는 inset="section"으로 명시
  • Subpath exports: ./core, ./visuals, ./shells — 필요한 계층만 import 가능
  • Dual format: ESM + CJS — 모든 번들러/런타임 호환
  • No source maps: 배포 아티팩트에 소스맵을 포함하지 않습니다 (1.0.3 패키징 정리에서 제거)
  • Tree-shakeable: splitting: true + ESM — 사용하는 컴포넌트만 번들에 포함
  • "use client" 배너: 클라이언트 진입점(index / core / visuals / shells / id-registry) 빌드 출력에만 "use client" 자동 주입 — meta, query, theme/server는 server-safe 모듈이라 제외
  • External all: 런타임 의존성 전부 external — 모노레포 앱에서 중복 번들 방지
  • Optional heavy deps: CodeMirror, Next.js는 optional peer — 사용하지 않으면 설치 불필요
  • CSS variable theming: 5개 프리셋 × light/dark compound themes, dark: 없이 CSS 변수로 자동 전환

License

MIT