Label 标签文字
表单项的标签:14/20、中等字重、标题色。for 指向控件的 id,点标签时把焦点交给控件,勾选类则直接切换。
for 指向控件的 id:点标签时文本类控件获得焦点,勾选类直接切换;标签文字同时成为控件的可访问名,控件上不必再写 aria-label。
<pt-label for="demo-label-email" required>邮箱地址</pt-label>
<pt-input id="demo-label-email" type="email" placeholder="name@example.com" required></pt-input>
<pt-label for="demo-label-agree">同意服务条款</pt-label>
<pt-checkbox id="demo-label-agree"></pt-checkbox>必填星号只是视觉提示(对读屏隐藏),必填语义写在控件的 required 上;禁用时半透明,点击不再转交。
<pt-label required>必填</pt-label>
<pt-label disabled>禁用</pt-label>点标签发生什么
for 的写法与原生 <label for> 相同。在 React 里属性名是 htmlFor(for 是 JS 保留字), 原生 HTML、Vue 与 Angular 模板里静态值照常写 for,要绑定变量时用 property 名(:html-for / [htmlFor])。
目标在标签所在的根里按 id 查找:标签在 document 里就找 document,被放进某个组件的影子树时就在那棵影子树里找。 找到之后按控件类型分两种处理,与浏览器对原生 label 的处理一致:
| 目标 | 行为 |
|---|---|
pt-checkbox、pt-switch、pt-button;原生 checkbox / radio / button / file… | 调用目标的 click():勾选类切换并发 ptChange,按钮类触发点击 |
其它(pt-input、pt-textarea、pt-select、原生文本框……) | 调用目标的 focus():我们的控件都是 delegatesFocus 宿主,焦点进入内部 |
下列情况不转交:没有 for、for 为空或找不到目标;disabled;点在标签里的链接或按钮上(那是在用它们); 目标本身就包在标签里(这次点击已经落在它身上了)。
标签不可聚焦,没有键盘交互。双击标签不会选中文字。
无障碍
标签文字就是控件的可访问名,控件上不用再写 aria-label。
原生 <label for> 做不到这一点:它只关联同一棵树里的原生可标注元素,而我们控件真正获得焦点的元素 (<input>、role="checkbox" 的方框……)在各自的影子树里。pt-label 通过内部的 utils/aria-link 把自己关联到那个元素上,下列控件都已接入:
| 控件 | 名字落在哪 |
|---|---|
pt-input / pt-textarea | 影子树里的 <input> / <textarea> |
pt-native-select | 影子树里的原生 <select> |
pt-select | 影子树里 role="combobox" 的触发器 |
pt-checkbox / pt-switch / pt-radio | 影子树里的方框 / 轨道 / 圆点(外部标签优先于插槽文字) |
pt-radio-group | 宿主上的 role="radiogroup"(经 ElementInternals) |
pt-slider | 单滑块:影子树里的 role="slider";多滑块:宿主的 role="group"(经 ElementInternals) |
pt-combobox | 影子树里 role="combobox" 的输入框 |
<pt-label for="email" required>邮箱地址</pt-label>
<pt-input id="email" type="email" required></pt-input>- 标签晚于控件出现、控件晚于标签出现、控件换了 id、标签被移除、标签文字改了 —— 关联都跟着更新。
- 标签优先:控件上同时写了
aria-label时,以标签文字为准(与原生aria-labelledby优先于aria-label一致); 标签移除后退回控件自己的aria-label。pt-radio-group例外:它的名字在宿主上,宿主写的aria-label优先。 - 多个标签指向同一个控件时,按页面顺序拼成一个名字。
- 指向原生
<input>等非 kit 元素时只有点击转交,没有可访问名关联 —— 原生元素请用原生<label>。
各浏览器的差异
关联同时走两条路,读屏念出的名字在各浏览器里一致,差别只在机制:
| 浏览器 | 机制 |
|---|---|
| Chrome / Edge 135+、Firefox 136+、Safari 16.4+ | ARIA 元素反射:内部元素的 aria-labelledby 直接引用外部的 pt-label 元素,读屏能跳到标签 |
| Chrome 114–134、Firefox 125–135 | 标签文字镜像进内部元素的 aria-label,文字变化时同步;读屏念得出名字,但不知道「由哪个元素标注」 |
镜像在支持反射的浏览器里也会写,只是不生效(aria-labelledby 优先):axe、Playwright 的 getByLabel 这类工具 只认 id 形式的 aria-labelledby,看不到反射出来的引用,靠镜像才能验证。 镜像是简化的文字提取:跳过 aria-hidden 与 hidden 的内容,子元素有 aria-label 时用它。
required 的星号对读屏隐藏(aria-hidden),它只是视觉提示;必填语义写在控件的 required 上, 控件会带上 aria-required 并参与表单校验。disabled 同理,只改标签的外观与点击行为,控件要自己禁用。
勾选类控件的文字本来就能放进它们自己的插槽(<pt-checkbox>同意条款</pt-checkbox>), 点文字切换、可访问名都是现成的 —— 优先这么写,只有布局上文字必须和控件分开时才用 pt-label。
API
属性
| 属性 | Attribute | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
disabled | disabled | boolean | false | 禁用:半透明、禁用光标,点击不再转交焦点。一般与目标控件的 disabled 同步设置 |
htmlFor | for | string | undefined | — | 关联控件的 id,attribute 是 `for`(与原生 label 相同)。property 叫 `htmlFor`: `for` 是 JS 保留字,框架包装层生成的解构与类型声明里当不了标识符; 这也是 React 与 DOM(HTMLLabelElement.htmlFor)的既有叫法。 |
required | required | boolean | false | 必填:文字后面跟一个危险色的星号。只是视觉提示,必填语义请写在目标控件上 |
插槽
| 名称 | 说明 |
|---|---|
(默认) | 标签文字 |
可定制的内部元素(::part())
| part | 说明 |
|---|---|
base | 文字容器 |
required | 必填星号 |