CSS 툴팁 말풍선 9종 — 한 줄 복붙으로
CSS 툴팁 말풍선은 버튼이나 아이콘에 커서를 올리거나 포커스를 두었을 때 짧은 설명을 그 옆에 띄우는 작은 말풍선으로, 이 글은 자바스크립트 없이 대부분 순수 CSS로 돌아가는 아홉 가지 방식을 모았습니다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
- 01 기본 상단 표시
- 02 페이드+확대 팝
- 03 공간 없으면 자동 뒤집힘
- 04 카드형 미리보기 툴팁
- 05 꼬리 화살표
- 06 지연 표시
- 07 키보드 포커스 시 표시
- 08 카톡 스타일 말풍선
- 09 도움말 펄스 점
아홉 개는 인기 순위가 아니라 툴팁이 화면에서 맞닥뜨리는 순서로 놓았습니다. 가장 단순한 등장 방식(01~02) 다음은 위치가 까다로워지는 경우(03~05), 그다음 나타나는 타이밍을 다르게 다루는 경우(06~07), 마지막은 툴팁이라는 틀을 살짝 벗어난 응용(08~09)입니다. 각 섹션에는 핵심 코드 조각만 남겼고, 파일 전체는 아래 zip 하나에 아홉 개 모두 들어 있습니다.
01기본 상단 표시
버튼 위 8px 지점에 말풍선을 두고 opacity와 위아래 이동만으로 나타나고 사라지게 하면 가장 기본적인 툴팁이 됩니다. 도움말 아이콘 옆이나 표 헤더의 줄임말을 풀어 설명하는 자리에 씁니다.
.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페이드+확대 팝
작게 줄어든 상태에서 시작해 튀어 오르듯 원래 크기로 커지면 딱딱한 정보 창이 아니라 방금 생긴 것 같은 인상을 줍니다. 신규 기능을 알리는 배지나 눈에 띄어야 하는 툴바 아이콘에 어울립니다.
.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을 걸어 두면, 화면 위쪽에 자리가 없을 때 브라우저가 좌표 계산 없이 알아서 아래로 뒤집습니다. 화면 하단 고정 바 근처 버튼이나 폭이 좁은 사이드바 안 아이콘처럼 위치가 매번 달라지는 자리에 씁니다.
.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에 상관없이 항상 다른 내용 위에 그려집니다. 상품 목록의 호버 미리보기나 프로필 이름 위에 뜨는 요약 카드처럼 이미지와 설명이 함께 필요한 자리에 씁니다.
.card[popover] {
position: absolute;
top: calc(100% + 12px);
left: 50%;
}
.card:popover-open {
position: fixed;
top: 150px;
left: 50%;
}
05꼬리 화살표
말풍선 아래 clip-path로 자른 작은 삼각형 하나를 더하면 어떤 요소를 가리키는지 눈으로 바로 알 수 있습니다. 폼 입력칸 옆의 형식 안내나 차트의 특정 데이터 포인트를 짚어 설명할 때 씁니다.
.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%를 그대로 투명하게 비워 두면 마우스가 스치듯 지나갈 때는 뜨지 않고, 잠깐 머문 경우에만 말풍선이 나타납니다. 아이콘이 촘촘히 붙어 있는 툴바나 목록의 각 행마다 반복되는 아이콘처럼 실수로 자꾸 뜨면 오히려 방해되는 자리에 씁니다.
@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 키로 이동하든 같은 말풍선이 뜹니다. 마우스를 쓰지 않는 사람을 위한 폼 라벨 설명이나 접근성을 지켜야 하는 아이콘 버튼에 필수입니다.
.tooltip {
opacity: 0;
transition: opacity $duration $easing;
}
.btn:hover + .tooltip,
.btn:focus-visible + .tooltip {
opacity: 1;
}
08카톡 스타일 말풍선
모서리 하나만 각지게 두고 clip-path로 만든 꼬리를 붙이면 정보 창이 아니라 말을 거는 듯한 채팅 말풍선이 됩니다. 챗봇의 첫 인사말이나 온보딩 가이드의 설명 문구를 띄울 때 씁니다.
.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도움말 펄스 점
작은 점 뒤에 흐린 후광을 겹치고 그 후광만 커졌다 옅어지길 반복하면, 말풍선이 뜨기 전부터 눌러볼 만한 정보가 있다는 걸 은은하게 알립니다. 새로 생긴 기능의 배지나 설정 화면 구석의 도움말 아이콘에 씁니다.
.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()를 호출하는 한 줄에만 쓰였습니다.
말풍선 색은 브랜드 컬러로 바꿔도 됩니까?
가능합니다. $color 변수 하나만 바꾸면 아홉 개 전부 한 번에 브랜드 컬러를 입힐 수 있습니다. 다만 05 꼬리 화살표처럼 얇은 삼각형이 들어간 형태는 배경과 대비가 약해지면 꼬리 윤곽이 묻히니 대비를 먼저 확인하십시오.
CSS Anchor Positioning은 어느 브라우저에서 동작합니까?
이 글을 쓰는 시점을 기준으로 Chrome과 Edge 125 이상에서 동작하고 Safari와 Firefox는 아직입니다. 실제 프로젝트에 쓰기 전에 caniuse.com에서 최신 지원표를 확인하고, 03 공간 없으면 자동 뒤집힘처럼 @supports로 감싼 폴백을 같이 넣으십시오.