Episode 1 of 8

CSS-only Christmas Lights

Create a beautiful string of animated, glowing Christmas lights using only HTML and CSS — no JavaScript required.

CSS-only Christmas Lights

In this episode we will build a festive string of glowing Christmas lights using pure CSS. The lights will glow, flicker, and animate with staggered timing — all without a single line of JavaScript.

The HTML Structure

<ul class="lights">
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>

Each <li> represents one light bulb. The <ul> acts as the wire connecting them all.

The Wire — Styling the UL

.lights {
    display: flex;
    justify-content: center;
    gap: 40px;
    padding: 20px;
    list-style: none;
    position: relative;
}

/* The wire connecting the lights */
.lights::before {
    content: "";
    position: absolute;
    top: 0;
    left: 5%;
    right: 5%;
    height: 4px;
    background: #333;
    border-radius: 2px;
}

The Bulbs — Styling Each LI

.lights li {
    width: 20px;
    height: 30px;
    border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
    position: relative;
    margin-top: 20px;
}

/* The cap on top of each bulb */
.lights li::before {
    content: "";
    position: absolute;
    top: -8px;
    left: 50%;
    transform: translateX(-50%);
    width: 12px;
    height: 12px;
    background: #444;
    border-radius: 3px 3px 0 0;
}

/* The string connecting to the wire */
.lights li::after {
    content: "";
    position: absolute;
    top: -20px;
    left: 50%;
    width: 2px;
    height: 12px;
    background: #333;
}

Assigning Colors with nth-child

.lights li:nth-child(1) { background: #ff4136; }
.lights li:nth-child(2) { background: #ffdc00; }
.lights li:nth-child(3) { background: #2ecc40; }
.lights li:nth-child(4) { background: #0074d9; }
.lights li:nth-child(5) { background: #ff851b; }
.lights li:nth-child(6) { background: #b10dc9; }
.lights li:nth-child(7) { background: #ff4136; }

Each bulb gets a different Christmas color. The pattern repeats for longer strings using higher nth-child values or using formulas like nth-child(7n+1).

The Glow Animation

@keyframes glow {
    0%, 100% {
        filter: brightness(1);
        box-shadow: 0 0 5px currentColor,
                    0 0 10px currentColor;
    }
    50% {
        filter: brightness(1.5);
        box-shadow: 0 0 15px currentColor,
                    0 0 30px currentColor,
                    0 0 45px currentColor;
    }
}

.lights li {
    animation: glow 2s ease-in-out infinite alternate;
}

The currentColor keyword automatically picks up each bulb's background color for the glow, so we only need one animation for all colors.

Staggered Timing with nth-child

.lights li:nth-child(1) { animation-delay: 0s; }
.lights li:nth-child(2) { animation-delay: 0.3s; }
.lights li:nth-child(3) { animation-delay: 0.6s; }
.lights li:nth-child(4) { animation-delay: 0.9s; }
.lights li:nth-child(5) { animation-delay: 1.2s; }
.lights li:nth-child(6) { animation-delay: 1.5s; }
.lights li:nth-child(7) { animation-delay: 1.8s; }

Each bulb starts its animation slightly after the previous one, creating a cascading glow effect that travels along the string.

Adding a Subtle Swing

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

.lights li {
    transform-origin: top center;
    animation: glow 2s ease-in-out infinite alternate,
               swing 4s ease-in-out infinite alternate;
}

We chain a second animation to make each bulb gently swing like it is hanging from a wire. The transform-origin: top center ensures it pivots from the cap at the top.

Dark Background for Maximum Effect

body {
    background: #111;
    min-height: 100vh;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 80px;
}

A dark background makes the glowing lights pop. The flex layout centers the string horizontally near the top of the page.

Key Takeaways

  • nth-child selectors let you assign unique colors and stagger animation delays to each bulb
  • currentColor in box-shadow automatically adapts the glow to each bulb's background color
  • Chaining multiple animations (glow + swing) on a single element creates rich, layered effects
  • Pseudo-elements (::before and ::after) are used to build the bulb caps and wire strings without extra HTML
  • A dark background is essential to showcase glow and box-shadow effects properly