Components
Tooltip
Hover and focus hints built on the Popover API with Interest Invokers and anchor positioning.
A <ui-tooltip> wraps a trigger button and its [data-slot="tooltip-content"] popover (popover="hint"), connected via interestfor on the trigger. The class form <div class="ui-tooltip"> is equivalent: the tag is CSS-only without JavaScript. Hover, focus, and long-press work automatically via the platform.
Default
Loading components…
<ui-tooltip> <button type="button" class="ui-button" interestfor="tooltip-example-default-1">Hover</button> <div data-slot="tooltip-content" id="tooltip-example-default-1" popover="hint" data-use-inverted-menu="false" > Add to library <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div></ui-tooltip>/** * tooltip.css: Tooltip (ui-tooltip | .ui-tooltip, [data-slot~="tooltip-content"]) * * @layer variables, reset, components * @requires layers.css, _variables.css, popover.css, kbd.css (--ui-kbd-*) * @uses interestfor: Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses [data-slot~="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 --ui-popover-position-* * @tokens --ui-tooltip-* (@layer variables) */@layer variables { :root { --ui-tooltip-background: var(--foreground); --ui-tooltip-foreground: var(--background); --ui-tooltip-border: transparent; --ui-tooltip-radius: var(--radius-sm); --ui-tooltip-shadow: var(--shadow-sm); --ui-tooltip-padding-inline: var(--gap-xs); --ui-tooltip-padding-block: var(--step-1); --ui-tooltip-gap: var(--step-1); --ui-tooltip-font-family: var(--font-family-mono); --ui-tooltip-font-size: var(--font-size-xs); --ui-tooltip-line-height: var(--leading-xs); --ui-tooltip-font-weight: var(--font-weight-body); --ui-tooltip-max-inline-size: 16ch; /* Arrow */ --ui-tooltip-arrow-size: var(--step-2); --ui-tooltip-arrow-color: var(--ui-tooltip-background); --ui-tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions: lighter than popover */ --ui-tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --ui-tooltip-transition-timing-function: var(--default-transition-timing-function); --ui-tooltip-transition-delay: 0ms; --ui-tooltip-transition: opacity var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), transform var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), overlay var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete, display var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete; --ui-tooltip-scale--start: 0.94; --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-tooltip-opacity--start: 0; --ui-tooltip-scale--end: 1; --ui-tooltip-translate--end: 0, 0, 0; --ui-tooltip-opacity--end: 1; /* Positioning: smaller offset than popover menus */ --ui-tooltip-margin-block: var(--step-1); --ui-tooltip-margin-inline: 0; /* Popover overrides */ --ui-popover-shadow: var(--shadow-xs); --ui-popover-min-inline-size: fit-content; --ui-popover-position-area: block-start span-all; --ui-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([data-slot~="tooltip-content"]) { color: var(--ui-tooltip-foreground); background-color: var(--ui-tooltip-background); min-inline-size: unset; max-inline-size: var(--ui-tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--ui-popover-origin); opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-tooltip-translate--start)); transition: var(--ui-tooltip-transition); } @supports (anchor-name: --zazz) { :where([data-slot~="tooltip-content"]) { margin-block: var(--ui-tooltip-margin-block); margin-inline: var(--ui-tooltip-margin-inline); } } [data-slot~="tooltip-content"]:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--ui-tooltip-opacity--end); transform: scale(var(--ui-tooltip-scale--end)) translate3d(var(--ui-tooltip-translate--end)); @starting-style { opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-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). =========================================================================== */ :where(ui-tooltip, .ui-tooltip) { anchor-scope: --ui-tooltip-trigger; display: inline-block; } :where(ui-tooltip, .ui-tooltip) > :where([interestfor], [data-slot~="tooltip-trigger"]) { anchor-name: --ui-tooltip-trigger; } [data-slot~="tooltip-trigger"] { display: inline-flex; } [data-slot~="tooltip-content"] { position-anchor: --ui-tooltip-trigger; display: flex; align-items: center; gap: var(--ui-tooltip-gap); inline-size: max-content; padding-inline: var(--ui-tooltip-padding-inline); padding-block: var(--ui-tooltip-padding-block); border: 1px solid var(--ui-tooltip-border); border-radius: var(--ui-tooltip-radius); box-shadow: var(--ui-tooltip-shadow); font-family: var(--ui-tooltip-font-family); font-size: var(--ui-tooltip-font-size); line-height: var(--ui-tooltip-line-height); font-weight: var(--ui-tooltip-font-weight); text-wrap: balance; } /* Descendant (not child) so keys wrapped in a ui-kbd-group still retint */ [data-slot~="tooltip-content"] :where(kbd) { --ui-kbd-background: color-mix(in oklch, var(--ui-tooltip-foreground) 15%, transparent); --ui-kbd-color: var(--ui-tooltip-foreground); --ui-kbd-min-inline-size: var(--step-4); --ui-kbd-min-block-size: var(--step-4); --ui-kbd-font-size: var(--font-size-xs); } [data-slot~="tooltip-content"]:has([data-slot~="tooltip-arrow"]) { overflow: visible; } /* Positioning: reuse --ui-popover-position-* hooks from popover.css via data-side */ [data-slot~="tooltip-content"][data-side="top"]:not([data-align]), [data-slot~="tooltip-content"][data-side="top"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-start span-all; --ui-popover-origin: bottom center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="bottom"]:not([data-align]), [data-slot~="tooltip-content"][data-side="bottom"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="left"]:not([data-align]), [data-slot~="tooltip-content"][data-side="left"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-start span-all; --ui-popover-origin: center right; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } [data-slot~="tooltip-content"][data-side="right"]:not([data-align]), [data-slot~="tooltip-content"][data-side="right"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-end span-all; --ui-popover-origin: center left; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } /* Tooltip arrow */ [data-slot~="tooltip-arrow"] { position: absolute; top: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--ui-tooltip-arrow-size); block-size: var(--ui-tooltip-arrow-size); background-color: var(--ui-tooltip-arrow-color); border-radius: var(--ui-tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ [data-slot~="tooltip-content"][data-side="top"] [data-slot~="tooltip-arrow"] { bottom: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="bottom"] [data-slot~="tooltip-arrow"] { top: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="left"] [data-slot~="tooltip-arrow"] { left: calc(100% - var(--ui-tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } [data-slot~="tooltip-content"][data-side="right"] [data-slot~="tooltip-arrow"] { right: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}With keyboard shortcut
Loading components…
<ui-tooltip> <button type="button" class="ui-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 data-slot="tooltip-content" id="tooltip-example-save-1" popover="hint" data-use-inverted-menu="false" > Save <ui-kbd-group> <kbd>⌘</kbd> <kbd>S</kbd> </ui-kbd-group> <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div></ui-tooltip>/** * tooltip.css: Tooltip (ui-tooltip | .ui-tooltip, [data-slot~="tooltip-content"]) * * @layer variables, reset, components * @requires layers.css, _variables.css, popover.css, kbd.css (--ui-kbd-*) * @uses interestfor: Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses [data-slot~="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 --ui-popover-position-* * @tokens --ui-tooltip-* (@layer variables) */@layer variables { :root { --ui-tooltip-background: var(--foreground); --ui-tooltip-foreground: var(--background); --ui-tooltip-border: transparent; --ui-tooltip-radius: var(--radius-sm); --ui-tooltip-shadow: var(--shadow-sm); --ui-tooltip-padding-inline: var(--gap-xs); --ui-tooltip-padding-block: var(--step-1); --ui-tooltip-gap: var(--step-1); --ui-tooltip-font-family: var(--font-family-mono); --ui-tooltip-font-size: var(--font-size-xs); --ui-tooltip-line-height: var(--leading-xs); --ui-tooltip-font-weight: var(--font-weight-body); --ui-tooltip-max-inline-size: 16ch; /* Arrow */ --ui-tooltip-arrow-size: var(--step-2); --ui-tooltip-arrow-color: var(--ui-tooltip-background); --ui-tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions: lighter than popover */ --ui-tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --ui-tooltip-transition-timing-function: var(--default-transition-timing-function); --ui-tooltip-transition-delay: 0ms; --ui-tooltip-transition: opacity var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), transform var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), overlay var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete, display var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete; --ui-tooltip-scale--start: 0.94; --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-tooltip-opacity--start: 0; --ui-tooltip-scale--end: 1; --ui-tooltip-translate--end: 0, 0, 0; --ui-tooltip-opacity--end: 1; /* Positioning: smaller offset than popover menus */ --ui-tooltip-margin-block: var(--step-1); --ui-tooltip-margin-inline: 0; /* Popover overrides */ --ui-popover-shadow: var(--shadow-xs); --ui-popover-min-inline-size: fit-content; --ui-popover-position-area: block-start span-all; --ui-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([data-slot~="tooltip-content"]) { color: var(--ui-tooltip-foreground); background-color: var(--ui-tooltip-background); min-inline-size: unset; max-inline-size: var(--ui-tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--ui-popover-origin); opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-tooltip-translate--start)); transition: var(--ui-tooltip-transition); } @supports (anchor-name: --zazz) { :where([data-slot~="tooltip-content"]) { margin-block: var(--ui-tooltip-margin-block); margin-inline: var(--ui-tooltip-margin-inline); } } [data-slot~="tooltip-content"]:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--ui-tooltip-opacity--end); transform: scale(var(--ui-tooltip-scale--end)) translate3d(var(--ui-tooltip-translate--end)); @starting-style { opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-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). =========================================================================== */ :where(ui-tooltip, .ui-tooltip) { anchor-scope: --ui-tooltip-trigger; display: inline-block; } :where(ui-tooltip, .ui-tooltip) > :where([interestfor], [data-slot~="tooltip-trigger"]) { anchor-name: --ui-tooltip-trigger; } [data-slot~="tooltip-trigger"] { display: inline-flex; } [data-slot~="tooltip-content"] { position-anchor: --ui-tooltip-trigger; display: flex; align-items: center; gap: var(--ui-tooltip-gap); inline-size: max-content; padding-inline: var(--ui-tooltip-padding-inline); padding-block: var(--ui-tooltip-padding-block); border: 1px solid var(--ui-tooltip-border); border-radius: var(--ui-tooltip-radius); box-shadow: var(--ui-tooltip-shadow); font-family: var(--ui-tooltip-font-family); font-size: var(--ui-tooltip-font-size); line-height: var(--ui-tooltip-line-height); font-weight: var(--ui-tooltip-font-weight); text-wrap: balance; } /* Descendant (not child) so keys wrapped in a ui-kbd-group still retint */ [data-slot~="tooltip-content"] :where(kbd) { --ui-kbd-background: color-mix(in oklch, var(--ui-tooltip-foreground) 15%, transparent); --ui-kbd-color: var(--ui-tooltip-foreground); --ui-kbd-min-inline-size: var(--step-4); --ui-kbd-min-block-size: var(--step-4); --ui-kbd-font-size: var(--font-size-xs); } [data-slot~="tooltip-content"]:has([data-slot~="tooltip-arrow"]) { overflow: visible; } /* Positioning: reuse --ui-popover-position-* hooks from popover.css via data-side */ [data-slot~="tooltip-content"][data-side="top"]:not([data-align]), [data-slot~="tooltip-content"][data-side="top"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-start span-all; --ui-popover-origin: bottom center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="bottom"]:not([data-align]), [data-slot~="tooltip-content"][data-side="bottom"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="left"]:not([data-align]), [data-slot~="tooltip-content"][data-side="left"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-start span-all; --ui-popover-origin: center right; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } [data-slot~="tooltip-content"][data-side="right"]:not([data-align]), [data-slot~="tooltip-content"][data-side="right"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-end span-all; --ui-popover-origin: center left; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } /* Tooltip arrow */ [data-slot~="tooltip-arrow"] { position: absolute; top: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--ui-tooltip-arrow-size); block-size: var(--ui-tooltip-arrow-size); background-color: var(--ui-tooltip-arrow-color); border-radius: var(--ui-tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ [data-slot~="tooltip-content"][data-side="top"] [data-slot~="tooltip-arrow"] { bottom: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="bottom"] [data-slot~="tooltip-arrow"] { top: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="left"] [data-slot~="tooltip-arrow"] { left: calc(100% - var(--ui-tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } [data-slot~="tooltip-content"][data-side="right"] [data-slot~="tooltip-arrow"] { right: calc(100% - var(--ui-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"> <ui-tooltip> <button type="button" class="ui-button" interestfor="tooltip-example-left-1">Left</button> <div data-slot="tooltip-content" id="tooltip-example-left-1" popover="hint" data-side="left" data-use-inverted-menu="false" > Tooltip on left <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div> </ui-tooltip> <ui-tooltip> <button type="button" class="ui-button" interestfor="tooltip-example-top-1">Top</button> <div data-slot="tooltip-content" id="tooltip-example-top-1" popover="hint" data-side="top" data-use-inverted-menu="false" > Tooltip on top <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div> </ui-tooltip> <ui-tooltip> <button type="button" class="ui-button" interestfor="tooltip-example-bottom-1">Bottom</button> <div data-slot="tooltip-content" id="tooltip-example-bottom-1" popover="hint" data-side="bottom" data-use-inverted-menu="false" > Tooltip on bottom <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div> </ui-tooltip> <ui-tooltip> <button type="button" class="ui-button" interestfor="tooltip-example-right-1">Right</button> <div data-slot="tooltip-content" id="tooltip-example-right-1" popover="hint" data-side="right" data-use-inverted-menu="false" > Tooltip on right <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div> </ui-tooltip></div>/** * tooltip.css: Tooltip (ui-tooltip | .ui-tooltip, [data-slot~="tooltip-content"]) * * @layer variables, reset, components * @requires layers.css, _variables.css, popover.css, kbd.css (--ui-kbd-*) * @uses interestfor: Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses [data-slot~="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 --ui-popover-position-* * @tokens --ui-tooltip-* (@layer variables) */@layer variables { :root { --ui-tooltip-background: var(--foreground); --ui-tooltip-foreground: var(--background); --ui-tooltip-border: transparent; --ui-tooltip-radius: var(--radius-sm); --ui-tooltip-shadow: var(--shadow-sm); --ui-tooltip-padding-inline: var(--gap-xs); --ui-tooltip-padding-block: var(--step-1); --ui-tooltip-gap: var(--step-1); --ui-tooltip-font-family: var(--font-family-mono); --ui-tooltip-font-size: var(--font-size-xs); --ui-tooltip-line-height: var(--leading-xs); --ui-tooltip-font-weight: var(--font-weight-body); --ui-tooltip-max-inline-size: 16ch; /* Arrow */ --ui-tooltip-arrow-size: var(--step-2); --ui-tooltip-arrow-color: var(--ui-tooltip-background); --ui-tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions: lighter than popover */ --ui-tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --ui-tooltip-transition-timing-function: var(--default-transition-timing-function); --ui-tooltip-transition-delay: 0ms; --ui-tooltip-transition: opacity var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), transform var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), overlay var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete, display var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete; --ui-tooltip-scale--start: 0.94; --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-tooltip-opacity--start: 0; --ui-tooltip-scale--end: 1; --ui-tooltip-translate--end: 0, 0, 0; --ui-tooltip-opacity--end: 1; /* Positioning: smaller offset than popover menus */ --ui-tooltip-margin-block: var(--step-1); --ui-tooltip-margin-inline: 0; /* Popover overrides */ --ui-popover-shadow: var(--shadow-xs); --ui-popover-min-inline-size: fit-content; --ui-popover-position-area: block-start span-all; --ui-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([data-slot~="tooltip-content"]) { color: var(--ui-tooltip-foreground); background-color: var(--ui-tooltip-background); min-inline-size: unset; max-inline-size: var(--ui-tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--ui-popover-origin); opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-tooltip-translate--start)); transition: var(--ui-tooltip-transition); } @supports (anchor-name: --zazz) { :where([data-slot~="tooltip-content"]) { margin-block: var(--ui-tooltip-margin-block); margin-inline: var(--ui-tooltip-margin-inline); } } [data-slot~="tooltip-content"]:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--ui-tooltip-opacity--end); transform: scale(var(--ui-tooltip-scale--end)) translate3d(var(--ui-tooltip-translate--end)); @starting-style { opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-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). =========================================================================== */ :where(ui-tooltip, .ui-tooltip) { anchor-scope: --ui-tooltip-trigger; display: inline-block; } :where(ui-tooltip, .ui-tooltip) > :where([interestfor], [data-slot~="tooltip-trigger"]) { anchor-name: --ui-tooltip-trigger; } [data-slot~="tooltip-trigger"] { display: inline-flex; } [data-slot~="tooltip-content"] { position-anchor: --ui-tooltip-trigger; display: flex; align-items: center; gap: var(--ui-tooltip-gap); inline-size: max-content; padding-inline: var(--ui-tooltip-padding-inline); padding-block: var(--ui-tooltip-padding-block); border: 1px solid var(--ui-tooltip-border); border-radius: var(--ui-tooltip-radius); box-shadow: var(--ui-tooltip-shadow); font-family: var(--ui-tooltip-font-family); font-size: var(--ui-tooltip-font-size); line-height: var(--ui-tooltip-line-height); font-weight: var(--ui-tooltip-font-weight); text-wrap: balance; } /* Descendant (not child) so keys wrapped in a ui-kbd-group still retint */ [data-slot~="tooltip-content"] :where(kbd) { --ui-kbd-background: color-mix(in oklch, var(--ui-tooltip-foreground) 15%, transparent); --ui-kbd-color: var(--ui-tooltip-foreground); --ui-kbd-min-inline-size: var(--step-4); --ui-kbd-min-block-size: var(--step-4); --ui-kbd-font-size: var(--font-size-xs); } [data-slot~="tooltip-content"]:has([data-slot~="tooltip-arrow"]) { overflow: visible; } /* Positioning: reuse --ui-popover-position-* hooks from popover.css via data-side */ [data-slot~="tooltip-content"][data-side="top"]:not([data-align]), [data-slot~="tooltip-content"][data-side="top"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-start span-all; --ui-popover-origin: bottom center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="bottom"]:not([data-align]), [data-slot~="tooltip-content"][data-side="bottom"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="left"]:not([data-align]), [data-slot~="tooltip-content"][data-side="left"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-start span-all; --ui-popover-origin: center right; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } [data-slot~="tooltip-content"][data-side="right"]:not([data-align]), [data-slot~="tooltip-content"][data-side="right"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-end span-all; --ui-popover-origin: center left; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } /* Tooltip arrow */ [data-slot~="tooltip-arrow"] { position: absolute; top: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--ui-tooltip-arrow-size); block-size: var(--ui-tooltip-arrow-size); background-color: var(--ui-tooltip-arrow-color); border-radius: var(--ui-tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ [data-slot~="tooltip-content"][data-side="top"] [data-slot~="tooltip-arrow"] { bottom: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="bottom"] [data-slot~="tooltip-arrow"] { top: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="left"] [data-slot~="tooltip-arrow"] { left: calc(100% - var(--ui-tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } [data-slot~="tooltip-content"][data-side="right"] [data-slot~="tooltip-arrow"] { right: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}Disabled trigger
Loading components…
<ui-tooltip> <button type="button" class="ui-button" aria-disabled="true" interestfor="tooltip-example-disabled-1" > Disabled </button> <div data-slot="tooltip-content" id="tooltip-example-disabled-1" popover="hint" data-use-inverted-menu="false" > This action is unavailable <span data-slot="tooltip-arrow" aria-hidden="true"></span> </div></ui-tooltip>/** * tooltip.css: Tooltip (ui-tooltip | .ui-tooltip, [data-slot~="tooltip-content"]) * * @layer variables, reset, components * @requires layers.css, _variables.css, popover.css, kbd.css (--ui-kbd-*) * @uses interestfor: Interest Invokers API (Chromium 142+; polyfill via invokers) * @uses [data-slot~="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 --ui-popover-position-* * @tokens --ui-tooltip-* (@layer variables) */@layer variables { :root { --ui-tooltip-background: var(--foreground); --ui-tooltip-foreground: var(--background); --ui-tooltip-border: transparent; --ui-tooltip-radius: var(--radius-sm); --ui-tooltip-shadow: var(--shadow-sm); --ui-tooltip-padding-inline: var(--gap-xs); --ui-tooltip-padding-block: var(--step-1); --ui-tooltip-gap: var(--step-1); --ui-tooltip-font-family: var(--font-family-mono); --ui-tooltip-font-size: var(--font-size-xs); --ui-tooltip-line-height: var(--leading-xs); --ui-tooltip-font-weight: var(--font-weight-body); --ui-tooltip-max-inline-size: 16ch; /* Arrow */ --ui-tooltip-arrow-size: var(--step-2); --ui-tooltip-arrow-color: var(--ui-tooltip-background); --ui-tooltip-arrow-radius: var(--step-0_5); /* Reveal transitions: lighter than popover */ --ui-tooltip-transition-duration: calc(var(--default-transition-duration) / 2); --ui-tooltip-transition-timing-function: var(--default-transition-timing-function); --ui-tooltip-transition-delay: 0ms; --ui-tooltip-transition: opacity var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), transform var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay), overlay var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete, display var(--ui-tooltip-transition-duration) var(--ui-tooltip-transition-timing-function) var(--ui-tooltip-transition-delay) allow-discrete; --ui-tooltip-scale--start: 0.94; --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-tooltip-opacity--start: 0; --ui-tooltip-scale--end: 1; --ui-tooltip-translate--end: 0, 0, 0; --ui-tooltip-opacity--end: 1; /* Positioning: smaller offset than popover menus */ --ui-tooltip-margin-block: var(--step-1); --ui-tooltip-margin-inline: 0; /* Popover overrides */ --ui-popover-shadow: var(--shadow-xs); --ui-popover-min-inline-size: fit-content; --ui-popover-position-area: block-start span-all; --ui-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([data-slot~="tooltip-content"]) { color: var(--ui-tooltip-foreground); background-color: var(--ui-tooltip-background); min-inline-size: unset; max-inline-size: var(--ui-tooltip-max-inline-size); backdrop-filter: none; transform-origin: var(--ui-popover-origin); opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-tooltip-translate--start)); transition: var(--ui-tooltip-transition); } @supports (anchor-name: --zazz) { :where([data-slot~="tooltip-content"]) { margin-block: var(--ui-tooltip-margin-block); margin-inline: var(--ui-tooltip-margin-inline); } } [data-slot~="tooltip-content"]:where(:popover-open, .\:popover-open) { display: flex; opacity: var(--ui-tooltip-opacity--end); transform: scale(var(--ui-tooltip-scale--end)) translate3d(var(--ui-tooltip-translate--end)); @starting-style { opacity: var(--ui-tooltip-opacity--start); transform: scale(var(--ui-tooltip-scale--start)) translate3d(var(--ui-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). =========================================================================== */ :where(ui-tooltip, .ui-tooltip) { anchor-scope: --ui-tooltip-trigger; display: inline-block; } :where(ui-tooltip, .ui-tooltip) > :where([interestfor], [data-slot~="tooltip-trigger"]) { anchor-name: --ui-tooltip-trigger; } [data-slot~="tooltip-trigger"] { display: inline-flex; } [data-slot~="tooltip-content"] { position-anchor: --ui-tooltip-trigger; display: flex; align-items: center; gap: var(--ui-tooltip-gap); inline-size: max-content; padding-inline: var(--ui-tooltip-padding-inline); padding-block: var(--ui-tooltip-padding-block); border: 1px solid var(--ui-tooltip-border); border-radius: var(--ui-tooltip-radius); box-shadow: var(--ui-tooltip-shadow); font-family: var(--ui-tooltip-font-family); font-size: var(--ui-tooltip-font-size); line-height: var(--ui-tooltip-line-height); font-weight: var(--ui-tooltip-font-weight); text-wrap: balance; } /* Descendant (not child) so keys wrapped in a ui-kbd-group still retint */ [data-slot~="tooltip-content"] :where(kbd) { --ui-kbd-background: color-mix(in oklch, var(--ui-tooltip-foreground) 15%, transparent); --ui-kbd-color: var(--ui-tooltip-foreground); --ui-kbd-min-inline-size: var(--step-4); --ui-kbd-min-block-size: var(--step-4); --ui-kbd-font-size: var(--font-size-xs); } [data-slot~="tooltip-content"]:has([data-slot~="tooltip-arrow"]) { overflow: visible; } /* Positioning: reuse --ui-popover-position-* hooks from popover.css via data-side */ [data-slot~="tooltip-content"][data-side="top"]:not([data-align]), [data-slot~="tooltip-content"][data-side="top"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-start span-all; --ui-popover-origin: bottom center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="bottom"]:not([data-align]), [data-slot~="tooltip-content"][data-side="bottom"][data-align="center"] { --ui-tooltip-translate--start: 0, 0.25em, 0; --ui-popover-position-area: block-end span-all; --ui-popover-origin: top center; --ui-tooltip-margin-block: var(--step-1); } [data-slot~="tooltip-content"][data-side="left"]:not([data-align]), [data-slot~="tooltip-content"][data-side="left"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-start span-all; --ui-popover-origin: center right; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } [data-slot~="tooltip-content"][data-side="right"]:not([data-align]), [data-slot~="tooltip-content"][data-side="right"][data-align="center"] { --ui-tooltip-translate--start: 0.25em, 0, 0; --ui-popover-position-area: inline-end span-all; --ui-popover-origin: center left; --ui-tooltip-margin-inline: var(--step-1); --ui-tooltip-margin-block: 0; } /* Tooltip arrow */ [data-slot~="tooltip-arrow"] { position: absolute; top: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: 50%; transform: translate(-50%, 0%) rotate(45deg); width: var(--ui-tooltip-arrow-size); block-size: var(--ui-tooltip-arrow-size); background-color: var(--ui-tooltip-arrow-color); border-radius: var(--ui-tooltip-arrow-radius); pointer-events: none; } /* Tooltip arrow position */ [data-slot~="tooltip-content"][data-side="top"] [data-slot~="tooltip-arrow"] { bottom: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="bottom"] [data-slot~="tooltip-arrow"] { top: calc((var(--ui-tooltip-arrow-size) / 2) * -1); left: 50%; } [data-slot~="tooltip-content"][data-side="left"] [data-slot~="tooltip-arrow"] { left: calc(100% - var(--ui-tooltip-arrow-size) / 2); top: 50%; transform: translate(0%, -50%) rotate(45deg); } [data-slot~="tooltip-content"][data-side="right"] [data-slot~="tooltip-arrow"] { right: calc(100% - var(--ui-tooltip-arrow-size) / 2); left: auto; top: 50%; transform: translate(0%, -50%) rotate(45deg); }}API
| Attribute | Target | Values |
|---|---|---|
interestfor | Trigger | ID of [data-slot="tooltip-content"] |
data-side | [data-slot="tooltip-content"] | top, bottom, left, right |
data-align | [data-slot="tooltip-content"] | center |
data-use-inverted-menu | [data-slot="tooltip-content"] | false |