22 CSS Image Galleries05 / 22
CSS Radial Orbit Image Gallery
Circular thumbnails arranged evenly around a central hero and slowly revolving around it like planets — a radial orbit gallery for 'our services', galleries and playful hero sections. Pure CSS transforms, pauses on hover.
Published
The code
<section class="gl-17" aria-label="Radial orbit gallery">
<div class="gl-17__system">
<div class="gl-17__center"><img src="https://loremflickr.com/300/300/galaxy,space?lock=1700" alt="Featured central image" loading="lazy" decoding="async"></div>
<div class="gl-17__ring">
<a class="gl-17__sat" href="#" style="--a:0deg"><img src="https://loremflickr.com/200/200/planet?lock=1701" alt="Orbit photo 1" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:45deg"><img src="https://loremflickr.com/200/200/moon?lock=1702" alt="Orbit photo 2" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:90deg"><img src="https://loremflickr.com/200/200/star?lock=1703" alt="Orbit photo 3" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:135deg"><img src="https://loremflickr.com/200/200/nebula?lock=1704" alt="Orbit photo 4" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:180deg"><img src="https://loremflickr.com/200/200/comet?lock=1705" alt="Orbit photo 5" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:225deg"><img src="https://loremflickr.com/200/200/sky,night?lock=1706" alt="Orbit photo 6" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:270deg"><img src="https://loremflickr.com/200/200/telescope?lock=1707" alt="Orbit photo 7" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:315deg"><img src="https://loremflickr.com/200/200/astronomy?lock=1708" alt="Orbit photo 8" loading="lazy" decoding="async"></a>
</div>
</div>
</section><section class="gl-17" aria-label="Radial orbit gallery">
<div class="gl-17__system">
<div class="gl-17__center"><img src="https://loremflickr.com/300/300/galaxy,space?lock=1700" alt="Featured central image" loading="lazy" decoding="async"></div>
<div class="gl-17__ring">
<a class="gl-17__sat" href="#" style="--a:0deg"><img src="https://loremflickr.com/200/200/planet?lock=1701" alt="Orbit photo 1" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:45deg"><img src="https://loremflickr.com/200/200/moon?lock=1702" alt="Orbit photo 2" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:90deg"><img src="https://loremflickr.com/200/200/star?lock=1703" alt="Orbit photo 3" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:135deg"><img src="https://loremflickr.com/200/200/nebula?lock=1704" alt="Orbit photo 4" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:180deg"><img src="https://loremflickr.com/200/200/comet?lock=1705" alt="Orbit photo 5" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:225deg"><img src="https://loremflickr.com/200/200/sky,night?lock=1706" alt="Orbit photo 6" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:270deg"><img src="https://loremflickr.com/200/200/telescope?lock=1707" alt="Orbit photo 7" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:315deg"><img src="https://loremflickr.com/200/200/astronomy?lock=1708" alt="Orbit photo 8" loading="lazy" decoding="async"></a>
</div>
</div>
</section>.gl-17,
.gl-17 *,
.gl-17 *::before,
.gl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-17 ::selection {
background: oklch(0.7 0.16 270);
color: #fff;
}
.gl-17 {
--r: 170px;
--accent: oklch(0.72 0.16 270);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: radial-gradient(120% 120% at 50% 40%,#1a1836,#06060f);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__system {
position: relative;
width: calc(var(--r) * 2 + 90px);
height: calc(var(--r) * 2 + 90px);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__center {
width: 120px;
height: 120px;
border-radius: 50%;
overflow: hidden;
z-index: 2;
box-shadow: 0 0 40px -6px var(--accent),0 0 0 4px rgba(255,255,255,.1);
}
.gl-17__center img {
width: 100%;
height: 100%;
object-fit: cover;
}
.gl-17__ring {
position: absolute;
inset: 0;
animation: gl-17-spin 26s linear infinite;
}
.gl-17__system:hover .gl-17__ring,
.gl-17__system:focus-within .gl-17__ring {
animation-play-state: paused;
}
.gl-17__sat {
position: absolute;
top: 50%;
left: 50%;
width: 66px;
height: 66px;
margin: -33px;
border-radius: 50%;
overflow: hidden;
text-decoration: none;
transform: rotate(var(--a)) translate(var(--r)) rotate(calc(-1 * var(--a)));
box-shadow: 0 0 0 3px rgba(255,255,255,.12),0 8px 20px -8px rgba(0,0,0,.6);
animation: gl-17-spin 26s linear infinite reverse;
transition: box-shadow .3s;
}
.gl-17__sat img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform .3s;
}
.gl-17__sat:hover img,
.gl-17__sat:focus-visible img {
transform: scale(1.12);
}
.gl-17__sat:hover,
.gl-17__sat:focus-visible {
box-shadow: 0 0 0 3px var(--accent),0 10px 26px -8px rgba(0,0,0,.7);
}
.gl-17__sat:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
@keyframes gl-17-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 560px) {
.gl-17 {
--r: 120px;
}
.gl-17__sat {
width: 54px;
height: 54px;
margin: -27px;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-17__ring,
.gl-17__sat {
animation: none;
}
}.gl-17,
.gl-17 *,
.gl-17 *::before,
.gl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-17 ::selection {
background: oklch(0.7 0.16 270);
color: #fff;
}
.gl-17 {
--r: 170px;
--accent: oklch(0.72 0.16 270);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: radial-gradient(120% 120% at 50% 40%,#1a1836,#06060f);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__system {
position: relative;
width: calc(var(--r) * 2 + 90px);
height: calc(var(--r) * 2 + 90px);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__center {
width: 120px;
height: 120px;
border-radius: 50%;
overflow: hidden;
z-index: 2;
box-shadow: 0 0 40px -6px var(--accent),0 0 0 4px rgba(255,255,255,.1);
}
.gl-17__center img {
width: 100%;
height: 100%;
object-fit: cover;
}
.gl-17__ring {
position: absolute;
inset: 0;
animation: gl-17-spin 26s linear infinite;
}
.gl-17__system:hover .gl-17__ring,
.gl-17__system:focus-within .gl-17__ring {
animation-play-state: paused;
}
.gl-17__sat {
position: absolute;
top: 50%;
left: 50%;
width: 66px;
height: 66px;
margin: -33px;
border-radius: 50%;
overflow: hidden;
text-decoration: none;
transform: rotate(var(--a)) translate(var(--r)) rotate(calc(-1 * var(--a)));
box-shadow: 0 0 0 3px rgba(255,255,255,.12),0 8px 20px -8px rgba(0,0,0,.6);
animation: gl-17-spin 26s linear infinite reverse;
transition: box-shadow .3s;
}
.gl-17__sat img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform .3s;
}
.gl-17__sat:hover img,
.gl-17__sat:focus-visible img {
transform: scale(1.12);
}
.gl-17__sat:hover,
.gl-17__sat:focus-visible {
box-shadow: 0 0 0 3px var(--accent),0 10px 26px -8px rgba(0,0,0,.7);
}
.gl-17__sat:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
@keyframes gl-17-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 560px) {
.gl-17 {
--r: 120px;
}
.gl-17__sat {
width: 54px;
height: 54px;
margin: -27px;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-17__ring,
.gl-17__sat {
animation: none;
}
}/* No JavaScript — a revolving ring with counter-rotating thumbnails; pauses on hover. */
/* No JavaScript — a revolving ring with counter-rotating thumbnails; pauses on hover. */Here's a working CSS Image Gallerie from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Radial Orbit Image Gallery
Source: https://codefronts.com/snippets/css-image-gallery/css-radial-orbit-image-gallery/
Circular thumbnails arranged evenly around a central hero and slowly revolving around it like planets — a radial orbit gallery for 'our services', galleries and playful hero sections. Pure CSS transforms, pauses on hover.
## HTML
```html
<section class="gl-17" aria-label="Radial orbit gallery">
<div class="gl-17__system">
<div class="gl-17__center"><img src="https://loremflickr.com/300/300/galaxy,space?lock=1700" alt="Featured central image" loading="lazy" decoding="async"></div>
<div class="gl-17__ring">
<a class="gl-17__sat" href="#" style="--a:0deg"><img src="https://loremflickr.com/200/200/planet?lock=1701" alt="Orbit photo 1" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:45deg"><img src="https://loremflickr.com/200/200/moon?lock=1702" alt="Orbit photo 2" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:90deg"><img src="https://loremflickr.com/200/200/star?lock=1703" alt="Orbit photo 3" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:135deg"><img src="https://loremflickr.com/200/200/nebula?lock=1704" alt="Orbit photo 4" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:180deg"><img src="https://loremflickr.com/200/200/comet?lock=1705" alt="Orbit photo 5" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:225deg"><img src="https://loremflickr.com/200/200/sky,night?lock=1706" alt="Orbit photo 6" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:270deg"><img src="https://loremflickr.com/200/200/telescope?lock=1707" alt="Orbit photo 7" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:315deg"><img src="https://loremflickr.com/200/200/astronomy?lock=1708" alt="Orbit photo 8" loading="lazy" decoding="async"></a>
</div>
</div>
</section>
```
## CSS
```css
.gl-17,
.gl-17 *,
.gl-17 *::before,
.gl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-17 ::selection {
background: oklch(0.7 0.16 270);
color: #fff;
}
.gl-17 {
--r: 170px;
--accent: oklch(0.72 0.16 270);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: radial-gradient(120% 120% at 50% 40%,#1a1836,#06060f);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__system {
position: relative;
width: calc(var(--r) * 2 + 90px);
height: calc(var(--r) * 2 + 90px);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__center {
width: 120px;
height: 120px;
border-radius: 50%;
overflow: hidden;
z-index: 2;
box-shadow: 0 0 40px -6px var(--accent),0 0 0 4px rgba(255,255,255,.1);
}
.gl-17__center img {
width: 100%;
height: 100%;
object-fit: cover;
}
.gl-17__ring {
position: absolute;
inset: 0;
animation: gl-17-spin 26s linear infinite;
}
.gl-17__system:hover .gl-17__ring,
.gl-17__system:focus-within .gl-17__ring {
animation-play-state: paused;
}
.gl-17__sat {
position: absolute;
top: 50%;
left: 50%;
width: 66px;
height: 66px;
margin: -33px;
border-radius: 50%;
overflow: hidden;
text-decoration: none;
transform: rotate(var(--a)) translate(var(--r)) rotate(calc(-1 * var(--a)));
box-shadow: 0 0 0 3px rgba(255,255,255,.12),0 8px 20px -8px rgba(0,0,0,.6);
animation: gl-17-spin 26s linear infinite reverse;
transition: box-shadow .3s;
}
.gl-17__sat img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform .3s;
}
.gl-17__sat:hover img,
.gl-17__sat:focus-visible img {
transform: scale(1.12);
}
.gl-17__sat:hover,
.gl-17__sat:focus-visible {
box-shadow: 0 0 0 3px var(--accent),0 10px 26px -8px rgba(0,0,0,.7);
}
.gl-17__sat:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
@keyframes gl-17-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 560px) {
.gl-17 {
--r: 120px;
}
.gl-17__sat {
width: 54px;
height: 54px;
margin: -27px;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-17__ring,
.gl-17__sat {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a revolving ring with counter-rotating thumbnails; pauses on hover. */
```Here's a working CSS Image Gallerie from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Radial Orbit Image Gallery
Source: https://codefronts.com/snippets/css-image-gallery/css-radial-orbit-image-gallery/
Circular thumbnails arranged evenly around a central hero and slowly revolving around it like planets — a radial orbit gallery for 'our services', galleries and playful hero sections. Pure CSS transforms, pauses on hover.
## HTML
```html
<section class="gl-17" aria-label="Radial orbit gallery">
<div class="gl-17__system">
<div class="gl-17__center"><img src="https://loremflickr.com/300/300/galaxy,space?lock=1700" alt="Featured central image" loading="lazy" decoding="async"></div>
<div class="gl-17__ring">
<a class="gl-17__sat" href="#" style="--a:0deg"><img src="https://loremflickr.com/200/200/planet?lock=1701" alt="Orbit photo 1" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:45deg"><img src="https://loremflickr.com/200/200/moon?lock=1702" alt="Orbit photo 2" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:90deg"><img src="https://loremflickr.com/200/200/star?lock=1703" alt="Orbit photo 3" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:135deg"><img src="https://loremflickr.com/200/200/nebula?lock=1704" alt="Orbit photo 4" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:180deg"><img src="https://loremflickr.com/200/200/comet?lock=1705" alt="Orbit photo 5" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:225deg"><img src="https://loremflickr.com/200/200/sky,night?lock=1706" alt="Orbit photo 6" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:270deg"><img src="https://loremflickr.com/200/200/telescope?lock=1707" alt="Orbit photo 7" loading="lazy" decoding="async"></a>
<a class="gl-17__sat" href="#" style="--a:315deg"><img src="https://loremflickr.com/200/200/astronomy?lock=1708" alt="Orbit photo 8" loading="lazy" decoding="async"></a>
</div>
</div>
</section>
```
## CSS
```css
.gl-17,
.gl-17 *,
.gl-17 *::before,
.gl-17 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-17 ::selection {
background: oklch(0.7 0.16 270);
color: #fff;
}
.gl-17 {
--r: 170px;
--accent: oklch(0.72 0.16 270);
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: radial-gradient(120% 120% at 50% 40%,#1a1836,#06060f);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__system {
position: relative;
width: calc(var(--r) * 2 + 90px);
height: calc(var(--r) * 2 + 90px);
display: flex;
align-items: center;
justify-content: center;
}
.gl-17__center {
width: 120px;
height: 120px;
border-radius: 50%;
overflow: hidden;
z-index: 2;
box-shadow: 0 0 40px -6px var(--accent),0 0 0 4px rgba(255,255,255,.1);
}
.gl-17__center img {
width: 100%;
height: 100%;
object-fit: cover;
}
.gl-17__ring {
position: absolute;
inset: 0;
animation: gl-17-spin 26s linear infinite;
}
.gl-17__system:hover .gl-17__ring,
.gl-17__system:focus-within .gl-17__ring {
animation-play-state: paused;
}
.gl-17__sat {
position: absolute;
top: 50%;
left: 50%;
width: 66px;
height: 66px;
margin: -33px;
border-radius: 50%;
overflow: hidden;
text-decoration: none;
transform: rotate(var(--a)) translate(var(--r)) rotate(calc(-1 * var(--a)));
box-shadow: 0 0 0 3px rgba(255,255,255,.12),0 8px 20px -8px rgba(0,0,0,.6);
animation: gl-17-spin 26s linear infinite reverse;
transition: box-shadow .3s;
}
.gl-17__sat img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform .3s;
}
.gl-17__sat:hover img,
.gl-17__sat:focus-visible img {
transform: scale(1.12);
}
.gl-17__sat:hover,
.gl-17__sat:focus-visible {
box-shadow: 0 0 0 3px var(--accent),0 10px 26px -8px rgba(0,0,0,.7);
}
.gl-17__sat:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
@keyframes gl-17-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 560px) {
.gl-17 {
--r: 120px;
}
.gl-17__sat {
width: 54px;
height: 54px;
margin: -27px;
}
}
@media (prefers-reduced-motion: reduce) {
.gl-17__ring,
.gl-17__sat {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a revolving ring with counter-rotating thumbnails; pauses on hover. */
```How this works
Each orbiting item is absolutely centred, then pushed out along a radius with transform: rotate(var(--a)) translate(radius) rotate(calc(-1 * var(--a))) — the first rotation sets its angle on the ring, the translate moves it out, and the counter-rotation keeps the thumbnail upright. A wrapping ring element runs a linear @keyframes spin, and each thumbnail runs the same animation in reverse so images never appear to tumble.
The orbit pauses on :hover/:focus-within so users can aim at a thumbnail, and the whole animation is disabled under prefers-reduced-motion.
Make it yours
- Add/remove satellites by changing the per-item
--aangles (360 ÷ count). - Grow the orbit by editing the translate radius.
- Speed or reverse the revolution via the spin
animation-duration/direction. - Swap the centre hero for a logo, title or call-to-action.
Gotchas — read before shipping
- Counter-rotate each thumbnail (and reverse its animation) or the images spin end-over-end as the ring turns.
- Pause on hover/focus so the moving targets are actually clickable.
- Honour
prefers-reduced-motion— a constantly revolving ring is a common motion-sensitivity trigger.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 43+.
CSS transforms + keyframes — supported everywhere; the counter-spin technique needs no modern features.