Flip card
A true 3D turn with a back face of its own, so what it reveals is never mirrored.
Installation
$ pnpm dlx shadcn@latest add @ssntl/flip-cardUsage
import { FlipCard } from "@/components/ui/flip-card"
<FlipCard flipped={flipped} front={<Front />} back={<Back />} />
<Button onClick={() => setFlipped(!flipped)}>Show the code</Button>Rules
What this component expects from you, as it ships in components/ui/flip-card.md next to the code. The full file adds when to use it, keyboard, the UI foundations it applies and every shared rule.
## Rules for Flip card
- **Readable flips.** A true 3D turn with a back face of its own, so the code on the back reads the right way round.
- **The hidden face is inert.** Focus and screen readers only reach the side you can see.
- Give the flip a visible control with a label that says what it shows; under reduced motion the faces cross-fade.Code
import * as React from "react"
import { cn } from "@/lib/utils"
// Readable flips: a true 3D turn with a back face of its own, so what it
// reveals is never mirrored. The hidden face is inert, so focus and screen
// readers only reach the side you can see. Under reduced motion the faces
// cross-fade instead of turning.
function FlipCard({
flipped,
front,
back,
className,
...props
}: Omit<React.ComponentProps<"div">, "children"> & {
flipped: boolean
front: React.ReactNode
back: React.ReactNode
}) {
const face =
"col-start-1 row-start-1 grid rounded-lg backface-hidden motion-reduce:transition-opacity motion-reduce:duration-(--duration-menu)"
return (
<div
data-slot="flip-card"
className={cn("grid perspective-distant", className)}
{...props}
>
<div
data-flipped={flipped || undefined}
className="grid transition-[rotate] duration-(--duration-panel) ease-in-out transform-3d data-flipped:rotate-y-180 motion-reduce:transition-none motion-reduce:data-flipped:rotate-y-0"
>
<div
inert={flipped}
className={cn(face, flipped && "motion-reduce:opacity-0")}
>
{front}
</div>
<div
inert={!flipped}
className={cn(
face,
"rotate-y-180 motion-reduce:rotate-y-0",
!flipped && "motion-reduce:opacity-0"
)}
>
{back}
</div>
</div>
</div>
)
}
export { FlipCard }