Skip to main content
Photography of Alvaro Montoro being a doofus
Alvaro Montoro

Sofia's Father

Drawing of a person in manga-style with the title: drawing a manga character with css from scratch

Drawing with CSS: Anime Character

html css css art webdev

Drawing in CSS is relaxing (at least for me) and an excellent way to practice CSS. Sometimes it's helpful to try new features that cannot always make it into production because not all browsers may support them.

In this article, I will explain how I drew the manga/anime drawing from the image above using HTML and CSS and dive into the creative process, getting into some detail about why I made some of the decisions and their pros and cons. At the end of the article, you will find a video with the whole process.

The Drawing

The drawing started from scratch. No HTML, No CSS. Just a blank page. Initially, I didn't know what I would draw at all! But I had volume 2 of the manga Pluto next to me, so I decided to code something manga-related, getting some inspiration from the cover (e.g., for the nose and the mouth).

I like the result, but I'm biased. Here is a live demo of the drawing (you can see the full version on CodePen):

Let's see how different parts of the image came to life.

Canvas

Don't worry. There's no place for <canvas> in CSS art. I like setting the root element of the drawing as a "canvas" and adding a grid at the beginning:

.canvas {
  width: 80vmin;   /* so it's responsive */
  aspect-ratio: 1; /* so it's a square.  */
  background:
    repeating-linear-gradient(#0003 0 0.125%, #0000 0 0.9875%, #0003 0 10%),
    repeating-linear-gradient(90deg, #0003 0 0.125%, #0000 0 0.9875%, #0003 0 10%);
}

This code generates a background with a 10x10 grid. Each square is 10%, and knowing that helps me place the items later: count the number of squares and multiply by 10 to calculate the percentage needed in top, left, width, or height.

The face breaks into three parts: the forehead, left, and right sides. For them, I used a single element (forehead), and then the ::before (left) and ::after (right) pseudo-elements for the sides.

I could have just done an egg shape, which is super easy to do in CSS with border-radius and different values for horizontal and vertical:

.egg-shape {
  width: 80vmin;
  height: 80vmin;
  border-radius: 100% / 80% 80% 120% 120%;
  background: red;
}

Instead, I overcomplicated things a little. However, having three elements allowed for sharper edges (e.g., the chin), and I wanted to try this approach as I always go the "egg-shape route." The result looks nice, but it might not have been worth it.

On the other hand, having different elements permitted some extra customization (like adding transform or inset shadows) that would have been more complicated if it was only one element. So not everything is bad.

Mouth

The mouth is not a simple line. It is also conformed of three parts to give some "texture," so it's not just a flat thing. In this case, I did it by playing with the border widths, but I could have done it with clip-paths (it would have been a pain later with the pseudo-elements) or linear gradients (it would have been a pain later with the shadows). Browsers may display slightly differently, but it is a price I'll pay.

The object is later used to provide some lip shadow with box-shadow: one on top and another on the side.

Ears

Both ears are the same. The trick to quickly flipping an element horizontally is to apply a transform of scaleX(-1) and voilà! It's done!

.ear {
  /* code to draw the ear: border-radius, backgrounds... */
}

/* select the second ear and flip it */
.ear + .ear {
  transform: scaleX(-1);
}

Once that's done, to make them look slightly different, I rotated the second one at a slightly different angle and also pushed it a little bit farther (from the other side). Combined with the various elements from both sides, that gives the impression of having two different ears.

Eyes

Drawing eyes is always tricky. They make or break the drawing, and the balance between one and the other is subtle. The eyes on this drawing are far from perfect, but they are Ok.

Each eye is two elements: a div (for the eye in itself) and one pseudo-class (for the pupil and iris). The eye will have an overflow:hidden so the pseudo-element doesn't extend beyond its scope.

The pupil and the iris are radial gradients, with more radial gradients for the light reflections and box-shadows to trace small lines on each eye side. However, after duplicating the eye and flipping the second one, there's something different left to do: to avoid having an unwanted symmetry that looks weird (light reflections coming from different directions), the pseudo-element of the second eye needs to be flipped too:

.eye {
  /* eye styles */
}

.eye + .eye {
  transform: scaleX(-1); /* flip the eye */
}

.eye + .eye::before {
  transform: scaleX(-1); /* flip the iris in the second eye */
}

Neck

The neck element is just going to be a container for its ::before and ::after pseudo-elements, which will be the sides of the neck.

Using box-shadow, the elements' shadow will be the colors of the neck, and it will provide some shadow. Using transform to rotate and skew, the shapes will be softer and look more like a cartoon neck than if I just had a rounded corner.

Hair

The hair consists of eight times the same element with slight variations (in position and size, but sometimes also in the background). I initially used borders and box shadows to draw the bangs, but that was a problem when trying to make them look different (plus, it looks terrible in browsers other than Chrome). Another problem with this approach is that the top part of the hair bangs has an undesired black line on Chrome (it has some trouble at times with things like that.)
So I opted for box shadows and gradients to draw the hair:

.bangs {
  width: 30%;
  height: 30%;
  border-radius: 0 0 100% 0;
  /* to draw the external border */
  box-shadow: inset -0.5vmin -0.25vmin; 
  /* the hair and internal border */
  background: radial-gradient(100% 170% at -10% 0, #0000 58.75%, #000 59% 60.25%, var(--hair-dark) 60.5%, var(--hair) 78%); 
}

Flipping the hair (remember the trick of scaleX(-1)) and rotating it makes it look different enough to be reusable... and that's what I did.

Face Lines

There are multiple lines in the drawing: eyebrow, nose, chin, etc. The "simple" way would be to have an element for each line (or group of lines), but the drawing will end up with many parts.

Instead, I opted for reducing the number of lines to a minimum and to shapes that can be replicated with background images (gradients). Then with a single element that covers the whole face, I added those lines one by one as radial-gradient or linear-gradient.

Accessibility

CSS art is not very accessible in general, but that doesn't mean that it doesn't have to be accessible. By adding a role of image and an aria-label with a description of the drawing, the drawing will be more accessible for assistive technology users.

<article role="img" aria-label="....">
  ...
</article>

I opted for using an <article> because it is a self-contained element. Ideally, instead of using aria-label I should have used aria-labelledby and placed an <h1> or <h2> with the title/description.

In this other article, I explain ways of making CSS art more accessible for everybody.

Live Coding Video

The whole process took around three hours, I sped the video by 2, so there's a 1.5 hours video showing how it is done step by step:

If that's too long or too slow, there is also an 8x speed video that only lasts around 20 minutes. 


If you like these videos or CSS art, check my Youtube channel! I publish videos about CSS and live-coding of CSS art.

Article originally published on