Build an Interactive Lily Garden

Create an animated garden with butterflies and cats using HTML, CSS, and JS.

Introduction

30 Second Summary

The best websites feel alive. Flowers sway, creatures move, and things respond to your touch.

In this project, you will build an interactive lily garden webpage with animated flowers, fluttering butterflies that follow your mouse, and cats that react when you click them. You will deploy it as a live website anyone can visit.

What You'll Build

An animated garden scene built with pure HTML, CSS, and JavaScript that features swaying lilies, drifting butterflies with 3D wing-flapping, and clickable cats with speech bubbles.

By the end of this project, you'll have:

  • A live animated garden website deployed on GitHub Pages that anyone can visit via a public URL.
  • Hands-on experience creating CSS keyframe animations including swaying flowers and butterflies with 3D flapping wings.
  • Working knowledge of JavaScript DOM manipulation and event listeners for mouse-following butterflies and clickable cat interactions.
  • Secret Mission: Add a day/night toggle that transforms the scene into a moonlit garden with glowing fireflies and cats with glowing eyes.

Are there any prerequisites?

You need a GitHub account. No other tools or installations are required because you will work entirely in GitHub Codespaces, a browser-based coding environment.

Set Up Your Project in GitHub Codespaces

Every garden starts with a patch of ground. Before you can grow lilies, animate butterflies, or make cats react to clicks, you need a place for your code to live.

In this step, you will create a GitHub repository to store your project files and open a GitHub Codespaces environment so you can start building right in your browser.

In this step, get ready to:
  • Create a public GitHub repository for your lily garden.
  • Create the three project files you need.
  • Set up HTML boilerplate and open a live preview.
Create a new repository and open a Codespace

A repository is like a folder on GitHub that stores all your project files and tracks their history. You will create one and then launch a cloud-based coding environment directly from it.

  • Open your browser and go to github.com. Sign in to your account.
  • In the upper-right corner of any page, click the + menu, then click New repository.
  • In the Repository name field, type lily-garden.
  • Under visibility, select Public.
  • Toggle Add a README file to on.
  • Click Create repository.

GitHub takes you to your new repository page. Now you will open a Codespace so you can write code without installing anything on your computer.

  • On your repository page, click the green Code button.
  • Click the Codespaces tab.
  • Click Create codespace on main.

What is a Codespace?

A Codespace is a full development environment that runs in the cloud. It gives you a VS Code editor, a terminal, and everything you need to code, all inside your browser. No local setup required.

After a moment, a browser-based editor opens with your repository files. You should see a README.md file in the Explorer panel on the left.

Codespace not opening?

Make sure you are signed in to your GitHub account. If the Codespaces tab does not appear, check that your repository is public and try refreshing the page.

help me troubleshoot why my Codespace is not opening

Create your project files

Your lily garden needs three files: one for the page structure, one for the visual styling, and one for interactivity. You will create all three from the Codespace terminal.

  • Open the terminal in your Codespace by pressing Ctrl+` (backtick key, usually above Tab). If the terminal is already visible at the bottom, you are all set.
  • Create the three files by running this command:
touch index.html style.css script.js
  • Confirm the files exist by running:
ls

You should see index.html, style.css, and script.js listed alongside README.md in the output.

Files not showing up?

Make sure you pressed Enter after the touch command. Check the Explorer panel on the left side of the editor for the new files. If they do not appear, click the refresh icon at the top of the Explorer.

help me figure out why my files did not get created in my Codespace

Set up HTML boilerplate and open a live preview

Every web page starts with an HTML skeleton that tells the browser what to load. You will add the boilerplate that links your CSS and JavaScript files, then open a preview panel so you can see changes as you build.

  • In the Explorer panel on the left, click index.html to open it in the editor.
  • Paste the following HTML boilerplate into the file:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lily Garden</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <script src="script.js"></script>
</body>
</html>

What does this code do?

The <!DOCTYPE html> declaration tells the browser this is a modern HTML5 page. The <meta> tags set the character encoding and make the page responsive on mobile devices.

The <link> tag connects your style.css file so the browser loads your styles. The <script> tag connects script.js so the browser loads your JavaScript.

  • Save the file by pressing Ctrl+S (Windows/Linux) or Cmd+S (macOS).

Now open a preview so you can see your page as you build it.

  • In the Codespace, click the Extensions icon in the Activity Bar on the left (it looks like four small squares).
  • In the search box at the top, type Live Preview.
  • Find the extension by Microsoft (ms-vscode.live-server) and click Install.
  • Once installed, go back to index.html in the editor.
  • Click the preview icon (a small page-with-magnifying-glass icon) in the top-right corner of the editor.

A preview panel opens inside the editor. You should see a blank white page. That is correct. You have not added any visible content yet.

Preview not refreshing later?

The Live Preview extension automatically refreshes when you save a file. If it ever gets stuck, close the preview tab and click the preview icon again.

✔️ Awesome, I've got everything!

Great. Double check you have saved index.html and that you see the blank preview panel open.

ⓧ I'd like to double check the full code

Here is the complete index.html file at this point:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lily Garden</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <script src="script.js"></script>
</body>
</html>

Your style.css and script.js files should both be empty at this point.

Your environment is ready. Next up, you will paint the garden scene with a sky gradient, green ground, and beautiful swaying lily flowers.

Build the Garden Background and Lily Flowers

In the previous step, you set up your project files and got a blank page showing in the Live Preview. Now it's time to bring that blank canvas to life.

Every garden scene needs a sky, some ground, and of course, flowers. In this step, you'll build the HTML structure for the full garden and style it with CSS gradients, positioned elements, and keyframe animations to make the lilies gently sway.

In this step, get ready to:
  • Create the garden scene HTML with sky, ground, and lily flower elements.
  • Style the garden with CSS gradients and position the lily flowers.
  • Add a swaying animation to make the lilies move in the breeze.
Create the garden scene in HTML

The garden is made up of layers. A sky sits behind everything, the ground covers the bottom, and lily flowers grow up from the ground. Each lily is built from nested divs representing stems, leaves, and petals.

  • In index.html, find the opening <body> tag.
  • Replace everything between <body> and </body> with the following garden structure:
    <div class="garden">
        <div class="sky"></div>
        <div class="ground"></div>

        <!-- First lily flower -->
        <div class="lily lily-1">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <!-- Second lily flower -->
        <div class="lily lily-2">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <!-- Third lily flower -->
        <div class="lily lily-3">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>
    </div>

    <script src="script.js"></script>

What does this HTML do?

The .garden container holds the entire scene. Inside it, .sky and .ground are the background layers.

Each .lily has a stem, two leaves (left and right), and a flower head made of five petals arranged in a circle around a center dot. The second class on each lily (lily-1, lily-2, lily-3) lets you position and animate each one independently.

  • Save index.html.
  • Check your Live Preview. You won't see much yet because the divs have no styling. The page may look blank or show only the default white background.

Seeing an error or broken layout?

Make sure every opening <div> tag has a matching closing </div>. Missing a single closing tag will break the nesting.

Confirm the <script src="script.js"></script> tag is still inside the body, after the closing </div> of .garden.

Still stuck? Help me fix my garden HTML structure.

Style the garden background and lily flowers

CSS transforms those empty divs into a visible scene. You'll use linear gradients for the sky and ground, absolute positioning to place elements in layers, and border-radius with transforms to shape the petals.

  • Open style.css in the Explorer sidebar.
  • Add the base layout styles by pasting this code:
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
    height: 100vh;
    width: 100vw;
}

.garden {
    position: relative;
    width: 100%;
    height: 100vh;
    overflow: hidden;
}

.sky {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 70%;
    background: linear-gradient(to bottom, #87CEEB, #B0E0E6, #98FB98);
}

.ground {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 30%;
    background: linear-gradient(to bottom, #228B22, #2E8B57);
    border-radius: 50% 50% 0 0 / 10% 10% 0 0;
}

What does this code do?

  • The * reset removes default browser spacing so the garden fills the full viewport.
  • The .sky uses a three-colour gradient that blends from light blue at the top down to a soft green at the horizon.
  • The .ground uses a green gradient with a curved top edge (via border-radius) to look like gently rolling hills.
  • Save style.css and check your Live Preview.
  • You should see a blue-to-green sky filling the top 70% of the page and a darker green ground at the bottom.

Don't see any colours?

Confirm your <link rel="stylesheet" href="style.css"> tag is in the <head> of index.html. If the file name doesn't match exactly (including lowercase), the browser can't find your styles.

Need a hand? Help me debug why my CSS isn't loading.

  • Below the .ground rule, add the lily plant structure styles:
/* Lily Flowers */
.lily {
    position: absolute;
    bottom: 25%;
    transform-origin: bottom center;
    animation: sway 3s ease-in-out infinite;
}

.lily-1 {
    left: 15%;
}

.lily-2 {
    left: 50%;
}

.lily-3 {
    left: 75%;
}

.stem {
    width: 4px;
    height: 120px;
    background-color: #2E8B57;
    margin: 0 auto;
    border-radius: 2px;
}

.leaf {
    position: absolute;
    width: 30px;
    height: 12px;
    background-color: #3CB371;
    border-radius: 50%;
    top: 80px;
}

.leaf-left {
    left: -12px;
    transform: rotate(-30deg);
}

.leaf-right {
    right: -12px;
    transform: rotate(30deg);
}

What's happening here?

  • Each .lily is absolutely positioned at 25% from the bottom, so the flowers appear to grow from the ground.
  • The transform-origin: bottom center ensures that when the lily sways, it pivots from its base like a real plant.
  • The three lily classes (.lily-1, .lily-2, .lily-3) space the flowers across the garden at 15%, 50%, and 75% from the left.
  • The leaves use rotate() transforms to angle away from the stem naturally.
  • Save style.css and check the preview.
  • You should see three green stems with small leaves sprouting from the ground area. The flowers won't have petals yet.

Stems not visible?

The stems are only 4px wide, so look closely. If you don't see them at all, make sure the .garden class in your HTML matches exactly (lowercase, no typos).

Also check that the /* Lily Flowers */ section is BELOW the .ground rule, not nested inside it.

  • Now add the flower and petal styles below your leaf rules:
.flower {
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
    width: 50px;
    height: 50px;
}

.petal {
    position: absolute;
    width: 18px;
    height: 35px;
    background-color: #FF69B4;
    border-radius: 50% 50% 50% 50% / 70% 70% 30% 30%;
    top: 50%;
    left: 50%;
    transform-origin: bottom center;
}

.petal-1 {
    transform: translate(-50%, -100%) rotate(0deg);
}

.petal-2 {
    transform: translate(-50%, -100%) rotate(72deg);
}

.petal-3 {
    transform: translate(-50%, -100%) rotate(144deg);
}

.petal-4 {
    transform: translate(-50%, -100%) rotate(216deg);
}

.petal-5 {
    transform: translate(-50%, -100%) rotate(288deg);
}

.center {
    position: absolute;
    width: 14px;
    height: 14px;
    background-color: #FFD700;
    border-radius: 50%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

How do the petals form a flower?

Each petal is an elongated oval shape created by the asymmetric border-radius values (wider at the top, narrower at the bottom).

All five petals start at the same center point and each one is rotated by 72 degrees (360 / 5 = 72). This fans them out evenly into a circle. The golden .center dot sits in the middle to complete the flower.

  • Save style.css and refresh your preview.
  • You should now see three pink lily flowers with golden centres sitting on top of the green stems. The flowers are positioned across the garden but not yet swaying.

Petals look like a blob instead of a flower?

Check that each .petal-N class has the translate(-50%, -100%) BEFORE the rotate() value. The translate pulls each petal outward from the centre so they fan out.

Also verify the rotation values increase by 72 each time: 0, 72, 144, 216, 288.

Need help? Help me figure out why my lily petals aren't forming a flower shape.

Add the sway animation with staggered timing

The animation: sway property is already on your .lily class, but nothing moves yet because the browser doesn't know what sway means. You need a @keyframes rule to define the motion, plus staggered delays so each lily sways at a different time.

  • In style.css, find the .lily-1 rule.
  • Find this code:
.lily-1 {
    left: 15%;
}

.lily-2 {
    left: 50%;
}

.lily-3 {
    left: 75%;
}
  • Add an animation-delay property to each lily class so the rules look like this:
.lily-1 {
    left: 15%;
    animation-delay: 0s;
}

.lily-2 {
    left: 50%;
    animation-delay: 0.5s;
}

.lily-3 {
    left: 75%;
    animation-delay: 1s;
}
  • Now add the @keyframes sway block between your .lily-3 rule and your .stem rule:
@keyframes sway {
    0%, 100% {
        transform: rotate(-2deg);
    }
    50% {
        transform: rotate(2deg);
    }
}

What does this animation do?

The @keyframes sway rule defines a rotation that goes from -2 degrees to +2 degrees and back. This small range creates a gentle rocking motion like wind blowing a real flower.

The staggered animation-delay values (0s, 0.5s, 1s) make each lily start its sway at a slightly different time. This prevents all three flowers from moving in sync, which looks more natural.

  • Save style.css and check your Live Preview.
  • All three lilies should now be gently swaying back and forth. Each one starts at a slightly different time, creating a natural breeze effect across the garden.

Flowers not swaying?

Make sure the name in your @keyframes sway matches the name in animation: sway 3s ease-in-out infinite on the .lily class. They must be identical.

Also check that the @keyframes block is NOT inside another CSS rule. It must be at the top level of the file, between the closing } of .lily-3 and the .stem rule.

Still not working? Help me debug my CSS keyframes sway animation.

✔️ Awesome, I've got everything!

Great. Double check you've saved both index.html and style.css before moving on.

ⓧ I'd like to double check the full code

Here is the full index.html at this point:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lily Garden</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="garden">
        <div class="sky"></div>
        <div class="ground"></div>

        <div class="lily lily-1">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="lily lily-2">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="lily lily-3">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

And here is the full style.css:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
    height: 100vh;
    width: 100vw;
}

.garden {
    position: relative;
    width: 100%;
    height: 100vh;
    overflow: hidden;
}

.sky {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 70%;
    background: linear-gradient(to bottom, #87CEEB, #B0E0E6, #98FB98);
}

.ground {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 30%;
    background: linear-gradient(to bottom, #228B22, #2E8B57);
    border-radius: 50% 50% 0 0 / 10% 10% 0 0;
}

/* Lily Flowers */
.lily {
    position: absolute;
    bottom: 25%;
    transform-origin: bottom center;
    animation: sway 3s ease-in-out infinite;
}

.lily-1 {
    left: 15%;
    animation-delay: 0s;
}

.lily-2 {
    left: 50%;
    animation-delay: 0.5s;
}

.lily-3 {
    left: 75%;
    animation-delay: 1s;
}

@keyframes sway {
    0%, 100% {
        transform: rotate(-2deg);
    }
    50% {
        transform: rotate(2deg);
    }
}

.stem {
    width: 4px;
    height: 120px;
    background-color: #2E8B57;
    margin: 0 auto;
    border-radius: 2px;
}

.leaf {
    position: absolute;
    width: 30px;
    height: 12px;
    background-color: #3CB371;
    border-radius: 50%;
    top: 80px;
}

.leaf-left {
    left: -12px;
    transform: rotate(-30deg);
}

.leaf-right {
    right: -12px;
    transform: rotate(30deg);
}

.flower {
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
    width: 50px;
    height: 50px;
}

.petal {
    position: absolute;
    width: 18px;
    height: 35px;
    background-color: #FF69B4;
    border-radius: 50% 50% 50% 50% / 70% 70% 30% 30%;
    top: 50%;
    left: 50%;
    transform-origin: bottom center;
}

.petal-1 {
    transform: translate(-50%, -100%) rotate(0deg);
}

.petal-2 {
    transform: translate(-50%, -100%) rotate(72deg);
}

.petal-3 {
    transform: translate(-50%, -100%) rotate(144deg);
}

.petal-4 {
    transform: translate(-50%, -100%) rotate(216deg);
}

.petal-5 {
    transform: translate(-50%, -100%) rotate(288deg);
}

.center {
    position: absolute;
    width: 14px;
    height: 14px;
    background-color: #FFD700;
    border-radius: 50%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

Your garden is looking beautiful. Next up, you'll add animated butterflies that flutter their wings and drift across the screen.

Add Animated Butterflies with Flapping Wings

Your garden has swaying lilies, but a real garden attracts visitors. Butterflies are the perfect addition to bring movement and life to the scene.

In this step, you'll create butterflies with realistic 3D wing-flapping using CSS rotateY transforms. You'll also animate them drifting across the screen on looping paths so each butterfly moves independently.

In this step, get ready to:
  • Add butterfly HTML elements to the garden.
  • Create a flapping wing animation with CSS 3D transforms.
  • Make butterflies drift across the screen on looping paths.
Add butterfly elements to the HTML

Each butterfly is built from three divs: a left wing, a right wing, and a thin body in the middle. You'll add three butterflies total, each with its own class for individual styling later.

  • In index.html, find the closing </div> tag of the last lily (lily-3). Add the following butterfly elements directly below it, before the closing </div> of the .garden container:
        <div class="butterfly butterfly-1">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="butterfly butterfly-2">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="butterfly butterfly-3" id="follower-butterfly">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

What does this code do?

Each butterfly uses the same three-part structure: two .wing divs (left and right) and a .butterfly-body div. CSS will shape these into wing and body forms.

The third butterfly has an id="follower-butterfly" because you'll use JavaScript to make it follow the mouse in a later step.

  • Save index.html.
  • Check your Live Preview. You won't see the butterflies yet because they have no size or colour until you add CSS in the next substep.

Seeing an error or broken layout?

Make sure the butterfly divs are placed inside the .garden container, not outside it. The closing </div> for .garden should come after all three butterflies.

Check that every opening <div> has a matching </div>. A missing closing tag can break the entire page layout.

Help me fix my butterfly HTML structure.

Style the wings and create the flap animation

To simulate a 3D flapping effect, you'll use rotateY to rotate each wing around its vertical axis. This makes the wings appear to fold inward and outward, just like a real butterfly.

  • In style.css, add the following butterfly styles at the bottom of the file, after the .center rule:
/* Butterflies */
.butterfly {
    position: absolute;
    width: 40px;
    height: 20px;
    z-index: 10;
}

.butterfly-1 {
    top: 20%;
    left: 30%;
    animation: drift1 8s ease-in-out infinite;
}

.butterfly-2 {
    top: 35%;
    left: 60%;
    animation: drift2 10s ease-in-out infinite;
}

.butterfly-3 {
    top: 25%;
    left: 45%;
    transition: transform 0.3s ease-out;
}

.butterfly-body {
    position: absolute;
    width: 4px;
    height: 20px;
    background-color: #333;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    border-radius: 2px;
}

.wing {
    position: absolute;
    width: 18px;
    height: 14px;
    border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
    top: 2px;
    animation: flap 0.4s ease-in-out infinite alternate;
}

.wing-left {
    left: 0;
    background-color: #FF6347;
    transform-origin: right center;
}

.wing-right {
    right: 0;
    background-color: #FF6347;
    transform-origin: left center;
}

@keyframes flap {
    0% {
        transform: rotateY(0deg);
    }
    100% {
        transform: rotateY(60deg);
    }
}

.butterfly-2 .wing-left,
.butterfly-2 .wing-right {
    background-color: #9370DB;
}

.butterfly-3 .wing-left,
.butterfly-3 .wing-right {
    background-color: #FF8C00;
}

What does this code do?

  • .butterfly uses position: absolute so each butterfly can be placed anywhere in the garden using top and left percentages.
  • The @keyframes flap animation rotates each wing from 0deg to 60deg around the Y axis. This creates the 3D illusion of wings opening and closing.
  • transform-origin: right center on the left wing (and left center on the right) makes each wing rotate from where it connects to the body, not from its own center.
  • Each butterfly gets a different wing colour: red (#FF6347), purple (#9370DB), and orange (#FF8C00).

What is rotateY?

CSS transforms let you move, rotate, and scale elements in 2D and 3D space. rotateY specifically rotates an element around its vertical axis, like a door swinging open.

Combined with the alternate direction on the animation, the wing rotates forward and then back, over and over. The result looks like a butterfly flapping.

  • Save style.css and check your Live Preview.
  • You should see three colourful butterflies positioned in the garden, each rapidly flapping their wings. They won't drift across the screen yet.

Don't see any butterflies?

Make sure you saved both index.html and style.css. The butterflies need both the HTML structure and the CSS styles to appear.

Check that the /* Butterflies */ section is placed after the .center rule, not inside another selector's curly braces.

Why aren't my CSS butterflies showing up?

Add drift animations for movement across the screen

Flapping wings look great, but butterflies should also float around the garden. CSS @keyframes animations can move elements along a multi-point path using translateX and translateY.

  • In style.css, find the .butterfly-3 rule you just added. Add the following drift keyframes directly below it, before the .butterfly-body rule:
@keyframes drift1 {
    0%, 100% {
        transform: translate(0, 0);
    }
    25% {
        transform: translate(80px, -30px);
    }
    50% {
        transform: translate(150px, 10px);
    }
    75% {
        transform: translate(60px, 30px);
    }
}

@keyframes drift2 {
    0%, 100% {
        transform: translate(0, 0);
    }
    25% {
        transform: translate(-60px, 20px);
    }
    50% {
        transform: translate(-120px, -20px);
    }
    75% {
        transform: translate(-40px, -40px);
    }
}

What does this code do?

  • Each @keyframes rule defines a looping path with four waypoints (0%, 25%, 50%, 75%). The butterfly smoothly travels between each point.
  • drift1 moves the first butterfly to the right and slightly up and down. drift2 moves the second butterfly to the left with different offsets.
  • The third butterfly (.butterfly-3) has no drift animation. It uses a transition instead, because JavaScript will move it to follow the mouse in the next step.
  • Different durations (8s vs 10s) mean the butterflies travel at different speeds, making the motion feel natural rather than synchronized.
  • Save style.css and check your Live Preview.
  • You should see two butterflies floating across the garden on different looping paths while all three butterflies continuously flap their wings.

Butterflies flap but don't drift?

Check that the keyframe names match exactly. The .butterfly-1 rule references animation: drift1, so the keyframes must be named @keyframes drift1 (case-sensitive).

Make sure the @keyframes blocks are not accidentally inside another rule's curly braces. They must be at the top level of your CSS.

Help me debug my butterfly drift animations.

✔️ Awesome, I've got everything!

Great. Double check you've saved both files before moving on.

ⓧ I'd like to double check the full code

Here's the full index.html at this point:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lily Garden</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="garden">
        <div class="sky"></div>
        <div class="ground"></div>

        <div class="lily lily-1">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="lily lily-2">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="lily lily-3">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="butterfly butterfly-1">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="butterfly butterfly-2">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="butterfly butterfly-3" id="follower-butterfly">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

And here's the full style.css:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
    height: 100vh;
    width: 100vw;
}

.garden {
    position: relative;
    width: 100%;
    height: 100vh;
    overflow: hidden;
}

.sky {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 70%;
    background: linear-gradient(to bottom, #87CEEB, #B0E0E6, #98FB98);
}

.ground {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 30%;
    background: linear-gradient(to bottom, #228B22, #2E8B57);
    border-radius: 50% 50% 0 0 / 10% 10% 0 0;
}

/* Lily Flowers */
.lily {
    position: absolute;
    bottom: 25%;
    transform-origin: bottom center;
    animation: sway 3s ease-in-out infinite;
}

.lily-1 {
    left: 15%;
    animation-delay: 0s;
}

.lily-2 {
    left: 50%;
    animation-delay: 0.5s;
}

.lily-3 {
    left: 75%;
    animation-delay: 1s;
}

@keyframes sway {
    0%, 100% {
        transform: rotate(-2deg);
    }
    50% {
        transform: rotate(2deg);
    }
}

.stem {
    width: 4px;
    height: 120px;
    background-color: #2E8B57;
    margin: 0 auto;
    border-radius: 2px;
}

.leaf {
    position: absolute;
    width: 30px;
    height: 12px;
    background-color: #3CB371;
    border-radius: 50%;
    top: 80px;
}

.leaf-left {
    left: -12px;
    transform: rotate(-30deg);
}

.leaf-right {
    right: -12px;
    transform: rotate(30deg);
}

.flower {
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
    width: 50px;
    height: 50px;
}

.petal {
    position: absolute;
    width: 18px;
    height: 35px;
    background-color: #FF69B4;
    border-radius: 50% 50% 50% 50% / 70% 70% 30% 30%;
    top: 50%;
    left: 50%;
    transform-origin: bottom center;
}

.petal-1 {
    transform: translate(-50%, -100%) rotate(0deg);
}

.petal-2 {
    transform: translate(-50%, -100%) rotate(72deg);
}

.petal-3 {
    transform: translate(-50%, -100%) rotate(144deg);
}

.petal-4 {
    transform: translate(-50%, -100%) rotate(216deg);
}

.petal-5 {
    transform: translate(-50%, -100%) rotate(288deg);
}

.center {
    position: absolute;
    width: 14px;
    height: 14px;
    background-color: #FFD700;
    border-radius: 50%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* Butterflies */
.butterfly {
    position: absolute;
    width: 40px;
    height: 20px;
    z-index: 10;
}

.butterfly-1 {
    top: 20%;
    left: 30%;
    animation: drift1 8s ease-in-out infinite;
}

.butterfly-2 {
    top: 35%;
    left: 60%;
    animation: drift2 10s ease-in-out infinite;
}

.butterfly-3 {
    top: 25%;
    left: 45%;
    transition: transform 0.3s ease-out;
}

@keyframes drift1 {
    0%, 100% {
        transform: translate(0, 0);
    }
    25% {
        transform: translate(80px, -30px);
    }
    50% {
        transform: translate(150px, 10px);
    }
    75% {
        transform: translate(60px, 30px);
    }
}

@keyframes drift2 {
    0%, 100% {
        transform: translate(0, 0);
    }
    25% {
        transform: translate(-60px, 20px);
    }
    50% {
        transform: translate(-120px, -20px);
    }
    75% {
        transform: translate(-40px, -40px);
    }
}

.butterfly-body {
    position: absolute;
    width: 4px;
    height: 20px;
    background-color: #333;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    border-radius: 2px;
}

.wing {
    position: absolute;
    width: 18px;
    height: 14px;
    border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
    top: 2px;
    animation: flap 0.4s ease-in-out infinite alternate;
}

.wing-left {
    left: 0;
    background-color: #FF6347;
    transform-origin: right center;
}

.wing-right {
    right: 0;
    background-color: #FF6347;
    transform-origin: left center;
}

@keyframes flap {
    0% {
        transform: rotateY(0deg);
    }
    100% {
        transform: rotateY(60deg);
    }
}

.butterfly-2 .wing-left,
.butterfly-2 .wing-right {
    background-color: #9370DB;
}

.butterfly-3 .wing-left,
.butterfly-3 .wing-right {
    background-color: #FF8C00;
}

Your garden is buzzing with life. Next up, you'll make one of those butterflies follow the visitor's mouse and add interactive cats that respond to clicks.

Make Butterflies Follow the Mouse and Cats React to Clicks

Your garden looks beautiful with swaying lilies and drifting butterflies. But right now, visitors can only watch.

In this step, you will make the garden respond to the visitor. You will add cats that sit among the flowers and write JavaScript that makes a butterfly follow the mouse and cats react when clicked.

In this step, get ready to:
  • Add two cat characters to the garden with HTML and CSS.
  • Make one butterfly smoothly follow the mouse cursor with JavaScript.
  • Make cats show speech bubbles and bounce when clicked.
Add cat elements to the garden

Each cat is built entirely from CSS shapes. Triangles for ears, circles for eyes, and rounded rectangles for the body.

  • In index.html, find the closing </div> tag for the third butterfly (butterfly-3).
  • Add the following two cat elements below it, still inside the .garden div:
        <div class="cat cat-1" id="cat-1">
            <div class="cat-ear cat-ear-left"></div>
            <div class="cat-ear cat-ear-right"></div>
            <div class="cat-head">
                <div class="cat-eye cat-eye-left"></div>
                <div class="cat-eye cat-eye-right"></div>
                <div class="cat-nose"></div>
            </div>
            <div class="cat-body"></div>
            <div class="cat-tail"></div>
            <div class="speech-bubble" id="bubble-1">Meow!</div>
        </div>

        <div class="cat cat-2" id="cat-2">
            <div class="cat-ear cat-ear-left"></div>
            <div class="cat-ear cat-ear-right"></div>
            <div class="cat-head">
                <div class="cat-eye cat-eye-left"></div>
                <div class="cat-eye cat-eye-right"></div>
                <div class="cat-nose"></div>
            </div>
            <div class="cat-body"></div>
            <div class="cat-tail"></div>
            <div class="speech-bubble" id="bubble-2">Purr...</div>
        </div>

What does this HTML do?

Each cat is a container with nested divs representing body parts. CSS will transform these simple divs into triangular ears, round eyes, and an oval body.

  • .cat-ear divs will become triangles using CSS borders.
  • .speech-bubble starts hidden and will appear when the cat is clicked.
  • Save index.html.

You won't see the cats styled yet because they have no CSS. Next, you will add the styles.

  • In style.css, add the following CSS at the very bottom of the file to style the cat body parts, ears, eyes, tail, and position them in the garden:
/* Cats */
.cat {
    position: absolute;
    bottom: 12%;
    cursor: pointer;
    z-index: 5;
}

.cat-1 {
    left: 30%;
}

.cat-2 {
    right: 20%;
}

.cat-body {
    width: 50px;
    height: 35px;
    background-color: #F4A460;
    border-radius: 50% 50% 40% 40%;
}

.cat-head {
    width: 35px;
    height: 30px;
    background-color: #F4A460;
    border-radius: 50%;
    position: relative;
    margin: 0 auto;
    margin-bottom: -5px;
}

.cat-ear {
    position: absolute;
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-bottom: 12px solid #F4A460;
    top: -10px;
}

.cat-ear-left {
    left: 3px;
}

.cat-ear-right {
    right: 3px;
}

.cat-eye {
    width: 6px;
    height: 6px;
    background-color: #2E8B57;
    border-radius: 50%;
    position: absolute;
    top: 10px;
}

.cat-eye-left {
    left: 8px;
}

.cat-eye-right {
    right: 8px;
}

.cat-nose {
    width: 4px;
    height: 3px;
    background-color: #FF69B4;
    border-radius: 50%;
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
}

.cat-tail {
    position: absolute;
    width: 8px;
    height: 40px;
    background-color: #F4A460;
    border-radius: 4px;
    bottom: 10px;
    right: -5px;
    transform: rotate(30deg);
    transform-origin: bottom center;
    animation: tailWag 1.5s ease-in-out infinite;
}

@keyframes tailWag {
    0%, 100% {
        transform: rotate(30deg);
    }
    50% {
        transform: rotate(50deg);
    }
}

.cat-2 .cat-body,
.cat-2 .cat-head,
.cat-2 .cat-ear-left,
.cat-2 .cat-ear-right,
.cat-2 .cat-tail {
    background-color: #808080;
    border-bottom-color: #808080;
}

What does this CSS do?

  • .cat-ear uses the CSS border triangle trick. Setting width and height to 0 and using transparent side borders with a colored bottom border creates a triangle shape.
  • @keyframes tailWag rotates the tail between 30 and 50 degrees to create a wagging motion.
  • The .cat-2 overrides change the orange cat colour to grey, giving you two distinct cats.
  • Save style.css and check your Live Preview.

You should see two small cats sitting on the grass at the bottom of the garden. One is orange (left) and one is grey (right), both with wagging tails.

Don't see the cats?

  • Make sure you pasted the cat HTML inside the .garden div, not after it.
  • Check that both files are saved. Unsaved changes won't appear in the preview.
  • Verify the CSS is at the bottom of style.css and not nested inside another rule block.

Help me figure out why my cats aren't showing up in the garden.

Make a butterfly follow the mouse

Right now all three butterflies drift on pre-set paths. You will write JavaScript that picks the third butterfly and gently steers it toward the visitor's cursor using requestAnimationFrame.

  • Open script.js (currently empty) and add the mouse-following logic by pasting this code:
// Mouse-following butterfly
const follower = document.getElementById('follower-butterfly');
let mouseX = window.innerWidth / 2;
let mouseY = window.innerHeight / 2;

document.addEventListener('mousemove', function(event) {
    mouseX = event.clientX;
    mouseY = event.clientY;
});

function moveButterfly() {
    const currentX = follower.offsetLeft;
    const currentY = follower.offsetTop;

    const newX = currentX + (mouseX - currentX - 20) * 0.02;
    const newY = currentY + (mouseY - currentY - 10) * 0.02;

    follower.style.left = newX + 'px';
    follower.style.top = newY + 'px';

    requestAnimationFrame(moveButterfly);
}

moveButterfly();

What does this code do?

  • document.getElementById('follower-butterfly') grabs the orange butterfly (the third one) by its ID.
  • The mousemove event listener updates mouseX and mouseY every time the cursor moves.
  • The 0.02 multiplier means the butterfly moves only 2% of the remaining distance each frame. This creates a smooth, lazy trailing effect instead of snapping directly to the cursor.
  • requestAnimationFrame(moveButterfly) calls the function again before the next screen repaint, creating a continuous animation loop.
  • Save script.js and move your mouse around the Live Preview.

You should see the orange butterfly gently drifting toward your cursor wherever you move it.

Butterfly not following the mouse?

  • Make sure you move the mouse inside the preview panel, not over the code editor.
  • Check that your index.html still has id="follower-butterfly" on the third butterfly element.
  • Open the browser console (right-click the preview, then select **Inspect** and click the **Console** tab) to check for any red error messages.

Help me debug why my butterfly isn't following my mouse.

Make cats react to clicks

Clicking a cat should trigger two things: a speech bubble appears above it and the cat bounces. You need the CSS that controls these effects first, then the JavaScript that triggers them.

  • In style.css, add the speech bubble and bounce animation styles at the very bottom of the file:
.speech-bubble {
    position: absolute;
    top: -40px;
    left: 50%;
    transform: translateX(-50%);
    background-color: white;
    border: 2px solid #333;
    border-radius: 12px;
    padding: 5px 10px;
    font-family: Arial, sans-serif;
    font-size: 12px;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
}

.speech-bubble.visible {
    opacity: 1;
}

.cat.bounce {
    animation: catBounce 0.5s ease;
}

@keyframes catBounce {
    0%, 100% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-15px);
    }
}

What does this CSS do?

  • The speech bubble starts with opacity: 0 (invisible). When JavaScript adds the .visible class, it fades in smoothly thanks to the transition property.
  • @keyframes catBounce lifts the cat 15px up at the halfway mark, then drops it back down. This plays when JavaScript adds the .bounce class.
  • Save style.css.

You won't see a visible change yet because nothing adds the .visible or .bounce classes. The next chunk of JavaScript handles that.

  • Return to script.js and add the cat click interaction code at the bottom of the file:
// Cat click interactions
const cats = document.querySelectorAll('.cat');

cats.forEach(function(cat) {
    cat.addEventListener('click', function() {
        const bubble = cat.querySelector('.speech-bubble');

        bubble.classList.add('visible');
        cat.classList.add('bounce');

        setTimeout(function() {
            bubble.classList.remove('visible');
            cat.classList.remove('bounce');
        }, 2000);
    });
});

What does this code do?

  • document.querySelectorAll('.cat') selects both cat elements so the same click behaviour applies to each one.
  • When a cat is clicked, it finds that cat's .speech-bubble child and adds the visible class to fade it in.
  • setTimeout waits 2000 milliseconds (2 seconds), then removes both classes so the bubble fades out and the bounce stops.
  • Save script.js and click one of the cats in the Live Preview.

You should see a speech bubble appear above the cat ("Meow!" for the orange cat, "Purr..." for the grey cat) and the cat bounces. Both effects disappear after 2 seconds.

Cat not reacting to clicks?

  • Make sure the cat click code is after the moveButterfly() call, not inside the moveButterfly function.
  • Verify the class names in your HTML match exactly. The JavaScript looks for elements with class .cat and .speech-bubble.
  • Check the browser console for errors. A typo in querySelector will show as a red message.

Help me debug why clicking my cats does nothing.

✔️ Awesome, I've got everything!

Great. Double check you have saved all three files before moving on.

ⓧ I'd like to double check the full code

Here is the complete index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lily Garden</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="garden">
        <div class="sky"></div>
        <div class="ground"></div>

        <div class="lily lily-1">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="lily lily-2">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="lily lily-3">
            <div class="stem"></div>
            <div class="leaf leaf-left"></div>
            <div class="leaf leaf-right"></div>
            <div class="flower">
                <div class="petal petal-1"></div>
                <div class="petal petal-2"></div>
                <div class="petal petal-3"></div>
                <div class="petal petal-4"></div>
                <div class="petal petal-5"></div>
                <div class="center"></div>
            </div>
        </div>

        <div class="butterfly butterfly-1">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="butterfly butterfly-2">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="butterfly butterfly-3" id="follower-butterfly">
            <div class="wing wing-left"></div>
            <div class="wing wing-right"></div>
            <div class="butterfly-body"></div>
        </div>

        <div class="cat cat-1" id="cat-1">
            <div class="cat-ear cat-ear-left"></div>
            <div class="cat-ear cat-ear-right"></div>
            <div class="cat-head">
                <div class="cat-eye cat-eye-left"></div>
                <div class="cat-eye cat-eye-right"></div>
                <div class="cat-nose"></div>
            </div>
            <div class="cat-body"></div>
            <div class="cat-tail"></div>
            <div class="speech-bubble" id="bubble-1">Meow!</div>
        </div>

        <div class="cat cat-2" id="cat-2">
            <div class="cat-ear cat-ear-left"></div>
            <div class="cat-ear cat-ear-right"></div>
            <div class="cat-head">
                <div class="cat-eye cat-eye-left"></div>
                <div class="cat-eye cat-eye-right"></div>
                <div class="cat-nose"></div>
            </div>
            <div class="cat-body"></div>
            <div class="cat-tail"></div>
            <div class="speech-bubble" id="bubble-2">Purr...</div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

Here is the complete style.css:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
    height: 100vh;
    width: 100vw;
}

.garden {
    position: relative;
    width: 100%;
    height: 100vh;
    overflow: hidden;
}

.sky {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 70%;
    background: linear-gradient(to bottom, #87CEEB, #B0E0E6, #98FB98);
}

.ground {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 30%;
    background: linear-gradient(to bottom, #228B22, #2E8B57);
    border-radius: 50% 50% 0 0 / 10% 10% 0 0;
}

/* Lily Flowers */
.lily {
    position: absolute;
    bottom: 25%;
    transform-origin: bottom center;
    animation: sway 3s ease-in-out infinite;
}

.lily-1 {
    left: 15%;
    animation-delay: 0s;
}

.lily-2 {
    left: 50%;
    animation-delay: 0.5s;
}

.lily-3 {
    left: 75%;
    animation-delay: 1s;
}

@keyframes sway {
    0%, 100% {
        transform: rotate(-2deg);
    }
    50% {
        transform: rotate(2deg);
    }
}

.stem {
    width: 4px;
    height: 120px;
    background-color: #2E8B57;
    margin: 0 auto;
    border-radius: 2px;
}

.leaf {
    position: absolute;
    width: 30px;
    height: 12px;
    background-color: #3CB371;
    border-radius: 50%;
    top: 80px;
}

.leaf-left {
    left: -12px;
    transform: rotate(-30deg);
}

.leaf-right {
    right: -12px;
    transform: rotate(30deg);
}

.flower {
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
    width: 50px;
    height: 50px;
}

.petal {
    position: absolute;
    width: 18px;
    height: 35px;
    background-color: #FF69B4;
    border-radius: 50% 50% 50% 50% / 70% 70% 30% 30%;
    top: 50%;
    left: 50%;
    transform-origin: bottom center;
}

.petal-1 {
    transform: translate(-50%, -100%) rotate(0deg);
}

.petal-2 {
    transform: translate(-50%, -100%) rotate(72deg);
}

.petal-3 {
    transform: translate(-50%, -100%) rotate(144deg);
}

.petal-4 {
    transform: translate(-50%, -100%) rotate(216deg);
}

.petal-5 {
    transform: translate(-50%, -100%) rotate(288deg);
}

.center {
    position: absolute;
    width: 14px;
    height: 14px;
    background-color: #FFD700;
    border-radius: 50%;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

/* Butterflies */
.butterfly {
    position: absolute;
    width: 40px;
    height: 20px;
    z-index: 10;
}

.butterfly-1 {
    top: 20%;
    left: 30%;
    animation: drift1 8s ease-in-out infinite;
}

.butterfly-2 {
    top: 35%;
    left: 60%;
    animation: drift2 10s ease-in-out infinite;
}

.butterfly-3 {
    top: 25%;
    left: 45%;
    transition: transform 0.3s ease-out;
}

@keyframes drift1 {
    0%, 100% {
        transform: translate(0, 0);
    }
    25% {
        transform: translate(80px, -30px);
    }
    50% {
        transform: translate(150px, 10px);
    }
    75% {
        transform: translate(60px, 30px);
    }
}

@keyframes drift2 {
    0%, 100% {
        transform: translate(0, 0);
    }
    25% {
        transform: translate(-60px, 20px);
    }
    50% {
        transform: translate(-120px, -20px);
    }
    75% {
        transform: translate(-40px, -40px);
    }
}

.butterfly-body {
    position: absolute;
    width: 4px;
    height: 20px;
    background-color: #333;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    border-radius: 2px;
}

.wing {
    position: absolute;
    width: 18px;
    height: 14px;
    border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
    top: 2px;
    animation: flap 0.4s ease-in-out infinite alternate;
}

.wing-left {
    left: 0;
    background-color: #FF6347;
    transform-origin: right center;
}

.wing-right {
    right: 0;
    background-color: #FF6347;
    transform-origin: left center;
}

@keyframes flap {
    0% {
        transform: rotateY(0deg);
    }
    100% {
        transform: rotateY(60deg);
    }
}

.butterfly-2 .wing-left,
.butterfly-2 .wing-right {
    background-color: #9370DB;
}

.butterfly-3 .wing-left,
.butterfly-3 .wing-right {
    background-color: #FF8C00;
}

/* Cats */
.cat {
    position: absolute;
    bottom: 12%;
    cursor: pointer;
    z-index: 5;
}

.cat-1 {
    left: 30%;
}

.cat-2 {
    right: 20%;
}

.cat-body {
    width: 50px;
    height: 35px;
    background-color: #F4A460;
    border-radius: 50% 50% 40% 40%;
}

.cat-head {
    width: 35px;
    height: 30px;
    background-color: #F4A460;
    border-radius: 50%;
    position: relative;
    margin: 0 auto;
    margin-bottom: -5px;
}

.cat-ear {
    position: absolute;
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-bottom: 12px solid #F4A460;
    top: -10px;
}

.cat-ear-left {
    left: 3px;
}

.cat-ear-right {
    right: 3px;
}

.cat-eye {
    width: 6px;
    height: 6px;
    background-color: #2E8B57;
    border-radius: 50%;
    position: absolute;
    top: 10px;
}

.cat-eye-left {
    left: 8px;
}

.cat-eye-right {
    right: 8px;
}

.cat-nose {
    width: 4px;
    height: 3px;
    background-color: #FF69B4;
    border-radius: 50%;
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
}

.cat-tail {
    position: absolute;
    width: 8px;
    height: 40px;
    background-color: #F4A460;
    border-radius: 4px;
    bottom: 10px;
    right: -5px;
    transform: rotate(30deg);
    transform-origin: bottom center;
    animation: tailWag 1.5s ease-in-out infinite;
}

@keyframes tailWag {
    0%, 100% {
        transform: rotate(30deg);
    }
    50% {
        transform: rotate(50deg);
    }
}

.cat-2 .cat-body,
.cat-2 .cat-head,
.cat-2 .cat-ear-left,
.cat-2 .cat-ear-right,
.cat-2 .cat-tail {
    background-color: #808080;
    border-bottom-color: #808080;
}

.speech-bubble {
    position: absolute;
    top: -40px;
    left: 50%;
    transform: translateX(-50%);
    background-color: white;
    border: 2px solid #333;
    border-radius: 12px;
    padding: 5px 10px;
    font-family: Arial, sans-serif;
    font-size: 12px;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
}

.speech-bubble.visible {
    opacity: 1;
}

.cat.bounce {
    animation: catBounce 0.5s ease;
}

@keyframes catBounce {
    0%, 100% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-15px);
    }
}

Here is the complete script.js:

// Mouse-following butterfly
const follower = document.getElementById('follower-butterfly');
let mouseX = window.innerWidth / 2;
let mouseY = window.innerHeight / 2;

document.addEventListener('mousemove', function(event) {
    mouseX = event.clientX;
    mouseY = event.clientY;
});

function moveButterfly() {
    const currentX = follower.offsetLeft;
    const currentY = follower.offsetTop;

    const newX = currentX + (mouseX - currentX - 20) * 0.02;
    const newY = currentY + (mouseY - currentY - 10) * 0.02;

    follower.style.left = newX + 'px';
    follower.style.top = newY + 'px';

    requestAnimationFrame(moveButterfly);
}

moveButterfly();

// Cat click interactions
const cats = document.querySelectorAll('.cat');

cats.forEach(function(cat) {
    cat.addEventListener('click', function() {
        const bubble = cat.querySelector('.speech-bubble');

        bubble.classList.add('visible');
        cat.classList.add('bounce');

        setTimeout(function() {
            bubble.classList.remove('visible');
            cat.classList.remove('bounce');
        }, 2000);
    });
});

Your garden is fully interactive now. Butterflies follow your mouse, cats react when clicked, and everything animates smoothly. Next up, you will push this project to GitHub and deploy it as a live website anyone can visit.

Deploy Your Garden to GitHub Pages

Your garden is alive with swaying lilies, fluttering butterflies, and playful cats. Now it's time to share it with the world.

GitHub Pages is a free hosting service that turns your repository into a live website. In this step, you'll push your code to GitHub and enable Pages so anyone can visit your garden with a simple URL.

In this step, get ready to:
  • Commit and push all your files to GitHub.
  • Enable GitHub Pages on your repository.
  • Visit your live garden website and share it.
Commit and push your files to GitHub

Your files only exist in this Codespace right now. To deploy them as a website, you need to push them to your GitHub repository first.

  • In the left Activity Bar of your Codespace, click the Source Control icon (it looks like a branching line).
  • You'll see your three files listed under Changes. Click the + icon next to Changes to stage all files.
  • In the message box at the top of the Source Control panel, type Add interactive lily garden.
  • Click Commit & Push.

What does staging mean?

Staging selects which files you want to include in your next commit. Clicking the + stages all changed files at once, bundling them into a single snapshot that gets pushed to your repository.

  • If prompted to confirm, click Yes or OK to allow the push.

After the push completes, the Source Control panel should show no pending changes. Your code is now on GitHub.

Still seeing files under Changes?

Make sure you clicked the + next to the word Changes (stages all files), not the + next to a single file. If the commit button is greyed out, check that you typed a commit message in the text box above it.

Help me push my files to GitHub from Codespaces.

Enable GitHub Pages in your repository settings

Your code is on GitHub, but it's not a website yet. GitHub Pages needs to be turned on so GitHub knows to serve your files as a live site.

  • Open a new browser tab and go to your lily-garden repository on GitHub.com.
  • Click the Settings tab at the top of the repository page.
  • In the left sidebar, under Code and automation, click Pages.
  • Under Build and deployment, set Source to Deploy from a branch.
  • In the Branch dropdown, select main.
  • In the folder dropdown, select / (root).
  • Click Save.

What did you just configure?

You told GitHub Pages to look at the main branch and serve files from the root folder. Since your index.html is at the top level of the repository, GitHub Pages will automatically use it as the homepage.

Don't see the Pages option?

Make sure you're in the Settings tab of your repository (not your account settings). Look for Pages in the left sidebar under Code and automation. If your repository is private, GitHub Pages won't be available on the free plan. Your repository needs to be public.

Help me find and enable GitHub Pages.

Visit your live garden

GitHub Pages takes a minute or two to build and publish your site. Once it's ready, your garden will be live on the internet for anyone to visit.

  • Wait 1-2 minutes for GitHub to finish building your site.
  • Open a new browser tab and visit your site at:
https://[[GITHUB_USERNAME="your GitHub username"]].github.io/lily-garden/
  • Confirm that the butterflies flutter and drift across the screen.
  • Move your mouse around the page and watch one butterfly follow your cursor.
  • Click on each cat and verify the speech bubble appears with a bounce animation.

How do I find my exact URL?

Go back to your repository's Settings > Pages. Once the site is deployed, a green banner appears at the top with the link to your live site. You can also click Visit site from that banner.

Seeing a 404 page?

GitHub Pages can take up to 10 minutes to publish. Try refreshing after a few minutes. If it still shows a 404, double-check that your repository has an index.html file at the root level (not inside a subfolder).

Also confirm that the branch is set to main and the folder is set to / (root) in the Pages settings. If you committed but forgot to push, go back to the Source Control panel and push again.

Help me fix my GitHub Pages deployment.

Share the link with a friend or post it somewhere. Your interactive lily garden is live on the internet for anyone to enjoy.

Secret mission

Add a Day/Night Toggle

Your garden is beautiful in daylight, but what about after dark? In this secret mission, you'll add a toggle button that transforms the entire scene into a moonlit garden with glowing fireflies and cats with luminous eyes.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely on GitHub (Codespaces for development and Pages for hosting), so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • GitHub Codespace (cloud development environment).
  • The lily-garden GitHub repository (includes your GitHub Pages site).

Keep everything running

No action needed. Choose this if you are still actively building or want to keep your live garden site online.

  • Your Codespace will automatically stop after a period of inactivity, so you will not be charged for compute while it is idle.
  • Your GitHub Pages site stays live at its public URL for free.
  • Inactive Codespaces are automatically deleted after 30 days by default.

Pause - I'll come back to this later

Stop your Codespace to free up compute resources. Your files remain saved inside the Codespace until you reopen it.

  • Navigate to github.com/codespaces in your browser.
  • Find your lily-garden Codespace in the list.
  • Click the ellipsis (...) to the right of the Codespace.
  • Click Stop codespace.

Your GitHub Pages site stays live. When you are ready to continue, reopen the Codespace from the same page by clicking its name.

Delete - I don't want to use this again

Remove all project resources and start fresh. This permanently deletes your Codespace, repository, and live site.

Delete your Codespace:

  • Navigate to github.com/codespaces in your browser.
  • Find your lily-garden Codespace in the list.
  • Click the ellipsis (...) to the right of the Codespace.
  • Click Delete.

Delete your repository (this also removes your GitHub Pages site):

  • Go to your lily-garden repository on GitHub.
  • Click Settings under the repository name.
  • Scroll down to the Danger Zone section.
  • Click Delete this repository.
  • Click I want to delete this repository.
  • Click I have read and understand these effects.
  • Type lily-garden to confirm, then click Delete this repository.

That's a Wrap!

That's a Wrap!

Nice work! You just built a fully interactive, animated lily garden from scratch and deployed it live on the internet.

You've learned how to:

  • Created CSS keyframe animations for swaying lily flowers and 3D butterfly wing-flapping using rotateY transforms.
  • Built JavaScript event listeners that make butterflies follow the mouse with requestAnimationFrame and cats react to clicks with speech bubbles and bounce animations.
  • Deployed a live website to GitHub Pages that anyone can visit via a public URL.
  • Secret Mission: Added a day/night toggle with CSS custom properties, glowing fireflies, and cats with glowing eyes.

Ready to quiz yourself?