Design system/Components
Tabs and filter bar
Tabs switch the view, and a pill bar filters the content of that view. Tabs have an orange underline; the active pill is charcoal.
In codeapp/globals.css (.tabbar, .pill)
Tab bar
Like "Upcoming" and "History" on the meetings page: two or more views of the same thing. The active tab gets primary text and a 2.5px orange line underneath, inset 10px on each side.
Three meetings this week.
Pill bar
For filtering a list: "All", "Today", "This week". The bar wraps with an 8px gap or scrolls horizontally; if it scrolls, it has vertical inner padding so shadows aren't clipped.
Rules
Do
Tabs for views of one thing; pills for filters.
Don't
Tabs for going to another page; that's a link.
Do
At most five tabs with short labels.
Don't
Tabs that overflow the screen on phones.
<div className="tabbar" role="tablist">
<button className="tabbar-tab" role="tab" data-on={tab === "up" || undefined}
aria-selected={tab === "up"} onClick={() => setTab("up")}>{tr("پیشرو")}</button>
</div>Sizes
padding 9px 14px 11px · 13.5px / 600 · gap 7pxpadding 12px 20px 14px · 15.5px · gap 9pxflex: 1 · padding-inline 6pxWidth is shared2.5px · inset-inline 10px · radius 99pxgap 2px · border-bottom 1px var(--border)21px · 11px / 700≥ 900px: 24px · 12pxStates
--text-secondary--text · color 0.15s easedata-on · --text · line --accent--accent on --accent-soft- .tabbar is only visual and sets no roles; write role="tablist", role="tab", and aria-selected in the markup, as in the code sample.
- An inactive tab is --text-secondary on the background: 4.72 to 1 in light and 6.32 in dark, AA in both. The active tab stands apart from the rest by both its text color and its orange underline.
- The underline uses logical inset-inline and stays under its own tab in both directions.
Do
On phones the tabs share the width; write short labels.
Don't
A label that doesn't fit in a third of 360px.
Related pages