Pill bar
A segmented control for mutually exclusive options: sort order, view mode, any pick-one group.
A pill bar is a row of options in a shared trough. Visually it reads as one control, not a row of separate buttons. Use it for choices where only one option can be active at a time.
Two variants
Radio inputs when the state must survive JavaScript and only one option is ever valid:
<div class="pill-bar" role="group" aria-label="Sort by">
<label class="pill-option">
<input type="radio" class="sr-only" name="sort" value="date" checked />
<span>Date added</span>
</label>
<label class="pill-option">
<input type="radio" class="sr-only" name="sort" value="title" />
<span>Title A-Z</span>
</label>
</div>
Buttons when JavaScript controls the state (view switching, DOM changes):
<div class="pill-bar" role="group" aria-label="Display mode">
<button class="pill-btn active" data-view="cards" aria-pressed="true">Cards</button>
<button class="pill-btn" data-view="list" aria-pressed="false">List</button>
</div>
CSS
.pill-bar {
display: flex;
gap: 2px;
background: var(--color-border);
border-radius: 0.5rem;
padding: 2px;
}
.pill-option { cursor: pointer; }
.pill-option span,
.pill-btn {
display: flex;
align-items: center;
padding: 0.25rem 0.65rem;
border-radius: 0.35rem;
font-size: 0.75rem;
font-family: inherit;
line-height: 1;
color: var(--color-text-muted);
white-space: nowrap;
transition: background 0.15s, color 0.15s;
}
.pill-option:has(input:checked) span,
.pill-btn.active {
background: var(--color-bg);
color: var(--color-text);
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.pill-btn {
border: none;
background: transparent;
cursor: pointer;
}
The line-height trap
Always set line-height: 1 on pill items. Buttons default to line-height: normal (~1.2), but spans and labels inherit the body line-height (~1.7). With identical padding, a label pill will be taller than a button pill. Pin it explicitly on the shared class.
This is a general rule: whenever mixing element types in a visually unified component, always pin line-height explicitly.
Usage rules
- Sort/filter options: use radio inputs with
:has(input:checked) - View mode toggle: use buttons with
.activeclass managed by JS - Always the same scale on one page
- Sits in the toolbar row above the content grid, never in the filter sidebar
- Sidebar is for filtering only. Toolbar is for sorting and view mode.
Where used
Library page (sort bar + view toggle), homepage stream (view toggle).