Ferrofluid Menu
NewInstallation
npx shadcn@latest add https://uibrik.com/r/ferrofluid-menu.jsonAdd the cn helper
Imports
cnfrom@/lib/utils, whichshadcn initcreates. Add it by hand without shadcn.Copy the source into your project
components/uibrik/ferrofluid-menu/ferrofluid-menu.tsx"use client"; // A menu made of ferrofluid. Closed, it's one glossy black drop that swells // toward the pointer and raises an even crown of spikes on that side as it // comes near, the way the fluid stands up along a magnet's field lines. // Pressed, droplets pull out of it one after another, stretching a neck of // fluid behind them, then spread into the menu's items. Once settled, a small // gap opens between each pair, bridged by a pinched neck and a scatter of // glue strings, each pair strung differently; they spring in with a wobble. Closing pours them back in. // Hovered items swell and lean toward the pointer, slanting and stretching // the glue, which thins as it's pulled; a pressed one gives a little before // the menu closes. // // The fluid is one signed distance field drawn in WebGL: the drop and every // item are smoothly unioned, so anything close enough flows together. Shapes // are moved by springs on the main thread; labels are real buttons that ride // on their droplet. The loop sleeps once everything has settled and the // pointer is away, and off screen. Reduced motion snaps the springs and drops // the spikes. Without WebGL the items fall back to plain filled pills. import { useCallback, useEffect, useId, useRef, useState } from "react"; import { cn } from "@/lib/utils"; export type FerrofluidMenuProps = {
Adds the files to
components/uibrik/ferrofluid-menu. With the @uibrik namespace set up: npx shadcn@latest add @uibrik/ferrofluid-menu.Props
FerrofluidMenu- items
- Type string[]
- ["Profile","Projects","Settings","Sign out"]
- Menu items, top to bottom. Up to 8.
- size
- Type number
- 56
- Diameter of the drop.
- spikes
- Type number
- 13
- Spikes round the full drop. Only the side facing the pointer raises them.
- spikeLength
- Type number
- 22
- Length of the tallest spike.
- reach
- Type number
- 220
- How far from the drop's edge the pointer is felt.
- viscosity
- Type number
- 22
- How far apart two shapes stay joined. Higher stretches longer necks before they snap.
- neck
- Type number
- 5
- Radius of the necks joining the open items where they meet each edge; they pinch to under half that in the middle. 0 leaves the items apart.
- strands
- Type number
- 5
- Glue strings scattered between two items, besides the neck. Each pair gets its own arrangement.
- stagger
- Type number
- 0.06
- Time between droplets leaving the drop.
- itemWidth
- Type number
- 188
- Width of the items.
- color
- Type string
- "#0b0b0c"
- Colour of the fluid. Labels stay white, so keep it dark.
- label
- Type string
- "Menu"
- Accessible name for the trigger and the menu.
- defaultOpen
- Type boolean
- false
- Start open.
- onSelect
- Type (item: string, index: number) => void
- –
- Called when an item is chosen. The menu closes after.
- className
- Type string
- –
- Classes for the wrapper, which is the size of the drop. The menu and spikes overhang it.