box-shadow (both outer and inner) to achieve relief and depth effects in a simple yet effective way.Modern web design is not just about static structures; small animations and visual details, like this experiment of a moon with craters and phases, add a layer of interaction that improves the user experience. Below, we will see how to achieve this effect step by step.
Building our Moon in three steps
1. The base structure and the lunar halo
It all starts with a simple circular div. To give it that "glowing" look, we will use the box-shadow property. Instead of a solid color, we will apply a pulsing animation that emulates the natural glow of the moon on a clear night.
I have modernized the positioning using left: 50% and transform to ensure the moon is always centered regardless of the container size:
#moon {
position: absolute;
top: 15%;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 200px;
border-radius: 50%;
background: #CCC;
box-shadow: 0 0 100px #FFFF8C;
z-index: 5;
animation: moonPulse 3s infinite ease-in-out;
}
@keyframes moonPulse {
0%, 100% { box-shadow: 0 0 80px #FFF; }
50% { box-shadow: 0 0 120px #FFF; }
}2. Craters and depth with Box-Shadow Inset
For the craters, the secret lies in the box-shadow: inset property. This creates an internal shadow that gives the sensation of a hollow without the need for complex images or SVGs. Additionally, we add a top layer to simulate lunar phases using shadows.
#moonFase {
position: absolute;
top: 15%;
left: 50%;
transform: translateX(-50%);
width: 200px;
height: 200px;
border-radius: 50%;
background: rgba(153, 153, 153, 0);
/* Internal shadow for phase effect */
box-shadow: inset -25px 0px 0 0px #999;
z-index: 6;
animation: moonFaseAnimation 600s infinite linear;
}
.crater {
position: absolute;
background: #555;
box-shadow: inset 3px -2px 0 0px #414043;
z-index: 7;
border-radius: 50%;
}SEO and Performance Tip: By using pure CSS instead of heavy images, we reduce page load time, a key factor for Google ranking.
3. Dynamic starry background with Modern JavaScript
Positioning 250 stars manually would be inefficient. Therefore, we use a small script that generates stars in random positions. I have updated the original code to ES6 using const and DocumentFragment to minimize browser "reflows," improving performance.
const generateStars = () => {
const w = window.innerWidth;
const h = window.innerHeight;
const fragment = document.createDocumentFragment();
for (let i = 0; i < 250; i++) {
const star = document.createElement("div");
star.className = "star";
// Random positioning
const x = Math.floor(Math.random() * w);
const y = Math.floor(Math.random() * h);
star.style.bottom = `${y}px`;
star.style.right = `${x}px`;
fragment.appendChild(star);
}
document.body.appendChild(fragment);
};
generateStars();The style of the stars remains minimalist, but with a slight glow so they appear to twinkle:
.star {
width: 2px;
height: 2px;
border-radius: 50%;
background: #FFF;
box-shadow: 0 0 2px 1px rgba(255, 255, 255, 0.8);
position: absolute;
}Taking it further: Customization
This experiment is the perfect foundation for practice. You can try changing the moon's color to a reddish tone for a "lunar eclipse," or modify the speed of the phase animation. If you want to dive deeper into the world of animations, I recommend reading my article on how to take your first steps with CSS animations.
Did you like this tutorial? Don't forget to also check out how to highlight content in an animated way with CSS to keep improving your web design skills.