Reticle@etched/reticle 0.0.0

Tabs

A row of tabs that switches views, with their TabsPanels or with the view the app renders.

Kumo source
import { Tabs, type TabsItem } from "@etched/reticle/tabs";

const sections: TabsItem[] = [
  { value: "overview", label: "Overview" },
  { value: "runs", label: "Runs" },
  { value: "owners", label: "Owners" },
];

export function Preview() {
  return <Tabs aria-label="Chip sections" tabs={sections} />;
}

Installation

import { Tabs, TabsNav, TabsPanel } from "@etched/reticle/tabs";

Examples

Segmented and underline

import { Tabs, type TabsItem } from "@etched/reticle/tabs";

const sections: TabsItem[] = [
  { value: "overview", label: "Overview" },
  { value: "runs", label: "Runs" },
  { value: "owners", label: "Owners" },
];

export function SegmentedAndUnderline() {
  return (
    <div className="grid">
      <Tabs aria-label="Chip sections" tabs={sections} />
      <Tabs aria-label="Chip sections, underline" variant="underline" tabs={sections} selectedValue="runs" />
    </div>
  );
}

Small

import { Tabs, type TabsItem } from "@etched/reticle/tabs";

const sections: TabsItem[] = [
  { value: "overview", label: "Overview" },
  { value: "runs", label: "Runs" },
  { value: "owners", label: "Owners" },
];

export function Small() {
  return (
    <div className="grid">
      <Tabs aria-label="Chip sections, small" size="sm" tabs={sections} />
      <Tabs aria-label="Chip sections, small underline" size="sm" variant="underline" tabs={sections} />
    </div>
  );
}

Controlled view switch

Showing the pipeline view.

import { useState } from "react";
import { Tabs, type TabsItem } from "@etched/reticle/tabs";

const views: TabsItem[] = [
  { value: "pipeline", label: "Pipeline" },
  { value: "summary", label: "Summary" },
];

export function ControlledViewSwitch() {
  const [view, setView] = useState("pipeline");

  return (
    <div className="grid">
      <Tabs aria-label="Role views" tabs={views} value={view} onValueChange={setView} />
      <p>Showing the {view} view.</p>
    </div>
  );
}

A disabled tab

import { Tabs, type TabsItem } from "@etched/reticle/tabs";

const settings: TabsItem[] = [
  { value: "users", label: "Users" },
  { value: "notifications", label: "Notifications" },
  { value: "data", label: "Data quality", disabled: true },
];

export function ADisabledTab() {
  return <Tabs aria-label="Settings" tabs={settings} />;
}

Overflow

Tabs that do not fit scroll sideways.

import { Tabs, type TabsItem } from "@etched/reticle/tabs";

const views: TabsItem[] = [
  { value: "pipeline", label: "Pipeline" },
  { value: "summary", label: "Summary" },
];

const many: TabsItem[] = ["Overview", "Runs", "Thermal", "Power", "Logs", "Owners", "History", "Alerts", "Settings"].map((label) => ({
  value: label.toLowerCase(),
  label,
}));

export function Overflow() {
  return (
    <div className="grid">
      <Tabs aria-label="Chip views" tabs={many} />
      <Tabs aria-label="Chip views, underline" variant="underline" tabs={many} />
    </div>
  );
}

Panels

TabsPanels inside Tabs are linked to their tabs and stay mounted while hidden: type a note, switch tabs, and come back.

import { InputArea } from "@etched/reticle/input";
import { Tabs, TabsPanel } from "@etched/reticle/tabs";

export function Panels() {
  return (
    <Tabs aria-label="Candidate workspace" variant="underline" tabs={[{ value: "notes", label: "Notes" }, { value: "activity", label: "Activity" }]}>
      <TabsPanel value="notes" className="grid">
        <InputArea aria-label="Note" placeholder="Type a note, then switch tabs" rows={2} />
      </TabsPanel>
      <TabsPanel value="activity">
        <p>Oct 14 · Moved to Interviewing by Sam Lee</p>
      </TabsPanel>
    </Tabs>
  );
}

API reference

TabsNav

PropTypeDefaultDescription
links *TabsLink[]–
aria-label *string–Names the navigation, for example "Settings sections".
variant"segmented" | "underline""segmented"
size"base" | "sm""base"
classNamestring–

TabsPanel

PropTypeDefaultDescription
value *string–The value of its tab.
children *ReactNode–
classNamestring–

Tabs

PropTypeDefaultDescription
tabs *TabsItem[]–
valuestring–Selected tab, controlled.
selectedValuestringthe first tabFirst selected tab, uncontrolled.
onValueChange(value: string) => void–
activateOnFocusbooleanfalseSelect a tab when the arrow keys move focus to it. Otherwise Enter or Space selects it.
variant"segmented" | "underline""segmented"
size"base" | "sm""base"
aria-labelstring–Names the tab list, for example "Role views".
childrenReactNode–TabsPanels, one per tab. Without them, the app renders the selected view itself.
classNamestring–Class on the root, for layout such as width.