本文へスキップ
Tab を1回押すと最初に「本文へスキップ」が現れ、Enter でナビ4件を飛ばして本文へフォーカスが移る。下の帯に今どこにフォーカスがあるかを表示。
プレビュー
操作クリック/タップで動きます。
仕様書
- 向く場面
- ナビが多く本文まで Tab が何回も要るページ
- 全ページ共通のヘッダーを持つサイト
- キーボード利用者の最初の 1 打を活かしたい場面
- 避ける場面
- display:none で隠す(Tab が止まる)
- 飛び先に tabindex=-1 を付けない
- ナビが 1〜2 件しかないページ
- 構成要素
- スキップリンク
- ヘッダーとナビ
- 本文(main)
- フォーカス位置の帯
- 持っている状態
- 通常
- フォーカス
- キーボード
Tab の 1 回目でスキップリンクが現れ、Enter で本文(main tabindex=-1)へフォーカスが移りナビ 4 件を飛ばす
- 支援技術
WCAG 2.4.1 ブロックスキップ。リンクは DOM の先頭に常に存在し、フォーカス時だけ可視化。飛び先の main に tabindex=-1。帯で現在のフォーカス位置を表示
- 動き
リンクの出現 150ms(transform+opacity)。reduced-motion で停止
デザインの要点
- スキップリンクは DOM の先頭に置く。Tab の1回目に来なければ、毎ページ4〜40回 Tab を押す人の時間は戻らない
- フォーカスが当たるまでは見えなくてよいが、当たったら必ず見える位置に出す。display:none は Tab 自体が止まるので使わない
- 飛び先の main に tabindex="-1" を付ける。無いと URL のハッシュだけ変わり、読み上げの位置が動かない
プロのサイトでの実例
実例は「型の使われ方」の観察であり、コード・ロゴの転載ではありません。サイト側の更新で見え方が変わることがあります。
使いどころ
グローバルナビが4件以上あり、全ページで同じヘッダーを繰り返すサイトに効く。キーボードだけで操作する人やスイッチデバイスの利用者は、ページを移動するたびに同じナビを Tab で数え直すことになるため、1打目で本文へ抜ける出口を用意する価値が大きい。逆に、ナビが1〜2件しかないランディングページや、ヘッダー自体がページごとに違う単発ページでは、飛ばす対象が小さく効果が薄い。アプリ的な画面で「本文」がどこを指すか一意に決まらない場合は、飛び先を main ではなく主要な操作領域にするか、「本文へ」「検索へ」のように飛び先を複数用意する形へ変える。
仕組み
DOM の先頭にリンクを置き、position:absolute で枠の外(上方向)へ逃がしておく。:focus でフォーカスが当たったときだけ translateY を 0 に戻し、150ms の transform+opacity で現れる。display:none や visibility:hidden を使わないのは、それらが要素をフォーカス順から外してしまい Tab で到達できなくなるため。クリック(Enter)時は既定のハッシュ移動に任せず preventDefault して main.focus() を呼んでいる。飛び先の main には tabindex="-1" を付けてあり、これが無いと main はフォーカス可能要素ではないので focus() が無視され、URL のハッシュだけが変わって読み上げ位置は動かない。見本は focusin を監視して、今どこにフォーカスがあるかを下の帯に文字で出している。
実装の注意
一番多い失敗は「見えなくする」方法の選び間違いで、display:none・visibility:hidden・hidden 属性はいずれも要素をフォーカス順から外すため、Tab を押しても現れない。clip-path や width:1px で潰す手法も、フォーカス時に元のサイズへ戻し忘れると「フォーカスは当たっているのに見えない」状態になり、かえって迷子になる。次に多いのが飛び先の tabindex="-1" 忘れで、見た目はスクロールするのに読み上げ位置が先頭のまま残る。position:sticky のヘッダーがあるサイトでは飛んだ先の見出しがヘッダーの下へ潜るので、飛び先に scroll-margin-top を付ける。z-index が足りずヘッダーの背面に隠れる事故もあるため、リンクは重なり順の最前面に置く。
アクセシビリティ
WCAG 2.0 から続く達成基準 2.4.1「ブロックスキップ」に直接対応する仕組みで、繰り返されるブロックを飛ばす手段としては最も実装が軽い。リンクは常に DOM に存在させ、視覚的な隠し方だけを切り替えるのが原則になる。飛び先には tabindex="-1" を付けてプログラムからフォーカスできるようにし、Tab 順には加えない(0 にすると本文そのものが余計な停止点になる)。見本の飛び先は main 要素なのでランドマークとしても機能し、読み上げソフトのランドマーク一覧からも到達できる。prefers-reduced-motion では出現のトランジションを止め、位置の移動なしで即座に表示へ切り替える。
調整して使う
触る値は3つ。ひとつめは出現位置で、既定は枠の左上に貼り付くが、中央寄せにするとヘッダーのロゴと重なりにくい。ふたつめは配色で、リンクの文字と背景のコントラストを 4.5:1 以上確保する。ヘッダーが画像やグラデーションのサイトでは、リンク側に不透明な背景色を必ず敷くこと。みっつめは飛び先の数で、検索が主要導線のサイトでは「本文へスキップ」「検索へスキップ」の2本を並べると効く。ただし3本以上並べると、今度はスキップリンク自体が飛ばしたいブロックになるので増やしすぎない。
コード(コピーして使えます)
<div class="skp-stage">
<a class="skp-link" href="#skp-main" id="skp-link">本文へスキップ</a>
<header class="skp-header">
<span class="skp-logo">ミナト工房</span>
<nav class="skp-nav" aria-label="グローバル">
<a href="#skp-nav-1">製品</a><a href="#skp-nav-2">料金</a><a href="#skp-nav-3">導入事例</a><a href="#skp-nav-4">サポート</a>
</nav>
</header>
<main class="skp-main" id="skp-main" tabindex="-1">
<h1>見積もりを3分で</h1>
<p>型番と数量を入れるだけ。担当者の確認なしに概算が出ます。</p>
<div class="skp-ctas">
<button type="button" class="skp-cta">見積もりを始める</button>
<button type="button" class="skp-sim" id="skp-sim">Tab を押す(模擬)</button>
</div>
</main>
<div class="skp-band"><span class="skp-tag">フォーカス</span><span id="skp-band">枠内をクリックして Tab を押すと、最初に「本文へスキップ」が現れます</span></div>
</div>