CSS Tooltip Without JavaScript
Two kinds of tooltip, both without a line of script: a classic hint on hover and keyboard focus, and a “toggletip” that opens on click, closes with Esc, can hold links — and positions itself next to its button with CSS anchor positioning.
- JavaScript
- 0 KB
- Built with
:focus-within,popover, anchor positioning- Browsers
- All current browsers (anchor positioning since early 2026)
- Updated
Yes. Put the hint as real text next to a focusable trigger, link the two with aria-describedby, and show the hint on :hover and :focus-within. For anything important — or anything with a link in it — use a toggletip instead: a popover opened by a button, which closes with Esc and needs no script either.
Live demo 1: hover and focus tooltips
Hover over the underlined terms, or Tab to them. You can move the mouse onto the bubble without it closing — that is required by accessibility guidelines, and most CSS tooltips get it wrong.
Before you launch in China, check your A registration with China's Ministry of Industry and Information Technology. Websites hosted in mainland China need one. and research demand in Baidu's search trend tool (百度指数) — like Google Trends, for searches on Baidu. .
Live demo 2: an accessible toggletip
Click the ? or press Enter on it. The tip appears above the button — or below, if there isn't room. Esc or a click elsewhere closes it, and it can contain a link.
We also help Western brands with Chinese GEO
GEO (generative engine optimisation) makes your content show up in the answers of AI assistants — in China, the assistants Chinese users ask instead of a search engine.
How the two tooltips work
- The hint is real text
Many CSS tooltips keep the hint in a
data-attribute and print it withcontent: attr(). Screen readers often can't reach that text, and search engines don't treat it as page content. Here it is a normal<span>in the HTML. - Linked with
aria-describedbyThe button points to the hint's
id. Screen readers read the term first, then the hint — even while the bubble is visually hidden. - Focusable trigger
The term is a
<button>, styled like text with a dotted underline. Keyboard users reach it with Tab, and:focus-withinopens the bubble — notabindextricks. - An invisible bridge
A transparent
::beforefills the gap between term and bubble, so moving the mouse onto the bubble doesn't close it. - The toggletip is a popover
popovertargeton a real button opens it. The browser adds Esc, click-outside-to-close, the expanded state for screen readers and focus return. - Anchor positioning places it
anchor-nameon the button andposition-anchoron the tip tie them together;position-area: topputs the tip above,position-try-fallbacks: flip-blockbelow if it doesn't fit.
Copy the HTML and CSS
Tooltip on hover and focus
<p class="ui-tip-text">
Before you launch in China, check your
<span class="ui-tip">
<button class="ui-tip__term" type="button" aria-describedby="ui-tip-icp">ICP filing</button>
<span class="ui-tip__bubble" role="tooltip" id="ui-tip-icp">A registration with China's Ministry of Industry and Information Technology. Websites hosted in mainland China need one.</span>
</span>
and research demand in
<span class="ui-tip">
<button class="ui-tip__term" type="button" aria-describedby="ui-tip-index">Baidu Index</button>
<span class="ui-tip__bubble" role="tooltip" id="ui-tip-index">Baidu's search trend tool (百度指数) — like Google Trends, for searches on Baidu.</span>
</span>.
</p>/* Tooltip — no JavaScript.
The hint is real text in the HTML, linked to its trigger with
aria-describedby. It shows on hover AND on keyboard focus. */
.ui-tip-text {
margin: 0;
font: 400 1.0625rem/1.9 system-ui, -apple-system, "Segoe UI", sans-serif;
color: #1b1f3b;
}
.ui-tip {
position: relative;
display: inline-block;
}
/* The term: a real button, so keyboard users can reach it */
.ui-tip__term {
padding: 0;
border: 0;
border-bottom: 2px dotted #4f46e5;
background: none;
font: inherit;
font-weight: 600;
color: inherit;
cursor: help;
}
.ui-tip__term:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 3px;
border-radius: 3px;
}
/* The bubble: hidden until hover or focus */
.ui-tip__bubble {
position: absolute;
bottom: calc(100% + 10px);
left: 50%;
z-index: 10;
width: max-content;
max-width: min(17rem, 80vw);
padding: 0.6rem 0.8rem;
border-radius: 8px;
background: #1b1f3b;
color: #fff;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
text-align: left;
translate: -50% 0;
/* display: none while hidden, so off-screen bubbles never
make the page scroll sideways on phones */
display: none;
opacity: 0;
transition: opacity 0.15s, display 0.15s allow-discrete;
}
/* Small arrow */
.ui-tip__bubble::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
translate: -50% 0;
border: 6px solid transparent;
border-top-color: #1b1f3b;
}
/* Invisible bridge: the mouse can move onto the bubble without it closing */
.ui-tip__bubble::before {
content: "";
position: absolute;
top: 100%;
left: 0;
right: 0;
height: 12px;
}
.ui-tip:hover .ui-tip__bubble,
.ui-tip:focus-within .ui-tip__bubble {
display: block;
opacity: 1;
}
/* Fade in (browsers without @starting-style just show it) */
@starting-style {
.ui-tip:hover .ui-tip__bubble,
.ui-tip:focus-within .ui-tip__bubble {
opacity: 0;
}
}Toggletip with popover and anchor positioning
<p class="ui-tip-text">
We also help Western brands with Chinese GEO
<button class="ui-toggletip__button" type="button" popovertarget="ui-geo-tip"
style="anchor-name: --ui-geo" aria-label="What is Chinese GEO?">?</button>
</p>
<div class="ui-toggletip" id="ui-geo-tip" popover style="position-anchor: --ui-geo">
<p><strong>GEO</strong> (generative engine optimisation) makes your content show up in the answers of AI assistants — in China, the assistants Chinese users ask instead of a search engine.</p>
<p><a href="/services/geo/china/">How we do Chinese GEO</a></p>
</div>/* Toggletip — no JavaScript.
Click (or Enter) opens it, Esc or a click outside closes it.
It can contain links. Anchor positioning puts it next to its button
and flips it below when there is no room above. */
.ui-toggletip__button {
display: inline-grid;
place-items: center;
width: 1.5rem;
height: 1.5rem;
margin-left: 0.2rem;
padding: 0;
border: 0;
border-radius: 50%;
background: #4f46e5;
color: #fff;
font: 700 0.875rem/1 system-ui, -apple-system, "Segoe UI", sans-serif;
vertical-align: 0.1em;
cursor: pointer;
}
.ui-toggletip__button:focus-visible,
.ui-toggletip a:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 3px;
}
.ui-toggletip {
margin: auto; /* fallback: centred on screen — survives a "* { margin: 0 }" reset */
width: min(19rem, calc(100vw - 2rem));
padding: 0.9rem 1rem;
border: 0;
border-radius: 10px;
background: #1b1f3b;
color: #fff;
box-shadow: 0 14px 34px rgb(10 12 25 / 0.3);
font: 400 0.9rem/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
}
.ui-toggletip p {
margin: 0 0 0.5rem;
}
.ui-toggletip p:last-child {
margin-bottom: 0;
}
.ui-toggletip a {
color: #b9b5ff;
font-weight: 600;
}
/* Next to its button, wherever anchor positioning is supported */
@supports (anchor-name: --a) {
.ui-toggletip {
inset: auto;
margin: 0 0 10px;
position-area: top;
position-try-fallbacks: flip-block;
}
}Exactly the code running in the demos. Each toggletip needs its own anchor-name (here set inline) and its own popover id. Browsers without anchor positioning show the tip centred on screen — still usable.
Accessibility: where a CSS-only tooltip stops
| Requirement | Hover tooltip | Toggletip | Details |
|---|---|---|---|
| Keyboard | Works | Works | Both triggers are buttons. |
| Screen readers | Works | Works | Hint via aria-describedby; toggletip as an expandable button. |
| Hoverable | Works | n/a | The pointer can move onto the bubble (WCAG 1.4.13). |
| Dismissible with Esc | Needs JS | Built in | WCAG 1.4.13 asks that hover content can be dismissed without moving the pointer. CSS alone can't react to Esc. |
| Touch screens | Partly | Works | There is no hover on touch; a tap focuses the button and shows the bubble, but that isn't obvious to users. |
| Links inside | Don't | Works | A hover tooltip disappears when focus moves into it. |
- Hover tooltips only for short, extra hints that nobody needs to complete a task.
- Toggletips for explanations people may actually need, and for anything with a link.
- Neither for labels of icon-only buttons — those need an accessible name (
aria-labelor visually hidden text), not a tooltip. - To make the hover tooltip close with Esc, add a small script on top — it is the one part CSS can't do.
Tooltips and SEO
Text in the HTML counts; text in attributes doesn't. You may read that hints in data- attributes give search engines “extra keyword context”. They don't: data- attributes and text generated by CSS are not treated as page content. A title attribute isn't page text either. If an explanation matters, put it in the HTML as text — as both demos here do.
Definitions are valuable content. Short explanations of terms like “ICP filing” or “Baidu Index” are exactly what people search for and what AI answers quote. If a definition matters for your topic, consider showing it in the text or in a glossary page instead of hiding it in a tooltip.
No layout shift. Both tooltips are positioned over the page, so opening them never pushes content around.
Questions about CSS tooltips
How do I make a tooltip with only HTML and CSS?
Wrap a focusable trigger and a hint <span> in a positioned element, link them with aria-describedby, and show the hint on :hover and :focus-within.
Is a CSS tooltip accessible?
Partly. It can work with keyboard and screen readers, but it can't be dismissed with Esc, which WCAG asks for. A toggletip built with popover meets all requirements without JavaScript.
What is a toggletip?
A tooltip that opens on click instead of hover. It works on touch screens, stays open while you read it, and can contain links.
Should I use the title attribute for tooltips?
Better not. Browsers show it only on mouse hover, after a delay, and not at all on touch screens or for keyboard users.
How do I keep a tooltip inside the screen?
Use CSS anchor positioning with position-try-fallbacks, as in the toggletip demo. It flips the tip to the other side when it would not fit — supported in all major browsers since early 2026.
This FAQ is built without JavaScript — see the accordion pattern.
Is your expertise visible to Chinese search and AI?
Clear explanations in the HTML are what Baidu ranks and AI assistants quote. We write and check that content in native Chinese, and run Chinese GEO and Baidu SEO for Western companies.
CSS-First UI Patterns are written and maintained by Jademond Digital, a Chinese digital marketing agency founded in Guangzhou in 2012. We build and audit websites for Baidu, Google and AI search — which is why we care what works without JavaScript. About us →