Button

An action or navigation control, with primary, secondary and text treatments.

A publication toolbar

Primary, secondary and quiet actions share one baseline.

Moving through a collection

Icon-only controls retain accessible names and the same control height.

Button styles

Click a button. The event appears below.

An icon and an action

Icons follow the button’s text colour.

Use primary for the main action, secondary for a supporting action, and no attribute for a text button. These are boolean attributes: primary="false" still enables primary. Remove the attribute to disable it.

AttributeTypeDefaultWhat it does
primarybooleanfalseAccent background.
secondarybooleanfalseQuiet background.
disabledbooleanfalseDisables the inner button and its action; a link loses its destination.
fixedbooleanfalseFour-square width instead of hugging the label.
widebooleanfalseEight-square width.
fitbooleanfalseExplicit alias for the default label-hugging width.
textstringEmptyTranslation key; otherwise use slot content.
labelstringEmptyThe control’s accessible name, as given, when the face alone does not say what the button does (a value or a number). Pass words already in the page’s language.
hrefstringEmptyMakes the face a real link (<a>) to this address.
targetstringEmptyThe link’s target.
relstringEmptyThe link’s rel; a new tab gets noopener noreferrer without it.
blankbooleanfalseOpens href in a new tab (target="_blank").

variant="primary" is not supported. Use <ds-button primary>.

const button = document.querySelector('ds-button');
button.addEventListener('click', () => {
console.log('Save requested');
});

The native click crosses the shadow boundary once. A disabled button does not trigger it.

With href="/icons/" the face is a real link drawn as the button: the browser navigates, so middle-click, ⌘- or Ctrl-click, Copy link and Enter work as on any link, and nothing navigates from a click handler. A disabled link has no href. React uses ButtonLink; see Links. For forms, use control="slotted". A native light-DOM button supplies submission, reset, focus, validation and disabled-fieldset behavior; DS1 owns its visual appearance. Keep button attributes on the native control and visual options on ds-button.

<ds-button control="slotted" primary>
<button type="submit" name="action" value="save">Save</button>
</ds-button>

Set disabled on the host; it forwards to the native control. Native form attributes (form, formaction, formnovalidate, formmethod, name, value) keep their browser behavior. The light-DOM control remains the actual submitter. The shared face is protected from document button resets; configure it through public --ds-button-* tokens and put layout on the host.

React products select button in their snapshot and import the shared binding:

import { Button } from './design/react/button';
<Button type="submit" primary disabled={saving}>Save</Button>

Every face, including React and native slotted controls, is one control high and stands at the foot of its line, so its words take the lower sixteen of the square; a menu entry is a row high with its words on the same sixteen. Buttons hug their label by default, including React and native slotted controls. fit remains an explicit alias. Optional --ds-button-min-width and --ds-button-align tokens control a minimum width and label alignment without reaching into the shadow root.

Slotted buttons are one row high. Use fixed for four squares, wide for eight squares, icon for one square, or --ds-button-width for a layout-sized control. Menu items retain their full-row width.

The adapter’s default type is button; set submit or reset deliberately. Refs and event targets are the real native button. className and style apply to the host for layout; ARIA, IDs, native attributes and handlers reach the native control. Registration happens in the browser before hydration, as for shared inputs. The adapter does not import browser globals during SSR.

Form · Icon · Tooltip

Use danger for destructive actions: the words are in the danger colour (--ds-danger-color) on every face, the default one included, as .ds-button--danger draws them on a native button. It can be combined with secondary.

The native control exposes ::part(control) for a library’s authored box model. Use the host for margins. The library editor measures the real control and writes opt-in padding, border, corner and gap rules; without these rules, standard button geometry stays unchanged.

A compact group for views, frame choices or repeated actions such as back, today and next. The chosen segment carries the accent. Only the outside corners take the current shape; interior joins stay square.

Choices and repeated actions

Choose a view or step through the date.

<ds-button split aria-label="View" options="day:Day,week:Week,month:Month" value="week"></ds-button>
<ds-button split actions aria-label="Date" options="back:@left:Back,today:Today,next:@right:Next"></ds-button>

options="day:Day,week:Week" supplies value/label pairs. A bare word is its own label and lowercase value. back:@left:Back draws the shared left icon with Back as its accessible label. Labels follow the shared language runtime; set literal for already translated or user-supplied words.

For arbitrary labels, including commas and colons, set the segments property to an array of { value, label, icon?, disabled? } objects. An empty array renders no segments. disabled disables the entire group, and a disabled fieldset disables it through the browser’s form association.

Set value for a choice. Choosing a different segment emits a bubbling, composed, cancelable change with detail.value. The value changes after the event unless a listener calls preventDefault(). Choosing the current value does nothing. With actions, every activation emits press with detail.value and none of the segments is selected.

Each segment is a native button: Tab focuses it, Enter and Space activate it. The group never submits a form or adds a form value. Give the host an aria-label; focus() focuses the selected enabled segment, or the first enabled segment in action mode.

Split is a Button mode, selected with split; it is grouped under Button in the catalogue and library editor. In a library, choose Single or Split in Button mode. Each mode keeps its own saved box-model rules; split segments retain square interior joins. In React, use the same Button entry:

import { Button } from './design/react/button';
<Button split label="Frame" value={frame} onPress={setFrame}
segments={[{ value: "browser", label: "Browser" }, { value: "phone", label: "Phone" }]} />
<Button split label="History" onPress={handleHistory}
segments={[{ value: "undo", label: "Undo", icon: "undo" }, { value: "redo", label: "Redo", icon: "redo", disabled: !canRedo }]} />

Supplying value keeps the choice controlled; omitting it selects repeated actions. compact keeps a single-character label square and gives longer numeric labels the compressed face. Outside corners follow the shared shape; interior joins stay square. The native segments retain Tab, Enter, Space and disabled-fieldset behavior. This mode groups choices or actions; it does not create a dropdown menu.

There is no separate split element: select button in Sync 1 and write <ds-button split> or <Button split>.