CSS Art: How to Draw a Minion

You will build a recognizable Minion using only HTML and CSS. By the end, you will have a clean, scalable character that renders without images or SVG. The process teaches layering, border tricks, and pseudo-elements to create complex shapes. You will also add tiny touches like a blinking animation, shiny pupils, and a denim pocket to ground the character in a playful, polished style.

Why CSS Art: How to Draw a Minion Matters

CSS art sharpens your understanding of layout, stacking, and shape construction. When you can draw a character with CSS, building badges, avatars, and mascots feels straightforward. You avoid network requests and asset pipelines. Everything remains themeable, responsive, and easy to tweak in a code review. You will also reuse the same building blocks for other icons and illustrations, which reduces maintenance. The Minion is a great subject because it relies on basic primitives like circles, rectangles, and triangles, all rendered with CSS.

Prerequisites

You only need a working knowledge of CSS and a bit of comfort with positioning. The tutorial stays focused on readable properties that map to your mental model of the layout.

  • Basic HTML
  • CSS custom properties
  • CSS pseudo-elements (::before / ::after)

Step 1: The HTML Structure

The markup is a single component with semantic roles for screen readers. The body contains every visual part: overalls, straps, a goggle band, eyes, a mouth, hair tufts, arms, and legs. Each piece is a lightweight div or span that we will style into shape. The final HTML below is the exact structure used in all later steps.


<div class="minion" role="img" aria-label="Friendly CSS Minion">
  <div class="minion__body">
    <div class="goggle-band" aria-hidden="true"></div>

    <div class="goggles" aria-hidden="true"></div>

    <div class="eye left" aria-hidden="true">
      <span class="iris"></span>
      <span class="pupil"></span>
      <span class="shine"></span>
    </div>
    <div class="eye right" aria-hidden="true">
      <span class="iris"></span>
      <span class="pupil"></span>
      <span class="shine"></span>
    </div>

    <div class="mouth" aria-hidden="true"></div>

    <div class="overalls" aria-hidden="true">
      <div class="strap left"></div>
      <div class="strap right"></div>
      <div class="button left"></div>
      <div class="button right"></div>
      <div class="pocket"></div>
    </div>

    <div class="hair h1" aria-hidden="true"></div>
    <div class="hair h2" aria-hidden="true"></div>
    <div class="hair h3" aria-hidden="true"></div>

    <div class="arm left" aria-hidden="true"></div>
    <div class="arm right" aria-hidden="true"></div>

    <div class="leg left" aria-hidden="true"></div>
    <div class="leg right" aria-hidden="true"></div>
  </div>
</div>

Step 2: The Basic CSS & Styling

Start with variables for color and a comfortable page layout. The Minion lives inside a centered container. The body uses a tall rounded shape that reads as a single-piece cartoon torso. From there, we will layer eyes, clothing, and limbs on top with absolute positioning. The variables make quick theme changes painless and keep the code compact.

/* CSS */
:root {
  --skin: #f7e04a;
  --denim: #2f5d8a;
  --denim-dark: #21425f;
  --goggle-silver: #b9bcbc;
  --goggle-edge: #8e8f90;
  --band: #1f1f1f;
  --eye-white: #ffffff;
  --iris: #8a4b20;
  --pupil: #121212;
  --stitch: #93b0ca;
  --shadow: rgba(0,0,0,0.15);
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: #f4f7fb;
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
  color: #0f1720;
}

.minion {
  width: 240px;
  margin: 60px auto;
  position: relative;
}

.minion__body {
  position: relative;
  width: 220px;
  height: 320px;
  margin: 0 auto;
  background: var(--skin);
  border-radius: 110px / 140px;
  box-shadow:
    inset 0 -8px 0 rgba(0,0,0,0.05),
    0 6px 18px var(--shadow);
  overflow: visible;
}

Advanced Tip: Variables let you reskin the character quickly. Swap –denim for green to make overalls teal, or darken –skin for night mode. Keep color mixing at the top so theming stays safe and predictable across components.

Step 3: Building the Body, Overalls, Arms, and Legs

The lower half of the Minion is a denim block with straps, a pocket, and buttons. Arms and legs are slender rectangles with rounded ends and small circles for gloves and shoes. Everything anchors to the body container with absolute positioning. The result reads like a layered sticker.

/* CSS */
.overalls {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 150px;
  background: var(--denim);
  border-radius: 0 0 100px 100px;
  box-shadow: inset 0 8px 0 var(--denim-dark);
}

/* Straps */
.overalls .strap {
  position: absolute;
  width: 22px;
  height: 120px;
  background: var(--denim);
  top: -60px;
  border-radius: 12px;
  box-shadow: inset 0 0 0 3px var(--denim-dark);
}
.overalls .strap.left {
  left: 40px;
  transform: rotate(-25deg);
}
.overalls .strap.right {
  right: 40px;
  transform: rotate(25deg);
}

/* Buttons on straps */
.overalls .button {
  position: absolute;
  width: 18px;
  height: 18px;
  background: var(--goggle-silver);
  border: 2px solid var(--goggle-edge);
  border-radius: 50%;
  top: 45px;
}
.overalls .button.left { left: 52px; }
.overalls .button.right { right: 52px; }

/* Front pocket */
.overalls .pocket {
  position: absolute;
  left: 50%;
  top: 30px;
  width: 70px;
  height: 58px;
  transform: translateX(-50%);
  background: #2b547c;
  border: 3px solid var(--denim-dark);
  border-top-left-radius: 14px;
  border-top-right-radius: 14px;
  border-bottom-left-radius: 30px;
  border-bottom-right-radius: 30px;
  box-shadow: inset 0 -2px 0 var(--stitch);
}

/* Arms */
.arm {
  position: absolute;
  width: 20px;
  height: 110px;
  background: var(--skin);
  border-radius: 12px;
  top: 170px;
  box-shadow: 0 2px 8px var(--shadow);
}
.arm.left { left: -8px; transform: rotate(18deg); }
.arm.right { right: -8px; transform: rotate(-18deg); }

/* Hands (gloves) via pseudo-elements */
.arm::after {
  content: "";
  position: absolute;
  bottom: -6px;
  left: 50%;
  width: 34px;
  height: 34px;
  transform: translateX(-50%);
  background: #121212;
  border-radius: 50%;
}

/* Legs */
.leg {
  position: absolute;
  bottom: -6px;
  width: 18px;
  height: 56px;
  background: var(--denim);
  border-radius: 10px;
}
.leg.left { left: 72px; }
.leg.right { right: 72px; }

/* Shoes */
.leg::after {
  content: "";
  position: absolute;
  bottom: -10px;
  left: 50%;
  width: 46px;
  height: 18px;
  transform: translateX(-50%);
  background: #121212;
  border-radius: 18px / 12px;
  box-shadow: 0 2px 4px var(--shadow);
}

How This Works (Code Breakdown)

The overalls are a simple positioned block clipped with a heavy bottom border-radius to hug the body shape. The inner shadow line at the top edge creates a seam. The two straps are tall rectangles rotated toward the shoulders. A thin inset border gives them structure so they do not blend into the main denim. The pocket is a rounded rectangle that uses different radii for the curved bottom and slightly squared top. That mix gives a stitched look without actual stitches.

Arms and legs are narrow rectangles placed with absolute positioning so they sit partially outside the body. Rounded ends make them feel organic. Gloves and shoes come from pseudo-elements that render as circles and ovals. If you want a refresher on how these basic shapes are built, the article on how to make a rectangle with CSS shows the exact mechanics you are layering here.

The layout sticks to a single stacking context under .minion__body. That way, z-index juggling is minimal. The limbs pick up a soft shadow to keep them legible against the page, and the pocket receives a barely lighter blue to separate it from the torso panel. Every piece remains tweakable without touching the HTML.

Step 4: Building the Goggles, Eyes, Mouth, and Hair

The face sells the character. The band anchors the goggles. Each goggle is a bordered circle with a white inner area, an iris, a pupil, and a tiny shine. The mouth is a curve made from a bottom border on a short element. Hair tufts are tiny triangles that sit on the crown and lean at different angles.

/* CSS */
.goggle-band {
  position: absolute;
  top: 98px;
  left: 0;
  width: 100%;
  height: 22px;
  background: var(--band);
  border-radius: 12px;
  box-shadow: inset 0 -2px 0 rgba(255,255,255,0.06);
}

/* Goggles: circular rims sit above the band */
.goggles {
  position: absolute;
  top: 74px;
  left: 0;
  width: 100%;
  height: 90px;
  pointer-events: none;
}

.eye {
  position: absolute;
  width: 74px;
  height: 74px;
  background: var(--eye-white);
  border: 10px solid var(--goggle-silver);
  border-radius: 50%;
  box-shadow:
    0 0 0 2px var(--goggle-edge),
    0 4px 8px var(--shadow);
}
.eye.left { left: 46px; }
.eye.right { right: 46px; }

/* Eyelid for blinking */
.eye::before {
  content: "";
  position: absolute;
  left: 0; top: 0;
  width: 100%;
  height: 0%;
  background: var(--skin);
  border-radius: 50%;
  transform-origin: top center;
  animation: blink 6s infinite;
}

/* Iris, pupil, and highlight */
.eye .iris {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 34px;
  height: 34px;
  transform: translate(-50%, -50%);
  background: var(--iris);
  border-radius: 50%;
  box-shadow: inset 0 0 0 3px rgba(0,0,0,0.15);
  animation: look 8s infinite;
}
.eye .pupil {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 14px;
  transform: translate(-50%, -50%);
  background: var(--pupil);
  border-radius: 50%;
  animation: look 8s infinite;
}
.eye .shine {
  position: absolute;
  left: 34%;
  top: 34%;
  width: 6px;
  height: 6px;
  background: #ffffff;
  border-radius: 50%;
  filter: drop-shadow(0 0 2px rgba(255,255,255,0.6));
}

/* Mouth: a simple curved smile via bottom border */
.mouth {
  position: absolute;
  top: 172px;
  left: 50%;
  width: 64px;
  height: 26px;
  transform: translateX(-50%);
  border-bottom: 5px solid #242424;
  border-radius: 0 0 64px 64px;
}

/* Hair: small triangles */
.hair {
  position: absolute;
  top: -8px;
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 18px solid #242424;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,0.3));
}
.hair.h1 { left: 48%; transform: translateX(-50%) rotate(-12deg); }
.hair.h2 { left: 54%; transform: translateX(-50%) rotate(6deg); }
.hair.h3 { left: 42%; transform: translateX(-50%) rotate(12deg); }

/* Animation keyframes */
@keyframes blink {
  0%, 88%, 92%, 100% { height: 0%; }
  89%, 91% { height: 100%; }
}

@keyframes look {
  0%   { transform: translate(-50%, -50%) translateX(0); }
  25%  { transform: translate(-50%, -50%) translateX(4px); }
  50%  { transform: translate(-50%, -50%) translateX(-4px); }
  75%  { transform: translate(-50%, -50%) translateX(0); }
  100% { transform: translate(-50%, -50%) translateX(0); }
}

/* Reduce motion preference */
@media (prefers-reduced-motion: reduce) {
  .eye::before, .eye .iris, .eye .pupil {
    animation: none;
  }
}

How This Works (Code Breakdown)

The goggle band is a rounded rectangle that spans the face. The eyes are circles with a thick silver border and a thin outer stroke for a metal edge. The eyelid is an absolutely positioned pseudo-element that shares the eye’s border-radius so it slides cleanly over the white. The blink animation grows height from the top, which mimics a lid closing. The iris and pupil share the same look animation to shift left and right in small arcs.

Hair uses the classic border triangle trick. The element has zero width and height; the visible shape comes from the border on one side while the other two sides are transparent. If you need a quick refresher, check the guide for how to make a triangle up with CSS. You can rotate each tuft for a messy style.

The mouth reads well as a bottom border on a short block with a large border-radius. This avoids path math while still feeling like a smile arc. The entire face benefits from circles: goggles, irises, pupils, and even the glove and shoe ends. If you want to dig deeper into circular building blocks, this primer on how to make a circle with CSS covers key techniques that appear throughout the component. The torso itself is an elongated rounded shape, which is just an ellipse; if that shape is new to you, the article on how to make an ellipse with CSS shows how border-radius values create that proportion.

Advanced Techniques: Animations & Hover Effects

You already have a blink and a subtle glance. You can add a friendly wiggle by easing the goggles and a button pop on hover. Keep it light so it does not distract from the rest of the page. The transitions below add a tiny bounce when the user hovers the component.

/* CSS */
.minion__body,
.eye,
.overalls .button {
  transition: transform 180ms cubic-bezier(.2,.8,.2,1), box-shadow 180ms ease;
}

.minion:hover .goggles,
.minion:hover .eye {
  transform: translateY(1px);
}

.minion:hover .overalls .button {
  transform: scale(1.08);
  box-shadow: 0 2px 6px var(--shadow);
}

/* Optional: a gentle full-body float */
@keyframes floaty {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}
.minion__body.floaty {
  animation: floaty 5s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .minion__body.floaty { animation: none; }
}

Accessibility & Performance

This Minion is decorative by default and does not convey state or data. At the same time, an accessible name can be helpful if it adds whimsy without noise. Keep interactivity light and respect motion preferences for people who prefer a calmer experience.

Accessibility

The container uses role=”img” with an aria-label. If the character is purely decorative for your page context, switch to aria-hidden=”true” on the top-level wrapper so screen readers skip it. The inner elements already carry aria-hidden for safety, so assistive tech will not pick them up as separate parts. For motion, you already gated animations behind prefers-reduced-motion. Avoid tab stops on decorative content by not adding focusable attributes.

Performance

Everything here is a painted shape: backgrounds, borders, and shadows. That keeps it fast. Animations touch transform and height on a small number of nodes, which remains smooth on modern devices. Heavy box-shadow animation or filter animation can cost frames, so keep those static. The component downloads zero images or fonts and ships no extra files beyond your CSS, which reduces bytes and avoids layout shifts.

Keep Your CSS Pencil Sharp

You built a complete Minion with nothing but CSS: an elliptical body, denim overalls, a metal goggle rim, animated eyes, and a friendly smile. You now have a reliable recipe for layering rectangles, circles, and triangles into expressive characters. Use the same approach to craft a full cast of CSS mascots, or mine these techniques for icons and avatars across your UI.

Leave a Comment