Generator tooltipów CSS
Twórz czysty kod CSS tooltipów: hover, focus, click. Wybierz pozycję (top/bottom/left/right), kolor, animację i skopiuj gotowy kod. Zero JavaScript, działa w każdej przeglądarce.
-
1Wprowadź dane
Wpisz treść, wklej tekst lub załaduj plik z dysku. -
2Kliknij przycisk
Narzędzie natychmiast przetworzy Twoje dane w przeglądarce. -
3Pobierz wynik
Skopiuj gotowy tekst lub zapisz plik na urządzeniu.
return "Wynik gotowy w 0.1s";
}
Rozmiar kodu CSS
dokładnie ten arkusz zapisuje „Pobierz .css”
Pozycja dymku
atrybut data-pos: top
Motyw
data-theme w HTML wybiera tło i kolor tekstu
Strzałka
romb 10 × 10 px obrócony o 45° (::before)
Maks. szerokość dymku
dłuższy tekst zawija się, nie rozpycha dymku
Przesunięcie
odstęp dymku od elementu; strzałka siedzi max(2, offset − 4) px za dymkiem
Długość tekstu dymku
treść trafia do atrybutu data-tooltip
/* Wynik generatora tooltipów CSS */
.ttg-trigger{position:relative;display:inline-flex;align-items:center;justify-content:center;cursor:help;line-height:1.2;gap:.4rem;}
.ttg-trigger:focus{outline:0;}
.ttg-trigger:focus-visible{outline:2px solid rgba(13,110,253,.5);outline-offset:3px;border-radius:6px;}
.ttg-trigger[data-ttg="1"]::after{content:attr(data-tooltip);position:absolute;max-width:280px;padding:10px 12px;border-radius:12px;font-size:14px;line-height:1.35;white-space:normal;opacity:0;pointer-events:none;transform:translate3d(0,0,0) scale(.98);transition:opacity .16s ease, transform .16s ease;z-index:10;box-shadow:0 12px 26px rgba(0,0,0,.18);}
.ttg-trigger[data-theme="dark"]::after{background:#111827;color:#F9FAFB;border:1px solid rgba(255,255,255,.08);}
.ttg-trigger[data-theme="light"]::after{background:#FFFFFF;color:#111827;border:1px solid rgba(17,24,39,.12);}
.ttg-trigger[data-ttg="1"]:hover::after,.ttg-trigger[data-ttg="1"]:focus::after,.ttg-trigger[data-ttg="1"]:focus-visible::after{opacity:1;transform:translate3d(0,0,0) scale(1);}
.ttg-trigger[data-pos="top"]::after{left:50%;bottom:calc(100% + 10px);transform:translateX(-50%) translateY(6px) scale(.98);}
.ttg-trigger[data-pos="top"]:hover::after,.ttg-trigger[data-pos="top"]:focus::after,.ttg-trigger[data-pos="top"]:focus-visible::after{transform:translateX(-50%) translateY(0) scale(1);}
.ttg-trigger[data-pos="bottom"]::after{left:50%;top:calc(100% + 10px);transform:translateX(-50%) translateY(-6px) scale(.98);}
.ttg-trigger[data-pos="bottom"]:hover::after,.ttg-trigger[data-pos="bottom"]:focus::after,.ttg-trigger[data-pos="bottom"]:focus-visible::after{transform:translateX(-50%) translateY(0) scale(1);}
.ttg-trigger[data-pos="right"]::after{left:calc(100% + 10px);top:50%;transform:translateX(-6px) translateY(-50%) scale(.98);}
.ttg-trigger[data-pos="right"]:hover::after,.ttg-trigger[data-pos="right"]:focus::after,.ttg-trigger[data-pos="right"]:focus-visible::after{transform:translateX(0) translateY(-50%) scale(1);}
.ttg-trigger[data-pos="left"]::after{right:calc(100% + 10px);top:50%;transform:translateX(6px) translateY(-50%) scale(.98);}
.ttg-trigger[data-pos="left"]:hover::after,.ttg-trigger[data-pos="left"]:focus::after,.ttg-trigger[data-pos="left"]:focus-visible::after{transform:translateX(0) translateY(-50%) scale(1);}
.ttg-trigger[data-ttg="1"][data-arrow="1"]::before{content:"";position:absolute;width:10px;height:10px;opacity:0;pointer-events:none;transform:translate3d(0,0,0) rotate(45deg);transition:opacity .16s ease, transform .16s ease;z-index:9;}
.ttg-trigger[data-ttg="1"][data-arrow="1"]:hover::before,.ttg-trigger[data-ttg="1"][data-arrow="1"]:focus::before,.ttg-trigger[data-ttg="1"][data-arrow="1"]:focus-visible::before{opacity:1;}
.ttg-trigger[data-theme="dark"][data-arrow="1"]::before{background:#111827;border:1px solid rgba(255,255,255,.08);}
.ttg-trigger[data-theme="light"][data-arrow="1"]::before{background:#FFFFFF;border:1px solid rgba(17,24,39,.12);}
.ttg-trigger[data-pos="top"][data-arrow="1"]::before{left:50%;bottom:calc(100% + 6px);transform:translateX(-50%) rotate(45deg);}
.ttg-trigger[data-pos="bottom"][data-arrow="1"]::before{left:50%;top:calc(100% + 6px);transform:translateX(-50%) rotate(45deg);}
.ttg-trigger[data-pos="right"][data-arrow="1"]::before{left:calc(100% + 6px);top:50%;transform:translateY(-50%) rotate(45deg);}
.ttg-trigger[data-pos="left"][data-arrow="1"]::before{right:calc(100% + 6px);top:50%;transform:translateY(-50%) rotate(45deg);}
@media (prefers-reduced-motion: reduce){.ttg-trigger[data-ttg="1"]::after{transition:none;}.ttg-trigger[data-ttg="1"][data-arrow="1"]::before{transition:none;}}
<!-- HTML -->
<span class="ttg-trigger" tabindex="0" data-ttg="1" data-tooltip="Skopiuj CSS i HTML, a następnie użyj tego tooltipa w dowolnym miejscu." data-pos="top" data-theme="dark" data-arrow="1">Najedź lub kliknij mnie</span>
Dymek to pseudoelement ::after z treścią czytaną funkcją attr(data-tooltip) — tekst siedzi w atrybucie HTML, więc CSS nie trzeba edytować przy zmianie treści. Wyświetlanie sterują trzy stany: :hover, :focus i :focus-visible.
| Fragment CSS | Dokładny zapis z silnika | Rola |
|---|---|---|
| .ttg-trigger | position: relative; display: inline-flex; cursor: help; |
element wyzwalający — punkt odniesienia dla absolutnego dymku |
| ::after — treść | content: attr(data-tooltip); |
tekst dymku brany z atrybutu data-tooltip elementu |
| ::after — wymiary | position: absolute; max-width: {W}px; padding: 10px 12px; border-radius: 12px; |
szerokość maksymalna z formularza; dłuższy tekst zawija się |
| ::after — niewidoczny domyślnie | opacity: 0; pointer-events: none; z-index: 10; box-shadow: 0 12px 26px rgba(0,0,0,.18); |
dymek nie łapie kliknięć i jest niewidoczny do czasu najechania lub skupienia klawiaturą |
| ::after — pokazanie | :hover::after, :focus::after, :focus-visible::after → opacity: 1; transform: … scale(1); |
działa myszką i z klawiatury; płynne przejście .16s ease |
| motyw dark | background: #111827; color: #F9FAFB; border: 1px solid rgba(255,255,255,.08); |
ciemny dymek (data-theme="dark") |
| motyw light | background: #FFFFFF; color: #111827; border: 1px solid rgba(17,24,39,.12); |
jasny dymek (data-theme="light") |
| strzałka ::before (data-arrow="1") | content: ""; width: 10px; height: 10px; transform: … rotate(45deg); |
romb w kolorze motywu, wystający max(2, offset − 4) px za dymkiem |
| prefers-reduced-motion | transition: none; (dla ::after i ::before) |
bez animacji, gdy system żąda ograniczenia ruchu |
| Pozycja (data-pos) | Rozmieszczenie ::after (dokładnie z silnika) | Strzałka ::before |
|---|---|---|
| top | left: 50%; bottom: calc(100% + {O}px); transform: translateX(-50%) translateY(6px) scale(.98); — w stanie spoczynku 6 px dodatkowego zejścia, po :hover transform wraca do translateY(0) / translateX(0) i scale(1) |
left: 50%; bottom: calc(100% + max(2, O − 4)px) |
| bottom | left: 50%; top: calc(100% + {O}px); transform: translateX(-50%) translateY(-6px) scale(.98); — w stanie spoczynku 6 px dodatkowego zejścia, po :hover transform wraca do translateY(0) / translateX(0) i scale(1) |
left: 50%; top: calc(100% + max(2, O − 4)px) |
| right | left: calc(100% + {O}px); top: 50%; transform: translateX(-6px) translateY(-50%) scale(.98); — w stanie spoczynku 6 px dodatkowego zejścia, po :hover transform wraca do translateY(0) / translateX(0) i scale(1) |
left: calc(100% + max(2, O − 4)px); top: 50% |
| left | right: calc(100% + {O}px); top: 50%; transform: translateX(6px) translateY(-50%) scale(.98); — w stanie spoczynku 6 px dodatkowego zejścia, po :hover transform wraca do translateY(0) / translateX(0) i scale(1) |
right: calc(100% + max(2, O − 4)px); top: 50% |
Litera O to pole „Przesunięcie” z formularza (6–24 px). Wartość max(2, O − 4) przy strzałce sprawia, że romb zawsze lekko zachodzi pod dymek, niezależnie od ustawionego odstępu.
Tooltip a dostępność — co warto wiedzieć
Treść z ::after nie trafia do czytników
Pseudoelementy nie istnieją w drzewie dostępności, więc treść dymku warto zduplikować dla technologii wspomagających: dodaj do wyzwalacza aria-describedby wskazujące na ukryty <span role="tooltip"> z tą samą treścią co data-tooltip.
Klawiatura i dotyk
Wygenerowany CSS pokazuje dymek także na :focus/:focus-visible — element ma tabindex="0", więc działa z Tabem. Na ekranie dotykowym nie ma hover: zapewnij alternatywę (klik togglujący klasę albo <details>), bo inaczej treść dymku jest nieosiągalna.
Nie chowaj w dymku czegoś ważnego
Tooltip znika po zejściu kursora, więc ceny, błędy walidacji czy instrukcje krytyczne muszą być też w zwykłej treści. Na plus: silnik respektuje prefers-reduced-motion i daje widoczny obrys :focus-visible, a cursor: help sygnalizuje, że podpowiedź istnieje.
Wskazówka: kafelki nad wynikiem pokazują na żywo pozycję, motyw, wymiary i rozmiar kodu, a „Pobierz .css” zapisuje sam arkusz — HTML wyzwalacza podejmujesz z bloku wyniku albo z podglądu.
Oceń to narzędzie:
Powiązane narzędzia
Inne narzędzia, które mogą Ci się przydaćGenerator tooltipów CSS – dymki podpowiedzi bez JavaScript
Tooltips (dymki podpowiedzi) informują użytkownika o funkcji elementu bez zajmowania miejsca w interfejsie. Narzędzie generuje czysty, gotowy do wklejenia kod CSS tooltipów – wybierasz pozycję, kolor, animację wejścia i styl obramowania. Bez JavaScript, bez zewnętrznych bibliotek, kompatybilne z każdą przeglądarką obsługującą CSS3.
Pozycje i kierunki tooltipów
Tooltip można umieścić w czterech kierunkach: top (nad elementem), bottom (pod), left (po lewej), right (po prawej). Zaawansowane warianty: top-left, top-right (tooltip zakotwiczony narożnikowo). Strzałka kierunkowa (::before) automatycznie obraca się do właściwego kierunku za pomocą border-trick.
Animacje wejścia i wyjścia
Fade-in (opacity: 0→1 + transition): subtelne pojawienie się. Slide (translateY/translateX): tooltip wysuwa się z kierunku wskazania. Scale (transform: scale(0.8)→1): efekt "pop". Opóźnienie (transition-delay): prevents flickering przy szybkim ruchu myszy nad elementem.
Dostępność i atrybuty ARIA
Tooltip CSS nie zastępuje natywnego atrybutu title – uzupełnia go. Dla dostępności: rola role="tooltip" na elemencie dymka, aria-describedby łączące trigger z tooltipem, widoczność przy focus (nie tylko hover) dla użytkowników klawiatury. Kontrast tła/tekstu: minimum 4.5:1 (WCAG AA).
Personalizacja stylów
Kolor tła: dowolny HEX/RGB/CSS variable. Kolor tekstu: kontrastowy. Zaokrąglenie rogów (border-radius): 4px (prostokąt), 8px (zaokrąglony), 50px (pigułka). Cień (box-shadow): głębokość i kolor. Maksymalna szerokość (max-width): 200-300px z word-break. Font-size: 12-14px dla czytelności.
Jak działa tooltip zbudowany wyłącznie z CSS
Podstawą jest para elementów: trigger i dymek. Trigger to zwykły element w treści strony (span, button, link), któremu nadajemy position: relative — dzięki temu staje się punktem odniesienia dla potomka pozycjonowanego absolutnie. Dymkiem nie jest osobny element HTML, lecz pseudo-element ::after ustawiony na position: absolute. Jego treść pochodzi z atrybutu zapisanego przy triggerze: content: attr(data-tooltip). Tekst siedzi więc w HTML, a CSS tylko go wyświetla — jedna reguła obsługuje dowolną liczbę dymków w dokumencie.
Drugi pseudo-element, ::before, rysuje strzałkę. To element o zerowych wymiarach, zbudowany z czterech przezroczystych ramek, z których tylko jedna ma kolor tła dymka. Zmiana koloru konkretnej ramki decyduje o tym, w którą stronę strzałka wskazuje, dlatego nie potrzeba obrotów ani obrazków. Widoczność dymka sterowana jest stanem triggera: w spoczynku opacity: 0 i visibility: hidden, a przy :hover oraz :focus-within opacity wraca do 1, a visibility do visible. Dodanie visibility jest istotne, bo sam opacity: 0 pozostawia dymek w drzewie dostępności i pozwala mu przechwytywać kliknięcia.
Kierunek to kwestia zestawu właściwości pozycjonujących i jednego transform. Dla wyśrodkowania używa się left: 50% plus translateX(-50%) — bez tego dymek zaczynałby się w połowie szerokości triggera, a nie był nad jego środkiem. Animacja wejścia to przejście na opacity i transform; ponieważ oba korzystają z tego samego transformu, wartość wyśrodkowująca i efekt skali muszą znaleźć się w jednej deklaracji, inaczej jedno nadpisze drugie.
Przykład: dymek nad przyciskiem, krok po kroku
- Wybierz trigger i nadaj mu position: relative. Bez tego dymek pozycjonowałby się względem najbliższego pozycjonowanego przodka albo całego okna i „uciekał” przy zmianie układu strony.
- Wpisz treść w atrybut triggera, na przykład data-tooltip="Zapisuje zmiany lokalnie". Tekst zostaje w HTML, więc jest widoczny dla wyszukiwarek i czytników ekranu, a CSS tylko pobiera go przez content: attr(data-tooltip).
- Ustaw kierunek górny: dymek dostaje bottom: 100% (czyli zaczyna się dokładnie nad triggerem), left: 50% i transform: translateX(-50%). Dodaj odstęp — np. margin-bottom kilka pikseli — żeby dymek nie dotykał przycisku.
- Dorysuj strzałkę w ::before: zerowe wymiary, przezroczyste ramki i kolor tylko na ramce górnej, zgodny z tłem dymka. Pozycja: top: 100%, left: 50%, translateX(-50%). Strzałka trafia wtedy w środek dolnej krawędzi dymka.
- Nadaj wygląd: ciemne tło, biały tekst, padding, zaokrąglone rogi i cień. Ustaw white-space: normal oraz word-break, żeby dłuższy opis zawijał się w kilka linii zamiast rozciągać dymek w nieskończoność.
- Podłącz stan i animację: w spoczynku opacity: 0, visibility: hidden i transform: translateX(-50%) scale(0.8); przy :hover i :focus-within opacity: 1, visibility: visible i transform: translateX(-50%) scale(1). Przejście ustaw na opacity i transform, a następnie sprawdź dymek klawiszem Tab, nie tylko myszą.
Tabela referencyjna: pozycjonowanie dymka i strzałki
| Wariant | Dymek (::after) | Strzałka (::before) |
|---|---|---|
| top | bottom: 100%; left: 50%; translateX(-50%) | top: 100%; kolor na ramce górnej |
| bottom | top: 100%; left: 50%; translateX(-50%) | bottom: 100%; kolor na ramce dolnej |
| left | right: 100%; top: 50%; translateY(-50%) | left: 100%; kolor na ramce lewej |
| right | left: 100%; top: 50%; translateY(-50%) | right: 100%; kolor na ramce prawej |
| top-left | bottom: 100%; left: 0 | top: 100%; przesunięta od lewej krawędzi |
| top-right | bottom: 100%; right: 0 | top: 100%; przesunięta od prawej krawędzi |
| Animacja fade-in | przejście na opacity, bez zmian w pozycji | bez zmian, dziedziczy widoczność dymka |
| Animacja scale | scale wpisany w ten sam transform co wyśrodkowanie | bez zmian, skaluje się razem z dymkiem |
Praktyczne wskazówki przy wdrażaniu
- Trzymaj tekst w atrybucie, nie w CSS — content: attr(data-tooltip) pozwala zmienić opis bez dotykania arkusza stylów, a jeden zestaw reguł obsługuje wszystkie dymki na stronie.
- Rozdziel opóźnienie wejścia i wyjścia — dymek powinien znikać natychmiast, ale pojawiać się po chwili. Ustaw transition-delay tylko na stanie spoczynku dla znikania i osobno dla pojawiania, inaczej przy szybkim ruchu kursora miga.
- Nie zostawiaj przerwy między triggerem a dymkiem — gdy między nimi jest wolna przestrzeń, kursor ją opuszcza i hover znika. Odstęp rób przezroczystym pseudo-elementem albo paddingiem triggera, nie marginesem dymka.
- Skuś transform w jednej deklaracji — translateX(-50%) i scale() muszą wystąpić razem; osobne reguły sprawią, że animacja skasuje wyśrodkowanie i dymek przesunie się w bok.
- Testuj klawiaturą i czytnikiem — dodaj role="tooltip" i aria-describedby, a widoczność powiąż z :focus-within. Dymek, który pojawia się tylko przy hover, jest niedostępny dla części użytkowników.
Czego generator nie uwzględnia
- Elementy zastępowane — img, input czy br nie generują pseudo-elementów ::before i ::after, więc dymka nie da się do nich przypiąć bezpośrednio. Konieczny jest wrapper, np. span otaczający obrazek lub pole formularza.
- Krawędzie okna i kontenerów — CSS nie wykrywa, że dymek wychodzi poza viewport, i nie przesuwa go automatycznie. Podobnie przodek z overflow: hidden lub auto obetnie dymek, nawet jeśli pozycja jest poprawna.
- Treść tylko tekstowa — pseudo-element nie może zawierać elementów potomnych, więc w dymku nie umieścisz linku, przycisku ani listy, a jego tekstu nie da się zaznaczyć myszą. Dla bogatszej treści potrzebny jest prawdziwy element HTML.
- Kontekst warstw i transformacje przodków — przodek z transform, filter albo opacity tworzy nowy kontekst stackingu, przez co z-index dymka działa inaczej, niż się zakłada, a position: fixed wewnątrz takiego przodka pozycjonuje się względem niego, nie względem okna.
Najczęstsze pytania
Czy tooltip CSS działa na urządzeniach dotykowych?
Hover nie istnieje na touch – tooltip jest niewidoczny na smartfonach bez dodatkowej obsługi JS. Alternatywne podejście: użyj :focus-visible (po tapnięciu) lub dodaj małą ikonkę ⓘ jako trigger z aria-expanded. Na mobile lepiej sprawdza się bottom sheet lub popover.
Jak zrobić tooltip z HTML wewnątrz (rich tooltip)?
Czysty CSS nie pozwala na interaktywną treść w tooltipie (linki, przyciski). Do rich tooltipów użyj CSS Popover API (nowość 2024, natywny HTML), Floating UI (JS), lub Radix UI Tooltip (React). Prosty HTML (span wewnątrz ::after) jest możliwy przez attr() ale z ograniczeniami.
Jaka jest różnica między tooltip a popover?
Tooltip: krótka podpowiedź (1-2 słowa), pojawia się tylko przy hover/focus, nie jest interaktywny. Popover: większa pływająca warstwa, może zawierać przyciski, pola formularzy i linki, wymaga kliknięcia. CSS Popover API (Chrome 114+) obsługuje oba natywnie.
Jak uniknąć ucinania tooltipów na krawędzi ekranu?
Problem: tooltip pozycjonowany absolute może wychodzić poza viewport. Rozwiązania CSS: overflow: visible na rodzicu, position: fixed (ale traci kontekst scrollowania). Dla niezawodnej pozycji używaj Floating UI lub nowego CSS Anchor Positioning (2024).
Czy tooltip wpływa na SEO?
Treść wygenerowana przez CSS ::after/content nie jest indeksowana przez Google. Jeśli tooltip zawiera ważne informacje SEO – umieść je w HTML (np. w ukrytym span) lub użyj atrybutu title. Tooltips służą UX, nie są nośnikiem treści SEO.
Jak zrobić tooltip CSS dla wielu elementów bez powielania kodu?
Wystarczy jedna reguła oparta na atrybucie. Triggerom nadajesz position: relative i atrybut data-tooltip z treścią, a dymek generujesz raz w selektorze ::after z content: attr(data-tooltip). Każdy element z tym atrybutem dostaje własny, poprawnie wyśrodkowany dymek bez dodatkowego CSS.
Dlaczego tooltip CSS miga, gdy szybko przesuwam mysz?
Najczęściej winna jest przerwa między triggerem a dymkiem albo brak opóźnienia wejścia. Kursor opuszcza trigger na ułamek sekundy, hover znika i dymek się przełącza. Pomaga przezroczysty odstęp wypełniający lukę oraz transition-delay ustawiony tylko dla pojawiania się.
Czy tooltip CSS można zrobić na obrazku lub polu formularza?
Bezpośrednio nie, ponieważ img i input to elementy zastępowane i nie generują pseudo-elementów. Rozwiązaniem jest otoczenie ich elementem span z position: relative i przypięcie dymka do tego wrappera.
Jak pokazać tooltip po kliknięciu, bez JavaScriptu?
Zamiast hover użyj stanu :focus lub :focus-within, a triggerem zrób element focusowalny, np. button albo link. Dymek pokaże się po kliknięciu i pozostanie widoczny, dopóki fokus nie zostanie przeniesiony — ukrycie wymaga kliknięcia poza element.
Jak wyrównać tooltip do krawędzi elementu, a nie do jego środka?
Użyj wariantów narożnikowych: zamiast left: 50% i translateX(-50%) ustaw left: 0 albo right: 0. Dymek zacznie się równo z krawędzią triggera, a strzałkę przesuń w głąb, żeby nie wychodziła poza obrys.
Powiązane narzędzia: generator animacji CSS, piaskownica Flexbox i generator gradientów CSS.