# @hm-design/ui 컴포넌트 카탈로그 (Web UI)
기준: `@hm-design/ui` 0.1.0, 공개 Export(`src/index.ts` → `dist/index.d.ts`, `dist/theme/index.d.ts`). 모든 컴포넌트 import는 `from "@hm-design/ui"`, Theme API는 `from "@hm-design/ui/theme"`입니다. **이 표에 없는 이름은 공개 API가 아닙니다.** 저장소의 `npm run check:ai-docs`가 이 문서와 실제 Export의 일치를 검사합니다.
- **구조:** shadcn/ui(Radix) 구조와 공개 API를 유지하고 외형만 HM Theme로 바꿨습니다. 아래 "HM Props"에 없는 Props는 shadcn·Radix 원래 Props입니다.
- **상태 소유:** 대부분 Radix처럼 `open`/`onOpenChange`, `value`/`onValueChange`(Controlled)와 `default*`(Uncontrolled)를 지원합니다.
- **외형 보정 금지:** HM 외형은 기본 API만으로 적용됩니다. `className`은 배치용이며 색·간격·radius를 덮어쓰지 않습니다.
## 1. Form
| 컴포넌트 (Export) | 용도 | HM Props·Variant (기본값) |
|---|---|---|
| `Button`, `buttonVariants` / 타입 `ButtonAppearance` | 작업 실행 | `variant`: `primary`(기본) · `secondary`(Brown) · `modify`(Orange) · `destructive`(Red) · `cancel`(Gray), 별칭 `default`(=primary) · `outline`(=primary+outline), 호환 `ghost` · `link`(HM 외형 없음). `appearance`: `brand`(기본) · `solid` · `outline`. `size`: HM 규격은 `sm`(28) · `default`(34) · `lg`(42), `xs`·`icon*`는 shadcn 기본. `asChild` |
| `Input` / 타입 `InputSize` | 한 줄 입력 | `size`: `small`(28) · `medium`(34) · `large`(42, 기본). 상태는 native `disabled`·`readOnly`·`aria-invalid` |
| `Textarea` / 타입 `TextareaResize` | 여러 줄 입력 | `resize`: `vertical`(기본) · `none` · `both` |
| `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `SelectGroup`, `SelectLabel`, `SelectSeparator`, `SelectScrollUpButton`, `SelectScrollDownButton` / 타입 `SelectSize` | 목록 선택 | `SelectTrigger size`: `medium`(34) · `large`(44, 기본). Content·Item은 Trigger 크기를 따름 |
| `Label` | 입력 라벨 | 없음. `htmlFor` ↔ control `id` 연결 |
| `Checkbox` | 복수 선택 | 없음(Radix) |
| `RadioGroup`, `RadioGroupItem` | 단일 선택 | 없음(Radix) |
| `Switch` | 켜기·끄기 | 없음(Radix) |
| `Slider` | 범위 값 | 없음(Radix) |
| `Toggle`, `toggleVariants` | 선택 상태 버튼 | `variant`: `default` · `outline`, `size`: `default` · `sm` · `lg` |
| `ToggleGroup`, `ToggleGroupItem` | 선택 버튼 묶음 | `variant`, `size`(Toggle과 같음), `spacing` |
**Button 조합 규칙(필수).** 디자인이 있는 조합만 씁니다. TypeScript가 통과해도 아래에 없는 조합은 디자인이 없습니다.
| variant | 허용 appearance |
|---|---|
| `primary` | `brand`, `solid`, `outline` |
| `secondary` | `brand`, `solid`, `outline` |
| `modify` | `brand`, `solid` (outline 없음) |
| `cancel` | `solid`, `outline` (brand 없음) |
| `destructive` | `solid`, `outline` (brand 없음) |
- `brand`는 페이지 대표 실행 1개에만 씁니다(팝업 안 금지). 일반 실행·저장·확인은 `solid`, 반복·보조·표 행 작업은 `outline`.
- 결정 세트(취소 + 저장/확인/삭제)는 같은 `appearance`·`size`: 예) `` + ``.
- 기본값이 `brand`이므로 `variant="cancel"`·`"destructive"`만 쓰면 미지원 조합이 됩니다. 항상 `appearance`를 명시합니다.
- 검색 영역: 조회 `primary`/`solid` + 초기화 `cancel`/`outline`, 같은 size.
## 2. Overlay
| 컴포넌트 (Export) | 용도 | HM Props·주의 |
|---|---|---|
| `Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogTitle`, `DialogDescription`, `DialogBody`, `DialogFooter`, `DialogClose`, `DialogOverlay`, `DialogPortal` | 모달 | `DialogBody`(HM 추가: 본문 여백·스크롤, Header·Footer 고정). `DialogContent showCloseButton`(기본 true), `DialogFooter showCloseButton`(기본 false) |
| `AlertDialog`, `AlertDialogTrigger`, `AlertDialogContent`, `AlertDialogHeader`, `AlertDialogTitle`, `AlertDialogDescription`, `AlertDialogFooter`, `AlertDialogAction`, `AlertDialogCancel`, `AlertDialogOverlay`, `AlertDialogPortal` | 작업 확인 | `AlertDialogAction`: `variant`(기본 `primary`)·`appearance`(기본 `solid`)·`size`. `AlertDialogCancel`: `variant`(기본 `cancel`)·`appearance`(기본 `solid`). 삭제 확인 = Action `variant="destructive"` |
| `Sheet`, `SheetTrigger`, `SheetContent`, `SheetHeader`, `SheetTitle`, `SheetDescription`, `SheetBody`, `SheetFooter`, `SheetClose`, `SheetOverlay`, `SheetPortal` | 측면 패널 | `SheetContent side`: `right`(기본) · `left` · `top` · `bottom`, `showCloseButton`(기본 true) |
| `Drawer`, `DrawerTrigger`, `DrawerContent`, `DrawerHeader`, `DrawerTitle`, `DrawerDescription`, `DrawerBody`, `DrawerFooter`, `DrawerClose`, `DrawerOverlay`, `DrawerPortal` | 하단 슬라이드(vaul) | 드래그 동작은 vaul 기본 |
| `Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverAnchor`, `PopoverClose` | 보조 팝업 | 없음 |
| `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider` | 보조 설명 | 없음 |
| `HoverCard`, `HoverCardTrigger`, `HoverCardContent` | Hover 카드 | 없음 |
| `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioGroup`, `DropdownMenuRadioItem`, `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuShortcut`, `DropdownMenuGroup`, `DropdownMenuPortal`, `DropdownMenuSub`, `DropdownMenuSubTrigger`, `DropdownMenuSubContent` | 작업 메뉴 | `DropdownMenuItem variant`: `default` · `destructive`, `inset` |
| `ContextMenu`, `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`, `ContextMenuCheckboxItem`, `ContextMenuRadioGroup`, `ContextMenuRadioItem`, `ContextMenuLabel`, `ContextMenuSeparator`, `ContextMenuShortcut`, `ContextMenuGroup`, `ContextMenuPortal`, `ContextMenuSub`, `ContextMenuSubTrigger`, `ContextMenuSubContent` | 우클릭 메뉴 | DropdownMenu와 같음 |
| `Toaster`, `toast` / 타입 `ExternalToast`, `ToasterProps` | 토스트(sonner) | 앱에 `` 한 번, 호출은 `toast("…")` |
## 3. Navigation (컴포넌트)
| 컴포넌트 (Export) | 용도 | HM Props |
|---|---|---|
| `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` / 타입 `TabsPanelSurface` | 탭 | `TabsList fill`(기본 true = 균등 폭 Primary Fill, false = Legacy), `TabsList panelSurface`: `content`(기본) · `surface` — 실제 배경(Card `surface`)과 맞춤 |
| `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, `BreadcrumbSeparator`, `BreadcrumbEllipsis` | 현재 경로 | `BreadcrumbLink asChild` |
| `Pagination`, `PaginationContent`, `PaginationItem`, `PaginationLink`, `PaginationPrevious`, `PaginationNext`, `PaginationEllipsis` | 페이지 선택 | `PaginationLink isActive`, `size`(기본 `icon`) |
| `NavigationMenu`, `NavigationMenuList`, `NavigationMenuItem`, `NavigationMenuTrigger`, `NavigationMenuContent`, `NavigationMenuLink`, `NavigationMenuIndicator`, `NavigationMenuViewport` | 링크 메뉴 | `NavigationMenu viewport` |
| `Menubar`, `MenubarMenu`, `MenubarTrigger`, `MenubarContent`, `MenubarItem`, `MenubarCheckboxItem`, `MenubarRadioGroup`, `MenubarRadioItem`, `MenubarLabel`, `MenubarSeparator`, `MenubarShortcut`, `MenubarGroup`, `MenubarPortal`, `MenubarSub`, `MenubarSubTrigger`, `MenubarSubContent` | 메뉴 막대 | DropdownMenu와 같음 |
페이지 Navigation(상단·좌측 메뉴)은 컴포넌트가 아니라 Pattern `GNB` / `LNB` / `DashboardLayout` 중 하나를 씁니다(§6).
## 4. Data & Display
| 컴포넌트 (Export) | 용도 | HM Props |
|---|---|---|
| `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent`, `CardFooter` / 타입 `CardSurface` | 콘텐츠 묶음 | `surface`: `surface`(White, 기본) · `content`(Beige), `size`: `default` · `sm` |
| `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption` | 표(표시만) | 없음. 정렬·선택·페이지가 필요하면 Pattern `DataTable` |
| `Badge`, `badgeVariants` | 상태·라벨 | `variant`: `default` · `secondary` · `destructive` · `outline`(success/warning/info 없음), `asChild` |
| `Alert`, `AlertTitle`, `AlertDescription` | 안내·경고 | `variant`: `default` · `destructive` |
| `Avatar`, `AvatarImage`, `AvatarFallback` | 사용자 이미지 | `size`: `default` · `sm` · `lg` |
| `Separator` | 구분선 | 없음 |
| `Progress` | 진행률 | 없음 |
| `Skeleton` | 로딩 자리 | 없음 |
| `ScrollArea`, `ScrollBar` | 스크롤 영역(HM 스크롤바) | 없음. 스크롤바 외형은 DESIGN_RULES §6 |
| `AspectRatio` | 비율 유지 | 없음 |
| `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `useCarousel` / 타입 `CarouselApi` | 슬라이드(embla) | `opts`, `plugins`, `orientation`, `setApi` |
| `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` | 접고 펼치기 | 없음 |
| `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | 영역 접기 | 없음 |
| `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle` | 크기 조절 | `ResizableHandle withHandle` |
## 5. Search & Date
| 컴포넌트 (Export) | 용도 | HM Props |
|---|---|---|
| `Calendar` | 달력(react-day-picker) | react-day-picker Props |
| `DatePicker` / 타입 `DatePickerSize` | 날짜 입력 | `value`·`defaultValue`(Date), `onValueChange`, `controlled`, `placeholder`, `size`: `medium` · `large`(기본), `format`(기본 `YYYY.MM.DD`), `calendarProps`(disabled·startMonth·endMonth·locale·captionLayout·weekStartsOn·today) |
| `Combobox` / 타입 `ComboboxOption`, `ComboboxSize` | 검색 가능한 선택 | `options: { value, label, disabled? }[]`(필수), `value`·`defaultValue`, `onValueChange`, `controlled`, `placeholder`, `searchPlaceholder`, `emptyText`, `size`: `medium` · `large`(기본) |
| `Command`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandSeparator`, `CommandShortcut` | 명령 검색(cmdk) | 없음 |
## 6. Pattern (업무 화면 단위)
Pattern은 위 컴포넌트를 조합한 것이며 자체 primitive 외형을 추가하지 않습니다. 조합 예시는 [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md).
| Pattern (Export) | 용도 | 주요 Props |
|---|---|---|
| `DataTable`, `useDataTable`, `DataTableColumnHeader`, `DataTablePagination`, `DataTableViewOptions`, `dataTableSelectColumn` / 타입 `DataTableColumnDef`, `DataTableInstance`, `DataTableOptions`, `DataTableProps` | 데이터 표(TanStack Table v9: 정렬·필터·선택·페이지·열 표시) | `useDataTable({ columns, data, … })`(columns·data는 참조 안정), `DataTable table loading emptyMessage pagination(기본 true) pageSizeOptions`, `DataTableColumnHeader column title`, `DataTableViewOptions table label labels`, `dataTableSelectColumn()` |
| `FilterBar`, `FilterBarSearch`, `FilterBarActions` / 타입 `FilterBarProps`, `FilterBarSearchProps` | 조회 조건 행(레이아웃) | `FilterBar layout`: `default` · `list-basic`(가운데) · `list-extended`(추가 조건 왼쪽). `FilterBarSearch value onValueChange size`(기본 `medium`) `placeholder`(기본 "검색") |
| `GNB`, `GNBBrand`, `GNBNav`, `GNBUtilities`, `GNBUser`, `GNBMobileMenu`, `GNBMobileLink`, `GNBMobileGroup` / 타입 `GNBUserProps`, `GNBMobileMenuProps` | 상단 Navigation | `GNB sticky`, `GNBBrand asChild`, `GNBUser name description`, `GNBMobileMenu title side open defaultOpen onOpenChange`, `GNBMobileLink active asChild`, `GNBMobileGroup label` |
| `LNB`, `LNBBrand`, `LNBNav`, `LNBItem`, `LNBGroup`, `LNBProfile` / 타입 `LNBGroupProps`, `LNBProfileProps` | 좌측 Navigation(3뎁스까지 `LNBGroup` 중첩) | `LNBItem active asChild`, `LNBGroup label current`, `LNBProfile name description` |
| `LNBMobileHeader`, `LNBMobileBrand`, `LNBMobileMenu`, `LNBMobileLink`, `LNBMobileGroup` / 타입 `LNBMobileMenuProps` | LNB 모바일 Header + Sheet | `LNBMobileMenu title side open defaultOpen onOpenChange`, `LNBMobileLink active` |
| `PageHeader`, `PageHeaderContent`, `PageHeaderTitle`, `PageHeaderDescription`, `PageHeaderActions` | 페이지 제목·설명·주요 작업 | HTML 속성만 |
| `DashboardLayout`, `DashboardHeader`, `DashboardMain`, `DashboardContent`, `DashboardSidebar` / 타입 `DashboardLayoutProps` | Dashboard 전용 레이아웃(Header 48px, 최소 폭 960px·내부 스크롤) | `DashboardLayout header sidebar children`. **신규 화면은 `sidebar`·`DashboardSidebar`를 쓰지 않음**(호환성 전용) |
| `dashboardStatusDefinitions`, `dashboardStatusAttributes`, `dashboardFilterDefinitions`, `dashboardFilterIds`, `countDashboardFilter`, `matchesDashboardFilter` / 타입 `DashboardStatusId`, `DashboardStatusDefinition`, `DashboardStatusRecord`, `DashboardStatusTone`, `DashboardFilterId`, `DashboardFilterDefinition` | Dashboard 업무 상태·필터 계약(데이터 helper) | 색은 `@hm-design/ui/dashboard-status.css`의 `--hm-dashboard-status-active` · `-hold` · `-done` · `-issue` 변수만 제공. Badge 등 자동 스타일 없음 — 소비자가 연결 |
| `LoadingScreen` | 전체·부분 로딩 | `fullscreen`(기본 false), `children`(기본 "Loading") |
## 7. Theme API (`@hm-design/ui/theme`)
| Export | 용도 |
|---|---|
| `applyRuntimeTheme(root, theme)` → `boolean` | `root`(예: `document.documentElement` 또는 `[data-hm-theme]` 요소)에 Runtime Theme 적용. 잘못된 입력은 전체 거부(false) |
| `resetRuntimeTheme(root)` | 적용한 값 제거, HM Default로 복원 |
| `isRuntimeTheme(value)` | 형식 검사 |
| `fetchRuntimeTheme(url, request?)`, `resolveRuntimeTheme(theme, defaults?)`, `loadRuntimeTheme(root, url, options?)` / 타입 `RuntimeThemeLoadOptions` | URL의 Theme JSON 불러오기·병합·적용 |
| `runtimeThemeContract`, `defaultRuntimeTheme` / 타입 `RuntimeTheme`, `RuntimeThemeToken` | 계약(토큰 키 → CSS 변수)과 HM Default 값. `RuntimeTheme = { schemaVersion: 1, tokens: Partial> }` |
Theme JSON 원본·CDN은 패키지에 포함되지 않습니다(URL은 소비 앱이 정함).
## 8. 공개하지 않은 것
`cn`, 내부 `lib/utils`, 각 컴포넌트 파일의 비공개 helper, `components/ui/*` 직접 경로, `theme/*.css` 개별 파일은 공개 API가 아닙니다. shadcn CLI로 같은 컴포넌트를 소비 프로젝트에 다시 추가하지 않습니다(같은 이름의 중복 컴포넌트가 됨).