Components

Row

One row of a list with a leading lane, a body, and an action lane

import { Row } from "@nyte-ai/ui";
import { Row } from "@nyte-ai/ui/row";

Both paths export the same component. Pick one per file.

One row of a list. Reach for it when a list needs its items to align down a column and share one hover, focus, and selection model.

Usage

  • Size arrives from the list container, not from the row. Set --nyte-row-height, --nyte-row-gap, --nyte-row-padding-inline, and --nyte-row-leading-size on the parent. There is no size or density prop.
  • Typography is inherited. The row declares no font family and no font size, so the surface owns both. Row.Description and Row.Meta step down from whatever the surface set.
  • Put the click target on Row.Primary, not on the row itself. Row.Actions is a sibling of the primary, because a row whose whole shell is a button cannot contain one.
  • interactive paints the row's own fill. A surface that writes --_row-fill itself owns every state of it and leaves interactive off.
  • Omit Row.Body when there is only a label. Use it to stack a description under the label.

Anatomy

<Row>
  <Row.Backdrop />
  <Row.Primary>
    <Row.Leading />
    <Row.Body>
      <Row.Label />
      <Row.Description />
    </Row.Body>
    <Row.Meta />
  </Row.Primary>
  <Row.Actions />
</Row>

Examples

A clickable row

render on Row.Primary swaps the element. The row resets the user agent padding, border, background, and font that a button or an a brings.

<Row interactive selected={id === currentId}>
  <Row.Primary render={<button type="button" onClick={() => setCurrentId(id)} />}>
    <Row.Leading>
      <IconBubbleText size={16} />
    </Row.Leading>
    <Row.Label>{title}</Row.Label>
    <Row.Meta>{updatedAt}</Row.Meta>
  </Row.Primary>
</Row>

A settings row

<Row>
  <Row.Body>
    <Row.Label>{setting.label}</Row.Label>
    <Row.Description>{setting.owner}</Row.Description>
  </Row.Body>
  <Row.Actions>
    <Switch.Root aria-label={setting.label} checked={setting.enabled} onCheckedChange={apply}>
      <Switch.Thumb />
    </Switch.Root>
  </Row.Actions>
</Row>

Actions on hover

revealActions hides the action lane until the row is hovered or holds focus. It sets --_row-actions-opacity and --_row-actions-pointer-events, which a surface reads to reclaim the room in the same frame rather than repeating the conditions.

<Row interactive revealActions>
  <Row.Primary render={<button type="button" />}>
    <Row.Label>{title}</Row.Label>
  </Row.Primary>
  <Row.Actions placement="overlay">
    <button type="button" aria-label={`Stop ${title}`} />
  </Row.Actions>
</Row>
const styles = stylex.create({
  label: { paddingInlineEnd: "calc(var(--_row-actions-opacity, 0) * 44px)" },
});

placement="overlay" floats the lane over the row instead of taking space in it, so the whole row stays clickable underneath. The surface reclaims the room itself.

A selection layer the surface animates

Row.Backdrop sits behind the row's content at z-index: -1. The row declares isolation: isolate so the backdrop lands behind the row rather than behind the list.

<Row selected={selected}>
  <Row.Backdrop render={<motion.span layoutId="selection" />} />
  <Row.Primary render={<button type="button" />}>
    <Row.Label>{title}</Row.Label>
  </Row.Primary>
</Row>

Sizing from the list

const styles = stylex.create({
  list: {
    "--nyte-row-height": "44px",
    "--nyte-row-gap": "12px",
    "--nyte-row-padding-inline": "12px",
    "--nyte-row-leading-size": "16px",
  },
});

Props

Row

Renders a <div>. RowProps takes every <div> prop, with className and style replaced by the styled versions, plus:

PropTypeDefaultDescription
interactivebooleanfalsePaints the row's own fill on hover, on focus within, and while selected.
selectedbooleanfalseMarks the row current. Sets data-selected.
revealActionsbooleanfalseHides the action lane until the row is hovered or holds focus.
renderReactElement | ((props: HTMLProps) => ReactElement)Replaces the element, or composes the row with another component.
className, style, xstylesee Theming

Row.Actions

Renders a <span>.

PropTypeDefaultDescription
placement"inline" | "overlay""inline"Takes space in the row, or floats over its trailing edge.

RowActionsPlacement is the exported type for placement.

Parts

Every part renders a <span> and takes render, className, style, xstyle, and the props of the element it renders.

PartData slotDescription
Row.Backdroprow-backdropSits behind the row's content, for a selection layer the surface animates.
Row.Primaryrow-primaryThe click target, wrapping whichever parts should be clickable.
Row.Leadingrow-leadingA fixed lane, so labels align down the list whatever glyph each row carries.
Row.Bodyrow-bodyStacks its children, so a label can carry a description under it.
Row.Labelrow-labelThe row's title. Truncates with an ellipsis.
Row.Descriptionrow-descriptionA second line under the label. Truncates with an ellipsis.
Row.Metarow-metaTrailing text such as a time or a count. Tabular figures.
Row.Actionsrow-actionsActions, a switch, or a chevron. A sibling of the primary, never a child.

CSS variables

The surface sets these. The row reads them.

VariableDefaultDescription
--nyte-row-height--nyte-control-height-mdMinimum height of the row.
--nyte-row-gap--nyte-control-padding-xsGap between the row's parts.
--nyte-row-padding-inline--nyte-control-padding-smInline padding, and the overlay inset.
--nyte-row-leading-sizeautoWidth of the leading lane.
--_row-filltransparentBackground of the row. interactive sets it, so leave it off to own it.
--_row-meta-color--nyte-color-tertiary-foregroundColor of Row.Meta.

revealActions sets the two below on the row, and Row.Actions reads them. A surface reads them too, to move with the lane.

VariableHiddenHovered or focused within
--_row-actions-opacity01
--_row-actions-pointer-eventsnoneauto

See Tokens for the control and color variables these fall back to.

Accessibility

  • The row is a <div> with no role. Meaning comes from what you render inside it.
  • Row.Primary takes the focus ring on :focus-visible. The outline is inset by 2px, because a row sits flush in a scroll container where an outset ring would clip.
  • Row.Backdrop is aria-hidden. No other part is.
  • selected sets data-selected and nothing else. Add aria-selected or aria-current on the element you render for Row.Primary.
  • An icon-only button inside Row.Actions needs aria-label. revealActions hides the lane with opacity, so it stays in the tab order, and focus anywhere in the row brings it back.