# 代替テキストを尋ねない画像のアップロード

Source: https://easeweb.dev/learn/alt-text-prompts
Topics: ページの構造 > エディターやアップロードが代替テキストを求め、見出しを保つ (EN 301 549 11.8.2, 11.8.3, 11.8.4, 11.8.5; Section 508 504)
The fix: 写真を加えるたびに、名前の付いた欄と装飾として指定する選択肢のあるダイアログで説明を求めるか、出品を公開する前にすべての写真を確認し、足りない説明をそこで求めます。代替テキストをファイル名で埋めることはしません。
Test with: keyboard, screen reader
References: https://www.etsi.org/deliver/etsi_en/301500_301599/301549/03.02.01_60/en_301549v030201p.pdf https://www.access-board.gov/ict/ https://www.w3.org/TR/ATAG20/ https://www.w3.org/WAI/tutorials/images/decision-tree/ https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog

よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。

## 場面

Makers Market は、小さな作り手が作品を出品するマーケットプレイスです。ある出品者が、縁が紺色の白いホーローのマグを出品します。出品フォームには、タイトルと「写真を追加」のボタンがあります。出品者はスマートフォンから写真を選び、出品に表示されたのを確かめて公開します。

フォームは、写真に何が写っているかを一度も尋ねませんでした。画像には何かしらの代替テキストが必要で、手元にあった文字列はそれだけだったため、ファイル名の IMG_2041.jpg を代替テキストにしました。スクリーンリーダーを使う買い手が写真に来ると、「IMG_2041.jpg、画像」と聞こえます。出品者は目で見る人のために写真でマグを丁寧に伝えましたが、そのどれも買い手には届きません。

## 確認できる問題

出品フォームは、出品者がWebコンテンツを作るために使うオーサリングツールです。EN 301 549 の11.8.2は、オーサリングツールが、Webの要件を満たすコンテンツの作成を「可能にし、導く」ことを求めます。Section 508 の504.3も、WCAGに適合するコンテンツを作るよう作成者に促すモードを求めています。説明を一度も尋ねないフォームは、どちらもしていません。写真を説明する場所がなく、説明するよう導くこともありません。

ファイル名で埋めると、さらに悪くなります。公開された写真は、「IMG_2041.jpg」が写真の目的を果たさないためWCAG 1.1.1を満たしませんが、alt 属性はあるので、属性の有無を見るチェックにも出品者にも完成しているように見えます。Section 508 の504.2は、アクセシビリティに必要な情報を作成者が上書きできることも求めています。ここでは、出品者が写真を装飾として指定できることを意味します。上書きは作成者の選択であって、ツールの推測ではありません。

## 利用者への影響

この問題が及ぶのは、フォームを使った出品者ではなく買い手で、フォームが公開するすべての出品で繰り返されます。何千人もの出品者がいるマーケットプレイスでは、フォームにない一つの質問が、何も伝えない何千枚もの写真になり、それはどの出品者にも直せません。

## 自動チェックとAIの限界

axe は各画像に alt 属性があることを確かめます。この画像にはあるため合格です。ANDI はファイル名を含む代替テキストを指摘し、この診断もその指摘を公開された画像についての画像の問題として伝えますが、画像を作ったフォームが原因だとは知りようがありません。診断に渡されたページに、画像をアップロードするのに説明の欄がないフォームがあれば、確認の手がかりとして報告します。フォームが、診断からは見えない次の手順で尋ねている可能性があるためです。オーサリングツールの多くはサインインの後ろにあるため、出品者用のページは手作業で確認してください。

AIによる画像の説明は、空白を埋めてくれることもあれば、誤解を招くこともあります。写真から生成された説明（「テーブルの上の白いカップ」）は、写っているものは伝えても、買い手が知りたいこと、つまりホーローで350 mL入り、縁が紺色であることは伝えません。出品者が読んで直す候補として使えば役に立ちますが、読まれないまま公開されれば、もう一つの既定の文字列にすぎません。

## 修正前：ファイル名で公開される写真

```html
<form action="/listings" method="post">
  <label for="title">タイトル</label>
  <input id="title" name="title">
  <label for="photo">写真を追加</label>
  <input id="photo" type="file" accept="image/*">
  <div id="photos"></div>
  <button type="submit">出品を公開</button>
</form>
```

```js
const photo = document.getElementById('photo');
const photos = document.getElementById('photos');

photo.addEventListener('change', async () => {
  const file = photo.files[0];
  const img = new Image();
  img.src = await upload(file);
  img.alt = file.name;
  photos.append(img);
});
```

フォームは写真を選んだ瞬間に加え、代替テキストをファイル名で埋めます。出品者は何が写っているかを尋ねられず、公開された出品では「IMG_2041.jpg」と読まれます。

## 修正後：写真を加えるときに説明を求める

```html
<form action="/listings" method="post">
  <label for="title">タイトル</label>
  <input id="title" name="title">
  <label for="photo">写真を追加</label>
  <input id="photo" type="file" accept="image/*">
  <div id="photos"></div>
  <button type="submit">出品を公開</button>
</form>

<dialog id="describe" aria-labelledby="describe-title">
  <form method="dialog">
    <h2 id="describe-title">この写真を説明する</h2>
    <img id="preview" alt="">
    <label for="photo-alt">写真には何が写っていますか？</label>
    <p id="photo-alt-hint">写真が見えない買い手には、代わりにこの説明が読まれます。
      色、大きさ、素材など、買い手が知りたいことを書いてください。</p>
    <textarea id="photo-alt" aria-describedby="photo-alt-hint"></textarea>
    <p id="photo-alt-error" hidden>写真を説明するか、下のボックスにチェックを入れてください。</p>
    <label><input type="checkbox" id="decorative"> 出品を飾るだけの写真で、説明することはない</label>
    <button value="add">この写真を追加</button>
    <button value="cancel">キャンセル</button>
  </form>
</dialog>
```

```js
const photo = document.getElementById('photo');
const photos = document.getElementById('photos');
const describe = document.getElementById('describe');
const text = document.getElementById('photo-alt');
const error = document.getElementById('photo-alt-error');
const decorative = document.getElementById('decorative');

photo.addEventListener('change', () => {
  document.getElementById('preview').src = URL.createObjectURL(photo.files[0]);
  text.value = '';
  decorative.checked = false;
  error.hidden = true;
  text.removeAttribute('aria-invalid');
  text.setAttribute('aria-describedby', 'photo-alt-hint');
  describe.returnValue = '';
  describe.showModal();
});

describe.querySelector('form').addEventListener('submit', (event) => {
  if (event.submitter?.value !== 'add' || text.value.trim() || decorative.checked) return;
  event.preventDefault();
  error.hidden = false;
  text.setAttribute('aria-invalid', 'true');
  text.setAttribute('aria-describedby', 'photo-alt-hint photo-alt-error');
  text.focus();
});

describe.addEventListener('close', async () => {
  if (describe.returnValue !== 'add') return;
  const img = new Image();
  img.src = await upload(photo.files[0]);
  img.alt = decorative.checked ? '' : text.value.trim();
  photos.append(img);
});
```

出品者は、写真のことをいちばんよく分かっている瞬間に、何が写っているかを尋ねられます。ヒントが、誰のための説明か、何を書けばよいかを伝えます。説明もチェックもないまま「この写真を追加」を押すと、代替テキストのない写真を加えるのではなく、欄の横で何をすればよいかを伝えます。チェックボックスは出品者による上書きです。装飾として指定した写真は空の alt で公開され、スクリーンリーダーは読み飛ばします。

## 修正後：出品を公開する前に写真を確認する

```html
<form action="/listings" method="post">
  <label for="title">タイトル</label>
  <input id="title" name="title">
  <label for="photo">写真を追加</label>
  <input id="photo" type="file" accept="image/*">
  <div id="photos"></div>
  <section id="check" hidden aria-labelledby="check-title">
    <h2 id="check-title" tabindex="-1">公開する前に写真を説明してください</h2>
    <p>写真が見えない買い手には、代わりにこの説明が読まれます。</p>
    <div id="check-list"></div>
  </section>
  <button type="submit">出品を公開</button>
</form>

<template id="check-row">
  <div class="check-row">
    <img alt="">
    <label><span></span> <textarea></textarea></label>
    <label><input type="checkbox"> 出品を飾るだけの写真で、説明することはない</label>
  </div>
</template>
```

```js
const photo = document.getElementById('photo');
const photos = document.getElementById('photos');
const check = document.getElementById('check');

photo.addEventListener('change', async () => {
  const img = new Image();
  img.src = await upload(photo.files[0]);
  photos.append(img); // alt はまだ付けない。公開前の確認で尋ねる。
});

photo.form.addEventListener('submit', (event) => {
  // 確認が表示されていれば、出品者が書いた説明を取り込む。
  for (const row of check.querySelectorAll('.check-row')) {
    const words = row.querySelector('textarea').value.trim();
    if (words || row.querySelector('input').checked) row.img.alt = words;
  }
  const missing = [...photos.querySelectorAll('img:not([alt])')];
  if (!missing.length) return;
  event.preventDefault();
  document.getElementById('check-list').replaceChildren(...missing.map((img, i) => {
    const row = document.getElementById('check-row').content.firstElementChild.cloneNode(true);
    row.img = img;
    row.querySelector('img').src = img.src;
    row.querySelector('label span').textContent = `写真${i + 1}には何が写っていますか？`;
    return row;
  }));
  check.hidden = false;
  document.getElementById('check-title').focus();
});
```

写真は alt 属性なしですぐに加わり、「出品を公開」を押すと、説明のない写真を一つずつ挙げて止まります。それぞれに説明を書く欄と、同じ装飾の選択肢があります。フォーカスが確認の見出しに移るため、スクリーンリーダーの利用者にも何を求められているかが聞こえます。ボタンではなく出品そのものを確認するため、ドラッグして入れた写真も、貼り付けた写真も、一括取り込みで持ち込んだ写真も、どの経路で来た写真も見逃しません。

## どの方法を選ぶか

どちらも要件を満たします。写真を加えるときに尋ねる方法は、出品者が写真を見ている間に説明を得られ、写真がボタン一つからしか入らないフォームに向いています。公開の前に確認する方法は、どの経路で来た写真も見逃さず、何枚もの写真を手早く加えてからまとめて説明できますが、尋ねるのは後になり、出品者の頭は公開のことでいっぱいです。写真を加える方法が複数あるツールでは、両方を使うのがいちばんです。ボタンにはダイアログを、それ以外には公開前の確認を使います。

## 実装の判断

作成者が答えるまで、alt 属性は付けません。alt がないことは確認で見つけられますが、ファイル名、出品のタイトル、「写真」という語は完成しているように見え、抜けを隠します。空の alt で公開するのは、作成者がそれを選んだときだけです。説明は写真とともに、独立した項目としてサーバーに保存し、写真を表示するすべてのページで使われるようにします。

欄そのものと同じくらい、欄の言葉が大切です。「写真には何が写っていますか？」と尋ねる名前と、誰のための説明かを伝えるヒントがあると、多くの出品者にはなじみのない「代替テキスト」という名前の欄よりも良い説明が集まります。数文字に制限しないでください。たいていは一文で十分で、サイズ表のようにそれより長いものは、[代替テキストがない、情報を伝える画像](/ja/learn/informative-images)で説明しているとおり、出品の本文に書きます。

AIによる説明は、候補と分かる形で下書きとして欄に入れ、出品者が読んで直してから写真を加えるようにできます。出品者が見ていない説明を公開したり、質問を省くために使ったりしないでください。

ツールへのすべての入り口で、同じ質問が必要です。

- ページにドロップした写真や貼り付けた写真は、ダイアログが待つ change イベントなしで入るため、ドロップと貼り付けにもダイアログか確認が必要です。
- スプレッドシートや別のショップからの一括取り込みには、説明の列を用意し、説明のない行を指摘します。
- スマートフォンのアプリから加える写真にも、アプリの中に同じ欄が必要です。
- 出品者が、最初に加えたときだけでなく、出品の写真から後で説明を直せるようにします。

説明を書く手順もツールの一部なので、障害のある作成者にも使える必要があります。目の見えない出品者が、友人の撮った写真を加えることもあります。ダイアログには名前が必要で、フォーカスを中に移して元に戻します。これは[背後にフォーカスが残るモーダル](/ja/learn/modal-focus-management)で説明しています。エラーは、表示している間だけ欄に結び付けます。

## 修正の確認

1. ボタン、ドラッグ＆ドロップ、貼り付け、取り込みなど、ツールで可能なすべての方法で写真を加え、どれも出品を公開する前に説明を求められることを確認する。
2. 説明を付けた写真のある出品を公開し、スクリーンリーダーで読む。写真が説明として読まれ、ファイル名が読まれないことを確認する。
3. 写真を装飾として指定して公開し、公開された画像が `alt=""` で、スクリーンリーダーに読み飛ばされることを確認する。
4. 説明を書く手順を、キーボードだけで、またスクリーンリーダーを使って進める。欄の名前とヒントが読まれ、説明が空のときに何をすればよいかを伝えるエラーが出ることを確認する。

OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。写真の加え方を変えたときに、再度確認します。

## 制限

このガイドは、オーサリングツールが画像の説明をどう求めるかを扱います。同じ条項は、ツールにさらに多くを求めています。テキストエディターでは、大きな太字ではなく本物の見出しやリストを作れること（ページの側からは[大きさで選ばれ、構造と合わない見出し](/ja/learn/heading-structure)で説明しています）、そして文章を貼り付けたり変換したりしてもそれが保たれること（EN 301 549 の11.8.3、Section 508 の504.2.1）が必要です。ツールがコンテンツを確認するなら、直し方を示すこと（11.8.4）、テンプレートを用意するなら、少なくとも一つはアクセシブルで、そうと示されていること（11.8.5）も求められます。ツールの画面そのものを障害のある作成者が使えるかは別の問題で、WCAGとATAG 2.0のパートAが扱います。出品者と、その出品を読む買い手と一緒に試してください。

---

このガイドをコーディングエージェントで使う: MCP https://easeweb.dev/mcp（設定は https://easeweb.dev/ja/services/developers）
