Can't I just use Toggle button group?
Sure, why not? Just don't forget the accessibility stuff.
Components
Sure, why not? Just don't forget the accessibility stuff.
<nav class="tabs underlined">
<div role="tablist" aria-label="Underlined tabs">
<button id="underlined-tab-1" role="tab" aria-controls="tabpanel-1" aria-selected="true" tabindex="0">
Profile
</button>
<button id="underlined-tab-2" role="tab" aria-controls="tabpanel-2" aria-selected="false" tabindex="-1">
Settings
</button>
<button id="underlined-tab-3" role="tab" aria-controls="tabpanel-3" aria-selected="false" tabindex="-1">
Notifications
</button>
</div>
</nav>
<nav class="tabs filled">
<div role="tablist" aria-label="Filled tabs">
<button
id="filled-tab-1"
role="tab"
aria-controls="tabpanel-1"
aria-selected="true"
tabindex="0"
>
Korg
</button>
<button
id="filled-tab-2"
role="tab"
aria-controls="tabpanel-2"
aria-selected="false"
tabindex="-1"
>
Yamaha
</button>
<button
id="filled-tab-3"
role="tab"
aria-controls="tabpanel-3"
aria-selected="false"
tabindex="-1"
>
Roland
</button>
</div>
</nav>
Tab buttons must contain the following:
Attribute | Value | Description |
---|---|---|
role | "tab" | Identifies the element as a tab |
id | "TAB_ID" | A unique ID for the tab |
aria-selected | "true/false" | Indicates if the tab is selected |
aria-controls | "panel-id" | The ID of the associated tab panel |
tabindex | "0/-1" | Indicates if the tab is focusable |
Tab panels must contain the following:
Attribute | Value | Description |
---|---|---|
role | "tabpanel" | Identifies the element as a tab panel |
id | "panel-id" | A unique ID for the panel |
aria-labelledby | "unique-tab-id" | The ID of the associated tab |
hidden | No value needed | Indicates if the panel is hidden (use hidden attribute for non-active panels) |
There's a lot more. Read about it here.
@layer components.base {
:where(nav.tabs) {
--_bg-color: transparent;
& > [role="tablist"] {
button {
outline-color: transparent;
outline-offset: -4px;
&[aria-selected="true"] {
&:focus-visible {
outline: 2px solid var(--text-color-1);
}
}
}
}
/* Underlined */
&.underlined {
/* Tab list */
& > [role="tablist"] {
border-bottom: 1px solid var(--border-color);
button {
--_ripple-color: light-dark(
color-mix(in oklch, var(--gray-2) 80%, var(--color-8)),
color-mix(in oklch, var(--gray-13) 80%, var(--color-8))
);
background: var(--_bg-color) var(--ripple, none);
font-weight: 500;
line-height: var(--font-lineheight-4);
padding: var(--size-2) var(--size-3);
&:focus-visible {
border-radius: 0;
}
&[aria-selected="true"] {
border-block-end: 2px solid var(--primary);
color: var(--primary);
}
@media (prefers-reduced-motion: no-preference) {
transition:
background-color 0.2s var(--ease-out-3),
border-color 0.2s var(--ease-out-3),
color 0.2s var(--ease-out-3),
outline-offset 0.05s var(--ease-1);
/*** Ripple effect */
background-position: center;
transition: background var(--button-ripple-duration);
&:where(:not(:active):hover) {
--ripple: radial-gradient(
circle,
transparent 1%,
var(--_ripple-color) 1%
)
center/15000%;
}
&:where(:hover:active) {
background-size: var(--button-ripple-size);
transition: background 0s;
}
&:hover {
background-color: light-dark(
oklch(from var(--primary) calc(l * 0.75) none h / 5%),
oklch(from var(--primary) calc(l * 1.25) none h / 5%)
);
}
}
}
}
}
/* Filled */
&.filled {
--_bg-color: var(--surface-default);
--_radius: var(--border-radius);
--_selected-bg: var(--surface-filled);
& > [role="tablist"] {
background-color: var(--_bg-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--_radius);
overflow: hidden;
padding: 0.792ex;
width: fit-content;
button {
background-color: var(--_bg-color);
border-radius: var(--_radius);
line-height: var(--font-lineheight-4);
padding-inline: var(--size-3);
&:hover {
background-color: oklch(from var(--_bg-color) calc(l * 1.25) c h);
}
&[aria-selected="true"] {
background-color: var(--_selected-bg);
&:hover {
background-color: oklch(
from var(--_selected-bg) calc(l * 1.25) c h
);
}
}
@media (prefers-reduced-motion: no-preference) {
transition:
background-color 0.2s var(--ease-out-3),
border-color 0.2s var(--ease-out-3),
color 0.2s var(--ease-out-3),
outline-offset 0.05s var(--ease-1);
}
}
}
}
}
}