NO JAVASCRIPT · NO IMAGES · ONE FILE

SOLSTICE

A night that lights up, a grid that runs forever, and a sky you can switch — every last interaction powered by hidden checkboxes and a CSS :has().

01 — THE CRAFT

Six tricks, zero scripts

Everything on this page is a classic CSS move. Here's the short list of what's actually doing the work.

01

Three-layer starfield

Three tiles of radial-gradient dots, each looping its own twinkle keyframe at a different speed. A hundred stars cost one element.

background-size
02

The sun with real holes

The stripes aren't painted on — a second mask layer punches actual holes in the lower half, so the sky genuinely shows through.

mask-image × 2
03

Infinite grid floor

One rotated plane, one background-position loop. Perspective does all the fake-3D math, and the tile loops exactly one cell so the seam is invisible.

perspective() rotateX()
04

Scroll, for free

animation-timeline: scroll() and view() drive the parallax, progress bar and card reveals. No scroll listener, no IntersectionObserver.

animation-timeline
05

Hidden state machine

The theme, the menu, the playground — all plain checkboxes and radios. :has() climbs the tree and restyles the whole page from any descendant.

body:has(:checked)
06

Free platform stuff

Native <details> accordion, smooth anchor scrolling, a pausable marquee, focus-visible rings, reduced-motion fallbacks. All in the box.

<details>

02 — PLAYGROUND

Poke the page

These controls are form inputs with no forms, no handlers — the state panel below is just CSS content reading the checkboxes.

Grid speed

Atmosphere

Live state

03 — FIELD NOTES

Fine print, plain language

The honest engineering notes — what degrades, where, and why it doesn't matter.

Where is the JavaScript?
There isn't any. #mode is a checkbox, the menu is a checkbox, the speed picker is three radios. CSS :has() lets an ancestor — here, body — react to the state of its descendants. That's the entire state machine on this page: no bundle, no runtime, no hydration, no framework, no build step.
Why does the parallax work for some visitors but not others?
Scroll-driven animations (animation-timeline: scroll() and view()) ship in Chrome, Edge and Opera. Safari and Firefox ignore the @supports blocks and simply render the static scene — the progress bar and parallax just don't exist there. Nothing breaks. That's progressive enhancement as a native CSS feature.
How does the grid loop seamlessly?
The floor is a 64×64px tile, and the keyframe shifts the background by exactly one tile. Start and end frames are pixel-identical, so the loop point is invisible. The starfield uses the same trick: radial-gradient dot tiles repeated via background-size.
How does the sky switch themes?
No data-* attributes, no class on <html>. The checkbox is the state. body:has(#mode:checked) rewrites a handful of custom properties, and downstream styles do the rest: sky layers crossfade on opacity, the grid color interpolates through currentColor, mountains transition background-color, stars dim via --star-o. One input, ~a dozen reactive styles.
Can I take this apart?
View source. There is no minification, no framework, no generated output — what you see is what I wrote. Delete any section and the page still works. And yes, the prefers-reduced-motion block really is in there.