Tooltip#
요소에 호버하거나 포커스할 때 설명 텍스트를 표시하는 툴팁 컴포넌트입니다.
Live Preview#
class TooltipDefaultExample extends StatefulComponent {
const TooltipDefaultExample({super.key});
@override
State<TooltipDefaultExample> createState() => _TooltipDefaultExampleState();
}
class _TooltipDefaultExampleState extends State<TooltipDefaultExample> {
@override
Component build(BuildContext context) {
return CoTooltip(
message: 'Helpful tip',
position: CoreTooltipPosition.bottom,
child: CoButton(
variant: CoreButtonVariant.outline,
onPressed: () {},
child: text('Hover me'),
),
);
}
}
class TooltipDefaultExample extends StatefulWidget {
const TooltipDefaultExample({super.key});
@override
State<TooltipDefaultExample> createState() => _TooltipDefaultExampleState();
}
class _TooltipDefaultExampleState extends State<TooltipDefaultExample> {
@override
Widget build(BuildContext context) {
return CoTooltip(
message: 'Helpful tip',
position: CoreTooltipPosition.bottom,
child: CoButton(
variant: CoreButtonVariant.outline,
onPressed: () {},
child: const Text('Hover me'),
),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 아이콘 버튼이나 축약된 UI에 추가 설명이 필요할 때
- 키보드 단축키 정보를 호버로 표시할 때
- 짧은 텍스트 힌트(1~2줄)로 충분할 때
대신 다른 컴포넌트를 사용하세요:
HoverCard: 이미지, 버튼 등 풍부한 콘텐츠가 필요할 때Popover: 클릭으로 열리는 인터랙티브 콘텐츠일 때Toast: 액션 결과 피드백을 표시할 때
기본 사용법 (Basic Usage)#
// 기본 툴팁
CoTooltip(
message: '이 버튼을 클릭하면 저장됩니다',
child: CoButton(
variant: CoreButtonVariant.primary,
onPressed: handleSave,
child: Icon(Icons.save),
),
)
// 위치 지정
CoTooltip(
message: '설정 메뉴',
position: CoreTooltipPosition.right,
child: Icon(Icons.settings),
)
// 리치 콘텐츠 툴팁
CoTooltip(
message: '단축키: Ctrl + S',
tooltipChild: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('단축키', style: TextStyle(fontWeight: FontWeight.bold)),
Text('Ctrl + S'),
],
),
child: Icon(Icons.keyboard),
)
// 기본 툴팁 (호버 시 텍스트 표시)
CoTooltip(
message: '이 버튼을 클릭하면 저장됩니다',
child: CoButton(
variant: CoreButtonVariant.primary,
onPressed: handleSave,
child: text('저장'),
),
)
// 위치 제어
CoTooltip(
message: '설정 메뉴',
position: CoreTooltipPosition.bottom,
child: Icon(Icons.settings),
)
// 단축키 정보 포함
CoTooltip(
message: '실행 취소 (Ctrl+Z)',
child: CoButton(
variant: CoreButtonVariant.ghost,
onPressed: handleUndo,
child: text('실행 취소'),
),
)
스타일 시스템 — tooltipStyle (Epic #1302)#
CoTooltip 의 panel chrome / 슬롯 미세 조정은 단일 tooltipStyle
(CoreTooltipStyle) 으로 흐릅니다. 시맨틱 enum (position) 은 위젯
파라미터.
CoTooltip(
message: '도움말',
position: CoreTooltipPosition.top,
child: Icon(Icons.info),
tooltipStyle: CoreTooltipStyle(
panelBackgroundColor: cs.inverseSurface,
panelBorderRadius: BorderRadius.circular(6),
panelPadding: 8,
surfaceBlur: 0,
surfaceOpacity: 0.95,
labelStyle: CoreTextStyle(fontSize: 12, color: cs.onInverseSurface),
),
)
CoreTooltipStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
panelBackgroundColor |
Color? / String? |
panel 배경 |
panelBorderRadius |
BorderRadiusGeometry? / String? |
panel 보더 반경 |
panelPadding | double? | panel 내부 패딩 |
surfaceBlur | double? | backdrop blur sigma |
surfaceOpacity | double? | panel 표면 불투명도 |
labelStyle | CoreTextStyle? | 라벨 텍스트 스타일 |
Resolve chain#
design system default
→ CoreTooltipTheme.style // 프로젝트 공통
→ 부모 컴포넌트 슬롯 오버라이드
→ widget.tooltipStyle // 인스턴스별
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
message | String | '' | 툴팁 텍스트 메시지 |
tooltipChild |
Widget? |
null |
커스텀 툴팁 콘텐츠 (message 대체) |
child | Widget | required | 툴팁을 표시할 대상 위젯 |
position |
CoreTooltipPosition |
top |
툴팁 위치 (위젯 파라미터) |
tooltipStyle |
CoreTooltipStyle? |
null |
panel chrome / nested labelStyle 묶음 |
waitDuration |
Duration |
150ms |
표시 지연 시간 (Flutter) |
showDuration |
Duration |
150ms |
표시/닫기 애니메이션 시간 (Flutter) |
변형 (Variants)#
위치#
// 위 (기본값)
CoTooltip(
message: '위',
position: CoreTooltipPosition.top,
child: target,
)
// 아래
CoTooltip(
message: '아래',
position: CoreTooltipPosition.bottom,
child: target,
)
// 좌/우
CoTooltip(
message: '우',
position: CoreTooltipPosition.right,
child: target,
)
지연 시간 조절 (Flutter)#
// 즉시 표시
CoTooltip(
message: '즉시 표시',
waitDuration: Duration.zero,
child: target,
)
// 긴 지연
CoTooltip(
message: '1초 후 표시',
waitDuration: Duration(seconds: 1),
child: target,
)
리치 툴팁#
CoTooltip(
message: '추가 정보',
tooltipChild: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.info, size: 16),
SizedBox(width: 8),
Text('추가 정보가 있습니다'),
],
),
child: target,
)
동작 스펙 (Behavior)#
타이밍#
| 파라미터 | 기본값 | 설명 |
|---|---|---|
waitDuration |
150ms (AnimationConstants.short) |
호버 후 표시까지 대기 (Flutter 전용) |
showDuration |
150ms (AnimationConstants.short) |
표시 애니메이션 시간 (Flutter 전용 — Web 은 토큰 고정) |
minDuration | 0ms | 최소 표시 유지 시간 (Flutter 전용) |
Web 은
waitDuration을 노출하지 않고 호버 시점에 즉시 마운트합니다 (브라우저:hover의 즉시성 패리티). 표시/닫기 애니메이션은 양 플랫폼 모두CorePopoverTokens.openAnimationDuration(150 ms) /closeAnimationDuration(100 ms wall-clock ×Interval(0, 2/3)≈ 67 ms 가시 모션) 으로 고정.
위치#
// CoreTooltipPosition 기반 위치 제어
CoTooltip(
position: CoreTooltipPosition.top,
message: '설명',
child: target,
)
-
position:top,bottom,left,right중 선택 - 뷰포트 경계 자동 감지 및 위치 조정
Surface 효과#
CoTooltip(
message: '블러 효과',
surfaceBlur: 10.0,
surfaceOpacity: 0.8,
child: target,
)
애니메이션#
양 플랫폼 모두 동일한 토큰을 사용하는 Fade + Scale 트랜지션입니다.
- Open: 150 ms
linear(AnimationConstants.short) -
Close: 67 ms 가시 모션 (100 ms wall-clock ×
Interval(0, 2/3)) — Flutter 의Interval곡선을 Web 은transition-duration단축으로 동등하게 표현 -
Scale:
0.9 ↔ 1.0(CorePopoverTokens.scaleClosed/scaleOpen) - Transform-origin:
center(트리거 중앙 기준)
Web 구현#
-
CoOverlayHost의tooltip레이어로 panel 을 portal 마운트 → ancestoroverflow: hidden/transform컨테이너 안에서도 잘리지 않음 - 트리거 래퍼는
inline-flex(line-height descender 회피) -
panel 은
position: fixed+ viewport 좌표 —top/left/transform: translate(...)인라인으로 출력 -
호버 enter/leave 는 JS 이벤트로
setState처리 (CSS:hover가 아니라mouseenter/mouseleave핸들러) - viewport scroll/resize 및 트리거
ResizeObserver변화 시 anchor 자동 재계산
사용 가이드라인 (Usage Guidelines)#
✅ Do#
아이콘 전용 버튼에 항상 툴팁을 추가하세요.
CoTooltip(
message: '저장 (Ctrl+S)',
child: CoButton(
variant: CoreButtonVariant.primary,
onPressed: handleSave,
child: Icon(Icons.save),
),
)
텍스트 라벨이 없는 아이콘은 의미를 알기 어렵습니다.
❌ Don't#
이미 레이블이 있는 버튼에 같은 내용의 툴팁을 추가하지 마세요.
// ❌ Bad — 중복 정보
CoTooltip(
message: '저장',
child: CoButton(variant: CoreButtonVariant.primary, onPressed: handleSave, child: Text('저장')),
)
단축키 같은 추가 정보가 있을 때만 사용하세요.
✅ Do#
툴팁 텍스트는 짧고 명확하게 작성하세요.
// ✅ Good
CoTooltip(message: '실행 취소 (Ctrl+Z)', child: undoButton)
// ❌ Bad — 너무 긴 설명
CoTooltip(message: '이 버튼을 클릭하면 마지막 작업이 취소되고 이전 상태로 복원됩니다.', child: undoButton)
1~2줄 이내로 유지합니다. 긴 설명은 HoverCard를 사용하세요.
❌ Don't#
인터랙티브 콘텐츠를 툴팁에 넣지 마세요.
마우스가 벗어나면 사라지므로 버튼, 링크 등을 배치할 수 없습니다. 클릭 가능한 콘텐츠는 Popover를 사용하세요.
✅ Do#
적절한 지연 시간을 유지하세요.
기본 500ms 지연이 대부분의 경우에 적합합니다. 너무 짧으면 의도치 않게 나타나고, 너무 길면 사용자가 기다립니다.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Focus | 포커스 시 툴팁 표시 |
Blur | 포커스 해제 시 툴팁 닫힘 |
Escape | 툴팁 즉시 닫기 |
스크린 리더#
- Flutter: 툴팁 메시지가 대상 위젯의 시맨틱에 추가됨
- Web: CSS 기반 표시로 스크린 리더에 제한적.
aria-label을 대상에 추가 권장
모바일#
- Flutter: 롱프레스로 툴팁 표시 (Hover 대안)
- Web: CSS hover 전용, 터치 대안 없음
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CoTooltip | CoTooltip |
| 메시지 | message: String | message: String |
| 리치 콘텐츠 | tooltipChild: Widget |
tooltipChild: Component |
| 위치 | position: CoreTooltipPosition |
position: CoreTooltipPosition |
| 표시 딜레이 | waitDuration (기본 150ms) | 즉시 (호버 시점에 마운트) |
| 애니메이션 | Fade + Scale (open 150 ms / close 67 ms, scale 0.9↔1.0) | Fade + Scale (open 150 ms / close 67 ms, scale 0.9↔1.0) — 동일 토큰 |
| 오버레이 마운트 | Root Overlay (OverlayManager) |
CoOverlayHost.tooltip 레이어 portal |
| 모바일 | 롱프레스 지원 | 미지원 |
| Surface 효과 | surfaceBlur, surfaceOpacity |
surfaceBlur (backdrop-filter), surfaceOpacity |
| 테마 | Theme.of(context) |
Tailwind CSS + CoreComponentTheme |
관련 컴포넌트 (Related Components)#
- HoverCard: 풍부한 호버 미리보기. Tooltip보다 크고 복잡한 콘텐츠
- Popover: 클릭 트리거 팝업. 인터랙티브 콘텐츠에 적합
- Toast: 알림 메시지. 액션 결과 피드백 용도
조합 예제#
// 툴바 아이콘 버튼 패턴
Row(children: [
CoTooltip(
message: '실행 취소 (Ctrl+Z)',
child: CoButton(
variant: CoreButtonVariant.ghost,
onPressed: handleUndo,
child: Icon(Icons.undo),
),
),
CoTooltip(
message: '다시 실행 (Ctrl+Y)',
child: CoButton(
variant: CoreButtonVariant.ghost,
onPressed: handleRedo,
child: Icon(Icons.redo),
),
),
CoTooltip(
message: '서식 지우기',
child: CoButton(
variant: CoreButtonVariant.ghost,
onPressed: handleClearFormat,
child: Icon(Icons.format_clear),
),
),
])