JavaScript Speed Dial

@lemonadejs/speeddial · ✓ 18 contract checks · framework-agnostic · zero dependencies

<Speeddial /> — a floating action button that fans out a column/row of small action buttons. New v6 block (no v5 ancestor).

One FAB toggles the fan on click; hovering opens it and leaving closes it after a 150ms grace timer (cancelled on re-enter and on unmount — destroy-clean). Escape closes. Picking an action fires its own onclick, then onaction(name, event), then closes the fan.

bind is the open state (named fanned), two-way: external writes flip the fan silently — onopen/onclose fire on user/api transitions only.

Fan-out stagger: each action carries an inline transition-delay of index * 30ms — deterministic, testable as a style attribute. The FAB icon rotates 45° while open (pure CSS on lm-speeddial-open). Action tooltips are plain CSS side labels — no Tooltip dependency.

Example

live
import { html } from 'lemonadejs';
import Speeddial from '@lemonadejs/speeddial';

const App = (props, { state }) => {
    const last = state('');

    const actions = [
        { name: 'New note', icon: 'edit' },
        { name: 'New event', icon: 'event' },
        { name: 'New contact', icon: 'person_add' },
        { name: 'Upload file', icon: 'upload_file' },
    ];

    return html`<div style="height:260px;display:flex;align-items:flex-end;justify-content:space-between">
        <p>Last action: <b>${() => last.value || 'none'}</b></p>
        <${Speeddial} options="${actions}" icon="add" label="Create"
            onaction="${(name) => (last.value = name)}" />
    </div>`;
};

Installation

npm install @lemonadejs/speeddial
import Speeddial from '@lemonadejs/speeddial';
import '@lemonadejs/speeddial/style.css';

The icons are Google Material Icons ligature names (add, edit, upload_file). Load the font once per page:

<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

Three deployment forms, one component:

html`<${Speeddial} />`                       // by value (no registration)
setComponents({ Speeddial });               // then <Speeddial /> by name anywhere
createWebComponent(Speeddial);              // <lm-speeddial> in plain HTML/any framework

Props

Every declared prop arrives as a live state — pass a value for a snapshot or a state for a two-way live wire. Attribute strings are coerced to the declared type.

PropTypeDefaultDescription
bindbooleanTwo-way bound value. .set() fires onchange; plain assignment is silent. two-way open state (fanned)
optionsarraySpeeddialAction[] — live
iconstring''FAB icon (default ‘add’ icon; rotates 45° to × when open)
directionstring''” = up
positionstring''” = static in flow
labelstring''aria-label for the FAB
disabledbooleanfalseblocks every trigger

Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

  • onopen — fan opened (user/api — bind writes are silent)
  • onclose — fan closed (user/api — bind writes are silent)
  • onaction — (name, event) when an action is picked

API

import { ref } from 'lemonadejs';
const speeddial = ref();
html`<${Speeddial} ref="${speeddial}" />`;
// speeddial.current.open(...)  ·  speeddial.current.close(...)  ·  speeddial.current.toggle(...)
  • open()
  • close()
  • toggle()

Styling

All classes follow the lm-speeddial-* convention; visual variants are data-* attributes on the root. Override freely — there is no styling engine to fight.

Contract

The machine-readable schema ships with the package:

import contract from '@lemonadejs/speeddial/contract.json';

verify.json carries the conformance proof produced by verify(Speeddial).

Looking for the v5 plugin? See the archived v5 documentation.