The Solar System has been done in CSS a lot of times — just search Codepen! So why do it again?
Because things get better and simpler — and we can now do a responsive solar system with just a few lines of CSS.
Let's start with some very basic markup:
<ol>
<li class="sun"></li>
<li class="mercury"></li>
<li class="venus"></li>
<li class="earth"></li>
<li class="mars"></li>
<li class="jupiter"></li>
<li class="saturn"></li>
<li class="uranus"></li>
<li class="neptune"></li>
</ol>
We use an ordered list, because the planets are in order.
Next, we unset
the default <ol>
-styles, and style it as a grid:
ol {
all: unset;
aspect-ratio: 1 / 1;
container-type: inline-size;
display: grid;
width: 100%;
}
Now, for the planet trajectories, we're going to use a "grid stack". Instead of position: absolute
, and a bunch of translations, we can simply stack all the grid items with:
li {
grid-area: 1 / -1;
place-self: center;
}
By setting a --d
-variable (for diameter) per planet, using width: var(--d);
, we get:
Cool! Let's add the planets using an ::after
pseudo-element:
li::after {
aspect-ratio: 1 / 1;
background: var(--b);
border-radius: 50%;
content: '';
display: block;
width: var(--w, 2cqi);
}
Let's ask ChatGPT to generate some nice radial-gradent
s for each planet — and while we're at it, let's tell it we're creating the Solar System and ask for planetary sizes between 1
and 6cqi
— not completely accurate, but still maintaining a sizeable, recognizable difference:
.mercury {
--b: radial-gradient(circle, #c2c2c2 0%, #8a8a8a 100%);
--w: 2.0526cqi;
}
.venus {
--b: radial-gradient(circle, #f4d03f 0%, #c39c43 100%);
--w: 2.6053cqi;
}
.earth {
--b: radial-gradient(circle, #3a82f7 0%, #2f9e44 80%, #1a5e20 100%);
--w: 3.1579cqi;
}
.mars {
--b: radial-gradient(circle, #e57373 0%, #af4448 100%);
--w: 3.7105cqi;
}
.jupiter {
--b: radial-gradient(circle, #d4a373 0%, #b36d32 50%, #f4e7d3 100%);
--w: 4.8158cqi;
}
.saturn {
--b: radial-gradient(circle, #e6dba0 0%, #c2a13e 100%);
--w: 5.3684cqi;
}
.uranus {
--b: radial-gradient(circle, #7de3f4 0%, #3ba0b5 100%);
--w: 4.2632cqi;
}
.neptune {
--b: radial-gradient(circle, #4c6ef5 0%, #1b3b8c 100%);
--w: 6cqi;
}
And now we have:
To animate the planets with different trajectory speeds, we add:
li::after {
/* previous styles */
animation: rotate var(--t, 3s) linear infinite;
offset-path: content-box;
}
Notice the offset-path
. That's the key to simplifying the trajectory-animations, because all we have to do to move the planet along the shape of the <li>
is this:
@keyframes rotate {
to {
offset-distance: 100%;
}
}
And that's all! I asked ChatGPT to calculate the timings based on "Neptune", with a rotation-speed of 20s
— and we get:
Conclusion
With just a few rules, we created a simple 2d version of the Solar System in pure CSS. If you want to dive deeper, you can:
- use real distances and sizes (with
calc()
) - add a
transform: rotateX(angle)
to the<ul>
to make it pseudo-3D:
... and maybe use matrix3d
to "re-flatten" the planets?
Happy coding!
UPDATE, 27.08.24: Some readers have poined out, that the scales and distances are not correct. That is true, and is on purpose. The goal of this article is to show the powers of
offset-path
and grid-stacks.
However, if we were to calc the system dimensions with the Sun's width at 10cqi
and the outer trajectory of Neptune at 100cqi
, then we have an AU (Astronomical Unit) of 2.994cqi
, and we get:
Awesome animation and nice use of ChatGPT for the orbits!