Components
Push button
A chunky button with visible depth: the face sits on its own edge, lifts a pixel on hover, travels down onto the edge when pressed and springs back when let go. For playful flows such as onboarding, lessons and rewards.
pnpm dlx shadcn@latest add @caliper/push-buttonWhen to use
Use it for
- Game-like moments: continue a lesson, claim a reward, finish a setup.
- Kiosk and touch screens, where a press should feel physical.
Skip it when
- Ordinary product actions; use `Button`, which keeps the calm product material.
- More than one on a screen as a primary; depth draws the eye.
Decisions
Why it is built this way, so you know what to keep when you change your copy.
Behaviour
- Fast down, springy up
- The press is almost instant so it feels like contact; the release uses the settle curve, which overshoots a little like a key returning.
- A real button
- It is a native button with its own focus ring; a disabled one sits pressed and grey.
Styling
- Edge from the face
- The edge is the face colour mixed with black, so any variant or theme keeps a believable side.
Installation
pnpm dlx shadcn@latest add @caliper/push-button
Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage
This is the code behind the preview above.
import { ArrowRight } from "lucide-react" import { type ReactNode } from "react" import { PushButton } from "@/components/ui/push-button" function Panel({ title, context, className = "max-w-md", children }: { title: string; context: string; className?: string; children: ReactNode }) { return ( <section className={`grid w-full gap-5 rounded-overlay border border-border bg-background p-6 shadow-resting max-sm:p-4 ${className}`}> <div className="grid gap-1"> <p className="font-medium">{title}</p> <p className="text-sm text-muted-foreground">{context}</p> </div> {children} </section> ); } export function PushButtonDemo() { return ( <Panel title="Lesson complete" context="12 new words · 4 minutes · no mistakes" className="max-w-sm"> <div className="grid justify-items-center gap-1 rounded-surface bg-muted py-6 text-center"> <p className="text-4xl font-bold tabular-nums">+20</p> <p className="text-sm text-muted-foreground">points today</p> </div> <div className="grid gap-3"> <PushButton variant="accent" size="lg"> Continue <ArrowRight aria-hidden /> </PushButton> <div className="grid grid-cols-2 gap-3"> <PushButton variant="secondary">Review</PushButton> <PushButton>Share</PushButton> </div> <PushButton disabled size="sm"> Next lesson unlocks tomorrow </PushButton> </div> </Panel> ); }
API reference
PushButton
component
Also accepts the native props of its root element and a ref.
Accessibility
It is built on native elements and keeps aria-hidden in step with state.
No custom key handling; Tab and the native activation keys do the work.
Motion
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.
Related
- ButtonA familiar action with immediate focus, a tactile press, and optional pending or label feedback that stays on the button itself.
- Hold to confirm
- Toggle and toggle groupA button that stays pressed, alone or in a raised group that holds one choice or many and moves with the arrow keys as a single tab stop.