Headless floating action button and speed-dial menu (WAI-ARIA menu button pattern).
|
|
| Package |
@vizejs/ui/floating-action-button |
| Maturity |
stable |
| Own the source |
vize lib pull floating-action-button |
| Requires |
collection, composite-navigation, context, controllable-state, dismissable-layer, id |
| Aliases |
fab, floating action button, speed dial, float button |
| Covers |
Material FAB, Material Speed Dial, WAI-ARIA Menu Button, Ant Design FloatButton |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { FloatingActionButton, SpeedDial, SpeedDialRoot, SpeedDialAction, SpeedDialContent, SpeedDialTrigger } from "@vizejs/ui/floating-action-button";
Or copy the source into your project with vize lib pull floating-action-button (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Extended floating action button with visible text that counts how many drafts it has started. -->
<script setup lang="ts">
import { ref } from "vue";
import { FloatingActionButton } from "@vizejs/ui/floating-action-button";
const drafts = ref(0);
function startDraft(): void {
drafts.value += 1;
}
</script>
<template>
<div>
<FloatingActionButton placement="bottom-end" extended @click="startDraft">
Compose
</FloatingActionButton>
<output>Drafts started: {{ drafts }}</output>
</div>
</template>
API
Source: src/families/actions/floating-action-button/floating-action-button.vue
Props
| Prop |
Type |
Default |
Description |
placement |
FloatingActionButtonPlacement |
"bottom-end" |
Viewport corner or edge the consumer anchors the button to, mirrored to data-placement. |
extended |
boolean |
false |
Whether the button shows a visible text label next to its icon. |
disabled |
boolean |
false |
Remove the button from activation and sequential focus. |
ariaLabel |
string |
undefined |
Accessible name. Required in practice for icon-only buttons. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired when the button is activated. |
Slots
| Slot |
Slot props |
Description |
default |
FloatingActionButtonSlotState |
Icon and optional label. Receives placement and extended state. |
Exposed
| Member |
Type |
Description |
element |
HTMLButtonElement | null |
Rendered native button. |
focus |
(options?: FocusOptions) => void |
Move focus to the button. |
SpeedDial
Source: src/families/actions/floating-action-button/speed-dial-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
direction |
SpeedDialDirection |
"up" |
Direction the actions fan out; also selects the arrow keys that move between actions. |
disabled |
boolean |
false |
Disable opening and every action. |
openOnHover |
boolean |
false |
Open while a mouse or pen hovers the speed dial and close when it leaves. |
closeOnSelect |
boolean |
true |
Close and return focus to the trigger after an action is selected. |
loop |
boolean |
true |
Whether arrow-key navigation wraps at the first and last action. |
Events
| Event |
Payload |
Description |
update:open |
[value: boolean] |
Fired when the speed dial requests a controlled open value. |
open-change |
[value: boolean, reason: SpeedDialChangeReason, nativeEvent: Event | null] |
Fired after any distinct open-state request, with its cause. |
select |
[event: SpeedDialSelectEvent] |
Fired when an action is selected. Call preventDefault() to keep the speed dial open. |
Slots
| Slot |
Slot props |
Description |
default |
SpeedDialSlotState |
Trigger and content. Receives the open state and direction. |
Exposed
| Member |
Type |
Description |
close |
— |
|
contentId |
— |
|
direction |
— |
|
disabled |
— |
|
id |
— |
|
open |
— |
|
openAndFocus |
— |
|
setOpen |
— |
|
state |
— |
|
triggerId |
— |
|
SpeedDialRoot
Source: src/families/actions/floating-action-button/speed-dial-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
open |
boolean |
undefined |
Controlled open state. undefined selects uncontrolled behavior. |
defaultOpen |
boolean |
false |
Initial open state for uncontrolled use. |
direction |
SpeedDialDirection |
"up" |
Direction the actions fan out; also selects the arrow keys that move between actions. |
disabled |
boolean |
false |
Disable opening and every action. |
openOnHover |
boolean |
false |
Open while a mouse or pen hovers the speed dial and close when it leaves. |
closeOnSelect |
boolean |
true |
Close and return focus to the trigger after an action is selected. |
loop |
boolean |
true |
Whether arrow-key navigation wraps at the first and last action. |
Events
| Event |
Payload |
Description |
update:open |
[value: boolean] |
Fired when the speed dial requests a controlled open value. |
open-change |
[value: boolean, reason: SpeedDialChangeReason, nativeEvent: Event | null] |
Fired after any distinct open-state request, with its cause. |
select |
[event: SpeedDialSelectEvent] |
Fired when an action is selected. Call preventDefault() to keep the speed dial open. |
Slots
| Slot |
Slot props |
Description |
default |
SpeedDialSlotState |
Trigger and content. Receives the open state and direction. |
Exposed
| Member |
Type |
Description |
open |
boolean |
Whether the actions are visible. |
state |
SpeedDialState |
Stable state token for styling and tests. |
direction |
SpeedDialDirection |
Direction the actions fan out. |
disabled |
boolean |
Whether the whole speed dial is disabled. |
id |
string |
Root-owned base id. |
triggerId |
string |
Id wired to the trigger. |
contentId |
string |
Id wired to the action menu. |
setOpen |
(value: boolean, event?: Event | null) => boolean |
Request a specific open value. |
openAndFocus |
(event?: Event | null) => boolean |
Open and move focus to the first enabled action. |
close |
(options?: { readonly focusTrigger?: boolean }) => boolean |
Close and optionally return focus to the trigger. |
SpeedDialAction
Source: src/families/actions/floating-action-button/speed-dial-action.vue
Props
| Prop |
Type |
Default |
Description |
value (required) |
string |
required |
Action identity reported by select. |
label (required) |
string |
required |
Accessible name. Speed-dial actions are usually icon-only, so the label is required and also offered to the slot for a visible tooltip. |
disabled |
boolean |
false |
Skip this action during navigation and ignore activation. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the speed dial handles the selection. Call preventDefault() to skip it. |
Slots
| Slot |
Slot props |
Description |
default |
SpeedDialActionSlotState |
Action icon and optional visible label. |
Exposed
| Member |
Type |
Description |
element |
HTMLButtonElement | null |
Rendered native button. |
focus |
(options?: FocusOptions) => void |
Move focus to the action. |
SpeedDialContent
Source: src/families/actions/floating-action-button/speed-dial-content.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for the action menu. undefined labels it with the trigger. |
Slots
| Slot |
Slot props |
Description |
default |
SpeedDialSlotState |
SpeedDialAction children. Receives the speed-dial state. |
Exposed
| Member |
Type |
Description |
open |
boolean |
Whether the actions are visible. |
state |
SpeedDialState |
Stable state token for styling and tests. |
direction |
SpeedDialDirection |
Direction the actions fan out. |
disabled |
boolean |
Whether the whole speed dial is disabled. |
element |
HTMLDivElement | null |
Rendered menu element. |
SpeedDialTrigger
Source: src/families/actions/floating-action-button/speed-dial-trigger.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name, for example "Create". Required in practice for icon-only triggers. |
Slots
| Slot |
Slot props |
Description |
default |
SpeedDialSlotState |
Trigger icon and label. Receives the speed-dial state. |
Exposed
| Member |
Type |
Description |
element |
HTMLButtonElement | null |
Rendered native button. |
focus |
(options?: FocusOptions) => void |
Move focus to the trigger. |
Behavior
Normative behavior for @vizejs/ui/floating-action-button. The speed dial follows the
WAI-ARIA APG Menu Button pattern.
Every row is proven by the named test.
| State x input |
Observable outcome |
Proven by |
floating-action-button.vue render and click |
Native labelled button with data-placement, data-extended, and a click event; positioning stays consumer-owned. |
floating action button renders a labelled native button with placement data |
| closed speed dial |
Trigger has aria-haspopup="menu", aria-expanded="false", aria-controls; the role="menu" content is hidden and labelled by the trigger; actions are menuitems named by label. |
speed dial trigger follows the menu button pattern |
| pointer activation of the trigger |
The menu opens and focus stays on the trigger; open-change reports "pointer". |
speed dial trigger follows the menu button pattern |
| Enter/Space on the trigger |
The menu opens and focus moves to the first enabled action. |
keyboard opening focuses the first action and arrows rove past disabled actions |
| arrows, Home, End inside the menu |
Roving focus moves between enabled actions along the direction axis, skipping disabled actions and wrapping when loop. |
keyboard opening focuses the first action and arrows rove past disabled actions |
| Escape inside the menu |
The menu closes and focus returns to the trigger. |
keyboard opening focuses the first action and arrows rove past disabled actions |
arrow matching direction on the trigger |
Opens and focuses the first action; other arrows are ignored; horizontal directions set aria-orientation="horizontal". |
the direction arrow on the trigger opens into the menu |
| action activation |
select fires with the value, the menu closes ("action"), and focus returns to the trigger. |
selecting an action emits select, closes, and restores trigger focus |
select handler calls preventDefault() |
The menu stays open. |
preventDefault in select keeps the speed dial open |
| outside pointer-down or Tab |
The menu closes through the dismissable layer or Tab handling. |
outside pointer-down and Tab close the speed dial |
openOnHover |
Mouse/pen enter opens and leave closes; touch is ignored. |
hover opening is opt-in and ignores touch |
disabled or controlled open |
Disabled roots never open; controlled roots emit update:open and follow the prop. |
disabled and controlled speed dials respect their owner |
| root expose |
Ids, openAndFocus, close({ focusTrigger }), setOpen, state, and direction are available. |
root exposes ids and programmatic controls |
parts outside speed-dial-root.vue |
speed-dial-trigger.vue, speed-dial-content.vue, and speed-dial-action.vue throw VIZE_UI_CONTEXT_MISSING. |
speed-dial parts require a root |
| SSR / hydration |
Closed markup is byte-identical and hydrates with zero warnings. |
renders byte-identical closed speed-dial markup, hydrates speed-dial markup without diagnostics |
| public types |
Placement, direction, change reasons, and required action labels are closed contracts. |
src/families/actions/floating-action-button/floating-action-button.types.test-d.ts |
| DOM/SSR/Vapor |
Every part compiles in each renderer lane. |
scripts/check-renderers.ts |
| Target |
Public contract |
| FAB |
part="root", data-vize-ui="floating-action-button", data-placement, data-extended, data-disabled |
| Root |
part="root", data-vize-ui="speed-dial-root", data-state, data-direction, data-disabled |
| Trigger |
part="trigger", data-vize-ui="speed-dial-trigger", data-state |
| Content |
part="content", data-vize-ui="speed-dial-content", data-state, data-direction |
| Action |
part="action", data-vize-ui="speed-dial-action", data-value, data-active, data-disabled |
The family ships no stylesheet.