Client point
Open the live client-point demo
Source for this preview
<article id="client-point-demo" class="demo-card client-point-card"> <div class="card-top"> <span class="number">D</span><span class="chip">virtual reference</span> </div> <h3>Cursor signal</h3> <p> Move across the field. A virtual reference follows the pointer instead of the whole element. </p> <div class="card-action"> <floating-root id="client-point-root" placement="top"> <floating-reference> <div class="cursor-field" tabindex="0"> <span id="pointer-label">Awaiting pointer</span> <i aria-hidden="true"></i><i aria-hidden="true"></i> <i aria-hidden="true"></i> </div> </floating-reference> <template slot="content"> <div class="cursor-tooltip">Pointer is the <b>reference</b></div> </template> </floating-root> </div> <code><floating-root> + clientPoint()</code></article>
<script> import { FloatingRootElement, clientPoint, dismiss, flip, hover, offset, role, shift, transformOrigin, } from '@floating-ui-plus/web-components'; import {initializeExample} from './initialize-example';
initializeExample('client-point', (scope) => { const floating = FloatingRootElement.query( scope, '#client-point-root', ); const field = scope.querySelector<HTMLElement>('.cursor-field'); const label = scope.querySelector<HTMLElement>('#pointer-label'); if (!field || !label) return;
floating.configure({ middleware: [ offset(16), flip(), shift({padding: 18}), transformOrigin({padding: 8}), ], plugins: [ hover({move: true}), clientPoint(), dismiss(), role({role: 'tooltip'}), ], });
let frame: number | null = null; let nextLabel = label.textContent ?? ''; field.addEventListener('mousemove', (event) => { const bounds = field.getBoundingClientRect(); nextLabel = `${Math.round(event.clientX - bounds.left)} × ${Math.round(event.clientY - bounds.top)}`; if (frame != null) return; frame = requestAnimationFrame(() => { frame = null; label.textContent = nextLabel; }); }); });</script><script setup lang="ts">import { FloatingContent, FloatingReference, FloatingRoot, autoUpdate, clientPoint, dismiss, flip, hover, offset, role, shift, transformOrigin,} from '@floating-ui-plus/vue';import {ref} from 'vue';
const open = ref(false);const label = ref('Awaiting pointer');const options = { placement: 'top', middleware: [ offset(16), flip(), shift({padding: 18}), transformOrigin({padding: 8}), ], whileElementsMounted: autoUpdate,} as const;const plugins = [ hover({move: true}), clientPoint(), dismiss(), role({role: 'tooltip'}),];
function track(event: MouseEvent) { const rect = (event.currentTarget as HTMLElement).getBoundingClientRect(); label.value = `${Math.round(event.clientX - rect.left)} × ${Math.round(event.clientY - rect.top)}`;}</script>
<template> <article class="vue-demo-card bg-vue-sand"> <div class="vue-card-top"><span class="vue-number">D</span><span>virtual reference</span></div> <h3>Cursor signal</h3> <p>A virtual reference follows the pointer instead of anchoring the tooltip to the whole field.</p> <div class="mt-auto pt-7"> <FloatingRoot v-model:open="open" :options="options" :plugins="plugins"> <FloatingReference as="div" class="vue-cursor-field" tabindex="0" @mousemove="track" > <i /><i /><i /><span>{{ label }}</span> </FloatingReference> <FloatingContent class="cursor-tooltip"> Pointer is the <b>reference</b> </FloatingContent> </FloatingRoot> </div> <code>FloatingReference → clientPoint()</code> </article></template>clientPoint() turns the pointer into a virtual reference. Combine it with
hover({move: true}) to follow the cursor:
floating.configure({ middleware: [offset(16), flip(), shift({padding: 18})], plugins: [ hover({move: true}), clientPoint(), dismiss(), role({role: 'tooltip'}), ],});The demo’s coordinate readout is application code updated with
requestAnimationFrame; Floating UI Plus only owns the virtual reference,
position, and interaction lifecycle. Use this pattern for cursor annotations,
context hints, and canvas overlays. The Vue adapter accepts the same
clientPoint() plugin through useFloating() or FloatingRoot.