3D CSS crop rows

In the spirit of New Earth USA, here’s some minimalist crop rows in 3D CSS.

Let’s look at how this is built. To start, here’s some basic HTML:

<div class="hero">
  <div class="wrapper">
    <div class="dirt"></div>
    <div class="rows"></div>

And some padding-busting CSS:

html,body {
 margin: 0;
 padding: 0;

Now for the elements. .hero is a simple container with overflow: hidden so that the 3D CSS doesn’t spill out of the browser window:

.hero {
 width: 100%;
 overflow: hidden;

Inside .hero there’s a .wrapper that provides the 3D context. We give it height: 100vh so it fills the browser window perfectly, and perspective: 200px so the ground looks like it disappears to the horizon. The background is just a light-blue-to-white gradient to give us a little bit of clear sky:

.wrapper {
 height: 100vh;
 perspective: 200px;
 background: linear-gradient(to bottom, #e2ebfb 0, #ffffff 50%);
 position: relative;

Now for the ground. .rows and dirt are two separate 3D layers, slightly separated to give the illusion that the crops are rising out of the ground. position: absolute and height: 100% ensures that we start with two screen-filling rectangles. We give them both width: 700% (and center them with margin: 0 -300%) so that the distant horizon is still wide enough to fill the full width of the wrapper — no math here, just a nice big round number found by trial and error. Set the 3D effect by adding transform: rotateX(90deg):

.rows,.dirt {
 position: absolute;
 height: 100%;
 width: 700%;
 margin: 0 -300%;
 transform: rotateX(90deg);

If you know how 3D CSS works, then you know that we’ve just made both divs invisible because they’ve been rotated away from us around the exact center of the screen. So we change the pivot to the bottom of the screen by adjusting the transform-origin. Since we want the .rows to be slightly above the .dirt, we put its transform-origin one percentage point higher:

.rows {
 transform-origin: 50% 98%;
.dirt {
 transform-origin: 50% 99%;

(The 50% isn’t actually necessary; since we’re only rotating in one direction, we could replace 50% with 0 or any other number without changing the result. I just find it more intuitive to keep it centered.)

The crop rows themselves are generated through the magic of background: repeating-linear-gradient() (support: IE10+ and all modern browsers). .rows alternates between stripes of transparency (to reveal the dirt beneath) and green; we can also add a little opacity to the edges of the green to help give the illusion that they’re rounded bulges instead of clean planes floating above the ground. Meanwhile, .dirt alternates between black and brown to give the illusion that the crop rows are casting shadows. The layering plus the 3D perspective helps sell that illusion:

.rows {
 background: repeating-linear-gradient(to right, transparent 0, rgba(36,130,41,0) 40px, rgba(36,130,41,1) 50px, rgba(86,183,91,1) 90px, rgba(86,183,91,0) 100px);
.dirt {
 background: repeating-linear-gradient(to right, #694418 0, #130b02 30px, #694418 100px);

Leave a Reply

Your email address will not be published. Required fields are marked *