Items that travel around a centre on one or more circular paths, such as the tools a product works with. The items are your own children and the orbit is plain CSS.
Optional, and kept apart.
Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.
import { OrbitingCircles, SparkleIcon, HomeIcon, ChartIcon, BellIcon, SettingsIcon } from "@rdloom/react";
const chip = "flex size-9 items-center justify-center rounded-full border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)] text-[var(--rd-color-text-default)]";
export default function OrbitingCirclesBasicExample() {
return (
<div className="flex w-full justify-center py-4">
<OrbitingCircles
radius={110}
iconSize={36}
center={
<span className="flex size-14 items-center justify-center rounded-full bg-[var(--rd-color-action-primary)] text-[var(--rd-color-action-on-primary)]">
<SparkleIcon className="size-6" />
</span>
}
>
<span className={chip}><HomeIcon /></span>
<span className={chip}><ChartIcon /></span>
<span className={chip}><BellIcon /></span>
<span className={chip}><SettingsIcon /></span>
</OrbitingCircles>
</div>
);
}Installation
npx rdloom add orbiting-circlesCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.
It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/orbiting-circles.jsonWorks, but without upgrade tracking, and you import the motion CSS yourself.
Usage
import { OrbitingCircles, SparkleIcon, HomeIcon, ChartIcon, BellIcon, SettingsIcon } from "@rdloom/react";
<div className="flex w-full justify-center py-4">
<OrbitingCircles
radius={110}
iconSize={36}
center={
<span className="flex size-14 items-center justify-center rounded-full bg-[var(--rd-color-action-primary)] text-[var(--rd-color-action-on-primary)]">
<SparkleIcon className="size-6" />
</span>
}
>
<span className={chip}><HomeIcon /></span>
<span className={chip}><ChartIcon /></span>
<span className={chip}><BellIcon /></span>
<span className={chip}><SettingsIcon /></span>
</OrbitingCircles>
</div>Two rings
- A
- B
- C
- D
- E
- F
- G
import { OrbitingCircles } from "@rdloom/react";
const names = ["A", "B", "C", "D", "E", "F", "G"];
export default function OrbitingCirclesTwoRingsExample() {
return (
<div className="flex w-full justify-center py-4">
<OrbitingCircles
radius={130}
rings={2}
duration={30}
iconSize={34}
label="Sample items on two paths"
center={<span className="text-sm font-medium text-[var(--rd-color-text-default)]">Core</span>}
>
{names.map((name) => (
<span
key={name}
className="flex size-[34px] items-center justify-center rounded-full border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-subtle)] text-sm text-[var(--rd-color-text-default)]"
>
{name}
</span>
))}
</OrbitingCircles>
</div>
);
}With pause on hover
import { OrbitingCircles } from "@rdloom/react";
const labels = ["Docs", "Chat", "Plan", "Ship", "Test"];
export default function OrbitingCirclesWithPauseOnHoverExample() {
return (
<div className="flex w-full justify-center py-4">
<OrbitingCircles radius={115} iconSize={52} pauseOnHover reverse showPaths label="Sample links">
{labels.map((label) => (
<a
key={label}
href="#"
className="flex size-[52px] items-center justify-center rounded-full border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-default)] text-xs text-[var(--rd-color-text-default)] outline-none focus-visible:ring-2 focus-visible:ring-[var(--rd-color-focus-ring)]"
>
{label}
</a>
))}
</OrbitingCircles>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
childrenrequiredThe items that orbit. Each child becomes one list item on a path. | node | none |
centerContent placed in the middle of the paths, such as a logo or a heading. | node | none |
radiusRadius of the outer path in pixels. | number | 120 |
ringsHow many paths. The children are shared out between them, and neighbouring paths turn in opposite directions. | number | 1 |
durationSeconds for one full turn of the outer path. Inner paths turn faster. | number | 24 |
iconSizeWidth and height of each item in pixels. | number | 36 |
reverseTurns the outer path the other way. | boolean | false |
showPathsDraws each path as a thin circle. | boolean | true |
pauseOnHoverPauses while the pointer is over the orbit or focus is inside it. | boolean | false |
labelAccessible name of the list of items. | string | "Orbiting items" |
isPausedStops the orbit. Moving content that lasts more than five seconds should be stoppable. | boolean | false |
Accessibility
Role list, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab: moves through any focusable item, in the order the children were given
Screen readers announce
- Announced as a list with the given label; each item is read in order as ordinary content
What your code must do
- The items are real children in a list, in a fixed DOM order, so the order never changes with the motion
- The drawn paths are decoration: hidden from assistive technology and never take pointer events
- The orbit is CSS only and starts after the page paints; the server HTML already shows the items spaced evenly
- It can be stopped: isPaused, an optional pause on hover and focus, and the rdm-still class (WCAG 2.2.2)
- Visitors who ask for less motion (prefers-reduced-motion) get the items evenly spaced and still
- Items stay upright while they travel, so text and icons stay readable
- In forced-colors (high contrast) mode the paths use a system colour
- Does not flash: nothing changes more than three times in a second (WCAG 2.3.1)
Guidelines
Use it when
- A hero section that shows the tools or integrations a product connects to
- A small decorative diagram with a logo in the middle
Avoid it when
- Essential links that people must be able to hit easily, because moving targets are hard to use
- More than about eight items on one path
Don't
- Putting the only way to reach a page on an orbiting item without pause on hover
- A very short duration, which makes the items hard to follow
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-border-default