skip-link必须是页面第一个可聚焦元素且紧贴<body>开头,不可嵌套;需用position+left视觉隐藏而非aria-hidden或display:none;href目标ID必须真实存在、大小写匹配并设tabindex="-1"以确保可聚焦。

skip-link 必须是页面第一个可聚焦元素
它得紧贴 <body> 开头放,不能嵌在 <header>、<nav> 或任何组件内部。一旦被包裹,Tab 顺序会跳过它,键盘用户根本碰不到——哪怕写了 tabindex="0" 也无效。
常见错误写法:<header><a href="#main-content">跳到主要内容</a></header>
正确写法:<body><a href="#main-content" class="skip-link">跳到主要内容</a><header>...</header>
别给 skip-link 加 tabindex="-1",这等于主动锁死它;也不要用 aria-hidden="true" 或 display: none,读屏器会彻底忽略。
立即学习“前端免费学习笔记(深入)”;
隐藏与显示必须用 position + left 移出视口
视觉隐藏 ≠ 语义隐藏。很多团队误用 clip-path、transform: scale(0) 或 opacity: 0,这些在旧版 NVDA 或 Safari + VoiceOver 组合下可能不触发重绘,焦点来了但链接不显示。
兼容性最稳的写法:
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 1rem;
top: 1rem;
z-index: 1000;
outline: 2px solid #007aff;
outline-offset: 2px;
}
注意:top: 0 容易被 position: fixed 的导航栏盖住;z-index 必须足够高(至少 1000),否则焦点出现但被遮挡。
href 目标必须真实存在且可聚焦
href="#main-content" 跳转失败,90% 是目标 ID 不匹配或不可聚焦:
- ID 大小写敏感:
id="main-content"和href="#Main-Content"不等价 -
<main id="main-content">默认不可聚焦,必须加tabindex="-1" - 目标元素不能设
display: none或visibility: hidden,Safari + VoiceOver 会跳过它 - 避免对目标元素设
overflow: hidden或position: fixed/absolute,会切断原生锚点滚动流 - 若用 React/Vue,确保
id="main-content"在初始 DOM 加载时就存在,别等useEffect或mounted后才挂载
测试时最容易忽略的三个细节
写了 skip-link 不等于它生效。必须用纯键盘验证:
打开页面 → 按一次 Tab → 确认焦点落在 skip-link 上 → 回车 → 视口是否滚动并聚焦到 #main-content 元素内
跳转后,再按一次 Tab,焦点是否自然进入主内容里的第一个可聚焦元素(比如 <h1> 或 <button>)?如果停在空白处或卡住,大概率是 <main> 缺了 tabindex="-1",或它被 CSS 干扰了可聚焦性。
最后一点常被忽略:skip-link 不是孤立功能。它必须和语义结构配合——<nav> 包裹导航、<main> 标记主体、ID 唯一且拼写一致,缺一不可。单独加个链接,其他部分没语义,整体可访问性依然残缺。



















