The galaxy button: orbiting stars and a 3D ring in CSS
jh3y posted a button that I couldn't stop hovering. It's a glow, sure, but there are tiny stars orbiting around it on a tilted ring, like the button is a little planet. Hover it.
The ring is the trick
A glow is easy. The part that makes this feel three-dimensional is the star
ring. The button sets transform-style: preserve-3d and a perspective, then a
flat circular element is tipped back with a stack of rotations so it reads as a
disc receding into the distance:
.galaxy__ring {
transform: rotateX(-24deg) rotateY(-30deg) rotateX(90deg);
transform-style: preserve-3d;
}The stars are just absolutely-positioned dots pushed outward along that disc and spun with a linear animation. Because they live inside a 3D-transformed parent, their circular orbit projects to an ellipse, which is what your eye reads as "around" rather than "on top of."
.star {
transform: translate(-50%, -50%) rotate(10deg)
translateY(calc(var(--distance) * 1px));
animation: galaxy-orbit calc(var(--duration) * 1s) infinite linear;
}One variable runs the show
Every other piece, the glow, the scale, the star opacity, the conic spark
sweeping around the rim, hangs off a single --active value that flips from 0
to 1 on hover or focus:
.galaxy-btn { --active: 0; scale: calc(1 + var(--active) * 0.1); }
.galaxy-btn:is(:hover, :focus-visible) { --active: 1; }Because each effect multiplies by --active, they all wake up together and
settle together. No JavaScript, no keyframe juggling, just one number and a
transition.
Porting it to React, I kept it honest: a real <button> so focus works, the
whole thing scoped to the component (the original toggled the page background
through :has), the stars hard-coded so server and client render the same, and
the animations parked behind prefers-reduced-motion.
Full credit to jh3y for the original. Grab the component on the Galaxy Button page.
Ask your agent to implement this
Read the full writeup at https://seangeng.com/writing/the-galaxy-button.md and implement it in my project.
It covers: The galaxy button: orbiting stars and a 3D ring in CSS — jh3y's glowy CTA, ported to a React component. Orbiting stars, a conic spark sweep, and a star ring tipped into 3D with transform-style: preserve-3d, all driven by one --active variable.
Requirements:
- Follow the technique/approach exactly as described in the writeup.
- Adapt names, colors, and styling to my project's existing conventions.
- If it's a component, make it reusable with sensible props and TypeScript types.
- Keep it accessible: semantic HTML, keyboard support, and respect prefers-reduced-motion.
- When done, tell me which files you created or changed and how to use it.Paste into Claude Code, Codex, Cursor, or any agent. view raw .md