Beta
Segmented <mv-segmented>
Segmented Control mit der Semantik einer Radiogruppe, formularfähig: Ein erhabener Griff gleitet mit leichtem Überschwingen zur gewählten Option, Haarlinien trennen die inaktiven Optionen, drei Größen und reine Icon-Optionen, die per Tooltip benannt sind.
| Kategorie | Formulare |
|---|---|
| Typ | Web Component (<mv-segmented>) |
| Status | Beta |
| Installiert auch | tooltip |
| Keywords | segmented, radio-group, toggle, inspector, sliding-indicator, tooltip, form-associated, editor, sizes |
When to use
- An inspector offers two to five mutually exclusive options, like text alignment or view mode
- Icon-only options need a compact radio group with tooltips for their names
- A form should submit one choice from a short set while keeping it visible at a glance
Avoid when
Installation
node scripts/add.mjs segmented --out ./src/marvelousKI-Agent mit dem MCP-Server von Marvelous UI: install_components({ slugs: ["segmented"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Kopierte Dateien (inklusive Abhängigkeiten): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/form.js, core/position.js, components/segmented/segmented.js, components/segmented/segmented.css, components/tooltip/tooltip.js, components/tooltip/tooltip.css.
Verwendung
Schnellstart mit dem kleinsten funktionierenden Markup:
<mv-segmented label="View" value="list">
<button value="list">List</button>
<button value="grid">Grid</button>
</mv-segmented>Referenz-Markup zum Starten und Anpassen mit Attributen, data-* und CSS-Variablen:
<div id="seg-demo">
<style>
#seg-demo { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: center; justify-content: center; width: 100%; }
#seg-demo .sgd-stage { display: grid; place-items: center; width: 17rem; height: 16.5rem; padding: 1.25rem; border-radius: var(--mv-radius-xl); background: var(--mv-bg); border: 1px solid var(--mv-border); }
#seg-demo .sgd-card { width: 100%; padding: 1rem 1.1rem; border-radius: var(--mv-radius-lg); background: var(--mv-surface-raised); border: 1px solid var(--mv-border); transition: box-shadow var(--mv-duration-normal) var(--mv-ease-out); }
#seg-demo .sgd-card h4 { margin: 0 0 .35rem; font: 650 1rem/1.25 var(--mv-font-sans); }
#seg-demo .sgd-card p { margin: 0; font-size: .8rem; line-height: 1.5; color: var(--mv-fg-muted); }
#seg-demo .sgd-card[data-density="compact"] { padding: .6rem .75rem; }
#seg-demo .sgd-card[data-density="roomy"] { padding: 1.5rem 1.4rem; }
#seg-demo .sgd-panel { display: grid; gap: .6rem; width: 17.5rem; padding: .8rem; border: 1px solid var(--mv-border); border-radius: var(--mv-radius-lg); background: var(--mv-surface); box-shadow: var(--mv-shadow-sm); }
#seg-demo .sgd-head { margin: 0 0 .1rem; font: 600 .7rem var(--mv-font-sans); letter-spacing: .06em; text-transform: uppercase; color: var(--mv-fg-subtle); }
#seg-demo .sgd-row { display: grid; grid-template-columns: 4.25rem 1fr; align-items: center; gap: .5rem; font-size: .75rem; color: var(--mv-fg-muted); }
#seg-demo .sgd-sizes { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; justify-content: center; width: 100%; }
</style>
<div class="sgd-stage">
<div class="sgd-card" id="seg-demo-card">
<h4>New collection</h4>
<p>Soft fabrics and clean cuts, made to last well beyond one season.</p>
</div>
</div>
<div class="sgd-panel" role="group" aria-label="Inspector">
<p class="sgd-head">Text</p>
<div class="sgd-row"><span id="seg-demo-l1">Alignment</span>
<mv-segmented id="seg-demo-align" icons full aria-labelledby="seg-demo-l1" name="align" value="left">
<button value="left" aria-label="Align left"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M4 12h10M4 18h13"/></svg></button>
<button value="center" aria-label="Align center"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M7 12h10M5.5 18h13"/></svg></button>
<button value="right" aria-label="Align right"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M10 12h10M7 18h13"/></svg></button>
<button value="justify" aria-label="Justify"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M4 12h16M4 18h16"/></svg></button>
</mv-segmented>
</div>
<div class="sgd-row"><span id="seg-demo-l2">Case</span>
<mv-segmented id="seg-demo-case" full aria-labelledby="seg-demo-l2" name="case" value="none">
<button value="none">Aa</button>
<button value="uppercase">AA</button>
<button value="lowercase">aa</button>
<button value="small-caps" disabled>Sᴄ</button>
</mv-segmented>
</div>
<p class="sgd-head" style="margin-top:.35rem">Card</p>
<div class="sgd-row"><span id="seg-demo-l3">Shadow</span>
<mv-segmented id="seg-demo-shadow" full aria-labelledby="seg-demo-l3" name="shadow" value="sm">
<button value="none">None</button>
<button value="sm">Soft</button>
<button value="xl">Strong</button>
</mv-segmented>
</div>
<div class="sgd-row"><span id="seg-demo-l4">Density</span>
<mv-segmented id="seg-demo-density" full data-size="sm" aria-labelledby="seg-demo-l4" name="density" value="default">
<button value="compact">Compact</button>
<button value="default">Default</button>
<button value="roomy">Roomy</button>
</mv-segmented>
</div>
</div>
<div class="sgd-sizes">
<mv-segmented data-size="lg" label="Billing period" value="yearly">
<button value="monthly">Monthly</button>
<button value="yearly">Yearly · save 20%</button>
</mv-segmented>
</div>
</div>
<script type="module">
const root = document.getElementById("seg-demo");
const card = document.getElementById("seg-demo-card");
const get = (id) => document.getElementById(`seg-demo-${id}`).value;
const apply = () => {
card.style.textAlign = get("align");
card.style.textTransform = get("case");
const s = get("shadow");
card.style.boxShadow = s === "none" ? "none" : `var(--mv-shadow-${s})`;
card.dataset.density = get("density");
};
root.addEventListener("mv-change", apply);
customElements.whenDefined("mv-segmented").then(() => requestAnimationFrame(apply));
</script>API
Attributes
| Name | Typ | Description |
|---|---|---|
value | string | Value of the selected option (otherwise the first enabled one). Restored on reset. |
name | string | Form field name. |
label | string | Accessible name of the group (or aria-label / aria-labelledby). |
icons | boolean | Icon-only options: each button’s aria-label becomes a tooltip (<mv-tooltip>). |
full | boolean | Full width, equal-width segments. |
data-size | sm | lg | Smaller or larger track (default between the two). |
disabled / readonly | boolean | Disables the whole group (also via <fieldset disabled>) or freezes it while keeping it focusable. |
Properties
| Name | Typ | Description |
|---|---|---|
value | string | Current value; setting it selects the matching option without firing events. |
items | HTMLButtonElement[] | Option buttons. Buttons added or removed later are picked up. |
selectedItem | HTMLButtonElement | null | Selected option. |
Methods
| Name | Description |
|---|---|
focus() | Focuses the selected option (or the first enabled one). |
Events
| Name | Description |
|---|---|
mv-input / mv-change | New option selected (click or arrow keys). detail: { value, item }. |
Content structure
| Name | Description |
|---|---|
<button value> | One option per child button (text, icon or both); disabled for an unavailable option. |
CSS classes
| Name | Description |
|---|---|
mv-segmented-item / -indicator | Options and gliding thumb. data-selected marks the chosen option, data-divider an idle option that shows a hairline. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-segmented-height | var(--mv-control-sm) | Height (overrides data-size). |
--mv-segmented-radius | var(--mv-radius-md) | Outer radius; the segments use this radius minus the inner padding. |
--mv-segmented-padding | 2px | Gap between the track and the thumb. |
--mv-segmented-bg / --mv-segmented-indicator | Track and thumb background. | |
--mv-segmented-selected-fg / --mv-segmented-divider | Selected label color and hairline color. | |
--mv-segmented-ease | cubic-bezier(0.32, 1.25, 0.6, 1) | Easing of the glide. |
Accessibility
Named role="radiogroup" (aria-disabled, aria-readonly), options are role="radio" with aria-checked. A single tab stop (roving tabindex); arrow keys move and select, wrapping and mirrored in right-to-left layouts, Home/End jump to the ends and disabled options are skipped; a read-only group only moves focus. In the icon variant the name comes from the aria-label and the tooltip, also shown on keyboard focus, is not announced twice. The glide follows reduced motion, hover tints only apply to fine pointers, and forced colors draw the thumb in Highlight.