Lumenv0.2

Components/Navigation

Tabs

Switch views, without the noise. An underline marks the active tab.

Default

A summary of the project, at a glance.

Props

PropTypeDefaultDescription
tabs{ label: string; content: ReactNode }[]Tab definitions
defaultTabnumber0Initially active index
onChange(index: number) => voidFires on tab change

Installation

Paste the source into components/tabs.tsx. No dependencies required.

'use client';

import { useState } from 'react';
import type { ReactNode } from 'react';

interface Tab {
  label: string;
  content: ReactNode;
}

interface TabsProps {
  tabs: Tab[];
  defaultTab?: number;
  onChange?: (index: number) => void;
}

export function Tabs({ tabs, defaultTab = 0, onChange }: TabsProps) {
  const [active, setActive] = useState(defaultTab);

  const select = (i: number) => {
    setActive(i);
    onChange?.(i);
  };

  return (
    <div style={{ width: '100%' }}>
      <div
        role="tablist"
        style={{
          display: 'flex',
          gap: '24px',
          borderBottom: '0.5px solid var(--color-border-soft)',
        }}
      >
        {tabs.map((tab, i) => {
          const isActive = i === active;
          return (
            <button
              key={tab.label}
              role="tab"
              aria-selected={isActive}
              onClick={() => select(i)}
              style={{
                background: 'none',
                border: 'none',
                padding: '0 0 10px',
                cursor: 'pointer',
                fontFamily: 'var(--font-sans)',
                fontSize: '14px',
                fontWeight: isActive ? 540 : 420,
                color: isActive ? 'var(--color-text)' : 'var(--color-text-muted)',
                borderBottom: isActive
                  ? '0.5px solid var(--color-text)'
                  : '0.5px solid transparent',
                marginBottom: '-0.5px',
              }}
            >
              {tab.label}
            </button>
          );
        })}
      </div>
      <div role="tabpanel" style={{ paddingTop: '20px' }}>
        {tabs[active]?.content}
      </div>
    </div>
  );
}

Built from Lumen tokens. Edit the tokens