Components
Tooltip
Built on top of Anchor.
Full support Supported since v144. Full support Supported since v151. Partial support
Missing:
popover-hint.
Wrap the trigger in <Tooltip> and pass a stable
id. Set interestfor, commandfor,
and command="toggle-popover" on the trigger element itself (these
attributes are only valid on real invokers like
<button> or <a>). Pass a
label prop for plain text or use the content slot
for richer markup.
Basics
Text only...
Pass plain text via the label prop.
Save your changes
<script setup lang="ts">import { Button, Tooltip } from "opui-css/vue"</script>
<template> <Tooltip label="Save your changes" id="tooltip-basic"> <Button interestfor="tooltip-basic" commandfor="tooltip-basic" command="toggle-popover" >Save</Button > </Tooltip></template><span class="ui-anchor ui-tooltip" style="" id="tooltip-basic" ><span interestfor="s0-0" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-basic" commandfor="tooltip-basic" command="toggle-popover" > <!--[-->Save<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s0-0" popover="hint" ><!--[-->Save your changes <!--[--><!--]--><!--]--></span ></span>... or any markup you want
Use the content slot instead, and it let's you put anything
in the tooltip.
Press ⌘ + K to open the command palette.
<script setup lang="ts">import { Button, Tooltip } from "opui-css/vue"</script>
<template> <Tooltip id="tooltip-rich"> <Button interestfor="tooltip-rich" commandfor="tooltip-rich" command="toggle-popover" >Keyboard shortcuts</Button > <template #content> Press <kbd>⌘</kbd> + <kbd>K</kbd> to open the command palette. </template> </Tooltip></template><span class="ui-anchor ui-tooltip" style="" id="tooltip-rich" ><span interestfor="s1-0" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-rich" commandfor="tooltip-rich" command="toggle-popover" > <!--[-->Keyboard shortcuts<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s1-0" popover="hint" ><!--[--> <!--[--> Press <kbd>⌘</kbd> + <kbd>K</kbd> to open the command palette. <!--]--><!--]--></span ></span>Alignment
Use the alignment prop.
Above
Before
After
Below
<script setup lang="ts">import { Button, Tooltip } from "opui-css/vue"</script>
<template> <div class="tooltip-alignment-grid"> <Tooltip label="Above" alignment="block-start" id="tooltip-top"> <Button interestfor="tooltip-top" commandfor="tooltip-top" command="toggle-popover" >Top</Button > </Tooltip> <Tooltip label="Before" alignment="inline-start" id="tooltip-start"> <Button interestfor="tooltip-start" commandfor="tooltip-start" command="toggle-popover" >Start</Button > </Tooltip> <Tooltip label="After" alignment="inline-end" id="tooltip-end"> <Button interestfor="tooltip-end" commandfor="tooltip-end" command="toggle-popover" >End</Button > </Tooltip> <Tooltip label="Below" alignment="block-end" id="tooltip-bottom"> <Button interestfor="tooltip-bottom" commandfor="tooltip-bottom" command="toggle-popover" >Bottom</Button > </Tooltip> </div>
<style> .tooltip-alignment-grid { display: grid; gap: var(--size-3); grid-template-areas: ". top . " "start . end" ". bottom . "; justify-items: center; align-items: center; }
.tooltip-alignment-grid > :nth-child(1) { grid-area: top; } .tooltip-alignment-grid > :nth-child(2) { grid-area: start; } .tooltip-alignment-grid > :nth-child(3) { grid-area: end; } .tooltip-alignment-grid > :nth-child(4) { grid-area: bottom; } </style></template><!--[--><div class="tooltip-alignment-grid"> <span class="ui-anchor ui-tooltip" style="--anchor-position-area: block-start" id="tooltip-top" ><span interestfor="s2-0" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-top" commandfor="tooltip-top" command="toggle-popover" > <!--[-->Top<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s2-0" popover="hint" ><!--[-->Above <!--[--><!--]--><!--]--></span ></span ><span class="ui-anchor ui-tooltip" style="--anchor-position-area: inline-start" id="tooltip-start" ><span interestfor="s2-1" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-start" commandfor="tooltip-start" command="toggle-popover" > <!--[-->Start<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s2-1" popover="hint" ><!--[-->Before <!--[--><!--]--><!--]--></span ></span ><span class="ui-anchor ui-tooltip" style="--anchor-position-area: inline-end" id="tooltip-end" ><span interestfor="s2-2" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-end" commandfor="tooltip-end" command="toggle-popover" > <!--[-->End<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s2-2" popover="hint" ><!--[-->After <!--[--><!--]--><!--]--></span ></span ><span class="ui-anchor ui-tooltip" style="--anchor-position-area: block-end" id="tooltip-bottom" ><span interestfor="s2-3" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-bottom" commandfor="tooltip-bottom" command="toggle-popover" > <!--[-->Bottom<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s2-3" popover="hint" ><!--[-->Below <!--[--><!--]--><!--]--></span ></span ></div><style> .tooltip-alignment-grid { display: grid; gap: var(--size-3); grid-template-areas: ". top . " "start . end" ". bottom . "; justify-items: center; align-items: center; }
.tooltip-alignment-grid > :nth-child(1) { grid-area: top; } .tooltip-alignment-grid > :nth-child(2) { grid-area: start; } .tooltip-alignment-grid > :nth-child(3) { grid-area: end; } .tooltip-alignment-grid > :nth-child(4) { grid-area: bottom; }</style><!--]-->Arrow
Set the arrow prop. This would be cool to solve with corner-shape
one day.
Save your changes
<script setup lang="ts">import { Button, Tooltip } from "opui-css/vue"</script>
<template> <Tooltip arrow label="Save your changes" id="tooltip-arrow"> <Button interestfor="tooltip-arrow" commandfor="tooltip-arrow" command="toggle-popover" >Save</Button > </Tooltip></template><span class="ui-anchor ui-tooltip ui-with-arrow" style="" id="tooltip-arrow" ><span interestfor="s3-0" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-arrow" commandfor="tooltip-arrow" command="toggle-popover" > <!--[-->Save<!--]--></button ><!--]--><!--]--></span ><span class="ui-anchor-floating" id="s3-0" popover="hint" ><!--[-->Save your changes <!--[--><!--]--><!--]--></span ></span>API
| Prop | Type | Default | Description |
|---|---|---|---|
alignment | string | "block-start" | Any valid position-area value. Controls where the tooltip is
placed relative to the trigger. |
arrow | boolean | false | When true, renders an arrow pointing from the tooltip
toward the trigger. The arrow flips automatically with the tooltip via
position-try-fallbacks. |
id | string | auto-generated | Identifier used to wire interestfor on the trigger to the floating
element. |
label | string | — | Plain-text tooltip content. For richer markup, use the content slot instead. |
Browser support
Full support Supported since v144. Full support Supported since v151. Partial support
Missing:
popover-hint.
See also the full browser support guide.
Installation
@layer components.extended { :where(.ui-tooltip) { --_tooltip-bg: light-dark(var(--gray-13), var(--gray-3)); --_tooltip-color: light-dark(var(--gray-1), var(--gray-15)); --_tooltip-offset: var(--size-2);
& > .ui-anchor-floating { background-color: var(--_tooltip-bg); border-radius: var(--radius-2); color: var(--_tooltip-color); font-size: var(--font-size-05); line-height: 1.3; margin: var(--_tooltip-offset); max-inline-size: 240px; /* UA sets overflow: auto on [popover], which clips the arrow. */ overflow: visible; padding-block: var(--size-1); padding-inline: var(--size-2); position-area: var(--anchor-position-area, block-start); position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline; position-visibility: anchors-visible; text-align: center; text-wrap: pretty; }
/* Optional arrow pointing from the tooltip toward the trigger. Anchored to the same --anchor so it flips together with the tooltip when position-try-fallbacks kicks in. */ &.ui-with-arrow { --_arrow-size: var(--size-2);
& > .ui-anchor-floating { /* Reserve space so the arrow doesn't overlap the trigger. */ --_tooltip-offset: calc(var(--size-2) + var(--_arrow-size) / 2); }
& > .ui-anchor-floating::before { background-color: var(--_tooltip-bg); block-size: var(--_arrow-size); content: ""; inline-size: var(--_arrow-size); inset-block-end: calc(var(--_arrow-size) / -2); inset-inline-start: 50%; position: absolute; rotate: 45deg; translate: -50% 0; } }
/* Fade */ & > .ui-anchor-floating[popover] { opacity: 0; transition: display 0.15s allow-discrete, opacity 0.15s var(--ease-out-1), overlay 0.15s allow-discrete;
&:popover-open { opacity: 1;
@starting-style { opacity: 0; } } } }}@layer components.root { :where(.ui-anchor) { anchor-name: --anchor; anchor-scope: --anchor; display: inline-block; inline-size: fit-content;
/* Always-visible */ & > .ui-anchor-floating:not([popover]) { block-size: max-content; inline-size: max-content; position: absolute; position-anchor: --anchor; position-area: var(--anchor-position-area, start end); translate: var(--_anchor-tx, 0) var(--_anchor-ty, 0); }
/* Hover-triggered */ & > .ui-anchor-floating[popover] { inset: unset; margin: 0; position: absolute; position-area: var(--anchor-position-area, start end); translate: var(--_anchor-tx, 0) var(--_anchor-ty, 0); } }}