2026年7月21日

2026年7月21日

WordPressのSVGにtitle・descを追加してスクリーンリーダーに対応する方法

SVGをスクリーンリーダーで正しく読み上げてもらうには、まず「意味のある画像か、飾りか」を分け、意味のあるインラインSVGには role="img" と <title>(必要なら <desc>)を付けて aria-labelledby で結び付け、飾りのSVGは aria-hidden="true" で読み上げ対象から外します。この記事では、WordPressでSVGを使う代表的な3つの場面(imgタグ、インラインSVG、アイコン付きのボタンやリンク)ごとに書き方を示し、テーマに組み込むPHPの関数例、既存の出力に属性を足す方法、確認のしかたまでを解説します。

まず「意味のある画像」か「飾り」かを決める

SVGのアクセシビリティ対応は、<title> を付けることから始めるのではなく、その画像がページの中で何を伝えているかを判断するところから始まります。W3C勧告であるWCAG 2.2(2026年10月現在)の達成基準1.1.1「非テキストコンテンツ」では、意味を持つ画像には同等の目的を果たすテキストの代替を、純粋な装飾は支援技術が無視できるようにすることを求めています。

  • 意味のある画像: グラフ、図解、ロゴ(会社名を表す)、それだけで操作内容を伝えるアイコン
  • 飾り: 見出しの横の模様、隣に同じ意味の文字があるアイコン(「検索」という文字の横の虫めがねなど)

飾りに説明を付けると、読み上げが冗長になり、かえって使いにくくなります。「全部のSVGにtitleを付ける」のは正しい対応ではない点に注意してください。

場面1: imgタグで表示するSVG

メディアライブラリにアップロードしたSVGを画像ブロックで表示する場合、出力は <img src="....svg"> になります。この場合の代替テキストは alt 属性で付けます。SVGファイルの中に <title> を書いても、imgタグで読み込んだときには使われません。

<!-- 意味のある画像 -->
<img src="/wp-content/uploads/2026/10/flow.svg" alt="申し込みから納品までの4つのステップ" role="img" width="640" height="360">

<!-- 飾り -->
<img src="/wp-content/uploads/2026/10/ornament.svg" alt="" width="40" height="8">

WordPressの画像ブロックでは、ブロックの設定にある代替テキストの欄に入力すれば alt が出力されます。飾りの場合は空のままにすると alt="" になります。

role="img" は、一部のブラウザとスクリーンリーダーの組み合わせでSVGのimgが画像として扱われない問題への対策として付けられてきたものです。現在の環境では不要な場合もありますが、付けても害はありません。また、width と height を指定しておくと、読み込み時にレイアウトがずれる(CLS)のを防げます。

なお、WordPressは初期状態ではSVGのアップロードを許可していません。SVGはスクリプトを含められる形式なので、許可する場合はSafe SVGのような、アップロード時にSVGを無害化するプラグインを使ってください。無害化の処理で <title> や <desc> が残るかどうかは設定やバージョンで異なるため、アップロード後のファイルを一度開いて確認しておくと安心です。

場面2: インラインSVGにtitleとdescを付ける

テーマのテンプレートやカスタムHTMLブロックでSVGのコードを直接書く場合は、SVGの中に代替テキストを持たせます。

<svg role="img" aria-labelledby="sales-title sales-desc" viewBox="0 0 400 200" width="400" height="200">
  <title id="sales-title">2026年の月別売上の推移</title>
  <desc id="sales-desc">1月から9月にかけて増加し、9月が最も多い。</desc>
  <!-- グラフの図形 -->
</svg>

ポイントは次のとおりです。

  • **<title> は <svg> の最初の子要素に置く**: 仕様上、最初に置いた <title> がその要素の名前として扱われます。
  • **aria-labelledby でidを指定する**: <title> だけでも名前として読まれる環境は増えていますが、ブラウザとスクリーンリーダーの組み合わせによって扱いに差があります。aria-labelledby で明示すると、より多くの環境で安定します。
  • **role="img" を付ける**: 図形がたくさん入ったSVGが、ひとまとまりの画像として扱われるようになります。
  • idはページ内で重複させない: 同じSVGを1ページに2回置くとidが重複し、意図と違う文字が読まれることがあります(後述のPHP関数で対策します)。

<desc> には、<title> だけでは伝わらない内容を書きます。グラフなら「何が増えたか、いつが最大か」といった要点です。数値をすべて伝える必要があるグラフは、SVGの中に詰め込まず、近くに表や本文で同じ情報を載せるほうが、スクリーンリーダー利用者にも、画像が見えない環境の人にも親切です。

飾りのインラインSVGは、次のように読み上げ対象から外します。

<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" width="24" height="24">...</svg>

focusable="false" は古いブラウザでSVGにフォーカスが当たってしまう問題への対策で、現在の主要ブラウザでは不要なことが多いものの、付けておいて困ることはありません。

場面3: アイコンだけのボタンやリンク

虫めがねのアイコンだけの検索ボタンや、SNSアイコンだけのリンクでは、SVGに説明を付けるより、ボタンやリンクそのものに名前を付けるのが基本です。WCAG 2.2の達成基準4.1.2「名前・役割・値」に関わる部分で、スクリーンリーダーは「検索、ボタン」のように、操作できる要素の名前を読み上げます。

<button type="submit" aria-label="サイト内を検索">
  <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" width="24" height="24">...</svg>
</button>

<a href="https://example.com/contact/">
  <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" width="20" height="20">...</svg>
  <span class="screen-reader-text">お問い合わせ</span>
</a>

screen-reader-text は、WordPressのテーマで広く使われている「画面には見えないがスクリーンリーダーには読まれる」ためのクラスです。お使いのテーマにこのクラスのCSSが定義されているか確認してください。定義が無いと、文字がそのまま画面に表示されます。

テーマに組み込む: アイコン出力用のPHP関数

テーマでアイコンを何度も使う場合は、出力を1つの関数にまとめると、飾りと意味のある画像の書き分けやidの重複対策を毎回考えずに済みます。次の例は、SVGスプライト(assets/icons.svg に <symbol id="icon-search"> などをまとめたファイル)を使う関数です。子テーマの functions.php に追加します。

<?php
/**
 * アイコンを出力する。$label が空なら飾りとして扱う。
 */
function my_icon( $name, $label = '' ) {
	$allowed = array( 'search', 'mail', 'chart' );
	if ( ! in_array( $name, $allowed, true ) ) {
		return '';
	}
	$href = esc_url( get_theme_file_uri( 'assets/icons.svg' ) . '#icon-' . $name );

	if ( '' === $label ) {
		return sprintf(
			'<svg class="icon" aria-hidden="true" focusable="false" viewBox="0 0 24 24" width="24" height="24"><use href="%s"></use></svg>',
			$href
		);
	}

	$id = wp_unique_id( 'icon-title-' );
	return sprintf(
		'<svg class="icon" role="img" aria-labelledby="%1$s" viewBox="0 0 24 24" width="24" height="24"><title id="%1$s">%2$s</title><use href="%3$s"></use></svg>',
		esc_attr( $id ),
		esc_html( $label ),
		$href
	);
}

テンプレートでは echo my_icon( 'chart', '月別の売上グラフ' ); や echo my_icon( 'search' ); のように使います。wp_unique_id() はリクエスト内で重複しない値を返すため、同じアイコンを何度置いてもidがぶつかりません。アイコン名を許可リストで確認し、ラベルは esc_html()、URLは esc_url() でエスケープしています。

外部ファイルのスプライトを <use> で参照する方法は、同じオリジンのファイルである必要があります。CDNなど別ドメインから配信している場合は、スプライトをページ内に埋め込む方法に切り替えてください。

既存のブロックの出力に属性を足す

プラグインや自作ブロックが出力するSVGに、後から aria-hidden を付けたい場合は、render_block フィルターと WP_HTML_Tag_Processor(WordPress 6.2以降)が使えます。

<?php
add_filter( 'render_block', function ( $html, $block ) {
	if ( 'my-plugin/feature-card' !== $block['blockName'] ) {
		return $html;
	}
	$p = new WP_HTML_Tag_Processor( $html );
	while ( $p->next_tag( array( 'tag_name' => 'svg' ) ) ) {
		if ( null === $p->get_attribute( 'aria-labelledby' ) && null === $p->get_attribute( 'aria-label' ) ) {
			$p->set_attribute( 'aria-hidden', 'true' );
			$p->set_attribute( 'focusable', 'false' );
		}
	}
	return $p->get_updated_html();
}, 10, 2 );

この例では、名前の付いていないSVGだけを飾りとして扱っています。WP_HTML_Tag_Processor は属性の追加や変更は得意ですが、<title> のような子要素を差し込む用途には向きません。意味のあるSVGに説明を付けたい場合は、SVGの元のコードやブロックの出力側を直してください。対象のブロック名は、実際のブロックに合わせて変更してください。

確認のしかた

書いたら、実際にどう読まれるかを確認します。

  1. ブラウザの開発者ツール: ChromeやFirefoxの開発者ツールには、要素のアクセシビリティ情報(名前と役割)を表示する機能があります。SVGを選んで、名前が意図どおりか、飾りが読み上げ対象から外れているかを見ます。
  2. スクリーンリーダー: WindowsならNVDA、MacやiPhoneならVoiceOverで、実際に読み上げを聞きます。
  3. 自動チェック: axe などのツールで、名前の無い画像やボタンを洗い出します。ただし、自動チェックで見つかるのは問題の一部です。「titleの文章が画像の意味を正しく伝えているか」は人が判断するしかありません。

まとめ

  • 最初に「意味のある画像」か「飾り」かを決め、飾りは alt="" や aria-hidden="true" で読み上げ対象から外す
  • imgタグのSVGは alt で、インラインSVGは role="img" と <title>・<desc> を aria-labelledby で結び付けて説明する
  • アイコンだけのボタンやリンクは、SVGではなくボタンやリンクに名前を付ける
  • テーマでは出力を関数にまとめ、wp_unique_id() でidの重複を防ぎ、ラベルはエスケープする
  • SVGのアップロードは無害化するプラグインを使い、開発者ツールとスクリーンリーダーで実際の読み上げを確認する

お気軽にご相談ください

お見積りへ お問い合わせへ
▲