I’ve been doing a bit of research into how the avant garde masters of the early 20th century manipulated type in their designs in order to achieve certain kinds of effects. I’m interested in how we might be able to duplicate some of those effects with CSS today. A lot of it is fairly straightforward, but lately I’ve tumbled down the rabbit hole of CSS perspective. It all started with this poster designed by Jan Tschichold.
The two headings in the poster, Die Frau Ohne Namen and Zweiter Teil appear to be receding into the distance. The lines go back toward a vanishing point, which is a classic illusion of perspective. I thought it would be fairly easy to achieve this effect with CSS because the transform property has a perspective() function, but it’s actually quite tricky, starting with the fact that…
There Are Two Ways To Do Perspective In CSS
The first way is to give an element its own perspective. This way, each element can be manipulated in any such manner as the designer desires, with full disregard to the perspective set for any other element around it. It's sort of like each element is in its own dimension.
The second way is to give a container perspective, inside which all of its child elements obey its singular perspective. Think of it like each element sharing a dimension with one another.
Additionally, further nested elements are not necessarily rendered in the 3D space. You can turn that on by using the
transform-style: preserve-3d;property. In my testing, Chrome appears to default to
preserve-3dwhile Firefox defaults to
flat. Make sure you set this switch so that all browsers share a common transform-style.
Through the rest of this article, I’ll be using the second perspective property, because I want my elements to interact on the same plane. And speaking of planes, that brings me to my second conundrum.
What The Eff Does That Length Mean?
What does it mean for the perspective to be 300 pixels or one inch or
According to MDN, it’s “the distance between the z=0 plane and the user.” From CSS Tricks, it’s “the distance between the Z plane and the user.” And, finally, from the W3C itself, the perspective property represents “the distance from the drawing plane to the the assumed position of the viewer’s eye.”
(I don’t think CSS Tricks is technically right here, or at least they’re not using the conventional terminology here. The z=0 plane is the xy-plane, not the z-plane. Z is an axis or coordinate value.)
Your screen, for example, represents the xy-plane—made up of the x-axis (left-to-right), and the y-axis (top-to-bottom). If you were traveling along the z-axis, which goes toward and away from you, by default z=0 would be the plane where your screen sits. Therefore, if you set
perspective equal to the exact distance between your eyes and the screen, that would be a “normal focal length”.
Does that mean
perspective sets how far away the element is from you in a 3D space? Not exactly. Now, if it’s not clear up to this point, I want to admit that I find all of this stuff pretty confounding, but I’m piecing together things as I understand them. If you know better what’s going on, feel free to poke holes in my hypotheses.
So, no, I don’t think
perspective sets the distance from the element exactly. If you have a very low perspective, there’s clearly evident distortion, and the element does look like it comes very close to you. In many cases, it will probably stretch way off the screen. However, at a very high perspective, the element appears almost as though you hadn’t transformed it at all. If, on the other hand, you were physically moving an element further away from you in a 3D space, it would get smaller and smaller until it seemed to disappear. That doesn’t happen here with
But, there is, in fact, already a transform function that will move an element closer to or further away from you:
Dolly vs. Zoom
Somewhere while sorting through all of this, my brain pieced together a cinematic metaphor. I think we can think of
translateZ() as dollying. That is, physically moving the camera closer to, or away from, an element; and we can think of
perspective as the amount of zoom we need to close that distance. In the real world, if you have a big object that’s very close to the camera, you don’t need any zoom, and sometimes the picture can appear distorted, especially if you have a wide-angle lens that’s capable of capturing a wider image than our eyes can normally see. (If you’ve ever taken a selfie and felt like the middle of your face seems swollen, try holding your arm out a little bit further.) On the other hand, if you watch sports or nature documentaries where they use very long-zoom telephoto lenses, the landscape really flattens out quite a bit!
In Alfred Hitchcock’s Vertigo, he took advantage of this disparity between proximity and zoom by dollying the camera toward his subject while simultaneously zooming out away from them (or vice versa). It creates a stylistic, disconcerting effect. This now-dubbed “vertigo effect” has been used many times over by other directors.
Bringing the Vertigo Effect to the Web
I felt reasonably satisfied with this conclusion, but then my 1:00 am brain kicked in and I wondered if I could actually recreate this vertigo effect in CSS. I got to work and put together something pretty satisfying. Keep in mind, this is a rough demo, so it’s not going to work on every device. Check it out when you’re on a desktop computer with Firefox or Chrome. (It may work in other setups, but I haven’t tried it out yet).
For this demo, we’re going to take a trip to The Frankie Zone. I put my actor Frankie into a “room” and gave it a
To bolster it all the more, I split Frankie’s face into three planes. His nose is on one plane, his face is on another, and his ears are on a third plane behind him. It gives a pretty convincing 3D effect. I also applied a blur on his ears and nose (as well as a more dramatic blur on the text behind), to simulate shortening up the depth of field.
There are at least a couple issues. The worst one is that Chrome’s rendering of the text blur is pretty terrible. Thankfully, that browser still uses the
-webkit prefix for filters, so I turned the blur off for it. Firefox’s transitioning of transforms is really janky. I tried setting
will-change on transitioned elements, but Firefox still didn’t seem to care. I finally got the jank to go away when I used the blur filter. It’s ironic that blur made Chrome worse, but Firefox better. I didn’t test in Safari or IE.
On the whole, CSS perspective is challenging to grasp. Most of the time I feel like I’m fiddling with the numbers to get the effect just so. That involves lots of “magic numbers”, which you usually want to avoid in design systems. But I think this dolly-zoom effect is a neat idea. I can imagine using it in a way that allows for a little art direction; something to make homepage hero images a little bit more interactive by drawing the viewer into a pseudo–three-dimensional world.
Relatively speaking, CSS perspective is still pretty new, and there’s not a lot of information out there about it. I hope that this post sheds a little bit more light on it, and I encourage you to play around with it. Let me know what kinds of things you come up with.