Creating Realistic Art with CSS
Tags: css, html, webdev, design
Published: 2020-08-24 12:32:00

It can be challenging enough to use CSS to match the exact layout you're going for, but could you imagine using it to paint a landscape? Or recreate an intricate playing card? Or even create a realistic portrait? That's exactly how Ben Evans uses CSS, and the methods he uses to make his art are fascinating.

![The Art of Ben Evans - A montage of the artwork we're covering in the article](https://dev-to-uploads.s3.amazonaws.com/i/4usptpgf1g9murz03byy.png)

I could write pages about how detailed and beautiful each of these pieces are. I love it when people push the limits of HTML and CSS, so we had to bring Ben in and learn his techniques!

## Unique HTML

The first thing that struck me when I look through Ben's code was HTML. There's an intense amount of styling happening, and yet he isn't using a single class or id. For example, here's the first 12 lines of code in his landscape piece:

```html
<landscape>
  <sky>
    <x>
      <x></x>
      <x></x>
      <x></x>
    </x>
    <x>
      <x></x>
      <x></x>
      <x></x>
      <x></x>
      ...
```

Ben explains that he's obsessed with small code and likes the appearance of the `<x>` elements. They're custom elements so there's no default CSS to battle against. For your own mental model, you can think of them as behaving just like divs.

So how does he style all these elements if they don't have classes?

## Styling with nth-of-type

Ben creates a bunch of elements in his HTML and then moves to his CSS. There he doesn't need to worry about naming each bit, because he's using the `nth-of-type` selector. This way he doesn't have to name things or change his HTML.

You might think this makes it harder to keep track of, but Ben leaves comments by the major pieces and that makes it easier to sort through.

A great tip from Ben for anyone trying CSS illustration: Start with the background and work forward instead of using z-index or translateZ. Ben says, "Safari in particular gets very confused with pseudo-elements and z positioning."

## Responsive rems

The fact that all of these pieces are responsive is pretty dang neat. How does he do it?

```css
html {
  font-size: 1vmin;
}
```

The `vmin` unit responds to the viewport's smallest side, so it will decrease if the screen gets smaller. The `rem` unit is relative to the font-size of the root element. So if we base everything in rems, they're all relative to the same value. Great technique!

## Drawing Techniques

Ben sets the elements he's drawing with to `position: absolute; top: 0; left: 0;` so that they leave the document flow. Then he moves them into place by using `transform: translate3d()`. He shapes the pieces with `border-radius` and various `transform`s, and sizes them with `rems`. Then most of the actual drawing uses `background: linear-gradient` and/or `box-shadow`.

`box-shadow` and `linear-gradient` are extra useful because they allow for multiple values separated by commas. Let's see how he used this technique to make the hair of the King of Hearts.

## King of Hearts

![A close up of a playing card illustrated with CSS](https://dev-to-uploads.s3.amazonaws.com/i/tnpuv2ccpehgm8uzx3iw.png)

### Box Shadow Hair

I've mainly used the `box-shadow` element for making soft blurry shadows under cards and other elements. Ben is using it for WAY more creative purposes in his work! The hair on the king is actually multiple layers of box-shadow. Let's check it out by messing with it.

![A diagram showing how box-shadow is used to draw the hair](https://dev-to-uploads.s3.amazonaws.com/i/urpwnqzz5e65ilm9to14.png)

By using commas you can put multiple box-shadows on a single element. Ben alternates blue and white, increasing the offset each time to space them out. In my version, I changed the `$blue` to different colors to show us how it's working.

The key for crisp lines? Setting the third property, `blur-radius`, to `0`.

### Animation

When you hover over the card it does a nice little spin and flip.

{% codepen https://codepen.io/ivorjetski/pen/ExaKmjw %}

Ben's doing a few things to achieve this great effect.

First, there's a bit of thickness to the card. It's really just two elements with white backs that Ben moves just a bit apart. The flip is quick enough that our eye registers the white in between as thickness. Awesome trick!

As the card rotates it gets dark when facing down and brighter when facing upwards. You almost don't pick up on it because it's so realistic. Ben has elements covering the front and back that animate between transparent, black, and white. Here's the code for the effect with a nice animation shorthand:

```css
@keyframes light {
  0%,
  100%,
  50% {
    background-color: $none;
  }
  25% {
    background-color: rgba($card, 0.7);
  }
  75% {
    background-color: $body;
  }
}
```

And you might not believe it, but the back of the card is just a single element. Pretty awesome.

Check out the video [timelapse](https://www.youtube.com/watch?v=mUfsozWywwM) for this one to watch it all come together. Check out the [King of Hearts](https://codepen.io/ivorjetski/pen/ExaKmjw) on CodePen.

## Isla

![A portrait of a girl drawn in CSS](https://dev-to-uploads.s3.amazonaws.com/i/nvejhfgu18a17nui14tx.png)

This was the first piece I saw of Bens, and it's still probably my favorite. All the subtle touches he added to this to make the skin feel more realistic are just fantastic.

If you look close you can see that the iris is a bunch of rounded squares rotated around the center point. Ben used the same technique in the next piece for the center of the lemons.

The perfectly detailed hair really makes this piece. The eyelashes, eyebrows, face fuzz, and head hair all have different properties and make it feel more realistic. Ben's using `box-shadow` here in a more complex way than he did for the King's hair. Instead of white between the colors, he makes the gap transparent and changes the color to add depth.

{% codepen https://codepen.io/ivorjetski/pen/dBYWWZ %}

Here's a [timelapse](https://www.youtube.com/watch?v=py8Nl_7Furc) of Ben making the piece, and here's [Isla on CodePen](https://codepen.io/ivorjetski/pen/dBYWWZ).

## Water and Lemons

![A glass of water and lemons, illustrated in CSS](https://dev-to-uploads.s3.amazonaws.com/i/1kd4gxgnavqyniu2lkxq.png)

### CSS Glass

I assumed the most difficult part of this piece was the glass. All the refractions and highlights are perfect and make this glass look so good!

Turns out I was mistaken. Ben says, "The main thing I have learned through all my CSS art is that man-made materials are a lot easier to draw with CSS than organic materials."

Makes sense now that I think about it. The shapes and coloring of the lemons are much more complex than most man-made things. Ben even points to our Frontend Friend [Oscar Salazar's awesome glass ball](https://codepen.io/raczo/pen/xxZyQGo) as an example of glass done well in CSS!

{% codepen https://codepen.io/ivorjetski/pen/xMJoYO %}

## Landscape

![A landscape illustration of a town overlooking the ocean](https://dev-to-uploads.s3.amazonaws.com/i/m8969f32l62213tif59d.png)

Not only is this piece stunning, but it holds a clever SCSS trick that lets the viewer tweak the colors. At the top of the CSS you see:

```css
// v CHANGE THE COLOUR OF THE SUN v //

$sun1: #f2de6f;
```

What's cool is that by using SCSS color functions, every other color is based on this one color. From that seed, Ben shifts the hue, changes the saturation, brightness, and opacity to create the rest of the colors.

As an example, let's set the variable `$start` to `aqua` then use the `adjust-hue` function to make a new color. Hue is a value between `0` and `360` as you can see in the color wheel below. We can make a new color by increasing the teal's hue by `60deg`.

![The color function adjust-hue and how it changes colors](https://dev-to-uploads.s3.amazonaws.com/i/gjvmhpy4c7cwtjo10qdb.png)

The result is more of a 'blue' rather than teal. If we were to use `-60deg` we'd get a green color.

Ben is using this function and many other [SCSS/Sass color functions](https://sass-lang.com/documentation/modules/color) to create his palette from the color you choose.

Let's look at some examples. On the left is Ben's original color and four of the colors that are based on it. Then check out the results as I change `$sun1` to `orange`, `dodgerblue`, and `darkgray`.

![Creating landscape colors from different seed colors](https://dev-to-uploads.s3.amazonaws.com/i/oj0jimpipshme2ooxxyl.png)

What a great way to utilize SCSS color functions! I love this concept and would love to see more people use it in their work.

### Halftone Dots

If you look closely you'll notice a dot pattern in the water similar to an old-school comic strip.

![A closeup of Halftone dots](https://dev-to-uploads.s3.amazonaws.com/i/74gkhoo6de3bs414njow.png)

They're called halftone dots, and I didn't realize they could be made with just a couple lines of CSS. Ben shares his solution with us:

```css
background-image: radial-gradient(blue 30%, #fff 0);
background-size: 1rem 1rem;
background-position: 0 0, 0.5rem 0.5rem;
```

Ben uses them in this landscape piece to add a bit of texture to the water. He's also using them on parts of the lemons in the previous piece.

{% codepen https://codepen.io/ivorjetski/pen/xxGYWQG %}

Check out the [timelapse here](https://www.youtube.com/watch?v=rUCVBNNyjC4).

## Wrap Up

Well, that's the end of the trail for us today. We learned so much by looking through Ben Evans's work. Ben doesn't use ids or classes but instead picks up the next element to style with `nth-of-type`. We saw just how powerful `box-shadow` can be, and how Ben uses it to make repetitive shapes or thin, wispy hair. Ben made his landscape customizable through Sass variables, and we saw how they work along with a few examples.

I hope this inspires you to give some CSS art a try, or maybe just use `box-shadow` in a new way on your next web build.

Huge thanks to Ben for teaching us his techniques. Go check out his [website](https://www.tinydesign.co.uk/) and follow him on [Twitter](https://twitter.com/ivorjetski).