CSS 3D Books That Don't Suck
Most 3D CSS components look kinda like they were made in 2010.
But this one? Actually clean. I built this for my x/ui library because I was tired of seeing janky book animations that made me want to close the tab.
Check out the full docs at ui.3x.gl/docs/book if you want to steal it.
Look at that smooth rotation. Zero jank.
Why Bother With 3D Books?
Because flat cards are kinda boring.
Seriously though, when you're showcasing content — portfolios, documentation, whatever — a little depth goes a long way. But most 3D CSS looks kinda like it was coded by someone who just discovered transform: rotateY()
.
This one actually feels like a book you'd want to pick up.
What I Built Instead
Multiple layers, proper perspective, and hover effects that don't make you nauseous:
interface BookProps {
radius?: "sm" | "md" | "lg";
size?: "sm" | "md" | "lg";
color?: keyof typeof colorMap;
isStatic?: boolean;
className?: string;
children: React.ReactNode;
}
Basic props, nothing crazy. The isStatic
prop is clutch — lets you decide if the book should rotate on hover or just stay tilted.
The Perspective Magic
<div className="group [perspective:800px] w-min">
800px perspective is the sweet spot. Too low and it looks weird, too high and you lose the effect. Trust me, I tried like 20 different values.
That Smooth Rotation
className={`[transform-style:preserve-3d] ${
isStatic
? "[transform:rotateY(-30deg)]"
: "[transform:rotateY(0deg)] group-hover:[transform:rotateY(-30deg)]"
}`}
30 degrees is perfect. Shows the spine without looking like the book is falling over.
The Layering Trick
Three layers make this work: front cover, spine, back cover. Each one positioned in 3D space like a real book.
Front Cover
<div
style={{
transform: "translateZ(25px)",
boxShadow: "5px 5px 20px var(--shadowColor)",
}}
>
Pushed forward 25px with a shadow that actually looks realistic.
The Spine (The Tricky Part)
<div
style={{
width: "48px",
transform: `translateX(${sizeMap[size].spineTranslation}) rotateY(90deg)`,
background: "linear-gradient(90deg, rgba(255,255,255,1) 50%, rgba(249,249,249,1) 50%)",
}}
/>
Rotated 90 degrees and positioned just right. That gradient makes it look like actual book binding.
Back Cover
<div
style={{
transform: "translateZ(-25px)",
boxShadow: "-10px 0 50px 10px var(--shadowColor)",
}}
/>
Pushed back with a different shadow. The shadows sell the depth.
The Details That Matter
Spine Texture (Because I'm Obsessive)
background: linear-gradient(90deg,
hsla(0, 0%, 100%, 0),
hsla(0, 0%, 100%, 0) 12%,
hsla(0, 0%, 100%, .25) 29.25%,
hsla(0, 0%, 100%, 0) 50.5%,
hsla(0, 0%, 100%, 0) 75.25%,
hsla(0, 0%, 100%, .25) 91%,
hsla(0, 0%, 100%, 0)
)
Yeah, this gradient is ridiculous. But it mimics those little ridges on real book spines. Details matter.
Customization (Because Everyone Wants Different Sizes)
Size Variants
const sizeMap = {
sm: { width: "180px", spineTranslation: "152px" },
md: { width: "220px", spineTranslation: "192px" },
lg: { width: "260px", spineTranslation: "232px" },
};
Those spine translation values? Calculated by hand because math is hard and I didn't want to do it in CSS.
22 Colors (Because Why Not)
const colorMap = {
blue: { from: "from-blue-900", to: "to-blue-700" },
red: { from: "from-red-900", to: "to-red-700" },
// ... 20 more colors
};
All using Tailwind's color palette. The gradient from 900 to 700 gives it that nice depth without being too much.
How to Use It
<Book size="md" color="blue" radius="lg">
<BookHeader>
<Badge>React</Badge>
<Badge>TypeScript</Badge>
</BookHeader>
<BookTitle>Component Design</BookTitle>
<BookDescription>
A comprehensive guide to building reusable UI components
</BookDescription>
</Book>
Clean composition pattern. Header, title, description — mix and match however you want.
No weird prop drilling or configuration objects. Just drop in your content.
Performance Notes
CSS Custom Properties for Theming
[--shadowColor:#bbb] dark:[--shadowColor:#111]
Shadows switch with your theme automatically. No weird CSS-in-JS nonsense.
Hardware Acceleration
3D transforms trigger GPU acceleration by default. Smooth on potato laptops.
Wrap It Up
This is part of x/ui, my component library that doesn't suck.
Check out ui.3x.gl/docs/book for more examples and ways to break it.
The whole thing is TypeScript, accessible, and actually works on mobile. Revolutionary, I know.
Steal it. Make it better. Just don't make it uglier.