Row

A row is one square high. It arranges pieces horizontally with one of three alignments, and with href or interactive the whole row becomes one press target.

A recording and its actions

The row opens the recording; its end-slot control remains a separate target.

Aligned setting values

Short and long labels share a stable value edge.

Label and value

Fill places content at opposite ends of the row.

A centred group

An icon and its label share one row.

AttributeTypeDefaultWhat it does
fillbooleanfalsePushes content to opposite ends.
centeredbooleanfalseCentres the group with small gaps.
endbooleanfalsePlaces the group at the end.
autobooleanfalseTakes the content’s height instead of one square.
stackedbooleanfalseStacks the content, each line one square: a tile or a card.
hrefstringMakes the whole row one link to this address.
interactivebooleanfalseMakes the whole row one button.
disabledbooleanfalseKeeps the row from being pressed and dims it. A disabled link has no href and stays a link with aria-disabled="true".
currentstringMarks the destination being shown: aria-current="page", or the value given (step, location, date, time).
navbooleanfalseDraws a navigation row, with the sidebar’s two-pixel corners.
labelstringThe press target’s accessible name, when the row’s words do not say what it does.
target, relstringPassed to the link.

Use one alignment at a time. Slot children can be ordinary HTML or components. The standard row is twelve squares wide and one square high for these alignments.

Rows a person presses

Press a row, or its own action at the end. The event appears below.

With href the row renders one link around its content; with interactive, one button. The pointer over the row fills it with --ds-row-fill-hover, a press with --ds-row-fill-press, an open row (data-open, its details shown beside the list) with --ds-row-fill-current, all as one layer a square high, two pixels around what the row holds (from two above the lower sixteen to two below the row’s foot, two past the content at each side) with 6px corners that follow the 4px icon tiles, drawn over the row so its words and icons stay where they stand on the grid; and keyboard focus draws the focus ring two pixels inside the row, so rows stacked edge to edge never overlap each other’s ring. The content keeps the row’s layout: alignment, gap and stacked apply inside the press target as they would outside it. The words stand 4px from the row’s edges (--ds-row-padding).

A control of its own, such as a quick action or a menu, goes in the end slot. It stands outside the press target and above it, so pressing it never presses the row; pressing anywhere else does. Do not put links or buttons in the default slot: they would be nested inside the row’s own link or button.

ds-row-press fires when the row is pressed, with { href }. Cancel it with preventDefault() to route a link yourself; a link pressed with a modifier key or the middle button is left to the browser, so new tabs and downloads keep working. row.focus() focuses the press target.

A tile or a card is a row with stacked and a width in whole squares: each child takes its own line of one square.

<ds-row interactive stacked style="width: calc(4 * var(--ds-unit))">
<ds-text size="small" tone="muted" literal>Cash</ds-text>
<ds-text literal>¥1,200</ds-text>
</ds-row>

A row that stands for a record (a payment, an event) names its parts with classes, so the page writes no layout of its own: a 16px picture first (an Avatar tile or a large Icon), which takes the first sixteen of its square while the row’s 4px gap puts the words on the next; the words in ds-row__name, which hug and give way first; a Tag after them where there is one; and the figure or date in ds-row__value, at the row’s end in tabular figures. A name wider than its room fades over its last square, never an ellipsis: the element measures it and marks it data-cut="end" while it is cut, so a name that fits stays crisp. A ds-text literal name fades the same way. A page without Lit marks the native .ds-row__name with startCutFades() from 0-face/cut-fade.ts. The List shows the rows under their headings.

<ds-row href="/coffee">
<ds-avatar shape="square" icon="coffee" color="var(--ds-yellow)"></ds-avatar>
<ds-text class="ds-row__name" literal>Coffee</ds-text>
<ds-tag>Cash</ds-tag>
<ds-text class="ds-row__value" literal>5.05 kr</ds-text>
</ds-row>
Navigation rows

The page being shown is the current row. Choose another.

A list of destinations is a <nav> of rows with href and nav; the page being shown takes current. The current row is drawn as the Sidebar’s: filled with --ds-row-fill-current and in the medium weight (--ds-row-current-weight), on two-pixel corners. Never mark the current page with the accent or a colour of its own.

Set open on the row whose details are showing. This fills it with the current-row colour without changing its text weight. The component exposes aria-expanded on its press target; it does not toggle itself. Keep the property tied to the displayed record, update it when stepping between records, and set it to false when the details close. Leave it unset on rows that do not open details.

<ds-row interactive open><span>Selected record</span></ds-row>
<button class="ds-row" data-open aria-expanded="true">Selected record</button>

Native rows use data-open on .ds-row, including a row with a separate .ds-row__target. Remove it (or set it to false) when closed, and update aria-expanded on the button that opens the details. The application owns record identity and opening/closing; hover never clears the open state. A native table uses data-open on its body <tr> instead.