页头
页面内容的开头部分。页面的标题作为标题元素,配有一句说明页面是什么的话、一行关于页面的信息、针对整个页面的操作,上方还有返回上级的途径。
- 类别
- 容器
- 令牌
comp.page-header- 层级
- 基础
- Web
tanrei-page-headerschedule - SwiftUI
TanreiPageHeaderradio_button_unchecked - Jetpack Compose
TanreiPageHeaderradio_button_unchecked - Flutter
TanreiPageHeaderradio_button_unchecked - React Native
PageHeaderradio_button_unchecked
生成自 docs/status/status.json, specs/page-header.spec.json
带有全部部件的页头
设计系统
把设计系统作为一个层来添加
层可以改变取值,也可以扩展词汇。它绝不会分叉出另一个组件。
页头是页面内容的开头。它包含页面的标题、一两句说明页面是什么的话、一行简短的信息、针对整个页面的操作,上方还有返回上级的途径。除标题以外,每个部件都是可选的。
它不是应用栏。应用栏是窗口的栏:它留在顶边,容纳产品的名称和图标按钮。页头位于内容之中,随内容一起滚走,容纳的是带标签的 Button。
在 Web 上,它是包在页面自己的元素外面的 <tanrei-page-header>。这个元素没有 shadow root。标题就是你自己的 h1:爬虫、阅读模式和不运行脚本的页面都能找到它,脚本到达时也不会有任何东西移动。其他平台尚未实现。
(本页的示例使用 3 级标题,因为本页已经有自己的标题。)
<link rel="stylesheet" href="@tanrei/web/page-header.css" />
<tanrei-page-header>
<p data-part="eyebrow">公告</p>
<h1>令牌由推导而来,设计系统是一个个层</h1>
<p>一个值只在一处改动,建立在它之上的一切随之改变。</p>
<div data-part="meta">
<time datetime="2026-09-20">2026 年 9 月 20 日</time>
<span>Tanrei 团队</span>
<span>阅读约 4 分钟</span>
</div>
<div data-part="actions">
<tanrei-button variant="outlined">编辑</tanrei-button>
<tanrei-button>分享</tanrei-button>
</div>
</tanrei-page-header>
<script type="module">
import '@tanrei/web/components/page-header';
</script>import { Button, PageHeader } from '@tanrei/react';
import '@tanrei/web/page-header.css';
<PageHeader divider>
<h1>{project.name}</h1>
<p>{project.summary}</p>
<div data-part="actions">
<Button onClick={invite}>邀请</Button>
</div>
</PageHeader>请按阅读的顺序书写各个部件。在任何宽度下,这个顺序同时也是焦点顺序。
| 部件 | 在 Web 上 |
|---|---|
| 面包屑导航 | <tanrei-breadcrumbs>,或者包着一个返回用的 Link 或你自己的路径的 <div data-part="breadcrumbs">。 |
| 眉标 | <p data-part="eyebrow">。按平常的写法书写;大写字母是绘制时加上的。 |
| 标题 | 标题元素:页面用 h1,页面的一部分用 h2,或者采用你的大纲所需要的级别。 |
| 描述 | 第一个不带 data-part 的 <p>。 |
| 元信息 | <div data-part="meta">。每个子元素是一项:一个 <time>、一个 <span>、一个 Link、一个 Tag。 |
| 操作 | 包着你的 Button 和 Link 的 <div data-part="actions">。 |
子元素中没有相应的元素时,headline、description 和 eyebrow 会替你写出这些元素。level 设置标题的级别;0(默认)表示由尺寸决定。
<tanrei-page-header headline="成员" description="可以打开此项目的人。"></tanrei-page-header>这种形式要等脚本运行之后才会画出来。在内容页面上,请自己书写这些元素。
三种尺寸
成员
可以打开此项目的人。
字体排印
文本样式按层级命名,绝不按尺寸命名。
一套系统,五个平台。
同样的令牌、同样的组件和同样的无障碍契约,各以自己的惯用方式呈现。
构成
| 部件 | 说明 |
|---|---|
1 container | The block that holds the head, as wide as its place in the layout and as tall as its parts, with no fill, no edge and no shape of its own: it is drawn on the surface the page is drawn on. It carries no state: nothing about it answers a pointer, a press or focus. |
2 breadcrumbs · 可选 | What stands above the title and leads back up: the product's Breadcrumbs, or one Link to the level above. It keeps its own look, its own landmark and its own behaviour; the page header only gives it a place. |
3 eyebrow · 可选 | A few words directly above the title that say what the page belongs to — a category, a product, a version — small, tracked wide and in capitals where the script has them. Text, not a heading and not a link. |
4 title | The page's title, and the heading of its level in the page's outline: level 1 for a page, level 2 for a part of one. It wraps onto as many lines as it needs and is never cut, hidden or drawn twice. |
5 description · 可选 | One or two sentences under the title that say what the page is: a lead, quieter than the title and no longer than a line of reading text. A paragraph, with the inline content the product gives it. |
6 meta · 可选 | A line of short facts about the page under the description — a date, an author's avatar and name, a reading time, a state, tags — each one item the product gives, in a row that wraps. |
7 separator · 可选 | The small dot the page header draws between two items that share a row of the meta line. Decorative, never at the start or the end of a row. |
8 actions · 可选 | The product's own Buttons and Links for the whole page: beside the title at the end edge while the head is wide, below the text while it is narrow or where the product asks. |
9 divider · 可选 | One hairline under the head, across its width, where the product asks for it: for a head above content that is not text (a table, a list of records, a set of tabs). |
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | "" | The page's title, as plain text. The page header draws it as a heading (PGH-2). The title slot takes its place where the product gives its own heading: on the web that is the page's own heading element, which is how a content page should give it. |
description | string | "" | One or two sentences under the title that say what the page is. Empty draws none. The description slot takes its place where the sentence holds a link, inline code or other inline content. |
eyebrow | string | "" | A few words above the title that say what the page belongs to ("Announcements", "Design system"). Plain text; empty draws none. Written as ordinary words: the page header draws the capitals. |
size | small | medium | large | "medium" | What the head opens. medium: a page. small: a part of a page (a section of a settings page, one record in a list of records). large: a landing page, whose title is a statement. A size sets the title's and the description's text styles and the spaces that go with them, and the heading's level where level is 0; it changes no part's place. A design system may add values. |
level | number | 0 | The level of the heading the title is exposed as, 1 to 6, where the page header makes the heading itself. 0, the default, takes it from the size: 1 for medium and large, 2 for small. Ignored where the product gives its own heading in the title slot, whose level is the product's. |
actionsPlacement | end | below | "end" | Where the actions stand. end: beside the title at the end edge while the head is at least comp.page-header.wide.container-width wide, and below the text while it is narrower. below: under the text at every width, for an article's head and a landing page's, whose actions follow the sentence that leads to them. |
divider | boolean | false | Draws one hairline under the head. For a head above content that is not text — a table, a list of records, tabs — where the eye needs an edge to start from; above an article it is not needed. |
插槽
| 插槽 | 说明 |
|---|---|
breadcrumbs | What stands above the title: the product's Breadcrumbs, or one standalone Link back to the level above. The page header places it and does nothing else to it. A fixture's string is drawn as plain text in its place. |
title | The product's own heading in place of the title prop: the page's heading element with its text and any inline content (inline code, a word in another language). The page header draws it in the title's style and changes neither its role nor its level. |
description | The product's own description in place of the description prop: a paragraph with links, inline code or a keyboard shortcut. |
actions | The actions for the whole page: the product's own Buttons and Links. The page header lays them out and does nothing else to them. |
条目
该组件重复排列的条目种类。测试样例按顺序列出条目;各平台以各自的惯用方式为每个种类命名。
meta-item
One fact of the meta line: a few words of text (a date, a reading time, a count), or the product's own content in its place — a Link, a Tag, an Avatar or a glyph followed by a name, a group of tags. The page header places it, gives its text the meta line's style and draws the dot between it and its neighbour; it has no props, no role and no states of its own.
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 条目 | [data-part="meta"] > * | meta | meta | meta | meta |
| 备注 | Not an element of its own: every element child of the meta part is one item, whatever it is (a time, a span, an a, a tanrei-tag, a ul of tags). | Each top-level view of the meta: view builder is one item (the layout's subviews). | Each composable emitted at the top level of the meta lambda is one item (the layout's measurables). | Each widget of the meta list is one item. | Each element of the meta array is one item; a string is drawn as text in the meta line's style. |
状态
行为
以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。
| ID | 需求 |
|---|---|
PGH-1 | THE page header SHALL lay out its parts from the top in this order, each at the start edge and each taking no room and no space while it is absent: what the breadcrumbs slot holds (PGH-5), with comp.page-header.breadcrumbs.gap below it; the eyebrow (PGH-6), with comp.page-header.eyebrow.gap below it; the title (PGH-2); the description (PGH-7), comp.page-header.description.gap.small, comp.page-header.description.gap.medium or comp.page-header.description.gap.large below the title by size; the meta line (PGH-8), comp.page-header.meta.gap below the part above it; and the actions (PGH-10, PGH-11). THE page header SHALL bring no space above its first part, and below its last part SHALL keep, inside its own box, comp.page-header.container.padding-block-end.small, comp.page-header.container.padding-block-end.medium or comp.page-header.container.padding-block-end.large by size. It SHALL draw no fill, no edge, no corner shape and no elevation: it stands on the surface its container is drawn on. |
PGH-2 | THE page header SHALL draw its title in the text style of its size — comp.page-header.title.typescale.small, comp.page-header.title.typescale.medium or comp.page-header.title.typescale.large — and in comp.page-header.title.color, and SHALL expose it as a heading: of the level the level prop gives, or WHILE level is 0 of level 1 for medium and large and of level 2 for small, on a platform whose headings have levels, and with the platform's heading trait on one whose headings have none. WHERE the title slot holds the product's own heading THE page header SHALL draw that heading in the title's place, style and color instead of the title prop and SHALL change neither its role nor its level. THE page header SHALL expose the title exactly once and as text: it SHALL NOT hide the title from sight or from assistive technology, SHALL NOT repeat it in another part, and SHALL NOT replace it with a picture. |
PGH-3 | THE page header SHALL wrap its title onto as many lines as it needs and SHALL never cut it, limit it to a number of lines or shrink its text to make it fit: lines break by the rules of the title's language (PRS-19: a line of Japanese or Chinese neither begins with closing punctuation or a small kana nor ends with opening punctuation), a string with no break opportunity that is longer than its line breaks anywhere rather than overflow, and the lines align to the start edge, never justified and never hyphenated unless the product asks. WHERE the platform can balance the lengths of a heading's lines THE page header SHALL ask it to, so that a title of two lines does not end in a single word. THE title SHALL take the whole width the head gives its text and SHALL NOT be limited by comp.page-header.description.measure. |
PGH-4 | THE page header SHALL set the title's and the eyebrow's letter-spacing by the script of their text, as PRS-20 sets a heading's: WHERE their language is Chinese, Japanese or Korean without a negative letter-spacing (the title's negative one is dropped; a positive one, as the eyebrow's, stays), and WHERE their script joins its letters or stacks marks on them (Arabic, Devanagari) at a letter-spacing of zero. No design system chooses this: it is not a token. |
PGH-5 | WHERE the breadcrumbs slot holds content THE page header SHALL place it first, at the start edge, across the head's whole width — above the actions too, which never stand beside it — and SHALL NOT restyle it, cut it, answer its presses or give it a role: Breadcrumbs keeps its own landmark, its own overflow and its own states, and a Link its own. It SHALL come first in the reading order and in the focus order of the head. |
PGH-6 | WHILE eyebrow is not empty THE page header SHALL draw it directly above the title in comp.page-header.eyebrow.typescale and comp.page-header.eyebrow.color, in capitals where its script has them, by the casing rules of its language, and unchanged where the script has none; the capitals SHALL be drawn only: assistive technology, a search of the page, a copy and a translation SHALL get the text as the product wrote it. THE eyebrow SHALL wrap and SHALL never be cut. It SHALL be exposed as text read before the title, SHALL NOT be a heading and SHALL NOT be part of the heading's text or of its accessible name. |
PGH-7 | WHILE description is not empty, or the description slot holds content, THE page header SHALL draw it below the title as a paragraph in the text style of its size — comp.page-header.description.typescale.small, comp.page-header.description.typescale.medium or comp.page-header.description.typescale.large — and in comp.page-header.description.color, no wider than comp.page-header.description.measure times its own font size, at the start edge and never centred; the slot's content SHALL take the description's place, style and color instead of the prop. THE description SHALL wrap by the rules of its language (PRS-19) and SHALL never be cut or limited to a number of lines. A link in it SHALL be underlined at rest (PRS-17), and an inline component in it (inline code, a keyboard shortcut) SHALL sit on its baseline without making its line taller. THE description SHALL be exposed as a paragraph that follows the heading: it is not a heading and not part of the heading's name. |
PGH-8 | WHERE the page header has meta items THE page header SHALL lay them out in the order given as a row that starts at the start edge and wraps, with comp.page-header.meta.item-gap between two items on one row and comp.page-header.meta.row-gap between two rows, the items of a row centred on that row's line of text. Text in an item SHALL take comp.page-header.meta.typescale and comp.page-header.meta.color where nothing inside sets another; WHERE an item holds a glyph or an Avatar followed by text THE page header SHALL centre the two on one line with comp.page-header.meta.icon.gap between them. THE page header SHALL keep an item whole on one row while the item is no wider than the head, SHALL let an item that is wider wrap inside itself, and SHALL cut nothing. It SHALL add no style to anything inside a component placed as an item — a Tag, a Link and an Avatar keep their own colors, sizes and states — SHALL give no item a role, and SHALL NOT reorder, sort or hide items. |
PGH-9 | THE page header SHALL draw, between two items that follow each other on one row of the meta line, a filled circle of comp.page-header.meta.separator.size in comp.page-header.meta.separator.color, centred in the space between them and on the row's line of text; it SHALL draw none before the first item of a row or after the last, whichever items a wrap puts there, and none where the meta line has one item. THE separator SHALL be kept from assistive technology, SHALL NOT be part of the text a person selects or copies, and SHALL NOT be mirrored. WHERE the product writes a separator of its own between items THE page header SHALL NOT remove it. |
PGH-10 | WHERE the actions slot holds content, WHILE actionsPlacement is end and the head is at least comp.page-header.wide.container-width wide, THE page header SHALL place the actions beside its text at the end edge: the eyebrow, the title, the description and the meta line form one column that takes the remaining width, and the actions stand comp.page-header.container.gap after that column, centred on the title's first line, however many lines the title has: one row of actions SHALL stay centred on that line however tall it is, standing out of the line by the same amount above and below and adding no height to the head; WHEN the actions wrap onto more than one row they SHALL instead start at the top of the title's first line and extend downward, never standing out of the head above or below, and the head SHALL be as tall as they need; the column SHALL keep at least half of the head's width, and actions that need more SHALL wrap inside their own box (PGH-11). WHILE actionsPlacement is below, or the head is narrower than that width, THE page header SHALL place the actions below the last part of the text, comp.page-header.actions.gap under it, starting at the start edge. THE width that decides is the head's own — the room its container gives it — never the window's, so a head in a pane beside a list and a head in a full page each lay themselves out for the room they have. |
PGH-11 | THE page header SHALL lay its actions out as a row in the order given, with comp.page-header.actions.item-gap between two actions along the row and between rows where they wrap; beside the text the row SHALL end at the head's end edge, and below the text it SHALL start at the start edge. An action's label SHALL NOT be cut to make the row fit. The actions are the product's own Buttons and Links: THE page header SHALL NOT restyle them, resize them or answer their presses. In the reading order and in the focus order they SHALL come after the meta line in both placements: the order — breadcrumbs, eyebrow, title, description, meta, actions — SHALL NOT change with the head's width. |
PGH-12 | WHILE size is large and the head is narrower than comp.page-header.wide.container-width THE page header SHALL draw its title in comp.page-header.title.typescale.medium and the space under it as comp.page-header.description.gap.medium, and everything else as a large head: a statement set for a wide page does not leave a narrow one two words a line. THE change SHALL be one step at that width, not a size that follows the width continuously, and SHALL change neither the heading's level nor the order of the parts. |
PGH-13 | WHILE divider is true THE page header SHALL draw one line comp.page-header.divider.thickness thick in comp.page-header.divider.color along the bottom edge of its box, across its whole width, below the space of PGH-1, without changing the head's height by more than the line or the place of any part; WHILE divider is false it SHALL draw none. THE line is decorative: it SHALL NOT be exposed as a separator, because the heading already says where the content begins. |
PGH-14 | THE page header SHALL lay itself out as a block in the flow of its container: it SHALL take the whole inline size the container gives it — the main region of an app shell inside its gutters (APS-8), a pane, a card, a dialog — with its start edge on the start edge of the content below it; it SHALL bring no outer space of its own (PRS-15), SHALL never be wider than its container, SHALL have no minimum width and no maximum width, and SHALL NOT scroll. THE page header SHALL scroll with the content it heads: it SHALL NOT stick to an edge, float over the content, collapse or change its size as the content scrolls. A title that stays at the top of the window is the app bar's. |
PGH-15 | THE page header SHALL have no state: it SHALL NOT answer a pointer, a press, a key or focus, SHALL draw no state layer, ripple or focus indicator of its own, SHALL take no place in the focus order and SHALL emit no event. Its text SHALL stay selectable where the platform lets text be selected. Input over a Button, a Link, a Tag or Breadcrumbs inside it reaches that component, which answers it by its own spec. |
PGH-16 | THE page header SHALL add no landmark, no region and no name of its own: it is not the page's banner, which is the frame's header (APS-21), and it is exposed as its parts in their order — what the breadcrumbs slot holds, the eyebrow's text, the heading, the description's paragraph, the meta items, the actions. THE heading SHALL be found by a person who moves by headings and by a machine that reads the document without running script, with the title's text and nothing else as its name. |
PGH-17 | WHEN the title, the description, the eyebrow, the size or what a slot holds changes while the page header is shown THE page header SHALL draw the new content at once, with no motion of its own, and SHALL NOT announce it or move focus: a page whose title changes without a navigation (a record renamed in place) is the product's to announce. |
PGH-18 | WHILE the text size is enlarged THE page header SHALL grow every text style and the description's measure with it, keep its gaps, wrap, and grow in height; the width of PGH-10 and PGH-12 SHALL grow with the text, so that a head whose text has doubled is one column up to twice that width; it SHALL set no fixed height on any part, SHALL cut nothing, 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 %. |
PGH-19 | 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 page header SHALL draw the eyebrow, the title, the description and the text of the meta line in the platform's text color, links in the platform's link color and still underlined, and the separator and the line of PGH-13 in the platform's text color; it SHALL rely on no fill of its own, having none. |
PGH-20 | WHERE the layout direction is right-to-left THE page header SHALL mirror its layout and apply every space by logical start and end: every part starts at the right edge, the meta line runs from the right and wraps to the right edge, and actions beside the text stand at the left edge, their row ending there. THE separator is the same dot in the same place between two items. THE page header SHALL mirror nothing it holds: Breadcrumbs, a Button's glyph and a meta item's glyph are mirrored by their own components and by the icon helper. |
PGH-21 | THE page header 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 page header is shown THE page header SHALL draw itself for the new direction at once, without a motion of its own, keeping its content and the focus of anything inside it. |
PGH-22 | THE page header SHALL lay out the title, the eyebrow and the description, each given as text, as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right product name, a version or a token's id keeps its own order in a right-to-left layout and reorders nothing beside it; THE page header SHALL still place each part and align its lines by the layout direction, not by the text's own. Each item of the meta line SHALL be a run of its own in the same way: the digits of a date, a time or a count SHALL NOT be reversed, and the order of the items is the layout's. |
PGH-23 | WHEN size names a value that the active design system does not have, or actionsPlacement names a value this spec does not have, THE page header SHALL render with the default value (the default size's tokens; end), and in debug builds report the unknown value; WHEN level is not 0 and not a whole number from 1 to 6 THE page header SHALL take the level of its size (PGH-2) and in debug builds report it. |
PGH-24 | IF the title is empty and the title slot holds nothing THE page header SHALL still draw the parts it has, SHALL expose no empty heading, and in debug builds SHALL report that a page header has no title: a head without a title names nothing. IF every part is empty THE page header SHALL draw nothing, take no space and expose nothing. WHERE the title slot holds content that is not a heading on a platform whose headings are elements of the content (the web) THE page header SHALL still draw it as the title and in debug builds report that the title is not a heading. |
各平台名称对照
对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。
| 规格 | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| 组件 | tanrei-page-header | TanreiPageHeader | TanreiPageHeader | TanreiPageHeader | PageHeader |
title · 属性 | headline | title | title | title | title |
description · 属性 | description | description | description | description | description |
eyebrow · 属性 | eyebrow | eyebrow | eyebrow | eyebrow | eyebrow |
size · 属性 | size | size | size | size | size |
level · 属性 | level | headingLevel | headingLevel | headingLevel | headingLevel |
actionsPlacement · 属性 | actionsPlacement | actionsPlacement | actionsPlacement | actionsPlacement | actionsPlacement |
divider · 属性 | divider | showsDivider | divider | divider | divider |
| 备注 | It is the form the figure and the description list take. <tanrei-page-header> is a custom element with **no shadow root**, wrapped around the page's own elements, which stay in the document: <tanrei-page-header><tanrei-breadcrumbs>…</tanrei-breadcrumbs><p data-part="eyebrow">Announcements</p><h1>Title</h1><p>Description</p><div data-part="meta"><time datetime="2026-09-21">21 September 2026</time><span><tanrei-avatar size="small" decorative …></tanrei-avatar> Name</span><tanrei-tag label="Tokens"></tanrei-tag></div><div data-part="actions"><tanrei-button>…</tanrei-button></div></tanrei-page-header>. Parts are found by what they are: the title is the first heading element among the children (h1 to h6; PGH-2: its level is the page's, never rewritten), the description the first p without a data-part, tanrei-breadcrumbs or [data-part="breadcrumbs"] the breadcrumbs slot, [data-part="eyebrow"], [data-part="meta"] (whose element children are the items) and [data-part="actions"] the rest; the spec's slots are these parts, and the element's children are written in the order of PGH-11, which is then the document's order and the focus order with no reordering in script. Everything drawn comes from one stylesheet, @tanrei/web/page-header.css, whose selectors start at tanrei-page-header and are wrapped in :where(); it styles the page before the element is defined and without script, so the title a crawler reads, the title a reader sees before the bundle arrives and the title afterwards are the same element in the same place, and nothing moves when the element is defined. Props are attributes of the wrapper: size, actions-placement, divider. headline (HTML's global title would show a tooltip over the whole head), description, eyebrow and level are conveniences for a head built by script or by the React wrapper: where a part is given as an attribute and no element for it is among the children, the element writes that element itself (an h1 to h6 by level and size); a content page writes the elements, and should. The host is display: block and a size container (container-type: inline-size), with no role; the wide layout of PGH-10 and PGH-12 is a container query at comp.page-header.wide.container-width expressed in rem, so it follows the head's own width and the reader's text size (PGH-18); beside the text the actions are placed by grid lines, not by order, and are centred on the title's first line by aligning to a first row as tall as that line (1lh of the title's style). The separator is a pseudo-element of each item but the first, drawn inside the gap, and the row's own start is clipped by one gap so the dot of an item that begins a row is not seen (PGH-9); it is not text, so it is neither read nor copied. The title takes text-wrap: balance and overflow-wrap: anywhere, line-break: strict under :lang(ja, zh), and letter-spacing: 0 under :lang(ja, zh, ko, ar, fa, ur, hi, mr, ne, bn, th) and the like (PGH-4), where the eyebrow also drops its tracking; the eyebrow's capitals are text-transform: uppercase (PGH-6). Text parts take unicode-bidi: plaintext with alignment by :dir() as productText() does (PGH-22). Under forced-colors: active the dot and the line are CanvasText. What the element adds once defined: the convenience parts above and the debug reports of PGH-23 and PGH-24 — nothing a reader sees. Inside prose the head is an island (tanrei-page-header is in the registry's tag names, PRS-16), so prose's heading and paragraph rules do not reach its h1 and p. The alternative form — a shadow root with a title slot — is still open. Whether the actions beside the title wrap onto more than one row (PGH-10) is measured by the element with a ResizeObserver on the actions box and written as the data-actions-wrapped state attribute on the head; the stylesheet alone keeps every row of actions centred on the title's first line, as one row is. | TanreiPageHeader(_ title: String, description:, eyebrow:, size:, headingLevel:, actionsPlacement:, showsDivider:) { actions } meta: { … } breadcrumbs: { … }, each slot a @ViewBuilder, with second initializers whose title and description are views (the title and description slots). The title carries .accessibilityAddTraits(.isHeader) and .accessibilityHeading(.h1 … .h6) by level. The width that decides the layout is the view's own, through ViewThatFits or a layout that measures its proposal, never the scene's size class. The platform's own large title is the navigation bar's (.navigationTitle with a large display mode): on a compact screen that shows it, the screen does not also draw a page header (the spec's accessibility notes). The meta line is a flow layout (Layout) that knows which item begins a row. | TanreiPageHeader(title, description, eyebrow, size, headingLevel, actionsPlacement, divider, breadcrumbs, titleContent, descriptionContent, meta, actions), the slots as composable lambdas. The title has semantics { heading() }; Compose's semantics have no heading level, so headingLevel is honoured where the platform gains one and is otherwise the trait alone. The layout reads its own constraints (BoxWithConstraints or a custom Layout), not the window size class. The meta line is a FlowRow with the dots drawn by the layout, which knows each row's first item. A screen whose top app bar is the platform's large, collapsing one shows the title there and draws no page header. | TanreiPageHeader(title: …, description:, eyebrow:, size:, headingLevel:, actionsPlacement:, divider:, breadcrumbs:, titleWidget:, descriptionWidget:, meta:, actions:), meta and actions as lists of widgets. The title is wrapped in Semantics(header: true, headingLevel:). The layout is a LayoutBuilder over the widget's own constraints; the meta line a custom multi-child layout that draws the dots between the items of one run. Text scaling is the framework's TextScaler; the width of PGH-10 is scaled by it. | <PageHeader title="…" description="…" eyebrow="…" size="medium" actions={…} meta={[…]} breadcrumbs={…} />; the title and description slots are the titleContent and descriptionContent props. The title has role="heading" (accessibilityRole="header"); a level is exposed where the platform has one (aria-level on the web target). The layout follows the view's own width from onLayout, with the narrow layout drawn first so nothing overlaps before the width is known. The eyebrow's capitals are textTransform: 'uppercase'. Follows the app's direction where an ancestor's is not known. |
令牌
| 令牌 | 引用 | 解析值(浅色) |
|---|---|---|
--tanrei-comp-page-header-actions-gap | sys.space.space200 | 16px |
--tanrei-comp-page-header-actions-item-gap | sys.space.space100 | 8px |
--tanrei-comp-page-header-breadcrumbs-gap | sys.space.space150 | 12px |
--tanrei-comp-page-header-container-gap | sys.space.space300 | 24px |
--tanrei-comp-page-header-container-padding-block-end-large | sys.space.space400 | 32px |
--tanrei-comp-page-header-container-padding-block-end-medium | sys.space.space200 | 16px |
--tanrei-comp-page-header-container-padding-block-end-small | sys.space.space100 | 8px |
--tanrei-comp-page-header-description-color | sys.color.on-surface-variant | |
--tanrei-comp-page-header-description-gap-large | sys.space.space200 | 16px |
--tanrei-comp-page-header-description-gap-medium | sys.space.space100 | 8px |
--tanrei-comp-page-header-description-gap-small | sys.space.space50 | 4px |
--tanrei-comp-page-header-description-measure | — | 36 |
--tanrei-comp-page-header-divider-color | sys.color.outline-variant | |
--tanrei-comp-page-header-divider-thickness | sys.border.hairline | 1px |
--tanrei-comp-page-header-eyebrow-color | sys.color.on-surface-variant | |
--tanrei-comp-page-header-eyebrow-gap | sys.space.space50 | 4px |
--tanrei-comp-page-header-meta-color | sys.color.on-surface-variant | |
--tanrei-comp-page-header-meta-gap | sys.space.space150 | 12px |
--tanrei-comp-page-header-meta-icon-gap | sys.space.space75 | 6px |
--tanrei-comp-page-header-meta-item-gap | sys.space.space200 | 16px |
--tanrei-comp-page-header-meta-row-gap | sys.space.space50 | 4px |
--tanrei-comp-page-header-meta-separator-color | sys.color.on-surface-variant | |
--tanrei-comp-page-header-meta-separator-size | sys.space.space50 | 4px |
--tanrei-comp-page-header-title-color | sys.color.on-surface | |
--tanrei-comp-page-header-wide-container-width | — | 480px |
--tanrei-comp-page-header-description-typescale-large | sys.typescale.body-large | 400 16px/1.625 |
--tanrei-comp-page-header-description-typescale-medium | sys.typescale.body-large | 400 16px/1.625 |
--tanrei-comp-page-header-description-typescale-small | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-page-header-eyebrow-typescale | sys.typescale.eyebrow | 500 12px/1.3333 |
--tanrei-comp-page-header-meta-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-page-header-title-typescale-large | sys.typescale.display | 450 48px/1.0833 |
--tanrei-comp-page-header-title-typescale-medium | sys.typescale.heading-1 | 500 32px/1.25 |
--tanrei-comp-page-header-title-typescale-small | sys.typescale.heading-2 | 500 24px/1.3333 |
测试样例
作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。
size: small (4)
small
title: Members description: People who can open this project. size: small small-actions
title: Access tokens size: small slots: actions small-divider
title: Access tokens description: Tokens let scripts act for you. Treat th… size: small divider: true slots: actions level
title: Danger zone description: Deleting a project cannot be undone. size: small level: 3 size: medium (23)
medium
title: Typography description: Text styles are named by level, never by… title-only
title: Typography eyebrow
title: Tokens are derived, and design systems a… eyebrow: Announcements breadcrumbs
title: Button slots: breadcrumbs meta
title: Tokens are derived, and design systems a… items: 3 meta-one
title: Roadmap items: 1 actions
title: Members description: People who can open this project. slots: actions actions-below
title: Members description: People who can open this project. actionsPlacement: below slots: actions divider
title: Typography description: Text styles are named by level, never by… divider: true title-slot
description: Every value a button reads, and what eac… slots: title description-slot
title: Status slots: description docs-page
title: Typography description: Text styles are named by level, never by… slots: breadcrumbs docs-component
title: Button description: Starts an action or leads to a destinati… items: 4 slots: breadcrumbs blog-post
title: Default typefaces and icons, choices of… description: Inter, Noto Sans JP, Roboto Mono and Mat… eyebrow: Announcements items: 4 long-title
title: Text styles by level, corner smoothing a… long-title-actions
title: Text styles by level, corner smoothing a… description: Text styles are named by level, never by… slots: actions unbroken-title
title: packages/web/src/components/navigation-d… everything
title: Add a design system as a layer description: A layer changes values and may extend th… eyebrow: Guides divider: true items: 3 slots: breadcrumbs, actions rtl
title: الرموز مشتقة وأنظمة التصميم طبقات description: قيمة واحدة تتغير في مكان واحد ويتبعها كل… eyebrow: الإعلانات direction: rtl items: 2 rtl-everything
title: أضف نظام تصميم كطبقة description: الطبقة تغيّر القيم وقد توسّع المفردات، و… eyebrow: الأدلة divider: true direction: rtl items: 3 slots: breadcrumbs, actions ltr-title-rtl
title: Tanrei 0.2 description: ملاحظات هذا الإصدار. direction: rtl items: 2 japanese
title: 階層で名付けたテキストスタイル、量としての角のスムージング、そして 4 つ減った… description: テキストスタイルは大きさではなく階層で名付けます。「見出し 2」はどのプロダクト… eyebrow: お知らせ items: 3 japanese-actions
title: メンバー description: このプロジェクトを開ける人。 slots: actions size: large (2)
large
title: One system. Five platforms. description: The same tokens, the same components and… size: large large-landing
title: One system. Five platforms. description: The same tokens, the same components and… eyebrow: Design system size: large actionsPlacement: below slots: actions 生成自 specs/page-header.spec.json, tokens/comp/page-header.tokens.json, design/tokens/
size |
标题 | 描述 | 用于 | 标题级别 |
|---|---|---|---|---|
small |
24 | 14 | 页面的一部分:设置中的一个分区、多条记录中的一条。 | 2 |
medium(默认) |
32 | 16 | 一个页面。 | 1 |
large |
48 | 16 | 落地页的主标语。 | 1 |
大号页头放在狭窄的地方时,绘制的是中号的标题。这是一级跳变,而不是随宽度连续变化的尺寸。
- 标题就是页面的标题元素。它以文本的形式显示一次,绝不会被截断、限制行数或缩小以适应空间。标题长,付出的是高度,而不是文字。
- 一个页面只有一个 1 级标题,就是这个标题。页面的各个部分,请使用
small页头或内容里的标题。 - 不要在应用栏和页头里显示同一个标题。每种窗口类别下只选其中一处。
- 面包屑导航沿着层级结构带用户返回上级。它排在最前面,占满页头的整个宽度。
- 眉标说明页面属于什么:一个类别、一个产品、一个版本。它是文本,不是链接,也不是标题元素。
- 一个页头可以两者都有。
- 一项是一条不需要名称的简短信息:日期、作者、阅读时间、状态。
- 同一行上的两项之间,页头会画一个小圆点。不要自己写分隔符。
- 一项在它所在的行里保持完整。一行排满了就换行。
- 作者是一项:一个装饰性的小头像加上姓名。
- 多个 Tag 是一项,由这一项容纳整组 Tag,所以两个 Tag 之间不画圆点。
- 带名称的信息(“类别:操作”)属于页头下方的描述列表,不是元信息项。
actionsPlacement |
页头宽度不小于 480 时 | 页头更窄时 |
|---|---|---|
end(默认) |
位于标题旁边的末尾边,与标题的第一行居中对齐。只有一行的操作即使比这一行更高,也保持居中。换到第二行的操作从这一行的顶部开始排,页头随之变高以容纳它们。 | 位于文本下方的起始边。 |
below |
位于文本下方。 | 位于文本下方。 |
- 起决定作用的是页头自己的宽度,不是窗口的宽度。窗格里的页头按这个窗格来排布自己。
- 无论画在哪里,操作在阅读顺序和焦点顺序中始终排在最后。
- 使用一到三个操作,主要的那个用 filled。
divider 在页头下方画一条细线。页头下面是非文本内容时使用它,例如表格、记录列表或标签页。在文章上方则不需要。
- 把描述写成一两句话。更长的文本属于内容。
- 日期请用
<time>元素,并带上机器可读的datetime。 - 给其他语言的文本设置
lang。在中文、日文和韩文里,标题去掉负字距绘制(眉标保留其正字距);在字母相连或在字母上叠加符号的文字(阿拉伯文、天城文)里,标题和眉标以零字距绘制:在这些文字里,字母之间无论拉开还是收紧,词都会散开。
- 不要把页头当作窗口的栏。那是应用栏。
- 不要用全大写书写眉标。有些屏幕阅读器会把大写字母逐个读出来。
- 不要只靠颜色表示状态。请使用标签文字能说明状态的 Tag。
- 角色
none- 标注
- The page header has no role and no name of its own. Its title is a heading — of level 1 for a page (medium, large), of level 2 for a part of a page (small), or of the level the product gives — whose name is the title's text alone. The eyebrow is text read before it, the description a paragraph read after it; neither is part of the heading. Meta items are read in order as the product wrote them; the dots between them are hidden. Breadcrumbs keeps its own navigation landmark and name; actions are the product's own controls with their own names.
- 最小目标
- 即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px
键盘操作
| 按键 | 动作 |
|---|---|
| Tab | Reaches, at the head's place in the page, the links of the breadcrumbs, then any link in the description, then the links and controls among the meta items, then the actions, in that order at every width (PGH-11). The page header itself is not a stop. |
备注
- Not interactive: the page header takes no input and no focus (PGH-15), so it has no target to size and minimumTarget holds trivially. The Buttons, Links and Breadcrumbs inside it are other components, each with its own target.
- One heading of level 1 per page or screen, and it is this title (WCAG 1.3.1, 2.4.6). The component cannot see the rest of the page, so that is the product's to keep: a page draws one medium or large page header; the heads of its parts are small, or headings of the content. The title should also be, or begin, the document's title, so that the tab, the history and the heading agree (WCAG 2.4.2).
- Do not show the same title twice. On a screen whose app bar already shows the page's title as its headline — a phone's screen, where the platform's own large title lives in the bar — the app bar's headline is the heading and the screen needs no page header; in a wide window, where the app bar holds the product's name and its search, the page header carries the page's title. Choose one per window class; never give both the heading role for the same words.
- No landmark (PGH-16). A head inside the main region is not a banner, and a region for every page head would be noise in the list of landmarks. People who use a screen reader reach it as the first heading of the main region; the skip link of the frame lands just before it (APS-19, APS-20).
- The eyebrow's capitals are drawn, not written (PGH-6): text written in capitals is spelled letter by letter by some screen readers and loses its meaning in translation. Write "Announcements".
- A date in the meta line is the product's text, formatted for the reader's language; on the web it is a time element with a machine-readable datetime. An author is a name in words; the avatar beside it is decorative, because the name is next to it (the avatar's decorative prop). A state is a Tag whose label says it in words (TAG-11): a status is never told by color alone (WCAG 1.4.1).
- Where the meta items are a set a person counts (three tags), the product groups them in its platform's list semantics as one item; the page header gives items no role (PGH-8).
- Reading order is fixed while the layout is not (PGH-11): in a wide head the actions are drawn level with the title and still come last. A person who tabs through a head with links in its meta line goes down the text and then to the actions at the far edge; that is the same order a narrow head has, which is what WCAG 2.4.3 asks when a layout changes with width.
- WCAG 1.4.3 (contrast) in the default system: the title is on-surface and the eyebrow, the description and the meta text are on-surface-variant, each held to 4.5:1 on every surface role by the token invariants. The page header brings no fill, so on a tinted band the product sets the four color tokens for that band and owns the contrast there. The dot and the line are decorative.
- WCAG 1.4.4, 1.4.10 and 1.4.12 (resize, reflow, text spacing): PGH-18. The title is never cut at any size (PGH-3), so a long title in a narrow window costs height, not words.
- Web: the title, the description, the eyebrow, the meta items and the actions are the page's own elements, children of `<tanrei-page-header>` in the document, and there is no shadow root: the heading is an `h1` a crawler, a reader mode, a page without script and a screen reader all find, and the head is drawn by a stylesheet before any script runs. Nothing is shown differently before the element is defined.
- The page header has no states, so no design system decides how one looks.
生成自 specs/page-header.spec.json, tokens/sys/measure.tokens.json
- 标题仍然是你写的那个标题元素,级别不变。它的名称只是标题的文本。
- 眉标在标题之前朗读,描述在标题之后朗读。两者都不是标题的一部分。
- 元信息各项之间的圆点不会被朗读,也不会被复制。
- 页头不增加地标,也不是键盘的停靠点。
- 在任何宽度下,阅读顺序和焦点顺序都相同。
- 在宽 320 的地方把文本放大到 200% 时,每个字、每一项和每个操作都会显示,没有任何内容需要横向滚动。
- 在强制颜色模式下,文本、圆点和线以系统的文本颜色绘制。
- 在从右到左的布局中,每个部件都从右侧开始,位于文本旁边的操作则在左侧。从左到右书写的标题保持自己的顺序。
- 每个页面一个 1 级标题。
- 文档的标题应当就是页面的标题,或者以页面的标题开头。
- 为你的操作命名,并为你放进页头的任何图片提供替代文本。