Radial / Circular Carousel — HTML CSS JS Snippet

Radial / Circular Carousel · Carousels · Plain HTML, CSS & JS · Live preview

What's included

Features

True circular arrangement — every item's position is computed from sin/cos, not hardcoded coordinates
One rotation variable drives the entire ring — change it once and every item repositions together
Front item detected by angle normalization, no separate active-index variable to keep in sync
Click any item to rotate it directly to the front, choosing the shortest rotation automatically
Active item scales up and glows via a CSS custom property layered on top of its position transform
Item count is read from the DOM — add or remove an .rc-item and the angle spacing recalculates itself

About this UI Snippet

Radial / Circular Carousel — Trigonometric Positioning Around a Ring

Screenshot of the Radial / Circular Carousel snippet rendered live

Instead of sliding items left-to-right, this carousel arranges them around the circumference of a circle and *rotates the whole ring* so a different item ends up at the "front" (the top position, nearest the label). Every item's position comes from one trigonometry call: x = sin(angle) * radius, y = -cos(angle) * radius, where angle is that item's fixed slot around the circle plus the ring's current rotation. Change rotation by one step and every item recomputes its (x, y) simultaneously — the whole ring visibly turns.

Detecting "which one is at the front" without tracking an index

Rather than keeping a separate "active index" variable, the front item is *derived*: after computing each item's angle, the code normalizes it to 0–360° and checks whether it falls within a small window around 0° (the top of the circle). This means clicking any item can work out exactly how many steps to rotate to bring *that* item to the front — normalize its current angle, and rotate by the negative of that amount — without a lookup table matching items to rotation offsets.

Scale and shadow, not just position

The active item additionally gets scale(1.15) and a colored glow, layered on top of its positional transform via a CSS custom property (--pos) that holds the translate() call — so the active state can add a scale without overwriting the position transform underneath it.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSSix items appear arranged around a circle, with the top one highlighted and labeled.
  2. 2
    Click the arrowsThe whole ring rotates one slot, bringing the next item to the top.
  3. 3
    Click any itemThe ring rotates directly to bring that specific item to the front — shortest path either direction.
  4. 4
    Use arrow keysLeft/Right rotates the ring one step at a time without the mouse.
  5. 5
    Add a seventh itemAdd one more .rc-item and its label — the angle math (360 / count) adjusts automatically.

Real-world uses

Common Use Cases

Category or icon pickers
A playful way to browse a small set of options — product categories, filters, or team member avatars.
Feature highlight wheels
Rotate through a handful of key features or stats on a landing page with genuine visual interest.
Skill or tag showcases
Arrange skills, technologies, or tags in a ring for a portfolio's about section.
Learning trigonometric layout
One of the clearest practical demonstrations of using sin/cos to place elements on a circle.

Got questions?

Frequently Asked Questions

Update the radius variable in the JS (in pixels) and, if needed, the .rc-ring width/height in the CSS to comfortably fit items at that radius plus their own size.

Because both the position (translate, changes every rotation) and the active-state emphasis (scale, changes only when active) need to combine into one transform. Storing the position in --pos lets the .rc-active rule append scale() without JavaScript needing to know or duplicate the current position value.

Yes — multiply the x calculation by a different radius than the y calculation (e.g. x = sin(angle) * radiusX, y = -cos(angle) * radiusY) to flatten the ring into an ellipse.

Call rotate(1) on a setInterval at your desired pace, and clear the interval on any manual interaction (click or keydown) so autoplay doesn't fight a user actively browsing.

The prev/next controls are real buttons with aria-labels, each item is independently clickable, and Left/Right arrow keys provide full keyboard control without a mouse.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to walk through exactly how the sin/cos calculation places each item and why -cos is used for the y-axis specifically (rather than plain cos) to make angle 0 land at the top of the circle instead of the right side. It's also worth asking the assistant to add momentum-based dragging (rotate the ring proportionally as a user drags, then snap to the nearest item on release) instead of the current click-to-rotate-only interaction, or to make the ring tilt in 3D via a perspective wrapper for a more dramatic carousel effect.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a radial (circular) carousel in plain HTML, CSS, and vanilla JavaScript where items are arranged around the circumference of a ring and the whole ring rotates to bring a different item to the front — no library.

Requirements:
- A set of item elements absolutely positioned inside a container, where each item's (x, y) position is computed with trigonometry (sine and cosine) from that item's fixed angular slot around the circle (360 degrees divided by the total item count) plus a single shared "rotation" variable, not from hardcoded per-item coordinates.
- Rotating the ring must mean changing only that one shared rotation variable and then recomputing every item's position from it in one pass — a single state change must visibly move every item simultaneously around the circle.
- The item currently nearest a fixed reference position (the top of the circle) must be automatically detected by normalizing its computed angle into the 0-360 degree range and checking whether it falls within a small tolerance window of zero, without maintaining a separate manually-tracked "active index" variable.
- The detected front item must receive a distinct visual treatment (a larger scale and a colored glow) layered on top of its positional transform, not replacing it — items must keep rotating around the circle in their scaled-up state too.
- Clicking any individual item must rotate the ring the shortest direction necessary to bring that specific item to the front position.
- Provide rotate-left and rotate-right buttons that step the ring by one item's worth of angle, plus Left/Right arrow key support performing the same action.
- The angular spacing between items must be calculated from the actual number of item elements present in the DOM, so adding or removing an item automatically redistributes the spacing.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="rc-stage">
  <div class="rc-ring" id="rcRing">
    <div class="rc-item" data-i="0"><span>🎧</span></div>
    <div class="rc-item" data-i="1"><span>📷</span></div>
    <div class="rc-item" data-i="2"><span>⌚</span></div>
    <div class="rc-item" data-i="3"><span>🎮</span></div>
    <div class="rc-item" data-i="4"><span>🔊</span></div>
    <div class="rc-item" data-i="5"><span>💻</span></div>
  </div>
  <div class="rc-label" id="rcLabel">Headphones</div>
  <div class="rc-controls">
    <button class="rc-btn" id="rcPrev" aria-label="Rotate left">‹</button>
    <button class="rc-btn" id="rcNext" aria-label="Rotate right">›</button>
  </div>
</div>