DEV Community

Cover image for How to create pure CSS illustrations and animate them - Part 1
Agathe Cocco
Agathe Cocco

Posted on

How to create pure CSS illustrations and animate them - Part 1

I've always had a great interest in design, illustrations and colour palettes, but having spent the last few years focusing on becoming a better front-end developer, I've been left with little time to practice my creative skills. When I was first introduced to CSS images, I couldn't wait to give it a try. At last I could play around with shapes and colors, and explore and develop my creativity while coding!

But first, what is a pure CSS image?

A pure CSS image is an illustration that has been built with HTML and CSS. It excludes the use of image file imports, or code generated by exporting graphics in illustration software. In other words, it's an image that has been entirely manually coded in a code editor, using HTML and CSS only.

The principle is simple: with HTML, you can create as many divs as you like, which will each represent the basic shape of a component of the final image. With CSS properties such as gradients, transform, border-radius, shadows and so on, you can transform these basic shapes and arrange them into a nice illustration.

Why CSS images?

But what’s even the point of CSS images, you ask? CSS is primarily meant to style web pages after all, and there are more adapted and efficient tools to produce quality and lightweight graphics for web design (think SVG). Indeed, not only does using CSS for illustration have some design limitations, the result can be overly complex, time consuming and face some major cross browser/device issues.

So there is definitely a place and time for CSS images. However, creating CSS Illustrations is a great way to get better at CSS and to be introduced to new tools and concepts, such as animations, preprocessors or more obscure CSS properties. I feel my CSS skills have skyrocketed ever since I started coding CSS images, and I have become much more familiar with concepts I didn't know much about, like the wide variety of CSS selectors, 3D transforms and keyframes animations.

Who is it for?

CSS illustrations are great for:

  • Illustrators or designers hoping to use their designing skills to learn, or get more confident with html/css
  • Front-end developers hoping to work on their creativity and develop a good eye for design
  • Anyone wanting to have a bit of fun while strengthening their CSS skills
  • Anyone hoping to connect with a community, inspire and be inspired
  • Or anyone up for a good challenge

In this series, we'll learn how to create three CSS illustrations, ranging from simple to complex. We'll learn the basics of CSS animations and how to use them to animate our illustrations. Along the way, we'll find out what concepts, tools and techniques can help speed up our workflow.

Part 1: Learning basics and workflow tips with a CSS Smiley Face
Part 2: Intro to CSS animations with a CSS Polaroid
Part 3: More advanced techniques with a CSS Lighthouse Scene

While this tutorial series doesn't require any advanced knowledge of web development, I'm assuming you are familiar at least familiar with HTML and CSS.

A couple of suggestions before we begin:

  • Open a CodePen account, if you don't have one already. It'll take away the pain of having to set up a project, especially since we'll be using CSS preprocessors and templating languages.
  • Use either Chrome or Firefox, as other browsers have proven to be buggy with CSS illustrations.

Basics

Okay enough talk, let’s get started with our first CSS image!

Here’s a circle:



<div class="circle"></div>


Enter fullscreen mode Exit fullscreen mode


html,
body {
  height: 100%;
  width: 100%;
  overflow: hidden;
  padding: 0;
  margin:0;
}
body {
  background: #FEEE9D;
}
.circle {
  position: absolute;
  width: 300px;
  height: 300px;
  transform: translate(-50%, -50%);
  top: 50%;
  left: 50%;
  border-radius: 50%;
  background-color: #FBD671;
}


Enter fullscreen mode Exit fullscreen mode

Nothing impressive so far.

We're going to add a few elements to turn this circle into a smiley face. The circle will be the head, and we can add eyes and a mouth:



<div class="head">
  <div class="face">
    <div class="mouth"></div>
    <div class="eye-group">
      <div class="eye eye-left"></div>
      <div class="eye eye-right"></div>
    </div>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode


html,
body {
  height: 100%;
  width: 100%;
  overflow: hidden;
  padding: 0;
  margin: 0;
}
body {
  background: #FEEE9D;
}
* {
  position: absolute;
}
.head {
  width: 300px;
  height: 300px;
  transform: translate(-50%, -50%);
  top: 50%;
  left: 50%;
  border-radius: 50%;
  background-color: #FBD671;
}
.face {
  width: 150px;
  height: 170px;
  left: 75px;
  top: 75px;
}
.mouth {
  width: 100%;
  height: 75px;
  bottom: 0;
  left: 0;
  background-color: #20184E;
  border-radius: 10px 10px 150px 150px;
  border: 5px solid #20184E;
}
.eye-group {
  width: 150px;
  height: 50px;
  top: 10px;
  left: 0;
}
.eye {
  background-color: #20184E;
  width: 30px;
  height: 50px;
  border-radius: 50%;
}
.eye-left {
  left: 15px;
}
.eye-right {
  right: 15px;
}


Enter fullscreen mode Exit fullscreen mode

Okay, let’s have a look at what’s going on here.

Main container

We have a main container, in this case the .head element, that contains every other HTML elements. This is, as you would expect, to center our illustration, and serve as a reference for the positioning of everything else.

Absolute positioning

All elements have the position: absolute property. This allows, with the help of the top/right/bottom/left properties, to precisely position them relatively to their parent. It's important to note that absolute positioning removes elements from the natural flow of the page. It means that the position of an element with that property will not affect the position of other elements, and will not be affected by their position either. Assigning the position: absolute property to all elements by default will ensure they will all be independent from each other. More on positioning here.



* {
  position:absolute;
}


Enter fullscreen mode Exit fullscreen mode

Nesting

Following this logic, we use nesting to group elements together. Observe the .face element. It is not an actual component of the image, but used only to group the eyes and the mouth together. Both .mouth and .eye-group 's position (and size, if we use % instead of px) will refer to .face. If, for example, we decide we want to reposition the face on the head later, we can do it at once instead of having to tweak the properties of both .mouth and .eyes. This is basic CSS logic and won't be new to you if you have experience in UI development. As a rule of thumb, nesting allows a clearer structure because it groups elements that belong together, and make the illustration easier to manipulate.

Stacking

Using absolute positioning allow us to have more control on the stacking order of the elements. Naturally, the stacking order follows the flow of the HTML elements. The first object will be at the bottom of the pile (at the far back), while the last one will be on top (closest to you). With the z-index property, we can change the position of each element in the pile and make them overlap how we want to, provided these elements belong to the same stacking context. The higher the value of z-index, the higher it'll be in the pile.

Stacking contexts are a bit tricky and don't always behave as you would expect them to. You can read more about what triggers the creation of a new stacking context here.

Classes only

We only use CSS classes to target and style our HTML elements as we don't want to be bothered by specificity issues.


Phew, that was a lot already. But these are very important concepts of CSS illustrations, and CSS in general. Once you are comfortable with positioning and stacking, your CSS skills will improve drastically.

Now that we have a basic understanding of how to position elements, let’s jazz up our smiley face a little. I want to add a few details like a tongue, pupils and a shadow.

The shadow is straightforward but adds a nice touch:



.head {
  width: 100%;
  height: 100%;
  background-color: #FBD671;
  border-radius: 50%;
  box-shadow:inset -10px -10px 0px #EFBB42;
}


Enter fullscreen mode Exit fullscreen mode

The tongue is a simple pink circle, but we only want to show part of it to create the illusion that it's inside the mouth. To do this, we nest .tongue in .mouth and apply the overflow: hidden property to .mouth.



<div class="head">
  <div class="face">
    <div class="mouth">
      <div class="tongue"></div>
    </div>
    <div class="eye-group">
      <div class="eye eye-left"></div>
      <div class="eye eye-right"></div>
    </div>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode


.mouth {
  width: 100%;
  height: 75px;
  background-color: #20184E;
  left: 0;
  bottom: 0;
  border-radius: 10px 10px 150px 150px;
  border: 5px solid #20184E;
  overflow:hidden;
}
.tongue {
  width: 100px;
  height: 80px;
  left: 25px;
  top: 30px;
  background-color: #F15962;
  border-radius: 50%;
}


Enter fullscreen mode Exit fullscreen mode

And we can add pupils with a simple oval inside our .eye elements.



<div class="container">
  <div class="head">
    <div class="face">
      <div class="mouth">
        <div class="tongue"></div>
      </div>
      <div class="eye-group">
        <div class="eye eye-left">
          <div class="pupil"></div>
        </div>
        <div class="eye eye-right">
          <div class="pupil"></div>
        </div>
      </div>
    </div>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode


.pupil {
  width: 10px;
  height: 15px;
  top: 5px;
  background-color: #FBD671;
  border-radius: 50%;
}


Enter fullscreen mode Exit fullscreen mode

It's looking much better already.

More advanced concepts

Now that we have built our first CSS image, let’s take a step back and look at how we can improve our code. While these steps aren't necessary at all to build CSS images, I find they greatly improve my workflow, and helped me getting much better at CSS in general.

CSS Preprocessors

If you are familiar with CSS preprocessors you know there’s a better way to do this. I won’t delve into how to set up a preprocessor as this is beyond the scope of this article. Instead I strongly recommend you use Codepen, which has all preprocessors built in. You just have to select the one you want in the CSS settings and you’re all set.

I personally like to use SASS/SCSS, but feel free to use any of the options available.

The best known feature of preprocessors is the ability to use variables. Preprocessor variables work just like javascript ones. You declare them and assign them a value once, then you can use and reuse them throughout your code. This is a great tool as it avoids repetition and gives you a lot more flexibility. Let's implement a few variables in our CSS. A good use for variables is to define colors.

A SCSS variable always starts with the $ sign:



$black: #20184E;


Enter fullscreen mode Exit fullscreen mode

You can name it whatever you like, but it must start with $ and be assigned a valid CSS property.

Then you can use it like so:



background-color: $black;


Enter fullscreen mode Exit fullscreen mode

You can see where this would come handy. If we later decide that we want to change this particular color, we only have to do it in one place and it will be reflected everywhere the variable is used in our code. Also when it comes to colors, it's a lot easier to remember the name of a variable you created than a RGB or HEX value.

I've added my new color variables to the top of the code so we can find them easily. Declaring variables in the global scope (as opposed to declaring them in a selector) will make sure we can use them everywhere.



$black: #20184E;
$head-color: #FBD671;
$background: #FEEE9D;
$tongue-color: #F15962;


Enter fullscreen mode Exit fullscreen mode

Now we can change our css properties so they point to the variables instead of the hard coded HEX values.

Eg:



.mouth {
  width: 100%;
  height: 75px;
  bottom: 0;
  background-color: $black;
  border: 5px solid $black;
  border-radius: 10px 10px 150px 150px;
  overflow: hidden;
}


Enter fullscreen mode Exit fullscreen mode

CSS now has native variables that pretty much make the preprocessor ones unnecessary. However, preprocessors offer a lot more than variables and are still extremely useful in many other ways.

SCSS also comes with many helper functions, including color functions. These functions are quite powerful as they allow you to manipulate colors very easily.

Let's have a look at the inset shadow applied to the head. Right now we are using a hard coded HEX value, which I've had to manually look up. We are going to use the darken function to generate this value instead:



.head {
  border-radius: 50%;
  width: 100%;
  height: 100%;
  background-color: $head-color;
  box-shadow: inset -10px -10px 0px darken($head-color, 20%);
}


Enter fullscreen mode Exit fullscreen mode

Basically, the function takes $head-color as a parameter then and increases the amount of darkness by 20%.

I love using color functions because not only do they eliminate the need to resort to some design software to generate colors, they also help with creating a more harmonious color palette for your illustration.

Another great feature of SASS is nesting. Nesting allows you to nest selectors within parent selectors in order to create shortcuts. It creates a better hierarchy and readability of your CSS, and avoids repeating selectors over and over.

For example



.pink {
  background-color: pink;
  .purple {
    background-color: purple;
  }

}


Enter fullscreen mode Exit fullscreen mode

will be compiled to:



.pink { 
  background-color: pink;
}
.pink .purple { 
  background-color: purple;
} 


Enter fullscreen mode Exit fullscreen mode

SASS also offers the & selector, which basically refers to the parent selector. So



.pink {
  background-color: pink;
  &.purple{
    background-color: purple;
  }
}


Enter fullscreen mode Exit fullscreen mode

will be compiled to:



.pink { 
  background-color: pink;
}
.pink.purple { 
  background-color: purple;
} 


Enter fullscreen mode Exit fullscreen mode

Using the & selector will not target child elements that have a class .purple like in the example above. It will target the parent element that has the classes .pink AND .purple.

Beware of too much nesting as it can make your code overly complex and less readable, which would be the opposite of what we're aiming for. As a rule, I tend to go for a maximum of 3 levels of nesting.

With this in mind, let's implement nesting in our code. For example:



.eye-group {
  top: 10px;
  left: 0;
  width: 150px;
  height: 50px;
}
.eye {
  background-color: $black;
  width: 30px;
  height: 50px;
  border-radius: 100%;
}
.eye-left {
  left: 15px;
}
.eye-right {
  right: 15px;
}


Enter fullscreen mode Exit fullscreen mode

becomes:



.eye-group {
  top:10px;
  left:0;
  width: 150px;
  height: 50px;
  .eye {
    background-color: $black;
    width: 30px;
    height: 50px;
    border-radius: 100%;
    &.eye-left {
      left:15px;
    }
    &.eye-right {
      right:15px;
    }
  }  
}


Enter fullscreen mode Exit fullscreen mode

Preprocessors are a great asset in building CSS images and will drastically speed up your workflow. They also offer more advanced options like function, loops, mixins etc. These will come in handy in more intricate illustrations, as we'll see in the last part of this series.

:before and :after

Until now, we’ve created a div for each shape in our smiley face. While this is totally fine, there is a way to reduce the number of HTML elements. This is where the pseudo-selectors :before and :after come in.

These pseudo-selectors come automatically with any HTML element you create. So for any element in your HTML, you’ll be given two extra containers within which to add content. Although they are not initially present in the DOM, you can target them with the content property, like so:



.some-class:before, .some-class:after { 
  content: "";
}


Enter fullscreen mode Exit fullscreen mode

This property can be used to insert content such as text or images. In CSS illustrations, we don't want any of that, but we still want to use the selector, which we can do by using empty quotes. While it might look like a unnecessary step, this is the property that will ensure these selectors are available, so make sure it's there. A good way to not forget it, as well as avoiding repetition, is to assign it to all :after and :before elements by default:



*:before, *:after {
  position: absolute;
  content: '';
}


Enter fullscreen mode Exit fullscreen mode

As you would expect, :before and :after depend on their parent for size and positioning.

Let's look at how we can use these in our code. Consider the .mouth element. It has a child: the .tongue element. We're going to replace the .tongue selector with an :after pseudo-selector:



.mouth {
  width: 100%;
  height: 75px;
  bottom: 0;
  background-color: $black;
  border: 5px solid $black;
  border-radius: 10px 10px 150px 150px;
  overflow: hidden;
  &:after {
    background-color: $tongue-color;
    width: 100px;
    height: 80px;
    border-radius: 50%;
    left:25px;
    top:30px;
  }
}


Enter fullscreen mode Exit fullscreen mode

The same logic can be applied to replace the .pupil elements:



.eye-group {
  top: 10px;
  width: 150px;
  height: 50px;
  .eye {
    background-color: $black;
    width: 30px;
    height: 50px;
    border-radius: 100%;
    border: 5px solid $black;
    &:after {
      width: 10px;
      height: 15px;
      top: 5px;
      background-color: #FBD671;
      border-radius: 50%;
    }
    &.eye-left {
      left: 15px;
    }
    &.eye-right {
      right: 15px;
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

Now we can get rid of the .tongue and .pupil divs in the html:



<div class="head">
  <div class="face">
    <div class="mouth"></div>
    <div class="eye-group">
      <div class="eye eye-left"></div>
      <div class="eye eye-right"></div>
    </div>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode

While this isn’t absolutely necessary, I find this practice helps to keep my code cleaner, and avoids overcrowding my HTML. Again, only use pseudo-selector when it makes sense to do so. (eg: to create an element visually related to its parent container).

HTML templating language

Another step we can take to improve our code is to use an HTML templating language. I like to use Pug, which is also built in to the Codepen editor.

Here’s how it works: Pug uses tag names to represent a full HTML element. For example, <div></div> will simply translate to div in Pug. It uses indentation to represent nesting, recreating the tree structure of HTML.

In CSS images, it is recommended to use div elements only, and assign them a class to be able to easily target them. The way to represent a div with a class in Pug is as follows:

Normal html:



<div class="container"></div>
```

Pug:

```html
div.container
```

Or, because `div` is the default tag name, it can be omitted: 

```html
.container
```

I find this syntax to be much cleaner, and it allows me to focus on the class. Since I already know that all my elements are divs, I don't need any useless syntax polluting my code.

Let's convert our HTML to Pug:

```html
.head
  .face
    .mouth
    .eye-group
      .eye.eye-left
      .eye.eye-right
```

Much cleaner. 

Similarly to CSS preprocessors, there are many things you can do with Pug as it is powered by JavaScript: mixins, includes etc. When it comes to CSS images, one of the most powerful features is loops. We'll see a bit later how to use them.

Here's the final project in CodePen. You can see that none of our latest changes have affected the illustration at all.




Right, we’ve learned a lot already! How to create various shapes and assemble them into a cohesive illustration, how to use hacks to emulate more complex shapes, how to use preprocessors, templating languages and pseudo-selectors to improve our workflow and clean up our code. In the second part of this series, we'll practice these new concepts and build a CSS Polaroid. Then, we'll learn how to animate it.
 
[Part 2: Intro to CSS animations with a CSS Polaroid](https://dev.to/agathacco/how-to-create-pure-css-illustrations-and-animate-them---part-2-1ao4) 
Enter fullscreen mode Exit fullscreen mode

Top comments (48)

Collapse
 
jmtiong profile image
Jia Ming

Splendid article! Very straight forward and neat. Thank you for this!

Just one question

* {
  position: absolute;
}
Enter fullscreen mode Exit fullscreen mode

Is this how you make element to be fixed in the position? I am always confused with absolute, fixed and relative

Collapse
 
agathacco profile image
Agathe Cocco

Thank you, glad you liked it!

Yeah CSS positioning is weird. Here's roughly how it works:

By default, all elements have a static position, which means the normal flow of the page controls their positioning. It basically means that all elements are interdependent and will make room for one other.

With position: relative, the normal flow of the page still decides where the element is positioned, but gives you the option to nudge it with the top/right/bottom/left properties. So if you set for example, top:100px, it will move the element from its original position

Absolute positioning removes the element from the flow of the page and basically gives you full control on its position on the document. BUT an element that has absolute positioning will always refer to another element for its position. By default, its the document, so if you start using the top/right/bottom/left properties, it will position your element relatively to the document.
In order to have a child respond to the position of its parent, you need to set position absolute (relative works too) to the parent as well. If the direct parent doesnt have a position absolute (or relative), the element will go up the html chain and check its grand parent, great grand parent etc, until it finds an ancestor that has the position absolute value. then it will refer to it for its own positioning.

Position fixed also remove the element from the page and lets you position it with the top/right/bottom/left properties, but it will always do so relatively to the document. That's not very useful with CSS images, but is a great way to keep a navbar at the top of the document for example, even when scrolling occurs.

I like to use the code snippet above in CSS images, because it makes sure that an element will always be positioned relatively to its parent.

I hope this makes some sense? Let me know!

Collapse
 
jmtiong profile image
Jia Ming

Thank you for the detailed explanation, I have a clearer idea of how this actually works,

In a sense, relative position will still be interdependent on the rest of the elements in the flow but allow me to move it around with the properties.

Absolute will always find a parent position to take reference from before applying the properties and fixed position does not care about anyone and will immediately apply the properties by taking reference to the document!

I shall go and experiment with this to firm up my foundation, and after I managed to draw something out, I shall show it to you! :D

Thread Thread
 
agathacco profile image
Agathe Cocco

That's it!
Have fun experimenting, I'm looking forward to seeing your first piece of css art!

Collapse
 
pandaa880 profile image
Prashant Chaudhari

* selects everything and make their position absolute because, we need absolute positioning in order to set the elements at desired place.

You can learn more about CSS Positioning on MDN CSS Docs.

Collapse
 
jmtiong profile image
Jia Ming

Sweet! okay let me google on that as well! Thank you very much! :)

Collapse
 
marvindanig profile image
Marvin Danig • Edited

This is an awesome tutorial, thanks!

My friends and I made an entire book of ABCs with CSS3 Animals for babies sometime back: bubblin.io/book/abcd-animal-book-b...

Collapse
 
agathacco profile image
Agathe Cocco

Thank you!

I love the baby book!! Great work!

Collapse
 
marvindanig profile image
Marvin Danig • Edited

I was wondering if you'd be interested to do an OSS book of just fruits with CSS3 in your spare time? Like whenever!

Thread Thread
 
agathacco profile image
Agathe Cocco

Yeah that sounds like a fun project!

Thread Thread
 
marvindanig profile image
Marvin Danig • Edited

Cool… I'll set up a project on Github shortly. Excited!

Updates: Here we go!: github.com/marvindanig/fruits-and-...

Let's connect over email perhaps… I'm at marvin*marvindanig*com!

Collapse
 
ben profile image
Ben Halpern

Wow, really great walk-through!

Collapse
 
rajatkantinandi profile image
Rajat Kanti Nandi

I had tried a few earlier.
But I'm very much inspired by your post & want to try out one daily.
Just created Chrome logo art.
Here: codepen.io/rajatkantinandi/full/GX...
Although it's not perfect but want reach the level of perfection that you showcased eventually.
Thanks for the great post.

Collapse
 
aspittel profile image
Ali Spittel

This is an awesome walkthrough!

Collapse
 
bitwombat profile image
Bit Wombat

I'm really interested in HOW you draw something like that.

At least with drawing software, there's a stylus or mouse that's sort of like a pencil.

But with numbers in CSS for position, size, etc., are you just trial-and-erroring things live until they look right? Nudging the positions, radii, aspect ratios around?

And how do you think up "oh, I need a red, clipped circle for the tongue, but elevated a bit from the mouth's bottom edge"?

CSS comes easily to me but for that stuff I'd appreciate some hints on how you think!

Many thanks.

Collapse
 
agathacco profile image
Agathe Cocco

I've been practicing CSS images for a while now so it comes more easily, but I didn't know where to start either when I first tried.
You can start by replicating an existing illustrations. Go on dribbble.com for example, and pick something simple to begin with. It'll let you focus on the actual process without having to worry about the creative aspect.
And yes, often play around a lot and try out different things before I'm happy with the result. Most of the time, I only have a rough idea of what I'm going to build, and my project takes shapes as I give it more thoughts. I also look at a lot of illustrations, drawings and photography, which keeps me inspired.
Hope this helps :)

Collapse
 
marcus profile image
Marcus M

Awesome, really great stuff!

Be sure to check for browser compatibility though 😶

Collapse
 
agathacco profile image
Agathe Cocco

Oh yes! There are definitely some bugs in safari that I'm still working on, totally forgot to mention it. Safari is a real pain with css illustrations. Thanks for bringing it up!

Collapse
 
marcus profile image
Marcus M

The beautiful things frontend-devs could create if people would actually update their browsers 😃

You haven't suffered until you try to implement a colored box-shadow in IE8 😭

Thread Thread
 
agathacco profile image
Agathe Cocco

Haha, true. I'm glad I don't have to worry about IE anymore in my current job. And certainly not in CSS images, they're only meant to be FUN! :D

Collapse
 
mohanant profile image
MohananT

Straight into my reading list

Collapse
 
macurious profile image
Mark

Thank you for this fun and detailed article! Going on with Part 2 now...

Just one thing. - What you do with this...

*:before, *:after {
  position: absolute;
  content: '';
}

...is to give every element on the page a :before and :after pseudo element. If you check the markup in the dev tools this doesn't look very nice. ;)

Maybe it would be better to create a mixin like this...

@mixin pseudo($pos: absolute, $content: '') {
  content: $content;
  position: $pos;
}

...and then call it in your pseudo element like so:

&:after {
  @include pseudo;
  width: 100px;
  height: 80px;
  ...
Collapse
 
agathacco profile image
Agathe Cocco

Very good point!
I think if you have SEO or performance in mind, then going for a lighter markup is a great idea. I admit than in the case of CSS images, I am more interested in avoiding repetition. But for real life projects, I would definitely think of using a @mixin.
Thanks for your input.

Collapse
 
bytrangle profile image
Trang Le

This is so inspiring, Agathe. I used to wonder what's the point of all those CSS illustrations on Codepen. Visually dazzling, yes, but I didn't get the point. Then recently I started playing with making CSS icons, and it dawned on me: It doesn't need to have a point. I have discovered aspects of CSS that I never knew, or never got the opportunity to practice. I'm excited by what cool drawings can be made with pure codes, and that's enough.