Cards & Grids

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.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add flip-reveal-card.en

New project or installation trouble? Start here →

Flip Reveal Card is a React 3D flip card component built with Tailwind CSS and Motion. The front and back faces stack in the same grid cell using CSS 3D transforms, the flip can be triggered on hover, on click or fully controlled, and it can turn on the horizontal or vertical axis.

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

1000
0.6
Active edited usageDefault props
<FlipRevealCard/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

When to use

  • Team pages: photo and role on the front, a short bio and contact link on the back.
  • Q&A cards, flashcards or quiz questions where a click reveals the answer.
  • Feature or plan highlight cards: one key point on the front, the details on the back.
  • Prize or offer reveals on campaign pages, flipped by your code at the right moment.
  • Not a good fit: when the back needs a full layout of content, use Morph Expand Card, which grows into a detail panel on click.

Installation

npx webberui@latest add https://webberui.com/r/flip-reveal-card.en.json

Or, 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>
</>

Examples

Team member card

The default trigger="hover" flips to the back on mouse hover or keyboard focus. Links on the back work too: the card stays flipped while focus is on a link inside it. Remember to name the card with aria-label.

import {
  FlipRevealCard,
  FlipRevealFront,
  FlipRevealBack,
} from "@/components/ui/flip-reveal-card";

<FlipRevealCard aria-label="Alex Chen, front-end engineer" className="h-56 w-48">
  <FlipRevealFront className="items-center justify-center gap-2">
    <img src="/team/alex.jpg" alt="" className="size-20 rounded-full object-cover" />
    <p className="font-semibold">Alex Chen</p>
    <p className="text-sm text-neutral-500">Front-end engineer</p>
  </FlipRevealFront>
  <FlipRevealBack className="justify-between">
    <p className="text-sm">Maintains the design system and component library, with a focus on React and accessibility.</p>
    <a href="mailto:alex@example.com" className="text-sm underline">
      Send an email
    </a>
  </FlipRevealBack>
</FlipRevealCard>

Click-to-flip quiz card

trigger="click" turns the card into a focusable button that toggles on click or Enter / Space, and axis="x" flips it top to bottom. Use onFlippedChange to record whether the user has seen the answer.

"use client";

import * as React from "react";
import {
  FlipRevealCard,
  FlipRevealFront,
  FlipRevealBack,
} from "@/components/ui/flip-reveal-card";

export function QuizCard() {
  const [revealed, setRevealed] = React.useState(false);

  return (
    <div className="space-y-2">
      <FlipRevealCard
        trigger="click"
        axis="x"
        onFlippedChange={(flipped) => flipped && setRevealed(true)}
        className="h-40 w-72"
      >
        <FlipRevealFront className="justify-center">
          <p className="font-medium">What does HTTP status 404 mean?</p>
        </FlipRevealFront>
        <FlipRevealBack className="justify-center">
          <p>The server could not find the requested resource (Not Found).</p>
        </FlipRevealBack>
      </FlipRevealCard>
      {revealed && <p className="text-sm text-neutral-500">Answer viewed</p>}
    </div>
  );
}

Custom faces and flip feel

Each face's className overrides the default border, background and padding; a smaller perspective gives a stronger 3D effect, and duration sets the flip time in seconds.

<FlipRevealCard perspective={600} duration={0.8} className="h-48 w-64">
  <FlipRevealFront className="border-0 bg-neutral-900 text-white">
    <p className="text-lg font-semibold">Monthly traffic report</p>
  </FlipRevealFront>
  <FlipRevealBack className="border-0 bg-amber-100 text-amber-950">
    <p className="text-sm">Visits, bounce rate and conversion rate compiled automatically and emailed on the 1st of each month.</p>
  </FlipRevealBack>
</FlipRevealCard>

Props

FlipRevealCard

PropTypeDefaultDescription
childrenReact.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
flippedboolean—Controlled state; supplying it disables the internal state
defaultFlippedbooleanfalseInitial flipped state in uncontrolled mode
onFlippedChange(flipped: boolean) => void—Fires when the flipped state changes
perspectivenumber10003D perspective distance (px); smaller values make the depth more pronounced
durationnumber0.6Duration of the flip animation (seconds)
classNamestring—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

PropTypeDefaultDescription
childrenReact.ReactNode—Content of that face
classNamestring—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: hidden to hide whichever one is turned away from the viewer; the rotating container carries transform-style: preserve-3d, so never add overflow-hidden to it (that flattens the 3D context).
  • The faces themselves are free to set overflow-hidden and rounded corners, because they are only flat content layers.
  • When axis changes, 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 (duration 0) with no rotation animation and no change to the DOM structure.
  • Whichever face is currently turned away is marked aria-hidden and given inert, so a screen reader will not announce it and focusable elements inside it stay out of the tab order.
  • With trigger="click", the container is role="button" and focusable, supports Enter / Space to toggle, and aria-pressed reflects 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 through aria-label is recommended.

FAQ

Does it work with the Next.js App Router?

Yes. The component file already starts with "use client", so import FlipRevealCard, FlipRevealFront and FlipRevealBack directly from a Server Component page; the content of both faces can render on the server.

How is the card height determined?

Both faces stack in the same CSS Grid cell, so the card takes the height of the taller face. For a fixed size, set width and height through FlipRevealCard's className, for example h-40 w-64.

Will screen readers read both faces?

No. The face currently turned away gets aria-hidden and inert, so it is not announced and any links or buttons inside it are out of the tab order.

Why do I get an error saying the face must be used inside FlipRevealCard?

FlipRevealFront and FlipRevealBack read the flip state from FlipRevealCard and throw when used on their own. Put both inside the same FlipRevealCard.

Does it respect reduced motion?

Yes. With "reduce motion" enabled, the flip switches instantly with a duration of 0 and no rotation in between. The only dependencies are motion, clsx and tailwind-merge; without the CLI, copy the source from the Manual tab in the installation section.