Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
DR.Simple_UI
Showing main. This can be ahead of the version your app has installed — check what a class says it needs before copying it. Releases (opens in a new tab)

Tabs and segmented

tier 2 — classes

Two ways to offer a small set of choices. .tabs swaps a region of content; .segmented changes a setting. If there is a panel below it, it is tabs.

The classes cannot supply the ARIA. Tabs need role="tablist", role="tab", aria-selected and aria-controls, plus arrow-key movement between them. Without that a screen reader hears a row of unrelated buttons. The segmented control needs none of it, because it is built from radios.

Tabs

The indicator is a transparent border that gains a colour, so the label does not jump when it is selected. A long row scrolls sideways rather than wrapping — a wrapped second row pushes the content down and the underline stops reading as one strip.

The selected tab can be marked either way: aria-selected="true", which is what a screen reader announces, or .tab--active for a tab strip that is genuinely not a tablist. Prefer the attribute — with the class alone the styling and the announcement can disagree.

The demo is live. data-tabs on the container hands the strip to drSimpleUi.tabs, which is where the behaviour the CSS cannot express lives: clicking, arrow keys, Home/End, showing the matching aria-controls panel, and the roving tabindex that makes Tab step over the whole tablist instead of through every tab in it. An app that drives its tabs from C# leaves data-tabs off — otherwise two things set aria-selected — and wires the arrow keys in the component.

Twelve orders are waiting on a first response.

<div>
    <div class="tabs" role="tablist" data-tabs aria-label="Order views">
        <button class="tab" role="tab" aria-selected="true" id="t-open" aria-controls="p-open" type="button">
            <i class="ri-inbox-line"></i> Open <span class="tab-count">12</span>
        </button>
        <button class="tab" role="tab" aria-selected="false" id="t-mine" aria-controls="p-mine" type="button" tabindex="-1">
            Assigned to me <span class="tab-count">3</span>
        </button>
        <button class="tab" role="tab" aria-selected="false" id="t-all" aria-controls="p-all" type="button" tabindex="-1">
            Everything
        </button>
        <button class="tab" role="tab" aria-selected="false" aria-disabled="true" disabled type="button" tabindex="-1">
            Archive
        </button>
    </div>
    <div class="tab-panel" role="tabpanel" id="p-open" aria-labelledby="t-open">
        <p class="lede">Twelve orders are waiting on a first response.</p>
    </div>
    <div class="tab-panel" role="tabpanel" id="p-mine" aria-labelledby="t-mine" hidden>
        <p class="lede">Three are yours: ORD-4209, ORD-4244 and ORD-4250.</p>
    </div>
    <div class="tab-panel" role="tabpanel" id="p-all" aria-labelledby="t-all" hidden>
        <p class="lede">Everything in the queue, decided or not.</p>
    </div>
</div>

Segmented control

Radios under the hood, so arrow keys move between the options and the chosen one is announced as chosen — with no ARIA at all. Give every input the same name, and give the group a name with a <fieldset> or aria-labelledby, or a screen reader announces three loose radios.

Reach for it instead of a <select> when the options are few and short: a select costs one click before the reader even learns what the choices are.

Time range
<div style="display:flex; flex-direction:column; gap:16px; align-items:flex-start">
    <fieldset class="form-section" style="margin:0">
        <legend>Time range</legend>
        <div class="segmented">
            <label class="segmented-option"><input type="radio" name="range" /> 24h</label>
            <label class="segmented-option"><input type="radio" name="range" checked /> 7d</label>
            <label class="segmented-option"><input type="radio" name="range" /> 30d</label>
        </div>
    </fieldset>
    <div class="segmented">
        <label class="segmented-option"><input type="radio" name="view" checked /><span><i class="ri-list-check"></i> List</span></label>
        <label class="segmented-option"><input type="radio" name="view" /><span><i class="ri-layout-grid-line"></i> Grid</span></label>
        <label class="segmented-option"><input type="radio" name="view" disabled /><span><i class="ri-calendar-line"></i> Calendar</span></label>
    </div>
</div>

Tabs in a card

Live — switch between them. The tablist goes in .card-head and the panels in the body, so the card head reads as the card's own switch rather than as a row of buttons above it.

Only the selected tab is a tab stop; Tab moves past the whole list and the arrow keys move within it. data-tabs maintains that roving tabindex, so write it in the markup for the initial state and leave the rest alone.

ItemQtyReserved
Desk lamp, matte22
Cable tray, 600 mm11
Monitor arm, dual20
<div class="card" style="max-width:520px">
    <div class="card-head">
        <div class="tabs" role="tablist" data-tabs aria-label="ORD-4209 detail">
            <button class="tab" role="tab" aria-selected="true" id="c-lines" aria-controls="cp-lines" type="button">
                Lines <span class="tab-count">5</span>
            </button>
            <button class="tab" role="tab" aria-selected="false" id="c-history" aria-controls="cp-history" type="button" tabindex="-1">
                History
            </button>
        </div>
    </div>
    <div class="card-body">
        <div class="tab-panel" role="tabpanel" id="cp-lines" aria-labelledby="c-lines">
            <table class="table">
                <thead>
                    <tr><th>Item</th><th>Qty</th><th class="col-num">Reserved</th></tr>
                </thead>
                <tbody>
                    <tr><td>Desk lamp, matte</td><td>2</td><td class="col-num">2</td></tr>
                    <tr><td>Cable tray, 600 mm</td><td>1</td><td class="col-num">1</td></tr>
                    <tr><td>Monitor arm, dual</td><td>2</td><td class="col-num">0</td></tr>
                </tbody>
            </table>
        </div>
        <div class="tab-panel" role="tabpanel" id="cp-history" aria-labelledby="c-history" hidden>
            <ol class="timeline">
                <li>
                    <span class="timeline-when">09:15</span>
                    <span class="timeline-what">Three of five lines reserved automatically.</span>
                </li>
                <li>
                    <span class="timeline-when">09:18</span>
                    <span class="timeline-what">Held for a decision by Alex Fischer.</span>
                </li>
            </ol>
        </div>
    </div>
</div>

Segmented in a toolbar

Two of them in one bar: a range inside .toolbar-filters, because it narrows what is listed, and a layout switch at the far end, because it changes how the same list is drawn. Which side a control sits on is what says whether it filters or presents.

A segmented control inside a toolbar still needs a group name. aria-label on the .segmented is enough here; a <fieldset> would add a border and a legend the bar has no room for.

<div class="toolbar">
    <div class="toolbar-filters">
        <input class="form-input toolbar-input" type="search" placeholder="Search orders…" />
        <div class="segmented" role="group" aria-label="Time range">
            <label class="segmented-option"><input type="radio" name="tb-range" /> 24h</label>
            <label class="segmented-option"><input type="radio" name="tb-range" checked /> 7d</label>
            <label class="segmented-option"><input type="radio" name="tb-range" /> 30d</label>
        </div>
    </div>
    <div class="segmented" role="group" aria-label="Layout">
        <label class="segmented-option">
            <input type="radio" name="tb-view" checked /><span><i class="ri-list-check"></i> List</span>
        </label>
        <label class="segmented-option">
            <input type="radio" name="tb-view" /><span><i class="ri-layout-grid-line"></i> Grid</span>
        </label>
    </div>
</div>