Skip to content

PlDataList

A list of labels and the values that go with them. The panel every detail screen ends with, and the whole reason it is a component is the markup: it says that "Owner" names "Ada Lovelace" rather than sitting beside it.

React
tsx
import { PlDataList, PlDataListItem } from 'plass-ui';

<PlDataList divider>
  <PlDataListItem label="Owner" value="Ada Lovelace" />
  <PlDataListItem label="Plan" value="Team" />
</PlDataList>;
dart
import 'package:plass_ui/plass_ui.dart';

PlDataList(
  divider: true,
  children: const <Widget>[
    PlDataListItem(label: Text('Owner'), value: Text('Ada Lovelace')),
    PlDataListItem(label: Text('Plan'), value: Text('Team')),
  ],
);

Props

PropTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Whether the label sits beside the value or above it
labelWidthnumber | string'10rem'How wide the label column is. A fixed width rather than the longest label, so two panels on one screen line up
dividerbooleanfalseDraws a hairline between the rows, and only between them
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The type scale of the labels and the values
densityshared'default' | 'compact''default'The space between the rows
childrenReactNodeThe PlDataListItems
PropTypeDefaultDescription
children * List<Widget>The PlDataListItems
orientationPlassOrientationPlassOrientation.horizontalWhether the label sits beside the value or above it
labelWidthdouble?160How wide the label column is. A fixed width rather than the longest label, so two panels on one screen line up
dividerboolfalseDraws a hairline between the rows, and only between them
sizesharedPlassSizePlassSize.mdThe type scale of the labels and the values
densitysharedPlassDensityPlassDensity.standardThe space between the rows

PlDataListItem

PropTypeDefaultDescription
labelReactNodeWhat the value is of
valueReactNodeThe value. Use children for a value with markup in it
iconReactNodeA glyph before the label
childrenReactNodeThe value, when it is more than a string
PropTypeDefaultDescription
labelWidget?What the value is of
valueWidget?The value. Use children for a value with markup in it
iconWidget?A glyph before the label

What the shared axes mean across the library is in prop conventions.

PlDataList, PlTable or PlList

Three components lay out rows of text, and they answer different questions.

PlDataListOne thing and its fields. A project's owner, plan, region, created date.
PlTableMany things with the same fields.
PlListA run of items of the same kind, with no fields at all.

A details panel built as a two-column table is the common mistake, and it is not a styling one: a table claims a row-and-column relationship that is not there, so a reader navigating it by cell is told there are two columns of data when there is a column of names and a column of values.

That is what the markup is for. It is a real <dl> with real <dt>s and <dd>s, each pair grouped in a <div>, which the HTML specification allows, and which is what lets a row be laid out side by side without giving up the grouping that makes it a pair.

The Dart half of the same claim is MergeSemantics around each row: the label and its value are announced together. A label read on its own is a word, and a value read on its own is a fact nobody can place.

orientation

horizontal puts the label beside the value in a column of its own, which is the shape a details panel takes. vertical puts it above, for a narrow column, or for values long enough that a label beside them leaves the value nowhere to go.

React

The label column is a fixed width rather than the width of the longest label, and that is deliberate: two panels on one screen line up with each other, and a value does not move when somebody renames a field. labelWidth sets it.

'12ch' is usually the right value. A label column is measured in characters, and no ladder of rem can spell that.

Examples

Rows that need a rule between them

divider draws a hairline between the rows, and only between them. A line above the first or below the last would be a box drawn around a list that has no box.

tsx
<PlCard>
  <PlDataList divider>…</PlDataList>
</PlCard>

A value that is not a string

The value takes anything: a chip for a status, an avatar for a person, a link for a reference.

tsx
<PlDataListItem label="Status">
  <PlChip color="success">Active</PlChip>
</PlDataListItem>

value and children say the same thing. Use value for a string and children for markup.

dart
PlDataListItem(label: const Text('Status'), value: const PlChip(child: Text('Active')));

Notes

  • The rows are children rather than data, unlike a PlTable's columns. A details panel is written out once and read in source order, and every value in it is a different shape, so a data array would be an array of render functions.
  • It draws no surface. A details panel sits in a PlCard, and a sheet inside a sheet is two sheets.
  • size and density come from the list and reach every row, so a panel is one decision rather than one per line.

Accessibility

  • The label and its value are announced as a pair. That is the component's whole reason to exist, and it is what a grid of <div>s cannot do.
  • An icon is decorative and is hidden from a screen reader: the label beside it already says what the field is.
  • A label is not a heading. Naming the panel is the page's job. A <h2> above it, or an aria-label on the region it sits in.

Released under the MIT License