On this page
Tooltip
Hover and focus tips. aria-description is the no-script
fallback. popover="hint" puts the bubble in the
top layer; interestfor opens it on hover, focus, or
long-press — no Tooltip plugin and no Popper.
| What runs it | JavaScript | |
|---|---|---|
| Bootstrap 5.3 | Tooltip plugin, including Popper | 15.3 KB gzip |
| This repo | aria-description and CSS generated content, or popover=hint. No Popper. | 0 KB |
When that API is missing,
this page also loads interestfor.js (2.8 KB gzip).
See Polyfills.
| Avoid |
|
|---|
How it works
-
Two markup paths. Use one per trigger: either
aria-descriptionplus a.bs-tooltip-*class on the control, orinterestforpointing at a.tooltipwithpopover="hint". Do not put both on the same control — Chromium would announce the description twice. -
Add
.bs-tooltip-top,.bs-tooltip-end,.bs-tooltip-bottom, or.bs-tooltip-startinstead ofdata-bs-placement. Default placement is top, matching Bootstrap. -
A hint popover is a separate hint stack: it does not close an open
popover="auto"dropdown. Escape dismisses interest. -
Keep tooltips on keyboard-focusable, interactive elements (links
and form controls), as Bootstrap’s docs recommend.
interestforis valid on<a>,<button>, and<area>only. -
Do not set
role="tooltip"oraria-describedbyon aninterestfortrigger. The browser associates the hint target itself.
No-script fallback
aria-description replaces
data-bs-title / title. The bubble is
::after (content: attr(aria-description))
and the arrow is ::before, positioned against the
trigger. Hover and keyboard focus (:hover /
:focus-within) replace Bootstrap’s default
trigger: 'hover focus'. Empty
aria-description values never display. The bubble is
not in the top layer, so a transformed or
overflow-clipped ancestor can still cover it.
Tooltips on links
Hover over the links below to see tooltips.
Placeholder text to demonstrate some inline links with tooltips. Content placed here just to mimic the presence of real text. The same pattern works for further links once the class and description sit on the trigger.
<p class="text-body-secondary mb-0">Placeholder text to demonstrate some <a href="#" class="bs-tooltip-top text-nowrap" aria-description="Default tooltip">inline links</a> with tooltips. Content placed here just to mimic the presence of <a href="#" class="bs-tooltip-top text-nowrap" aria-description="Another tooltip">real text</a>. The same pattern works for <a href="#" class="bs-tooltip-top text-nowrap" aria-description="Another one here too">further links</a> once the class and description sit on <a href="#" class="bs-tooltip-top text-nowrap" aria-description="The last tip">the trigger</a>.</p> Custom tooltips
Customize appearance with CSS variables on a class on the trigger (Bootstrap’s data-bs-custom-class injected that class from JS).
<button type="button" class="btn btn-secondary bs-tooltip-top custom-tooltip" aria-description="This top tooltip is themed via CSS variables.">
Custom tooltip
</button> Directions
Four options are available: top, right, bottom, and left. Directions are mirrored when using Bootstrap in RTL. Add .bs-tooltip-top, .bs-tooltip-end, .bs-tooltip-bottom, or .bs-tooltip-start.
.bs-tooltip-auto matches
.bs-tooltip-top and does not reorient. Use a hint
popover with .bs-tooltip-auto when you need
position-try-fallbacks.
<div class="d-flex flex-wrap justify-content-center gap-2 py-4">
<button type="button" class="btn btn-secondary bs-tooltip-top" aria-description="Tooltip on top">
Tooltip on top
</button>
<button type="button" class="btn btn-secondary bs-tooltip-end" aria-description="Tooltip on right">
Tooltip on right
</button>
<button type="button" class="btn btn-secondary bs-tooltip-bottom" aria-description="Tooltip on bottom">
Tooltip on bottom
</button>
<button type="button" class="btn btn-secondary bs-tooltip-start" aria-description="Tooltip on left">
Tooltip on left
</button>
</div> Tooltip with HTML
Bootstrap’s data-bs-html can put markup in the bubble. The CSS fallback cannot.
aria-description via CSS
content: attr(aria-description). That value is a
string, so the bubble is plain text only — no
<em>, <u>,
<b>, or other HTML. Use a
hint popover or a
popover when the
content needs markup.
<button type="button" class="btn btn-secondary bs-tooltip-top" aria-description="Tooltip with HTML">
Tooltip with HTML
</button> Disabled elements
Elements with the disabled attribute aren’t interactive, so the tooltip lives on a keyboard-focusable wrapper.
<span tabindex="0"> around the
disabled control. Put .bs-tooltip-* and
aria-description on that wrapper, not on the
disabled button.
<span class="d-inline-block bs-tooltip-top" tabindex="0" aria-description="Disabled tooltip">
<button class="btn btn-primary" type="button" disabled>Disabled button</button>
</span> Hint popover
interestfor is Chrome 142+ and is not native in Firefox
or Safari. popover="hint" is Chrome 151+,
Firefox 153+, and Safari preview. The
interestfor polyfill
loads when that API is missing and the page has
[interestfor]. It does not polyfill
popover="hint", so Safari and Firefox before
153 still need the
no-script fallback.
The bubble is a .tooltip with
popover="hint" and real
.tooltip-inner markup. Put
interestfor on the trigger, matching the bubble’s
id. CSS Anchor Positioning tethers it to that
invoker. Hover, keyboard focus, and long-press replace Bootstrap’s
trigger: 'hover focus'.
Four directions
Same four placements as the CSS fallback, now in the top layer. Hover or focus the buttons.
<div class="d-flex flex-wrap justify-content-center gap-2 py-4">
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintTop">
Tooltip on top
</button>
<div class="tooltip bs-tooltip-top" popover="hint" id="tipHintTop">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Tooltip on top</div>
</div>
</span>
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintEnd">
Tooltip on right
</button>
<div class="tooltip bs-tooltip-end" popover="hint" id="tipHintEnd">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Tooltip on right</div>
</div>
</span>
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintBottom">
Tooltip on bottom
</button>
<div class="tooltip bs-tooltip-bottom" popover="hint" id="tipHintBottom">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Tooltip on bottom</div>
</div>
</span>
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintStart">
Tooltip on left
</button>
<div class="tooltip bs-tooltip-start" popover="hint" id="tipHintStart">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Tooltip on left</div>
</div>
</span>
</div> Auto placement
.bs-tooltip-auto still defaults to top. On a hint popover, position-try-fallbacks flips the bubble when it would overflow. The arrow stays in the top orientation after a flip.
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintAuto">
Tooltip auto
</button>
<div class="tooltip bs-tooltip-auto" popover="hint" id="tipHintAuto">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Auto tooltip. It flips when there is no room.</div>
</div>
</span> Custom hint tooltips
Put the custom class on the .tooltip element so the CSS variables apply to the bubble.
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintCustom">
Custom tooltip
</button>
<div class="tooltip bs-tooltip-top custom-tooltip" popover="hint" id="tipHintCustom">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">This top tooltip is themed via CSS variables.</div>
</div>
</span> Hint tooltip with HTML
The inner is markup, so em, u, and b work. That replaces data-bs-html.
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="tipHintHtml">
Tooltip with HTML
</button>
<div class="tooltip bs-tooltip-top" popover="hint" id="tipHintHtml">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner"><em>Tooltip</em> <u>with</u> <b>HTML</b></div>
</div>
</span> Delay
interest-delay-start and interest-delay-end on the trigger replace data-bs-delay. Hint triggers default to 0s so they match Bootstrap’s delay: 0. The polyfill reads --interest-delay-start / --interest-delay-end; hint CSS sets both. Override --bs-tooltip-delay-start / --bs-tooltip-delay-end on the trigger.
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" style="--bs-tooltip-delay-start: .5s; --bs-tooltip-delay-end: .15s" interestfor="tipHintDelay">
Delayed tooltip
</button>
<div class="tooltip bs-tooltip-top" popover="hint" id="tipHintDelay">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Shows after 500 ms, hides after 150 ms.</div>
</div>
</span> Hint on links
interestfor is valid on a, not only button.
Placeholder text to demonstrate some inline links with hint tooltips. Content placed here just to mimic the presence of real text. Default tooltip Another tooltip
<p class="text-body-secondary mb-0">Placeholder text to demonstrate some <a href="#" interestfor="tipHintLink1">inline links</a> with hint tooltips. Content placed here just to mimic the presence of <a href="#" interestfor="tipHintLink2">real text</a>.
<span class="tooltip bs-tooltip-top" popover="hint" id="tipHintLink1">
<span class="tooltip-arrow"></span>
<span class="tooltip-inner">Default tooltip</span>
</span>
<span class="tooltip bs-tooltip-top" popover="hint" id="tipHintLink2">
<span class="tooltip-arrow"></span>
<span class="tooltip-inner">Another tooltip</span>
</span>
</p> Disabled hint trigger
interestfor needs a button or a link. Wrap a visually disabled .btn.disabled in a transparent button.
<button> is invalid, so the inner
control is a <span class="btn disabled">,
the same wrapper as a disabled popover.
<span class="d-inline-block">
<button type="button" class="border-0 bg-transparent p-0" interestfor="tipHintDisabled">
<span class="btn btn-primary disabled">Disabled button</span>
</button>
<div class="tooltip bs-tooltip-top" popover="hint" id="tipHintDisabled">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Disabled tooltip</div>
</div>
</span> Accessibility
aria-description is a description, not the accessible name. Give icon-only controls a name (aria-label or visible text) and use the description for extra help.
The CSS fallback bubble is generated content with empty alternative text, so assistive technology is not read a second copy. Hover and :focus-within show it. It is not in the top layer.
Native interestfor gives a hint popover an implicit tooltip role. The polyfill sets aria-describedby on a plain hint. Escape dismisses interest. The pointer can move onto the bubble (WCAG 1.4.13). Do not add role="tooltip" yourself on the native path.
Browser support
| No script | Polyfill | No fallback |
|---|---|---|
aria-description and content: attr(). Hover and focus. Plain text, not in the top layer. | interestfor when interestForElement is missing and the page has [interestfor]. It does not polyfill popover="hint". | popover="hint" is Chrome 151+, Firefox 153+, Safari preview. Without hint the polyfill cannot open the top-layer bubble. .bs-tooltip-auto on a hint popover flips with position-try-fallbacks. The arrow stays in the default (top) orientation after a flip. |