npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reynsu/react-floaty

v0.1.2

Published

Mobile-first floating action toolbar for React. Animated, outside-click dismissable, themable via CSS vars. Zero runtime dependencies.

Readme

@reynsu/react-floaty

npm bundle size types license

Mobile-first floating action toolbar for React. Zero runtime dependencies (React/ReactDOM as peers).

  • ~8 KB ESM, tree-shakable
  • CSS-only animations — no framer-motion, no JS animation lib
  • External store via useSyncExternalStore — consumers re-render only when they read state
  • Headless-friendly: theme via CSS custom properties, override with className
  • Accessible: WAI-ARIA toolbar role, ESC + outside-click dismiss
  • SSR-safe

Install

npm i @reynsu/react-floaty

Quick start

import { FloaterActionsProvider, useFloaterActions } from '@reynsu/react-floaty';
import '@reynsu/react-floaty/styles.css';

function App() {
  return (
    <FloaterActionsProvider maxVisible={3}>
      <Page />
    </FloaterActionsProvider>
  );
}

function Page() {
  const { show } = useFloaterActions();
  return (
    <button
      onClick={() =>
        show([
          { id: 'copy',   label: 'Copy',   onSelect: () => copy() },
          { id: 'share',  label: 'Share',  onSelect: () => share() },
          { id: 'delete', label: 'Delete', variant: 'danger', onSelect: () => del() },
          { id: 'archive', label: 'Archive', onSelect: () => archive() },
          { id: 'pin',    label: 'Pin',    onSelect: () => pin() },
        ])
      }
    >
      Open actions
    </button>
  );
}

The first three actions render as visible buttons. Anything beyond maxVisible collapses into a + overflow popover.

Real-world example — bulk actions

Each callback owns the consumer's state. Floaty stays neutral.

Keyboard-driven palette

toggle() opens the bar from any handler — bind it to ⌘K and you have a command palette in three lines.

API

<FloaterActionsProvider />

| Prop | Type | Default | Description | |---|---|---|---| | maxVisible | number | 3 | Visible action buttons before overflow | | portalContainer | HTMLElement | document.body | Portal target | | className | string | — | Extra class applied to the bar | | closeOnOutsideClick | boolean | true | Pointerdown outside the bar dismisses it | | closeOnEscape | boolean | true | Escape key dismisses it |

useFloaterActions()

Returns { open, actions, options, show, hide, toggle }.

| Method | Signature | Notes | |---|---|---| | show | (actions: FloaterAction[], options?: ShowOptions) => void | No-op + dev warning on empty array | | hide | () => void | | | toggle | (actions?: FloaterAction[]) => void | Reuses last actions if not provided |

FloaterAction

type FloaterAction = {
  id: string;
  label?: string;       // optional — omit for icon-only buttons
  icon?: ReactNode;     // optional — supply alongside or instead of label
  ariaLabel?: string;   // overrides accessible name; required when label is omitted
  onSelect: () => void;
  disabled?: boolean;
  variant?: 'default' | 'danger';
};

The component picks the right rendering based on what you pass:

// Text only
{ id: 'copy', label: 'Copy', onSelect: copy }

// Icon + text (icon renders left of label)
{ id: 'copy', label: 'Copy', icon: <CopyIcon />, onSelect: copy }

// Icon only — supply ariaLabel for screen readers.
// The button auto-squares to --fa-action-h via [data-icon-only].
{ id: 'pin', icon: <PinIcon />, ariaLabel: 'Pin item', onSelect: pin }

ShowOptions

type ShowOptions = {
  maxVisible?: number;       // override Provider default for this show call
  dismissOnSelect?: boolean; // default true
};

Theming

All visual tokens are CSS custom properties on .fa-bar. Override them to get any look — the demo site ships nine ready-made variants.

Override globally or via className:

.fa-bar.theme-dark {
  --fa-bg: #1a1d24;
  --fa-fg: #f5f6fa;
  --fa-action-bg: rgba(255,255,255,0.06);
  --fa-action-bg-hover: rgba(255,255,255,0.12);
  --fa-radius: 16px;
  --fa-z: 1000;
}

Then pass it to the provider:

<FloaterActionsProvider className="theme-dark">…</FloaterActionsProvider>

Beyond the row — radial, arc, grid, spiral

The bar exposes three structural knobs and per-button index/count vars so you can build any layout in pure CSS:

| Knob | Default | Purpose | |---|---|---| | --fa-display | flex | Set to block / grid to escape the default flex row | | --fa-width | min(100% - 24px, 520px) | Bar width | | --fa-height | auto | Bar height — set to a fixed value for square/round canvases | | --fa-i (per button) | 0..n-1 | The button's index — set as inline style on every .fa-action | | --fa-n (per button) | slot count | Total visible slots (visible actions + overflow trigger) |

Example — radial donut with 6 actions orbiting a center point:

.theme-radial.fa-bar {
  --fa-display: block;
  --fa-width:  220px;
  --fa-height: 220px;
  --fa-bg: transparent;
  --fa-shadow: none;
}
.theme-radial .fa-action {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  transform:
    translate(-50%, -50%)
    rotate(calc(var(--fa-i) * 360deg / var(--fa-n)))
    translateY(-90px)
    rotate(calc(-1 * var(--fa-i) * 360deg / var(--fa-n)));
  transition-delay: calc(var(--fa-i) * 35ms);
}

The same primitives compose into arcs (180deg instead of 360deg), spirals (animate translateY over var(--fa-i)), or grids (display: grid + grid-area).

Accessibility

  • Bar uses role="toolbar" and aria-label="Floating actions"
  • Overflow uses role="menu" + role="menuitem", with aria-haspopup and aria-expanded on the trigger
  • prefers-reduced-motion: reduce disables transitions

Local development

npm install
npm run typecheck
npm test
npm run build

To run the demo app:

cd examples/vite-demo
npm install
npm run dev

License

MIT © reynsu