Organic Growth

Wrap any element and vines, leaves and flowers grow out of its edges on hover or focus.

Read how it's built

Preview

Setup

Install with the shadcn CLI

$npx shadcn@latest add https://kinetikui.vercel.app/r/organic-growth.json

One command adds every file, the npm packages and any Kinetik components it builds on. Needs a components.json; the CLI offers to create one if your project has none.

Or install manually

  1. 1

    Create a components/ui/organic-growth/ folder and copy every file from the Code tab into it. Each file does one job, so you can read or change a piece without the rest.

  2. 2

    Import and render:

    Example
    import OrganicGrowth from "@/components/ui/organic-growth"
    
    <OrganicGrowth padding={70}>
      <div className="rounded-2xl border p-6">Hover me</div>
    </OrganicGrowth>

Usage

Hover the card or focus a field and the plant grows; leave and it retracts. Pass active to keep it grown, for example after a form is sent.

import OrganicGrowth from "@/components/ui/organic-growth"

export function Card() {
  return (
    <OrganicGrowth padding={70}>
      <div className="rounded-2xl border p-6">Hover me</div>
    </OrganicGrowth>
  )
}

Each vine is described by a Sprout: which edge it grows from, where along that edge, which way it bends, how long it is, and how it ends.

const sprouts: Sprout[] = [
  { edge: "top", t: 0.2, side: -1, length: 60, end: "curl" },
  { edge: "right", t: 0.5, side: 1, length: 80, end: "bloom", blooms: [0.5] },
]

The plant is built from a seeded random generator, so the same seed always gives the same arrangement. Colours read --og-* CSS variables, or pass a palette to override them.

Props

PropTypeDefaultNotes
activeboolean—Force grown or retracted; ignores hover and focus while set.
trigger"hover" | "focus" | "hover-focus" | "manual""hover-focus"What grows the vines when active is unset.
sproutsSprout[]14 vinesWhere vines grow, see Usage.
seednumber2Change it for a different arrangement.
paddingnumber1Room (px) around the element for vines to grow into.
tucknumber18How far (px) vines start hidden under the edge.
speednumber0.8Above 1 grows faster, below 1 slower.
staggernumber0.01Delay (s) between sprouts starting.
palettePartial<GrowthPalette>—Stem, leaf, petal and pollen colours, plus flowerScale.
onGrowChange(grown: boolean) => void—Fires whenever the plant grows or retracts.

Accessibility

  • The plant is a decorative aria-hidden SVG behind your content, which stays fully interactive
  • With prefers-reduced-motion, vines fade in instead of growing
  • On touch screens, tapping the element grows it and tapping elsewhere retracts it

Further reading

How I Built OrganicGrowth, a 7-minute walkthrough of the recipe behind the vines: Bézier stems, stroke-dashoffset timing for the leaves, and the seeded random generator that keeps every plant repeatable.