容器
Toolbar 工具栏
贴在工作区边缘的那条动作栏。
用法
什么时候用它
import { Toolbar } from '@misoto22/design'说明
贴在工作区边缘的那条动作栏。
两个页面模板各自独立地做出了同一个东西——一条钉住的、底色是 --paper、沿着它所贴的那条边画着一根线的条带——这就是它该待在这里、而不是待在它们各自那里的信号。
底是不透明的 --paper,不是毛玻璃。内容是从这条栏底下滚过去的,所以任何半透明的处理都会把表格的最后一行放到提交按钮背后,两个都读不了;FloatingIconButton 用模糊,是因为它浮在一段空隙之上,那是另一个问题。
它不是 role="toolbar"。那个角色的约定是整条栏只占一个 Tab 停靠点、由方向键在里面的控件之间移动,而这里根本没有实现那一套——声明了角色却不给行为,等于叫读屏用户去按一些什么都不会发生的方向键。它是一个有名字的 group,所以控件各自保留在 Tab 顺序里的位置,而这条栏仍然会被播报。
结构
| 部件 | 说明 |
|---|---|
| Bar必填 | A <div role="group"> named by label, wrapping its children on a flex row at --z-sticky. It is not role="toolbar": that role promises a single tab stop with arrow keys between the controls, and this implements no such thing. |
| Ground必填 | Opaque --paper, and deliberately not a blur. Content scrolls UNDER this bar, so anything translucent puts the last row of a table behind the submit button and makes both unreadable. |
| Edge必填 | A --rule-2 hairline on the side the bar sticks to: border-t for bottom, border-b for top. position="static" keeps the rule and drops the stickiness. |
| Actions必填 | children, on a flex-wrap row with a --gap of 3. align places them along the inline axis and defaults to end, which is where a form’s primary action goes. |
实践建议
推荐
- Write label as what the bar IS — "Form actions", "List filters". A group with no name is announced as "group", and a page with two of them announces the same nothing twice.
- Give the scrolling ancestor a height for position="bottom". A sticky element sticks within its scroll container, so a bar inside a container that is as tall as its content has nothing to stick to and simply sits at the end.
- Keep it to the actions. A bar that has grown a title, a status and a breadcrumb is a page header, and a page header that follows the reader down the screen is a page with less of itself visible.
避免
- Do not add role="toolbar" through props. The role tells a screen-reader user that arrow keys move between the controls; nothing here implements roving tabindex, so those keys would do nothing and the promise would be false.
- Do not make the ground translucent to "let the content show through". The content it would show through is the row the reader is trying to read, and the button they are trying to press.
示例
form actions
The bar of actions at the foot of a working surface. The ground is opaque --paper rather than a blur, because content scrolls under it — anything translucent puts the last row of the form behind the submit button. It is a named group and not role="toolbar": that role promises arrow keys between the controls, and nothing here implements them.
Scroll this panel — the bar below stays where it is.
A sticky element sticks within its scroll container, so the container needs a height of its own for the bar to have anything to stick to.
Keep the bar to the actions. One that has grown a title and a breadcrumb is a page header following the reader down the screen.
a filter bar on top
The same component at the other edge. position top sticks the bar to the head of its scroll container and moves the hairline to the bottom, so a reader scrolling a long list keeps the controls that shaped it. align between is what the shape needs here: the filters read from the start edge and the action sits at the end, which is one bar rather than a filter row and a button row. A sticky element sticks inside its scroll container, so the container is the thing that needs a height — not the bar.
- a1b2c3d
- 9f8e7d6
- 4c5b6a7
- 77aa2b1
- e3d4c5b
- 0099aab
- 12ff34e
every control is a tab stop
A bulk-action bar, and the reason the role is group rather than toolbar. The ARIA toolbar contract is a single tab stop with the arrow keys moving between the controls inside it; this implements no roving tabindex, so declaring that role would tell a screen-reader user to press keys that do nothing. As a named group every control keeps its own place in the tab order — Tab reaches Archive, then Delete, then Clear — and the bar is still announced by name, which is why label is required rather than optional.
类型
export type ToolbarPosition = 'bottom' | 'top' | 'static'
export type ToolbarAlign = 'start' | 'center' | 'end' | 'between'键盘操作
| 按键 | 作用 |
|---|---|
| Tab | 依次走到每一个控件——这条栏本身不是一个停靠点。 |
无障碍
- label 必填,它就是这个 group 的可访问名称,所以一个同时有筛选条和动作条的页面播报出来的是两个不同的东西。
- 每个控件都保留自己在 Tab 顺序里的位置,因为这条栏刻意不去声明 role="toolbar",也就不去认它那份“只占一个 Tab 停靠点”的约定。
- 底是不透明的,所以栏上的控件对比度永远是对着 --paper 算的,而不是对着此刻正从背后滚过去的随便什么东西。