Components
Tooltip
Hover and focus hints on the Popover API with Interest Invokers and anchor positioning.
A tooltip pairs a trigger with a .tooltip__content hint (popover="hint"). Wire them with
interestfor on the trigger. Hover, focus, and long-press all work automatically, ARIA
included. Position the hint with data-side.
The hint holds a <span class="tooltip__arrow" aria-hidden="true"></span> that points back at
the trigger, and carries data-use-inverted-menu="false" to opt out of the inverted dark
popover scheme.
Default
Loading components…
<div class="tooltip"> <button type="button" class="button" interestfor="tooltip-example-default-1">Hover</button> <div class="tooltip__content" id="tooltip-example-default-1" popover="hint" data-use-inverted-menu="false" > Add to library <span class="tooltip__arrow" aria-hidden="true"></span> </div></div>/** * _tooltip.css — Tooltip (.tooltip, .tooltip__content) * * @layer variables, reset, components * @requires layers.css, _variables.css, _popover.css * @uses interestfor — Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses .tooltip__content — hint popover (light dismiss, auto-hides sibling hints) * @uses anchor-name, position-anchor, position-area, position-try-fallbacks — * explicit anchors for polyfill; data-side maps to --popover-position-* * @tokens --tooltip-* (@layer variables) */ @layer variables { :root { --tooltip-background: var(--foreground); --tooltip-foreground: var(--background); --tooltip-border: transparent; --tooltip-radius: var(--radius-sm); --tooltip-shadow: var(--shadow-sm); --tooltip-padding-inline: var(--gap-xs); --tooltip-padding-block: var(--step-1); --tooltip-gap: var(--step-1); --tooltip-font-family: var(--font-family-mono); --tooltip-font-size: var(--font-size-xs); --tooltip-line-height: var(--leading-xs); --tooltip-font-weight: var(--font-weight-body); --tooltip-max-inline-size: 16ch; /* Arrow */ --tooltip-arrow-size: var(--step-2); --tooltip-arrow-color: var(--tooltip-background); --tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions — lighter than popover */ --tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --tooltip-transition-timing-function: var(--default-transition-timing-function); --tooltip-transition-delay: 0ms; --tooltip-transition: opacity var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), transform var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), overlay var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete, display var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete; --tooltip-scale--start: 0.94; --tooltip-translate--start: 0, 0.25em, 0; --tooltip-opacity--start: 0; --tooltip-scale--end: 1; --tooltip-translate--end: 0, 0, 0; --tooltip-opacity--end: 1; /* Positioning — smaller offset than popover menus */ --tooltip-margin-block: var(--step-1); --tooltip-margin-inline: 0; /* Popover overrides */ --popover-shadow: var(--shadow-xs); --popover-min-inline-size: fit-content; --popover-position-area: block-start span-all; --popover-origin: bottom center; }}@layer reset { /* =========================================================================== HINT POPOVER OVERRIDES - [popover] defaults in _popover.css target menus; hint tooltips need a compact surface and must not inherit min-inline-size or backdrop blur. =========================================================================== */ :where(.tooltip__content) { color: var(--tooltip-foreground); background-color: var(--tooltip-background); min-inline-size: unset; max-inline-size: var(--tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--popover-origin); opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); transition: var(--tooltip-transition); } @supports (anchor-name: --zazz) { :where(.tooltip__content) { margin-block: var(--tooltip-margin-block); margin-inline: var(--tooltip-margin-inline); } } .tooltip__content:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--tooltip-opacity--end); transform: scale(var(--tooltip-scale--end)) translate3d(var(--tooltip-translate--end)); @starting-style { opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); } }}@layer zazz.components { /* =========================================================================== TOOLTIP — interestfor trigger + anchored hint popover - interestfor wires hover/focus/long-press to the hint popover and sets ARIA (aria-describedby / aria-details). Do not add role="tooltip" or manual ARIA. - Explicit anchor-name supports the invokers/anchor polyfills (implicit anchors from interestfor are not polyfilled). =========================================================================== */ .tooltip { anchor-scope: --tooltip-trigger; display: inline-block; } .tooltip> :where([interestfor], .tooltip__trigger) { anchor-name: --tooltip-trigger; } .tooltip__trigger { display: inline-flex; } .tooltip__content { position-anchor: --tooltip-trigger; display: flex; align-items: center; gap: var(--tooltip-gap); inline-size: max-content; padding-inline: var(--tooltip-padding-inline); padding-block: var(--tooltip-padding-block); border: 1px solid var(--tooltip-border); border-radius: var(--tooltip-radius); box-shadow: var(--tooltip-shadow); font-family: var(--tooltip-font-family); font-size: var(--tooltip-font-size); line-height: var(--tooltip-line-height); font-weight: var(--tooltip-font-weight); text-wrap: balance; } .tooltip__content>kbd { --kbd-background: color-mix(in oklch, var(--tooltip-foreground) 15%, transparent); --kbd-color: var(--tooltip-foreground); --kbd-inline-size: var(--step-4); --kbd-block-size: var(--step-4); --kbd-font-size: var(--font-size-xs); } .tooltip__content:has(.tooltip__arrow) { overflow: visible; } /* Positioning — reuse --popover-position-* hooks from _popover.css via data-side */ .tooltip__content[data-side="top"]:not([data-align]), .tooltip__content[data-side="top"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-start span-all; --popover-origin: bottom center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="bottom"]:not([data-align]), .tooltip__content[data-side="bottom"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-end span-all; --popover-origin: top center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="left"]:not([data-align]), .tooltip__content[data-side="left"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-start span-all; --popover-origin: center right; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } .tooltip__content[data-side="right"]:not([data-align]), .tooltip__content[data-side="right"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-end span-all; --popover-origin: center left; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } /* Tooltip arrow */ .tooltip__arrow { position: absolute; top: calc(100% - var(--tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--tooltip-arrow-size); block-size: var(--tooltip-arrow-size); background-color: var(--tooltip-arrow-color); border-radius: var(--tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ .tooltip__content[data-side="top"] .tooltip__arrow { bottom: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="bottom"] .tooltip__arrow { top: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="left"] .tooltip__arrow { left: calc(100% - var(--tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } .tooltip__content[data-side="right"] .tooltip__arrow { right: calc(100% - var(--tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}With keyboard shortcut
Loading components…
<div class="tooltip"> <button type="button" class="button" data-variant="muted" data-size="icon" interestfor="tooltip-example-save-1" aria-label="Save" > <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <path fill="currentColor" d="M74.34,85.66A8,8,0,0,1,85.66,74.34L120,108.69V24a8,8,0,0,1,16,0v84.69l34.34-34.35a8,8,0,0,1,11.32,11.32l-48,48a8,8,0,0,1-11.32,0ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H84.4a4,4,0,0,1,2.83,1.17L111,145A24,24,0,0,0,145,145l23.8-23.8A4,4,0,0,1,171.6,120H224A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z" /> </svg> </button> <div class="tooltip__content" id="tooltip-example-save-1" popover="hint" data-use-inverted-menu="false" > Save <kbd>⌘</kbd> <kbd>S</kbd> <span class="tooltip__arrow" aria-hidden="true"></span> </div></div>/** * _tooltip.css — Tooltip (.tooltip, .tooltip__content) * * @layer variables, reset, components * @requires layers.css, _variables.css, _popover.css * @uses interestfor — Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses .tooltip__content — hint popover (light dismiss, auto-hides sibling hints) * @uses anchor-name, position-anchor, position-area, position-try-fallbacks — * explicit anchors for polyfill; data-side maps to --popover-position-* * @tokens --tooltip-* (@layer variables) */ @layer variables { :root { --tooltip-background: var(--foreground); --tooltip-foreground: var(--background); --tooltip-border: transparent; --tooltip-radius: var(--radius-sm); --tooltip-shadow: var(--shadow-sm); --tooltip-padding-inline: var(--gap-xs); --tooltip-padding-block: var(--step-1); --tooltip-gap: var(--step-1); --tooltip-font-family: var(--font-family-mono); --tooltip-font-size: var(--font-size-xs); --tooltip-line-height: var(--leading-xs); --tooltip-font-weight: var(--font-weight-body); --tooltip-max-inline-size: 16ch; /* Arrow */ --tooltip-arrow-size: var(--step-2); --tooltip-arrow-color: var(--tooltip-background); --tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions — lighter than popover */ --tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --tooltip-transition-timing-function: var(--default-transition-timing-function); --tooltip-transition-delay: 0ms; --tooltip-transition: opacity var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), transform var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), overlay var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete, display var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete; --tooltip-scale--start: 0.94; --tooltip-translate--start: 0, 0.25em, 0; --tooltip-opacity--start: 0; --tooltip-scale--end: 1; --tooltip-translate--end: 0, 0, 0; --tooltip-opacity--end: 1; /* Positioning — smaller offset than popover menus */ --tooltip-margin-block: var(--step-1); --tooltip-margin-inline: 0; /* Popover overrides */ --popover-shadow: var(--shadow-xs); --popover-min-inline-size: fit-content; --popover-position-area: block-start span-all; --popover-origin: bottom center; }}@layer reset { /* =========================================================================== HINT POPOVER OVERRIDES - [popover] defaults in _popover.css target menus; hint tooltips need a compact surface and must not inherit min-inline-size or backdrop blur. =========================================================================== */ :where(.tooltip__content) { color: var(--tooltip-foreground); background-color: var(--tooltip-background); min-inline-size: unset; max-inline-size: var(--tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--popover-origin); opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); transition: var(--tooltip-transition); } @supports (anchor-name: --zazz) { :where(.tooltip__content) { margin-block: var(--tooltip-margin-block); margin-inline: var(--tooltip-margin-inline); } } .tooltip__content:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--tooltip-opacity--end); transform: scale(var(--tooltip-scale--end)) translate3d(var(--tooltip-translate--end)); @starting-style { opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); } }}@layer zazz.components { /* =========================================================================== TOOLTIP — interestfor trigger + anchored hint popover - interestfor wires hover/focus/long-press to the hint popover and sets ARIA (aria-describedby / aria-details). Do not add role="tooltip" or manual ARIA. - Explicit anchor-name supports the invokers/anchor polyfills (implicit anchors from interestfor are not polyfilled). =========================================================================== */ .tooltip { anchor-scope: --tooltip-trigger; display: inline-block; } .tooltip> :where([interestfor], .tooltip__trigger) { anchor-name: --tooltip-trigger; } .tooltip__trigger { display: inline-flex; } .tooltip__content { position-anchor: --tooltip-trigger; display: flex; align-items: center; gap: var(--tooltip-gap); inline-size: max-content; padding-inline: var(--tooltip-padding-inline); padding-block: var(--tooltip-padding-block); border: 1px solid var(--tooltip-border); border-radius: var(--tooltip-radius); box-shadow: var(--tooltip-shadow); font-family: var(--tooltip-font-family); font-size: var(--tooltip-font-size); line-height: var(--tooltip-line-height); font-weight: var(--tooltip-font-weight); text-wrap: balance; } .tooltip__content>kbd { --kbd-background: color-mix(in oklch, var(--tooltip-foreground) 15%, transparent); --kbd-color: var(--tooltip-foreground); --kbd-inline-size: var(--step-4); --kbd-block-size: var(--step-4); --kbd-font-size: var(--font-size-xs); } .tooltip__content:has(.tooltip__arrow) { overflow: visible; } /* Positioning — reuse --popover-position-* hooks from _popover.css via data-side */ .tooltip__content[data-side="top"]:not([data-align]), .tooltip__content[data-side="top"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-start span-all; --popover-origin: bottom center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="bottom"]:not([data-align]), .tooltip__content[data-side="bottom"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-end span-all; --popover-origin: top center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="left"]:not([data-align]), .tooltip__content[data-side="left"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-start span-all; --popover-origin: center right; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } .tooltip__content[data-side="right"]:not([data-align]), .tooltip__content[data-side="right"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-end span-all; --popover-origin: center left; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } /* Tooltip arrow */ .tooltip__arrow { position: absolute; top: calc(100% - var(--tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--tooltip-arrow-size); block-size: var(--tooltip-arrow-size); background-color: var(--tooltip-arrow-color); border-radius: var(--tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ .tooltip__content[data-side="top"] .tooltip__arrow { bottom: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="bottom"] .tooltip__arrow { top: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="left"] .tooltip__arrow { left: calc(100% - var(--tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } .tooltip__content[data-side="right"] .tooltip__arrow { right: calc(100% - var(--tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}Sides
Loading components…
<div class="flex flex-wrap items-center gap-md"> <div class="tooltip"> <button type="button" class="button" interestfor="tooltip-example-left-1">Left</button> <div class="tooltip__content" id="tooltip-example-left-1" popover="hint" data-side="left" data-use-inverted-menu="false" > Tooltip on left <span class="tooltip__arrow" aria-hidden="true"></span> </div> </div> <div class="tooltip"> <button type="button" class="button" interestfor="tooltip-example-top-1">Top</button> <div class="tooltip__content" id="tooltip-example-top-1" popover="hint" data-side="top" data-use-inverted-menu="false" > Tooltip on top <span class="tooltip__arrow" aria-hidden="true"></span> </div> </div> <div class="tooltip"> <button type="button" class="button" interestfor="tooltip-example-bottom-1">Bottom</button> <div class="tooltip__content" id="tooltip-example-bottom-1" popover="hint" data-side="bottom" data-use-inverted-menu="false" > Tooltip on bottom <span class="tooltip__arrow" aria-hidden="true"></span> </div> </div> <div class="tooltip"> <button type="button" class="button" interestfor="tooltip-example-right-1">Right</button> <div class="tooltip__content" id="tooltip-example-right-1" popover="hint" data-side="right" data-use-inverted-menu="false" > Tooltip on right <span class="tooltip__arrow" aria-hidden="true"></span> </div> </div></div>/** * _tooltip.css — Tooltip (.tooltip, .tooltip__content) * * @layer variables, reset, components * @requires layers.css, _variables.css, _popover.css * @uses interestfor — Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses .tooltip__content — hint popover (light dismiss, auto-hides sibling hints) * @uses anchor-name, position-anchor, position-area, position-try-fallbacks — * explicit anchors for polyfill; data-side maps to --popover-position-* * @tokens --tooltip-* (@layer variables) */ @layer variables { :root { --tooltip-background: var(--foreground); --tooltip-foreground: var(--background); --tooltip-border: transparent; --tooltip-radius: var(--radius-sm); --tooltip-shadow: var(--shadow-sm); --tooltip-padding-inline: var(--gap-xs); --tooltip-padding-block: var(--step-1); --tooltip-gap: var(--step-1); --tooltip-font-family: var(--font-family-mono); --tooltip-font-size: var(--font-size-xs); --tooltip-line-height: var(--leading-xs); --tooltip-font-weight: var(--font-weight-body); --tooltip-max-inline-size: 16ch; /* Arrow */ --tooltip-arrow-size: var(--step-2); --tooltip-arrow-color: var(--tooltip-background); --tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions — lighter than popover */ --tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --tooltip-transition-timing-function: var(--default-transition-timing-function); --tooltip-transition-delay: 0ms; --tooltip-transition: opacity var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), transform var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), overlay var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete, display var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete; --tooltip-scale--start: 0.94; --tooltip-translate--start: 0, 0.25em, 0; --tooltip-opacity--start: 0; --tooltip-scale--end: 1; --tooltip-translate--end: 0, 0, 0; --tooltip-opacity--end: 1; /* Positioning — smaller offset than popover menus */ --tooltip-margin-block: var(--step-1); --tooltip-margin-inline: 0; /* Popover overrides */ --popover-shadow: var(--shadow-xs); --popover-min-inline-size: fit-content; --popover-position-area: block-start span-all; --popover-origin: bottom center; }}@layer reset { /* =========================================================================== HINT POPOVER OVERRIDES - [popover] defaults in _popover.css target menus; hint tooltips need a compact surface and must not inherit min-inline-size or backdrop blur. =========================================================================== */ :where(.tooltip__content) { color: var(--tooltip-foreground); background-color: var(--tooltip-background); min-inline-size: unset; max-inline-size: var(--tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--popover-origin); opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); transition: var(--tooltip-transition); } @supports (anchor-name: --zazz) { :where(.tooltip__content) { margin-block: var(--tooltip-margin-block); margin-inline: var(--tooltip-margin-inline); } } .tooltip__content:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--tooltip-opacity--end); transform: scale(var(--tooltip-scale--end)) translate3d(var(--tooltip-translate--end)); @starting-style { opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); } }}@layer zazz.components { /* =========================================================================== TOOLTIP — interestfor trigger + anchored hint popover - interestfor wires hover/focus/long-press to the hint popover and sets ARIA (aria-describedby / aria-details). Do not add role="tooltip" or manual ARIA. - Explicit anchor-name supports the invokers/anchor polyfills (implicit anchors from interestfor are not polyfilled). =========================================================================== */ .tooltip { anchor-scope: --tooltip-trigger; display: inline-block; } .tooltip> :where([interestfor], .tooltip__trigger) { anchor-name: --tooltip-trigger; } .tooltip__trigger { display: inline-flex; } .tooltip__content { position-anchor: --tooltip-trigger; display: flex; align-items: center; gap: var(--tooltip-gap); inline-size: max-content; padding-inline: var(--tooltip-padding-inline); padding-block: var(--tooltip-padding-block); border: 1px solid var(--tooltip-border); border-radius: var(--tooltip-radius); box-shadow: var(--tooltip-shadow); font-family: var(--tooltip-font-family); font-size: var(--tooltip-font-size); line-height: var(--tooltip-line-height); font-weight: var(--tooltip-font-weight); text-wrap: balance; } .tooltip__content>kbd { --kbd-background: color-mix(in oklch, var(--tooltip-foreground) 15%, transparent); --kbd-color: var(--tooltip-foreground); --kbd-inline-size: var(--step-4); --kbd-block-size: var(--step-4); --kbd-font-size: var(--font-size-xs); } .tooltip__content:has(.tooltip__arrow) { overflow: visible; } /* Positioning — reuse --popover-position-* hooks from _popover.css via data-side */ .tooltip__content[data-side="top"]:not([data-align]), .tooltip__content[data-side="top"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-start span-all; --popover-origin: bottom center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="bottom"]:not([data-align]), .tooltip__content[data-side="bottom"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-end span-all; --popover-origin: top center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="left"]:not([data-align]), .tooltip__content[data-side="left"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-start span-all; --popover-origin: center right; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } .tooltip__content[data-side="right"]:not([data-align]), .tooltip__content[data-side="right"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-end span-all; --popover-origin: center left; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } /* Tooltip arrow */ .tooltip__arrow { position: absolute; top: calc(100% - var(--tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--tooltip-arrow-size); block-size: var(--tooltip-arrow-size); background-color: var(--tooltip-arrow-color); border-radius: var(--tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ .tooltip__content[data-side="top"] .tooltip__arrow { bottom: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="bottom"] .tooltip__arrow { top: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="left"] .tooltip__arrow { left: calc(100% - var(--tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } .tooltip__content[data-side="right"] .tooltip__arrow { right: calc(100% - var(--tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}Disabled trigger
Loading components…
<div class="tooltip"> <button type="button" class="button" aria-disabled="true" interestfor="tooltip-example-disabled-1" > Disabled </button> <div class="tooltip__content" id="tooltip-example-disabled-1" popover="hint" data-use-inverted-menu="false" > This action is unavailable <span class="tooltip__arrow" aria-hidden="true"></span> </div></div>/** * _tooltip.css — Tooltip (.tooltip, .tooltip__content) * * @layer variables, reset, components * @requires layers.css, _variables.css, _popover.css * @uses interestfor — Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses .tooltip__content — hint popover (light dismiss, auto-hides sibling hints) * @uses anchor-name, position-anchor, position-area, position-try-fallbacks — * explicit anchors for polyfill; data-side maps to --popover-position-* * @tokens --tooltip-* (@layer variables) */ @layer variables { :root { --tooltip-background: var(--foreground); --tooltip-foreground: var(--background); --tooltip-border: transparent; --tooltip-radius: var(--radius-sm); --tooltip-shadow: var(--shadow-sm); --tooltip-padding-inline: var(--gap-xs); --tooltip-padding-block: var(--step-1); --tooltip-gap: var(--step-1); --tooltip-font-family: var(--font-family-mono); --tooltip-font-size: var(--font-size-xs); --tooltip-line-height: var(--leading-xs); --tooltip-font-weight: var(--font-weight-body); --tooltip-max-inline-size: 16ch; /* Arrow */ --tooltip-arrow-size: var(--step-2); --tooltip-arrow-color: var(--tooltip-background); --tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions — lighter than popover */ --tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --tooltip-transition-timing-function: var(--default-transition-timing-function); --tooltip-transition-delay: 0ms; --tooltip-transition: opacity var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), transform var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay), overlay var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete, display var(--tooltip-transition-duration) var(--tooltip-transition-timing-function) var(--tooltip-transition-delay) allow-discrete; --tooltip-scale--start: 0.94; --tooltip-translate--start: 0, 0.25em, 0; --tooltip-opacity--start: 0; --tooltip-scale--end: 1; --tooltip-translate--end: 0, 0, 0; --tooltip-opacity--end: 1; /* Positioning — smaller offset than popover menus */ --tooltip-margin-block: var(--step-1); --tooltip-margin-inline: 0; /* Popover overrides */ --popover-shadow: var(--shadow-xs); --popover-min-inline-size: fit-content; --popover-position-area: block-start span-all; --popover-origin: bottom center; }}@layer reset { /* =========================================================================== HINT POPOVER OVERRIDES - [popover] defaults in _popover.css target menus; hint tooltips need a compact surface and must not inherit min-inline-size or backdrop blur. =========================================================================== */ :where(.tooltip__content) { color: var(--tooltip-foreground); background-color: var(--tooltip-background); min-inline-size: unset; max-inline-size: var(--tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--popover-origin); opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); transition: var(--tooltip-transition); } @supports (anchor-name: --zazz) { :where(.tooltip__content) { margin-block: var(--tooltip-margin-block); margin-inline: var(--tooltip-margin-inline); } } .tooltip__content:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--tooltip-opacity--end); transform: scale(var(--tooltip-scale--end)) translate3d(var(--tooltip-translate--end)); @starting-style { opacity: var(--tooltip-opacity--start); transform: scale(var(--tooltip-scale--start)) translate3d(var(--tooltip-translate--start)); } }}@layer zazz.components { /* =========================================================================== TOOLTIP — interestfor trigger + anchored hint popover - interestfor wires hover/focus/long-press to the hint popover and sets ARIA (aria-describedby / aria-details). Do not add role="tooltip" or manual ARIA. - Explicit anchor-name supports the invokers/anchor polyfills (implicit anchors from interestfor are not polyfilled). =========================================================================== */ .tooltip { anchor-scope: --tooltip-trigger; display: inline-block; } .tooltip> :where([interestfor], .tooltip__trigger) { anchor-name: --tooltip-trigger; } .tooltip__trigger { display: inline-flex; } .tooltip__content { position-anchor: --tooltip-trigger; display: flex; align-items: center; gap: var(--tooltip-gap); inline-size: max-content; padding-inline: var(--tooltip-padding-inline); padding-block: var(--tooltip-padding-block); border: 1px solid var(--tooltip-border); border-radius: var(--tooltip-radius); box-shadow: var(--tooltip-shadow); font-family: var(--tooltip-font-family); font-size: var(--tooltip-font-size); line-height: var(--tooltip-line-height); font-weight: var(--tooltip-font-weight); text-wrap: balance; } .tooltip__content>kbd { --kbd-background: color-mix(in oklch, var(--tooltip-foreground) 15%, transparent); --kbd-color: var(--tooltip-foreground); --kbd-inline-size: var(--step-4); --kbd-block-size: var(--step-4); --kbd-font-size: var(--font-size-xs); } .tooltip__content:has(.tooltip__arrow) { overflow: visible; } /* Positioning — reuse --popover-position-* hooks from _popover.css via data-side */ .tooltip__content[data-side="top"]:not([data-align]), .tooltip__content[data-side="top"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-start span-all; --popover-origin: bottom center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="bottom"]:not([data-align]), .tooltip__content[data-side="bottom"][data-align="center"] { --tooltip-translate--start: 0, 0.25em, 0; --popover-position-area: block-end span-all; --popover-origin: top center; --tooltip-margin-block: var(--step-1); } .tooltip__content[data-side="left"]:not([data-align]), .tooltip__content[data-side="left"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-start span-all; --popover-origin: center right; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } .tooltip__content[data-side="right"]:not([data-align]), .tooltip__content[data-side="right"][data-align="center"] { --tooltip-translate--start: 0.25em, 0, 0; --popover-position-area: inline-end span-all; --popover-origin: center left; --tooltip-margin-inline: var(--step-1); --tooltip-margin-block: 0; } /* Tooltip arrow */ .tooltip__arrow { position: absolute; top: calc(100% - var(--tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--tooltip-arrow-size); block-size: var(--tooltip-arrow-size); background-color: var(--tooltip-arrow-color); border-radius: var(--tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ .tooltip__content[data-side="top"] .tooltip__arrow { bottom: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="bottom"] .tooltip__arrow { top: calc((var(--tooltip-arrow-size) / 2) * -1); left: 50%; } .tooltip__content[data-side="left"] .tooltip__arrow { left: calc(100% - var(--tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } .tooltip__content[data-side="right"] .tooltip__arrow { right: calc(100% - var(--tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}API
| Attribute | Where | Values |
|---|---|---|
interestfor | trigger | id of the .tooltip__content |
data-side | .tooltip__content | top, bottom, left, right |
data-align | .tooltip__content | center |
data-use-inverted-menu | .tooltip__content | false |