Groo Design

Tooltip

마우스를 올리거나 키보드 초점이 오면 트리거 옆에 뜨는 짧은 설명 — 아이콘 버튼 · 줄인 글이 무엇인지 보여 주는 보조다. 손가락으로 누르면 열리지 않으므로 이름 · 막힌 이유처럼 꼭 알아야 하는 것은 툴팁에만 두지 않는다. 폰에서도 읽어야 하는 설명은 같은 모양의 Help Bubble(눌러서)이다. 앱에도 둔다 — 하드웨어 키보드 초점 · 마우스에서만 열린다. 손가락 누름 · 길게 누르기로는 열지 않는다(2026-10-02 사용자 결정 8A — 길게 누르기 안은 고르지 않았다 · 2026-10-08 "앱도 하드웨어 키보드").

구조는 당근 SEED Help Bubble Tooltip(Apache-2.0)을 따른다 — Help Bubble 과 같은 말풍선(짙은 바탕 · 13 · 모서리 12 · 화살표 · 최대 280)을 마우스 · 키보드로 여는 것이다. 값은 groo 토큰이고, SEED 와 다른 자리는 맨 아래 "SEED 와 다른 점" 에 적었다(2026-10-02 사용자 결정).

수치 원본은 help-bubble.yaml의 opens: hover 다 — 모양은 Help Bubble 과 한 벌이다. 툴팁만의 수치 파일(tooltip.yaml · 내보낸 tooltip.json)은 두지 않는다 — SEED 도 rootage 에 help-bubble 하나다. 웹 · 앱의 검사는 툴팁을 help-bubble 의 opens: hover 로 잰다(구현 세션 질문 Q-009-1). 수치 표 자리([표: …])와 그림 자리([그림: …])는 사이트가 그 파일로 그린다 — GitHub 에서는 그 파일 · 그림 코드로 가는 링크로 보인다.

desk.groo.app
워크스페이스
기록
가계부
desk.groo.app
워크스페이스
기록
자산
가계부
desk.groo.app
워크스페이스
기록
가계부
desk.groo.app
워크스페이스
기록
자산
가계부
아이콘 버튼 · 접힌 사이드바의 툴팁 — 라이트 · 다크

직접 골라 보기

글 길이 · 위치를 고르면 스펙대로 그린 툴팁과 그 코드가 바뀐다. 마우스를 올리거나 Tab 으로 초점을 옮겨 열 수 있다.

글(설정)
위치
브랜드
모드
import { Settings } from "lucide-react"
import { TopNavigationIconButton } from "@/components/ui/top-navigation"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"

{/* 화면에 한 번 — 이어서 여는 툴팁은 기다리지 않는다 */}
<TooltipProvider>
  <Tooltip>
    <TooltipTrigger asChild>
      <TopNavigationIconButton aria-label="설정" onClick={openSettings}><Settings /></TopNavigationIconButton>
    </TooltipTrigger>
    <TooltipContent>설정</TooltipContent>
  </Tooltip>
</TooltipProvider>

Anatomy

알림
ⓐ Container — Help Bubble 과 같다ⓑ Arrow — 늘 트리거 가운데ⓒ Title — 글 하나(굵게)
툴팁은 말풍선 · 화살표 · 글 하나
부위설명
ⓐ Container말풍선 — Help Bubble 과 같다. 누를 것이 없다.
ⓑ Arrow화살표 — 늘 트리거 가운데를 가리킨다.
ⓒ Title글 — 한 줄로 짧게.

Properties

말풍선

모양 · 여백 · 위치는 Help Bubble 과 같다 — 위아래 10 · 좌우 12, 모서리 12, 글 13 / 18, 최대 280, 화살표 끝과 트리거 사이 4. 툴팁은 글 하나만 둔다(굵게 — Help Bubble 의 제목 자리).

말풍선

부위속성값비고
root최대 너비280px여백을 포함한 폭 — 한 줄 20자 안팎, 2 ~ 3줄이면 넘지 않는다. 쓸 수 있는 폭(화면 − 양쪽 16)이 더 좁으면 그만큼(SEED 와 같다)
root상하 여백spacing-x2_5 10px
root좌우 여백spacing-x3 12px
root모서리radius-r3 12px
root배경color-bg-neutral-inverted #1A1F2E · 다크 #F5F6FA다크에서는 밝은 말풍선
root그림자none
root트리거와 거리spacing-x1 4px화살표 끝 ↔ 트리거 4(말풍선 몸통과는 12)
root바깥 여백spacing-x4 16px화면 가장자리와 16 — 모자라면 반대편으로 뒤집고 옆으로 민다. 화살표를 트리거 가운데 · 모서리 14 에 두느라 모자라면 이 값이 진다 — 하한 없이 화면 끝(0)까지(2026-10-15 사용자 결정 — 구현 세션 질문 Q-004-1)
rootmarginMin0px트리거가 화면 끝에 붙었을 때 — 상단 바 끝 6 의 아이콘 버튼 44 면 8, 화면 끝 0 의 ⓘ 40 이면 0. 누르는 자리가 40 이상이면 말풍선이 화면 밖으로 나가지 않는다
rootannounce제목 · 설명열릴 때 한 번, 초점을 옮기지 않고 — 웹 aria-live polite · 앱 Semantics liveRegion. 처음부터 열린 안내도(2026-10-15 사용자 결정 — 구현 세션 질문 Q-004-2)
rootz-index210z-tooltip — specs/z-index.md L4
arrow너비12px
arrow높이8px끝 모서리 2 — 말풍선과 같은 색. 트리거와 4 는 이 상자의 끝에서 잰다 — 둥근 끝 때문에 그림 끝은 1 더 떨어져 보인다(SEED 와 같다)
arrow바깥 여백14px말풍선 모서리에서 최소 14 — 화살표는 트리거 가운데에서 움직이지 않고 말풍선을 민다(화살표 가운데 ↔ 말풍선 끝 20). 이 값이 화면 가장자리 16 보다 먼저다
arrow자리트리거 가운데비켜 그리지 않고 지우지 않는다 — 웹 Radix 의 숨김(cannotCenterArrow)을 쓰지 않는다
title글자text-t3 13px / 18px단어 단위 줄바꿈(v114) · 줄바꿈 문자는 그대로
title굵기700
title글자·아이콘color-fg-neutral-inverted #FFFFFF · 다크 #242938
description글자text-t3 13px / 18px
description굵기400
description글자·아이콘color-fg-neutral-inverted #FFFFFF · 다크 #242938
description간격spacing-x0_5 2px제목 ↔ 설명

여는 방식

마우스를 올리면 200ms 뒤에 열고, 트리거와 말풍선을 모두 벗어나면 100ms 뒤에 닫는다 — 말풍선 위로 포인터를 옮겨도 닫히지 않는다. 키보드 초점이 오면 바로 연다. 하나가 열린 뒤 옆 트리거로 옮기면 기다리지 않고 모션 없이 바로 바꿔 연다. 손가락으로 누르면 열지 않는다.

마우스
올림알림200ms벗어남+100ms
올리고 200ms 뒤에 연다 · 트리거와 말풍선을 모두 벗어나면 100ms 뒤에 닫는다(말풍선 위로 옮기는 동안은 열어 둔다)
키보드
Tab알림초점 떠남
Tab 으로 초점이 오면 기다리지 않고 바로 연다 · 초점이 떠나면 닫는다
이어서
알림닫힘옆으로 옮김설정
하나가 열린 동안이나 닫힌 뒤 300ms 안에 옆 트리거로 옮기면 기다리지 않고 모션 없이 바로 연다
0100200300400500600700ms
열림 모션 200ms(크기 0.9 → 1 · 나타남) · 이어서 열 때는 없다 · 손가락으로 누르면 열지 않는다
마우스 200ms · 키보드 바로 · 이어서 옮기면 바로

여는 방식

opens열림 지연닫힘 지연이어 열기 시간설명
press (기본)———Help Bubble — 트리거를 누르면 열고 닫는다(처음부터 열어 둘 수도 있다)
hover200ms — 마우스 — 키보드 초점은 바로(0). 하나가 열린 뒤 옆 트리거로 옮기면 바로 · 모션 없이100ms — 트리거와 말풍선을 모두 벗어난 뒤 — 말풍선 위로 옮겨도 닫히지 않는다(WCAG 1.4.13)300ms — 하나가 닫힌 뒤 이 시간 안에 옆 트리거로 옮기면 기다리지 않고 모션 없이 바로 연다Tooltip — 마우스를 올리면 200ms 뒤, 키보드 초점이 오면 바로 연다. 터치로는 열지 않는다

모션

전환시간곡선대상비고
열림motion-duration-d4 200msmotion-ease-enter cubic-bezier(0, 0, 0.15, 1)scale 0.9 → 1, opacity 0 → 1화살표 끝을 기준점으로 커진다 — 툴팁이 이어서 열리면 모션 없이 바로
닫힘motion-duration-d4 200msmotion-ease-easing cubic-bezier(0.35, 0, 0.35, 1)opacity → 0크기는 그대로, 투명도만

Guidelines

툴팁은 보조다

툴팁은 마우스 · 키보드에서만 열린다. 그래서 툴팁에만 있는 정보는 손가락으로 쓰는 사람에게 없다.

  • 이름은 aria-label — 아이콘 버튼의 이름은 늘 aria-label 에 둔다. 툴팁은 그 이름을 마우스 · 키보드 사용자에게 보여 주는 것이지 이름을 대신하지 않는다.
  • 막힌 이유는 가까운 글로 — 막힌 버튼은 초점을 받지 못해 키보드로 툴팁을 열 수 없다. 왜 안 되는지는 버튼 가까이 글로 보인다("복사할 지난달 예산이 없어요.").
  • 폰에서도 읽어야 하면 Help Bubble — ⓘ 를 눌러 여는 말풍선.
  • 네이티브 title 은 쓰지 않는다 — 터치 · 키보드에서 뜨지 않고 모양도 브라우저마다 다르다. 이름은 aria-label, 설명은 툴팁 · 보이는 글.
알림
aria-label="알림"
이렇게 — 아이콘 버튼의 이름은 aria-label — 툴팁은 그 이름을 마우스 · 키보드 사용자에게 보여 줄 뿐이다
복사할 지난달 예산이 없어요.
이렇게 — 막힌 버튼은 초점을 받지 못한다 — 왜 안 되는지는 버튼 가까이 글로 보인다
복사할 지난달 예산이 없어요
이렇게 하지 않는다 — 막힌 이유를 툴팁 · 네이티브 title 에만 — 마우스를 올려야만 보이고, 손가락 · 키보드로는 끝내 볼 수 없다
9:41
휴가
남은 연차11일
연차 사용 규정입사 1년 미만은 한 달에 1일씩 생기고, 1년이 지나면 15일이 생겨요.
이렇게 — 폰에서도 읽어야 하는 설명은 ⓘ 를 눌러 여는 Help Bubble 로
이름은 aria-label · 막힌 이유는 가까운 글 · 툴팁에만 두지 않는다

어디에 쓰나

자리툴팁
아이콘만 있는 버튼(툴바 · 머리)이름을 보여 준다 — aria-label 과 같은 글
접힌 사이드바의 아이콘메뉴 이름을 보여 준다
줄여 보인 글(말줄임)다 보여 준다
글자가 이미 보이는 버튼두지 않는다 — 같은 말을 두 번
막힌 버튼의 이유두지 않는다 — 가까운 글로
링크 · 버튼이 든 설명두지 않는다 — Popover

짧게

한 줄이 원칙이다 — "검색" · "알림" · "필터 초기화". 최대 280 을 넘기면 줄을 바꾸지만, 두 줄이 넘는 설명은 Help Bubble 로 옮긴다. 이름이면 명사 · 동사구 그대로, 문장이면 해요체에 마침표.

코드

레시피 recipes/shadcn/components/ui/tooltip.tsx 를 쓴다(Radix Tooltip 위 — 모양은 Help Bubble 과 같은 클래스). 화면에 TooltipProvider 를 한 번 두면 이어서 여는 툴팁이 기다리지 않는다. 아래 미리보기는 스펙 값으로 그린 모습이다.

아이콘 버튼의 이름

가계부
이번 달 지출1,240,000원

마우스를 올리면 잠시 뒤, Tab 으로 옮기면 바로 이름이 떠요. 손가락으로 누르면 뜨지 않아요.

import { Settings } from "lucide-react"
import { TopNavigationIconButton } from "@/components/ui/top-navigation"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"

{/* Desk 웹 데스크톱 머리의 아이콘 버튼(알림 · 설정) */}
<Tooltip>
  <TooltipTrigger asChild>
    {/* 이름은 aria-label — 툴팁은 그 이름을 마우스 · 키보드 사용자에게 보여 준다 */}
    <TopNavigationIconButton aria-label="설정" onClick={openSettings}><Settings /></TopNavigationIconButton>
  </TooltipTrigger>
  <TooltipContent>설정</TooltipContent>
</Tooltip>

막힌 버튼 — 이유는 툴팁이 아니라 가까운 글

복사할 지난달 예산이 없어요.

지난달 예산

<div className="flex flex-col gap-1.5">
  <Button variant="neutralWeak" disabled={!hasLastMonth} aria-describedby="copy-reason">
    지난달 예산 복사
  </Button>
  {!hasLastMonth && (
    <p id="copy-reason" className="text-t3 text-fg-neutral-subtle">복사할 지난달 예산이 없어요.</p>
  )}
</div>

Behavior

인터랙션동작
마우스를 올림200ms 뒤 연다
마우스가 트리거 · 말풍선을 벗어남100ms 뒤 닫는다 — 말풍선 위로 옮기는 동안은 열어 둔다
키보드 초점(Tab)바로 연다. 초점이 떠나면 닫는다 — 그동안 포인터가 지나가도 닫지 않는다
Esc닫는다. 초점은 트리거에 그대로
트리거 누르기툴팁을 열지도 닫지도 않는다 — 누름은 트리거의 동작이다. 마우스를 올려 기다리던 열기는 거둔다
손가락으로 누르기열지 않는다
이어서 옆 트리거로하나가 열려 있거나 닫힌 지 300ms 안에 다른 트리거로 옮기면 기다리지 않고 모션 없이 바로(help-bubble.yaml 의 skipDelay)
스크롤트리거를 따라간다

Accessibility

기준검증
WCAG 1.1.1 Non-text Content아이콘 버튼의 이름은 aria-label — 툴팁이 없어도 이름이 있다
WCAG 1.4.3 Color contrast(글자 ≥ 4.5:1)글 fg-neutral-inverted 말풍선(bg-neutral-inverted) 위 16.41 · 다크 13.42 ✓
WCAG 1.4.11 Non-text contrast(≥ 3:1)말풍선 면 페이지 위 16.41 · 다크 13.42 ✓
WCAG 1.4.13 Content on Hover or Focus닫을 수 있다(Esc) · 말풍선 위로 옮겨도 남는다 · 벗어나기 전까지 사라지지 않는다 ✓
WCAG 2.1.1 Keyboard키보드 초점으로 바로 열린다 ✓ — 막힌 버튼은 초점을 받지 못하므로 이유는 가까운 글로
ARIA말풍선 role="tooltip", 트리거에 aria-describedby. 트리거의 이름은 툴팁이 아니라 aria-label · 보이는 글

Do / Don't

✅ Do

  • 아이콘 버튼에는 aria-label 과 같은 글의 툴팁.
  • 한 줄로 짧게.
  • 막힌 이유는 버튼 가까이 글로.
  • 화면에 TooltipProvider 한 번.

❌ Don't

  • 이름 · 막힌 이유 · 꼭 알아야 하는 정보를 툴팁에만.
  • 네이티브 title.
  • 툴팁 안에 링크 · 버튼.
  • 글자가 이미 보이는 버튼에 같은 말의 툴팁.

Specification

툴팁은 Help Bubble 과 같은 help-bubble.yaml 을 쓴다 — opens: hover 의 규칙이 툴팁이다.

Base

부위상태속성값
rootenabled최대 너비maxWidth280px여백을 포함한 폭 — 한 줄 20자 안팎, 2 ~ 3줄이면 넘지 않는다. 쓸 수 있는 폭(화면 − 양쪽 16)이 더 좁으면 그만큼(SEED 와 같다)
상하 여백paddingYspacing-x2_510px
좌우 여백paddingXspacing-x312px
바깥 여백marginspacing-x416px화면 가장자리와 16 — 모자라면 반대편으로 뒤집고 옆으로 민다. 화살표를 트리거 가운데 · 모서리 14 에 두느라 모자라면 이 값이 진다 — 하한 없이 화면 끝(0)까지(2026-10-15 사용자 결정 — 구현 세션 질문 Q-004-1)
모서리radiusradius-r312px
배경backgroundbg-neutral-inverted#1A1F2E다크 #F5F6FA다크에서는 밝은 말풍선
그림자shadownone
z-indexzIndex210z-tooltip — specs/z-index.md L4
트리거와 거리offsetspacing-x14px화살표 끝 ↔ 트리거 4(말풍선 몸통과는 12)
marginMinmarginMin0px트리거가 화면 끝에 붙었을 때 — 상단 바 끝 6 의 아이콘 버튼 44 면 8, 화면 끝 0 의 ⓘ 40 이면 0. 누르는 자리가 40 이상이면 말풍선이 화면 밖으로 나가지 않는다
announceannounce제목 · 설명열릴 때 한 번, 초점을 옮기지 않고 — 웹 aria-live polite · 앱 Semantics liveRegion. 처음부터 열린 안내도(2026-10-15 사용자 결정 — 구현 세션 질문 Q-004-2)
arrowenabled높이height8px끝 모서리 2 — 말풍선과 같은 색. 트리거와 4 는 이 상자의 끝에서 잰다 — 둥근 끝 때문에 그림 끝은 1 더 떨어져 보인다(SEED 와 같다)
너비width12px
바깥 여백margin14px말풍선 모서리에서 최소 14 — 화살표는 트리거 가운데에서 움직이지 않고 말풍선을 민다(화살표 가운데 ↔ 말풍선 끝 20). 이 값이 화면 가장자리 16 보다 먼저다
자리position트리거 가운데비켜 그리지 않고 지우지 않는다 — 웹 Radix 의 숨김(cannotCenterArrow)을 쓰지 않는다
titleenabled글자·아이콘foregroundfg-neutral-inverted#FFFFFF다크 #242938
글자typographytext-t313px / 18px단어 단위 줄바꿈(v114) · 줄바꿈 문자는 그대로
굵기fontWeight700
descriptionenabled간격gapspacing-x0_52px제목 ↔ 설명
글자·아이콘foregroundfg-neutral-inverted#FFFFFF다크 #242938
글자typographytext-t313px / 18px
굵기fontWeight400
focusRingfocused링 두께width2px
링 색colorfg-neutral-inverted#FFFFFF다크 #242938말풍선 글자색 — 브랜드 링(stroke-focus-ring)은 짙은 말풍선 위에서 Desk 1.96 · HR 2.01 로 3:1 에 못 미친다
링 간격offset-2px닫기 버튼 안쪽
말풍선 링 간격rootOffset2px닫기 버튼이 없는 말풍선에 Tab 으로 들어오면 말풍선 둘레 바깥 2(모서리를 따라)
말풍선 링 색rootColorstroke-focus-ringDesk#0147AD다크 #7AA9F6HR#075B3C다크 #2ABE84페이지 위에 그려지므로 브랜드 링

opens=hover

부위상태속성값
rootenabled열림 지연openDelay200ms마우스 — 키보드 초점은 바로(0). 하나가 열린 뒤 옆 트리거로 옮기면 바로 · 모션 없이
닫힘 지연closeDelay100ms트리거와 말풍선을 모두 벗어난 뒤 — 말풍선 위로 옮겨도 닫히지 않는다(WCAG 1.4.13)
이어 열기 시간skipDelay300ms하나가 닫힌 뒤 이 시간 안에 옆 트리거로 옮기면 기다리지 않고 모션 없이 바로 연다

opens=press, closeButton=shown

부위상태속성값
closeButtonenabled크기size38px투명 상자 — 말풍선 오른쪽 위 모서리에 붙는다(위 0 · 오른쪽 0). 글과 이 상자 사이 4(SEED 와 같다)
아이콘 크기iconSize14pxlucide x — 위 12 · 오른쪽 12 자리
모서리radiusradius-r312px바탕이 없어 초점 링에만 보인다(SEED 와 같다)
색colorfg-neutral-inverted#FFFFFF다크 #242938
터치 영역touchTarget44 × 44사방 3 넓힌다(SEED 38)
closeButtonpressed배율scale2px 거리 축소바탕 없이 아이콘만 — 모션 줄이기면 하지 않는다
축소 시간scaleDurationmotion-duration-pressed-scale150ms
축소 곡선scaleEasingmotion-ease-pressed-scalecubic-bezier(0, 0, 0.15, 1)

SEED 와 다른 점

  • 말풍선 위로 포인터를 옮겨도 닫히지 않는다(WCAG 1.4.13) — SEED 는 기본으로 닫히고 keepOpenOnContentHover 를 켜야 남는다.
  • 바탕은 groo bg-neutral-inverted — Help Bubble 과 같다.
  • z-index 는 specs/z-index.md 의 L4(z-tooltip 210) — SEED 는 포털 없이 99.

Migration notes

2026-10-16 — 머리의 예시를 알림 · 설정으로

데스크톱 머리에서 금액 가리기 눈을 뺐다(2026-10-16 사용자 결정 — 구현 세션 질문 Q-017-3c = Q-018-3 B, Top Navigation). 툴팁 예시 · 그림의 트리거를 머리에 남은 알림 · 설정으로 바꿨다. 규칙은 그대로다.

2026-10-15 — 수치 파일 하나 · 앱에도 둔다

구현 세션 질문 두 가지를 있는 규칙으로 정리했다.

  • tooltip.json 을 따로 만들지 않는다(Q-009-1) — 원본은 help-bubble.yaml 하나다(SEED 도 하나). 따로 두면 같은 말풍선을 두 곳에 정의하게 된다. 웹 카탈로그 · 검사기는 툴팁을 help-bubble 의 opens: hover 로 잡는다.
  • 앱에도 둔다(Q-009-2) — 2026-10-02 8A(마우스 · 키보드에서만, 터치에 기대지 않음)와 2026-10-08 "앱도 하드웨어 키보드로 옮길 때 누를 수 있는 것은 모두 초점을 받는다" 가 답한다. 손가락 사용자에게는 차이가 없고(길게 누르기 툴팁은 그때 고르지 않았다), 키보드 · 마우스를 붙인 사용자에게는 이름이 보인다. 옛 길게 누르기 툴팁은 옮길 때 걷는다.

2026-10-02 — SEED Help Bubble Tooltip 으로 바꾼다

사용자가 비교 페이지에서 정했다 — 툴팁 모양은 SEED Help Bubble(짙은 바탕 · 13 · 모서리 12 · 화살표 · 최대 280, 옛 반전 13 · 모서리 2 · 화살표 없음 · 그림자에서), 쓰임은 SEED(마우스 200 / 100ms · 키보드 바로 · 터치에 기대지 않음 · 이름은 aria-label · 막힌 이유는 가까운 글 · title 걷음). 옛 스펙은 tooltip.history/v-pre-seed-tooltip.* — 수치 파일(tooltip.yaml)은 help-bubble.yaml 로 합쳤다.

제품은 앱 적용 단계에서 옮긴다(2026-10-02 조사).

  • Desk 웹 — 툴팁은 접힌 사이드바 하나(지연 0ms, 태블릿 터치로 탭하면 안 뜬다). 네이티브 title 29곳 — 아이콘 버튼의 유일한 이름 7곳, 막힌 버튼의 이유 2곳(예산 "복사할 지난달 예산이 없어요"), 키보드로 못 닿는 설명 4곳. 데스크톱 줄의 아이콘 6개는 이름도 툴팁도 없다.
  • Desk 앱 — PTooltip 17(반전 13 · 모서리 2) · Material 기본 툴팁 9(회색 14 · 모서리 4), 길게 누르면 뜬다. 아이콘 버튼 37 중 20 이 이름이 없다.
  • HR 웹 — 툴팁은 브랜드 파랑 12 · 모서리 6 · 화살표(바탕 대비 4.95 · 4.73), 지연 늘 0(Provider 가 겹친다). 네이티브 title 11곳.
  • 차트 · 데이터 툴팁(웹 7 · 앱 8 · HR 2)은 이 스펙이 아니다 — groo 차트 부품으로 따로 정하고 말풍선 모양만 맞춘다.

이 페이지