What You'll Build

In this tutorial, you'll build a richly animated day/night toggle switch using HTML, CSS, inline SVG, and a small amount of JavaScript. From the outside it looks like a simple pill-shaped checkbox, but inside the switch lives an entire miniature landscape — layered SVG mountains, a floating orb that acts as both moon and sun, eleven tiny star dots, and three drifting cloud shapes, all confined within the toggle's rounded boundary by overflow: hidden.

Clicking the toggle triggers a cascade of simultaneous transitions: the sky gradient shifts from a deep indigo-to-black night to a soft aqua-to-green day, the orb slides from left to right while its gradient swaps from a cool moon-blue to a warm sun-yellow, the stars translate upward and vanish, and the clouds slide down from off-screen into view. A separate JavaScript listener handles the one thing CSS can't do: updating stop-color attributes directly on SVG linearGradient elements to recolor the mountain silhouettes and the ground layer.

It's a fantastic project for learning the CSS checkbox hack, how to layer SVG inside HTML components, how to manage multi-element state transitions with a single checked selector, and how to coordinate CSS and JavaScript to animate what CSS alone cannot reach.

💡 Who Is This For? This project suits beginners who are comfortable with basic HTML/CSS and want hands-on practice with CSS sibling selectors, SVG basics, and multi-element transitions. It's also a great reference for intermediate developers exploring the boundaries of pure-CSS state management.

Key Features of This Day Night Toggle Switch

🌙
Night Sky Gradient
The default background uses a deep indigo-to-black linear-gradient on .slider to paint a realistic dark night sky inside the pill.
☀️
Day Sky Transition
On :checked, the gradient flips to a soft aqua-to-green palette, smoothly transitioning the sky background in 0.4s to represent daytime.
🌕
Moon-to-Sun Sliding Orb
The .slider:before pseudo-element is a circle that slides 110px to the right on toggle, changing its gradient from cool moon-blue to warm sun-yellow.
Star Dot Elements
Eleven .circle divs act as stars, scattered in varying sizes (1–4px) across the switch. They translateY(-200px) off-screen when day mode is activated.
☁️
Sliding Cloud SVGs
Three inline SVG cloud shapes start at translateY(200px) (below the component) and slide up into view when the day sky is active, creating a natural reveal.
⛰️
Layered Mountain Silhouettes
Four absolutely-positioned SVG mountains with linearGradient fills sit at the bottom of the switch, recolored via JavaScript from night purple-to-dark to day green-to-teal.
🎛️
CSS Checkbox Hack
A hidden <input type="checkbox"> is the state holder. All sibling and general-sibling CSS selectors react to :checked with no class toggling needed for the visuals.
🎨
SVG Gradient JavaScript Updates
A single change event listener calls setAttribute('stop-color', ...) on gradient stops to recolor the mountain fills and orb — the CSS-unreachable finishing touch.

Full Source Code (Free)

The entire project lives in a single HTML file — no build tools, no external dependencies. CSS handles all the transitions, transforms, and gradient changes for the background, orb, stars, and clouds. JavaScript's role is deliberately narrow: it updates SVG linearGradient stop colors on the mountain shapes and orb that can't be reached with CSS property changes alone. Use the tabs below to explore each section.

HTML — index.html
CSS — style.css
JS — script.js
💡 Quick Start: Paste all the code into a single index.html file. No libraries or build steps required — open it in any modern browser and click the toggle to watch the full scene animate.

How It Works — Step by Step

Here's a breakdown of the eight core techniques that make this animated day/night toggle work, from the outermost shell down to the SVG gradient JavaScript updates.

01

The Switch Shell — Label, Checkbox & Slider

Everything is wrapped in a <label class="switch"> element sized at 200×90px with border-radius: 100px and overflow: hidden. The hidden <input type="checkbox"> sits first inside the label with opacity: 0; width: 0; height: 0; so it's accessible but invisible, followed by the .slider span that fills the entire label area absolutely.

02

The Sky Background Gradient

.slider uses background-image: linear-gradient(to bottom, #364BBA, #A979D9) as its default night sky — a rich indigo top blending to a soft purple-violet at the base. The input:checked + .slider rule switches this to linear-gradient(to bottom, #ABFAFF, #D5FFAB) — a sky-blue to meadow-green palette — with the 0.4s transition already declared on the base .slider rule handling the fade.

03

The Moon/Sun Orb — :before Pseudo-element

.slider:before creates a 70×70px white circle positioned at top: 10px; left: 10px;. When the checkbox is checked, input:checked + .slider:before applies transform: translateX(110px), sliding the orb from the moon's position on the left to the sun's position on the right. The gradient color change from the cool moon-blue to the warm sun-yellow is handled by JavaScript updating the SVG gradient stops on the orb's <circle>.

04

The Horizon Ground Layer SVG

A .slider-bg SVG path draws an irregular ground/horizon silhouette across the bottom half of the switch using an absolute position at top: 41px; left: -4px;. Its linearGradient fill transitions from the night palette (#6A86EB → #010203) to the day palette (#9FDEF2 → #A8FFAC) via the JavaScript change listener updating each gradient stop's stop-color attribute.

05

Layered Mountain Silhouettes

Four .mountain SVGs (two large, two medium) are absolutely positioned across the bottom of the switch with z-index: 7, sitting above the horizon layer. Each mountain has its own named linearGradient with uniquely ID'd stops (e.g. mountain1-stop1). The JavaScript listener updates all eight stop-color values: dark night purple-blue converts to a natural day green-to-teal range, making the mountains look like living hills in daylight.

06

Star Dots — Fade Up and Out

Eleven .circle divs with sizes between 1px and 4px and a cream-white background color are scattered at specific top/left/right positions across the upper half of the switch. The 0.4s transition on .circle means that when input:checked ~ .circle applies transform: translateY(-200px), all eleven stars smoothly exit upward through the hidden overflow: hidden boundary.

07

Cloud SVGs — Slide In From Below

Three .cloud SVGs (one large and two small) start off-screen with transform: translateY(200px) — pushed below the switch's visible area. The 0.4s transition on .cloud is the mirror of the star transition: when input:checked ~ .cloud resets the transform to translateY(0), the clouds glide upward into the day sky scene.

08

JavaScript SVG Gradient Updates

A single DOMContentLoaded listener on the #toggle input listens for the change event. When fired, it selects each named gradient stop element by ID and calls setAttribute('stop-color', newHex) with the correct day or night palette value. This covers the slider-bg ground layer, the SVG orb circle gradient, and all four mountain gradients — giving every element its own perfectly themed color for each sky mode.

⚠️ SVG Gradient IDs Must Be Unique Per SVG: Each mountain uses its own linearGradient with unique id attributes (e.g. paint0_linear_217_219 through paint0_linear_217_222). If you duplicate SVGs without renaming gradient IDs, all mountains will read from the same gradient definition and only one set of stop-color updates will take effect — the others will share that same gradient source.

Customization Ideas

The toggle's clean, layered architecture makes it straightforward to extend or re-theme. Here are some practical directions to make it your own.

  • Resize the switch — The component uses fixed pixel sizes. To scale it up or down, increase or decrease .switch's width and height, then adjust the orb's translateX distance in input:checked + .slider:before to match.
  • Change sky palettes — Replace the gradient stops in .slider's base and :checked rules to pick any two sky moods — a stormy grey-to-purple, a sunset orange-to-red, or a dawn pink-to-gold.
  • Add a twinkling star animation — Wrap each .circle star in a simple CSS @keyframes that oscillates opacity between 0.3 and 1 with staggered animation-delay values for a realistic twinkle effect in night mode.
  • Swap mountains for a cityscape — Replace the mountain SVG paths with rectangular building silhouettes to create an urban skyline variant. Keep the same z-index layering for the depth effect.
  • Add a sunrise/sunset color stage — If you hook the toggle's change event into your own JavaScript theme system, you could add a brief intermediate orange-sky state that plays before committing to full daytime — a three-phase transition using setTimeout and class swapping.
  • Wire it to a real dark mode — Add an event listener on the checkbox that reads this.checked and toggles a data-theme="light" attribute on <html>, then define CSS custom property overrides under [data-theme="light"] to theme your entire page. Persist the preference with localStorage.
  • Change the body background color — The JavaScript already updates document.body.style.backgroundColor between #ffffff (day) and #000000 (night). Hook this into your full site's color scheme by swapping CSS custom properties instead of a raw hex value.
💡 Tip: Because overflow: hidden on .switch clips everything to the pill shape, you can add new decorative elements — birds, a shooting star, raindrops — by positioning them absolutely inside the label and animating them with transform. They'll be masked automatically by the pill boundary.

Browser Compatibility

Every technique in this toggle — CSS gradients, transitions, transforms, overflow: hidden, inline SVG with linearGradient, and setAttribute() via JavaScript — is part of the web platform's stable baseline. There are no cutting-edge CSS features or experimental APIs in use.

Browser CSS Transitions & Transforms Inline SVG + linearGradient Overall Support
Chrome / Edge Yes Yes Full
Firefox Yes Yes Full
Safari (macOS & iOS) Yes Yes Full
Samsung Internet Yes Yes Full
Internet Explorer 11 Partial Limited Not Recommended
💡 Note: The one edge case to watch is CSS transitions on SVG background-image gradients. The slider background itself transitions cleanly in all modern browsers via the transition: 0.4s on the .slider element. Internet Explorer 11 lacks full support for CSS gradient transitions and is not a viable target for new projects in 2026.

Performance & Responsiveness

Despite the visual complexity — multiple SVG layers, eleven animated elements, and coordinated color shifts — this component is remarkably lightweight and performs smoothly on modest hardware.

  • GPU-composited transitions: Every animation in the component uses transform (for the orb slide, star exit, and cloud entrance) or a background-image property change with transition. Both paths avoid layout recalculation, keeping the browser in its compositor thread for a consistent frame rate.
  • No raster images: All graphic content — mountains, clouds, ground silhouette, orb — is inline SVG with vector paths. The switch looks crisp at any device pixel ratio, from standard 1x screens to 3x Retina displays, with zero additional HTTP requests.
  • Minimal JavaScript footprint: The JS is a single DOMContentLoaded callback that caches element references once and calls setAttribute on toggle. There's no animation loop, no library, and no repeated DOM queries.
  • Self-contained sizing: The component uses fixed pixel dimensions (200×90px) which makes it predictable inside any layout system — flex, grid, or static. To make the switch responsive, wrap it in a CSS transform: scale() applied at a breakpoint rather than reflowing internal pixel positions.
  • Accessibility baseline: Because the toggle is built on a real <input type="checkbox"> inside a <label>, it is keyboard-focusable and operable by default. Adding aria-label="Toggle day and night mode" to the label completes the accessibility baseline.
💡 Key Takeaway: Choosing transform over positional properties like top/left for the orb, stars, and clouds is what keeps this visually dense component smooth. Even with eleven simultaneously animating elements, the browser's compositor handles all of them in a single pass.

Frequently Asked Questions

How does the day/night toggle switch work without a JavaScript animation library?
The entire toggle animation is driven by CSS. A hidden checkbox input acts as the state holder. When it's checked, CSS sibling selectors like input:checked + .slider and input:checked ~ .cloud target every visual element in the component — changing background gradients, moving the orb with translateX, sliding clouds in, and hiding stars — all via CSS transition and transform. JavaScript is only used for the one task CSS can't do: updating SVG linearGradient stop-color attributes for the mountain and orb color changes.
What is the CSS checkbox hack and how is it used here?
The checkbox hack is a pure-CSS state management technique. A real <input type="checkbox"> element is hidden with opacity:0 and zero dimensions but remains in the DOM. Because it's a sibling of .slider and a general sibling (~) of .circle and .cloud elements, CSS can target input:checked + .slider, input:checked ~ .circle, and so on. Clicking the visible label triggers the checkbox, and every CSS rule watching :checked instantly updates all the visual layers — no class toggling required.
Why are the SVG gradients updated with JavaScript instead of CSS?
CSS cannot directly animate or transition SVG presentation attributes like stop-color inside a <linearGradient> definition. While CSS custom properties can work in some browsers, the most reliable cross-browser approach is to update the stop-color attribute directly with JavaScript's setAttribute(). The toggle's change event listener sets new hex values on each gradient stop for the mountain fill and orb gradient, giving a clean color shift between night and day palettes that CSS alone cannot achieve.
How are the stars created and how do they disappear in day mode?
Each star is a tiny <div class="circle"> with a fixed pixel size (1–4px), a circular border-radius, and a cream-white background color, absolutely positioned inside the .switch container. In their default state they're visible against the dark night gradient. When input:checked is active, the CSS rule input:checked ~ .circle applies transform: translateY(-200px) with a 0.4s transition, sliding them upward out of the overflow: hidden switch container and out of view — a simple but effective disappearing trick.
Is this project suitable for beginners?
Yes, absolutely. The core animation requires only beginner-level CSS — gradients, transitions, and translateX/Y transforms. The checkbox hack is a great concept to learn early because it shows how to manage UI state without JavaScript. The JavaScript portion is minimal: a single event listener that calls setAttribute() on a handful of SVG nodes. It's an ideal project for anyone who knows basic HTML/CSS positioning and wants to practice CSS sibling selectors and SVG basics in a fun, visual context.
Can I use this day/night toggle as a real dark mode switch for my website?
Definitely. To wire it up as a functional dark mode switch, add an id to the checkbox and listen for its change event with JavaScript. On change, toggle a dark or light class on <body> or <html>, then use CSS custom properties under those classes to swap your site's colors. You can also save the user's preference to localStorage so it persists across page loads. The toggle component itself is purely visual and self-contained, so it slots into any existing design system without conflict.

Conclusion

This animated day/night toggle switch shows just how much visual storytelling you can pack into a 200×90px container with nothing but HTML, CSS, and a few lines of JavaScript. By combining the CSS checkbox hack, layered inline SVG, coordinated sibling transitions, and targeted JavaScript SVG attribute updates, the toggle becomes a miniature animated scene — not just a UI control.

The techniques here extend well beyond this specific project. The pattern of using a hidden input as a state flag, letting CSS sibling selectors drive all visual changes, and reserving JavaScript only for what CSS genuinely cannot do is a clean, maintainable approach for any interactive component.

Looking to keep building? Try the Neon Cursor Animation for advanced pointer effects driven by JavaScript, or the Animated Button with Star Effects to explore GSAP-powered micro-interactions.

Found this useful? Explore more HTML CSS JavaScript projects in the sidebar.