Tooltip | CoUI

Tooltip

툴팁 컴포넌트

Tooltip#

요소에 호버하거나 포커스할 때 설명 텍스트를 표시하는 툴팁 컴포넌트입니다.

Live Preview#

Web
Flutter
Loading Flutter...
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 보더 반경
panelPaddingdouble?panel 내부 패딩
surfaceBlurdouble?backdrop blur sigma
surfaceOpacitydouble?panel 표면 불투명도
labelStyleCoreTextStyle?라벨 텍스트 스타일

Resolve chain#

design system default
  → CoreTooltipTheme.style                       // 프로젝트 공통
  → 부모 컴포넌트 슬롯 오버라이드
  → widget.tooltipStyle                          // 인스턴스별

Props / Parameters#

속성타입기본값설명
messageString''툴팁 텍스트 메시지
tooltipChild Widget? null 커스텀 툴팁 콘텐츠 (message 대체)
childWidgetrequired툴팁을 표시할 대상 위젯
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 은 토큰 고정)
minDuration0ms최소 표시 유지 시간 (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 구현#

  • CoOverlayHosttooltip 레이어로 panel 을 portal 마운트 → ancestor overflow: 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)#

항목FlutterWeb
클래스명CoTooltipCoTooltip
메시지message: Stringmessage: 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
  • 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),
    ),
  ),
])