Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

FloatingToolbar

A floating pill of actions anchored under an element, such as crop mode's size, Reset, Cancel and Apply.

import { FloatingToolbar } from "@butteryui/react";

Demo

Title slide

Resizing to 1920 × 1080

When to use it

Use it for

  • Actions for a mode that works on one element: crop, trim, a selection

Not for

  • Actions that are always there: put them in the HeaderBar or an inspector
  • A message: use toast()

Examples

Crop bar

Resize mode's bar under the frame, shown from state: the size, then Revert, Discard and Keep size.

Title slide

Slide 1600 × 900

Selection actions

Actions for the slides picked, shown while any are: how many, then Clear and Duplicate.

Slide actions

Actions under a slide while it's picked: press the slide to pick it, and Done to put them away.

0 copies

Variants and states

Resize mode

12px under the slide, centered and kept inside the viewport; it rises in a beat after resize mode starts and follows the slide as the page scrolls.

Title slide

Props

FloatingToolbar

PropTypeDefaultDescription
dir"ltr" | "rtl"Which way the arrow keys run: "ltr" or "rtl". Follows the page when left out.
anchor

required

RefObject<HTMLElement | null>The element it sits under, such as the crop frame. It follows the element as it moves.
label

required

stringNames the toolbar, such as "Slide size".
openbooleantrueWhether it shows. Hiding it plays its exit. Defaults to true.
metaReactNodeText at its start, such as the size being cropped to. Tabular, and it truncates.
delayedbooleanWaits delay-follow (80ms) before arriving, so it comes in after its anchor changes, as in crop mode.
focusOnOpenbooleanfalseMoves focus to its first button that can act when it opens, for a toolbar that's the next step, such as crop mode's. Either way, focus inside it goes back where it came from when it closes. Defaults to false.
children

required

ReactNodeThe buttons: FloatingToolbar.Buttons.

It also takes Omit<ComponentPropsWithRef<"div">, "children" | "dir">.

FloatingToolbar.Button

PropTypeDefaultDescription
variant"primary" | "secondary" | "soft" | "ghost""soft""primary" for the one main action in view, "soft" (the default) for the rest, "secondary" on a tinted surface such as the stage, and "ghost" for quiet actions in toolbars.
size"sm" | "md" | "cta""md""sm" (28px), "md" (36px, the default) or "cta" (48px, for dialog steps and calls to action).
shape"pill" | "rounded""pill""pill" (the default) or "rounded", with 12px corners, for calls to action in dialogs.
loadingbooleanShows a spinner over the label, keeping the button's size, and ignores presses while it's busy.
iconStartComponentType<IconComponentProps>An icon before the label, such as Plus from lucide-react.
iconEndComponentType<IconComponentProps>An icon after the label.
arrow"forward" | "back"A navigation arrow: "forward" after the label for Next, Continue and Get started, "back" before it. Its chevron grows a stem on hover or focus. Leave it off Done, Save, Submit and destructive actions.
shortcutShortcutA keyboard shortcut to show after the label, written as useShortcut takes it, such as "n" or "mod+n". It's announced through aria-keyshortcuts; bind it with useShortcut.
asChildbooleanRender the single child element (such as a link) as the button, with the button's look and parts merged in. The child's own content is the label.

It also takes ComponentPropsWithRef<"button">.

Accessibility

A toolbar named by label: one Tab stop, with the arrow keys moving between its buttons. Toasts and notices move out of its way instead of covering it.

KeysWhat they do
TabMoves into the toolbar, on the button that last had focus, and out again: the toolbar is one tab stop
Left and Right arrowsMove between its buttons, wrapping at the ends; with dir="rtl" they run the other way
Home or EndMoves to its first or last button
Enter or SpacePresses the focused button

Motion

It appears with a `Presence` preset and leaves faster than it came, in about 60% of the time.

With reduced motion: It fades in and out in 100ms where it stands, without rising, and a delayed one arrives without its 80ms wait.

Layout

It's a 48px pill (--bt-h-cta) as wide as its meta and buttons, and never wider than the viewport less 8px a side. It sits centered 12px under its anchor, or above it when there's no room below, and stays 8px inside the viewport as the anchor moves; the meta truncates with its full text in its title, and the buttons keep their size.

See it in the Deckhand demo.