WebberUI

Ornament Toolbar

A visionOS-style ornament toolbar floating just outside the edge of a content panel: it shrinks to a pill while scrolling and expands to full controls when you stop, with Z-axis lift and a follow-along parallax.

Loading preview…
npx shadcn@latest add https://webberui.com/r/ornament-toolbar.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

20
360
<OrnamentToolbar />

Installation

npx shadcn@latest add https://webberui.com/r/ornament-toolbar.json

Or, once registries are configured in components.json, install it as @webberui/ornament-toolbar.

Usage

OrnamentToolbar wraps a scrollable content panel (OrnamentContent) and a floating toolbar (Ornament). The toolbar docks just outside the panel's edge, shrinking to a pill while OrnamentContent scrolls and expanding when scrolling stops; hovering with the mouse or focusing from the keyboard also forces it open. Use OrnamentItem for each control, and insert an OrnamentSeparator between groups.

import {
  Ornament,
  OrnamentContent,
  OrnamentItem,
  OrnamentSeparator,
  OrnamentToolbar,
} from "@/components/ui/ornament-toolbar";
import { Bookmark, Minus, Plus, Share2 } from "lucide-react";

export function Example() {
  return (
    <OrnamentToolbar side="bottom" className="h-[280px]">
      <OrnamentContent className="p-5">{/* scrollable content */}</OrnamentContent>

      <Ornament aria-label="Document toolbar">
        <OrnamentItem icon={<Minus />} aria-label="Zoom out" />
        <OrnamentItem icon={<Plus />} label="100%" aria-label="Zoom in" />
        <OrnamentSeparator />
        <OrnamentItem icon={<Bookmark />} aria-label="Bookmark" />
        <OrnamentItem icon={<Share2 />} aria-label="Share" />
      </Ornament>
    </OrnamentToolbar>
  );
}

Controlled mode: pass collapsed and onCollapsedChange to own the collapsed state yourself (scrolling then no longer collapses it automatically).

Anatomy

ComponentDescription
OrnamentToolbarOutermost container; provides scroll detection and the collapsed state — set the size here with className
OrnamentContentThe content panel, itself the scroll container; the toolbar collapses and expands based on its scrolling
OrnamentThe toolbar itself, floating just outside the panel edge, role="toolbar"
OrnamentItemA single control; expands into a pill when it has a label, leaving only the icon when collapsed
OrnamentSeparatorA vertical separator between groups of controls

Props

OrnamentToolbar

PropTypeDefaultDescription
side"top" | "bottom""bottom"Which panel edge the toolbar floats outside of
offsetnumber20Distance between the toolbar and the panel edge (px)
collapsedbooleanControlled collapsed state; once passed, scrolling no longer collapses it automatically
defaultCollapsedbooleanfalseInitial collapsed state in uncontrolled mode
onCollapsedChange(collapsed: boolean) => voidCollapsed-state change callback
collapseOnScrollbooleantrueWhether to collapse automatically while scrolling and expand when it stops
idleDelaynumber360Delay after scrolling stops before it counts as idle and expands (ms)
classNamestringAppended to the container; set the size here

Ornament

PropTypeDefaultDescription
aria-labelstring"工具列"Accessible name for the toolbar
classNamestringAppended to the pill container

OrnamentItem

PropTypeDefaultDescription
iconReact.ReactNodeControl icon (lucide-react recommended)
labelReact.ReactNodeText label; collapses smoothly when shrinking into a pill
activebooleanfalseActive state, marked with aria-pressed and highlighted
aria-labelstringRequired when there is no label, as the accessible name for the icon button

All other native <button> attributes (onClick, disabled, and so on) are forwarded.

How it works

  • Floating outside the edge: Ornament is absolutely positioned just outside the panel's top / bottom edge and horizontally centered, with offset controlling the distance from the panel. The outermost container does not clip, so remember to reserve space below (or above) it.
  • Collapse on scroll, expand when idle: it watches OrnamentContent's scroll events, collapsing to a pill while scrolling and expanding once there has been no scrolling for idleDelay. On collapse, each control's label folds away smoothly with a spring into an icon-only pill.
  • Z-axis lift and follow-along parallax: when collapsed the shadow deepens and the whole thing shrinks slightly, simulating a lift towards the user; the scroll velocity, smoothed by a spring, maps to a slight vertical follow offset that produces a floating parallax.
  • Hover / focus force it open: whenever the mouse enters or keyboard focus lands inside the toolbar it is always expanded, so both pointer and keyboard users can see the full set of controls.

Accessibility

  • Ornament is a role="toolbar" with aria-orientation; arrow keys move focus between controls and Home / End jump to the first and last.
  • OrnamentItem's active is expressed through aria-pressed; an aria-label is required when there is no text label, and when there is a label its text becomes the accessible name automatically.
  • Focusing the toolbar from the keyboard forces it open and shows the labels, so the collapsed state never gets in the way of keyboard operation.
  • When the user has "reduce motion" enabled at the system level, automatic collapsing and the follow-along parallax are disabled, the toolbar stays expanded, and the labels and scaling become instant changes.

On this page