空状态
站在本该有内容却没有内容的位置上——没有结果的搜索、还没有人添加过东西的列表、第一项出现之前的页面——说明缺的是什么、为什么缺或该怎么做,并提供完成这件事的操作。
- 类别
- 信息传达
- 令牌
comp.empty-state- 层级
- 基础
- Web
tanrei-empty-stateschedule - SwiftUI
TanreiEmptyStateradio_button_unchecked - Jetpack Compose
TanreiEmptyStateradio_button_unchecked - Flutter
TanreiEmptyStateradio_button_unchecked - React Native
EmptyStateradio_button_unchecked
生成自 docs/status/status.json, specs/empty-state.spec.json
还没有人添加过东西的列表,以及什么也没找到的搜索
空状态站在本该有内容、却什么也没有的位置上:它取代列表、行或结果。它用几个词说明缺的是什么,用一句话说明为什么缺或该怎么做,并提供完成这件事的控件。它低调而扁平——没有填充,没有边线——而且它是内容,在它所在的位置被读到:它不接受焦点,也没有状态。
在 Web 上它是 <tanrei-empty-state>。其他平台尚未实现。
描述是元素自己的内容。标题是 headline 属性,它以 level 给出的级别作为标题元素公开;如果标题是你自己的标题元素或其他内容,则放进 title 插槽。图标集里的字形或你自己的图片放进 media 插槽,你自己的 Button 或链接放进 actions 插槽。每一个字都由你来写:组件自己不带任何文字。
<!-- 视图里的一块区域:一个字形、一个标题、一句话,以及操作。 -->
<tanrei-empty-state headline="还没有项目">
<span slot="media">folder_open</span>
项目把一个设计系统的设计层、导出内容和成员放在一起。
<tanrei-button slot="actions">新建项目</tanrei-button>
<tanrei-button slot="actions" variant="text">导入设计层</tanrei-button>
</tanrei-empty-state>
<!-- 整个页面都是空的:用大号,它的标题就是页面的标题元素。 -->
<tanrei-empty-state size="large">
<h1 slot="title">此页面不存在</h1>
地址可能输入有误,或者页面已被移走。
<a slot="actions" class="tanrei-link" href="/">前往首页</a>
</tanrei-empty-state>
<!-- 在侧边面板里:靠起始侧边缘,也就是列表本该开始的地方。 -->
<tanrei-empty-state align="start" headline="目前与基础值没有差异">
在左侧更改一个值,它就会列在这里。
</tanrei-empty-state>
<!-- 作为对筛选的回应:显示出来时,以礼貌的方式朗读一次。 -->
<tanrei-empty-state id="no-rows" live hidden headline="没有符合这些筛选条件的条目">
已启用三个筛选条件。移除其中一个,会显示更多条目。
<tanrei-button slot="actions" variant="outlined">清除筛选条件</tanrei-button>
</tanrei-empty-state>
<script type="module">
import '@tanrei/web/components/empty-state';
import '@tanrei/web/empty-state.css'; // 服务器渲染的空状态在元素定义之前所占的位置
</script>import { Button, EmptyState } from '@tanrei/react';
{rows.length === 0 && (
<EmptyState live headline="没有符合这些筛选条件的条目">
已启用三个筛选条件。移除其中一个,会显示更多条目。
<Button slot="actions" variant="outlined" onClick={clearFilters}>清除筛选条件</Button>
</EmptyState>
)}各个部件,逐一加上
靠起始侧边缘,以及大号尺寸
构成
构成
title 之上。| 部件 | 说明 |
|---|---|
1 container | The block that holds the message, as wide as the region it stands in and as tall as its content and its padding, with no fill, no edge and no elevation: it is drawn on the surface of the region whose content is missing. It carries no state. |
2 media · 可选 | What pictures the empty place, above the title: a glyph of the system's icon set, in a quiet color, or a picture of the product's own. Decorative: the title says it. |
3 title | What is missing, in a few words ("No results", "No projects yet"). The one part at the surface's full text color. Plain text unless the product gives a heading; it wraps and is never cut. |
4 description · 可选 | Why the place is empty, or what the person can do about it, in a sentence or two, in a quiet color. The product's content: text, with links where it needs them. |
5 actions · 可选 | One or two of the product's own Buttons or links that fill the place or change what was asked for ("New project", "Clear the filter"), in a row below the text. |
尺寸
尺寸
| 长度 | 令牌 | 值(px) |
|---|---|---|
container padding-inline | tanrei.comp.empty-state.container.padding-inline | 16 |
title 之上。属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | "" | What is missing, in a few words, in the product's language ("No results for tokne"). Exposed as a heading of the level `level` gives, since it is drawn as one. The title slot takes its place where the product needs inline content or a heading element of its own. |
level | number | 2 | The level of the heading the title prop is exposed as, 1 to 6, so that the empty state takes its place in the outline of the page around it: 2, the default, for the main region of a view under the page's own title; deeper inside a section, a card or a panel that has a heading of its own. Ignored where the title slot holds the product's own content, whose role and level are the product's. |
size | medium | large | "medium" | Medium is for an empty region inside a view: a list, a table's body, a card, a panel, a search's results. Large is for a view that is empty as a whole: the main region of a page before its first item, a page that was not found. It changes the room above and below, the glyph's size and the text styles. A design system may add values. |
align | center | start | "center" | Where the parts stand on the inline axis. Center, the default, is for a region that is otherwise blank. Start puts every part at the region's start edge with its text aligned there, for a narrow column, a list in a side panel or a table whose header stays in view, where the content would have begun at that edge. |
live | boolean | false | Whether the empty state is announced. False, the default, is for a place that is empty when its view is presented: it is read where it stands. True is for one that appears in answer to what the person did (a search that found nothing, a filter that matches no row, the last item removed): it is a live region, announced once, politely, when it is shown and when its text changes. It is never announced assertively and never takes focus: an empty place is not an error. It changes nothing that is drawn. |
插槽
| 插槽 | 说明 |
|---|---|
default | The description: text, with the product's own links and inline content. Empty draws the title alone. |
media | A glyph of the system's icon set, by name, drawn through the platform's icon helper at the media size; or a picture of the product's own (an illustration), which keeps its own size and colors. |
title | The product's own title content in place of the title prop: a heading of the level the page's outline needs, or text with inline content. The empty state draws it in the title's style and changes neither its role nor its level. |
actions | The actions: the product's own Buttons or links, one or two, the main one first. The empty state lays them out below the text and does nothing else to them. |
状态
行为
以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。
| ID | 需求 |
|---|---|
EMP-1 | THE empty state SHALL lay itself out as a block that takes the whole inline size of the region it is placed in and is as tall as its content and its padding: it SHALL lay out in one column, in this order, its media, its title, its description and its actions, those it has, padded above the first and below the last by the block padding of its size (comp.empty-state.container.padding-block.medium for medium, comp.empty-state.container.padding-block.large for large) and at the start and the end by comp.empty-state.container.padding-inline. THE empty state SHALL draw no fill, no edge and no elevation, SHALL bring no outer space of its own, SHALL set no height of its own and SHALL NOT scroll: where the region is taller, placing the block in it is the region's. |
EMP-2 | WHILE align is center THE empty state SHALL centre every part on the inline axis of its container and centre the lines of its title and its description; WHILE align is start THE empty state SHALL place every part at the container's start edge, inside the inline padding, and align the lines of its text to the start. In both THE empty state SHALL keep its title and its description no wider than comp.empty-state.content.measure times the description's font size, and no wider than the container less its inline padding. |
EMP-3 | WHERE the media slot holds a glyph THE empty state SHALL draw it first, in a box of the media size of its size (comp.empty-state.media.size.medium for medium, comp.empty-state.media.size.large for large), in comp.empty-state.media.color, with sys.icon.family.unselected and sys.icon.fill.unselected, sys.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max, and SHALL begin the title comp.empty-state.media.gap below it. WHERE the slot holds a picture or a view of the product's own THE empty state SHALL draw it in the same place at its own size, never wider than the container less its inline padding and with its proportions kept, and SHALL NOT color, clip or mirror it. THE empty state SHALL keep the media from assistive technology, and SHALL draw no media the product did not give. |
EMP-4 | THE empty state SHALL draw its title in the text style of its size (comp.empty-state.title.typescale.medium for medium, comp.empty-state.title.typescale.large for large) and in comp.empty-state.title.color; the title SHALL wrap and SHALL never be cut. THE empty state SHALL expose a title given as text as a heading — of the level the level prop gives on a platform whose headings have levels, and with the platform's heading trait on one whose headings have none — because it is drawn larger and heavier than what follows it, and a text drawn as a heading and not exposed as one tells by its look alone what its structure is. IF level is not a whole number from 1 to 6 THE empty state SHALL use 2 and report it in debug builds. WHERE the title slot holds content THE empty state SHALL draw that content in the title's place, style and color instead of the title prop, and SHALL change neither its role nor its level: a title the product gives as a heading stays a heading of the level the product gave it, and inline content the product gives stays what it is. THE empty state SHALL set the title's letter-spacing by the script of its text, as every title is set (PRS-20): no negative letter-spacing WHERE its language is Chinese, Japanese or Korean, and none WHERE its script joins its letters or stacks marks on them. |
EMP-5 | WHERE the default slot holds content THE empty state SHALL draw it as the description, comp.empty-state.title.gap below the title, in the text style of its size (comp.empty-state.description.typescale.medium for medium, comp.empty-state.description.typescale.large for large) and in comp.empty-state.description.color; the description SHALL wrap and SHALL never be cut or limited to a number of lines. A link in the description is the product's own and is drawn by its own spec, in its own color and underlined. |
EMP-6 | WHERE the actions slot holds content THE empty state SHALL lay it out in one row comp.empty-state.actions.gap below the text, in the order given from the start, comp.empty-state.actions.item-gap apart, centred or at the start as align says (EMP-2); WHERE the row does not fit the container THE empty state SHALL wrap it, comp.empty-state.actions.item-gap between its lines, and SHALL never cut or shrink an action below its own minimum. THE empty state SHALL NOT restyle, resize, reorder or rename the actions, and SHALL emit no event of its own: an action's press is that control's. |
EMP-7 | THE empty state SHALL NOT take pointer, touch or keyboard input, SHALL NOT take focus or be a stop in the focus order, and SHALL NOT change anything it draws under a pointer, a press or focus: it has no hovered, focused, pressed, selected or disabled state and draws no state layer and no focus indicator. What the product places in it — a link in the description, a Button in the actions — takes input and focus by its own spec, in the order the parts are drawn, and text in the title and the description SHALL remain selectable where the platform lets text be selected. |
EMP-8 | WHILE live is false THE empty state SHALL be exposed to assistive technology as plain content, read where it stands in reading order: its title as a heading (of the level level gives, or the product's own content in the title slot as the product gave it), then its description, then its actions as the controls they are. It SHALL have no role and no name of its own, SHALL NOT be announced when it is shown or when its text changes, SHALL NOT move focus, and SHALL NOT be exposed as an alert, a status, a dialog or a note: an empty place is content, not a message. |
EMP-9 | WHILE live is true THE empty state SHALL be a polite live region. WHEN it is shown after its page or view was first presented, and WHEN its title or its description changes while it is shown, THE empty state SHALL have assistive technology announce it once, after what is being read: the title, then the description, and not the names of its actions. It SHALL never announce assertively and SHALL NOT move keyboard or screen-reader focus. A live empty state that is drawn with the first presentation of its page or view SHALL NOT announce itself then. live SHALL change nothing that is drawn. |
EMP-10 | WHEN title, the title slot's content, the description, the media or the actions change THE empty state SHALL draw and expose the new value at once, without a motion, and SHALL announce it only as EMP-9 says. THE empty state SHALL neither show nor hide itself: whether the place is empty is the product's to know, and the product puts the empty state there and takes it away. |
EMP-11 | WHILE the text size is enlarged THE empty state SHALL grow its title and its description, and the measure with them, keep its paddings and gaps, wrap, and grow in height; it SHALL keep a glyph's box at the media size of its size, SHALL cut nothing and set no fixed height, SHALL lose no content when the reader raises line height, paragraph spacing, letter spacing or word spacing, and SHALL need no horizontal scrolling at a width of 320 points with text at 200 %, its actions then each on a line of their own where two do not fit side by side. |
EMP-12 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE empty state SHALL draw its glyph, its title and its description in the platform's text color, and leave links, Buttons and a picture of the product's own to their own specs and colors; nothing the empty state draws relies on a fill. |
EMP-13 | WHERE the layout direction is right-to-left THE empty state SHALL apply its paddings by logical start and end and mirror the order of its actions, so that the first action is at the right; WHILE align is start it SHALL place every part at the right edge and align its text to the right; WHILE align is center the column stays centred. |
EMP-14 | THE empty state SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the empty state is shown THE empty state SHALL draw itself for the new direction at once, without a motion of its own, keeping what it shows. |
EMP-15 | WHERE the layout direction is right-to-left THE empty state SHALL draw the glyph of its media slot mirrored left to right exactly when the system's icon set marks that glyph's name as mirrored or the product forces mirroring for that use, and unmirrored when the product forbids it: the icon helper mirrors the glyph inside its own box, about the box's vertical centre line, changing neither its place, size, color and fill nor the order EMP-13 gives. THE empty state SHALL NOT mirror a glyph itself, and SHALL NOT mirror content the product gives as a picture or a view of its own rather than by a glyph's name. |
EMP-16 | THE empty state SHALL lay out a title given as text as text of its own direction, isolated from the text around it: it takes the base direction of its first strong character, or the layout direction where it has none, so that a title that quotes what the person typed, a file's name or a number keeps its own order in a right-to-left layout; THE empty state SHALL still place the title and align its lines by the layout direction and by align, not by the text's own. The description's direction is its content's. |
EMP-17 | WHEN size names a value that the active design system does not have, or align names a value this spec does not have, THE empty state SHALL render with the default value, and in debug builds report the unknown value. |
EMP-18 | IF the title, the title slot and the description are all empty THE empty state SHALL draw nothing, take no space and expose nothing, and in debug builds report that an empty state says nothing: a glyph or actions alone do not tell a person what is missing. IF the description is given and the title and the title slot are empty THE empty state SHALL still render and in debug builds report that an empty state has no title. |
各平台名称对照
对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 组件 | tanrei-empty-state | TanreiEmptyState | TanreiEmptyState | TanreiEmptyState | EmptyState |
title · 属性 | headline | title | title | title | title |
level · 属性 | level | headingLevel | headingLevel | headingLevel | headingLevel |
size · 属性 | size | size | size | size | size |
align · 属性 | align | alignment | alignment | alignment | align |
live · 属性 | live | isLive | live | live | live |
| 备注 | It is the form the callout takes. <tanrei-empty-state> with the description as light-DOM children, the glyph or the picture in the media slot, the title in the title slot (<p slot="title">, or <h2 slot="title"> where it is a heading) or the headline attribute (HTML's global title attribute would show a tooltip over the whole block, as on the callout), which the shadow root draws as an element with role="heading" and aria-level from level (a number property and attribute, 2 by default), and Buttons or links in the actions slot. Everything the product writes stays in the document: the title where it is slotted, the description, the links and the Buttons are light-DOM content, read by a crawler and by a page without script. **Before the element is defined** the stylesheet of @tanrei/web gives tanrei-empty-state:not(:defined) display: block, the block and inline padding of its size, the description's text style and color, and text-align by its align, so the content is readable in its place and the block keeps most of its height; a headline attribute does not show until the element is defined, which is why a page that must read without script gives the title in the slot. The host has no role, no tabindex and no listeners; the glyph is aria-hidden. With live the announcement is made through a visually hidden role="status" region in the shadow root that exists from the element's first render and is filled, with the title and the description's text, only when the element is connected or changed after the document's first presentation (EMP-9); the host itself gets no live role, so slotted content is not read twice. The text block's width is max-inline-size: calc(var(--tanrei-comp-empty-state-content-measure) * 1em) on the element that carries the description's font size (EMP-2). The description takes its color and text style through inherited color and font, so text placed directly in the element is styled. In a table the element goes in one <td colspan> of a body row, or directly after the table, which keeps its caption and its header row (the Table spec says nothing of an empty body); in a list it takes the list's place rather than being an item. Nothing changes under forced-colors: active but the colors the mode sets (EMP-12). | TanreiEmptyState(_ title: String, size:, alignment:, isLive:, media:, actions:) { description }, the glyph given by name through icon: or a view through media:; a heading title is the title: view builder with the header trait set by the product. Not the platform's own unavailable-content view, whose sizes and colors are the platform's: the same structure drawn from Tanrei's tokens. isLive posts one announcement with the title and the description when the view appears after its screen did. Not built on this platform yet: EMP-4's title given as text exposed as a heading, and level. | TanreiEmptyState(title, size, alignment, live, media, actions) { description }, the glyph given by name through icon or a composable through media. With live the text column has liveRegion = LiveRegionMode.Polite, set only after the first composition of its screen. Not built on this platform yet: EMP-4's title given as text exposed as a heading, and level. | TanreiEmptyState(title: …, size:, alignment:, live:, icon:, media:, actions: […], child: description); titleWidget: takes a heading of the product's own. With live the text is wrapped in Semantics(liveRegion: true) after the first frame of its route, and announced through SemanticsService.announce with polite assertiveness where the platform does not read live regions. Not built on this platform yet: EMP-4's title given as text exposed as a heading, and level. | <EmptyState title="No projects yet" icon="folder_open" actions={…}>description</EmptyState>; media takes an element of the product's own. With live, accessibilityLiveRegion="polite" on Android and one AccessibilityInfo.announceForAccessibility call on iOS when it mounts after its screen. Follows the app's direction where an ancestor's is not known. Not built on this platform yet: EMP-4's title given as text exposed as a heading, and level. |
令牌
| 令牌 | 引用 | 解析值(浅色) |
|---|---|---|
--tanrei-comp-empty-state-actions-gap | sys.space.space200 | 16px |
--tanrei-comp-empty-state-actions-item-gap | sys.space.space100 | 8px |
--tanrei-comp-empty-state-container-padding-block-large | sys.space.space800 | 64px |
--tanrei-comp-empty-state-container-padding-block-medium | sys.space.space400 | 32px |
--tanrei-comp-empty-state-container-padding-inline | sys.space.space200 | 16px |
--tanrei-comp-empty-state-content-measure | — | 30 |
--tanrei-comp-empty-state-description-color | sys.color.on-surface-variant | |
--tanrei-comp-empty-state-media-color | sys.color.on-surface-variant | |
--tanrei-comp-empty-state-media-gap | sys.space.space200 | 16px |
--tanrei-comp-empty-state-media-size-large | — | 48px |
--tanrei-comp-empty-state-media-size-medium | sys.icon.large | 32px |
--tanrei-comp-empty-state-title-color | sys.color.on-surface | |
--tanrei-comp-empty-state-title-gap | sys.space.space50 | 4px |
--tanrei-comp-empty-state-description-typescale-large | sys.typescale.body-large | 400 16px/1.625 |
--tanrei-comp-empty-state-description-typescale-medium | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-empty-state-title-typescale-large | sys.typescale.heading-3 | 500 20px/1.4 |
--tanrei-comp-empty-state-title-typescale-medium | sys.typescale.heading-5 | 500 16px/1.5 |
测试样例
作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。
size: medium (12)
title
title: No projects yet description
title: No projects yet media
title: No projects yet slots: media actions
title: No projects yet slots: media, actions start
title: No projects yet align: start slots: media, actions search-no-results
title: No results for “tokne” live: true slots: media, actions table-no-rows
title: No entries match these filters slots: actions list-no-items
title: Nothing differs from the base yet align: start long-text
title: No results for “the contrast of a status… slots: media, actions actions-rtl
title: لا توجد مشاريع بعد direction: rtl slots: media, actions start-rtl
title: لا توجد مشاريع بعد align: start direction: rtl slots: media, actions search-no-results-rtl
title: لا نتائج عن “tokne” live: true direction: rtl slots: media, actions size: large (4)
large
title: No projects yet size: large slots: media, actions large-title
title: This page does not exist size: large large-start
title: This page does not exist size: large align: start slots: actions title-slot
size: large slots: title, actions 生成自 specs/empty-state.spec.json, tokens/comp/empty-state.tokens.json, design/tokens/
| 组件 | 它是什么 | 它在哪里 |
|---|---|---|
| 空状态 | 没有内容时,站在内容位置上的东西。 | 列表、行或结果本该在的地方。 |
| 提示块 | 关于现有内容的一条通知。 | 在那些内容旁边,位于文档流中。 |
| 消息条 | 刚刚发生的某件事的结果。 | 在内容上方,短暂停留。 |
| 搜索的状态文本 | 找到了多少条(“没有找到‘令排’的结果”)。 | 在搜索自己的状态行里。 |
空着的地方不是错误。加载失败的内容,是同一位置上一个带错误状态的提示块;仍在加载的内容,是一个加载指示器。一块区域的三种状态——加载中、空、失败——由这块区域自己来切换;空状态既不显示自己,也不隐藏自己。
size |
上下留白 | 字形 | 标题 | 描述 | 用于 |
|---|---|---|---|---|---|
medium(默认) |
32 | 32 | 16 | 14 | 视图里的一块区域:列表、表格的主体、卡片、面板、搜索的结果。 |
large |
64 | 48 | 20 | 16 | 整个都是空的视图:第一项出现之前的页面、没有找到的页面。 |
这个块与它所在的区域一样宽,高度则等于它的内容加上内边距。区域比它高时,把这个块放在区域里的什么位置——让它居中、保持表格的最小高度——由区域决定。文本每行限制在大约 60 个拉丁字符(描述字号的 30 倍),所以宽表格中间的一句话不会从一边排到另一边。
align 为 center(默认)时,用于除此之外一片空白的区域;为 start 时,用于窄栏、侧边面板里的列表,或者表头始终可见的表格:这时消息从内容本该开始的地方开始。
headline 画出来就是标题的样子,所以以标题元素公开:默认级别为 2,适合页面自身标题下方的视图主区域。请把 level 设为页面大纲需要的级别:在自带标题的分区、卡片或面板里,用更深的级别。title 插槽里的标题会画成普通的标题,空状态既不改变它的角色,也不改变它的级别:在那里放你自己的标题元素;标题不应成为标题元素时,就放文本。
在由服务器渲染的页面上,请把标题放进 title 插槽:headline 属性是由元素绘制的,所以在元素定义之前不会显示。
字形以它的名称给出,写在一个元素里:<span slot="media">folder_open</span>。它按媒体尺寸绘制,用低调的颜色。插槽里的其他任何东西——<img>、<svg>、你自己的元素——都是图片:它保持自己的尺寸和颜色,绝不会比这个块更宽,也绝不镜像。媒体是装饰性的,并且对辅助技术隐藏,所以不要把唯一的出路放在图片里。Tanrei 不自带任何插图。
操作是你自己的 Button 或链接,一到两个,主要的那个放在前面。空状态把它们排成会换行的一行,除此之外不对它们做任何事。标签比所在的行还长的操作会独占这一行:这时 Button 会让标签换行,所以什么都不会被截断,也不会伸出这个块。
在窄栏里,每个操作各占一行,长标签会换行
live 用于由用户刚刚做的事所导致的空状态:没有任何行符合的筛选条件、最后一项被移除。它在显示出来时,以及文本变化时,会以礼貌(polite)的方式朗读一次——先是标题,再是描述,不包括操作。用 hidden 把它留在页面里,或者在适用的时候再添加它。
带 live 的空状态,作为对一次按下的回应而显示
写在页面标记里、并在页面加载时就显示的空状态,在那时不会被朗读。在 Web 上,页面加载之后由你的脚本添加进来的带 live 的空状态会被朗读;元素无法分辨一个空状态是作为对某件事的回应而添加的,还是随单页应用的新视图一起添加的,所以对于只是新视图一部分的空状态,请不要写 live。
在搜索中,结果的数量是搜索自己的状态文本,本来就会被朗读。如果你还在搜索的视图里显示空状态,请不要写它的 live,否则同样的文字会读两遍。
没有行的表格保留它的说明文字和表头行;空状态站在表体的行本该在的地方——放在一个横跨各列的单元格里,或者紧跟在表格之后。没有项的列表并不是只有一项的列表:空状态取代的是整个列表。
描述、标题插槽和操作都是元素自己的子元素,所以它们就在文档里:页面内查找能找到,可以被翻译、被选中,没有脚本也能读到。@tanrei/web/empty-state.css 为尚未定义的空状态提供内边距、文本样式和对齐方式,所以这些文字在它们将来所在的位置上就是可读的。在此之前,它显示时不带字形。
- 在标题里用平实的话说明缺的是什么,在描述里说明该怎么做。
- 提供能把这个位置填上、或者能改变所提要求的操作。
- 搜索没有结果时,在标题里引用用户键入的内容。
- 不要给它状态颜色或警报(alert)角色。空着的地方只是关于内容的一个事实。
- 不要对视图打开时就是空的地方使用
live。 - 不要手工画一句居中的灰色文字,也不要画一个只有一项、而这一项又不是项的列表。
- 不要把它用于控件内部一行的“这里什么也没有”,例如没有选项的菜单:那是控件自己的文本。
- 角色
none- 标注
- An empty state that is not live has no role and no name: it is content, read where it stands — the title (a heading of the level level gives, or the product's own content in the title slot as the product gave it), the description, then the actions, which are the product's own controls with their own names. A live empty state is a polite live region instead (the status role on the web), announced once when it is shown after its view was presented and when its text changes. The glyph or the picture is hidden.
- 最小目标
- 即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px
键盘操作
| 按键 | 动作 |
|---|
备注
- Not interactive: the empty state takes no input and no focus (EMP-7), so there is no target of its own to size and minimumTarget holds trivially. Its actions meet the minimum target by their own specs.
- Never an alert. An empty place is a fact about the content, not something that went wrong: it is announced only when it results from what the person just did (live), and then politely (EMP-9). A failure to load is a different thing and is said by a callout with the error status in the same place.
- It replaces content, so it must not hide that the container is still there. A table that has no rows keeps its caption and its header; the empty state stands where the body's rows would be (on the web, outside the table element or in one cell that spans the columns, as the product's table allows). A list with no items is not exposed as a list of one item: the empty state stands in the list's place.
- In a search, the count of results is the search's own status text, which is already a live region (SRC-48). Where the product also shows an empty state in the search's view, only one of the two is live, or the same words are announced twice: leave the empty state's live false there and let the status text speak.
- The title is a heading (EMP-4) because it looks like one (WCAG 1.3.1). Its level is the product's to fit the page's outline: 2 by default, for the main region of a view under the page's title; deeper inside a section, a card or a panel that has a heading of its own. A product whose title must not be a heading gives it as inline content in the title slot.
- Contrast in the default system: the title is on-surface and the description and the glyph are on-surface-variant, each held to 4.5:1 on every surface tone by the token invariants, so the empty state can stand on a page, in a card or in a panel. The glyph is decorative.
- Say what to do, not only what is missing: a description and an action are what make an empty place a way forward. Do not put the only way forward in the picture.
- Text size, text spacing and reflow: EMP-11.
生成自 specs/empty-state.spec.json, tokens/sys/measure.tokens.json
- 它是没有角色、也没有名称的普通内容:标题、描述,然后是操作,在它们所在的位置被读到。
- 带
live时,它通过自己的状态区域以礼貌的方式朗读一次:在页面呈现之后它被显示出来时,以及它的标题或描述变化时。它从不以立即打断(assertive)的方式朗读。 - 字形或图片对辅助技术隐藏。
- 以文本给出的标题是
level给出级别的标题元素。你放进title插槽的内容保留它的角色和级别。 - 它不是焦点顺序中的停靠点,也从不移动焦点。Tab 先到达描述里的链接,再到达操作。
- 文本放大时,所有内容都会换行并增高;在宽度为 320、文本为 200% 时,没有任何东西需要横向滚动,每个操作各占一行。
- 在强制颜色模式下,字形、标题和描述都用文本颜色绘制。没有任何东西依赖填充。
- 在从右到左的布局中,起始侧是右边缘,第一个操作在右边;图标集标明需要镜像的字形会镜像;标题里引用的从左到右的查询词保持它自己的顺序。
- 调试构建会报告:未知的尺寸或对齐方式、既没有标题也没有描述的空状态,以及有描述却没有标题的空状态。
- 每一个字都用页面的语言来写。组件不提供任何文字。
- 让
level符合页面大纲,或者在title插槽里放你自己的标题元素或文本。 - 只对作为对某件事的回应而出现的空状态使用
live,并且绝不要放在另一个说着同样内容的实时区域旁边。 - 不要把控件放进
media插槽:它对辅助技术是隐藏的。 - 给操作一个放得下的标签。行太短时标签会换行,但比一行还长的单个词无法换行。