Components · Navigation
Tabs
BetaSwitches between views of one thing, such as a client's profile.
- Source
components/ui/tabs.tsx - shadcn/ui Tabs
- Figma: not linked yet (Q13)
When to use
- To switch between views of the same thing without leaving the page: a client's overview, program, nutrition and payments.
- To filter one list into a few fixed groups: active, pending, archived clients.
When not to use
- For steps in a sequence (onboarding, a program wizard): use a stepper.
- To navigate between pages: use the Sidebar or links.
- For 2 or 3 options that change a setting: use a Radio group or toggle group.
Anatomy
- 1. Tab list, labelled for screen readers
- 2. Tab: the selected one has a raised background
- 3. Panel: the content for the selected tab
Variants
| Variant | Use for |
|---|---|
default | Switching views inside a card or a section |
line | Page-level sections and list filters, under a page header |
States
| State | What changes | Token |
|---|---|---|
| Selected | Background and full-strength text | bg-background, text-foreground, shadow-sm |
| Not selected | Muted text | text-muted-foreground (changed from shadcn's text-foreground/60, which failed contrast) |
| Hover | Text darkens | text-foreground |
| Focus-visible | Ring | ring-ring/50 |
| Disabled | Half opacity | opacity-(--opacity-disabled) |
Usage guidelines
- 2 to 6 tabs. Labels fit on one line; on small screens the list scrolls rather than wrapping.
- The first tab is the most used view, and is selected by default.
- Keep the selected tab when the coach comes back to the page.
Content guidelines
- One or two words per tab, nouns, sentence case: "Overview", "Payments".
- Counts in brackets are fine for filters: "Pending (3)".
Accessibility
| Key | Does |
|---|---|
| Tab | Moves into the tab list (to the selected tab), then to the panel |
| Arrow left / right | Moves between tabs and shows each one. Reversed in RTL |
| Home / End | First / last tab |
tablist,tabandtabpanelroles, witharia-selectedandaria-controls. Announced as "Program, tab, 2 of 4".- Give the list an
aria-labelthat names what the tabs switch: "Client profile".
RTL
The first tab is on the right, and arrow keys follow the reading order. This works because the site provides Radix's DirectionProvider; without it, Radix assumes LTR.
Tokens used
Read from components/ui/tabs.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.
| Token | Utilities | SuperCoach | Coach |
|---|---|---|---|
| --background | bg-background | white | white |
| --foreground | text-foreground bg-foreground | neutral-950 | neutral-950 |
| --muted | bg-muted | neutral-100 | neutral-100 |
| --muted-foreground | text-muted-foreground | neutral-600 | neutral-600 |
| --ring | border-ring ring-ring/50 outline-ring | brand-600 | orange-500 |
- Opacity
opacity-(--opacity-disabled) - Radius
rounded-lg - Radius
rounded-md - Radius
rounded-none - Shadow
shadow-sm - Type
font-medium - Type
text-sm
Props
<Tabs>
Also takes every prop of Radix Tabs.Root
No props of its own.
<TabsList>
Also takes every prop of Radix Tabs.List
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "line" | "default" |
<TabsTrigger>
Also takes every prop of Radix Tabs.Trigger
No props of its own.
<TabsContent>
Also takes every prop of Radix Tabs.Content
No props of its own.
Replaces in coach-admin
From the read-only audit of coach-admin: 76 style values, 108 uses.
Classes and selectors
.nav-tabs18.tab-content13.tabs10.tab-pane10.nav-tabs .nav-link.active8.tab8.tab-btn7.nav-tabs .nav-link.active:hover5.tab-pane.active3.tab-btn.active2.tab-btn:hover1.nav-tabs .nav-link1
Files
src/reusableComponents/formComponents/language-tabs/LanguageTabs.scss19
The inventory lists the first 12 locations of each value, so file counts are a lower bound.
SCSS variables in these styles
$aside-menu-nav-padding-ynot a color: spacing, size or layout$font-size-basenot a color: spacing, size or layout$navbar-heightnot a color: spacing, size or layout$primary-color→--primary$spacernot a color: spacing, size or layout$input-border-colornot a color: spacing, size or layout