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. Styling

스타일링

data-attribute 셀렉터, className 오버라이드, 다크모드, 애니메이션 패턴을 설명합니다.

reopt design · Updated Jun 26, 2026

data-[attr] 셀렉터

opt-ui primitives는 컴포넌트 상태를 data attribute로 노출합니다. Tailwind의 data-* variant로 상태별 스타일을 선언적으로 적용할 수 있습니다.

셀렉터의미사용 예
data-[active-item]Composite 내 현재 활성 아이템data-[active-item]:bg-bg-subtle
data-[focus-visible]키보드 포커스 표시data-[focus-visible]:ring-2
data-[enter]진입 트랜지션 상태data-[enter]:opacity-100
data-[leave]퇴장 트랜지션 상태data-[leave]:opacity-0
data-[open]Disclosure/Dialog 열림 상태[[data-open]>&]:rotate-180
aria-[invalid=true]폼 유효성 검증 실패aria-[invalid=true]:border-red-500

className 오버라이드

Primitive의 핵심 스타일 패턴은 className ?? defaultClass입니다.

tsx
// className을 전달하지 않으면 기본 스타일 적용
<DisclosureTrigger>열기</DisclosureTrigger>

// className을 전달하면 기본 스타일을 완전히 교체
<DisclosureTrigger className="custom-trigger-style">
  열기
</DisclosureTrigger>

tailwind-merge를 사용하지 않는 이유: 병합 시 어떤 스타일이 최종 적용되는지 예측하기 어렵습니다. 완전 교체 방식은 결과가 명확하고, 기본 스타일을 원하면 전달하지 않으면 됩니다.

tsx
// primitives/disclosure.tsx — 실제 구현
const triggerClass =
  "flex w-full items-center justify-between px-4 py-4 text-left ...";

export function DisclosureTrigger({
  className,
  ...props
}: DisclosureTriggerProps) {
  return (
    <AriaDisclosure
      className={className ?? triggerClass}  // 전달 시 교체, 미전달 시 기본
      {...props}
    />
  );
}

다크 모드

OptThemeProvider가 data-theme를 관리하고, Tailwind dark: variant는 [data-theme$="-dark"] 셀렉터로 오버라이드됩니다. 모든 Primitive의 기본 스타일에 dark: variant가 포함되어 있어 별도 설정 없이 다크모드가 동작합니다.

tsx
// 기본 스타일에 dark: variant 포함
const triggerClass =
  "... text-text-primary hover:bg-bg-subtle dark:hover:bg-white/[0.08]/50 ...";

애니메이션 상태

Primitive 레이어가 노출하는 data-enter, data-leave, data-backdrop 속성을 globals.css에서 전역 트랜지션으로 처리합니다.

css
/* globals.css — primitive transition states */

[data-enter] {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 150ms ease-out,
    transform 150ms ease-out;
}

[data-leave] {
  opacity: 0;
  transform: translateY(-4px);
  transition:
    opacity 100ms ease-in,
    transform 100ms ease-in;
}

[data-backdrop] {
  transition: opacity 200ms ease-out;
}

[data-backdrop][data-leave] {
  transition: opacity 150ms ease-in;
}

Disclosure의 콘텐츠 영역은 Tailwind CSS 애니메이션 유틸리티와 data-[enter]/data-[leave]를 함께 사용합니다:

tsx
const contentClass =
  "overflow-hidden " +
  "data-[enter]:animate-in data-[enter]:fade-in data-[enter]:slide-in-from-top-1 " +
  "data-[leave]:animate-out data-[leave]:fade-out";
PreviousKeyboard PatternsCompositeZone, roving tabindex, 방향키 탐색, Esc/Enter 패턴을 예제로 정리Core Concepts
Go to Keyboard Patterns
NextTheme SystemCompound Theme 아키텍처, 5개 프리셋 × light/dark 모드, CSS 변수 체계Core Concepts