GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

CSS 툴팁 말풍선 9종 — 한 줄 복붙으로

CSS 툴팁 말풍선은 버튼이나 아이콘에 커서를 올리거나 포커스를 두었을 때 짧은 설명을 그 옆에 띄우는 작은 말풍선으로, 이 글은 자바스크립트 없이 대부분 순수 CSS로 돌아가는 아홉 가지 방식을 모았습니다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 인기 순위가 아니라 툴팁이 화면에서 맞닥뜨리는 순서로 놓았습니다. 가장 단순한 등장 방식(01~02) 다음은 위치가 까다로워지는 경우(03~05), 그다음 나타나는 타이밍을 다르게 다루는 경우(06~07), 마지막은 툴팁이라는 틀을 살짝 벗어난 응용(08~09)입니다. 각 섹션에는 핵심 코드 조각만 남겼고, 파일 전체는 아래 zip 하나에 아홉 개 모두 들어 있습니다. 이보다 넓은 뜻의 클릭 응답을 모아 보면 바이브코딩 마이크로 인터랙션 9종이 있고, 포인터가 어디에 있는지 자체를 효과로 만드는 쪽은 CSS 마우스 커서 효과 9종입니다.

01기본 상단 표시

어떻게 움직이나

버튼 위 8px 지점에 말풍선을 두고 opacity와 위아래 이동만으로 나타나고 사라지게 하면 가장 기본적인 툴팁이 됩니다. 이번 개편에서는 뜨는 순간 스케일이 1.12까지 부풀었다가 화살표 축(아래변 중심)을 기준으로 2.6도→-1.5도→0.6도로 잦아드는 감쇠 회전 흔들림 한 겹을 더했습니다.

어디에 쓰나

도움말 아이콘 옆이나 표 헤더의 줄임말을 풀어 설명하는 자리에 씁니다.

기본 상단 표시
새 창에서 열기

01 · 기본 상단 표시 — 측정 누적면적 4.959% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

상단 8px 간격opacity + translate 4px200ms
.tooltip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translate(-50%, 4px);
  opacity: 0;
}
.tooltip.is-demo {
  animation: tip-loop $duration $easing infinite;
}
@keyframes tip-loop {
  0%, 100% { opacity: 0; transform: translate(-50%, 4px); }
  20%, 55% { opacity: 1; transform: translate(-50%, 0); }
  80%      { opacity: 0; transform: translate(-50%, 4px); }
}

02페이드+확대 팝

어떻게 움직이나

작게 줄어든 상태에서 시작해 튀어 오르듯 원래 크기로 커지면 딱딱한 정보 창이 아니라 방금 생긴 것 같은 인상을 줍니다. 스케일이 1.14까지 오버슛한 뒤 화살표 축(윗변 중심)을 기준으로 -2.4도→1.4도→-0.5도로 잦아드는 감쇠 회전이 한 번 더 얹혔습니다.

어디에 쓰나

신규 기능을 알리는 배지나 눈에 띄어야 하는 툴바 아이콘에 어울립니다.

페이드+확대 팝
새 창에서 열기

02 · 페이드+확대 팝 — 측정 누적면적 5.799% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scale(.9)→1cubic-bezier 팝180ms
.tooltip {
  transform-origin: top center;
  transform: translateX(-50%) scale(.85);
  opacity: 0;
}
.tooltip.is-demo {
  animation: pop-loop $duration $easing infinite;
}
@keyframes pop-loop {
  0%, 100% { opacity: 0; transform: translateX(-50%) scale(.85); }
  25%, 55% { opacity: 1; transform: translateX(-50%) scale(1); }
  80%      { opacity: 0; transform: translateX(-50%) scale(.85); }
}

03공간 없으면 자동 뒤집힘

어떻게 움직이나

트리거에 anchor-name을 붙이고 툴팁에 position-anchor와 position-try-fallbacks: flip-block을 걸어 두면, 화면 위쪽에 자리가 없을 때 브라우저가 좌표 계산 없이 알아서 아래로 뒤집습니다. 폴백 배치와 앵커 배치 두 축 모두에 스케일 1.1 오버슛과 2.2도→-1.3도→0.5도 감쇠 회전 흔들림을 같은 모양으로 얹었습니다.

어디에 쓰나

화면 하단 고정 바 근처 버튼이나 폭이 좁은 사이드바 안 아이콘처럼 위치가 매번 달라지는 자리에 씁니다.

공간 없으면 자동 뒤집힘
새 창에서 열기

03 · 공간 없으면 자동 뒤집힘 — 측정 누적면적 6.054% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

anchor-nameposition-try-fallbacks자동 뒤집힘
.trigger { anchor-name: --tip03; }
@supports (position-anchor: --tip03) {
  .tooltip {
    position: fixed;
    position-anchor: --tip03;
    position-area: top;
    position-try-fallbacks: flip-block;
  }
}

04카드형 미리보기 툴팁

어떻게 움직이나

popover="manual" 속성을 준 카드를 스크립트 한 줄로 showPopover()를 호출해 두면, 부모 요소의 overflow나 z-index에 상관없이 항상 다른 내용 위에 그려집니다. 카드가 뜰 때도 스케일 1.08 오버슛과 윗변 축 기준 1.8도→-1도→0.4도 감쇠 회전 흔들림이 한 겹 더해집니다.

어디에 쓰나

상품 목록의 호버 미리보기나 프로필 이름 위에 뜨는 요약 카드처럼 이미지와 설명이 함께 필요한 자리에 씁니다.

카드형 미리보기 툴팁
새 창에서 열기

04 · 카드형 미리보기 툴팁 — 측정 누적면적 23.918% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

popover=manualtop layer280px 카드
.card[popover] {
  position: absolute;
  top: calc(100% + 12px);
  left: 50%;
}
.card:popover-open {
  position: fixed;
  top: 150px;
  left: 50%;
}

05꼬리 화살표

어떻게 움직이나

말풍선 아래 clip-path로 자른 작은 삼각형 하나를 더하면 어떤 요소를 가리키는지 눈으로 바로 알 수 있습니다. 등장 스프링도 이 화살표 축을 회전 중심으로 삼아 2.6도→-1.5도→0.6도로 잦아드는 감쇠 흔들림을 더합니다.

어디에 쓰나

폼 입력칸 옆의 형식 안내나 차트의 특정 데이터 포인트를 짚어 설명할 때 씁니다.

꼬리 화살표
새 창에서 열기

05 · 꼬리 화살표 — 측정 누적면적 4.799% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

clip-path 삼각형4px 간격opacity
.tooltip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  width: 14px;
  height: 8px;
  clip-path: polygon(50% 100%, 0 0, 100% 0);
}

06지연 표시

어떻게 움직이나

키프레임의 앞 20%를 그대로 투명하게 비워 두면 마우스가 스치듯 지나갈 때는 뜨지 않고, 잠깐 머문 경우에만 말풍선이 나타납니다. 지연 뒤 실제로 나타나는 순간에는 스케일 1.12 오버슛과 아래변 축 기준 2.4도→-1.4도→0.5도 감쇠 회전 흔들림이 겹칩니다.

어디에 쓰나

아이콘이 촘촘히 붙어 있는 툴바나 목록의 각 행마다 반복되는 아이콘처럼 실수로 자꾸 뜨면 오히려 방해되는 자리에 씁니다.

지연 표시
새 창에서 열기

06 · 지연 표시 — 측정 누적면적 6.83% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes delay-loop짧은 등장긴 유지
@keyframes delay-loop {
  0%, 20%  { opacity: 0; }
  40%, 80% { opacity: 1; }
  100%     { opacity: 0; }
}
.tooltip.is-demo {
  animation: delay-loop $duration $easing infinite;
}

07키보드 포커스 시 표시

마우스 호버 효과만으로 충분한가

:hover와 :focus-visible을 같은 규칙에 나란히 걸어 두면 마우스로 올리든 Tab 키로 이동하든 같은 말풍선이 뜹니다. 뜨는 순간에는 스케일 1.12 오버슛과 아래변 축 기준 2.6도→-1.5도→0.6도 감쇠 회전 흔들림 한 겹이 한 번만 재생됩니다.

어디에 쓰나

마우스를 쓰지 않는 사람을 위한 폼 라벨 설명이나 접근성을 지켜야 하는 아이콘 버튼에 필수입니다.

키보드 포커스 시 표시
새 창에서 열기

07 · 키보드 포커스 시 표시 — 측정 누적면적 5.953% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

:focus-visible:hover 동시 대응aria-describedby
.tooltip {
  opacity: 0;
  transition: opacity $duration $easing;
}
.btn:hover + .tooltip,
.btn:focus-visible + .tooltip {
  opacity: 1;
}

08카톡 스타일 말풍선

어떻게 움직이나

모서리 하나만 각지게 두고 clip-path로 만든 꼬리를 붙이면 정보 창이 아니라 말을 거는 듯한 채팅 말풍선이 됩니다. 스케일 1.14 오버슛 뒤 꼬리 축(왼쪽 아래)을 중심으로 3도→-1.7도→0.7도로 잦아드는 감쇠 회전 흔들림이 더해졌습니다.

어디에 쓰나

챗봇의 첫 인사말이나 온보딩 가이드의 설명 문구를 띄울 때 씁니다.

카톡 스타일 말풍선
새 창에서 열기

08 · 카톡 스타일 말풍선 — 측정 누적면적 9.751% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

border-radius 18px꼬리 pseudo등장 바운스
.bubble {
  border-radius: 18px 18px 18px 4px;
  transform-origin: bottom left;
  transform: scale(.85);
  opacity: 0;
}
.bubble.is-demo {
  animation: bubble-loop $duration $easing infinite;
}
@keyframes bubble-loop {
  0%, 100% { opacity: 0; transform: scale(.85); }
  25%, 55% { opacity: 1; transform: scale(1); }
  80%      { opacity: 0; transform: scale(.85); }
}

09도움말 펄스 점

어떻게 움직이나

작은 점 뒤에 흐린 후광을 겹치고 그 후광만 커졌다 옅어지길 반복하면, 말풍선이 뜨기 전부터 눌러볼 만한 정보가 있다는 걸 은은하게 알립니다. 말풍선이 실제로 뜰 때는 스케일 1.12 오버슛과 아래변 축 기준 2.6도→-1.5도→0.6도 감쇠 회전 흔들림이 함께 재생됩니다.

어디에 쓰나

새로 생긴 기능의 배지나 설정 화면 구석의 도움말 아이콘에 씁니다.

도움말 펄스 점
새 창에서 열기

09 · 도움말 펄스 점 — 측정 누적면적 7.869% · 움직인 프레임 20/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes pulse-ring2초 주기opacity 감쇠
.dot::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  animation: pulse-ring $duration $easing infinite;
}
@keyframes pulse-ring {
  0%, 100% { transform: scale(.6); opacity: .5; }
  50%      { transform: scale(1.6); opacity: 0; }
}

어디서 깨지는가 — 함정 하나

처음 잡은 위치

아홉 개 중 04 카드형 미리보기 툴팁에서 시간을 가장 많이 썼습니다. popover 속성을 준 요소는 열리는 순간 최상위 레이어로 옮겨지는데, 처음엔 position: absolute; top: calc(100% + 12px)를 그대로 두면 트리거 바로 아래에 붙을 거라 생각했습니다.

왜 화면에서 사라졌나

실제로 렌더링해 보니 카드가 무대 밖으로 완전히 밀려나 화면에 아예 잡히지 않았고, 원인은 최상위 레이어로 옮겨진 요소의 퍼센트 기준 top이 더 이상 부모가 아니라 뷰포트를 기준으로 계산됐기 때문입니다.

어떻게 고쳤나

:popover-open 상태에만 걸리는 규칙으로 위치를 무대 높이 기준의 고정값으로 다시 잡고 나서야 카드가 제자리에 보였습니다. 최상위 레이어로 올라간 뒤에도 자리가 어긋나지 않는지까지 하나하나 직접 렌더링해 눈으로 맞춰 본 결과물이라, 압축 파일을 여는 비밀번호 uauwf4hn 도 이 문장 안에 바로 적어 둡니다 — 아래 zip 버튼을 누르면 지금 바로 열립니다. Popover API의 렌더링 구조는 MDN Popover API 문서에서, 최상위 레이어 자체의 정의는 MDN 용어집에서 확인할 수 있습니다. 말풍선 뒤에 깔리는 배경까지 도드라지게 만들고 싶다면 그레인 얹은 오로라 그라디언트 랜딩의 색 층이 이 틀 뒤편을 채웁니다.

접근성

동작 줄이기 설정에서

기기의 동작 줄이기 설정을 켜 둔 사람에게는 아홉 개 말풍선 전부 prefers-reduced-motion: reduce 블록이 따로 응답합니다. 등장·펄스 같은 반복 동작만 멈추고 내용 자체는 계속 보이는 상태로 남겨 뒀습니다.

키보드에서는 어떻게 되나

07 키보드 포커스 시 표시는 애니메이션과 별개로 :focus-visible 자체가 항상 살아 있어서, 동작을 줄인 환경에서도 Tab으로 이동하면 즉시 말풍선이 나타납니다. 이 미디어 쿼리가 어떤 값을 받고 각 운영체제 설정과 어떻게 연결되는지는 MDN 레퍼런스에 정리돼 있습니다.

@media (prefers-reduced-motion: reduce) {
  .tooltip, .bubble, .card, .dot::before {
    animation: none !important;
    opacity: 1;
  }
}

말풍선 말고 다른 css 호버 효과가 궁금하면 CSS 카테고리 목록을 열어 보고, 이 블로그를 왜 만들었는지는 소개 글에 적어 뒀습니다.

FAQ

CSS 툴팁 말풍선에 꼭 자바스크립트가 필요합니까?

아니요. 01 기본 상단 표시부터 08 카톡 스타일 말풍선까지 여덟 개는 순수 CSS만으로 만들었습니다. 자바스크립트는 04 카드형 미리보기 툴팁이 showPopover()를 호출하는 한 줄에만 쓰였습니다. 08 카톡 스타일을 대화가 계속 이어지는 형태로 키우면 채팅 말풍선 CSS 애니메이션 9종이 그 확장판입니다.

css 호버 효과로 뜨는 말풍선 색은 브랜드 컬러로 바꿔도 됩니까?

가능합니다. $color 변수 하나만 바꾸면 아홉 개 전부 한 번에 브랜드 컬러를 입힐 수 있습니다. 다만 05 꼬리 화살표처럼 얇은 삼각형이 들어간 형태는 배경과 대비가 약해지면 꼬리 윤곽이 묻히니 대비를 먼저 확인하십시오.

CSS Anchor Positioning은 어느 브라우저에서 동작합니까?

이 글을 쓰는 시점을 기준으로 Chrome과 Edge 125 이상에서 동작하고 Safari와 Firefox는 아직입니다. 실제 프로젝트에 쓰기 전에 caniuse.com에서 최신 지원표를 확인하고, 03 공간 없으면 자동 뒤집힘처럼 @supports로 감싼 폴백을 같이 넣으십시오.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.