Skip to content
WANCRAFT

H.323D & Experimental

Orbital Skills

An interactive orbital diagram of tools and skills with upright labels and a selectable centre.

ReactMotionCSSTailwindintermediate

Overview

Orbital Skills arranges your tools on concentric rings that rotate at Kepler-ish speeds — outer orbits move more slowly — while every label counter-rotates to stay upright. Each ring is driven by a single CSS variable written from a requestAnimationFrame loop, so React never re-renders while it turns, and the loop pauses on hover, on focus, offscreen and in background tabs. Nodes are real toggle buttons: click or press Enter to select one and its details appear in the centre, announced politely. The whole diagram scales with its container through container query units.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/orbital-skills.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/orbital-skills.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/orbital-skills.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
1 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { OrbitalSkills, type OrbitalNode } from "@/components/ui/orbital-skills";

export function OrbitalSkillsExample() {
  const nodes: OrbitalNode[] = [
    { id: "react", label: "React", orbit: 1, description: "Components and hooks." },
    { id: "ts", label: "TypeScript", orbit: 1, description: "Strict types end to end." },
    { id: "design", label: "Design", orbit: 2, description: "Systems and detail." },
    { id: "motion", label: "Motion", orbit: 2, description: "Springs and gestures." },
    { id: "node", label: "Node", orbit: 3, description: "APIs and tooling." },
    { id: "postgres", label: "Postgres", orbit: 3, description: "Relational data." },
  ];

  return (
    <div className="w-full max-w-md">
      <OrbitalSkills nodes={nodes} />
    </div>
  );
}

03Props

PropTypeDefaultDescription
nodes{ id: string; label: string; orbit: number; icon?: ReactNode; description?: string }[]—Nodes; equal orbit values share a ring (lower = inner).
titlestring"Toolkit"Centre heading while nothing is selected; also names the node group.
subtitlestring"Select a node"Centre hint while nothing is selected.
speednumber10Innermost orbit speed in °/s; outer orbits are slower. 0 stops rotation.
direction"alternate" | "clockwise" | "counterclockwise""alternate"Rotation direction; alternate flips every other ring.
pauseOnHoverbooleantruePause while hovered (mouse) or while focus is inside.
showLabelsbooleantrueVisible text labels; when false, labels become aria-label/title.
showRingsbooleantrueDashed orbit paths with a marker dot.
accentstring"#FF7448"Selection, markers and focus ring.
defaultSelectedIdstring | nullnullInitially selected node.
onSelect(node: OrbitalNode | null) => void—Called when the selection changes.

04Features

  • 01Rings auto-assigned from orbit values; nodes spread evenly
  • 02Counter-rotated, always-upright labels
  • 03One CSS variable per ring per frame — no React renders while rotating
  • 04Pauses on hover, focus, offscreen and in hidden tabs
  • 05Scales with its container via container query units
motion · continuousmotion · tweeninput · clickinput · hoverinput · keyboardinput · focus

05Accessibility

  • Nodes are native toggle buttons (aria-pressed) inside a labelled group; Enter/Space selects, Escape clears.
  • Arrow keys move focus between nodes in orbit order.
  • The centre details update inside a polite, atomic live region.
  • Rotation pauses while focus is inside, so keyboard targets stay still; with prefers-reduced-motion the layout is static.