CSS perspective and opening book as in 3d
Tags: codepen, css, design, tutorial
Published: 2024-04-03 11:29:27

If you read about 3d in CSS and didn’t fully understand how to make 3d in the way you want using plain CSS, I hope this mini article can be of help. In the end you will be able to build something like this:

{% codepen https://codepen.io/juliashlykova/pen/XWQzXVm %}

## Table of contents

1. [CSS Perspective](#perspective)
2. [perspective-origin](#perspective-origin)
3. [transform-style](#transform-style)
4. [Book animation](#book-animation)

## Perspective

What is perspective in CSS? When we set perspective for the element, it tells the browser that a child of this element should behave as though they are in 3D-space.
> Amount of perspective determines the distance between user and z=0 plane.

I tried to figure out how it can be that the more perspective amount we set the less perspective we see. It was really confusing for me at first. Unless I draw some schema:

![Imaginary perspective](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y4dynhkq9rs99q4m8uy0.png)
When we change z coordinate (`translateZ()`) for the child, we move the child along this imaginary z-axis toward the user. So the further this z=0 plane from us the less noticeable the change is.

## Perspective-origin
`perspective-origin: horizontal-position vertical-position` determines the position of user’s eyes relative to the transformed elements. By default, this position is centered: `perspective-origin: 50% 50%`.

##  Transform-style
`transform-style: preserve-3d` allows children of the element be positioned in 3D space. And my question was: but isn’t it what perspective for? Actually, no. Transform-style doesn’t add depth. If there is no perspective in parent’s element then we won’t see actual 3d-representation:
{% codepen https://codepen.io/juliashlykova/pen/BaEmWqq %}

## Book animation
Now, let’s do some cool animation.

First of all, our index.html  body will look like this:
```html
<div class="container">
  <div class="book">
    <span class="shadow"></span>
    <div class="back"></div>
    <div class="cover-end"></div>
    <div class="page last">
      <button class="btn-tale">Tale begins...</button>
    </div>
    <div class="page third"></div>
    <div class="page second"></div>
    <div class="page first"></div>
    <div class="cover">
      <img src="https://cdn.pixabay.com/photo/2024/03/30/12/44/landscape-8664708_1280.png" alt="">
    </div>
  </div>
</div>
```
> In what follows, I omit insignificant properties for the topic, you can look at them in the original code in the introduction.

We have `div` block for every page, since every page will behave differently. Let’s first add `perspective` to the container block:
```css
.container {
  perspective: 500px;
  perspective-origin: 50% 50%;
}
```
We use default perspective-origin. But I preferred to explicitly set it for better understanding. So, our eyes are at the center of the container and `z=0` plane is at 500px distance from us.

At this  point, our book block is set on the plane `z=0` and doesn’t have any 3d features. Let’s add `transform-style` to allow pages behave in their own 3d-space:
```css
.book {
  position: relative;
  transform-style: preserve-3d;
}

.book>div {
  position: absolute;
  top: 0;
  left:0;
  transition: transform 2s;
}
```
Now, let’s add actual 3d:
```css
.cover {
  transform: scaleY(1.05) rotateY(-10deg);
}

.page.first {
  transform: translateX(2px) rotateY(-10deg);
}

.page.second {
  transform: translateX(4px) rotateY(-10deg);
}

.page.third {
  transform: translateX(6px) rotateY(-10deg);
}

.page.last {
  transform: translateX(8px) rotateY(-10deg);
}
```
To achieve volume we move every page a little to the right and rotate it around y-axis. Now, let’s change the behavior of the book when we hover over it:
```css
.book:hover .cover{
  transform: rotateY(-150deg);
}

.book:hover .page.first{
  transform: translateX(2px) rotateY(-150deg);
}

.book:hover .page.second{
  transform: translateX(4px) rotateY(-130deg);
}

.book:hover .page.third{
  transform: translateX(6px) rotateY(-110deg);
}
```
Alright, here we change rotation from `-10deg` to more extreme values to achieve “opening effect”. Also, we should save our `translateX`, since `transform` rewrites all its’ previous values.

And… that’s it! I hope, you understand now a little more about perspective in CSS.