Lighthouseで名前のないリンクを指摘されたときの直し方

​​Lighthouse​HTML,アクセシビリティ,トラブルシュート

Lighthouse のアクセシビリティの監査で「Links do not have a discernible name」(日本語のレポートでは「リンクに識別可能な名前が指定されていません」)と出たときは、ページの中に、読み上げ用の名前が付いていないリンクがあります。ヘッダーのロゴ画像、RSS や SNS のアイコン、お問い合わせのメールのアイコンのように、中に文字がなく画像やアイコンだけが入ったリンクが対象です。リンクの中の img に alt を付けるか、a 要素に aria-label を付けるか、画面には出さない文字をリンクの中に入れると、指摘は消えます。

私は検証環境で、名前のないリンクが4つあるページに Lighthouse を実行し、指摘が出ることと、直すと消えることを確かめました。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Lighthouse 13.6.0(npm で入れた CLI。中で使われている axe-core は 4.14.0)、ブラウザは Google Chrome for Testing 153 のヘッドレスです。アクセシビリティの項目だけ(--only-categories=accessibility)を、初期設定のモバイル向けの条件で実行しました。ページは手元で作り、127.0.0.1 から配信しました。Chrome の DevTools の Lighthouse パネルや PageSpeed Insights、実際のスクリーンリーダーでの読み上げは試していません。

指摘が出るページ

検証に使ったのは、ヘッダーにロゴと RSS のリンク、本文に記事カードのサムネイルのリンク、フッターにお問い合わせのリンクを置いたページです。

html
<header>
<!-- 1. alt のない img だけのロゴのリンク -->
<a href="/" class="logo"><img src="logo.svg" width="120" height="32"></a>
<!-- 2. SVG のアイコンだけのリンク -->
<a href="/feed.xml" class="rss">
  <svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">…</svg>
</a>
</header>
<main>
<h1>新着メモ</h1>
<!-- 3. alt="" の画像だけのリンク(記事カードのサムネイル) -->
<a href="/notes/1" class="thumb"><img src="thumb.svg" width="160" height="90" alt=""></a>
<p>Lighthouse の点数を読む</p>
</main>
<footer>
<!-- 4. CSS で描いたアイコン(中身が空)のリンク -->
<a href="/contact" class="mail"><span class="icon-mail"></span></a>
</footer>

SVG の中身(path など)は長いので「…」で省いています。このページを python3 -m http.server で配信し、次のように Lighthouse を実行しました。

bash
npx lighthouse http://127.0.0.1:8000/bad.html \
  --only-categories=accessibility \
  --output=json --output=html --output-path=./out/bad \
  --chrome-flags="--headless=new --no-sandbox" --quiet

HTML のレポートを開くと、4つのリンクがすべて「Failing Elements」に並びました。

Lighthouse のレポートで「Links do not have a discernible name」を開いた画面。a.logo、a.rss、a.thumb、a.mail の4つが Failing Elements に並び、それぞれのリンクの位置が枠で示されている
直す前のページのレポート。名前のない4つのリンクが並ぶ

JSON のレポートから、アクセシビリティの点数と、この監査の結果を取り出すと次のとおりです。4つのリンクの理由は同じなので、1つ目だけ載せています。Lighthouse のバージョンや点数の内訳などの行も省いています。

text
accessibility score: 80
link-name title: Links do not have a discernible name
link-name score: 0 (binary)
- body > header > a.logo | <a href="/" class="logo">
   Fix all of the following:
     Element is in tab order and does not have accessible text
   
   Fix any of the following:
     Element does not have text that is visible to screen readers
     aria-label attribute does not exist or is empty
     aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
     Element has no title attribute
failed accessibility audits: image-alt, link-name

「Fix any of the following」の下に並んでいるのが、リンクに名前を付ける方法です。この判定は、Lighthouse が中で使っている axe-core の link-name のルールで決まっています。リンクの中の読み上げられる文字、aria-label、aria-labelledby、title 属性のどれか(any)があれば合格です。同じルールに、対象は href のある a 要素(a[href])だけ、とも書かれています。

ロゴのリンクは、中の img(a.logo > img)に alt がないので、「Image elements do not have [alt] attributes」(image-alt)でも同時に指摘されました。サムネイルのリンクは alt="" なので image-alt には合格しますが、リンクの名前にはなりません。

4つの直し方

次のように直すと、指摘は消えました。

html
<!-- 1. img の alt にリンク先を表す言葉を入れる -->
<a href="/" class="logo"><img src="logo.svg" width="120" height="32" alt="トップページ"></a>
<!-- 2. aria-label で名前を付ける -->
<a href="/feed.xml" class="rss" aria-label="RSS フィード">
  <svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">…</svg>
</a>
<!-- 3. 画像と見出しを1つのリンクにまとめる -->
<a href="/notes/1" class="thumb"><img src="thumb.svg" width="160" height="90" alt="">
<p>Lighthouse の点数を読む</p></a>
<!-- 4. 画面には出さないテキストを入れる -->
<a href="/contact" class="mail"><span class="icon-mail"></span><span class="visually-hidden">お問い合わせ</span></a>

4つ目の visually-hidden は、文字を1ピクセルの枠に押し込めて、画面では見えなくする CSS です。検証のページでも、「お問い合わせ」の文字は 1×1 ピクセルの大きさになっていました。

css
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

ロゴのように画像そのものがリンクになっているときは、画像の alt がそのままリンクの名前になります。レポートの link-name の説明にも、リンクの文字(画像をリンクにしているときは、その代わりのテキスト)が区別できると、スクリーンリーダーを使う人が移動しやすくなる、と書かれています。

記事カードのように、サムネイルの横に記事のタイトルがあるときは、画像とタイトルを1つの a 要素で囲むと、タイトルがそのままリンクの名前になります。画像の alt は空のままにしたので、下の ariaSnapshot() の結果でも、リンクの名前はタイトルの1回分だけになっています。ただし、タイトルの文字もリンクの中に入るので、ブラウザの初期のスタイルのままだと、青い文字に下線が付いた表示に変わりました(検証では rgb(0, 0, 238) と underline)。元の見た目に戻したいときは、CSS でリンクの色と下線を指定します。

同じ条件で Lighthouse を実行し直すと、指摘は消え、点数は100になりました(点数の内訳などの行は省いています)。

text
accessibility score: 100
link-name title: Links have a discernible name
link-name score: 1 (binary)
failed accessibility audits: (none)

Playwright の ariaSnapshot() でも、リンクの名前を確かめました。ページの構造を書き出すと、直す前のリンクは名前のない link で、直した後は alt、aria-label、囲んだタイトル、隠した文字がそれぞれ名前になっていました。

text
--- bad.html
- banner:
  - link:
    - /url: /
    - img
  - link:
    - /url: /feed.xml
- main:
  - heading "新着メモ" [level=1]
  - link:
    - /url: /notes/1
  - paragraph: Lighthouse の点数を読む
- contentinfo:
  - link:
    - /url: /contact
--- good.html
- banner:
  - link "トップページ":
    - /url: /
    - img "トップページ"
  - link "RSS フィード":
    - /url: /feed.xml
- main:
  - heading "新着メモ" [level=1]
  - link "Lighthouse の点数を読む":
    - /url: /notes/1
    - paragraph: Lighthouse の点数を読む
- contentinfo:
  - link "お問い合わせ":
    - /url: /contact

名前にならない書き方と、チェックの対象から外れるだけの書き方

ほかに、次の4つの書き方も試しました。Failing Elements に残ったのは、aria-hidden="true" の文字を入れたロゴのリンクだけでした。

html
<!-- 1. 文字に aria-hidden="true" を付ける -->
<a href="/" class="logo"><img src="logo.svg" width="120" height="32" alt=""><span class="visually-hidden" aria-hidden="true">トップページ</span></a>
<!-- 2. SVG の中に title 要素を入れる -->
<a href="/feed.xml" class="rss">
  <svg viewBox="0 0 24 24" width="24" height="24"><title>RSS フィード</title>…</svg>
</a>
<!-- 3. a 要素に title 属性だけを付ける -->
<a href="/notes/1" class="thumb" title="Lighthouse の点数を読む"><img src="thumb.svg" width="160" height="90" alt=""></a>
<p>Lighthouse の点数を読む</p>
<!-- 4. href のない a 要素(クリックは JavaScript で受ける) -->
<a class="mail" onclick="location.href='/contact'"><span class="icon-mail"></span></a>

Lighthouse の結果は次のとおりです(「Fix any of the following」と理由の行は、直す前と同じなので省いています。点数の内訳などの行も省いています)。

text
accessibility score: 92
link-name title: Links do not have a discernible name
link-name score: 0 (binary)
- body > header > a.logo | <a href="/" class="logo">
failed accessibility audits: link-name

ariaSnapshot() で書き出した名前は、次のとおりです。

text
--- partial.html
- banner:
  - link:
    - /url: /
  - link "RSS フィード":
    - /url: /feed.xml
    - img "RSS フィード"
- main:
  - heading "新着メモ" [level=1]
  - link "Lighthouse の点数を読む":
    - /url: /notes/1
  - paragraph: Lighthouse の点数を読む
- contentinfo

aria-hidden="true" を付けた文字は、リンクの名前になりませんでした。aria-hidden="true" は、その要素と中身をアクセシビリティツリーから外す属性です。画面に出さない文字で名前を付けるときは、visually-hidden の CSS だけを使い、aria-hidden は付けません。

SVG の中に title 要素を入れたリンクと、a 要素に title 属性を付けたリンクは、どちらも Failing Elements に出ませんでした。この2つは、名前の付け方として使えます。ariaSnapshot() でも、それぞれ「RSS フィード」「Lighthouse の点数を読む」が名前になっています。

気を付けたいのは4つ目の、href のない a 要素です。Lighthouse の指摘には出てきませんが、これは直ったのではなく、link-name のルールの対象(a[href])から外れただけです。ariaSnapshot() では、フッター(contentinfo)の中にリンクがなくなりました。Tab キーを押してフォーカスが移る順番を書き出すと、href のない a 要素には一度もフォーカスが当たりませんでした。

text
--- bad.html: a.logo → a.rss → a.thumb → a.mail → body → a.logo
--- partial.html: a.logo → a.rss → a.thumb → body → a.logo → a.rss

キーボードだけで操作する人は、このお問い合わせのリンクに移れません。指摘を消すために href を外すのではなく、href を残したまま名前を付けます。

リンクを1つ残すと点数はどうなるか

Lighthouse は axe-core の結果を監査ごとに違反があれば0点、なければ1点にし、その重み付きの平均をアクセシビリティの点数にします。4つのうち3つの指摘が消えた上のページでも、link-name は0点のままで、点数は92でした。Lighthouse 13.6.0 の設定では、link-name の重みは7で、image-alt(10)より少し小さくなっています。

検証で出た点数を、JSON のレポートの重みから計算し直すと次のとおりです。

ページ 落ちた監査 重みの合計(取れた分 / 全体) 点数
直す前 link-name、image-alt 69 / 86 80
名前にならない書き方などを混ぜたページ link-name 79 / 86 92
直した後 なし 130 / 130 100

直した後のページだけ全体の重みが大きいのは、aria-valid-attr などの ARIA 属性の監査が計算に加わったためです。ほかの2つのページでは、これらの監査は「該当なし」として計算から外れていました。

​Posted by 86