Drawing a Turkey with CSS
Tags: css, webdev, beginners, tutorial
Published: 2022-11-23 18:48:15

If you were tasked with creating a piece of art, what tools would you use? Photoshop? Canva? Figma?

What if I told you there was another way that’s more unwieldy and requires a lot more guess work? If that excited you, using pure CSS to create images is for you!

In all seriousness, while you probably don’t want to create full images in CSS, understanding how you **could** and what CSS primitives are available can help improve your designs and overall CSS knowledge. Since Thanksgiving is coming up, let’s make this turkey:

![CSS Turkey](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3x4tygwnmc9qdky28y7q.png)

# Where do we even start?

Let’s start off very simple and just create a circle. I’m going to use a React application, but the same things apply to raw HTML, Vue, whatever you prefer.

First, we’ll create a container that will hold all the components of the image. Then, we’ll create a circle that’s directly in the center of the container, and we’ll break down how it works afterwards.

```css
.container {
    position: relative;
    /* Add margin to the top so it isn't right against the top of the screen */
    margin: 32px auto auto;
    width: 500px;
    height: 500px;
    /* Add a border for debugging, remove later on */
    border: 1px solid green;
}

.circle {
    position: absolute;
    top: 25%;
    left: 25%;
    width: 50%;
    height: 50%;
    background: #5A391E;
    border-radius: 50%;
}
```

```jsx
function Turkey() {
    return <div className="container">
        <div className="circle" />
    </div>
}
```

![CSS Circle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hl4qb5l8chuf4d3d0evd.png)

So let’s break down the `circle` class. It starts with `position: absolute` which allows us to place it in an exact position within our container. 

We’ll use **percentages** for our height, width, left, and top so that everything will fit and resizing our container won’t mess up the image.

```css
top: 25%;
left: 25%;
width: 50%;
height: 50%;
```

The width and height of the image are 50% of the total container size. To center the circle, we place the leftmost side at 25% and the topmost side at 25%. How did we get 25%? Well, there are three variables to consider: 

- The space on the left of the shape (as specified by **left**)
- The shape itself (as specified **width**)
- The space on the right of the shape (implicitly defined by whatever is left over, or 100% - **width** - **left**)

A shape is centered if the space on the left and right are equal. Since 50% is taken up by the shape itself, we split the remaining 50% equally on left and right (50 / 2 = 25).

And finally, the border-radius is used to turn the square into a circle by smoothing all the corners.

Let’s move it up a bit and make it a bit smaller:

```css
.head {
    position: absolute;
    top: 16.5%;
    left: 35%;
    width: 30%;
    height: 30%;
    background: #5A391E;
    border-radius: 50%;
    z-index: 0;
}
```

![CSS Turkey head](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t81xy9utqm6ue21nqxo8.png)

And now we have the head of our turkey!

# Adding and positioning the eyes

Our turkey component so far looks like this:

```jsx
function Turkey() {
    return <div className="container">
        <div className="head" />
    </div>
}
```

When we add eyes, we’ll want to add them as children of the `head` div:

```jsx
function Turkey() {
    return <div className="container">
        <div className="head">
            <div className="eye eye-left" />
            <div className="eye eye-right" />
        </div>
    </div>
}
```

The positioning of the eyes are now going to be relative to the “head” container. Let’s look at the CSS for the eyes:

```css
.eye {
    position: absolute;
    /* The eye shouldn't be a perfect circle, 
       so we make the height slightly larger */
    width: 25%;
    height: 31%;
    /* The eyes are aligned vertically.
       Note that this position is relative to the head div not the container div */
    top: 25%;
    background: white;
    border-radius: 50%;
    /* It needs to be displayed in front of the head, so add a larger z-index */
    z-index: 10;
}

.eye-left {
    left: 25%;
}

.eye-right {
    right: 25%;
}
```

The only difference between the eyes is that one is on the left and one is on the right, so they share the `eye` class, and then their positioning on the head is changed via the `eye-left` and `eye-right` classes.

![CSS turkey with eyes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/adwgb1701mqkml83ygcd.png)

Without pupils, it’s a little spooky... let’s quickly add those.

```css
.pupil {
    position: absolute;
    width: 35%;
    height: 31%;
    top: 25%;
    left: 35%;
    background: black;
    border-radius: 50%;
}
```

```jsx
function Turkey() {
    return <div className="container">
        <div className="head">
            <div className="eye eye-left">
                <div className="pupil"/>
            </div>
            <div className="eye eye-right">
                <div className="pupil"/>
            </div>
        </div>
    </div>
}
```

![CSS Turkey with eyes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fs5m3sxpeikfcbluxlvt.png)

# How do we create custom shapes?

So far, our turkey is made up of exclusively circles and ovals. To add a beak, we’re going to need a more custom shape. Luckily, we can use the css property [clip-path](https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path), which allows us to specify custom polygons.

Here’s a quick example of what we can do with clip-path:

```css
.pentagon {
    position: absolute;
    top: 25%;
    left: 25%;
    width: 50%;
    height: 50%;
    background: #5A391E;
    /* These make up the vertices of a pentagon */
    clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%);
}
```

![css pentagon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eis0ye6qpmd9181kwyj6.png)

And this is actually the only other piece of the puzzle we need to complete the turkey. We can make arbitrary shapes, scale them to whatever size we want, and position them anywhere in our container. 

It’s painful and tedious to manually create your own clip-path, but luckily tools like [this](https://bennettfeely.com/clippy/) exist to make it significantly easier.

# Adding the beak

We’re going to use [Bennett Feely’s clip-path maker](https://bennettfeely.com/clippy/) and draw our best beak. After that, the rest is exactly the same as all our other elements - we just need to position and size them.

```css
.beak {
    position: absolute;
    top: 40%;
    left: 35%;
    width: 30%;
    height: 30%;
    background: #FEE05F;
    /* Hand drawn with <3 */
    clip-path: polygon(7% 36%, 40% 90%, 44% 95%, 47% 96%, 50% 96%, 55% 95%, 60% 90%, 94% 36%, 95% 30%, 95% 26%, 90% 23%, 86% 20%, 77% 18%, 10% 20%, 6% 22%, 4% 26%, 4% 31%);
}
```

![turkey with beak](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpnauhhsodkqgnk6dnc6.png)

Not bad for something hand-drawn!

# Adding the body and feathers

The body should be very easy to add because it’s just another oval.

```css
.body {
    position: absolute;
    top: 35%;
    left: 25%;
    width: 50%;
    height: 55%;
    background: #5A391E;
    border-radius: 50%;
    z-index: 5;
}
```

![css turkey with beak and feathers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47xbx5laz5dc5vp0vdg2.png)

We’re almost there, we just need some feathers. To achieve this, we’re going to actually just create one single feather and every other feather will just be a rotated and recolored version of that first one. 

Let’s remove everything for a second and just create a single feather-like oval. Here’s our oval in the clip-path generator:

![Oval](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddwv8rxg3861cnf4ia9c.png)

Here’s the CSS for two feathers, one of which is rotated 90 degrees from the other:

```css
.base-feather {
    position: absolute;
    top: 25%;
    left: 25%;
    width: 50%;
    height: 50%;
    clip-path: ellipse(25% 40% at 50% 50%);
    background: red;
}

.rotated-feather {
    position: absolute;
    top: 25%;
    left: 25%;
    width: 50%;
    height: 50%;
    clip-path: ellipse(25% 40% at 50% 50%);
    background: orange;
    rotate: 90deg;
}
```

![rotated ovals](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3cul1v80ytme13b5oe2.png)

One thing to note is the rotation happens from the **center** of the shape. If we want our feathers to fan out like this

![feathers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/104dbg3qpz3h0n511fx2.png)

we instead would want the rotation to be centered around the end of the oval.

There’s actually a pretty simple fix here - what if the center of our “shape” was the end of the oval?

![Shifted oval](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0yw840i812iojftao98n.png)

By making a custom oval shape that ends in the center, when we rotate our shape, it’ll be around the base of the feather.

And we are now ready to put it all together! Here’s the css for our feather:

```css
.feather {
    position: absolute;
    top: 10%;
    left: 15%;
    width: 70%;
    height: 100%;
    clip-path: ellipse(15% 25% at 50% 25%);
    z-index: -1;
}
```

Along with some code to cycle through different colors and rotations:

```jsx
const FEATHER_COLORS = ['#D63420', '#E26831', '#FCAF2C']

function Turkey() {
    const feathersRotationDegrees = [-90, -75, -60, -45, -30, -15, 0, 15, 30, 45, 60, 75, 90]

    return <div className="container">
        <div className="head">
            <div className="eye eye-left">
                <div className="pupil"/>
            </div>
            <div className="eye eye-right">
                <div className="pupil"/>
            </div>
            <div className="beak"/>
        </div>
        <div className="body"/>
        {feathersRotationDegrees.map((degree, i) => (
            <div key={degree} className="feather" style={{
                rotate: `${degree}deg`,
                background: `${FEATHER_COLORS[i % FEATHER_COLORS.length]}`
            }}/>
        ))}
    </div>
}
```

Which produces our final image:

![css turkey](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igjfgtho448612lkdmvq.png)

# Customizing the number of feathers

In our above code snippet, we hard coded the degree offsets for each of the feathers. We can actually generate those offsets programmatically:

```jsx
function getRotationDegreesForFeathers(numFeathers) {
    const numFeathersOnEachSide = Math.floor(numFeathers / 2)
    const feathersRotationDegrees = [0]
    // Return early to avoid dividing by 0
    if (numFeathersOnEachSide === 0) {
        return feathersRotationDegrees
    }

    const step = 90 / numFeathersOnEachSide
    for (let degree = step; degree <= 90; degree += step) {
        feathersRotationDegrees.push(-degree)
        feathersRotationDegrees.push(degree)
    }

    // Since we use the ordering to determine each feather's color
    //    we need to sort.
    return feathersRotationDegrees.sort((a, b) => a - b)
}
```

And then you can easily generate turkeys with 21 feathers:

![turkey with 21 feathers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y6odqetv4bwhenukrdrc.png)

Or the sad looking 5 feathered turkey:

![turkey with 5 feathers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cs7an50msadvdxesieh.png)

Or the very overwhelming 201 feathered turkey:

![turkey with 201 feathers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cui8v2rj808odo9m5yw8.png)

# Summary

In this post, we learned how to generate arbitrary shapes with CSS. We then learned how to scale, rotate, and position those shapes in order to create your own artwork. 

While you probably shouldn’t create giant works of art in pure CSS, you can use these techniques to add a little more pizzazz to your designs.