Button
사용자 액션을 트리거하는 버튼 컴포넌트입니다.
개요
Button은 가장 기본적인 인터랙티브 요소로, 클릭 이벤트를 처리하고 사용자 액션을 실행합니다. Cals Button은 Cals 브랜드 컬러(Pink, Blue, Purple)와 예약 상태 컬러 5가지가 적용되어 있으며, Foundation Button의 모든 기능을 상속합니다.
주요 특징:
- 6가지 기본 Variant + 5가지 예약 상태 Variant
- 3가지 Size (sm, md, lg)
- Cals 브랜드 컬러 적용 (Pink
#e91e63, Blue#03a9f4, Purple#9c27b0) - 예약 상태 컬러 완벽 지원 (Available, Pending, Confirmed, Cancelled, Completed)
- Loading 상태 지원
- Icon 지원
- 완벽한 접근성 (WCAG 2.1 AA)
사용 사례:
- Cals 예약 시스템 액션 버튼 (예약하기, 승인하기, 취소하기)
- 예약 상태 변경 버튼
- 예약 관리 대시보드 액션
- 고객 예약 확정/취소 버튼
설치
npx @vortex/cli add button --package cals기본 사용법
import "@vortex/ui-cals/theme"; // Cals 테마 적용
import { Button } from "@vortex/ui-cals";
export default function Example() {
return <Button>Click me</Button>;
}Variants (변형)
Primary (Cals Pink)
기본 액션에 사용하는 Cals Pink 버튼입니다.
<Button variant="primary">예약하기</Button>Secondary (Cals Blue)
보조 액션에 사용하는 Cals Blue 버튼입니다.
<Button variant="secondary">예약 상세보기</Button>Accent (Cals Purple)
특별한 상태나 강조가 필요한 액션에 사용하는 Cals Purple 버튼입니다.
<Button variant="accent">VIP 예약</Button>Outline
아웃라인 스타일의 버튼입니다.
<Button variant="outline">취소</Button>Ghost
배경 없이 텍스트만 표시되는 버튼입니다.
<Button variant="ghost">이전으로</Button>Link
링크 스타일의 버튼입니다.
<Button variant="link">예약 규정 보기</Button>Destructive
삭제와 같은 위험한 액션에 사용하는 버튼입니다.
<Button variant="destructive">예약 영구 삭제</Button>예약 상태 Variants ⭐
Available (예약 가능)
예약 가능한 상태를 나타내는 Green 버튼입니다.
<Button variant="available">지금 예약하기</Button>사용 예시:
- 예약 가능한 시간대 선택 버튼
- 예약 가능 상태로 변경
- 신규 예약 등록
Pending (승인 대기)
승인 대기 상태를 나타내는 Orange 버튼입니다.
<Button variant="pending">승인 대기중</Button>사용 예시:
- 관리자 승인 대기 표시
- 대기 목록 등록
- 승인 요청 버튼
Confirmed (예약 확정)
예약 확정 상태를 나타내는 Blue 버튼입니다.
<Button variant="confirmed">예약 확정됨</Button>사용 예시:
- 확정된 예약 표시
- 예약 확정 처리 버튼
- 예약 확인서 발급
Cancelled (예약 취소)
예약 취소 상태를 나타내는 Red 버튼입니다.
<Button variant="cancelled">예약 취소</Button>사용 예시:
- 예약 취소 처리 버튼
- 취소된 예약 표시
- 환불 처리
Completed (서비스 완료)
서비스 완료 상태를 나타내는 Purple 버튼입니다.
<Button variant="completed">서비스 완료</Button>사용 예시:
- 완료된 예약 표시
- 서비스 완료 처리 버튼
- 리뷰 작성 유도
Sizes (크기)
Small
<Button size="sm">Small Button</Button>Medium (기본)
<Button size="md">Medium Button</Button>Large
<Button size="lg">Large Button</Button>States (상태)
Disabled
<Button disabled>Disabled Button</Button>Loading
<Button loading>Loading...</Button>Icon 사용
Icon + Text
import { Calendar, Check, X, Clock } from 'lucide-react'
<Button variant="available">
<Calendar size={16} className="mr-xs" />
예약하기
</Button>
<Button variant="confirmed">
<Check size={16} className="mr-xs" />
예약 확정
</Button>
<Button variant="cancelled">
<X size={16} className="mr-xs" />
예약 취소
</Button>
<Button variant="pending">
<Clock size={16} className="mr-xs" />
승인 대기
</Button>Icon Only
import { RefreshCw } from "lucide-react";
<Button size="sm" aria-label="예약 새로고침">
<RefreshCw size={16} />
</Button>;Cals 브랜딩
브랜드 컬러
Cals Button은 다음 브랜드 컬러를 사용합니다:
- Primary (Pink):
#e91e63- 주요 액션, CTA 버튼 (예약하기) - Secondary (Blue):
#03a9f4- 보조 액션 (상세보기) - Accent (Purple):
#9c27b0- 특별한 상태 (VIP 예약)
예약 상태 컬러 ⭐
Cals는 예약 시스템에 특화된 5가지 상태 컬러를 제공합니다:
- Available (Green):
#4caf50- 예약 가능 - Pending (Orange):
#ff9800- 승인 대기 - Confirmed (Blue):
#03a9f4- 예약 확정 - Cancelled (Red):
#f44336- 예약 취소 - Completed (Purple):
#9c27b0- 서비스 완료
Cals 특화 사용 가이드
예약 관리 시스템 액션:
// 신규 예약 등록 - Primary (Cals Pink)
<Button variant="primary">예약하기</Button>
// 예약 가능 상태 - Available (Green)
<Button variant="available">지금 예약 가능</Button>
// 승인 대기 - Pending (Orange)
<Button variant="pending">승인 요청</Button>
// 예약 확정 - Confirmed (Blue)
<Button variant="confirmed">예약 확정</Button>
// 예약 취소 - Cancelled (Red)
<Button variant="cancelled">예약 취소</Button>
// 서비스 완료 - Completed (Purple)
<Button variant="completed">서비스 완료</Button>예약 상태 변경 워크플로우:
<div className="flex gap-md">
<Button variant="outline">이전으로</Button>
<Button variant="confirmed">예약 확정하기</Button>
</div>예약 목록 액션 버튼:
import { Calendar, Check, X, Clock } from "lucide-react";
<div className="flex gap-sm">
<Button variant="available">
<Calendar size={16} className="mr-xs" />
신규 예약
</Button>
<Button variant="pending">
<Clock size={16} className="mr-xs" />
승인 대기 목록
</Button>
<Button variant="confirmed">
<Check size={16} className="mr-xs" />
확정된 예약
</Button>
<Button variant="cancelled">
<X size={16} className="mr-xs" />
취소된 예약
</Button>
</div>;Foundation/iCignal/Cals 비교
| 속성 | Foundation | iCignal | Cals |
|---|---|---|---|
| Primary Color | #3B82F6 (중립 블루) | #2196f3 (iCignal 블루) | #e91e63 (Cals 핑크) |
| 브랜드 적용 | 없음 | iCignal Analytics | Cals 예약 시스템 |
| 특화 Variants | 없음 | 없음 | 5가지 예약 상태 |
| 사용 맥락 | 범용 | 데이터 분석 | 예약 관리 |
| 예약 상태 지원 | ❌ | ❌ | ✅ Available/Pending/Confirmed/Cancelled/Completed |
| 커스터마이징 | 자유롭게 가능 | 브랜드 가이드 준수 | 브랜드 가이드 + 예약 컨텍스트 준수 |
| 테마 | 중립적 테마 | iCignal 테마 | Cals 테마 (@vortex/ui-cals/theme) |
import 경로 차이:
// Foundation
import { Button } from "@vortex/ui-foundation";
// iCignal
import "@vortex/ui-icignal/theme";
import { Button } from "@vortex/ui-icignal";
// Cals
import "@vortex/ui-cals/theme"; // 테마 import 필수
import { Button } from "@vortex/ui-cals";Props API
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | ’primary’ | ‘secondary’ | ‘accent’ | ‘outline’ | ‘ghost’ | ‘link’ | ‘destructive’ | ‘available’ | ‘pending’ | ‘confirmed’ | ‘cancelled’ | ‘completed' | 'primary’ | 버튼 변형 |
| size | ’sm’ | ‘md’ | ‘lg' | 'md’ | 버튼 크기 |
| disabled | boolean | false | 비활성화 상태 |
| loading | boolean | false | 로딩 상태 |
| onClick | () => void | - | 클릭 이벤트 핸들러 |
| type | ’button’ | ‘submit’ | ‘reset' | 'button’ | 버튼 타입 |
| className | string | - | 추가 CSS 클래스 |
| children | ReactNode | - | 버튼 내용 |
접근성
Button 컴포넌트는 WCAG 2.1 AA 기준을 준수합니다.
ARIA 속성:
role="button"(자동 적용)aria-disabled="true"(disabled 상태일 때)aria-label(Icon Only 버튼에 필수)
키보드 네비게이션:
Enter/Space: 버튼 클릭Tab: 다음 요소로 포커스 이동Shift + Tab: 이전 요소로 포커스 이동
스크린 리더 지원:
- 버튼 텍스트가 명확하게 읽힘
- Loading 상태 안내
- Disabled 상태 안내
색상 대비 (Cals 브랜드 컬러 기준):
- Primary (Pink): 4.5:1 이상
- Secondary (Blue): 4.5:1 이상
- Accent (Purple): 4.5:1 이상
- Available (Green): 4.5:1 이상
- Pending (Orange): 4.5:1 이상
- Confirmed (Blue): 4.5:1 이상
- Cancelled (Red): 4.5:1 이상
- Completed (Purple): 4.5:1 이상
예제
예제 1: 예약 관리 대시보드 ⭐
import "@vortex/ui-cals/theme";
import { Button } from "@vortex/ui-cals";
import { Calendar, Check, X, Clock, CheckCircle } from "lucide-react";
export default function ReservationDashboard() {
return (
<div className="space-y-lg">
<h2>예약 관리 대시보드</h2>
{/* 예약 상태별 액션 버튼 */}
<div className="grid grid-cols-5 gap-md">
<Button variant="available" size="lg" className="flex-col py-lg">
<Calendar size={24} className="mb-sm" />
<span className="text-sm">예약 가능</span>
<span className="text-2xl font-bold mt-xs">12</span>
</Button>
<Button variant="pending" size="lg" className="flex-col py-lg">
<Clock size={24} className="mb-sm" />
<span className="text-sm">승인 대기</span>
<span className="text-2xl font-bold mt-xs">5</span>
</Button>
<Button variant="confirmed" size="lg" className="flex-col py-lg">
<Check size={24} className="mb-sm" />
<span className="text-sm">예약 확정</span>
<span className="text-2xl font-bold mt-xs">28</span>
</Button>
<Button variant="cancelled" size="lg" className="flex-col py-lg">
<X size={24} className="mb-sm" />
<span className="text-sm">예약 취소</span>
<span className="text-2xl font-bold mt-xs">3</span>
</Button>
<Button variant="completed" size="lg" className="flex-col py-lg">
<CheckCircle size={24} className="mb-sm" />
<span className="text-sm">서비스 완료</span>
<span className="text-2xl font-bold mt-xs">45</span>
</Button>
</div>
{/* 신규 예약 버튼 */}
<Button variant="primary" size="lg" className="w-full">
<Calendar size={20} className="mr-sm" />
신규 예약 등록
</Button>
</div>
);
}예제 2: 예약 상태 변경 워크플로우 ⭐
import "@vortex/ui-cals/theme";
import { Button } from "@vortex/ui-cals";
import { Check, X, Clock } from "lucide-react";
export default function ReservationStatusChange() {
const [status, setStatus] = useState("pending");
const [loading, setLoading] = useState(false);
const handleConfirm = async () => {
setLoading(true);
await confirmReservation();
setStatus("confirmed");
setLoading(false);
};
const handleCancel = async () => {
setLoading(true);
await cancelReservation();
setStatus("cancelled");
setLoading(false);
};
return (
<Card>
<CardHeader>
<h3>예약 #12345</h3>
{status === "pending" && (
<Badge variant="pending">
<Clock size={14} className="mr-xs" />
승인 대기중
</Badge>
)}
{status === "confirmed" && (
<Badge variant="confirmed">
<Check size={14} className="mr-xs" />
예약 확정됨
</Badge>
)}
{status === "cancelled" && (
<Badge variant="cancelled">
<X size={14} className="mr-xs" />
예약 취소됨
</Badge>
)}
</CardHeader>
<CardContent>
<div className="space-y-sm">
<p>
<strong>고객명:</strong> 홍길동
</p>
<p>
<strong>예약일시:</strong> 2024-01-20 14:00
</p>
<p>
<strong>서비스:</strong> 프리미엄 패키지
</p>
</div>
</CardContent>
<CardFooter className="flex gap-md justify-end">
{status === "pending" && (
<>
<Button
variant="cancelled"
loading={loading}
onClick={handleCancel}
>
<X size={16} className="mr-xs" />
예약 거절
</Button>
<Button
variant="confirmed"
loading={loading}
onClick={handleConfirm}
>
<Check size={16} className="mr-xs" />
예약 확정
</Button>
</>
)}
{status === "confirmed" && (
<Button variant="completed">
<CheckCircle size={16} className="mr-xs" />
서비스 완료 처리
</Button>
)}
</CardFooter>
</Card>
);
}예제 3: 예약 가능 시간대 선택 ⭐
import "@vortex/ui-cals/theme";
import { Button } from "@vortex/ui-cals";
import { Calendar, Users } from "lucide-react";
export default function TimeSlotSelection() {
const [selectedTime, setSelectedTime] = useState(null);
const timeSlots = [
{ time: "10:00", status: "available", seats: 5 },
{ time: "11:00", status: "available", seats: 3 },
{ time: "12:00", status: "pending", seats: 2 },
{ time: "13:00", status: "confirmed", seats: 0 },
{ time: "14:00", status: "available", seats: 8 },
{ time: "15:00", status: "completed", seats: 0 },
];
return (
<div className="space-y-lg">
<div className="flex items-center gap-md">
<Calendar size={24} />
<h3>2024년 1월 20일 (토) 예약 가능 시간</h3>
</div>
<div className="grid grid-cols-3 gap-md">
{timeSlots.map((slot) => (
<Button
key={slot.time}
variant={slot.status}
size="lg"
disabled={slot.status !== "available"}
onClick={() => setSelectedTime(slot.time)}
className="flex-col py-lg"
>
<span className="text-xl font-bold">{slot.time}</span>
{slot.status === "available" && (
<span className="text-sm mt-xs flex items-center">
<Users size={14} className="mr-xs" />
{slot.seats}석 남음
</span>
)}
{slot.status === "pending" && (
<span className="text-xs mt-xs">승인 대기</span>
)}
{slot.status === "confirmed" && (
<span className="text-xs mt-xs">예약 마감</span>
)}
{slot.status === "completed" && (
<span className="text-xs mt-xs">완료됨</span>
)}
</Button>
))}
</div>
{selectedTime && (
<Button variant="primary" size="lg" className="w-full">
<Calendar size={20} className="mr-sm" />
{selectedTime} 예약하기
</Button>
)}
</div>
);
}예제 4: 예약 취소 확인 다이얼로그
import "@vortex/ui-cals/theme";
import { Button } from "@vortex/ui-cals";
import { X, AlertTriangle } from "lucide-react";
export default function CancelReservationDialog() {
const [loading, setLoading] = useState(false);
const handleCancel = async () => {
setLoading(true);
await cancelReservation();
setLoading(false);
};
return (
<Dialog>
<DialogContent>
<DialogHeader>
<AlertTriangle size={24} className="text-warning" />
<h3>예약 취소 확인</h3>
</DialogHeader>
<p>
정말로 이 예약을 취소하시겠습니까? 취소 후에는 되돌릴 수 없습니다.
</p>
<div className="bg-muted p-md rounded-md">
<p className="text-sm">
<strong>예약 번호:</strong> #12345
</p>
<p className="text-sm">
<strong>예약일시:</strong> 2024-01-20 14:00
</p>
<p className="text-sm">
<strong>고객명:</strong> 홍길동
</p>
</div>
<DialogFooter className="flex gap-md">
<Button variant="outline">돌아가기</Button>
<Button variant="cancelled" loading={loading} onClick={handleCancel}>
<X size={16} className="mr-xs" />
{loading ? "취소 처리 중..." : "예약 취소"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}관련 컴포넌트
- Foundation Button - 기본 버전 참조
- iCignal Button - iCignal 버전 참조
- Badge - 예약 상태 표시 뱃지
- Card - 버튼 그룹 컨테이너
- Dialog - 모달 내 액션 버튼
Last updated on