跳到正文
misoto22 design

容器

DescriptionList 描述列表

一条记录的各个字段,用真正的 <dl>,不是一堆 div 拼的网格。

用法

什么时候用它

正面看一条记录——详情页、摘要面板。从上往下看好几条记录,那是 Table。
TSX
import { DescriptionList } from '@misoto22/design'

说明

一条记录的各个字段:标签、值、标签、值。

这是任何详情页里重复得最多的那个形状,也是最常被人用一堆 <div> 拼成网格手搓出来的那个——而那样一拼,标记原本携带的唯一一件事就没了。<dl> 告诉读屏软件“Owner”命名的是它旁边那个东西;一堆 div 拼的网格告诉它的是这里有两列互不相干的文字,配对关系只能靠阅读顺序去猜。

有好几条记录时请改用 Table。这个是正面看一条记录,表格是从上往下看很多条。

每一对都包在一个 <div> 里——HTML 规范允许 <dl> 里出现它,正是为了让一对可以作为一个整体来排版——所以那条细线能贯穿整行,而不是断在列间距里。

items 为空时渲染 null,而不是一个空的带边框的盒子。一条一个字段都没有的记录,是上面那个页面该用 EmptyState 去处理的状态;而一圈围着空气的细线,看起来就像一个没加载出来的组件。

结构

DescriptionList anatomy
部件说明
List必填The <dl>. It is the element that carries the pairing: a grid of divs looks identical and tells a screen reader there are two columns of unrelated text.
Pair必填A <div> around each dt/dd, which the HTML specification allows inside a <dl> precisely so a pair can be laid out as a unit. It is what the hairline is drawn on, so the rule crosses the whole row rather than stopping in the column gap.
Term必填item.term, rendered as <dt> at --ink-3-aa. In the row layout it holds a 12rem column at sm and above and stacks below it, because a 12rem label column on a phone leaves the value about eight characters wide.
Description必填item.description, rendered as <dd> at --ink-2 with its browser margin reset. It takes a node, not a string, so a value can be a Badge, a link or a Timestamp.
Hairlinedivided, on by default: a --rule under every pair but the last. Turn it off inside a Card, which already has an edge of its own.

实践建议

推荐

  • Let it render nothing when items is empty. It returns null rather than an empty bordered box, so the page above is free to show an EmptyState instead of a hairline around no content.
  • Put an element in description when the value is a state or a link — it is a <dd>, so a Badge, an anchor or a Timestamp belongs there and a string of text does not have to be faked into one.
  • Pass id on each item when rows are added, removed or reordered. The index is the key without one, which is right for the fixed field list a record page renders and wrong for a list that changes shape.

避免

  • Do not reach for it to show several records. Every dt would repeat down the page and a reader comparing two records has to hold both in their head — that is what a Table’s column headings exist to avoid.
  • Do not use layout="row" inside a narrow sidebar. It only collapses on the sm breakpoint, which is the viewport, not the container — a 12rem label column inside a 20rem panel leaves nothing for the value. Use layout="stacked" there.

示例

record fields

A record seen from the front. The markup is a real dl/dt/dd, which is what tells a screen reader that "Owner" names the value beside it — a grid of divs looks identical and says nothing. The value is a node, so a state is a Badge and a date is a Timestamp rather than two strings formatted by hand.

Owner
Henry Chen
Region
ap-southeast-2
Status
Deployed
Last deploy

row and stacked

The same three fields under both layouts. row is the record-page shape — the labels line up down one edge and a reader scans them rather than reading them — and it collapses to one column under the sm breakpoint, because a 12rem label column on a phone leaves the value about eight characters wide. stacked keeps the value under its label at every width, which is the answer for a container that is narrow by design rather than by viewport: a card, a sidebar, a popover. Pick by the container, not by the number of fields.

Runtime
Node 24 on Cloudflare Workers
Region
ap-southeast-2
Build
pnpm --filter @misoto22/design build
Runtime
Node 24 on Cloudflare Workers
Region
ap-southeast-2
Build
pnpm --filter @misoto22/design build

a metadata rail

The shape it was built for: a detail page with the record on the left and its metadata in a rail beside it. The rail is stacked and undivided — it is already inside a bordered column, and a second set of hairlines inside one box is a grid pretending to be a table. Everything in the rail is a fact about the thing rather than part of it, which is the line to draw when deciding what belongs here: the title and the body are the record, the owner and the last deploy are what the page knows about it.

ui.misoto22.com

The documentation site for the design package. Statically exported, so every page is HTML on a CDN and the only JavaScript is the parts a reader touches.

类型

TSX
export type DescriptionListLayout = 'row' | 'stacked'

无障碍

  • 真正的 <dl>、<dt> 和 <dd>,读屏软件靠它才知道一个标签命名的是它旁边那个值。
  • 每一对包在一个 <div> 里,规范允许 <dl> 里出现它,辅助技术也会直接读穿过去。
  • 空列表渲染成 null,而不是一个空的 <dl>,所以不会有谁去播报一个一项都没有的列表。