vue-afloat

Tooltips, popovers, dropdown, menus... for Vue
1,676
Install
npm install vue-afloat
Latest Version:0.2.0
Vue:3
License:MIT
Last Updated:Sep 21, 2026
Links: npm
Maintainer: antfu

vue-afloat

npm version npm downloads bundle License

Tooltips, dropdowns and menus for Vue, powered by Floating UI.

A slimmed-down port of floating-vue, with modern build, CSS-vars-based styling system, ESM-only and Composition API.

Install

pnpm add vue-afloat

Usage

Register the plugin and import the stylesheet once:

import { createApp } from 'vue'
import FloatingVue from 'vue-afloat'
import App from './App.vue'
import 'vue-afloat/style.css'

createApp(App).use(FloatingVue).mount('#app')

Tooltip directive

<template>
  <button v-tooltip="'Hello tooltip'">
    Hover me
  </button>

  <!-- placement via modifier, and HTML content -->
  <button v-tooltip.bottom="{ content: '<b>Rich</b> tooltip', html: true }">
    Hover me
  </button>
</template>

Components

VTooltip, VDropdown and VMenu share the same shape: a default slot for the trigger and a #popper slot for the floating content. Use v-close-popper inside a dropdown to close it.

<template>
  <VDropdown>
    <button>Open</button>
    <template #popper>
      <MyMenu />
      <button v-close-popper>
        Close
      </button>
    </template>
  </VDropdown>

  <VMenu placement="right-start">
    <button>File</button>
    <template #popper>
      <VMenu placement="right-start">
        <button>Export ›</button>
        <template #popper>
          …
        </template>
      </VMenu>
    </template>
  </VMenu>
</template>

Shadow DOM and custom elements

Inside an open shadow root, components and v-tooltip keep their floating content in that root by default. Include the stylesheet in the shadow root, since a document-level import cannot style its contents. With Vue's defineCustomElement and Vite:

import { defineCustomElement, h } from 'vue'
import { Dropdown } from 'vue-afloat'
import styles from 'vue-afloat/style.css?inline'

const MyDropdown = defineCustomElement({
  styles: [styles],
  setup() {
    return () => h(Dropdown, {}, {
      default: () => h('button', 'Open'),
      popper: () => h('div', 'Content inside the shadow root'),
    })
  },
})

customElements.define('my-dropdown', MyDropdown)

The stylesheet defines defaults on :host as well as :root. Add your own :host rules after it, or set --vue-afloat-* properties on the custom element, to customize each instance without injecting styles into the document.

The default container: 'body' uses the reference element's shadow root when present, otherwise the document body. Other container selectors resolve only within that root. You can also pass an HTMLElement or ShadowRoot directly, or use container: false to keep content beside its trigger. Passing document.body explicitly moves content outside the shadow root and requires document-level styles.

Styling

Tooltip, dropdown and menu share one default look that adapts to light/dark via prefers-color-scheme. Import the stylesheet and override the --vue-afloat-* custom properties globally, per type, or per instance. No theme registry.

/* global */
:root {
  --vue-afloat-border-radius: 12px;
}

/* per type: .vue-afloat-{tooltip,dropdown,menu} */
.vue-afloat-tooltip {
  --vue-afloat-background: #18181b;
  --vue-afloat-color: #fff;
}

/* per instance: pass popper-class="glass" */
.vue-afloat-tooltip.glass {
  --vue-afloat-background: rgb(255 255 255 / 0.55);
  --vue-afloat-backdrop-blur: 12px;
}

CSS classes

Class Applied to Purpose
.vue-afloat reference wrapper, floating popper base block
.vue-afloat--shown / .vue-afloat--hidden reference wrapper, floating popper current visibility state
.vue-afloat--show-from / .vue-afloat--show-to floating popper present while the show transition runs
.vue-afloat--hide-from / .vue-afloat--hide-to floating popper present while the hide transition runs
.vue-afloat--skip-transition floating popper disables the opacity/visibility transition
.vue-afloat--top / --bottom / --left / --right floating popper resolved placement side (replaces [data-popper-placement])
.vue-afloat--no-arrow floating popper applied automatically by the no-arrow prop; hides the arrow
.vue-afloat--has-tooltip trigger element (v-tooltip) marks an element with an attached tooltip
.vue-afloat--tooltip-loading floating popper (VTooltip/v-tooltip only) present while async tooltip content is loading
.vue-afloat--some-open <body> or shadow host present while at least one popper in that root is open
.vue-afloat-tooltip / .vue-afloat-dropdown / .vue-afloat-menu floating popper per-component hook, override tokens here to diverge per type
.vue-afloat__backdrop floating popper (child) full-size click-catcher used by autoHide
.vue-afloat__wrapper floating popper (child) wraps the content and the arrow
.vue-afloat__inner floating popper (child) scrollable box holding your slot content
.vue-afloat__arrow-container floating popper (child) positions the arrow
.vue-afloat__arrow-inner / .vue-afloat__arrow-outer floating popper (child) arrow fill and outline layers

CSS variables

Variable Default (light / dark) Used for
--vue-afloat-background #fff / #1e1e24 content background, and the arrow fill fallback
--vue-afloat-color #1f2933 / rgb(255 255 255 / 0.92) text color
--vue-afloat-border-color rgb(0 0 0 / 0.1) / rgb(255 255 255 / 0.14) border, and the arrow outline fallback
--vue-afloat-border-width 1px content border width
--vue-afloat-border-radius 8px content corner radius
--vue-afloat-box-shadow 0 4px 24px rgb(0 0 0 / 0.12) / 0 4px 24px rgb(0 0 0 / 0.5) drop shadow
--vue-afloat-padding 6px 10px content padding
--vue-afloat-backdrop-blur 0px backdrop-filter: blur() on the content
--vue-afloat-arrow-size 6px arrow border-width
--vue-afloat-arrow-color unset (falls back to --vue-afloat-background) arrow fill, set to diverge from the content background
--vue-afloat-arrow-border-color unset (falls back to --vue-afloat-border-color) arrow outline, set to diverge from the content border
--vue-afloat-z-index 10000 floating popper z-index
--vue-afloat-transition-duration 0.15s show/hide transition duration

Global config

Pass defaults to the plugin, or mutate the exported options object:

app.use(FloatingVue, {
  distance: 8,
  disposeTimeout: 150,
  autoHideOnMousedown: false,
})

Options reference

Option Default Description
disabled false Disable all poppers
placement 'bottom' Preferred placement relative to the reference element
distance 5 Position offset along the main axis (px)
skidding 0 Position offset along the cross axis (px)
container 'body' Container element, shadow root, root-scoped selector, or false for the trigger's parent. The default uses the reference's shadow root when present.
boundary undefined Element used to compute position and size boundaries
instantMove false Skip delay & CSS transitions when moving from one popper to another
disposeTimeout 150 Auto destroy popper DOM nodes after hiding (ms)
popperTriggers [] Triggers on the popper itself
strategy 'absolute' Positioning strategy ('absolute' or 'fixed')
preventOverflow true Prevent overflow
flip true Flip to the opposite placement if needed
shift true Shift on the cross axis to prevent overflow
shiftCrossAxis false Also shift on the cross axis (perpendicular to the placement)
overflowPadding 0 Overflow padding (px)
arrowPadding 0 Arrow padding (px)
noArrow false Hide the arrow
autoHideOnMousedown false Compute auto hide on mousedown instead of click

API

  • Components: Tooltip (VTooltip), Dropdown (VDropdown), Menu (VMenu)
  • Directives: vTooltip, vClosePopper
  • Directive helpers: createTooltip, destroyTooltip
  • Low-level: createPopper (renderless factory) + PopperContent for custom popovers, createAfloatComponent for building your own Tooltip/Dropdown/Menu-shaped component
  • Globals: hideAllPoppers, recomputeAllPoppers, placements, options
  • Types: Placement, TriggerEvent, FloatingVueConfig

Creating your own component

Tooltip, Dropdown and Menu are all built with createAfloatComponent, which bakes in default props and a CSS class for a createPopper + PopperContent pair. Use it to define your own variant with different defaults:

import { createAfloatComponent } from 'vue-afloat'

const VDropdown = createAfloatComponent('VDropdown', 'vue-afloat-dropdown', {
  placement: 'bottom',
  triggers: () => ['click'],
  delay: 0,
  handleResize: true,
  autoHide: true,
})

The second argument is the class applied to the floating popper (see CSS classes); the third is a map of prop defaults, applied on top of the standard popper props.

Migrate from floating-vue

The main usage (the plugin, the three components, v-tooltip and v-close-popper) is unchanged. The differences:

  • Imports: floating-vue → vue-afloat, and floating-vue/dist/style.css → vue-afloat/style.css.
  • CSS classes renamed: v-popper* → vue-afloat* throughout (e.g. v-popper__inner → vue-afloat__inner, v-popper--theme-tooltip → vue-afloat-tooltip). See the CSS classes table above for the full mapping.
  • Theme system removed: there is no config.themes, no theme prop, and no getThemeClasses. The built-in components keep their look through the same .vue-afloat-* classes; customise with the --vue-afloat-* CSS variables instead of registering themes.
  • Deprecated APIs removed: use vTooltip/vClosePopper (not the VTooltip/VClosePopper constants); the PopperMethods/ThemeClass mixins are gone (use the Composition API); autoMinSize → autoSize="min" and autoMaxSize → autoBoundaryMaxSize.
  • Dropped: the Nuxt module, the UMD build (ESM-only now), the SHOW_EVENT_MAP/HIDE_EVENT_MAP exports, and the rarely-used props autoSize, autoBoundaryMaxSize, arrowOverflow, computeTransformOrigin, showGroup and positioningDisabled.

Credits

Forked from floating-vue by Guillaume Chau. All credit for the original design and API goes to the floating-vue authors and contributors.

License

MIT License © Anthony Fu MIT License © Guillaume Chau

Related Packages

floating-ui

Easy Tooltips, dropdowns, menus & popovers using floating-ui

165,538
floating-vue

Easy Vue tooltips, dropdowns, menus & popovers using floating-ui

25,513,569 3,458
@novu/floating-vue

Easy Vue tooltips, dropdowns, menus & popovers using floating-ui

75,451 3,460
floating-vue2

Tooltips, dropdowns and popovers using floating-ui

1,672 0
@dolusoft/floating-vue

Dolusoft fork of floating-vue: Vue 3.5 compiler build + dispose() retainer fix....

1,089 0

Version History

Version Released License
0.2.0 MIT
0.1.1 MIT
0.1.0 MIT
0.0.0 MIT