The foundational list row — a universal tappable element for settings, contacts, menus, and transactions.
Overview
Cell is the fundamental building block for lists — a universal tappable row for settings, menu items, transactions, addresses, and contacts. It always fills its container width, and every preset is a locked configuration of a single CustomCell base with all slots configurable.
-
1
Simple
-
2
Icon
-
3
Squircle · S
-
4
Squircle · M
-
5
Squircle · L
-
6
Custom
States
Three states, the same on every preset — one block each. Press is feedback, not a state: see Animation.
Standard
-
1
Background var(--background-primary) — the row's fill.The default — any DS background token can replace it.
-
2
Title var(--text-and-icon-primary) — the title rows of start-text and end-text.
-
3
Subtitle var(--text-and-icon-secondary) — the subtitle, additional and description rows.
-
4
Chevron var(--text-and-icon-primary) — locked in the presets.
-
5
Icon var(--text-and-icon-primary) — the start-slot icon.
-
6
Squircle Its own Standard state — from the Squircle component.
Disabled
-
1
Background var(--background-primary) — unchanged.
-
2
Title var(--text-and-icon-disabled) — set once by
.is-disabledon the root; the rows inherit it.A colour swap, not an opacity. -
3
Subtitle var(--text-and-icon-disabled) — the secondary rows follow.
-
4
Chevron var(--text-and-icon-disabled)
-
5
Icon var(--text-and-icon-disabled)
-
6
Squircle Its own Disabled state —
.squircle--disabled, from the Squircle component. A control in the end-slot takes its own disabled state the same way.
Skeleton
-
General var(--skeleton-on-white) — the placeholder fill.var(--pattern-shimmer) — the DS sweep, from the Skeleton component.The same shapes on every preset: the start-slot as a rounded box of its own size, the title and subtitle as bars. The end-slot has no placeholder.
Anatomy
Two containers: the row of four slots, and the optional description under it. Only start-text is required.
-
1
topContainer The horizontal row that holds the four slots; no fill of its own.Required.
-
2
start-slot var(--sp-s40) — its width; an IconContainer by default.An icon, a boxed icon or nothing; a Squircle in the Squircle presets. Optional.
-
3
start-text Body/Main Body — the title row.Body/Compact Body — the subtitle and additional rows, hidden by default.Up to three TextRow rows. Fills the width and truncates first. Required.
-
4
end-text The same three rows, end-aligned. Hugs its text and never truncates.Hidden by default. Optional.
-
5
end-slot One item — the chevron by default in the presets — or two in CustomCell.Chevron, icon, value, badge, a Checkbox, RadioButton or Switcher, Squircle, Button, Tag, Loader or Avatar — the full list is in the spec, §8. Optional.
-
6
description Body/Compact Body — the text, with a divider above it.Text and divider, text only or divider only. Hidden by default. Optional.
Layout
CustomCell with every slot on. The presets lock a subset of these slots; only the Squircle sizes change the start-slot and, at M, the vertical padding.
General
-
Container var(--sp-s16) — left and right padding.var(--sp-s10) — top and bottom padding.var(--sp-s8) — the same padding in the M Squircle preset.var(--sp-s8) — every gap between slots, and between two items in one end-slot.var(--sp-s48) — the minimum height; the row hugs its content above it.Fills its container's width — never sized by its content.
-
Priority The end side keeps its width and start-text truncates first. The priority can move to the start side when a layout needs it.
Slots
-
Height var(--sp-s36) — every slot's height floor.The row is as tall as its tallest slot; every slot fills that height and centres its content. The Squircle presets keep the Squircle on the row's top edge.
-
start-slot var(--sp-s40) — its width; the Squircle's own size in the Squircle presets.
-
start-text Fills the width the other slots leave.
-
end-text, end-slot Hug their content.
Squircle sizes
-
S var(--sp-s40) — the Squircle and its slot; the row is 60.
-
M var(--sp-s48) — the Squircle and its slot.var(--sp-s8) — top and bottom padding; the row is 64.
-
L var(--sp-s56) — the Squircle and its slot; the row is 76.
-
Row Height Body/Main Body — the title on every size.var(--sp-s16) — the left padding on every size.The row is the Squircle plus the vertical padding — the minimum for any number of text rows; the text centres against the Squircle.
Selection controls
-
1
Row (Default) var(--sp-s24) — the drawn box, without its safe zone, in the chevron column.The row is the hit area and the control.
-
2
Separate var(--sp-s48) — the control's own hit area, dashed.var(--sp-s12) — its overlap on every side, into the row's padding instead of layout space, so the row and the box sit exactly as in Row.
-
RadioButton, Switcher RadioButton follows Checkbox. Switcher draws its 52 × 32 container; its safe zone is vertical only, 8 above and below.
Card-wrap
-
Wrapper var(--sp-s16) — the corner radius.var(--sp-s16) — the horizontal margin from the container.Owns the radius and
overflow: hidden, so the press overlay clips to the corners. The cell keeps its own padding.
RTL
-
1
LTR The default. start-slot on the left, end-slot on the right.
-
2
RTL The row mirrors: the end-slot on the left, the start-slot on the right; the text aligns to the start side.Only the chevron mirrors among the icons; a Switcher takes its own RTL variant. The web takes the direction from
dir="rtl"— opt-in per instance.
Animation
Motion follows the motion system § 6.1 — pushHighlight, the press without scale. The row has three motions of its own — the press, the disabled colour change and the skeleton's shimmer; a selection control brings its own.
Press
-
General --component-push-highlight — press and release, 150 ms linear.var(--shadow-pressed) — the overlay over the row's background; it fades in and out.No scale: a scaled row would push its neighbours. The same on every preset.
-
Separate Only the row action presses the row; a press on the end-slot control leaves the row still.
-
Repeated Taps The press plays on every tap of an enabled row; the action runs once — the first tap runs it and a tap inside the window opened by the last accepted one is ignored, so a quick double tap opens a chevron row's screen once and toggles a selection row once.
-
Platforms Android — the native ripple over the row.iOS — the table-cell highlight, a background change.Flutter — an
InkWellwith its splash in the press overlay colour, black 10% (theState/PressedStateOverlaystyle). -
Reduced Motion Handled at the token:
motion.csszeroes every --component-* insideprefers-reduced-motion: reduce, so the overlay appears without a fade; no override of its own.
Disabled
-
Colour Change --transforming-state-default — 200 ms slow-ease-out, Standard ↔ Disabled.The text and icon colour moves to var(--text-and-icon-disabled) and back; no opacity.
-
Nested Components A Squircle or a control runs its own disabled transition.
-
Reduced Motion --transforming-state-default — zeroed at the token, so the colour changes in one frame.
Selection control
-
The Control's Own --transforming-state-default — the control's own motion, 200 ms; see Checkbox and RadioButton.The row adds only its press; there is no selected tint to animate.
-
Switcher Keeps its own motion on iOS and Android; it has no web build yet.The knob drags as well as taps: in a row, a drag that carries it across toggles the row the same way as a tap. A switch that cannot change does not follow the finger.
Skeleton
-
Shimmer var(--pattern-shimmer) — the DS sweep on every shape, from the Skeleton component.It runs in reverse in RTL.
-
Reduced Motion var(--pattern-shimmer) — zeroed at the token, so the skeleton holds still.
Usage
Cell is the row of a list — settings, menus, contacts, trip history, a choice.
-
1
A Settings List Rows that open a screen: an icon, a title and the chevron. The presets lock the rest, so every list reads the same.
-
2
One Choice Out Of Several RadioButton rows: the whole row is the control and the list is the
radiogroup. Any number of choices is Checkbox rows; an on/off setting is a Switcher row. -
3
✕ A Tinted Row As The Selection Cell has no selected state — a tint is colour alone and is not announced. Put a Checkbox or a RadioButton in the end-slot; the control carries the selection.
-
4
✕ A Lone Row On A Screen Cell is a list building block. One row on its own goes in the card-wrap, which gives it the radius and the margin of a button.
-
5
✕ Three Actions In One Row A row carries at most two: its own action and one control (
controlTarget: separate). More actions go to a swipe or a context menu.
Settings
Share my trip
Payment
Saved places
Accessibility
A row is one focus stop — or two when its control is a separate target. The full model and the per-platform tables live in cell-a11y.md.
-
1
Tappable Row
<button type="button">— the button role natively;type="button"keeps a cell inside a form from submitting it.The name comes from the cell's own text — title, then subtitle.aria-labelonly when that text is not a good name.TalkBack: "{title}, {subtitle}", Button, "Double tap to activate".VoiceOver: "{title}, {subtitle}",.button, "Double tap to activate". -
2
Chevron The hint "Tap to open" —
aria-describedbyto a visually hidden span; the chevron itself is not announced.TalkBack: "Double tap to open". VoiceOver: "Double tap to open". -
3
Focus Ring var(--sp-s2) — the ring's width;
:focus-visibleonly.--text-and-icon-primary — its colour.var(--sp-s2) — the inward offset: a list clips its rows, so an outer ring would not show.The same ring on.cell__action; on a row cell it is the control's ring too. -
4
Checkbox Row The row is the control:
role="checkbox"andaria-checkedon the row, its text the name. The box inside is drawn without its safe zone and carriesaria-hidden="true"— announced once, by the row.TalkBack: "{title}, {subtitle}, Checkbox, Not checked", "Double tap to toggle".VoiceOver: one element; the box's state merges into the row's traits — selected / not selected; "Double tap to toggle setting".aria-checked,data-ds-stateand the box's--selectedclass change in one handler. A Switcher row is the same withrole="switch".After a toggle the new state is read at once, the focus still on the row — the state lives on the row's own element. A state left on the box is never reported; one written only into the label is not reliably read.UI tests read the state on the row: Androidchecked, iOS value (Switcher) / selected trait (Checkbox, RadioButton), webaria-checked.Cell has no selected state of its own and noaria-selected— a selected row is announced through its control. -
5
RadioButton Rows The list is the group:
role="radiogroup"with its own name; each rowrole="radio".One Tab stop —tabindex="0"on the selected row, or on the first enabled row when the selected one is disabled.TalkBack: "{title}, {subtitle}, Radio button, Checked, 1 of 2"; "Double tap to select", none once selected.VoiceOver: the selected trait; "Double tap to select", none once selected. -
6
Row Action
.cell__action— a<button>stretched over the cell, first in the DOM, so it takes the focus first.The root is a<div>: a<button>cannot contain another button — the trap of this model.TalkBack: "{title}", Button, with "{subtitle}" as its description and the row's hint.VoiceOver: "{title}, {subtitle}",.button, the row's hint.The text block isaria-hidden="true"so swipe navigation does not read it a second time; the label and description references still resolve. -
7
Its Control A second focus stop with its own role,
aria-checkedand name — the title.TalkBack: "{title}, Checkbox, Not checked". VoiceOver: "{title}", its own trait and state. -
8
Disabled Native
disabled— out of the Tab order, the trait announced. The control inside takes its own disabled state.TalkBack: "Disabled" after the label, no hint; the row stays in swipe navigation and a double tap does nothing. A selected row reads its value first — "Checked", then "Disabled".VoiceOver: "Dimmed", no activation hint; the row stays in the rotor. A selected row reads "Selected", then "Dimmed".The trap: on a separate cell.is-disabledon the<div>is visual only —disabledgoes on both buttons. -
9
Skeleton Not focusable, not announced: the shapes are
aria-hidden, and the loading list carries the busy state once —aria-busy="true"on the web. VoiceOver announces the container once. -
Keyboard Tab moves between cells; a RadioButton list is one stop, a separate cell two.Space and Enter activate a plain or chevron cell. On a Checkbox row Space toggles, on a RadioButton row it selects, and Enter does nothing; the arrows move the choice in a list and skip disabled rows.The trap: a row is a real
<button>, which synthesizes a click on Enter — a bare click handler would toggle a Checkbox row on Enter. The handler flags Enter onkeydownand swallows that click, so Enter stays free to submit the form. -
Target Size var(--sp-s48) — the row's minimum height; the whole row is the target.The separate control keeps its own 48 × 48 area inside the row.
-
Reduced Motion --component-push-highlight — the press overlay appears in one frame.--transforming-state-default — the disabled colour changes in one frame.var(--pattern-shimmer) — the shimmer holds still. All zeroed at the token.
-
Contrast --text-and-icon-primary on --background-primary — the title, the icon, the chevron and the focus ring: ≈ 17.8:1 Day / ≈ 17.0:1 Night.--text-and-icon-secondary — the subtitle: ≈ 5.25:1 / ≈ 7.92:1, above 4.5:1.A selection control keeps its own pairs — see its spec.