ERP-node/design-system.md

5.4 KiB

WACE PLM — Modal Design System

이 파일은 Claude Code(CLAUDE.md)와 Cursor(.cursor/rules/modal-design.mdc)에서 자동 참조됩니다. 모달 패턴만 정의합니다. Designer 페이지 레이아웃은 변경하지 않습니다.


1. 모달 Shell 구조

<Dialog open={open} onOpenChange={onOpenChange}>
  <DialogContent className="max-w-[{SIZE}] h-[80vh] overflow-hidden flex flex-col p-0 [&>button]:hidden">
    <DialogTitle className="sr-only">{title}</DialogTitle>
    <DialogDescription className="sr-only">{description}</DialogDescription>

    {/* Header */}
    <div className="px-6 py-4 border-b border-border flex items-center justify-between">
      <div className="flex items-center gap-2">
        <{Icon} className="w-4 h-4 text-blue-600" />
        <h2 className="text-base font-semibold text-foreground">{title}</h2>
      </div>
      <Button variant="ghost" size="icon" onClick={() => onOpenChange(false)} className="w-8 h-8">
        <X className="w-4 h-4" />
      </Button>
    </div>

    {/* Tab (선택) */}
    {/* → Section 2 참조 */}

    {/* Content */}
    <div className="flex-1 overflow-y-auto px-6 py-4">
      {/* 콘텐츠 */}
    </div>

    {/* Footer */}
    <div className="px-6 py-4 border-t border-border flex items-center justify-end gap-2">
      <Button variant="outline" onClick={() => onOpenChange(false)}>취소</Button>
      <Button className="bg-blue-600 hover:bg-blue-700">저장</Button>
    </div>
  </DialogContent>
</Dialog>

사이즈 변형

용도 className
기본 (512px) max-w-lg
중간 (672px) max-w-2xl
넓음 (800px) max-w-[800px]
최대 (1024px) max-w-5xl

2. 탭 패턴 (모달 내부)

<div className="mx-6 mt-3">
  <div className="h-9 bg-muted/30 rounded-lg p-0.5 inline-flex">
    {tabs.map(tab => (
      <button
        key={tab.value}
        onClick={() => setActiveTab(tab.value)}
        className={`px-3 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-1.5 ${
          activeTab === tab.value
            ? "bg-blue-50 text-blue-700 shadow-sm"
            : "bg-transparent text-foreground hover:text-foreground/80"
        }`}
      >
        <tab.Icon className="w-3.5 h-3.5" />
        {tab.label}
      </button>
    ))}
  </div>
</div>
  • shadcn <Tabs> 컴포넌트 사용 금지 — 위 커스텀 버튼 패턴 사용
  • 활성 탭: bg-blue-50 text-blue-700 shadow-sm
  • 비활성 탭: bg-transparent text-foreground

3. 섹션 패턴

정보 섹션 (강조, Teal)

<div className="bg-teal-50 border border-teal-200 rounded-xl p-4">
  {/* 데이터 소스, 정보 입력 등 주요 섹션 */}
</div>

일반 섹션 (흰색)

<div className="bg-white border border-border rounded-xl p-4 space-y-4">
  {/* 설정, 목록 등 */}
</div>

4. 폼 필드 패턴

{/* Label + Input */}
<div className="space-y-2">
  <Label className="text-xs font-medium text-foreground">라벨</Label>
  <Input className="h-9 text-sm" placeholder="..." />
</div>

{/* Label + Select */}
<div className="space-y-2">
  <Label className="text-xs font-medium text-foreground">라벨</Label>
  <Select>
    <SelectTrigger className="h-9 text-sm">
      <SelectValue placeholder="선택하세요" />
    </SelectTrigger>
    <SelectContent>...</SelectContent>
  </Select>
</div>

{/* 읽기 전용 Input */}
<div className="space-y-2">
  <Label className="text-xs font-medium text-muted-foreground">라벨 (자동 감지)</Label>
  <Input className="h-9 text-sm bg-muted" readOnly />
</div>
  • 필드 간격: space-y-3
  • Label: text-xs font-medium
  • Input/Select 높이: h-9

5. 버튼 규칙 (모달 내)

위치 variant 용도
Footer 취소 outline 닫기, 취소
Footer 저장 className="bg-blue-600 hover:bg-blue-700" 저장, 확인
Footer 삭제 variant="destructive" 삭제 확인
콘텐츠 내 추가 variant="outline" size="sm" className="w-full gap-2" 행/항목 추가
콘텐츠 내 아이콘 variant="ghost" size="sm" 인라인 액션
삭제 아이콘 variant="ghost" size="sm" className="text-destructive hover:text-destructive hover:bg-destructive/10" 인라인 삭제

6. 오버레이 & 애니메이션

  • 오버레이: rgba(0, 0, 0, 0.6) — globals.css에 이미 전역 설정됨
  • 모달 열기: fade-in 200ms + zoom-in-95 (shadcn 기본)
  • 모달 닫기: fade-out 150ms + zoom-out-95 (shadcn 기본)
  • 탭 전환: 애니메이션 없음 (즉시)

7. 아이콘 규칙 (모달 내)

위치 크기 색상
헤더 타이틀 아이콘 w-4 h-4 (16px) text-blue-600
탭 내부 아이콘 w-3.5 h-3.5 (14px) 탭 상태에 따라 자동
닫기 버튼 X w-4 h-4 (16px) 기본 foreground
콘텐츠 내 액션 아이콘 w-3 h-3 (12px) text-muted-foreground
행 추가 버튼 아이콘 w-3.5 h-3.5 (14px) 기본

8. 접근성 필수 사항

{/* DialogContent 내부 반드시 포함 */}
<DialogTitle className="sr-only">{모달 목적 설명}</DialogTitle>
<DialogDescription className="sr-only">{상세 설명}</DialogDescription>
  • Escape 키로 닫기: shadcn Dialog 기본 제공
  • 포커스 트랩: shadcn Dialog 기본 제공
  • 닫기 버튼에 aria-label 불필요 (shadcn 처리)