Flip Reveal Card
A 3D flip card that rotates around the X or Y axis to reveal its front and back, supporting hover, click, and fully controlled use.
npx shadcn@latest add https://webberui.com/r/flip-reveal-card.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<FlipRevealCard />
Installation
npx shadcn@latest add https://webberui.com/r/flip-reveal-card.jsonOr, once registries are configured in components.json, install it as @webberui/flip-reveal-card.
Usage
import {
FlipRevealCard,
FlipRevealFront,
FlipRevealBack,
} from "@/components/ui/flip-reveal-card";
<FlipRevealCard trigger="hover" axis="y" className="h-40 w-64">
<FlipRevealFront>
<h3 className="text-base font-semibold">Front</h3>
</FlipRevealFront>
<FlipRevealBack>
<p className="text-sm">Back-side content, only visible once you flip it over.</p>
</FlipRevealBack>
</FlipRevealCard>The two faces stack automatically in the same grid cell and the card takes the height of the taller one; you can also set a fixed height directly on FlipRevealCard through className (for example h-40).
Controlled mode
Passing flipped switches to controlled mode, and combining it with trigger="none" puts the flip entirely under external control:
const [flipped, setFlipped] = React.useState(false);
<>
<FlipRevealCard trigger="none" flipped={flipped} className="h-40 w-64">
<FlipRevealFront>Front</FlipRevealFront>
<FlipRevealBack>Back</FlipRevealBack>
</FlipRevealCard>
<button onClick={() => setFlipped((v) => !v)}>Flip</button>
</>Props
FlipRevealCard
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Put FlipRevealFront and FlipRevealBack here |
trigger | "hover" | "click" | "none" | "hover" | How it is triggered: hover / focus, click to toggle, or fully controlled |
axis | "x" | "y" | "y" | Flip axis: y flips left-to-right, x flips top-to-bottom |
flipped | boolean | — | Controlled state; supplying it disables the internal state |
defaultFlipped | boolean | false | Initial flipped state in uncontrolled mode |
onFlippedChange | (flipped: boolean) => void | — | Fires when the flipped state changes |
perspective | number | 1000 | 3D perspective distance (px); smaller values make the depth more pronounced |
duration | number | 0.6 | Duration of the flip animation (seconds) |
className | string | — | Appended to the outermost container (commonly used to set width and height) |
The remaining standard div attributes (such as aria-label) are supported as well.
FlipRevealFront / FlipRevealBack
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Content of that face |
className | string | — | Override the default face appearance (border, background, padding, and so on) |
Both inherit the standard div attributes.
How it works
- The two faces stack in the same CSS Grid cell and use
backface-visibility: hiddento hide whichever one is turned away from the viewer; the rotating container carriestransform-style: preserve-3d, so never addoverflow-hiddento it (that flattens the 3D context). - The faces themselves are free to set
overflow-hiddenand rounded corners, because they are only flat content layers. - When
axischanges, the back face automatically switches to a 180° pre-rotation on the matching axis, so the front and back always stay coplanar and aligned.
Accessibility
- When the user has "reduce motion" enabled at the system level, the flip becomes an instant switch (
duration0) with no rotation animation and no change to the DOM structure. - Whichever face is currently turned away is marked
aria-hiddenand giveninert, so a screen reader will not announce it and focusable elements inside it stay out of the tab order. - With
trigger="click", the container isrole="button"and focusable, supports Enter / Space to toggle, andaria-pressedreflects the current face. - With
trigger="hover", the container is focusable and keyboard focus flips it (equivalent to hovering), flipping back on blur; giving the card a name througharia-labelis recommended.