CSS Art
This is the recreation of a simple image created exclusively using CSS.
I gave myself the following restrictions for this challenge:
- No
clip-pathnor gradients, all shapes should be created using basic CSS features - No
z-index, layering should be defined by the element structure - Everything needs to be perfectly scalable
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
- Parent
- Parent
:before - Children
- 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:
- You have to repeat the value of the animated property both on
@keyframesand on the:hoverstyle, making it rather tedious to update. - Every elements that needs to be animated needs its own
@keyframesif the property or the amount it's animating is different from the others. - You are either stuck with no animation when the mouse leaves the
:hoverelement, or it will show the "un-hover" animation at the start.
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...