Live preview · v1.0.1
Start with a thoughtful foundation.
Live preview · v1.0.1
Free · MIT license

Orbit Tabs

A calm, rounded home for your navigation.

ReactTypeScriptv1.0.1InteractiveKeyboard accessible
Styling
inline CSS
Delivery
Reusable React component
Download v1.0.1

Add it to your project

pnpm dlx shadcn@latest add "https://designmyui.dev/r/orbit-tabs?version=1.0.1"

Uses React and inline CSS. No additional runtime libraries. Installation guide ↗

♡ Save to favorites

Free source is public. Sign in to keep this version in your library.

Source & usage

The source below is checked against this release’s published SHA-256 hash before delivery. Copy the file or use the registry.

/**
 * Copyright (c) 2026 DesignMyUI
 * SPDX-License-Identifier: MIT
 */
"use client";
import { useId, useRef, useState } from "react";
export function OrbitTabs({
  items = [
    { label: "Design", content: "Start with a thoughtful foundation." },
    { label: "Develop", content: "Make each detail your own." },
    { label: "Ship", content: "Bring your next interface to life." },
  ],
}: {
  items?: { label: string; content: string }[];
}) {
  const [active, setActive] = useState(0);
  const id = useId();
  const refs = useRef<(HTMLButtonElement | null)[]>([]);
  return (
    <div style={{ width: "100%", maxWidth: 310, textAlign: "center" }}>
      <div
        role="tablist"
        aria-label="Building steps"
        style={{
          display: "flex",
          gap: 4,
          padding: 5,
          border: "1px solid #e8e5ef",
          borderRadius: 99,
          background: "#ffffffc9",
          boxShadow: "0 4px 10px #2d254508",
        }}
      >
        {items.map((item, i) => (
          <button
            ref={(el) => {
              refs.current[i] = el;
            }}
            key={item.label}
            role="tab"
            id={`${id}-tab-${i}`}
            aria-controls={`${id}-panel`}
            aria-selected={i === active}
            tabIndex={i === active ? 0 : -1}
            onClick={() => setActive(i)}
            onKeyDown={(e) => {
              if (["ArrowRight", "ArrowLeft", "Home", "End"].includes(e.key)) {
                e.preventDefault();
                const next =
                  e.key === "Home"
                    ? 0
                    : e.key === "End"
                      ? items.length - 1
                      : (active +
                          (e.key === "ArrowRight" ? 1 : -1) +
                          items.length) %
                        items.length;
                setActive(next);
                refs.current[next]?.focus();
              }
            }}
            style={{
              flex: 1,
              padding: "10px 12px",
              fontSize: 12,
              fontWeight: 500,
              border: 0,
              borderRadius: 99,
              background: i === active ? "#292735" : "transparent",
              color: i === active ? "white" : "#77747f",
              cursor: "pointer",
            }}
          >
            {item.label}
          </button>
        ))}
      </div>
      <div
        role="tabpanel"
        id={`${id}-panel`}
        aria-labelledby={`${id}-tab-${active}`}
        style={{ fontSize: 12, color: "#75697f", marginTop: 20, minHeight: 36 }}
      >
        {items[active]?.content}
      </div>
    </div>
  );
}

Use it in your interface

Interactive handlers belong in a React client component. Replace the content and props with your own.

import { OrbitTabs } from "./orbit-tabs";

<OrbitTabs items={[
  { label: "Overview", content: "Your overview content." },
  { label: "Details", content: "Your detail content." },
]} />

Published versions

Each download keeps its original source. Choose a release to inspect and download that exact version.

v1.0.0

Initial published source.

Published Oct 11, 2026