Animated Button
motiq/animated-buttonFreeComponent
A real button with press-scale feedback and accessible loading (aria-busy + polite spinner) and success states. Solid, outline, and ghost variants. Reduced-motion safe.
Live preview
live · rendered by the registry
Rendered by motiq on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://www.motiq.dev/r/animated-button.jsonSource
1"use client";23import * as React from "react";4import {5 AnimatePresence,6 motion,7 useReducedMotion,8 type HTMLMotionProps,9} from "motion/react";1011import { cn } from "@/lib/utils";1213type Variant = "solid" | "outline" | "ghost";1415const VARIANT_CLASS: Record<Variant, string> = {16 // A transparent border keeps sizing identical across variants and, under17 // forced-colors (Windows High Contrast), the UA renders it as `ButtonText`,18 // giving the solid/ghost variants a visible boundary they'd otherwise lose.19 solid:20 "border border-transparent bg-[var(--color-accent,#695cff)] text-white hover:bg-[var(--color-accent,#695cff)]/90",21 outline:22 "border border-[var(--color-border,#e4e7ec)] bg-[var(--color-surface,#fff)] text-[var(--color-fg,#111318)] hover:bg-[var(--color-fg,#111318)]/[0.04]",23 ghost:24 "border border-transparent bg-transparent text-[var(--color-fg,#111318)] hover:bg-[var(--color-fg,#111318)]/[0.06]",25};2627export interface AnimatedButtonProps28 extends Omit<HTMLMotionProps<"button">, "ref"> {29 variant?: Variant;30 /** Show an accessible spinner and disable interaction. */31 loading?: boolean;32 /** Briefly show a success check in place of the label. */33 success?: boolean;34}3536/**37 * AnimatedButton — a real `<button>` with press feedback and loading/success38 * states.39 *40 * Accessibility: forwards its ref and spreads native button props, so type,41 * form, and disabled semantics are intact. `loading` sets `aria-busy` and42 * exposes a polite "Loading" status; the spinner + check SVGs are decorative43 * (`aria-hidden`). Focus-visible shows a 2px outline (survives forced-colors);44 * every variant keeps a boundary in high-contrast. Under `prefers-reduced-motion`45 * the press-scale is disabled. Clean-room original.46 */47export const AnimatedButton = React.forwardRef<48 HTMLButtonElement,49 AnimatedButtonProps50>(51 (52 {53 variant = "solid",54 loading = false,55 success = false,56 disabled,57 className,58 children,59 ...props60 },61 ref,62 ) => {63 const reduce = useReducedMotion();64 const isDisabled = disabled || loading;6566 return (67 <motion.button68 ref={ref}69 type="button"70 disabled={isDisabled}71 aria-busy={loading || undefined}72 whileTap={reduce || isDisabled ? undefined : { scale: 0.96 }}73 transition={{ type: "spring", stiffness: 600, damping: 30 }}74 className={cn(75// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from motiq
All 100 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Activity Streamactivity-stream | motiq | Components | Free | 1 dep | |
| Adaptive Safe-Zone Gridadaptive-safe-zone-grid | motiq | Components | Free | no deps | |
| Agent Run Timelineagent-run-timeline | motiq | Components | Free | 1 dep | |
| AI Response Streamai-response-stream | motiq | Components | Free | 1 dep | |
| Animated Accordionanimated-accordion | motiq | Components | Free | 2 deps | |
| Animated Dialoganimated-dialog | motiq | Components | Free | 2 deps | |
| Animated Gridanimated-grid | motiq | Components | Free | 2 deps | |
| Animated Iconsanimated-icons | motiq | Components | Free | 1 dep |
