Orbiting Skills

Animated skill badges that orbit a center element. On desktop the ring follows the cursor on hover; on mobile it's always visible.

Made by Léo
Open in

With icons

Text only

demo-orbiting-skills.tsx
"use client";

import type { ReactNode } from "react";

import {
  Braces,
  Database,
  Figma,
  Github,
  Globe,
  Layers,
  Palette,
  Wind,
} from "lucide-react";

import {
  OrbitingSkills,
  type OrbitSkillItem,
  type OrbitingSkillsProps,
} from "@/components/unlumen-ui/orbiting-skills";

const ICON_SKILLS: OrbitSkillItem[] = [
  { label: "React", icon: <Globe className="size-4" /> },
  { label: "TypeScript", icon: <Braces className="size-4" /> },
  { label: "Tailwind", icon: <Wind className="size-4" /> },
  { label: "Figma", icon: <Figma className="size-4" /> },
  { label: "GitHub", icon: <Github className="size-4" /> },
  { label: "Database", icon: <Database className="size-4" /> },
];

const TEXT_SKILLS: OrbitSkillItem[] = [
  { label: "Design" },
  { label: "Motion" },
  { label: "UX" },
  { label: "CSS" },
  { label: "a11y" },
  { label: "Perf" },
];

type OrbitingSkillsDemoProps = Pick<
  OrbitingSkillsProps,
  "radius" | "duration" | "showPath" | "followCursor"
>;

function Avatar({ children }: { children: ReactNode }) {
  return (
    <div className="flex size-20 items-center justify-center rounded-full border border-border bg-background shadow-md">
      {children}
    </div>
  );
}

export const OrbitingSkillsDemo = ({
  radius = 96,
  duration = 18,
  showPath = true,
  followCursor = true,
}: OrbitingSkillsDemoProps) => {
  return (
    <div className="flex min-h-[400px] flex-wrap items-center justify-center gap-24 p-16">
      {/* ── With Lucide icons ── */}
      <div className="flex flex-col items-center gap-4">
        <OrbitingSkills
          items={ICON_SKILLS}
          radius={radius}
          duration={duration}
          showPath={showPath}
          followCursor={followCursor}
        >
          <Avatar>
            <Layers className="size-8 text-foreground/70" />
          </Avatar>
        </OrbitingSkills>
        <p className="text-xs text-muted-foreground">With icons</p>
      </div>

      {/* ── Text only ── */}
      <div className="flex flex-col items-center gap-4">
        <OrbitingSkills
          items={TEXT_SKILLS}
          radius={radius}
          duration={duration}
          showPath={showPath}
          followCursor={followCursor}
        >
          <Avatar>
            <Palette className="size-8 text-foreground/70" />
          </Avatar>
        </OrbitingSkills>
        <p className="text-xs text-muted-foreground">Text only</p>
      </div>
    </div>
  );
};

Installation

This component is part of unlumen Pro

Includes advanced motion, patterns, and premium support.

Get Started
Maybe later
Thanks 🙏

Usage

import {
  OrbitingSkills,
  type OrbitSkillItem,
} from "@/components/unlumen-ui/orbiting-skills";

const SKILLS: OrbitSkillItem[] = [
  { label: "React", icon: <span>⚛️</span> },
  { label: "TypeScript", icon: <span>🔷</span> },
  { label: "Tailwind", icon: <span>🌊</span> },
];

<OrbitingSkills items={SKILLS} radius={100}>
  <div className="size-20 rounded-full bg-muted" />
</OrbitingSkills>;

Props

PropTypeDefaultDescription
itemsOrbitSkillItem[]Skill items (label + optional icon) to orbit
radiusnumber88Orbit radius in pixels
durationnumber18Duration of one full orbit in seconds
showPathbooleantrueWhether to render the circular orbit ring
followCursorbooleantrueOn desktop: follow the cursor on hover. Set false to always stay centered
childrenReactNodeCenter element (avatar, logo, …)
classNamestringExtra class names on the wrapper

OrbitSkillItem

type OrbitSkillItem = {
  label: string; // Text displayed under the icon
  icon?: ReactNode; // Any icon element
};

Behavior

  • Desktop (followCursor={true}) — the orbit ring and badges appear on hover and smoothly track the cursor position via a spring animation.
  • Desktop (followCursor={false}) — the orbit is always visible and centered on the children element.
  • Mobile — the orbit is always on and centered, with a spring entrance animation when the component scrolls into view. Badges are slightly smaller to avoid overflow.
  • All badges counter-rotate so their labels stay upright regardless of the orbit angle.

Built by Léo from Unlumen :3

Last updated: 3/15/2026