Link 还是 ButtonLink vs Button
先看怎么选
页面上有个可点击的东西,你要决定用 `<a>` 还是 `<button>`。选错的代价:键盘用户 Tab 不到(少了 button)、屏幕阅读器误导(「链接」但点了不跳转)、右键「在新标签页打开」失效。
朱批看点了之后去哪——去别的地方(URL 变)用 Link;停在原地做个操作(URL 不变)用 Button。看起来像链接但行为像按钮的,是反模式。
Link
用户点了之后会离开当前页面,或跳到页面内另一个位置。URL 栏会变(或锚点变)。语义上是「去某个地方」,不是「做某件事」。
- 适合
- 导航到其他页面、下载文件、跳到页面内锚点、打开外部链接。用户期望右键「在新标签页打开」「复制链接」能用。
- 不适合
- 不要用 Link 做操作(提交表单、打开弹窗、触发 JS 逻辑但不跳转)——那些是 Button 的事。看起来像链接(蓝色下划线)但 `href="#"` 或 `href="javascript:void(0)"` 的,是反模式。
实现细节与 Prompt
- 语义标签
- `<a href="...">`,必须有真实 href(不是 # 或 javascript:)
- 键盘
- Enter 触发;Tab 可达;屏幕阅读器读作「链接,去往 XX」
- 样式重置
- 浏览器默认蓝色 + 下划线;自定义时保持「可点击」信号(颜色/下划线/图标)
- 可访问性
- href 缺失或 `#` 会让屏幕阅读器困惑;`target="_blank"` 需配 `rel="noopener"`
Create a link for [destination]. Requirements:
- Use `<a href="...">` with a real URL (not # or javascript:)
- Style: Cinnabar (#C13E23) with underline, or Ink with subtle underline
- Hover: darken color, keep underline
- If external link: add icon (inline SVG arrow-up-right) + `rel="noopener"`
- If opens new tab: add `target="_blank"` + visible indicator
- Keyboard: Enter triggers navigation
- Screen reader: announces as "link, [text]"
Button
用户点了之后停在当前页面,触发一个操作(提交表单、打开弹窗、展开/收起、触发 JS 逻辑)。URL 不变,页面可能局部更新但不是「去别的地方」。
- 适合
- 提交表单、打开/关闭弹窗、展开/收起内容、删除、保存、触发任何不跳转的 JS 逻辑。用户不期望右键「在新标签页打开」。
- 不适合
- 不要用 Button 做导航(去其他页面)——那会让键盘用户和屏幕阅读器用户困惑(「按钮」但点了跳走了)。也不要给 Button 套 href。
实现细节与 Prompt
- 语义标签
- `<button type="button">` 或 `<button type="submit">`(表单内)
- 键盘
- Enter 和 Space 都能触发;Tab 可达;屏幕阅读器读作「按钮,XX」
- type 属性
- 明确写 `type="button"`(防止表单内误触发提交)或 `type="submit"`
- 可访问性
- 禁用时 `disabled` + `aria-disabled="true"`;loading 时保持可聚焦但禁用
Create a button for [action]. Requirements:
- Use `<button type="button">` (or `type="submit"` if in a form)
- Style: Primary/Secondary/Ghost/Danger per action importance (see Button variants term)
- States: Normal, Hover, Active, Disabled, Loading
- Keyboard: Enter and Space both trigger; Tab-reachable
- Screen reader: announces as "button, [text]"
- Never add href to a button; never wrap button in <a>
详细说明
Link 还是 Button
它是什么
页面上能点的东西只有两种语义身份:Link(链接)= 去某个地方,Button(按钮)= 做某件事。
选对的唯一判断标准:点了之后 URL 变不变。
- URL 会变(或锚点变)→ Link
- URL 不变,停在原地做操作 → Button
不是「长得像哪个就用哪个」,是「行为像哪个就用哪个」。一个看起来像链接(蓝色下划线)但点了不跳转的,是反模式——骗了用户也骗了屏幕阅读器。
快速判断
| 用户点了之后 | 用哪个 | 例子 |
|---|---|---|
| 去其他页面、去页面内锚点、下载文件 | Link | 导航栏链接、面包屑、文章标题、「了解更多」 |
| 停在原地,触发操作(提交/打开/关闭/删除) | Button | 提交表单、打开弹窗、展开手风琴、删除、保存 |
| 看起来像链接但点了不跳转 | 反模式 | <a href="#">展开</a>、<a href="javascript:void(0)">删除</a> |
常见误区:
- ❌ 「能点的就用 Button」— 错,导航必须用 Link
- ❌ 「蓝色下划线的就用 Link」— 错,看行为不看样式
- ❌ 「Button 不能跳转」— 对,Button 只做操作不做导航
为什么要区分
用错标签的代价:
| 错误 | 后果 |
|---|---|
| 导航用了 Button | 右键「在新标签页打开」失效;Cmd+点击失效;屏幕阅读器说「按钮」但行为是跳转,误导 |
| 操作用了 Link | 屏幕阅读器说「链接,去往 XX」但点了不跳转,困惑;键盘只能 Enter 不能 Space |
Link 的 href 是 # 或 javascript: |
浏览器历史记录污染;右键菜单功能全失效;屏幕阅读器读出「链接」但没真实目的地 |
可访问性核心差别:
- Link:屏幕阅读器读作「链接,去往 XX」,只响应 Enter
- Button:屏幕阅读器读作「按钮,XX」,响应 Enter 和 Space
技术标注
| 考虑点 | Link | Button |
|---|---|---|
| HTML 标签 | <a href="真实 URL"> |
<button type="button"> 或 type="submit" |
| 必需属性 | href(必须是真实 URL,不是 # 或 javascript:) |
type(明确写,防止表单内误触发) |
| 键盘 | Enter 触发 | Enter 和 Space 都触发 |
| 右键菜单 | 「在新标签页打开」「复制链接」可用 | 无特殊右键功能 |
| 屏幕阅读器 | 「链接,去往 [href]」 | 「按钮,[文本]」 |
| 焦点样式 | 默认蓝色轮廓(需自定义为品牌色) | 同 Link |
| 外部链接 | 加 target="_blank" + rel="noopener" |
N/A |
什么时候用这个词
做设计决策时
场景 1:导航栏
需求:顶部导航栏有「首页」「关于」「联系」三个可点击项
错误:用 Button 因为「看起来像按钮」
├─ 右键「在新标签页打开」失效
├─ Cmd+点击失效
└─ 屏幕阅读器困惑(「按钮」但行为是跳转)
正确:用 Link
├─ <a href="/">首页</a>
├─ <a href="/about">关于</a>
└─ <a href="/contact">联系</a>
→ 所有浏览器功能正常;键盘和屏幕阅读器符合预期
场景 2:卡片上的「了解更多」
需求:文章卡片底部有「了解更多」,点了去文章详情页
错误:<button onclick="location.href='/article/1'">了解更多</button>
├─ 右键菜单失效
├─ 屏幕阅读器说「按钮」但行为是导航
└─ JS 禁用时整个卡片失效
正确:<a href="/article/1">了解更多</a>
→ 即使 JS 禁用也能点;右键功能齐全
场景 3:「展开更多」
需求:折叠内容下方有「展开更多」,点了展开下方隐藏内容(不跳转)
错误:<a href="#" onclick="expand()">展开更多</a>
├─ href="#" 污染浏览器历史
├─ 屏幕阅读器说「链接」但点了不去任何地方
└─ 右键「在新标签页打开」会打开一个一样的页面(没意义)
正确:<button type="button" onclick="expand()">展开更多</button>
→ 语义正确;键盘 Enter/Space 都能触发;屏幕阅读器说「按钮,展开更多」
给 agent 发指令时
不要只说「加个可点击的」——说清是导航还是操作:
需求 1:文章列表每篇文章的标题可点击,去文章详情页
→ 用 Link:<a href="/article/123">文章标题</a>
需求 2:文章详情页底部有「删除文章」,点了弹确认框
→ 用 Button:<button type="button" onclick="confirmDelete()">删除文章</button>
需求 3:侧边栏有「下载 PDF」,点了下载文件
→ 用 Link(下载也是导航的一种):<a href="/report.pdf" download>下载 PDF</a>
常见错误
错误 1:Link 的 href 是 #
<a href="#" onclick="doSomething()">点我</a>
问题:
- 浏览器历史记录里多一条 `#`
- 屏幕阅读器读「链接」但没真实目的地
- 右键「复制链接」得到的是当前页面 URL + #
正确:改用 Button
<button type="button" onclick="doSomething()">点我</button>
错误 2:Button 做导航
<button onclick="location.href='/page2'">下一页</button>
问题:
- 右键「在新标签页打开」失效
- Cmd+点击失效
- 屏幕阅读器说「按钮」但行为是跳转
正确:改用 Link
<a href="/page2">下一页</a>
错误 3:Link 长得像 Button
<a href="/submit" class="btn btn-primary">提交表单</a>
问题:
- 看起来像提交按钮,但点了是跳转(用户困惑)
- 如果真的要提交表单,应该用 <button type="submit">
何时可以:
- 「提交」后真的要跳到另一个页面(比如支付跳转第三方)
- 但这种情况文案应该改成「前往支付」而不是「提交」
错误 4:Button 包在 Link 里(或反过来)
<a href="/page"><button>点我</button></a> <!-- 非法 HTML -->
问题:
- HTML 规范不允许
- 浏览器行为不一致
正确:只用一个
- 导航:<a href="/page">点我</a>
- 操作:<button type="button">点我</button>
相关词条
- №03「按钮怎么选」 — Button 确定后,选 Primary / Secondary / Ghost / Danger 的规则
- №02「页面导航架构」 — 顶栏导航、侧边栏、面包屑都是 Link 的组合应用
- №03「表单结构与校验」 — 表单的提交按钮用
<button type="submit"> - №05「页面转场」 — Link 的点击可以触发转场动画(Button 不会)
完整可复制的 Prompt
我需要在页面上加一个可点击元素,帮我判断用 Link 还是 Button:
场景描述:[用户点了之后会发生什么?去其他地方 or 停在原地做操作]
如果是 Link:
- 用 <a href="真实 URL">文本</a>
- 样式遵循 TellYourAgent 设计系统:
* 导航链接:Ink (#1C1B18) + 无下划线,hover 变 Cinnabar (#C13E23)
* 正文链接:Cinnabar + 下划线,hover 变 Cinnabar-deep (#A33418)
- 如果是外部链接:加 target="_blank" + rel="noopener" + 外部图标(inline SVG)
- 焦点态:2px Cinnabar outline
如果是 Button:
- 用 <button type="button">文本</button>(表单内用 type="submit")
- 样式按操作重要性选 Primary / Secondary / Ghost / Danger(见「按钮怎么选」词条)
- 所有状态:Normal / Hover / Active / Disabled / Loading
- 焦点态:2px Cinnabar outline
- 键盘:Enter 和 Space 都能触发
如果我描述的场景是「看起来像链接但点了不跳转」:
→ 这是反模式,改成 Button 并去掉链接样式
输出:HTML + CSS(所有状态)+ 使用说明
为什么这个决策很重要
Link vs Button 是 Web 语义的基石。选对了,浏览器的所有功能(右键菜单、键盘快捷键、历史记录、书签)都正常工作;选错了,所有这些都会以微妙的方式坏掉——用户可能说不清哪里不对,但就是觉得「这个网站用起来怪怪的」。
更重要的是,屏幕阅读器用户完全依赖这个语义区分来理解「点了之后会发生什么」。Link 说「你会去某个地方」,Button 说「停在这里做个操作」——搞混了就是在对使用辅助技术的用户撒谎。
一句话:Link 和 Button 的区分,是 Web 可访问性的第一课。