Controls
What a reader presses, follows or reads a state off. Everything here is
small, appears in a bar or a row of actions, and is a real <button> or
<a> underneath.
sds-button#
The action that starts work. One primary per view. A second makes neither mean anything.
<sds-button variant="primary" type="submit">Send the message</sds-button>
<sds-button variant="ghost" size="sm" for="filters" command="toggle">
<sds-icon name="actions-filter"></sds-icon>
</sds-button><sds-button variant="primary" type="submit">Send the message</sds-button>
<sds-button variant="ghost" size="sm" for="filters" command="toggle">
<sds-icon name="actions-filter"></sds-icon>
</sds-button>
The label is content, not a property. A button's label is often a name in mono, a count, or a glyph, and none of those fits in a string.
primary is the action that starts work. secondary stands beside
it. ghost belongs in a bar or a head, where a filled box is the
loudest thing on the surface. danger is the press with no undo:
status colour as ink and a hairline, never a fill, and a label that names
what goes. It stands last, after the way out. Colours
says why it is the one control with a status colour.
-
variant# -
- type
- "primary" | "secondary" | "ghost" | "danger"
- default
- "primary"
primaryis the action that starts work.secondarystands beside it.ghostbelongs in a bar or a head, where a filled box is the loudest thing on the surface.dangeris the press with no undo: status colour as ink and a hairline, never a fill, and a label that names what goes. It stands last, after the way out. Colours says why it is the one control with a status colour.
sm is for a control inside another surface, a table head, a code
block's chrome, not to make a page fit. lg is the one action a screen
is for, a landing's single call. Beside a second large button neither is
the one, and that is what md is for.
-
size# -
- type
- "md" | "sm" | "lg"
- default
- "md"
smis for a control inside another surface, a table head, a code block's chrome, not to make a page fit.lgis the one action a screen is for, a landing's single call. Beside a second large button neither is the one, and that is whatmdis for.
The default is the whole reason for the property. A <button> with no
type inside a <form> submits it. So a filter or a Cancel drawn
with this element sends the form on the press. A real submit says so, and
then Enter in a text field submits too, which only that button carries.
-
type# -
- type
- "button" | "submit" | "reset"
- default
- "button"
The default is the whole reason for the property. A
<button>with no type inside a<form>submits it. So a filter or a Cancel drawn with this element sends the form on the press. A real submit says so, and then Enter in a text field submits too, which only that button carries.
Emits is-disabled beside the button's own classes.
-
disabled# -
- type
- boolean
- default
- false
Emits
is-disabledbeside the button's own classes.
The label is one glyph, and the button is the square. The element infers it where it can read the label. A caller says it where the label arrives as markup, or the button loses its shape in a bar.
-
icon-only# -
- type
- boolean
- default
- false
The label is one glyph, and the button is the square. The element infers it where it can read the label. A caller says it where the label arrives as markup, or the button loses its shape in a bar.
An icon-only button must have one, because nothing else names it.
-
title# -
- type
- string
An icon-only button must have one, because nothing else names it.
Where it goes, for the press that is a link. It renders an <a> and
nothing else changes: same classes, same shape. The browser adds its own
middle-click, hover target and status line, which a <button> with a
handler does not have. A link has no disabled state, so disabled
drops there. A control nobody must follow is one nobody writes.
-
href# -
- type
- string
Where it goes, for the press that is a link. It renders an
<a>and nothing else changes: same classes, same shape. The browser adds its own middle-click, hover target and status line, which a<button>with a handler does not have. A link has no disabled state, sodisableddrops there. A control nobody must follow is one nobody writes.
What that link is to this page: prev, next, external. Only
with href, as the anchor's own attribute.
-
rel# -
- type
- string
What that link is to this page:
prev,next,external. Only withhref, as the anchor's own attribute.
The id of what this button acts on. A press dispatches sds-command
on that element. Without it the button keeps its own click.
-
for# -
- type
- string
The id of what this button acts on. A press dispatches
sds-commandon that element. Without it the button keeps its own click.
What it asks: show, close, toggle, or a word a page's own
listener understands.
-
command# -
- type
- string
- default
- "show"
What it asks:
show,close,toggle, or a word a page's own listener understands.
<!-- The class equivalent, for a surface that runs no JavaScript. -->
<button class="sds-btn sds-btn--primary" type="button">Send the message</button><!-- The class equivalent, for a surface that runs no JavaScript. -->
<button class="sds-btn sds-btn--primary" type="button">Send the message</button>
sds-dropdown#
A button, and the short list it opens under itself.
The card draws the control open, the button pressed and its list under it, as a box in the flow. That is the one state a specimen can hold: it runs no script, and nothing static opens a popover. Everything that makes the panel a flyout hangs off the attribute. So what a surface with no JavaScript writes is exactly what the card draws, down to the distance of the list from its button.
<sds-dropdown label="Language" name="Language"></sds-dropdown><sds-dropdown label="Language" name="Language"></sds-dropdown>
The entries decide what the list is. Entries with href are pages, so
the panel is a disclosure of links, and Tab walks them as well. Entries
without one are commands, so it is a menu with role="menu". The element
asks the entries, not the caller. A caller who has to say which one it is can
say the wrong one. Menu commands over a list of pages is a promise the panel
cannot keep.
The arrows belong to both. From the button they open the panel and step into
it from the end the key came from. Inside it they walk the rows and stop at
the ends. Home and End go straight there. A reader on the button
presses down before anything else. A panel that answers that in one list and
not in the other is a control to learn twice.
The trigger is a real button of this system, from the same classes, so it
takes the variants and sizes every other one does. What a dropdown says
about itself, expanded, and which panel it controls, stands on the
<button> itself. That is why it is not an <sds-button> with
attributes.
The panel is a popover. The top layer holds it, so no ancestor's overflow clips it and nothing on the page stacks over it. The open, the press outside that closes it, Escape and the focus back on the button are the platform's. Placement is the one part that is not.
Where the engine has anchor positioning, the stylesheet does it. Where it
has not, the element measures the button and writes the edges itself:
src/lib/flyout.ts, which sds-search uses for its own drop. Both
routes write the same two edges from anchor(), not a position-area.
An area is a box the panel fits into, and it pushes a list wider than its
control off its own anchor.
The window is the one edge the top layer does not answer for. A button near
the side the panel grows towards leaves less room than the panel needs. What
leaves the window is out of reach. So the panel hangs from the button's
other edge instead, on both routes. position-try-fallbacks: flip-inline
where the engine anchors, and the same question of the measurement where it
does not.
align says which side it starts from and is a preference. To stay on
the page is not one.
The entries, in the order a reader reads them. Set from script, as a list. label, then
href for a page, icon for a glyph before the label, current
for the one in force, disabled, external. And lang where the
entry names a language. That last one makes a reader hear "Deutsch" in
German, not in the voice of the page.
-
choices# -
- type
- DropdownChoice[]
The entries, in the order a reader reads them. Set from script, as a list.
label, thenhreffor a page,iconfor a glyph before the label,currentfor the one in force,disabled,external. Andlangwhere the entry names a language. That last one makes a reader hear "Deutsch" in German, not in the voice of the page.
What the button says. A dropdown whose entries are settings names the
setting, not the value, and lets current mark the one in force.
-
label# -
- type
- string
What the button says. A dropdown whose entries are settings names the setting, not the value, and lets
currentmark the one in force.
The control's name, where the label is too short to say it: a language code for "Language". It stands in front of the label, not instead of it. An accessible name that drops the word a reader can see is a name they cannot ask for by voice.
-
name# -
- type
- string
The control's name, where the label is too short to say it: a language code for "Language". It stands in front of the label, not instead of it. An accessible name that drops the word a reader can see is a name they cannot ask for by voice.
Which side the panel hangs from. end where the button sits at the end
of a row, so the list opens back over the row. A side with no room for
the panel is the placement's business: the panel hangs from the button's
other edge instead.
-
align# -
- type
- "start" | "end"
- default
- "start"
Which side the panel hangs from.
endwhere the button sits at the end of a row, so the list opens back over the row. A side with no room for the panel is the placement's business: the panel hangs from the button's other edge instead.
The button's own variant. size beside it takes the button's sizes.
-
variant# -
- type
- "primary" | "secondary" | "ghost"
- default
- "secondary"
The button's own variant.
sizebeside it takes the button's sizes.
The label drops and icon stands alone. Then name is mandatory:
nothing else says what the control is.
-
icon-only# -
- type
- boolean
The label drops and
iconstands alone. Thennameis mandatory: nothing else says what the control is.
A chosen entry dispatches sds-dropdown-choose with the entry and its
position. A page that never listens still works. An entry with a target is a
link and stays one, so the event stands beside the navigation, not
instead of it. preventDefault() is how an app takes the navigation over.
sds-link#
A link. Always an <a> with an href, the external one included.
Anything else looks like a link, takes no focus, opens in no new tab, and is
invisible to whatever reads the page as a document.
<sds-link label="The changelog" href="/changelog"></sds-link>
<sds-link label="On GitHub" href="https://github.com/…" external></sds-link><sds-link label="The changelog" href="/changelog"></sds-link>
<sds-link label="On GitHub" href="https://github.com/…" external></sds-link>
In a sentence, a paragraph, a list item or a cell, it carries an underline at rest, as every link there does. It has nothing to stand apart from, and the link ink alone does not say that a word is a link. Outside prose the underline arrives with the pointer.
The words. A link is never a bare glyph. A row of marks is a row of pictures the reader has to know already.
-
labelrequired # -
- type
- string
The words. A link is never a bare glyph. A row of marks is a row of pictures the reader has to know already.
-
href# -
- type
- string
- default
- "#"
Opens away from this surface. It gets the glyph, and says so to the browser and to the eye.
-
external# -
- type
- boolean
- default
- false
Opens away from this surface. It gets the glyph, and says so to the browser and to the eye.
A glyph beside the label: a repository, a chat, a feed. The component decides if it leads or follows. An arrow, a chevron or a caret says where the press goes and follows the label. Everything else says what the link is and leads it.
-
icon# -
- type
- icon id
A glyph beside the label: a repository, a chat, a feed. The component decides if it leads or follows. An arrow, a chevron or a caret says where the press goes and follows the label. Everything else says what the link is and leads it.
The mark alone, with icon: drawn at 24, the label carried for
whoever cannot see it, and the external glyph dropped. Two marks on one
link say one thing twice. For a row of accounts at the end of a footer,
where a reader looks for marks by position. Nowhere a link stands in a
sentence.
-
bare# -
- type
- boolean
- default
- false
The mark alone, with
icon: drawn at 24, thelabelcarried for whoever cannot see it, and the external glyph dropped. Two marks on one link say one thing twice. For a row of accounts at the end of a footer, where a reader looks for marks by position. Nowhere a link stands in a sentence.
sds-badge#
A small, named piece of state. accent names where an answer came from.
The status tones are the result of one.
<sds-badge label="1.4.0" tone="accent"></sds-badge>
<sds-badge label="answered" tone="ok"></sds-badge><sds-badge label="1.4.0" tone="accent"></sds-badge>
<sds-badge label="answered" tone="ok"></sds-badge>
-
labelrequired # -
- type
- string
The three result tones carry a glyph and a colour. Colour alone leaves the meaning to anyone who cannot tell three hues apart.
-
tone# -
- type
- "default" | "accent" | "ok" | "warn" | "error"
- default
- "default"
The three result tones carry a glyph and a colour. Colour alone leaves the meaning to anyone who cannot tell three hues apart.
An explicit glyph, where the icon adds a fact the word does not.
-
icon# -
- type
- icon id
An explicit glyph, where the icon adds a fact the word does not.
Status colour belongs in a badge, in code output, in a result row and in a diagram about status. Never as page furniture. A colour for "something is wrong" on a header says it about the page.
Status colour belongs in a badge, in code output, in a result row and in a diagram about status. Never as page furniture. A colour for "something is wrong" on a header says it about the page.
sds-progress#
How far a running job has got: a share, not a sequence of stops.
sds-steps claims that step two follows step one. This claims a distance,
and the outside drives it. Set value as the work reports, and the bar
travels to the new width in --duration-fast.
The fill takes its colour from that same distance. The ink comes from
the share itself: grey with nothing to report, and the whole way to
--status-ok as the work approaches a complete run. So the colour says
what the length says, moves as slowly as the bar does, and needs no
threshold. A flat colour at every moment, never a gradient.
It never passes through red or amber. A job at a fifth is not a failure. A colour that says so is the one thing on the page that claims a fault.
<sds-progress caption="Rendering the manual" value="42"
note="Chapter 5 of 12 — writing the search index next."></sds-progress>
<sds-progress caption="Uploading the release" value="3" max="12"
readout="count" unit="files"></sds-progress><sds-progress caption="Rendering the manual" value="42"
note="Chapter 5 of 12 — writing the search index next."></sds-progress>
<sds-progress caption="Uploading the release" value="3" max="12"
readout="count" unit="files"></sds-progress>
What the work is, over the bar. Without one the bar is bare, right where
the surface around it names the job, and it still owes label.
-
caption# -
- type
- string
What the work is, over the bar. Without one the bar is bare, right where the surface around it names the job, and it still owes
label.
Its name for anything that cannot see what it sits beside. The track is
the progressbar, and a bar with no name reads out as a number out of
a hundred of nothing.
-
label# -
- type
- string
Its name for anything that cannot see what it sits beside. The track is the
progressbar, and a bar with no name reads out as a number out of a hundred of nothing.
Where it stands, in the unit of max. Clamped to the run, so work that
overruns its own estimate draws a full bar, not one out of its track.
-
value# -
- type
- number
- default
- 0
Where it stands, in the unit of
max. Clamped to the run, so work that overruns its own estimate draws a full bar, not one out of its track.
The whole the value is a part of.
-
max# -
- type
- number
- default
- 100
The whole the value is a part of.
How it says the position. count gives the two numbers themselves, "3
of 12 files", where the count is the useful part. A percentage of twelve
is arithmetic the reader has to undo.
-
readout# -
- type
- "percent" | "count" | "none"
- default
- "percent"
How it says the position.
countgives the two numbers themselves, "3 of 12 files", where the count is the useful part. A percentage of twelve is arithmetic the reader has to undo.
What the numbers count, after them in a count read-out.
-
unit# -
- type
- string
What the numbers count, after them in a
countread-out.
What the work does right now. Over 2s this line has to say why. The same line can stand between the tags where it carries a link or a name in mono.
-
note# -
- type
- string
What the work does right now. Over 2s this line has to say why. The same line can stand between the tags where it carries a link or a name in mono.
small thins the track alone, for a bar in a row of other things. The
read-out over it is the same line it is anywhere else.
-
size# -
- type
- "medium" | "small"
- default
- "medium"
smallthins the track alone, for a bar in a row of other things. The read-out over it is the same line it is anywhere else.
Work happens right now: a hatch travels through the filled part while the bar stands still, the one thing a bar at rest cannot say. Use it where reports arrive far apart. A bar with no movement for ten seconds and a stalled one look the same otherwise. Turn it off the moment the work stops, and at the end of the run. A bar at work at a standstill claims something nobody measured.
It sets aria-busy while it runs. Reduced motion keeps the hatch and
stops its travel, so a working bar still reads as one. The note under the
bar, never the movement alone, says what happens.
The stripes are the system's second and last gradient, beside the lit frame of a card under the pointer. One ink at two strengths, for motion, not colour. See Colours.
-
pulsing# -
- type
- boolean
- default
- false
Work happens right now: a hatch travels through the filled part while the bar stands still, the one thing a bar at rest cannot say. Use it where reports arrive far apart. A bar with no movement for ten seconds and a stalled one look the same otherwise. Turn it off the moment the work stops, and at the end of the run. A bar at work at a standstill claims something nobody measured.
It sets
aria-busywhile it runs. Reduced motion keeps the hatch and stops its travel, so a working bar still reads as one. The note under the bar, never the movement alone, says what happens.The stripes are the system's second and last gradient, beside the lit frame of a card under the pointer. One ink at two strengths, for motion, not colour. See Colours.
Where the share is unknown, there is nothing to fill. That is
.sds-loading with a spinner, which claims no distance; see the
states guideline. A bar that advances by itself
tells the reader something the work never said.
Where the share is unknown, there is nothing to fill. That is
.sds-loading with a spinner, which claims no distance; see the
states guideline. A bar that advances by itself
tells the reader something the work never said.
sds-run#
Work in progress, as its stops. sds-progress above says how far.
This says what the work goes through, and it is the one component in the
system that changes while a reader watches it.
Not sds-steps, and the difference is not the drawing. An instruction renders before the page ships and never changes. A run arrives one stop at a time, and each stop carries what it wrote. The stops fold, and the whole ends on a verdict an instruction has no place for. Nothing in a document still runs, which is why this element is an application's and appears in no rendered page here.
<sds-run heading="Reading docs.typo3.org" verdict="running"
note="Step 3 of 5 · 1m 27s so far" open
.steps="${[
{ label: 'Fetch the sitemap', state: 'done', meta: '0.4s' },
{ label: 'Build the index', state: 'running', meta: '23s',
output: '→ 12880 of 18412 pages' },
{ label: 'Swap it in', state: 'ahead' },
]}"></sds-run><sds-run heading="Reading docs.typo3.org" verdict="running"
note="Step 3 of 5 · 1m 27s so far" open
.steps="${[
{ label: 'Fetch the sitemap', state: 'done', meta: '0.4s' },
{ label: 'Build the index', state: 'running', meta: '23s',
output: '→ 12880 of 18412 pages' },
{ label: 'Swap it in', state: 'ahead' },
]}"></sds-run>
What the run is, in one line. Or what became of it, which is what a set of jobs says at the top: "Some checks haven't completed yet".
-
headingrequired # -
- type
- string
What the run is, in one line. Or what became of it, which is what a set of jobs says at the top: "Some checks haven't completed yet".
What became of the whole. It is the mark beside the heading, and the one thing a folded run still says.
-
verdict# -
- type
- "running | done | failed"
- default
- running
What became of the whole. It is the mark beside the heading, and the one thing a folded run still says.
The line under the heading: where the work has got to, or the counts.
-
note# -
- type
- string
The line under the heading: where the work has got to, or the counts.
The stops, set from script, as a list that changes. state is
ahead, running, done or failed. meta is the quiet word
at the far end of the row, a duration or a count. note is what happens
to it in words, which a queue owes a reader that a mark cannot say.
output is what it wrote.
-
stepsrequired # -
- type
- "{ label, state, meta?, note?, output?, group? }[]"
The stops, set from script, as a list that changes.
stateisahead,running,doneorfailed.metais the quiet word at the far end of the row, a duration or a count.noteis what happens to it in words, which a queue owes a reader that a mark cannot say.outputis what it wrote.
Named on a step, not on the run. Where the work is many jobs at once, the order says nothing and the state sorts them. So the stops carry their group, and each group folds under a name with its own count. Stops with no group are one run, in order.
-
group# -
- type
- string
Named on a step, not on the run. Where the work is many jobs at once, the order says nothing and the state sorts them. So the stops carry their group, and each group folds under a name with its own count. Stops with no group are one run, in order.
If the whole stands unfolded. A run under watch is open. One in a
list of past runs is not, and the head is then the whole of it.
-
open# -
- type
- boolean
- default
- false
If the whole stands unfolded. A run under watch is
open. One in a list of past runs is not, and the head is then the whole of it.
The names of the states, where the page is not in English. Partial: a page names the ones it has a word for, and the rest keep theirs. So a language that arrives one string at a time is never a run with no words.
-
state-words# -
- type
- "{ ahead?, running?, done?, failed? }"
The names of the states, where the page is not in English. Partial: a page names the ones it has a word for, and the rest keep theirs. So a language that arrives one string at a time is never a run with no words.
A stop that wrote nothing does not open. It draws no chevron and takes no press. A control that opens onto an empty box is a promise the row cannot keep. A stop that wrote something opens by itself while it is in hand and closes once it is behind. A press is the reader's answer to that question, kept for as long as the run is on screen.
The mark has a name, not only a drawing. A shape and a colour are one
claim, and neither reaches a reader who hears the page. So every state
carries its word; see Accessibility. The word is
English until state-words says otherwise. It is the only part of a run
this element writes itself. Without the words, a page in another language
draws its own labels and announces somebody else's.
The row in hand carries a band and the page's own ink, never the accent. The accent marks three things, and a step is none of them. The movement says that this is the row under work, which the two settled ends have no need of.
The share is sds-progress, above it, where the work reports one. Most
runs cannot. A job of five steps knows its step and nothing about how long
the fourth takes. A bar that advances by itself tells the reader something
the work never said.
The share is sds-progress, above it, where the work reports one. Most
runs cannot. A job of five steps knows its step and nothing about how long
the fourth takes. A bar that advances by itself tells the reader something
the work never said.
sds-icon#
A TYPO3 icon, in the document, not linked from it, so it inherits
currentColor. Colour that follows the UI is the whole icon rule.
<sds-icon name="actions-check-circle"></sds-icon>
<sds-icon name="actions-search" size="24" label="Search"></sds-icon><sds-icon name="actions-check-circle"></sds-icon>
<sds-icon name="actions-search" size="24" label="Search"></sds-icon>
An identifier from the set this system ships. An unknown one throws
instead of a blank. A missing glyph reads as a design decision, and the
fix is a one-line edit and make icons.
-
namerequired # -
- type
- icon id
An identifier from the set this system ships. An unknown one throws instead of a blank. A missing glyph reads as a design decision, and the fix is a one-line edit and
make icons.
em is the default because an icon almost always sits inside something
with a text size: a button's label, a badge, a table cell. A match makes
a glyph look placed, not dropped in. A number is for a glyph on its own,
and 16 is the floor.
-
size# -
- type
- 16 | 20 | 24 | 32 | 48 | "em"
- default
- "em"
emis the default because an icon almost always sits inside something with a text size: a button's label, a badge, a table cell. A match makes a glyph look placed, not dropped in. A number is for a glyph on its own, and 16 is the floor.
For an icon without text beside it, and only for that. An icon beside its own label hides from assistive technology, so nothing reads twice.
-
label# -
- type
- string
For an icon without text beside it, and only for that. An icon beside its own label hides from assistive technology, so nothing reads twice.
Icons for the set, where a missing one comes from, and which state glyphs can stand alone in text.
Icons for the set, where a missing one comes from, and which state glyphs can stand alone in text.
sds-theme#
The mode the page is in, as one press that changes it: a state a reader flips, not a choice from a list.
<sds-theme></sds-theme><sds-theme></sds-theme>
It is the system's own icon button and draws no control of its own. Square,
ghost, and with its sentence in title, which is both the accessible name
and the words the pointer reveals. It draws three marks and fades two out,
so a press confirms the change without movement on the row.
There are three states, and one press steps to the next. The machine's setting, light, dark, and round again. The machine's is the default most readers are on, so it is a stop on the way, not something only a cleared key gives back. A control that reaches two of its three states takes the default away from whoever tries it once.
The document says which mark stands, and the stylesheet decides it. No
data-theme is the machine's, and the attribute names the other two. So
the button is right before a script runs. A button drawn from its own state
renders its construction value, which on a prerendered dark page is a sun. The sentence in title names no state for the same reason.
Where a script has read the document, aria-label names both the state
and where the press goes.
Where the choice lives. The boot script in the document head has the same default, both ends on one name. Two products on one origin are two keys, and then each end gets its own; see As a standalone frontend.
-
key# -
- type
- string
- default
- "soul-theme"
Where the choice lives. The boot script in the document head has the same default, both ends on one name. Two products on one origin are two keys, and then each end gets its own; see As a standalone frontend.
The element reads data-theme off the document, keeps no idea of its
own, and watches it. The boot script writes it before the first paint,
the machine's setting changes it, and a second tab changes it too.
Same-origin frames on the page get it as well, which keeps a specimen
from a light state inside a dark page.
The element reads data-theme off the document, keeps no idea of its
own, and watches it. The boot script writes it before the first paint,
the machine's setting changes it, and a second tab changes it too.
Same-origin frames on the page get it as well, which keeps a specimen
from a light state inside a dark page.