Single Div Drawings with CSS

Why A Single Div?

In May of 2013 I attended CSSConf and saw Lea Verou speak about the humble border-radius. It was an eye-opening talk and I realized there was much about CSS behavior I did not fully understand. This reminded me of my time as a fine arts student where I was constantly pushed to become a master of my chosen medium. As a web designer, CSS is my medium and so I challenged myself to learn all I could about it and to explore and experiment with its limits.

But why a single div?

When I was learning to paint, my class did these color mixing exercises where we created the many colors of the spectrum from only the three primary colors: red, yellow, and blue. The purpose of the exercise is to learn the behavior of the medium and the constraints show us the power of combination. You can certainly buy green paint, but you can also create green from blue and yellow. Restricting your available options forces you to re-evaluate the tools you already have.

I decided to start a CSS drawing project, every few days illustrating something new with only CSS. To further challenge and explore what CSS is capable of, I gave myself the constraint of using only a single div in the markup. Instead of buying green paint (or adding another div), I’d need to stretch and combine CSS properties to achieve my goals.

The Toolkit

With only a single div and browser-supported CSS properties, it may seem like the tools are too limited. I found it’s not always what you have to work with, but how you look at them.

Pseudo elements

With one div in the HTML, I actually have three elements to work with because of CSS pseudo classes. So with div, div:before, and div:after, we can get something like this:

pseudo elements

  div { background: red; }
  div:before { background: yellow; }
  div:after { background: blue; }

It helps to think about these three elements as things that can come in sequence and as three stackable layers. So in my mind, it would usually look more like this:

pseudo elements as layers

Shapes

With CSS and one element, we are afforded three basic shape types. We can use width and height properties to create squares/rectangles, border-radius to create circles/ellipses, and border to create triangles/trapezoids.

css shapes

There are others we can create with CSS, but most things can be simplified to some combination of these shapes. And these are the easiest to create and manipulate.

Multiples of the same shape

With multiple box-shadows, we can create many versions of the same shape in varying size, color, and blur. Offsetting them on the x- and y-axes gives us almost endless multiples.

multiple box-shadows

div {
    box-shadow: 170px 0 10px yellow,
                330px 0 0 -20px blue,
                330px 5px 5px -20px black;
}

We can even give our box-shadows box-shadows. Pay attention to the order they are declared. Again, It’s helpful to think of them as layers.

Gradients

Gradients can be used to add shading and depth by implying a light source. This makes the simple, flat shapes feel more realistic. Combining multiple background-images allows us to use many, layered gradients to achieve more complex shading and even more shapes.

gradients

div {
    background-image: linear-gradient(to right, gray, white, gray, black);
}
 
div:after {
    background-image: radial-gradient(circle, yellow 50%, transparent 50%),
                      linear-gradient(to right, blue, red);
}

Visualizing

The most difficult part is visualizing how to piece these parts into a whole recognizable drawing. As much as I focus on the technical aspects of the drawings, this part of the process is critical. To help with this, I’ll often look at a photograph of the subject and break it up visually into pieces. Everything is a shape and everything is a color. I simplify the overall picture into smaller shapes or blocks of color I know can (or suspect can) be achieved with CSS.

Demos

Let’s take a closer look at two drawings and break down some of the pieces that make up the larger pictures. First up is the green crayon.

A crayon is made up of two primary shapes: the rectangular body and the triangular drawing tip.

crayon shapes

I had to guarantee the following things to capture a realistic image:

  • the different color of the paper wrapper
  • the printed graphics and words on the wrapper
  • the shading that shows the roundness of the crayon
  • the glossiness that shows roundness and a light source

So first, I created the main body of the crayon with the div and a background color, top-to-bottom gradient, and box-shadow to show some dimension:

crayon body

(Note, I’m using a mixin of black(a) and white(a) here in place of rgba)

div {
    background: #237449;
    background-image: linear-gradient(to bottom,
                                  transparent 62%,
                                  black(.3) 100%);
    box-shadow: 2px 2px 3px black(.3);
}

Then I added a left-to-right linear-gradient to create the wrapper. It has an alpha value of .6 so some of that previous gradient shows through.

crayon wrapper

div {
    background-image: linear-gradient(to right,
                                  transparent 12px,
                                  rgba(41,237,133,.6) 12px,
                                  rgba(41,237,133,.6) 235px,
                                  transparent 235px);
}

Next I used the same left-to-right gradient technique to create the printed stripes on the crayon.

crayon printing

div {
    background-image: linear-gradient(to right,
                                  transparent 25px,
                                  black(.6) 25px,
                                  black(.6) 30px,
                                  transparent 30px,
                                  transparent 35px,
                                  black(.6) 35px,
                                  black(.6) 40px,
                                  transparent 40px,
                                  transparent 210px,
                                  black(.6) 210px,
                                  black(.6) 215px,
                                  transparent 215px,
                                  transparent 220px,
                                  black(.6) 220px,
                                  black(.6) 225px,
                                  transparent 225px);
}

And for the printed ellipse, a radial-gradient works great!

crayon printing

div {
    background-image: radial-gradient(ellipse at top,
                                  black(.6) 50px,
                                  transparent 54px);
}

I have it broken up to demonstrate each piece, but keep in mind the background-image would actually look like this:

div {
                      // ellipse printed on wrapper
    background-image: radial-gradient(ellipse at top,
                                  black(.6) 50px,
                                  transparent 54px),
                      // printed stripes
                      linear-gradient(to right,
                                  transparent 25px,
                                  black(.6) 25px,
                                  black(.6) 30px,
                                  transparent 30px,
                                  transparent 35px,
                                  black(.6) 35px,
                                  black(.6) 40px,
                                  transparent 40px,
                                  transparent 210px,
                                  black(.6) 210px,
                                  black(.6) 215px,
                                  transparent 215px,
                                  transparent 220px,
                                  black(.6) 220px,
                                  black(.6) 225px,
                                  transparent 225px),
                      // wrapper
                      linear-gradient(to right,
                                  transparent 12px,
                                  rgba(41,237,133,.6) 12px,
                                  rgba(41,237,133,.6) 235px,
                                  transparent 235px),
                      // crayon body shading
                      linear-gradient(to bottom,
                                  transparent 62%,
                                  black(.3) 100%)
}

So after completing the div, I moved on to the :before pseudo element to create the crayon’s triangular tip. Using solid and transparent borders, I made a triangle and positioned it next to the div I just drew.

triangle crayon tip

div:before {
    height: 10px;
    border-right: 48px solid #237449;
    border-bottom: 13px solid transparent;
    border-top: 13px solid transparent;
}

It looks a little flat next to the crayon’s body, but it will be fixed with the :after pseudo element. With this, I added a top-to-bottom linear-gradient to create a reflective gloss effect that spans the width of the crayon.

crayon gloss

div:after {
    background-image: linear-gradient(to bottom,
                                    white(0) 12px,
                                    white(.2) 17px,
                                    white(.2) 19px,
                                    white(0) 24px);
}

This adds even more dimension and realism and helps with that flat triangle. As a finishing touch, I added some text content to the :after and positioned it as another printed element on the crayon’s wrapper.

crayon color label

div:after {
    content: 'green';
    font-family: Arial, sans-serif;
    font-size: 12px;
    font-weight: bold;
    color: black(.3);
    text-align: right;
    padding-right: 47px;
    padding-top: 17px;
}

And that’s it!

Let’s take a look at another one

The crayon is a good example of using background-image and gradients to produce realistic results. Here’s an example that shows the power of multiple box-shadows: a single div camera.

Here’s the body of the camera, created with background-image and border-image.

camera body

Here’s a gif illustrating the :before pseudo element (the black rectangle) and the many details created with its box-shadows.

camera rectangle

div:before {
    background: #333;
    box-shadow: 0 0 0 2px #eee,
                -1px -1px 1px 3px #333,
                -95px 6px 0 0 #ccc,
                30px 3px 0 12px #ccc,
                -18px 37px 0 46px #ccc,
 
                -96px -6px 0 -6px #555,
                -96px -9px 0 -6px #ddd,
 
                -155px -10px 1px 3px #888,
                -165px -10px 1px 3px #999,
                -170px -10px 1px 3px #666,
                -162px -8px 0 5px #555,
 
                85px -4px 1px -3px #ccc,
                79px -4px 1px -3px #888,
                82px 1px 0 -4px #555;
}

Similarly, here’s the :after (the grey circle) and its several box-shadow details.

camera circle

div:after {
    background: linear-gradient(45deg, #ccc 40%, #ddd 100%);
    border-radius: 50%;
    box-shadow: 0 3px 2px #999,
                1px -2px 0 white,
                -1px -3px 2px #555,
                0 0 0 15px #c2c2c2,
                0 -2px 0 15px white,
                -2px -5px 1px 17px #666,
                0 10px 10px 15px black(.3),
 
                -90px -51px 1px -43px #aaa,
                -90px -50px 1px -40px #888,
                -90px -51px 0 -34px #ccc,
                -90px -50px 0 -30px #aaa,
                -90px -48px 1px -28px black(.2),
 
                -124px -73px 1px -48px #eee,
                -125px -72px 0 -46px #666,
                -85px -73px 1px -48px #eee,
                -86px -72px 0 -46px #666,
                42px -82px 1px -48px #eee,
                41px -81px 0 -46px #777,
                67px -73px 1px -48px #eee,
                66px -72px 0 -46px #666,
 
                -46px -86px 1px -45px #444,
                -44px -87px 0 -38px #333,
                -44px -86px 0 -37px #ccc,
                -44px -85px 0 -34px #999,
 
                14px -89px 1px -48px #eee,
                12px -84px 1px -48px #999,
                23px -85px 0 -47px #444,
                23px -87px 0 -46px #888;
}

A little crazy, but as you can see, multiple box-shadows can add a lot of detail to a single div drawing.

Biggest challenges

Two of the biggest obstacles I came across were limitations around triangle shapes and the natural behavior of gradients.

The trouble with triangles

Because triangles are created using borders, it limits how much I can do with them. Adding gradients with border-image apply to all the borders, not just one side. Box-shadows apply to the shape of the box, not the triangle shape the border creates, so creating multiple triangle shapes can be difficult. Here’s an example of what that looks like:

trouble with triangles

div {
    border-left: 80px solid transparent;
    border-right: 80px solid transparent;
    border-bottom: 80px solid red;
}
 
div:before {
    border-left: 80px solid transparent;
    border-right: 80px solid transparent;
    border-bottom: 80px solid red;
    border-image: linear-gradient(to right, red, blue);
}
 
div:after {
    border-left: 80px solid transparent;
    border-right: 80px solid transparent;
    border-bottom: 80px solid red;
    box-shadow: 5px 5px 5px gray;
}

Layering gradients

With gradients, their natural behavior is to fill the entire background. This can get a little tricky when layering multiple gradients on top of each other. It takes some extra time to think through transparency, z-index, and understanding what will and won’t be visible. By using this technique effectively, our drawings can have surprising detail.

The Tardis is a good example of showing and hiding gradients to create a detailed picture. Here’s the drawing mid-process that shows some of the top-to-bottom gradients that span the entire width of the container.

single div tardis in-process

Using left-to-right and right-to-left gradients, I was able to cover parts of the gradient and leave some parts exposed.

single div tardis in-process

The resulting drawing appears to have many shapes making up the front facade of the Tardis, but it’s strategically layered linear-gradients. Sometimes you have to fake it.

See them in action

One awesome thing that popped up because of this project is a really cool and useful Chrome browser extension by Rafael Carício called CSS Gradient Inspector. It extends the developer tools to inspect and toggle on/off each element’s gradients as if they were layers. (It’s very helpful with everyday projects, too.)

I’m super excited to see designers and developers experimenting and riffing on these drawings with animations and JavaScript functionality. Check out the site and play around with the CSS yourself at a.singlediv.com or on GitHub!

About Lynn Fisher

Lynn Fisher is an artist and designer based in Chandler, Arizona. She works with the fine folks at &yet where she designs and develops interfaces and occasionally draws pictures. She loves mixing art and technology and improving the way designers and developers work together.

More articles by Lynn Fisher…

About Robert Nyman [Editor emeritus]

Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City. He regularly also blogs at http://robertnyman.com and loves to travel and meet people.

More articles by Robert Nyman [Editor emeritus]…


17 comments

  1. Evgeny Stepanischev

    Look at this:

    http://bolknote.ru/files/webkit-purecss-image.html

    September 15th, 2014 at 06:12

    1. Robert Nyman [Editor]

      Looks nice! Is there only a WebKit version?

      September 16th, 2014 at 00:11

    2. Rizqi Nizamil

      Wow, it’s freakin’ cool!

      September 16th, 2014 at 02:29

    3. Joao

      x)

      September 16th, 2014 at 06:12

  2. njn

    Very cool!

    September 15th, 2014 at 20:11

  3. Felipe Moura

    Amazing work, and a real great way to show, step-by-step, the magic happening! :)

    I’ve written about geometric forms in CSS a while ago, but it was much more simpler than these of yours:
    http://felipenmoura.org/en/articles/creating-geometric-forms-using-divs-and-css3/

    September 16th, 2014 at 06:12

    1. Lynn Fisher

      Awesome! It’s articles like this that really helped me get started with CSS drawing experiments.

      Thanks for sharing! :)

      September 16th, 2014 at 09:12

  4. Jose Damian

    Wuau!!! This is really cool! I didnt know that you could do such great things just with CSS. Well, I really want to learn things like this.

    September 16th, 2014 at 07:04

    1. Lynn Fisher

      Definitely try it! Experiment and see what you can make. It was a really great learning experience for me. (And a lot of fun.)

      September 16th, 2014 at 09:15

  5. Rusty GB

    It was amazing!!! it’s just awesome to do with one div. I really would like to know your path on this findings and learnings

    September 16th, 2014 at 12:02

  6. Arrow Fan

    Oh my god! This is so cool. The tardis is really impressive. CSS is taking less space than an image for sure.

    September 17th, 2014 at 13:14

  7. Nowai Matthew

    Wow! What a great article! Never thought about using CSS in this way.

    September 18th, 2014 at 07:07

  8. Bijay

    Thats really cool man. Loved it.

    September 20th, 2014 at 21:59

  9. Jay Wilson Jr.com

    Lynn, great article!

    I came across your site a few weeks ago, I wondered how and why you created the images. Your site is a source of inspiration and it opened my mind to see what’s possible with pure CSS…

    Fantastic work!!

    September 22nd, 2014 at 17:16

  10. Dimas Dono

    This article is good and suitable for the creative guy. I thought it was difficult to create another drawing with CSS, even the abstract drawings. This article improves me to know more about CSS and realizes me to be the creative person and express my ideas in web design. So, thank you for the information. This is really amazing and useful. :-)

    September 26th, 2014 at 16:05

  11. Jane

    Awesome

    September 27th, 2014 at 00:02

  12. ArrowFanToo

    very nice. Thank you.

    October 2nd, 2014 at 13:45

Comments are closed for this article.