Segment Bar
Equal-width horizontal segments in a single row. Pass a data array of color classes with optional per-segment tooltips and aria labels. Segments animate in on mount, lift on hover and focus, and respect reduced motion.
Default
24-hour API health strip with status legend, weekly activity, and portfolio allocation bars.
API health (24h)
checkout-api
HealthyDegradedDownMaintenanceNo data
Weekly activity
Portfolio allocation
examples.tsx
1import { cn } from "@/lib/utils";2import { SegmentBar, type SegmentBarItem } from "@/registry/ui/segment-bar";34type ApiHealthStatus = "healthy" | "degraded" | "down" | "maintenance" | "no-data";56const apiHealthStyles: Record<7 ApiHealthStatus,8 { color: string; label: string }9> = {10 healthy: { color: "bg-emerald-500/85", label: "Healthy" },11 degraded: { color: "bg-amber-500/85", label: "Degraded" },12 down: { color: "bg-rose-500/85", label: "Down" },13 maintenance: { color: "bg-sky-500/50", label: "Maintenance" },14 "no-data": { color: "bg-muted", label: "No data" },15};1617function formatHourLabel(hour: number): string {18 const period = hour >= 12 ? "PM" : "AM";19 const hour12 = hour % 12 === 0 ? 12 : hour % 12;20 return `${hour12}:00 ${period}`;21}2223/** Sample hourly API health for a checkout service (24 segments). */24const hourlyApiHealth: Array<{25 hour: number;26 status: ApiHealthStatus;27 detail?: string;28}> = [29 { hour: 0, status: "healthy", detail: "38ms p99 · 100% success" },30 { hour: 1, status: "healthy", detail: "35ms p99 · 100% success" },31 { hour: 2, status: "maintenance", detail: "DB migration window" },32 { hour: 3, status: "maintenance", detail: "DB migration window" },33 { hour: 4, status: "healthy", detail: "41ms p99 · 100% success" },34 { hour: 5, status: "healthy", detail: "44ms p99 · 100% success" },35 { hour: 6, status: "healthy", detail: "52ms p99 · 99.99% success" },36 { hour: 7, status: "healthy", detail: "61ms p99 · 99.98% success" },37 { hour: 8, status: "healthy", detail: "74ms p99 · 99.97% success" },38 { hour: 9, status: "degraded", detail: "186ms p99 · 1.4% 5xx" },39 { hour: 10, status: "healthy", detail: "68ms p99 · 99.96% success" },40 { hour: 11, status: "healthy", detail: "59ms p99 · 99.98% success" },41 { hour: 12, status: "degraded", detail: "312ms p99 · 2.8% 5xx" },42 { hour: 13, status: "degraded", detail: "278ms p99 · 1.9% 5xx" },43 { hour: 14, status: "healthy", detail: "71ms p99 · 99.97% success" },44 { hour: 15, status: "degraded", detail: "deploy canary · 240ms p99" },45 { hour: 16, status: "healthy", detail: "63ms p99 · 99.98% success" },46 { hour: 17, status: "degraded", detail: "412ms p99 · queue backlog" },47 { hour: 18, status: "down", detail: "incident #4821 · payment gateway" },48 { hour: 19, status: "degraded", detail: "recovery · 520ms p99" },49 { hour: 20, status: "healthy", detail: "58ms p99 · 99.95% success" },50 { hour: 21, status: "healthy", detail: "49ms p99 · 99.98% success" },51 { hour: 22, status: "healthy", detail: "42ms p99 · 100% success" },52 { hour: 23, status: "no-data", detail: "metrics delayed" },53];5455const dailyApiHealth: SegmentBarItem[] = hourlyApiHealth.map(56 ({ hour, status, detail }) => {57 const { color, label } = apiHealthStyles[status];58 const time = formatHourLabel(hour);59 const suffix = detail ? ` · ${detail}` : "";6061 return {62 id: `hour-${hour}`,63 color,64 tooltip: `${time} - ${label}${suffix}`,65 ariaLabel: `${time}, ${label}${suffix}`,66 };67 },68);6970const weeklyActivity: SegmentBarItem[] = [71 { id: "mon", color: "bg-emerald-500/90", tooltip: "Mon - 4 sessions" },72 { id: "tue", color: "bg-emerald-500/70", tooltip: "Tue - 3 sessions" },73 { id: "wed", color: "bg-emerald-500/50", tooltip: "Wed - 2 sessions" },74 { id: "thu", color: "bg-emerald-500/90", tooltip: "Thu - 4 sessions" },75 { id: "fri", color: "bg-emerald-500/30", tooltip: "Fri - 1 session" },76 {77 id: "sat",78 color: "bg-muted",79 tooltip: "Sat - rest day",80 ariaLabel: "Saturday rest day",81 },82 { id: "sun", color: "bg-emerald-500/60", tooltip: "Sun - 2 sessions" },83];8485const portfolioAllocation: SegmentBarItem[] = [86 { id: "equities", color: "bg-sky-500/90", tooltip: "Equities - 42%" },87 { id: "bonds", color: "bg-indigo-500/80", tooltip: "Bonds - 28%" },88 { id: "cash", color: "bg-amber-500/70", tooltip: "Cash - 18%" },89 {90 id: "alternatives",91 color: "bg-rose-500/80",92 tooltip: "Alternatives - 12%",93 },94];9596export function SegmentBarExamples() {97 return (98 <div className="mx-auto flex w-full max-w-xl flex-col gap-8 py-6">99 <div className="space-y-2">100 <div className="flex items-baseline justify-between gap-2">101 <p className="text-sm text-muted-foreground">API health (24h)</p>102 <p className="text-xs text-muted-foreground">checkout-api</p>103 </div>104 <SegmentBar data={dailyApiHealth} defaultBackgroundColor="bg-muted" />105 <div className="flex flex-wrap gap-x-4 gap-y-1 pt-1 text-xs text-muted-foreground">106 {(107 Object.entries(apiHealthStyles) as Array<108 [ApiHealthStatus, { color: string; label: string }]109 >110 ).map(([status, { color, label }]) => (111 <span key={status} className="inline-flex items-center gap-1.5">112 <span className={cn("size-2 rounded-[1px]", color)} aria-hidden />113 {label}114 </span>115 ))}116 </div>117 </div>118119 <div className="space-y-2">120 <p className="text-sm text-muted-foreground">Weekly activity</p>121 <SegmentBar data={weeklyActivity} />122 </div>123124 <div className="space-y-2">125 <p className="text-sm text-muted-foreground">Portfolio allocation</p>126 <SegmentBar127 data={portfolioAllocation}128 defaultBackgroundColor="bg-muted"129 />130 </div>131 </div>132 );133}
Installation & source
Install via the shadcn CLI or copy the registry files manually.
bash
npx shadcn@latest add @tt-ui/segment-bar
Props
| Name | Type | Default | Description |
|---|---|---|---|
| data | SegmentBarItem[] | [] | Segment definitions: color classes, tooltip content, and optional aria labels |
| defaultBackgroundColor | string | "bg-secondary" | Tailwind background class for segments without a color |
| disabledTooltip | boolean | false | Disables tooltips for every segment in the bar |
| className | string | undefined | Additional classes on the root container |