Browse all of Kata

Row

Source docs/design/components/row.mdMarkdown

On this page

The generic list row: an optional leading slot, a title, an optional subtitle, a trailing slot, and a chevron when it goes somewhere.

Light

In a flush card: title, subtitle, leading, trailing, chevron

ATAna TorresGreen · 14 months
Hand techniques16
Waiver on fileSigned Aug 22, 2026

On the page: no card, no hairlines for five rows or fewer

Coaching2 clips awaiting review
Past classesYesterday · 90 min

Dark

In a flush card: title, subtitle, leading, trailing, chevron

ATAna TorresGreen · 14 months
Hand techniques16
Waiver on fileSigned Aug 22, 2026

On the page: no card, no hairlines for five rows or fewer

Coaching2 clips awaiting review
Past classesYesterday · 90 min

Source: Row.tsx. How lists are built from it: index-list.

When to use it

  • Any list of things you open: techniques, students, classes, settings.
  • A static fact with a label and a value, with showChevron={false}.

Use FeedRow for the Social feed and SessionRow for training history — they are this row with feature-specific content. Do not build a third.

Anatomy

Part Token
Row floor controlHeight.row 64 — a floor, never a fixed height
Vertical padding spacing.s12, on the content column
Gaps spacing.s12 leading to text and text to trailing; spacing.s4 title to subtitle
Title typography.rowTitle 16/600, text.primary
Subtitle typography.caption 13/400, text.secondary
Chevron caretRight at iconSize.inline 16, text.secondary
Divider 1px border.hairline on surface.card, inset to the text edge, dropped on the last row (isLast)

The hairline belongs to the content column, so it starts where the words start and never runs under the avatar. On the page, five rows or fewer get no hairlines at all — 12pt between groups.

States

Light

Default

Stances7

Pressed

Stances7

Focus

Stances7

Dark

Default

Stances7

Pressed

Stances7

Focus

Stances7
  • Pressed: opacity.pressed 0.85 across the row.
  • Focus: a 2px border.focus ring.
  • Static: no press, no chevron, still one grouped element for a screen reader.
  • Disabled: none. A row that cannot open says why in its subtitle.
  • Loading: skeleton rows of the same shape, in the list that owns them.

On web the ring is the browser's. Row does not draw its own focus ring; on RN-web it relies on the browser's default focus outline. Input, SearchField and SelectTrigger draw the 2px border.focus themselves; the specimen shows the ring this component owes.

Content

  • The subject is first and boldest. Metadata goes in the subtitle, never above the title.
  • Neither line truncates. A long name wraps and the row grows.
  • A count in the trailing slot is caption in text.secondary; a status is a Badge.

Accessibility

  • A pressable row is role button by default and reads title and subtitle together.
  • A static row groups its lines by hand (accessible plus a label), so it is read once, not as three fragments.
  • trailingInteractive splits the row when the trailing slot holds something tappable — a ⋯ or a switch. On iOS a pressable fuses its children into one element, so a nested button is unreachable by VoiceOver. With the flag the text column becomes the pressable and the trailing control stays its own stop.
  • The subtitle is text.secondary, not text.tertiary, because a row sits directly on surface.page as often as on a card, and text.tertiary on surface.page is 4.23:1, under the 4.5:1 body text needs.

Do and don't

Ana TorresLast trained 3 days ago

Do

The thing they are looking for is first and boldest.

Ana TorresLast trained 3 days ago

Don’t

Metadata louder than its subject. The name is the row.

Code

import { Avatar, Card, Row, Text } from '../../components/ui';

<Card padded={false}>
  <Row
    title="Ana Torres"
    subtitle="Green · 14 months"
    leading={<Avatar name="Ana Torres" size={32} />}
    onPress={() => openStudent(ana.id)}
    testID="roster-row-ana"
  />
  <Row title="Hand techniques" trailing={<Text variant="caption" tone="secondary">16</Text>} onPress={openHand} isLast />
</Card>