CSS Art

This is the recreation of a simple image created exclusively using CSS.

I gave myself the following restrictions for this challenge:

You can also boop his snoot!

Code
Explanation

How I made it

Here I'll describe a bit what I learning trying to make this drawing.

There are probably better guides than this if you want to make your own CSS art, I just wanted to write down my learnings.

Reasons for the self-imposed limitations

Modern CSS allows several advanced techniques that make creating arbitrary shapes rather trivial so I wanted to avoid them to learn how to hack CSS in unintended ways.

I also wanted to make sure I make full use of the vector-based CSS features and keep the drawing scalable with its container.

Scalability and units

CSS offers several "kinds" of length units:
You have absolute length units like px, cm, etc. Of course, these are not viable for content that needs to scale with its container.
Units relative to the viewport like vw are also not useful here.
There are percentages, which work for sizes but have different meanings for other properties so they can't really be used as your main unit.
Some units are relative to the font size like em and ex.
The best starting point is units based on the container length like cqw but these might vary with nested elements so you can't really use them directly.

My hack: defining the font size of each element that is a direct child of the main container using cqw units, then using ex which is relative to the font size everywhere a length value is needed. I scaled the font size to make ex roughly equal to 1 pixel with the default width. Of course the actual size might vary by changing the font family on the image element.

css-pic > * {
    font-size: calc(100cqw / 265.65);
}
        

Shapes!

This is the fun part, trying to make various shapes using CSS. Some techniques I knew already while others I learned while making this.

The examples here will use absolute units to simplify the CSS as much as possible.

The basics

Most of the shapes you can create with CSS are based on rectangles with rounded corners/

Rounder corners

If you set your border-radius to 100%, you'll get ellipses:

Of course, you can have different corners have different roundness and also on the same corner you can have different radii on the x and y coordinates:

Fancy borders

By using very thick borders you can get trapezoids and triangles:

If you have a circle and set one of its border to have thickness and all the other borders have 0 width, you get a crescent shape:

Other tricks

3 for the price of 1

Whenever you have an element in the HTML, you can actually get 3 shapes out of it by enabling its :before and :after. Keep in mind that the default rendering order is

  1. Parent
  2. Parent:before
  3. Children
  4. Parent:after

Selecting selectors

If you have slight variations of the same shape, or you want to change the position of different instances you can do it without specifying classes for every element by using :nth-of-type. It will select a specific element based on how many elements with the same tag appear in their parent. This works even if there are other elements mixed into their parent (unlike :nth-child).

Animations

I wanted to animate the snoot on hover. My first approach was to use animations:

This approach has several drawbacks:

So next I went to transition instead, basically it's a shorthand for animations but it's applied every time a property changes:

This worked well enough but it clashed with my scalability hack described above. Basically when the parent element is resize, changes to its font size caused by calc() were interpreted by the browser as changes in the properties using ex units. This made the animated items not move properly.

I resolved this by using margin as an offset from the absolute position.

Here is a recreation of the picture using CSS but without restrictions. All the shapes are actually gradients:

Code
Explanation

Gradient Basics

The main trick is that you can specify multiple backgrounds for an element. Using gradients with different sizes and positions (and no-repeat) you can create different shapes.

If you use percentages for the size and position, the gradient will scale seamlessly. While the size if fairly intuitive (80% means 80% of the width or height), the position is a bit less so: For the X coordinate, 0% will result the left side of the gradient to touch the left side of the container, 100% will result on the right side of the gradient to touch the right side of the container.

Linear gradients allow you to fill a rectagle or draw rotated stripes:

Radial gradients allow you to draw ellipses, using farthest-side (or a radial size of 50% 50%) ensures a centered ellipse will have stops touching the edges of the gradient box at 100%.

You can also move the center and size of the ellipse however you want. Only the area within the gradient box will be displayed.

You'll need to add some transparent stops to actually see the ellipse. While having one stop start immediately after the previous ends does work, it can lead to some aliasing so leaving small gaps between them helps with smoothing this.

Finally, you have conic gradients, with which you can draw triangles. As with radials, you can move their center around.

Animating Variables

For transition to work on CSS variables, you need to define them as a @property. After that the variable can be animated like any other CSS property.

@property --hover {
    syntax: "<number>";
    initial-value: 0;
    inherits: true;
}
.glax-simple {
    --hover: 0;
    transition: --hover 0.2s;
}
.glax-simple:hover {
    --hover: 1;
}
        

Now for a more complex image, again recreated using only gradients:

Code
Explanation

Since aligning all the gradients is a right pain, for this I created a simple visual editor and I created this image using that. The editor is a bit rough but beats constantly tweaking CSS values...