# tweenly > The motion layer for shadcn/ui. Beautifully crafted animated components. Tweak them live, copy the code, and own it. This file contains the complete reference for all 60 tweenly components. An index is available at https://trytweenly.vercel.app/llms.txt. ## Installation tweenly is a shadcn/ui registry. Components are copied into your project (no runtime package). Requires a project already set up with shadcn/ui and Tailwind CSS. tweenly is listed in the shadcn registry directory, so the CLI resolves the `@tweenly` namespace with no setup: ```bash npx shadcn@latest add @tweenly/ ``` The full registry URL also works: ```bash npx shadcn@latest add https://trytweenly.vercel.app/r/.json ``` For AI agents, the shadcn MCP server can browse and install tweenly components. Run `npx shadcn@latest mcp init --client claude` (or `cursor`, `vscode`, `codex`) and add the namespace to `components.json` so the server can search it: ```json { "registries": { "@tweenly": "https://trytweenly.vercel.app/r/{name}.json" } } ``` Components land in `@/components/` and their npm dependencies (for example `motion`) are installed automatically. --- ## Fade In Scroll-triggered reveal with direction, distance, easing, scale and blur. - Category: Text - Docs: https://trytweenly.vercel.app/docs/components/fade-in - Import: `import { FadeIn } from "@/components/fade-in"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/fade-in # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/fade-in.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | Content to reveal. | | `direction` | `"up" \| "down" \| "left" \| "right" \| "none"` | `"up"` | Direction the element travels while appearing. | | `distance` | `number` | `24` | Travel distance in px. | | `duration` | `number` | `0.6` | Animation length in seconds. | | `delay` | `number` | `0` | Delay before starting, in seconds. | | `ease` | `"smooth" \| "snappy" \| "linear" \| "spring"` | `"smooth"` | Easing preset. | | `scale` | `number` | `1` | Starting scale (1 = no scale). | | `blur` | `number` | `0` | Starting blur in px. | | `once` | `boolean` | `true` | Animate only the first time it enters the viewport. | | `amount` | `number` | `0.2` | Fraction of the element that must be visible, 0 to 1. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { FadeIn } from "@/components/fade-in" export function Demo() { return (
Hello, tweenly
) } ``` --- ## Text Reveal Word-by-word or character-by-character reveal with blur and stagger. - Category: Text - Docs: https://trytweenly.vercel.app/docs/components/text-reveal - Import: `import { TextReveal } from "@/components/text-reveal"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/text-reveal # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/text-reveal.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` (required) | `string` | - | The text to animate. | | `split` | `"word" \| "char"` | `"word"` | Animate each word or each character. | | `direction` | `"up" \| "down" \| "none"` | `"up"` | Direction each piece travels from. | | `stagger` | `number` | `0.06` | Delay between pieces, in seconds. | | `duration` | `number` | `0.5` | Duration of each piece, in seconds. | | `delay` | `number` | `0` | Delay before the first piece, in seconds. | | `blur` | `number` | `8` | Starting blur in px. | | `distance` | `number` | `16` | Travel distance in px. | | `once` | `boolean` | `true` | Animate only the first time it enters the viewport. | | `as` | `"p" \| "h1" \| "h2" \| "h3" \| "span"` | `"p"` | Element to render as. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { TextReveal } from "@/components/text-reveal" export function Demo() { return ( ) } ``` --- ## Typewriter Types and deletes a list of phrases with a blinking cursor. - Category: Text - Docs: https://trytweenly.vercel.app/docs/components/typewriter - Import: `import { Typewriter } from "@/components/typewriter"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/typewriter # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/typewriter.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `words` (required) | `string[]` | - | Words or phrases to type, in order. | | `typeSpeed` | `number` | `70` | Milliseconds per typed character. | | `deleteSpeed` | `number` | `40` | Milliseconds per deleted character. | | `pause` | `number` | `1400` | Pause after a word is typed, in ms. | | `loop` | `boolean` | `true` | Cycle through the words forever. | | `cursor` | `boolean` | `true` | Show a blinking cursor. | | `cursorChar` | `string` | `"|"` | Character used for the cursor. | | `className` | `string` | - | Additional classes for the root element. | | `cursorClassName` | `string` | - | Additional classes for the cursor. | Usage: ```tsx import { Typewriter } from "@/components/typewriter" export function Demo() { return ( ) } ``` --- ## Word Rotate Cycles through words with slide, fade, blur or flip transitions. - Category: Text - Docs: https://trytweenly.vercel.app/docs/components/word-rotate - Import: `import { WordRotate } from "@/components/word-rotate"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/word-rotate # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/word-rotate.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `words` (required) | `string[]` | - | Words to cycle through. | | `interval` | `number` | `2200` | Time each word stays visible, in ms. | | `effect` | `"slide" \| "fade" \| "blur" \| "flip"` | `"slide"` | Transition style between words. | | `duration` | `number` | `0.4` | Transition duration in seconds. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { WordRotate } from "@/components/word-rotate" export function Demo() { return (

Build interfaces that feel{" "}

) } ``` --- ## Shimmer Text A band of light that sweeps across text on a loop. - Category: Text - Docs: https://trytweenly.vercel.app/docs/components/shimmer-text - Import: `import { ShimmerText } from "@/components/shimmer-text"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/shimmer-text # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/shimmer-text.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `string` | - | Text to shimmer. | | `duration` | `number` | `2` | Seconds for one sweep. | | `spread` | `number` | `60` | Width of the highlight band in px. | | `repeatDelay` | `number` | `0.4` | Pause between sweeps, in seconds. | | `baseColor` | `string` | `"var(--muted-foreground)"` | Base text color (any CSS color). | | `shimmerColor` | `string` | `"var(--foreground)"` | Highlight color (any CSS color). | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ShimmerText } from "@/components/shimmer-text" export function Demo() { return ( Generating response… ) } ``` --- ## Number Ticker Counts up to a value when scrolled into view. - Category: Text - Docs: https://trytweenly.vercel.app/docs/components/number-ticker - Import: `import { NumberTicker } from "@/components/number-ticker"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/number-ticker # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/number-ticker.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` (required) | `number` | - | Final value. | | `from` | `number` | `0` | Starting value. | | `duration` | `number` | `2` | Animation length in seconds. | | `delay` | `number` | `0` | Delay before counting, in seconds. | | `decimals` | `number` | `0` | Number of decimal places. | | `prefix` | `string` | `""` | Text rendered before the number. | | `suffix` | `string` | `""` | Text rendered after the number. | | `separator` | `boolean` | `true` | Group thousands with the locale separator. | | `once` | `boolean` | `true` | Count only the first time it enters the viewport. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { NumberTicker } from "@/components/number-ticker" export function Demo() { return ( ) } ``` --- ## Shine Button Pill button with a light beam circling its edge and a soft sheen on hover. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/shine-button - Import: `import { ShineButton } from "@/components/shine-button"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/shine-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/shine-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | - | Button content. | | `beamColor` | `string` | `"#ff6a2b"` | Color of the light that runs around the edge. | | `duration` | `number` | `3` | Seconds for the beam to complete one lap. | | `sheen` | `boolean` | `true` | Sweep a soft sheen across the face on hover. | | `variant` | `"solid" \| "outline"` | `"solid"` | Filled or outlined face. | | `className` | `string` | - | Additional classes for the root element. | | `...props` | `ButtonHTMLAttributes` | - | Native button attributes are forwarded. | Usage: ```tsx import { ShineButton } from "@/components/shine-button" export function Demo() { return ( Get started ) } ``` --- ## Ripple Button Material-style ripples that expand from the exact point you press. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/ripple-button - Import: `import { RippleButton } from "@/components/ripple-button"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/ripple-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/ripple-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | - | Button content. | | `rippleColor` | `string` | `"currentColor"` | Ripple color (any CSS color). | | `duration` | `number` | `0.6` | Seconds each ripple takes to expand and fade. | | `variant` | `"solid" \| "outline" \| "ghost"` | `"solid"` | Visual style. | | `className` | `string` | - | Additional classes for the root element. | | `...props` | `ButtonHTMLAttributes` | - | Native button attributes are forwarded. | Usage: ```tsx import { RippleButton } from "@/components/ripple-button" export function Demo() { return ( Click me ) } ``` --- ## Hold Button Press and hold to confirm destructive actions. A fill sweeps across while held. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/hold-button - Import: `import { HoldButton } from "@/components/hold-button"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/hold-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/hold-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `holdDuration` | `number` | `1.2` | Seconds the button must be held to confirm. | | `label` | `string` | `"Hold to delete"` | Idle label. | | `confirmedLabel` | `string` | `"Deleted"` | Label shown once confirmed. | | `fillColor` | `string` | `"#ef4444"` | Color of the fill that sweeps across while holding. | | `resetAfter` | `number` | `1800` | Return to idle after confirming, in ms (0 = stay confirmed). | | `onConfirm` | `() => void` | - | Called once the hold completes. | | `className` | `string` | - | Additional classes for the root element. | | `...props` | `ButtonHTMLAttributes` | - | Native button attributes are forwarded. | Usage: ```tsx import { HoldButton } from "@/components/hold-button" export function Demo() { return ( deleteProject()} /> ) } ``` --- ## Slide Button Slide-to-confirm track with a draggable knob, shimmering label and keyboard support. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/slide-button - Import: `import { SlideButton } from "@/components/slide-button"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/slide-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/slide-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `"Slide to confirm"` | Text shown on the track. | | `completeLabel` | `string` | `"Confirmed"` | Text shown once completed. | | `threshold` | `number` | `0.9` | Fraction of the track the knob must pass to complete, 0 to 1. | | `width` | `number` | `280` | Track width in px. | | `accentColor` | `string` | `"var(--foreground)"` | Color of the knob and the trailing fill. | | `resetAfter` | `number` | `2000` | Return to the start after completing, in ms (0 = stay complete). | | `onComplete` | `() => void` | - | Called when the knob reaches the end. | | `disabled` | `boolean` | `false` | Disable interaction. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { SlideButton } from "@/components/slide-button" export function Demo() { return ( placeOrder()} /> ) } ``` --- ## Like Button Heart toggle with a spring pop, ring burst, particles and a rolling counter. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/like-button - Import: `import { LikeButton } from "@/components/like-button"` Dependencies: `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/like-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/like-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `defaultLiked` | `boolean` | `false` | Initial state when uncontrolled. | | `liked` | `boolean` | - | Controlled liked state. | | `onChange` | `(liked: boolean) => void` | - | Called with the next state when toggled. | | `count` | `number` | `128` | Like count, excluding the viewer's own like. | | `color` | `string` | `"#f43f5e"` | Heart, ring and particle color. | | `size` | `number` | `22` | Heart size in px. | | `particles` | `number` | `8` | Number of particles in the burst. | | `className` | `string` | - | Additional classes for the root element. | | `...props` | `ButtonHTMLAttributes` | - | Native button attributes are forwarded. | Usage: ```tsx import { LikeButton } from "@/components/like-button" export function Demo() { return ( ) } ``` --- ## Segmented Control Tabs with a springy pill that slides behind the active option, with arrow-key navigation. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/segmented-control - Import: `import { SegmentedControl } from "@/components/segmented-control"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/segmented-control # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/segmented-control.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `options` (required) | `{ value: string; label: string; icon?: ReactNode }[]` | - | Options to choose from, in order. | | `value` | `string` | - | Controlled selected value. | | `defaultValue` | `string` | - | Initially selected value when uncontrolled. Defaults to the first option. | | `onValueChange` | `(value: string) => void` | - | Called with the new value when the selection changes. | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Control size. | | `pillColor` | `string` | - | Pill color (any CSS color). Defaults to the theme surface color. | | `stiffness` | `number` | `400` | Spring stiffness of the sliding pill. | | `damping` | `number` | `32` | Spring damping of the sliding pill. | | `aria-label` | `string` | - | Accessible label for the group. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { SegmentedControl } from "@/components/segmented-control" const options = [ { value: "day", label: "Day" }, { value: "week", label: "Week" }, { value: "month", label: "Month" }, ] export function Demo() { return ( ) } ``` --- ## Number Stepper Minus / plus stepper whose digits roll individually, with hold-to-repeat and a shake at the limits. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/number-stepper - Import: `import { NumberStepper } from "@/components/number-stepper"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/number-stepper # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/number-stepper.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `number` | - | Controlled value. | | `defaultValue` | `number` | - | Initial value when uncontrolled. Defaults to min. | | `onValueChange` | `(value: number) => void` | - | Called with the new value on every change. | | `min` | `number` | `0` | Lowest allowed value. | | `max` | `number` | `99` | Highest allowed value. | | `step` | `number` | `1` | Amount added or removed per step. | | `prefix` | `string` | - | Text shown before the value, e.g. "$". | | `suffix` | `string` | - | Text shown after the value, e.g. "kg". | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Control size. | | `aria-label` | `string` | `"Quantity"` | Accessible label for the value. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { NumberStepper } from "@/components/number-stepper" export function Demo() { return ( ) } ``` --- ## Elastic Switch Toggle whose knob stretches while pressed and springs across with overshoot, with optional sun / moon icons. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/elastic-switch - Import: `import { ElasticSwitch } from "@/components/elastic-switch"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/elastic-switch # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/elastic-switch.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `checked` | `boolean` | - | Controlled state. | | `defaultChecked` | `boolean` | `false` | Initial state when uncontrolled. | | `onCheckedChange` | `(checked: boolean) => void` | - | Called with the new state when toggled. | | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Switch size. | | `onColor` | `string` | `"#ff6a2b"` | Track color when on. | | `offColor` | `string` | - | Track color when off. Defaults to a muted theme color. | | `icons` | `boolean` | `false` | Show a sun / moon icon inside the knob. | | `label` | `string` | - | Visible label next to the switch. | | `disabled` | `boolean` | `false` | Disable interaction. | | `aria-label` | `string` | - | Accessible label when no visible label is given. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ElasticSwitch } from "@/components/elastic-switch" export function Demo() { return ( ) } ``` --- ## Expand Input A pill button that morphs into an email field, then a spinner and a drawn check on submit. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/expand-input - Import: `import { ExpandInput } from "@/components/expand-input"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/expand-input # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/expand-input.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | `"Subscribe"` | Label of the collapsed button. | | `placeholder` | `string` | `"you@example.com"` | Input placeholder. | | `successLabel` | `string` | `"You're in!"` | Message shown after a successful submit. | | `onSubmit` | `(value: string) => Promise \| void` | - | Called with the email. The spinner waits for the promise; a rejection returns to the input. | | `resetAfter` | `number` | `2500` | Collapse back to the button after success, in ms (0 = stay). | | `width` | `number` | `300` | Width of the expanded input in px. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ExpandInput } from "@/components/expand-input" export function Demo() { return ( subscribe(email)} /> ) } ``` --- ## Rating Star rating with a sweeping hover fill, half stars, and a springy pop with sparkles on select. - Category: Buttons - Docs: https://trytweenly.vercel.app/docs/components/rating - Import: `import { Rating } from "@/components/rating"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/rating # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/rating.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` | `number` | - | Controlled rating. | | `defaultValue` | `number` | `0` | Initial rating when uncontrolled. | | `onValueChange` | `(value: number) => void` | - | Called with the new rating when the user picks one. | | `max` | `number` | `5` | Number of stars. | | `allowHalf` | `boolean` | `true` | Allow half-star ratings. | | `size` | `number` | `28` | Star size in px. | | `color` | `string` | `"#f59e0b"` | Fill color. | | `readOnly` | `boolean` | `false` | Display only, no interaction. | | `aria-label` | `string` | `"Rating"` | Accessible label. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Rating } from "@/components/rating" export function Demo() { return ( ) } ``` --- ## Fill Button Button with a GSAP clip-path fill that sweeps in on hover and focus. - Category: Interactive - Docs: https://trytweenly.vercel.app/docs/components/fill-button - Import: `import { FillButton } from "@/components/fill-button"` Dependencies: `gsap` Install: ```bash npx shadcn@latest add @tweenly/fill-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/fill-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `direction` | `"up" \| "down" \| "left" \| "right"` | `"up"` | Direction the fill sweeps toward. | | `duration` | `number` | `0.45` | Sweep length in seconds. | | `ease` | `string` | `"power3.out"` | Any GSAP ease string. | | `...props` | `ButtonHTMLAttributes` | - | All native button attributes are forwarded. | Usage: ```tsx import { FillButton } from "@/components/fill-button" export function Demo() { return ( Hover me ) } ``` --- ## Magnetic Wrap anything to make it drift toward the cursor on a spring. - Category: Interactive - Docs: https://trytweenly.vercel.app/docs/components/magnetic - Import: `import { Magnetic } from "@/components/magnetic"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/magnetic # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/magnetic.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | Element to make magnetic. | | `strength` | `number` | `0.35` | How far the element follows the cursor, 0 to 1. | | `range` | `number` | `40` | Extra hit area around the element, in px. | | `stiffness` | `number` | `180` | Spring stiffness. Higher is snappier. | | `damping` | `number` | `14` | Spring damping. Higher is less wobbly. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Magnetic } from "@/components/magnetic" export function Demo() { return ( ) } ``` --- ## Tilt Card 3D perspective tilt that follows the pointer, with optional glare. - Category: Interactive - Docs: https://trytweenly.vercel.app/docs/components/tilt-card - Import: `import { TiltCard } from "@/components/tilt-card"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/tilt-card # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/tilt-card.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | Card content. | | `maxTilt` | `number` | `12` | Maximum rotation in degrees. | | `perspective` | `number` | `900` | CSS perspective in px. Lower is more dramatic. | | `scale` | `number` | `1.03` | Scale while hovered. | | `glare` | `boolean` | `true` | Show a glare that follows the cursor. | | `glareOpacity` | `number` | `0.25` | Glare opacity, 0 to 1. | | `reverse` | `boolean` | `false` | Invert the tilt direction. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { TiltCard } from "@/components/tilt-card" export function Demo() { return ( {/* card content */} ) } ``` --- ## Spotlight Card Card with a radial spotlight and glowing border that track the cursor. - Category: Interactive - Docs: https://trytweenly.vercel.app/docs/components/spotlight-card - Import: `import { SpotlightCard } from "@/components/spotlight-card"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/spotlight-card # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/spotlight-card.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | Card content. | | `size` | `number` | `280` | Spotlight radius in px. | | `color` | `string` | `"color-mix(in oklab, var(--foreground) 10%, transparent)"` | Spotlight color (any CSS color). | | `border` | `boolean` | `true` | Also light up the card border. | | `borderColor` | `string` | `"color-mix(in oklab, var(--foreground) 50%, transparent)"` | Border highlight color. | | `...props` | `HTMLAttributes` | - | All native div attributes are forwarded. | Usage: ```tsx import { SpotlightCard } from "@/components/spotlight-card" export function Demo() { return ( {/* card content */} ) } ``` --- ## Cursor Custom cursors: ring, dot, blend, crosshair, sparkle trail, and sunflower or rose that shed petals. - Category: Interactive - Docs: https://trytweenly.vercel.app/docs/components/cursor - Import: `import { Cursor } from "@/components/cursor"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/cursor # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/cursor.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"ring" \| "dot" \| "blend" \| "crosshair" \| "sparkle" \| "sunflower" \| "rose"` | `"ring"` | Cursor style. | | `color` | `string` | - | Main color (any CSS color). Defaults to foreground, gold for sparkle. | | `size` | `number` | `1` | Size multiplier. | | `stiffness` | `number` | `400` | Follow spring stiffness. Higher is tighter. | | `damping` | `number` | `32` | Follow spring damping. | | `trail` | `boolean` | `true` | Drop particles (sparkle, sunflower, rose). | | `hoverSelector` | `string` | `"a, button, [role=button], …"` | Elements that trigger the hover state. Add data-cursor-hover to anything. | | `container` | `HTMLElement \| null` | - | Limit the cursor to this element. Defaults to the whole page. | Usage: ```tsx import { Cursor } from "@/components/cursor" export function Demo() { return ( ) } ``` --- ## Swipe Row Swipeable list rows with iOS-style reveal actions, full-swipe commits and floating cards. Removed rows fly off and the rest glide into place. - Category: Interactive - Docs: https://trytweenly.vercel.app/docs/components/swipe-row - Import: `import { SwipeList } from "@/components/swipe-row"` Dependencies: `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/swipe-row # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/swipe-row.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"reveal" \| "full" \| "card"` | `"reveal"` | Reveal action buttons that stay open, commit with a full swipe, or drag floating cards with tilt. | | `items` (required) | `T[]` | - | Items to render. Removed items animate out and siblings glide up. | | `getKey` (required) | `(item: T) => string \| number` | - | Returns a stable key for an item. | | `renderItem` (required) | `(item: T, index: number) => ReactNode` | - | Renders the content of a row. | | `leftActions` | `SwipeAction[] \| ((item: T) => SwipeAction[])` | `"[]"` | Actions revealed when dragging right, listed from the outer edge inward. Full and card variants commit the first. | | `rightActions` | `SwipeAction[] \| ((item: T) => SwipeAction[])` | `"[]"` | Actions revealed when dragging left. Each action: { id, label, icon?, color?, onAction?, destructive? }. Destructive actions remove the row. | | `threshold` | `number` | `0.4` | Fraction of the row width that commits a full swipe. | | `elastic` | `number` | `0.5` | Rubber-band resistance past the drag limits. | | `haptics` | `boolean` | `true` | Vibrate briefly on supported devices when a swipe arms or commits. | | `accent` | `string` | `"#ff4d12"` | Focus rings and actions without their own color. | | `radius` | `number` | `14` | Corner radius of the list (or of each card). | | `gap` | `number` | `8` | Space between rows in the card variant. | | `disabled` | `boolean` | `false` | Disable dragging and actions. | | `onSwipe` | `(direction, action, item) => void` | - | Called after an action runs. | | `onRemove` | `(item: T) => void` | - | Called when a destructive action removes an item. | | `emptyState` | `ReactNode` | - | Shown once every item is gone. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { SwipeList } from "@/components/swipe-row" import { Archive, Clock, Trash2 } from "lucide-react" const emails = [ { id: "1", sender: "Maya Chen", subject: "Design review moved to Thursday" }, { id: "2", sender: "Stripe", subject: "Your payout is on the way" }, { id: "3", sender: "Vercel", subject: "Deployment ready" }, ] const leftActions = [{ id: "snooze", label: "Snooze", icon: }] const rightActions = [ { id: "delete", label: "Delete", icon: , color: "#ef4444", destructive: true }, { id: "archive", label: "Archive", icon: , color: "#3b82f6", destructive: true }, ] export function Demo() { return ( email.id} leftActions={leftActions} rightActions={rightActions} renderItem={(email) => (

{email.sender}

{email.subject}

)} /> ) } ``` --- ## Navbar Five minimal, animated navigation bars: glass pill, scroll morph, sliding underline, dynamic island mega-menu and full-screen overlay. - Category: Navigation - Docs: https://trytweenly.vercel.app/docs/components/navbar - Import: `import { Navbar } from "@/components/navbar"` Dependencies: `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/navbar # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/navbar.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `links` (required) | `{ label: string; href: string; description?: string; children?: { label: string; href: string; description?: string }[] }[]` | - | Top-level links. Links with children open a mega-menu in the island variant. | | `variant` | `"pill" \| "morph" \| "underline" \| "island" \| "overlay"` | `"pill"` | Visual style. | | `logo` | `ReactNode` | - | Brand shown on the left. Defaults to a small wordmark. | | `cta` | `{ label: string; href: string }` | - | Call-to-action button on the right. | | `activeHref` | `string` | - | Href of the current page, marked as active. | | `scrollThreshold` | `number` | `40` | Scroll distance before the morph variant turns into a pill, in px. | | `hideOnScroll` | `boolean` | `true` | Hide on fast downward scroll and reveal on scroll up (morph). | | `position` | `"fixed" \| "sticky" \| "absolute"` | `"sticky"` | CSS positioning of the bar. | | `accentColor` | `string` | - | Indicator and CTA color (any CSS color). Defaults to the foreground color. | | `scroller` | `HTMLElement \| null` | - | Element to read the scroll position from. Defaults to the window. | | `onNavigate` | `(href: string, e: MouseEvent) => void` | - | Called when a link is clicked. Call e.preventDefault() to handle routing yourself. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Navbar } from "@/components/navbar" const links = [ { label: "Product", href: "/product", children: [ { label: "Analytics", href: "/analytics", description: "Understand every interaction." }, { label: "Automations", href: "/automations", description: "Workflows that run themselves." }, ], }, { label: "Pricing", href: "/pricing" }, { label: "Docs", href: "/docs" }, ] export function Demo() { return ( ) } ``` --- ## Table of Contents Scroll-synced "On This Page" sidebar with a plane that flies along the heading trail, a sliding rail, or a spotlight list. - Category: Navigation - Docs: https://trytweenly.vercel.app/docs/components/table-of-contents - Import: `import { TableOfContents } from "@/components/table-of-contents"` Dependencies: `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/table-of-contents # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/table-of-contents.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` (required) | `{ id: string; title: string; level?: 1 \| 2 \| 3 }[]` | - | Headings to list, in document order. `id` must match the heading element's id. | | `variant` | `"trail" \| "rail" \| "spotlight"` | `"trail"` | Visual style: a path with a travelling plane, a sliding rail highlight, or glowing dashes. | | `title` | `string` | `"On This Page"` | Header label. | | `activeId` | `string` | - | Controlled active heading id. When omitted, the active heading is tracked from scroll. | | `container` | `HTMLElement \| null` | - | Scroll container whose headings are observed. Defaults to the window and document. | | `offset` | `number` | `96` | Distance from the top of the viewport or container at which a heading becomes active, in px. | | `accentColor` | `string` | `"currentColor"` | Color of the active marker, line and glow (any CSS color). | | `onNavigate` | `(id: string) => void` | - | Called with the heading id when an entry is clicked. | | `smoothScroll` | `boolean` | `true` | Smoothly scroll to the heading on click. When false, jumps instantly. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { TableOfContents } from "@/components/table-of-contents" export function Demo() { return ( ) } ``` --- ## Page Transition Route transitions for the App Router: a provider covers the page, navigates, waits for the new route to render, then reveals it. Curtain, stairs, iris from the click point, card-deck slide and blinds. - Category: Navigation - Docs: https://trytweenly.vercel.app/docs/components/page-transition - Import: `import { PageTransitionProvider } from "@/components/page-transition"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/page-transition # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/page-transition.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"curtain" \| "stairs" \| "iris" \| "slide" \| "blinds"` | `"curtain"` | Transition style. | | `duration` | `number` | `0.8` | Length of each phase (cover and reveal), in seconds. | | `color` | `string` | `"#0a0a0a"` | Cover color. | | `foreground` | `string` | `"#ededed"` | Text color on the cover. | | `accent` | `string` | `"#ff4d12"` | Accent dot beside the label. | | `label` | `string \| ((href: string) => string \| undefined)` | - | Text shown on the cover. TransitionLink's own label overrides it. | | `columns` | `number` | `5` | Number of columns (stairs) or bars (blinds). | | `ease` | `"power4" \| "expo" \| "circ" \| "sine"` | `"power4"` | GSAP ease family used for cover and reveal. | | `position` | `"fixed" \| "absolute"` | `"fixed"` | Cover the viewport, or the nearest positioned parent. | | `navigate` | `(href: string) => void \| Promise` | - | Performs the navigation. Defaults to useRouter().push and waits for usePathname() to change. | | `children` | `ReactNode` | - | Your pages. | | `className` | `string` | - | Classes for the element wrapping your pages (the slide variant transforms it). | | `overlayClassName` | `string` | - | Classes for the cover overlay. | Usage: ```tsx import { PageTransitionProvider } from "@/components/page-transition" // app/layout.tsx: wrap your pages once with the provider below. // Then, anywhere inside, swap / for : // // import { TransitionLink, usePageTransition } from "@/components/page-transition" // // Work // // const { navigate, isTransitioning } = usePageTransition() // navigate("/about", { label: "About" }) export function Demo() { return ( {children} ) } ``` --- ## OTP Input Verification code input with springy digits, a gliding focus ring and animated success and error states. - Category: Forms - Docs: https://trytweenly.vercel.app/docs/components/otp-input - Import: `import { OtpInput } from "@/components/otp-input"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/otp-input # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/otp-input.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"boxes" \| "line"` | `"boxes"` | Rounded boxes, or minimal underlines with large digits. | | `length` | `number` | `6` | Number of characters in the code. | | `value` | `string` | - | Controlled value. | | `defaultValue` | `string` | `""` | Initial value when uncontrolled. | | `onChange` | `(value: string) => void` | - | Called on every change with the sanitized value. | | `onComplete` | `(code: string) => void` | - | Called once every slot is filled. | | `verify` | `(code: string) => Promise \| boolean` | - | Checks the completed code. Shows a loading shimmer while pending, then success or error automatically. | | `status` | `"idle" \| "loading" \| "success" \| "error"` | - | Controlled status. Overrides the internal verify state. | | `masked` | `boolean` | `false` | Show dots instead of characters. | | `pattern` | `"numeric" \| "alphanumeric"` | `"numeric"` | Accepted characters. Alphanumeric input is uppercased. | | `autoFocus` | `boolean` | `false` | Focus the input on mount. | | `disabled` | `boolean` | `false` | Disable input. | | `successColor` | `string` | `"#22c55e"` | Color of the success state. | | `errorColor` | `string` | `"#ef4444"` | Color of the error state. | | `resetOnError` | `boolean` | `true` | Clear the code right-to-left and refocus after an error. | | `collapseOnSuccess` | `boolean` | `true` | Boxes variant: collapse the slots into a check badge on success. | | `label` | `string` | `"Verification code"` | Accessible label for the input. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { OtpInput } from "@/components/otp-input" export function Demo() { return ( code === "123456"} /> ) } ``` --- ## Envelope A send-a-message form as a letter: it folds into the envelope, the flap closes, a seal stamps on and the envelope flies off. - Category: Forms - Docs: https://trytweenly.vercel.app/docs/components/envelope - Import: `import { EnvelopeComposer } from "@/components/envelope"` Dependencies: `gsap`, `@gsap/react`, `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/envelope # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/envelope.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"classic" \| "airmail" \| "minimal" \| "glass"` | `"classic"` | Kraft paper with a wax seal, airmail stripes with a stamp and postmark, flat theme-aware, or frosted glass over a glow. | | `color` | `string` | - | Envelope color. Defaults to the variant's color. | | `paper` | `string` | - | Letter paper color. Defaults to the variant's paper. | | `ink` | `string` | - | Letter text color. Defaults to the variant's ink. | | `seal` | `string` | - | Wax seal, stamp or dot color. Defaults to the variant's seal. | | `accent` | `string` | `"#ff4d12"` | Accent for buttons, focus, glow and the minimal seal. | | `sealLabel` | `string` | - | Initial or monogram on the seal or stamp. Defaults to the sender's initial, else "T". | | `onSend` | `(values: EnvelopeValues) => Promise \| void` | - | Called with the trimmed { name, email, subject, message }. The envelope waits for the promise; a rejection reopens it and shakes the letter. | | `fields` | `{ name?: boolean; email?: boolean; subject?: boolean }` | `"{ name: true, email: true, subject: false }"` | Which fields to show. The message is always shown; name is required when shown, email is validated when filled. | | `placeholders` | `Partial>` | - | Placeholder per field (name, email, subject, message). | | `labels` | `Partial>` | `"{ name: \"From\", email: \"Email\", subject: \"Subject\", message: \"Message\" }"` | Label per field. | | `messageMaxLength` | `number` | `500` | Maximum message length, shown as a live counter. | | `sendLabel` | `string` | `"Send"` | Send button text. | | `againLabel` | `string` | `"Write another"` | Button text in the success state. | | `successTitle` | `string \| ((values: EnvelopeValues) => string)` | - | Success heading, or a function of the sent values. Defaults to "Sent. Thanks, {first name}." | | `successMessage` | `string \| ((values: EnvelopeValues) => string)` | `"Your letter is on its way. We'll write back soon."` | Success body, or a function of the sent values. | | `errorMessage` | `string` | `"Couldn't send. Try again."` | Shown when onSend rejects. An Error's message is used when present. | | `flyDirection` | `"up-right" \| "up" \| "right"` | `"up-right"` | Where the sealed envelope flies off to. | | `resetAfter` | `number \| false` | `false` | Bring a fresh envelope back after this many ms, or wait for "Write another". | | `duration` | `number` | `1` | Speed multiplier for the choreography; 2 plays it twice as slow. | | `width` | `number` | `420` | Envelope width in px. Shrinks to fit its container. | | `radius` | `number` | `14` | Envelope corner radius in px. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { EnvelopeComposer } from "@/components/envelope" export function Demo() { return ( { await fetch("/api/contact", { method: "POST", body: JSON.stringify(values) }) }} /> ) } ``` --- ## Loader Three loading animations: bouncing dots, equalizer bars and an orbit spinner. - Category: Feedback - Docs: https://trytweenly.vercel.app/docs/components/loader - Import: `import { Loader } from "@/components/loader"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/loader # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/loader.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"dots" \| "bars" \| "orbit"` | `"dots"` | Animation style. | | `size` | `number` | `40` | Overall size in px. | | `color` | `string` | `"currentColor"` | Color (any CSS color). | | `speed` | `number` | `1` | Seconds per cycle. Lower is faster. | | `count` | `number` | - | Number of dots or bars. Default 3 for dots, 5 for bars. | | `label` | `string` | `"Loading"` | Accessible label. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Loader } from "@/components/loader" export function Demo() { return ( ) } ``` --- ## Download Button Button that morphs from Download to a live progress fill and a drawn check mark. - Category: Feedback - Docs: https://trytweenly.vercel.app/docs/components/download-button - Import: `import { DownloadButton } from "@/components/download-button"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/download-button # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/download-button.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `href` | `string` | - | File to download, triggered when the animation completes. | | `fileName` | `string` | - | Suggested file name for href. | | `onDownload` | `() => Promise \| void` | - | Run your own download. The bar waits for the promise. | | `progress` | `number` | - | Controlled progress 0–100. The bar follows it instead of simulating. | | `duration` | `number` | `2` | Length of the simulated progress in seconds. | | `resetAfter` | `number` | `2200` | Return to idle after finishing, in ms (0 = stay done). | | `label` | `string` | `"Download"` | Idle label. | | `doneLabel` | `string` | `"Downloaded"` | Finished label. | | `showPercent` | `boolean` | `true` | Show the percentage while loading. | | `fillColor` | `string` | `"color-mix(in oklab, var(--background) 25%, transparent)"` | Progress fill color. | | `...props` | `ButtonHTMLAttributes` | - | Native button attributes are forwarded. | Usage: ```tsx import { DownloadButton } from "@/components/download-button" export function Demo() { return ( ) } ``` --- ## Preloader Full-screen loader with a counting percentage and rotating quotes that reveals the page at 100%. - Category: Feedback - Docs: https://trytweenly.vercel.app/docs/components/preloader - Import: `import { Preloader } from "@/components/preloader"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/preloader # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/preloader.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | - | Page content, revealed when loading finishes. | | `quotes` | `{ text: string; author?: string }[]` | - | Quotes that cycle while loading. | | `duration` | `number` | `3` | Length of the simulated load, in seconds. | | `progress` | `number` | - | Controlled progress 0–100. The counter follows it instead of simulating. | | `exit` | `"curtain" \| "split" \| "fade"` | `"curtain"` | How the loader leaves. | | `background` | `string` | `"#0a0a0a"` | Overlay background. | | `color` | `string` | `"#ededed"` | Text color. | | `accent` | `string` | `"#ff6a2b"` | Progress line color. | | `showCounter` | `boolean` | `true` | Show the big percentage counter. | | `lockScroll` | `boolean` | `true` | Prevent page scrolling while loading. | | `position` | `"fixed" \| "absolute"` | `"fixed"` | Cover the viewport, or the nearest positioned parent. | | `onComplete` | `() => void` | - | Called once the exit animation has finished. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Preloader } from "@/components/preloader" export function Demo() { return ( {/* your page */} ) } ``` --- ## Confetti High-performance canvas confetti with bursts, cannons, fireworks, rain and streams. Ships an imperative confetti() function, a useConfetti hook, a button and a declarative component. - Category: Feedback - Docs: https://trytweenly.vercel.app/docs/components/confetti - Import: `import { ConfettiButton } from "@/components/confetti"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/confetti # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/confetti.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `preset` | `"burst" \| "cannons" \| "fireworks" \| "rain" \| "pride" \| "stream"` | `"burst"` | Effect preset. Cannons fire from both bottom corners, fireworks launch rockets that explode, rain falls from the top, pride and stream shoot from both sides. | | `particleCount` | `number` | `90` | Particles per shot (per cannon, per firework). For rain and streams, the total over duration. Default depends on the preset. | | `spread` | `number` | `70` | Cone width in degrees. Default depends on the preset. | | `angle` | `number` | `90` | Launch direction in degrees. 90 is straight up. | | `startVelocity` | `number` | `45` | Initial speed in px per frame. Default depends on the preset. | | `gravity` | `number` | `1` | Gravity multiplier. | | `drift` | `number` | `0` | Constant sideways drift. Negative drifts left. | | `decay` | `number` | `0.9` | Velocity kept each frame (0 to 1). Lower stops particles faster. | | `scalar` | `number` | `1` | Particle size multiplier. | | `ticks` | `number` | `200` | Particle lifetime in frames (60 per second). | | `duration` | `number` | `2.5` | Length of timed presets (fireworks, rain, pride, stream) in seconds. Default depends on the preset. | | `origin` | `{ x?: number; y?: number }` | - | Launch point as a fraction of the viewport (0 to 1). The button defaults to its own center. | | `colors` | `string[]` | - | Particle colors. Default: ["#ff4d12", "#ff8a4c", "#ffd2b8", "#ededed", "#8a8a8a", "#0a0a0a"]. | | `shapes` | `("square" \| "circle" \| "strip" \| "star")[]` | - | Particle shapes, picked at random. Default: ["square", "circle", "strip"]. | | `emoji` | `string[]` | - | Render these emoji as particles instead of shapes. | | `zIndex` | `number` | `100` | z-index of the full-viewport canvas. | | `disableForReducedMotion` | `boolean` | `true` | Do nothing when the user prefers reduced motion. | | `color` | `string` | `"#ff4d12"` | Button background (any CSS color). | | `children` | `ReactNode` | `"Celebrate"` | Button label. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ConfettiButton } from "@/components/confetti" // Also exported: confetti(options), useConfetti(ref?) and // import { confetti } from "@/components/confetti" // await confetti({ preset: "fireworks", colors: ["#ff4d12", "#ededed"] }) export function Demo() { return ( Ship it ) } ``` --- ## Page Loader Website intro loader orchestrated with GSAP timelines. Five exits: staggered stairs, rolling odometer, flashing greetings with a curved sweep, an iris that opens from a tick dial, and blinds. - Category: Feedback - Docs: https://trytweenly.vercel.app/docs/components/page-loader - Import: `import { PageLoader } from "@/components/page-loader"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/page-loader # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/page-loader.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"stairs" \| "counter" \| "words" \| "iris" \| "blinds"` | `"stairs"` | Visual style of the loader and its exit. | | `children` | `ReactNode` | - | Page content, rendered underneath and revealed when loading finishes. | | `duration` | `number` | `2.4` | Length of the simulated load, in seconds. | | `progress` | `number` | - | Controlled progress 0–100. The loader follows it and exits at 100. | | `words` | `string[]` | - | Words flashed in sequence by the words variant. Defaults to greetings: Hello, Bonjour, Ciao, Hola… | | `columns` | `number` | `5` | Number of columns (stairs) or bars (blinds). | | `background` | `string` | `"#0a0a0a"` | Loader background. | | `color` | `string` | `"#ededed"` | Text color. | | `accent` | `string` | `"#ff4d12"` | Accent for progress marks. | | `ease` | `"power4" \| "expo" \| "circ" \| "sine"` | `"power4"` | GSAP ease family used for the intro and exit. | | `exitDuration` | `number` | `1` | Length of the exit animation, in seconds. | | `label` | `string` | `"Loading"` | Small label shown while loading. | | `showCounter` | `boolean` | `true` | Show the percentage counter. | | `lockScroll` | `boolean` | `true` | Prevent page scrolling while loading (fixed only). | | `position` | `"fixed" \| "absolute"` | `"fixed"` | Cover the viewport, or the nearest positioned parent. | | `onComplete` | `() => void` | - | Called once the exit animation has finished. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { PageLoader } from "@/components/page-loader" export function Demo() { return ( {/* your page */} ) } ``` --- ## Notification Feed Layout-animated notifications as a Sonner-style stack, inbox list, dynamic island or timeline cards, with swipe to dismiss and pausable timers. - Category: Feedback - Docs: https://trytweenly.vercel.app/docs/components/notification-feed - Import: `import { NotificationFeed } from "@/components/notification-feed"` Dependencies: `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/notification-feed # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/notification-feed.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"stack" \| "list" \| "island" \| "cards"` | `"stack"` | Collapsed stack that fans out, inbox list, morphing dynamic island, or timeline cards. | | `items` (required) | `NotificationItem[]` | - | Notifications, newest first: { id, title, description?, icon?, avatar?, time?, tone?, action?, read? }. | | `onDismiss` | `(id: string) => void` | - | Called on swipe, dismiss button or timeout. | | `position` | `"top-right" \| "top-left" \| "bottom-right" \| "bottom-left" \| "top-center" \| "bottom-center"` | `"top-center"` | Corner for the stack and island variants. | | `expand` | `"hover" \| "always" \| "click"` | `"hover"` | When the stack or island opens into a full list. | | `max` | `number` | `4` | Maximum visible items. | | `duration` | `number` | `5000` | Auto-dismiss delay, paused on hover. 0 disables. | | `gap` | `number` | `12` | Space between expanded items. | | `offset` | `number` | `24` | Distance from the container edges. | | `width` | `number` | `360` | Width of the feed. | | `accent` | `string` | `"#ff4d12"` | Progress line, unread dots and focus rings. | | `radius` | `number` | `16` | Corner radius of items. | | `grouped` | `boolean` | `false` | List variant: group items under "Now" and "Earlier". | | `swipeToDismiss` | `boolean` | `true` | Drag items sideways to dismiss. | | `strategy` | `"fixed" \| "absolute"` | `"fixed"` | Positioning context for the stack and island. Use absolute inside a relative container. | | `emptyState` | `ReactNode` | `"You're all caught up"` | Shown by the list and cards variants when empty. | | `label` | `string` | `"Notifications"` | Accessible name of the region. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { NotificationFeed } from "@/components/notification-feed" // Inside your component: // const { items, notify, dismiss, clear } = useNotifications() // // notify({ // title: "Deploy succeeded", // description: "tweenly-docs is live on production.", // tone: "success", // }) export function Demo() { return ( ) } ``` --- ## Chart Line Multi-series line and area chart from plain row data, with draw-in lines, halftone fills, a snapping crosshair tooltip and a toggleable legend. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-line - Import: `import { ChartLine } from "@/components/chart-line"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-line # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-line.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows of data. Each row holds the index value and one number per series. | | `index` (required) | `string` | - | Key in each row used for the x-axis labels. | | `series` (required) | `ChartSeries[]` | - | Keys to plot: { key, label?, color?, texture? }. One line per series. | | `curve` | `"smooth" \| "linear" \| "step"` | `"smooth"` | Line interpolation. Smooth is monotone, so it never overshoots the data. | | `fill` | `"none" \| "gradient" \| "dots" \| "hatch"` | `"dots"` | Area under each line. | | `strokeWidth` | `number` | `2.5` | Line thickness in px. | | `showPoints` | `"none" \| "hover" \| "all" \| "last"` | `"last"` | Which points get a marker. "last" pulses at the end of the first series. | | `showTooltip` | `boolean` | `true` | Crosshair and tooltip on hover. Arrow keys move it when the chart is focused. | | `showLegend` | `boolean` | - | Toggleable legend. Defaults to true when there is more than one series. | | `showGrid` | `boolean` | `true` | Horizontal grid lines at each y tick. | | `showXAxis` | `boolean` | `true` | Index labels under the plot. | | `showYAxis` | `boolean` | `true` | Value labels left of the plot. | | `yTicks` | `number` | `4` | Approximate number of y-axis ticks. | | `yMin` | `number` | - | Lower bound of the y domain. Defaults to 0, or a nice minimum for negative data. | | `yMax` | `number` | - | Upper bound of the y domain. Defaults to a nice maximum above the data. | | `xLabelEvery` | `number` | - | Show every nth x label. Defaults to auto-thinning based on width. | | `height` | `number` | `220` | Plot height in px, excluding the x-axis. | | `total` | `"sum" \| "last" \| "none"` | `"last"` | Headline figure computed from the first series. | | `delta` | `number \| "auto"` | `"auto"` | Change pill next to the headline, in percent. "auto" compares the first and last value of the first series. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface. | | `accent` | `string` | `"#FF4D12"` | Main accent color; the series palette is derived from it. | | `palette` | `string[]` | - | Series colors in order; overrides the accent-derived palette. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in the headline, labels and tooltip. Axis labels use a compact variant. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to auto. | | `animate` | `boolean` | `true` | Play the entrance animation. Reduced motion always disables it. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartLine } from "@/components/chart-line" const data = [ { month: "Jan", revenue: 18600, expenses: 12400 }, { month: "Feb", revenue: 21400, expenses: 13100 }, { month: "Mar", revenue: 19800, expenses: 12900 }, { month: "Apr", revenue: 24300, expenses: 14200 }, { month: "May", revenue: 26900, expenses: 15600 }, { month: "Jun", revenue: 25100, expenses: 15100 }, { month: "Jul", revenue: 28700, expenses: 16300 }, { month: "Aug", revenue: 31200, expenses: 17000 }, { month: "Sep", revenue: 29400, expenses: 16800 }, { month: "Oct", revenue: 33800, expenses: 18200 }, { month: "Nov", revenue: 36100, expenses: 19400 }, { month: "Dec", revenue: 39500, expenses: 20600 }, ] export function Demo() { return ( ) } ``` --- ## Chart Bar Grouped or stacked bar chart, vertical or horizontal, with springy staggered growth, textured series, hover highlight and a toggleable legend. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-bar - Import: `import { ChartBar } from "@/components/chart-bar"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-bar # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-bar.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows of data. Each row holds the index value and one number per series. Negative values grow down from a zero line. | | `index` (required) | `string` | - | Key in each row used for the category labels. | | `series` (required) | `ChartSeries[]` | - | Keys to plot: { key, label?, color?, texture? }. Textures default to solid, hatch, muted. | | `layout` | `"vertical" \| "horizontal"` | `"vertical"` | "vertical" draws columns, "horizontal" draws rows. | | `stacked` | `boolean` | `false` | Stack series instead of grouping them side by side. | | `barRadius` | `number` | `6` | Bar corner radius in px. | | `barGap` | `number` | `4` | Gap between bars in a group, or between segments in a stack, in px. | | `categoryGap` | `number` | `0.3` | Share of each category slot left empty. | | `showValues` | `boolean` | `false` | Value labels at the end of each bar (each stack when stacked). | | `highlight` | `"hover" \| "none"` | `"hover"` | "hover" dims the other categories while one is hovered. | | `showTooltip` | `boolean` | `true` | Tooltip on hover. Arrow keys move it when the chart is focused. | | `showLegend` | `boolean` | - | Toggleable legend. Defaults to true when there is more than one series. | | `showGrid` | `boolean` | `true` | Grid lines at each value tick. | | `showXAxis` | `boolean` | `true` | Bottom axis labels (categories when vertical, values when horizontal). | | `showYAxis` | `boolean` | `true` | Left axis labels (values when vertical, categories when horizontal). | | `yTicks` | `number` | `4` | Approximate number of value-axis ticks. | | `height` | `number` | `220` | Plot height in px, excluding the bottom axis. | | `total` | `"sum" \| "last" \| "none"` | `"sum"` | Headline figure computed from the first series. | | `delta` | `number \| "auto"` | `"auto"` | Change pill next to the headline, in percent. "auto" compares the first and last value of the first series. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface. | | `accent` | `string` | `"#FF4D12"` | Main accent color; the series palette is derived from it. | | `palette` | `string[]` | - | Series colors in order; overrides the accent-derived palette. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in the headline, labels and tooltip. Axis labels use a compact variant. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to auto. | | `animate` | `boolean` | `true` | Play the entrance animation. Reduced motion always disables it. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartBar } from "@/components/chart-bar" const data = [ { month: "Jan", desktop: 186, mobile: 80 }, { month: "Feb", desktop: 305, mobile: 200 }, { month: "Mar", desktop: 237, mobile: 120 }, { month: "Apr", desktop: 73, mobile: 190 }, { month: "May", desktop: 209, mobile: 130 }, { month: "Jun", desktop: 214, mobile: 140 }, ] export function Demo() { return ( ) } ``` --- ## Chart Radial Donut, pie or gauge from your rows. Slices sweep in one after another, re-flow when toggled from the legend and pop out on hover. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-radial - Import: `import { ChartRadial } from "@/components/chart-radial"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-radial # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-radial.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows to plot, one slice per row. | | `nameKey` (required) | `string` | - | Key holding each slice's name, e.g. "browser". | | `valueKey` (required) | `string` | - | Key holding each slice's value, e.g. "visitors". | | `colorKey` | `string` | - | Optional key holding a CSS color per row. | | `textures` | `ChartTexture[]` | `"[\"solid\", \"hatch\", \"solid\", \"muted\", \"hatch\", \"muted\"]"` | Fill styles cycled per slice: "solid", "hatch", "dots" or "muted". | | `innerRadius` | `number` | `0.62` | Hole size as a fraction of the radius; 0 draws a pie. | | `padAngle` | `number` | `1.5` | Gap between slices in degrees. | | `cornerRadius` | `number` | `4` | Rounding of slice corners in px. | | `startAngle` | `number` | `0` | Where the first slice starts, in degrees (0 = 12 o'clock). | | `endAngle` | `number` | `360` | Where the last slice ends. Use -90 / 90 for a half-circle gauge. | | `showLabels` | `"none" \| "percent" \| "value" \| "name"` | `"percent"` | Text drawn on each slice; hidden on slices too small to fit it. | | `center` | `"total" \| "active" \| "none"` | `"total"` | Content of the hole. "total" switches to the hovered slice; "active" shows the hovered, highlighted or largest slice's share. | | `centerLabel` | `string` | `"Total"` | Caption under the total. | | `highlight` | `number` | - | Index (after sorting) of a slice pushed outward. | | `hoverEffect` | `"explode" \| "dim" \| "none"` | `"explode"` | What hovering a slice does. | | `showLegend` | `boolean` | `true` | Legend with values and shares; click a row to toggle its slice. | | `showTooltip` | `boolean` | `false` | Show a tooltip next to the pointer. | | `size` | `number` | `220` | Chart diameter in px. | | `sortBy` | `"none" \| "value"` | `"none"` | Slice order. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface; every color derives from it. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; overrides the accent-derived palette. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels, legends and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to automatic. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartRadial } from "@/components/chart-radial" const data = [ { browser: "Chrome", visitors: 275 }, { browser: "Safari", visitors: 200 }, { browser: "Firefox", visitors: 187 }, { browser: "Edge", visitors: 173 }, { browser: "Other", visitors: 90 }, ] export function Demo() { return ( ) } ``` --- ## Chart Rings Concentric progress rings against per-row goals. Rings fill with a springy stagger, numbers count up and overshoot draws a second lap. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-rings - Import: `import { ChartRings } from "@/components/chart-rings"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-rings # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-rings.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows to plot, one ring per row, outermost first. | | `nameKey` (required) | `string` | - | Key holding each ring's name, e.g. "goal". | | `valueKey` (required) | `string` | - | Key holding each ring's current value. | | `maxKey` | `string` | - | Key holding a per-row goal; falls back to max. | | `max` | `number` | `100` | Goal used when a row has no maxKey value. | | `colorKey` | `string` | - | Optional key holding a CSS color per row. | | `thickness` | `number` | `14` | Ring thickness in px. | | `gap` | `number` | `6` | Space between rings in px. | | `trackTexture` | `"solid" \| "hatch" \| "none"` | `"hatch"` | Style of the unfilled track. | | `roundedCaps` | `boolean` | `true` | Round the ends of each ring. | | `startAngle` | `number` | `0` | Where rings start filling, in degrees (0 = 12 o'clock). | | `showValues` | `"percent" \| "value" \| "none"` | `"percent"` | What the list next to the rings shows. | | `layout` | `"side" \| "stacked"` | `"side"` | List beside the rings or stacked under them. | | `size` | `number` | `200` | Chart diameter in px. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface; every color derives from it. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; overrides the accent-derived palette. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels, legends and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to automatic. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartRings } from "@/components/chart-rings" const data = [ { goal: "Move", value: 420, target: 600 }, { goal: "Exercise", value: 38, target: 30 }, { goal: "Stand", value: 9, target: 12 }, ] export function Demo() { return ( ) } ``` --- ## Chart Radar Spider chart comparing series across axes. The grid draws in, polygons spring out from the center and hovering an axis lists every value. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-radar - Import: `import { ChartRadar } from "@/components/chart-radar"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-radar # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-radar.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows to plot, one axis per row. | | `index` (required) | `string` | - | Key holding each axis label, e.g. "metric". | | `series` (required) | `ChartSeries[]` | - | Series drawn as polygons: { key, label?, color?, texture? }. | | `max` | `number` | - | Value at the outer ring. Defaults to a nice number above the largest value. | | `levels` | `number` | `4` | Number of grid rings. | | `gridShape` | `"polygon" \| "circle"` | `"polygon"` | Shape of the grid rings. | | `fillOpacity` | `number` | `0.25` | Opacity of solid fills; textured fills scale with it. | | `showPoints` | `boolean` | `true` | Draw a dot at every value. | | `showAxisLabels` | `boolean` | `true` | Show axis labels around the chart. | | `showLevelLabels` | `boolean` | `false` | Show each ring's value on the top axis. | | `showTooltip` | `boolean` | `true` | Hovering an axis highlights it and lists every series' value. | | `showLegend` | `boolean` | `true` | Legend; click an item to toggle its series. | | `size` | `number` | `260` | Chart height in px; labels get extra room on the sides. | | `curve` | `"linear" \| "smooth"` | `"linear"` | Polygon edges: straight or a closed smooth curve. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface; every color derives from it. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; overrides the accent-derived palette. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels, legends and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to automatic. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartRadar } from "@/components/chart-radar" const data = [ { metric: "Speed", team: 86, benchmark: 72 }, { metric: "Quality", team: 78, benchmark: 80 }, { metric: "Uptime", team: 94, benchmark: 88 }, { metric: "Security", team: 70, benchmark: 76 }, { metric: "Cost", team: 62, benchmark: 70 }, { metric: "Support", team: 88, benchmark: 65 }, ] export function Demo() { return ( ) } ``` --- ## Chart KPI Stat card with a count-up headline, delta pill, halftone sparkline with scrubbing, and an optional goal bar. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-kpi - Import: `import { ChartKpi } from "@/components/chart-kpi"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-kpi # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-kpi.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows of data, oldest first. | | `index` (required) | `string` | - | Key holding each row's label, e.g. "date". | | `valueKey` (required) | `string` | - | Key holding the metric. | | `compareKey` | `string` | - | Key holding the previous period, drawn as a dashed line and used for the delta. | | `aggregate` | `"sum" \| "last" \| "average"` | `"sum"` | How rows combine into the headline figure. | | `delta` | `number \| "auto"` | `"auto"` | Change in percent. "auto" compares with compareKey, or the first row with the last. | | `goal` | `number` | - | Target for the headline; shows a thin progress bar. | | `sparkline` | `"area" \| "line" \| "bars"` | `"area"` | Sparkline style. "area" uses a halftone dot fill. | | `curve` | `"smooth" \| "linear" \| "step"` | `"smooth"` | Line interpolation. | | `sparkHeight` | `number` | `64` | Sparkline height in px. | | `showTooltip` | `boolean` | `true` | Scrub the sparkline to see each row. | | `layout` | `"stacked" \| "inline"` | `"stacked"` | Sparkline under the number, or beside it. | | `trendColor` | `"accent" \| "semantic"` | `"accent"` | "semantic" paints rises green and falls red. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; the first overrides the accent for the fill. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to auto. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartKpi } from "@/components/chart-kpi" const data = [ { date: "Jun 01", revenue: 4210, previous: 3890 }, { date: "Jun 02", revenue: 3980, previous: 4020 }, { date: "Jun 03", revenue: 4630, previous: 3710 }, { date: "Jun 04", revenue: 4890, previous: 3950 }, { date: "Jun 05", revenue: 4420, previous: 4180 }, { date: "Jun 06", revenue: 3610, previous: 3320 }, { date: "Jun 07", revenue: 3340, previous: 3050 }, { date: "Jun 08", revenue: 4750, previous: 3980 }, { date: "Jun 09", revenue: 5120, previous: 4110 }, { date: "Jun 10", revenue: 5380, previous: 4270 }, { date: "Jun 11", revenue: 4960, previous: 4390 }, { date: "Jun 12", revenue: 5570, previous: 4210 }, { date: "Jun 13", revenue: 4280, previous: 3760 }, { date: "Jun 14", revenue: 5840, previous: 3940 }, ] export function Demo() { return ( ) } ``` --- ## Chart Heatmap GitHub-style contribution calendar with hatched empty days, a diagonal reveal wave, and hover tooltips. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-heatmap - Import: `import { ChartHeatmap } from "@/components/chart-heatmap"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-heatmap # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-heatmap.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `data` (required) | `ChartDatum[]` | - | Rows like { date: "2025-06-01", count: 4 }. Missing days count as zero; dates are read as UTC. | | `dateKey` | `string` | `"date"` | Key holding an ISO date (YYYY-MM-DD) or a timestamp. | | `valueKey` | `string` | `"count"` | Key holding the value for that day. | | `weeks` | `number` | `20` | Weeks shown, ending at the latest date. | | `levels` | `number` | `5` | Color steps, including the empty step. | | `cellSize` | `number` | `12` | Cell size in px. | | `cellGap` | `number` | `3` | Space between cells in px. | | `cellRadius` | `number` | `3` | Cell corner radius in px. | | `weekStart` | `0 \| 1` | `0` | First day of the week: 0 = Sunday, 1 = Monday. | | `showMonthLabels` | `boolean` | `true` | Show month names above the grid. | | `showDayLabels` | `boolean` | `true` | Show Mon / Wed / Fri beside the grid. | | `emptyTexture` | `"solid" \| "hatch"` | `"hatch"` | Fill of days with no activity. | | `scale` | `"linear" \| "quantile"` | `"linear"` | "quantile" spreads colors evenly across your data; "linear" scales to the busiest day. | | `showTooltip` | `boolean` | `true` | Show a tooltip on hover. | | `showLegend` | `boolean` | `true` | Show the Less / More key. | | `showTotal` | `boolean` | `true` | Show the total for the range as the headline. | | `unit` | `string` | `"contributions"` | Word used in the tooltip. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; the first overrides the accent for the fill. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to auto. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartHeatmap } from "@/components/chart-heatmap" const data = [ { date: "2025-02-10", count: 10 }, { date: "2025-02-11", count: 10 }, { date: "2025-02-12", count: 0 }, { date: "2025-02-13", count: 4 }, { date: "2025-02-14", count: 0 }, { date: "2025-02-15", count: 0 }, { date: "2025-02-16", count: 4 }, { date: "2025-02-17", count: 5 }, { date: "2025-02-18", count: 7 }, { date: "2025-02-19", count: 9 }, { date: "2025-02-20", count: 6 }, { date: "2025-02-21", count: 4 }, { date: "2025-02-22", count: 0 }, { date: "2025-02-23", count: 0 }, { date: "2025-02-24", count: 10 }, { date: "2025-02-25", count: 2 }, { date: "2025-02-26", count: 1 }, { date: "2025-02-27", count: 6 }, { date: "2025-02-28", count: 0 }, { date: "2025-03-01", count: 0 }, { date: "2025-03-02", count: 4 }, { date: "2025-03-03", count: 0 }, { date: "2025-03-04", count: 12 }, { date: "2025-03-05", count: 8 }, { date: "2025-03-06", count: 4 }, { date: "2025-03-07", count: 9 }, { date: "2025-03-08", count: 0 }, { date: "2025-03-09", count: 0 }, { date: "2025-03-10", count: 8 }, { date: "2025-03-11", count: 13 }, { date: "2025-03-12", count: 2 }, { date: "2025-03-13", count: 12 }, { date: "2025-03-14", count: 7 }, { date: "2025-03-15", count: 0 }, { date: "2025-03-16", count: 0 }, { date: "2025-03-17", count: 0 }, { date: "2025-03-18", count: 0 }, { date: "2025-03-19", count: 11 }, { date: "2025-03-20", count: 0 }, { date: "2025-03-21", count: 10 }, { date: "2025-03-22", count: 2 }, { date: "2025-03-23", count: 0 }, { date: "2025-03-24", count: 12 }, { date: "2025-03-25", count: 7 }, { date: "2025-03-26", count: 14 }, { date: "2025-03-27", count: 13 }, { date: "2025-03-28", count: 4 }, { date: "2025-03-29", count: 4 }, { date: "2025-03-30", count: 0 }, { date: "2025-03-31", count: 0 }, { date: "2025-04-01", count: 12 }, { date: "2025-04-02", count: 3 }, { date: "2025-04-03", count: 11 }, { date: "2025-04-04", count: 5 }, { date: "2025-04-05", count: 0 }, { date: "2025-04-06", count: 0 }, { date: "2025-04-07", count: 3 }, { date: "2025-04-08", count: 11 }, { date: "2025-04-09", count: 4 }, { date: "2025-04-10", count: 5 }, { date: "2025-04-11", count: 0 }, { date: "2025-04-12", count: 0 }, { date: "2025-04-13", count: 0 }, { date: "2025-04-14", count: 14 }, { date: "2025-04-15", count: 4 }, { date: "2025-04-16", count: 9 }, { date: "2025-04-17", count: 0 }, { date: "2025-04-18", count: 13 }, { date: "2025-04-19", count: 5 }, { date: "2025-04-20", count: 0 }, { date: "2025-04-21", count: 7 }, { date: "2025-04-22", count: 15 }, { date: "2025-04-23", count: 5 }, { date: "2025-04-24", count: 14 }, { date: "2025-04-25", count: 16 }, { date: "2025-04-26", count: 0 }, { date: "2025-04-27", count: 1 }, { date: "2025-04-28", count: 4 }, { date: "2025-04-29", count: 14 }, { date: "2025-04-30", count: 10 }, { date: "2025-05-01", count: 3 }, { date: "2025-05-02", count: 0 }, { date: "2025-05-03", count: 0 }, { date: "2025-05-04", count: 0 }, { date: "2025-05-05", count: 2 }, { date: "2025-05-06", count: 9 }, { date: "2025-05-07", count: 3 }, { date: "2025-05-08", count: 9 }, { date: "2025-05-09", count: 10 }, { date: "2025-05-10", count: 0 }, { date: "2025-05-11", count: 2 }, { date: "2025-05-12", count: 3 }, { date: "2025-05-13", count: 14 }, { date: "2025-05-14", count: 2 }, { date: "2025-05-15", count: 2 }, { date: "2025-05-16", count: 3 }, { date: "2025-05-17", count: 3 }, { date: "2025-05-18", count: 3 }, { date: "2025-05-19", count: 6 }, { date: "2025-05-20", count: 6 }, { date: "2025-05-21", count: 0 }, { date: "2025-05-22", count: 11 }, { date: "2025-05-23", count: 15 }, { date: "2025-05-24", count: 0 }, { date: "2025-05-25", count: 0 }, { date: "2025-05-26", count: 18 }, { date: "2025-05-27", count: 1 }, { date: "2025-05-28", count: 10 }, { date: "2025-05-29", count: 0 }, { date: "2025-05-30", count: 18 }, { date: "2025-05-31", count: 0 }, { date: "2025-06-01", count: 0 }, { date: "2025-06-02", count: 5 }, { date: "2025-06-03", count: 19 }, { date: "2025-06-04", count: 3 }, { date: "2025-06-05", count: 3 }, { date: "2025-06-06", count: 1 }, { date: "2025-06-07", count: 0 }, { date: "2025-06-08", count: 0 }, { date: "2025-06-09", count: 19 }, { date: "2025-06-10", count: 1 }, { date: "2025-06-11", count: 9 }, { date: "2025-06-12", count: 0 }, { date: "2025-06-13", count: 15 }, { date: "2025-06-14", count: 0 }, { date: "2025-06-15", count: 0 }, { date: "2025-06-16", count: 18 }, { date: "2025-06-17", count: 13 }, { date: "2025-06-18", count: 0 }, { date: "2025-06-19", count: 0 }, { date: "2025-06-20", count: 20 }, { date: "2025-06-21", count: 6 }, { date: "2025-06-22", count: 6 }, { date: "2025-06-23", count: 0 }, { date: "2025-06-24", count: 5 }, { date: "2025-06-25", count: 0 }, { date: "2025-06-26", count: 12 }, { date: "2025-06-27", count: 19 }, { date: "2025-06-28", count: 4 }, { date: "2025-06-29", count: 0 }, ] export function Demo() { return ( ) } ``` --- ## Chart Progress Progress toward a real target: a wavy, grainy fill over a hatched remainder, with a thermometer marker, quota line and stacked segments. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-progress - Import: `import { ChartProgress } from "@/components/chart-progress"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-progress # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-progress.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` (required) | `number` | - | Current amount, in the same unit as max. Ignored when segments are given. | | `max` | `number` | `100` | Amount that fills the bar. | | `target` | `number` | - | Marker for a goal or quota, in the same unit as value. | | `targetLabel` | `string` | `"Target"` | Label of the target marker. | | `label` | `string` | `"Progress"` | Caption above the bar. | | `showPercent` | `boolean` | `true` | Show the share of max as a big percentage. | | `segments` | `{ label: string; value: number }[]` | - | Split the fill into stacked, textured parts with a legend; their sum becomes the value. | | `thickness` | `number` | `40` | Bar thickness in px. | | `length` | `number` | `220` | Bar length in px when vertical. | | `texture` | `"wave" \| "solid" \| "grain"` | `"wave"` | Look of the fill. | | `remainder` | `"hatch" \| "track" \| "none"` | `"hatch"` | Look of the unfilled part. | | `showMarker` | `boolean` | `true` | Show the thermometer marker at the end of the fill. | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Bar direction. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; the first overrides the accent for the fill. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.4` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to auto. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartProgress } from "@/components/chart-progress" export function Demo() { return ( ) } ``` --- ## Chart Dots Dot matrix of filled versus hatched units with a partial last dot, spring wave in fill order, and a huge count-up figure. - Category: Charts - Docs: https://trytweenly.vercel.app/docs/components/chart-dots - Import: `import { ChartDots } from "@/components/chart-dots"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/chart-dots # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/chart-dots.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `value` (required) | `number` | - | Filled amount, e.g. 41 seats. | | `total` | `number` | `100` | Amount that fills every dot, e.g. 50 seats. | | `columns` | `number` | `6` | Dots per row. | | `rows` | `number` | `4` | Number of rows. | | `partial` | `boolean` | `true` | Show a fractional share as a partly filled dot. | | `caption` | `string` | `"Pattern Hero"` | Text under the headline figure. | | `display` | `"percent" \| "value" \| "fraction"` | `"percent"` | Headline as a percentage, the raw value, or "value/total". | | `shape` | `"circle" \| "square" \| "rounded"` | `"circle"` | Dot shape. | | `emptyTexture` | `"hatch" \| "track"` | `"hatch"` | Fill of empty dots. | | `order` | `"row" \| "column" \| "spiral"` | `"row"` | Order in which dots fill. | | `gap` | `number` | `10` | Space between dots in px. | | `title` | `string` | - | Card heading. | | `description` | `string` | - | Small line under the heading. | | `titleSize` | `"hero" \| "default"` | `"default"` | "hero" is a huge bold heading; "default" is a compact dashboard header. | | `surface` | `"dark" \| "light"` | `"dark"` | Card surface. | | `accent` | `string` | `"#FF4D12"` | Main accent color. | | `palette` | `string[]` | - | Colors in order; the first overrides the accent for the fill. | | `icon` | `ReactNode` | - | Content of the round badge in the top-right corner. Pass null to hide it. | | `radius` | `number` | `28` | Card corner radius in px. | | `bare` | `boolean` | `false` | Render only the chart, without the card. | | `animate` | `boolean` | `true` | Play the entrance animation. | | `duration` | `number` | `1.2` | Entrance duration in seconds. | | `delay` | `number` | `0` | Delay before the entrance starts, in seconds. | | `once` | `boolean` | `true` | Animate only the first time the chart enters view. | | `valueFormat` | `"number" \| "compact" \| "percent" \| "currency" \| ((value: number) => string)` | `"number"` | How values are formatted in labels and tooltips. | | `currency` | `string` | `"USD"` | Currency code used when valueFormat is "currency". | | `decimals` | `number` | - | Fraction digits for formatted values. Defaults to auto. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ChartDots } from "@/components/chart-dots" export function Demo() { return ( ) } ``` --- ## ASCII Image Renders any image as ASCII art, with a glittering pixel trail under the cursor. - Category: Media - Docs: https://trytweenly.vercel.app/docs/components/ascii-image - Import: `import { AsciiImage } from "@/components/ascii-image"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/ascii-image # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/ascii-image.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `src` (required) | `string` | - | Image URL. Remote images must allow CORS so their pixels can be read. | | `alt` (required) | `string` | - | Accessible description of the image. | | `cellSize` | `number` | `10` | Size of each character cell in px. | | `characters` | `string` | `" .:-=+*#%@"` | Characters from lightest to densest. | | `color` | `string` | `"#3b5bff"` | Character color (any canvas color, not CSS variables). | | `sparkColor` | `string` | `"#ffffff"` | Color of the glitter sparks near the cursor. | | `radius` | `number` | `90` | Radius of the hover region in px. | | `decay` | `number` | `0.93` | How long the hover trail lingers, 0 to 1. | | `glitter` | `number` | `0.12` | Chance per frame that a lit cell sparkles. | | `contrast` | `number` | `1.3` | Contrast applied before mapping to characters. | | `invert` | `boolean` | `false` | Map dark pixels to dense characters. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { AsciiImage } from "@/components/ascii-image" export function Demo() { return ( ) } ``` --- ## Hover Media Inline keyword that reveals a floating image or video following the cursor. - Category: Media - Docs: https://trytweenly.vercel.app/docs/components/hover-media - Import: `import { HoverMedia } from "@/components/hover-media"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/hover-media # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/hover-media.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | The keyword(s) that trigger the preview. | | `src` (required) | `string` | - | Image or video URL. | | `type` | `"image" \| "video"` | - | Media type. Detected from the file extension when omitted. | | `alt` | `string` | `""` | Alt text for images. | | `width` | `number` | `220` | Preview width in px. | | `aspectRatio` | `string` | `"4 / 3"` | Preview aspect ratio. | | `tilt` | `number` | `12` | Maximum tilt in degrees, driven by cursor speed. | | `offset` | `number` | `20` | Distance from the cursor in px. | | `stiffness` | `number` | `300` | Spring stiffness for the follow motion. | | `damping` | `number` | `28` | Spring damping for the follow motion. | | `className` | `string` | - | Additional classes for the root element. | | `mediaClassName` | `string` | - | Classes for the floating preview. | Usage: ```tsx import { HoverMedia } from "@/components/hover-media" export function Demo() { return (

We craft{" "} motion

) } ``` --- ## Image Fan One image rises from below, then the rest slide out from under it into an overlapping fan. - Category: Media - Docs: https://trytweenly.vercel.app/docs/components/image-fan - Import: `import { ImageFan } from "@/components/image-fan"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/image-fan # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/image-fan.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `images` (required) | `ImageFanItem[]` | - | { src, alt }[] — e.g. files in public/gallery/. | | `direction` | `"right" \| "left" \| "center"` | `"right"` | Which way the stack spreads after the first image rises. | | `speed` | `number` | `1` | Speed multiplier. 2 is twice as fast. | | `blur` | `number` | `10` | Blur in px while cards move in. | | `rise` | `number` | `1.2` | How far the first card rises from, in card heights. | | `overlap` | `number` | `0.72` | Gap between cards as a fraction of card width (shrinks to fit). | | `rotate` | `number` | `3` | Tilt added per card, in degrees. | | `arc` | `number` | `6` | Downward curve toward the ends, in px. | | `stagger` | `number` | `0.06` | Delay between cards sliding out, in seconds. | | `radius` | `number` | `14` | Corner radius in px. | | `hoverLift` | `boolean` | `true` | Lift cards on hover. | | `cardWidth` | `string` | `"min(20cqw, 220px)"` | Card width (any CSS length). | | `aspectRatio` | `string` | `"3 / 4"` | Card aspect ratio. | | `once` | `boolean` | `true` | Play only the first time it enters view. | | `scroller` | `string \| HTMLElement` | - | Scroll container to watch instead of the window. | | `height` | `string` | `"28rem"` | Section height. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ImageFan } from "@/components/image-fan" export function Demo() { return ( ) } ``` --- ## Image Arc Images on a half-circle wheel that turns on its own or with the scroll. - Category: Media - Docs: https://trytweenly.vercel.app/docs/components/image-arc - Import: `import { ImageArc } from "@/components/image-arc"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/image-arc # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/image-arc.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `images` (required) | `ImageArcItem[]` | - | { src, alt }[]. Repeated around the wheel if there aren't enough. | | `mode` | `"auto" \| "scroll"` | `"auto"` | Spin continuously, or turn as the page scrolls. | | `duration` | `number` | `80` | Seconds per revolution (auto). Lower is faster. | | `direction` | `"clockwise" \| "counterclockwise"` | `"clockwise"` | Spin direction. | | `scrollRotation` | `number` | `120` | Degrees turned while scrolling past (scroll mode). | | `radius` | `number` | `0.42` | Wheel radius as a fraction of the container width. | | `itemSize` | `number` | `0.26` | Card width as a fraction of the radius. | | `gap` | `number` | `28` | Space between cards along the circle, in px. | | `rounded` | `number` | `12` | Corner radius in px. | | `pauseOnHover` | `boolean` | `true` | Pause while a card is hovered (auto). | | `fade` | `boolean` | `true` | Fade the lower edge into the background. | | `aspectRatio` | `string` | `"3 / 4"` | Card aspect ratio. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track in scroll mode. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ImageArc } from "@/components/image-arc" export function Demo() { return ( ) } ``` --- ## Scroll Text Reveal Pins the section and reveals text as you scroll. The page resumes once the reveal completes. - Category: Scroll - Docs: https://trytweenly.vercel.app/docs/components/scroll-text-reveal - Import: `import { ScrollTextReveal } from "@/components/scroll-text-reveal"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/scroll-text-reveal # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/scroll-text-reveal.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `text` (required) | `string` | - | The text to reveal. | | `split` | `"word" \| "char"` | `"word"` | Reveal word by word or character by character. | | `dim` | `number` | `0.15` | Opacity of unrevealed text, 0 to 1. | | `blur` | `number` | `0` | Starting blur in px. | | `lift` | `number` | `0` | Starting vertical offset in px. | | `scrollLength` | `number` | `2` | Section heights of scrolling the reveal lasts. | | `scrub` | `number` | `0.8` | Scroll smoothing in seconds (0 = instant). | | `pin` | `boolean` | `true` | Hold the section in place until the reveal completes. | | `align` | `"left" \| "center"` | `"left"` | Text alignment. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track instead of the window. | | `height` | `string` | `"100vh"` | Height of the pinned section. | | `className` | `string` | - | Additional classes for the root element. | | `textClassName` | `string` | - | Classes for the text element. | Usage: ```tsx import { ScrollTextReveal } from "@/components/scroll-text-reveal" export function Demo() { return ( ) } ``` --- ## Horizontal Scroll Pins a row of images and scrolls it sideways with the page, then releases to normal scroll. - Category: Scroll - Docs: https://trytweenly.vercel.app/docs/components/horizontal-scroll - Import: `import { HorizontalScroll } from "@/components/horizontal-scroll"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/horizontal-scroll # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/horizontal-scroll.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` (required) | `HorizontalScrollItem[]` | - | { src, alt, caption? }[] | | `itemWidth` | `string` | `"min(70cqw, 520px)"` | Width of each image (any CSS length). | | `aspectRatio` | `string` | `"4 / 5"` | Image aspect ratio. | | `gap` | `number` | `24` | Gap between images in px. | | `speed` | `number` | `1` | Scroll distance multiplier. Higher is slower. | | `scrub` | `number` | `1` | Scroll smoothing in seconds. | | `parallax` | `boolean` | `true` | Subtle zoom on each image as it crosses the viewport. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track instead of the window. | | `height` | `string` | `"100vh"` | Height of the pinned section. | | `className` | `string` | - | Additional classes for the root element. | | `itemClassName` | `string` | - | Classes for each image frame. | Usage: ```tsx import { HorizontalScroll } from "@/components/horizontal-scroll" export function Demo() { return ( ) } ``` --- ## Image Reveal Images fly in from their own direction as they scroll into view. - Category: Scroll - Docs: https://trytweenly.vercel.app/docs/components/image-reveal - Import: `import { ImageReveal } from "@/components/image-reveal"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/image-reveal # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/image-reveal.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `images` (required) | `ImageRevealItem[]` | - | { src, alt, from?: "left" \| "right" \| "top" \| "bottom" \| "scale", span? }[] | | `columns` | `number` | `2` | Grid columns. | | `gap` | `number` | `16` | Gap between images in px. | | `distance` | `number` | `160` | Travel distance in px. | | `rotate` | `number` | `4` | Starting rotation in degrees (left / right only). | | `blur` | `number` | `0` | Starting blur in px. | | `scrub` | `boolean` | `true` | Tie progress to the scrollbar. Off plays once on enter. | | `duration` | `number` | `0.9` | Duration when scrub is off, in seconds. | | `aspectRatio` | `string` | `"4 / 3"` | Image aspect ratio. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track instead of the window. | | `className` | `string` | - | Additional classes for the root element. | | `itemClassName` | `string` | - | Classes for each image frame. | Usage: ```tsx import { ImageReveal } from "@/components/image-reveal" export function Demo() { return ( ) } ``` --- ## Expand Gallery A row of small thumbnails where each one grows large in turn as you scroll. - Category: Scroll - Docs: https://trytweenly.vercel.app/docs/components/expand-gallery - Import: `import { ExpandGallery } from "@/components/expand-gallery"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/expand-gallery # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/expand-gallery.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `images` (required) | `ExpandGalleryItem[]` | - | { src, alt }[] | | `smallWidth` | `number` | `9` | Thumbnail width, % of section width. | | `largeWidth` | `number` | `20` | Expanded width, % of section width. | | `largeHeight` | `number` | `72` | Expanded height, % of section height. | | `smallAspect` | `string` | `"3 / 4"` | Thumbnail aspect ratio. | | `gap` | `number` | `12` | Gap between images in px. | | `align` | `"start" \| "center"` | `"start"` | Row alignment. | | `scrollPerImage` | `number` | `0.6` | Section heights of scrolling per image. | | `scrub` | `number` | `0.8` | Scroll smoothing in seconds. | | `counter` | `boolean` | `true` | Show an "03 / 06" counter. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track instead of the window. | | `height` | `string` | `"100vh"` | Height of the pinned section. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ExpandGallery } from "@/components/expand-gallery" export function Demo() { return ( ) } ``` --- ## Scroll Focus A column of images where the one in the center grows, with a title list that stays in sync. - Category: Scroll - Docs: https://trytweenly.vercel.app/docs/components/scroll-focus - Import: `import { ScrollFocus } from "@/components/scroll-focus"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/scroll-focus # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/scroll-focus.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `items` (required) | `ScrollFocusItem[]` | - | { src, alt, title }[] | | `side` | `"left" \| "right"` | `"right"` | Side the synced title list sits on. | | `smallWidth` | `number` | `26` | Width away from focus, % of section width. | | `largeWidth` | `number` | `40` | Width of the focused image, % of section width. | | `aspectRatio` | `string` | `"2 / 1"` | Image aspect ratio. | | `gap` | `number` | `14` | Vertical gap between images in px. | | `scrollPerItem` | `number` | `0.5` | Section heights of scrolling per image. | | `scrub` | `number` | `0.8` | Scroll smoothing in seconds. | | `showIndex` | `boolean` | `true` | Show "(1)" index labels beside images. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track instead of the window. | | `height` | `string` | `"100vh"` | Height of the pinned section. | | `className` | `string` | - | Additional classes for the root element. | | `titleClassName` | `string` | - | Classes for the title list. | Usage: ```tsx import { ScrollFocus } from "@/components/scroll-focus" export function Demo() { return ( ) } ``` --- ## Sticky Cards A stack of same-size cards pinned on scroll. The front card lifts away, then the next, one by one. - Category: Scroll - Docs: https://trytweenly.vercel.app/docs/components/sticky-cards - Import: `import { StickyCards } from "@/components/sticky-cards"` Dependencies: `gsap`, `@gsap/react` Install: ```bash npx shadcn@latest add @tweenly/sticky-cards # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/sticky-cards.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `cards` (required) | `StickyCardItem[]` | - | { id, tag, title, image?, color?, textColor? }[]. Colors default to the purple, orange, red, sky, navy palette. | | `cardYOffset` | `number` | `4` | Vertical offset (%) between stacked cards. | | `cardScaleStep` | `number` | `0.05` | Scale reduction per card behind the front one. | | `stepInterval` | `number` | `1.2` | Timeline gap between each card's exit. | | `stepDuration` | `number` | `1` | Duration of each card's exit. | | `scrollLengthPerCard` | `number` | `1.8` | Section heights of scroll per card. | | `background` | `string` | `"#111111"` | Section background. | | `exitLast` | `boolean` | `false` | Animate the last card away too. When false it stays on screen as the pin releases. | | `scroller` | `string \| HTMLElement` | - | Scroll container to track instead of the window. | | `height` | `string` | `"100vh"` | Height of the pinned section. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { StickyCards } from "@/components/sticky-cards" export function Demo() { return ( ) } ``` --- ## Stagger Reveals its children one after another as they enter the viewport. - Category: Layout - Docs: https://trytweenly.vercel.app/docs/components/stagger - Import: `import { Stagger } from "@/components/stagger"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/stagger # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/stagger.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | Each direct child is animated in turn. | | `direction` | `"up" \| "down" \| "left" \| "right" \| "scale"` | `"up"` | Where each child enters from. | | `stagger` | `number` | `0.08` | Delay between children, in seconds. | | `duration` | `number` | `0.5` | Duration of each child, in seconds. | | `delay` | `number` | `0` | Delay before the first child, in seconds. | | `distance` | `number` | `20` | Travel distance in px. | | `once` | `boolean` | `true` | Animate only the first time it enters the viewport. | | `className` | `string` | - | Additional classes for the root element. | | `itemClassName` | `string` | - | Classes applied to each child wrapper. | Usage: ```tsx import { Stagger } from "@/components/stagger" export function Demo() { return ( {items.map((item) => ( ))} ) } ``` --- ## Marquee Infinite, seamless scrolling row or column of content. - Category: Layout - Docs: https://trytweenly.vercel.app/docs/components/marquee - Import: `import { Marquee } from "@/components/marquee"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/marquee # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/marquee.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` (required) | `ReactNode` | - | Items to scroll. | | `duration` | `number` | `30` | Seconds for one loop. Lower is faster. | | `reverse` | `boolean` | `false` | Scroll the other way. | | `vertical` | `boolean` | `false` | Scroll vertically. | | `pauseOnHover` | `boolean` | `true` | Pause while hovered. | | `gap` | `number` | `16` | Gap between items in px. | | `fade` | `boolean` | `true` | Fade the edges into the background. | | `repeat` | `number` | `4` | Copies of the content to render. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Marquee } from "@/components/marquee" export function Demo() { return ( {logos.map((logo) => ( ))} ) } ``` --- ## Product Card Full-bleed product card with a swipeable image carousel, glass pills, rolling price and an add-to-cart button that morphs into a quantity stepper. - Category: Cards - Docs: https://trytweenly.vercel.app/docs/components/product-card - Import: `import { ProductCard } from "@/components/product-card"` Dependencies: `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/product-card # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/product-card.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `images` | `string[]` | `"[]"` | Image URLs for the carousel. Swipe, drag or use the dots. Missing or broken images fall back to a gradient derived from `tint`. | | `title` (required) | `string` | - | Product name. | | `description` | `string` | - | Short description, clamped to three lines. | | `price` | `number` | `0` | Price. Digits roll when it changes. | | `prices` | `number[]` | - | Optional price per image; the price rolls to the matching value when the image changes. | | `currency` | `string` | `"₹"` | Currency symbol before the price. | | `locale` | `string` | `"en-IN"` | Locale used to format numbers. | | `compareAt` | `number` | - | Original price, struck through inside the price pill. | | `discount` | `string` | - | Top-right glass pill. Hidden when empty. | | `badges` | `(string \| { label: string; icon?: ReactNode })[]` | `"[]"` | Glass chips under the description. | | `location` | `string` | - | Location chip with a pin icon. | | `ctaLabel` | `string` | `"Add to cart"` | Button label. | | `onAddToCart` | `(quantity: number) => void` | - | Called with 1 when the button is pressed. | | `quantity` | `number` | - | Controlled quantity. 0 shows the button, more shows the stepper. | | `defaultQuantity` | `number` | `0` | Initial quantity when uncontrolled. | | `onQuantityChange` | `(quantity: number) => void` | - | Called whenever the quantity changes. | | `maxQuantity` | `number` | `99` | Upper bound of the stepper. | | `tint` | `string` | `"#d9870b"` | Scrim and fallback color. Match it to your photo. | | `accent` | `string` | `"#ffffff"` | Button background. | | `accentForeground` | `string` | `"#0a0a0a"` | Button text color. | | `radius` | `number` | `28` | Corner radius. | | `aspect` | `string` | `"3 / 4.4"` | CSS aspect ratio of the card. | | `autoplay` | `number \| false` | `0` | Advance every N ms, pausing on hover. 0 or false disables. | | `wishlist` | `boolean` | `false` | Show a wishlist heart top-left. | | `defaultWishlisted` | `boolean` | `false` | Initial wishlist state. | | `onWishlistChange` | `(wishlisted: boolean) => void` | - | Called when the heart is toggled. | | `imageFit` | `"cover" \| "contain"` | `"cover"` | How images fill the card. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { ProductCard } from "@/components/product-card" const images = ["/gallery/01.jpg", "/gallery/02.jpg", "/gallery/03.jpg"] export function Demo() { return ( console.log("added", qty)} /> ) } ``` --- ## Stamp Card Collectible postage stamp with real perforated edges, 3D tilt and glare, and an inked postmark that stamps down on click. - Category: Cards - Docs: https://trytweenly.vercel.app/docs/components/stamp-card - Import: `import { StampCard } from "@/components/stamp-card"` Dependencies: `motion` Install: ```bash npx shadcn@latest add @tweenly/stamp-card # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/stamp-card.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `image` | `string` | - | Artwork URL. Missing or broken images fall back to a duotone illustration. | | `variant` | `"classic" \| "airmail" \| "minimal"` | `"classic"` | Cream paper with a thin frame, red/blue airmail border, or flat duotone. The demo brings the chosen variant to the front. | | `title` | `string` | `""` | Title under the artwork. | | `value` | `string` | `"₹25"` | Denomination in the top-right corner. | | `country` | `string` | `"India"` | Country or brand, also printed around the postmark. | | `caption` | `string` | `""` | Small caption next to the country. | | `paper` | `string` | - | Paper color. Defaults per variant. | | `ink` | `string` | `"#0a0a0a"` | Postmark ink color. | | `accent` | `string` | `"#ff4d12"` | Frames, text and fallback artwork color. | | `perforation` | `number` | `8` | Perforation hole diameter. | | `size` | `number` | `180` | Stamp width; the height snaps to the perforation grid. | | `postmarked` | `boolean` | - | Controlled postmark state. | | `defaultPostmarked` | `boolean` | `false` | Initial postmark state when uncontrolled. | | `onPostmark` | `(postmarked: boolean) => void` | - | Called with the new state when the stamp is clicked. | | `clickToPostmark` | `boolean` | `true` | Toggle the postmark on click or Enter. | | `postmarkLabel` | `string` | `"PAID"` | Text in the middle of the postmark. | | `tilt` | `number` | `12` | Maximum hover tilt. 0 disables. | | `glare` | `boolean` | `true` | Pointer-following glare. | | `rotate` | `number` | `0` | Resting rotation. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { StampCard } from "@/components/stamp-card" // Also exported: , a fanned stack that spreads on hover export function Demo() { return ( ) } ``` --- ## Envelope Reveal A sealed envelope that opens on click, hover or scroll: the seal pops, the flap swings open and the letter slides out and unfolds. - Category: Cards - Docs: https://trytweenly.vercel.app/docs/components/envelope-reveal - Import: `import { Envelope } from "@/components/envelope"` Dependencies: `gsap`, `@gsap/react`, `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/envelope-reveal # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/envelope-reveal.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `children` | `ReactNode` | - | Content of the letter, e.g. an invitation or a thank-you note. | | `variant` | `"classic" \| "airmail" \| "minimal" \| "glass"` | `"classic"` | Kraft paper with a wax seal, airmail stripes with a stamp and postmark, flat theme-aware, or frosted glass over a glow. | | `color` | `string` | - | Envelope color. Defaults to the variant's color. | | `paper` | `string` | - | Letter paper color. Defaults to the variant's paper. | | `ink` | `string` | - | Letter text color. Defaults to the variant's ink. | | `seal` | `string` | - | Wax seal, stamp or dot color. Defaults to the variant's seal. | | `accent` | `string` | `"#ff4d12"` | Accent for buttons, focus, glow and the minimal seal. | | `sealLabel` | `string` | `"T"` | Initial or monogram on the seal or stamp. | | `trigger` | `"click" \| "hover" \| "inView" \| "manual"` | `"click"` | What opens the envelope. "manual" only follows the open prop. | | `open` | `boolean` | - | Controlled open state. | | `defaultOpen` | `boolean` | `false` | Initial open state when uncontrolled. | | `onOpenChange` | `(open: boolean) => void` | - | Called when the envelope asks to open or close. | | `label` | `string` | `"Open envelope"` | Accessible label for the open/close button. | | `letterClassName` | `string` | - | Classes for the letter paper. | | `duration` | `number` | `1` | Speed multiplier for the choreography; 2 plays it twice as slow. | | `width` | `number` | `420` | Envelope width in px. Shrinks to fit its container. | | `radius` | `number` | `14` | Envelope corner radius in px. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Envelope } from "@/components/envelope" export function Demo() { return (

You're invited.

) } ``` --- ## Footer Four premium, minimal footers: a sticky reveal uncovered as the page scrolls away, a giant kinetic wordmark, classic columns with a newsletter, and a curtain panel that expands to full bleed with a magnetic CTA. - Category: Sections - Docs: https://trytweenly.vercel.app/docs/components/footer - Import: `import { Footer } from "@/components/footer"` Dependencies: `gsap`, `@gsap/react`, `motion`, `lucide-react` Install: ```bash npx shadcn@latest add @tweenly/footer # or by URL npx shadcn@latest add https://trytweenly.vercel.app/r/footer.json ``` Props: | Name | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"sticky-reveal" \| "big-type" \| "columns" \| "curtain"` | `"sticky-reveal"` | Layout and motion style. | | `brand` | `string` | `"Motion"` | Brand name, used for the giant wordmark and the default copyright. | | `description` | `string` | `"Interfaces that feel quietly alive."` | Short line under the brand (the eyebrow in the curtain variant). | | `columns` | `{ title: string; links: { label: string; href: string; badge?: string }[] }[]` | - | Link groups. Defaults to Product / Company / Resources. | | `socials` | `{ label: string; href: string; icon?: ReactNode }[]` | - | Social links. Icons for GitHub, X, LinkedIn, Dribbble, YouTube, Instagram and email are picked by label. | | `logo` | `ReactNode` | - | Mark shown before the brand name. Defaults to an accent dot. | | `newsletter` | `boolean` | `true` | Show the newsletter form with an animated submit → check (columns and big-type). | | `onSubscribe` | `(email: string) => Promise \| void` | - | Called with the email on submit. Throw or reject to show the error state. | | `cta` | `{ label: string; href: string }` | `"{ label: \"Start a project\", href: \"#\" }"` | Magnetic call-to-action button in the curtain variant. | | `marqueeText` | `string` | `"Let's work together"` | Scrolling headline in the curtain variant. | | `copyright` | `string` | - | Copyright line. Defaults to “© {year} {brand}. All rights reserved.” | | `legal` | `{ label: string; href: string }[]` | - | Links in the bottom bar. Defaults to Privacy / Terms / Cookies. | | `background` | `string` | `"#0a0a0a"` | Footer background (any CSS color). | | `color` | `string` | `"#ededed"` | Main text color. | | `accent` | `string` | `"#ff4d12"` | Accent for badges, focus rings, the CTA and hover states. | | `muted` | `string` | `"#8a8a8a"` | Secondary text color for links and captions. | | `radius` | `number` | `12` | Corner radius of inputs and buttons; the curtain panel starts at twice this. | | `height` | `string` | `"70vh"` | Height of the sticky-reveal footer (any CSS length), capped at the scroller height. | | `wordmarkSize` | `number` | `1` | Wordmark size relative to the width-filling size (1 = edge to edge). | | `showWordmark` | `boolean` | `true` | Show the giant wordmark (sticky-reveal and big-type). | | `animate` | `boolean` | `true` | Run scroll and entrance animations. Reduced motion always renders the final state. | | `scroller` | `HTMLElement \| null` | - | Scroll container to track instead of the window. | | `className` | `string` | - | Additional classes for the root element. | Usage: ```tsx import { Footer } from "@/components/footer" const columns = [ { title: "Product", links: [ { label: "Features", href: "/features" }, { label: "Pricing", href: "/pricing" }, { label: "Changelog", href: "/changelog", badge: "New" }, ], }, { title: "Company", links: [ { label: "About", href: "/about" }, { label: "Careers", href: "/careers", badge: "Hiring" }, { label: "Contact", href: "/contact" }, ], }, { title: "Resources", links: [ { label: "Docs", href: "/docs" }, { label: "Guides", href: "/guides" }, { label: "Blog", href: "/blog" }, ], }, { title: "Legal", links: [ { label: "Privacy", href: "/privacy" }, { label: "Terms", href: "/terms" }, ], }, ] const socials = [ { label: "GitHub", href: "https://github.com" }, { label: "X", href: "https://x.com" }, { label: "LinkedIn", href: "https://linkedin.com" }, { label: "Dribbble", href: "https://dribbble.com" }, ] export function Demo() { return (