Tooltip
개요
Tooltip은 UI 요소에 마우스를 올렸을 때 짧은 설명을 표시하는 컴포넌트입니다. Cals에서는 예약 상태 설명, 아이콘 의미 설명, 버튼 기능 안내 등에 사용됩니다.
언제 사용하는가
- 예약 상태 아이콘 설명
- 버튼 기능 안내
- 축약된 텍스트 전체 내용
- 아이콘 의미 설명
- 짧은 도움말 (1-2줄)
언제 사용하지 말아야 하는가
- 긴 설명이나 복잡한 정보 → Popover 또는 Dialog 사용
- 중요한 정보 → Alert 사용
- 클릭 가능한 콘텐츠 → Popover 사용
- 모바일에서 필수 정보 → 명시적으로 표시
설치
npx @vortex/cli add tooltip기본 사용법
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Button } from "@/components/ui/button";
import { Info } from "lucide-react";
export default function TooltipDemo() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Info className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>예약 상태 정보</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}Variants
Tooltip은 단일 스타일이지만, 용도에 따라 내용과 위치를 다르게 구성합니다.
Default Tooltip
기본 Tooltip입니다.
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">마우스를 올리세요</Button>
</TooltipTrigger>
<TooltipContent>
<p>기본 툴팁입니다</p>
</TooltipContent>
</Tooltip>Icon Tooltip
아이콘 버튼에 대한 설명을 제공합니다.
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Calendar className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>달력 보기</p>
</TooltipContent>
</Tooltip>Status Tooltip
상태 아이콘에 대한 설명을 제공합니다.
<Tooltip>
<TooltipTrigger asChild>
<Badge className="bg-blue-500">확정</Badge>
</TooltipTrigger>
<TooltipContent>
<p>예약이 확정되었습니다</p>
</TooltipContent>
</Tooltip>Multi-line Tooltip
여러 줄의 설명을 제공합니다.
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<HelpCircle className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p>예약은 최소 24시간 전에 해주세요.</p>
<p className="mt-1">당일 예약은 전화로 문의하세요.</p>
</TooltipContent>
</Tooltip>Cals 브랜딩
브랜드 컬러
Cals의 Primary Pink를 활용한 브랜드 Tooltip 스타일입니다.
<Tooltip>
<TooltipTrigger asChild>
<Button className="bg-cals-primary hover:bg-cals-primary/90">Cals</Button>
</TooltipTrigger>
<TooltipContent className="bg-cals-primary text-white">
<p>Cals 예약 시스템</p>
</TooltipContent>
</Tooltip>예약 상태 컬러
예약 상태별 Tooltip 스타일을 제공합니다.
<TooltipProvider>
<div className="flex gap-2">
{/* Available - 예약 가능 */}
<Tooltip>
<TooltipTrigger asChild>
<Badge className="bg-green-500 cursor-help">예약 가능</Badge>
</TooltipTrigger>
<TooltipContent className="bg-green-500 text-white">
<p>해당 시간대에 예약하실 수 있습니다</p>
</TooltipContent>
</Tooltip>
{/* Pending - 승인 대기 */}
<Tooltip>
<TooltipTrigger asChild>
<Badge className="bg-orange-500 cursor-help">승인 대기</Badge>
</TooltipTrigger>
<TooltipContent className="bg-orange-500 text-white">
<p>예약 승인을 기다리고 있습니다</p>
</TooltipContent>
</Tooltip>
{/* Confirmed - 예약 확정 */}
<Tooltip>
<TooltipTrigger asChild>
<Badge className="bg-blue-500 cursor-help">예약 확정</Badge>
</TooltipTrigger>
<TooltipContent className="bg-blue-500 text-white">
<p>예약이 확정되었습니다</p>
</TooltipContent>
</Tooltip>
{/* Cancelled - 예약 취소 */}
<Tooltip>
<TooltipTrigger asChild>
<Badge className="bg-red-500 cursor-help">예약 취소</Badge>
</TooltipTrigger>
<TooltipContent className="bg-red-500 text-white">
<p>예약이 취소되었습니다</p>
</TooltipContent>
</Tooltip>
{/* Completed - 예약 완료 */}
<Tooltip>
<TooltipTrigger asChild>
<Badge className="bg-purple-500 cursor-help">서비스 완료</Badge>
</TooltipTrigger>
<TooltipContent className="bg-purple-500 text-white">
<p>서비스가 완료되었습니다</p>
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>브랜드 비교표
| 속성 | Foundation | iCignal | Cals |
|---|---|---|---|
| Primary Color | Neutral Gray | Corporate Blue #0066cc | Primary Pink #e91e63 |
| Use Case | 범용 도움말 | 기업 UI 가이드 | 예약 상태 설명 |
| Content | Short (1 line) | Medium (2-3 lines) | Short with Status |
| Background | Dark Gray | Corporate Blue | Status Color |
| Delay | 700ms | 500ms (빠른 피드백) | 700ms |
| Arrow | Yes | Yes | Yes |
Props API
TooltipProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| delayDuration | number | 700 | Tooltip 표시 지연 시간 (ms) |
| skipDelayDuration | number | 300 | 연속 표시 시 지연 시간 (ms) |
| disableHoverableContent | boolean | false | Tooltip 콘텐츠 호버 비활성화 |
Tooltip
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | - | Tooltip 열림 상태 (controlled) |
| onOpenChange | (open: boolean) => void | - | 열림 상태 변경 핸들러 |
| defaultOpen | boolean | false | 초기 열림 상태 (uncontrolled) |
| delayDuration | number | - | Provider 설정 오버라이드 |
TooltipTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | 자식 요소를 트리거로 사용 |
TooltipContent
| Prop | Type | Default | Description |
|---|---|---|---|
| side | "top" | "right" | "bottom" | "left" | "top" | 표시 위치 |
| align | "start" | "center" | "end" | "center" | 정렬 위치 |
| sideOffset | number | 4 | 트리거와의 간격 (px) |
| className | string | - | 커스텀 CSS 클래스 |
접근성
- Role:
role="tooltip"자동 적용 - ARIA:
aria-describedby자동 연결 - Keyboard: 키보드 포커스 시에도 표시
- ESC Key: ESC 키로 닫기
- Screen Reader: 스크린 리더가 자동으로 읽음
- Focus: Tooltip 자체는 포커스를 받지 않음
- Mobile: 터치 시에는 표시되지 않음 (접근성 고려 필요)
예제
예약 상태 아이콘
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Badge } from "@/components/ui/badge";
import {
CheckCircle2,
Clock,
XCircle,
Calendar,
AlertCircle,
} from "lucide-react";
export default function ReservationStatusTooltips() {
return (
<TooltipProvider>
<div className="flex gap-3">
{/* Available */}
<Tooltip>
<TooltipTrigger asChild>
<button className="p-2 rounded-lg hover:bg-green-50">
<Calendar className="h-5 w-5 text-green-500" />
</button>
</TooltipTrigger>
<TooltipContent className="bg-green-500 text-white">
<p>예약 가능</p>
</TooltipContent>
</Tooltip>
{/* Pending */}
<Tooltip>
<TooltipTrigger asChild>
<button className="p-2 rounded-lg hover:bg-orange-50">
<Clock className="h-5 w-5 text-orange-500" />
</button>
</TooltipTrigger>
<TooltipContent className="bg-orange-500 text-white">
<p>승인 대기 중</p>
</TooltipContent>
</Tooltip>
{/* Confirmed */}
<Tooltip>
<TooltipTrigger asChild>
<button className="p-2 rounded-lg hover:bg-blue-50">
<CheckCircle2 className="h-5 w-5 text-blue-500" />
</button>
</TooltipTrigger>
<TooltipContent className="bg-blue-500 text-white">
<p>예약 확정</p>
</TooltipContent>
</Tooltip>
{/* Cancelled */}
<Tooltip>
<TooltipTrigger asChild>
<button className="p-2 rounded-lg hover:bg-red-50">
<XCircle className="h-5 w-5 text-red-500" />
</button>
</TooltipTrigger>
<TooltipContent className="bg-red-500 text-white">
<p>예약 취소</p>
</TooltipContent>
</Tooltip>
{/* Completed */}
<Tooltip>
<TooltipTrigger asChild>
<button className="p-2 rounded-lg hover:bg-purple-50">
<CheckCircle2 className="h-5 w-5 text-purple-500" />
</button>
</TooltipTrigger>
<TooltipContent className="bg-purple-500 text-white">
<p>서비스 완료</p>
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);
}액션 버튼 가이드
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Button } from "@/components/ui/button";
import { Edit, Trash2, Copy, Download, Share2 } from "lucide-react";
export default function ActionButtonTooltips() {
return (
<TooltipProvider>
<div className="flex gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Edit className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>예약 수정</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Copy className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>예약 복제</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Download className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>예약 내역 다운로드</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Share2 className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>예약 정보 공유</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<Trash2 className="h-4 w-4 text-red-500" />
</Button>
</TooltipTrigger>
<TooltipContent className="bg-red-500 text-white">
<p>예약 삭제</p>
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);
}예약 캘린더 시간대
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Button } from "@/components/ui/button";
export default function CalendarTimeSlots() {
const timeSlots = [
{ time: "09:00", status: "available", label: "예약 가능" },
{ time: "10:00", status: "confirmed", label: "홍길동 - 컨설팅" },
{ time: "11:00", status: "available", label: "예약 가능" },
{ time: "14:00", status: "pending", label: "김철수 - 승인 대기" },
{ time: "15:00", status: "confirmed", label: "이영희 - 미팅" },
{ time: "16:00", status: "cancelled", label: "예약 취소됨" },
];
const getStatusColor = (status: string) => {
switch (status) {
case "available":
return "bg-green-100 text-green-700 hover:bg-green-200";
case "confirmed":
return "bg-blue-100 text-blue-700 hover:bg-blue-200";
case "pending":
return "bg-orange-100 text-orange-700 hover:bg-orange-200";
case "cancelled":
return "bg-red-100 text-red-700 hover:bg-red-200";
default:
return "bg-gray-100 text-gray-700 hover:bg-gray-200";
}
};
const getTooltipColor = (status: string) => {
switch (status) {
case "available":
return "bg-green-500 text-white";
case "confirmed":
return "bg-blue-500 text-white";
case "pending":
return "bg-orange-500 text-white";
case "cancelled":
return "bg-red-500 text-white";
default:
return "";
}
};
return (
<TooltipProvider>
<div className="grid grid-cols-3 gap-2">
{timeSlots.map((slot) => (
<Tooltip key={slot.time}>
<TooltipTrigger asChild>
<Button
variant="outline"
className={`${getStatusColor(slot.status)} border-none`}
>
{slot.time}
</Button>
</TooltipTrigger>
<TooltipContent className={getTooltipColor(slot.status)}>
<p>{slot.label}</p>
</TooltipContent>
</Tooltip>
))}
</div>
</TooltipProvider>
);
}다중 줄 Tooltip
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Button } from "@/components/ui/button";
import { HelpCircle } from "lucide-react";
export default function MultiLineTooltip() {
return (
<TooltipProvider>
<div className="flex items-center gap-2">
<span className="text-sm font-medium">예약 정책</span>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="h-5 w-5">
<HelpCircle className="h-4 w-4 text-muted-foreground" />
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<div className="space-y-1">
<p className="font-semibold">예약 정책 안내</p>
<p className="text-xs">• 최소 24시간 전 예약 필수</p>
<p className="text-xs">• 당일 예약은 전화 문의</p>
<p className="text-xs">• 취소 시 수수료 부과될 수 있음</p>
</div>
</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);
}관련 컴포넌트
Last updated on