Link Switch
Two to four links to versions of the same page, one of them current: 90 | 60, In person | Remote. Each choice is a URL, so it can be shared, bookmarked and printed.
When to Use
Use when:
The reader picks which version of this page to see and the choice belongs in the URL, such as a recipe's length or setting. It needs no JavaScript.
Don't use when:
Panels switch in place without a reload (Tabs), a setting saves (Toggle), or a list narrows (Filter Bar).
Variants
With a visible label
This one is live: choosing an option reloads this page with ?length= set. The current option is aria-current="true".
Length
<?= ws_link_switch([
['label' => '90 min', 'href' => '?code=sprint-retro&length=90', 'current' => true],
['label' => '60 min', 'href' => '?code=sprint-retro&length=60'],
], ['label' => 'Length', 'showLabel' => true]) ?>Without a visible label
When the page already says what is being chosen, label still names the list for screen readers.
<?= ws_link_switch([
['label' => 'In person', 'href' => '?mode=in-person', 'current' => true],
['label' => 'Remote', 'href' => '?mode=remote'],
], ['label' => 'Setting']) ?>Options
| Option | Type | Default | Purpose |
|---|---|---|---|
$items | array | required | Each option: label, href, current |
label | string | required | What is being switched; names the list |
showLabel | bool | false | Show the label as text before the options |
id | string | '' | Element ID; the visible label's id is {id}-label |
class | string | '' | Additional CSS classes |
attrs | array | [] | Extra HTML attributes |
Accessibility
| Concern | Behavior |
|---|---|
| Semantics | A list of links, named by the visible label (aria-labelledby) or by aria-label. Not a tablist: choosing an option loads a page. |
| Current | aria-current="true" on the current option, so it is announced as current, not only shown. On screen it is a 2px --brand ring, 3.82:1 against the white of the other options, with semibold --brand-ink text. In high-contrast mode it fills with Highlight, since every border there repaints alike. |
| Target size | Each option is at least 44px tall. |
| Focus | The platform --focus-ring, drawn outside the pill at --focus-ring-offset. |
Tokens
| Token | Used for |
|---|---|
--brand, --brand-tint, --brand-ink | The current option: its ring, fill and text |
--gray-300, --bg-surface, --color-ink-secondary | The other options: outline, fill and text |
--brand-line | An option's outline on hover, over --brand-tint |
--radius-full | The pill shape |
--font-body, --text-small | The label and options |
--focus-ring, --focus-ring-offset | Keyboard focus |
CSS Classes
| Class | Purpose |
|---|---|
.ws-link-switch | The label and the options |
.ws-link-switch__label | The visible label |
.ws-link-switch__options | The row of options |
.ws-link-switch__option / .is-current | One option, and the current one |
Files
| File | Purpose |
|---|---|
includes/components/helpers.php | ws_link_switch() |
includes/components/link-switch.php | Template |
includes/components/components.css | Styles (.ws-link-switch rules) |