Components
Tooltip
Built on top of Anchor.
Full supportSupported since v144.Full supportSupported since v151.Partial supportMissing:
popover-hint.Wrap the trigger in <Tooltip> and pass a stableid. 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 alabel 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" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-basic" commandfor="tooltip-basic" command="toggle-popover" > <!--[-->Save<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-basic" 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" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-rich" commandfor="tooltip-rich" command="toggle-popover" > <!--[-->Keyboard shortcuts<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-rich" 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" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-top" commandfor="tooltip-top" command="toggle-popover" > <!--[-->Top<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-top" popover="hint" ><!--[-->Above <!--[--><!--]--><!--]--></span ></span ><span class="ui-anchor ui-tooltip" style="--anchor-position-area: inline-start" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-start" commandfor="tooltip-start" command="toggle-popover" > <!--[-->Start<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-start" popover="hint" ><!--[-->Before <!--[--><!--]--><!--]--></span ></span ><span class="ui-anchor ui-tooltip" style="--anchor-position-area: inline-end" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-end" commandfor="tooltip-end" command="toggle-popover" > <!--[-->End<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-end" popover="hint" ><!--[-->After <!--[--><!--]--><!--]--></span ></span ><span class="ui-anchor ui-tooltip" style="--anchor-position-area: block-end" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-bottom" commandfor="tooltip-bottom" command="toggle-popover" > <!--[-->Bottom<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-bottom" 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-shapeone 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" ><!--[--><!--[--><button class="ui-button" interestfor="tooltip-arrow" commandfor="tooltip-arrow" command="toggle-popover" > <!--[-->Save<!--]--></button ><!--]--><!--]--><span class="ui-anchor-floating" id="tooltip-arrow" popover="hint" ><!--[-->Save your changes <!--[--><!--]--><!--]--></span ></span>API
Tooltip API
| Prop | Type | Default | Description |
|---|---|---|---|
alignment | string | "block-start" | Any valid position-area value. Controls where the tooltip is placed. |
arrow | boolean | false | Adds an arrow that points to the trigger. |
id | string | - | The id of the tooltip. Add interestfor with the same id to the trigger. |
label | string | - | The tooltip, a popover="hint". |
Slots
| Slot | Description |
|---|---|
content | The tooltip, a popover="hint". |
default | The trigger that shows the tooltip on hover and focus. |
Browser support
Full supportSupported since v144.Full supportSupported since v151.Partial supportMissing:
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; }
/* Arrow */ &.ui-with-arrow { --_arrow-size: var(--size-2);
& > .ui-anchor-floating { /* Offset 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); } }}