# キーボードでは署名できない手書きの署名欄

Source: https://easeweb.dev/learn/signature-pads
Topics: キーボードとフォーカス > 例外なく、すべてキーボードで操作できる (WCAG 2.1.3)
The fix: パッドの隣に「署名として氏名を入力」というラベル付きの入力欄を加え、どちらか一方があればフォームを受け付けます。または、パッドをこの入力欄に置き換えます。手書きは追加の手段として残せますが、署名の唯一の方法にはしません。
Test with: keyboard, screen reader, speech input
References: https://www.w3.org/WAI/WCAG22/Understanding/keyboard-no-exception.html https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html https://www.w3.org/WAI/WCAG22/Understanding/pointer-gestures.html https://github.com/szimek/signature_pad https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas

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

## 場面

ある自転車のレンタル店では、自転車を受け取る前に、オンラインでレンタル契約に署名してもらいます。フォームには「氏名」の欄、署名の線と「書き直す」ボタンのある「署名」の枠、そして「署名して送信」ボタンがあります。この枠は署名パッドと呼ばれるもので、マウスや指、スタイラスを押したまま動かすと、その軌跡に線が描かれる canvas です。紙に署名する感覚に近いため、店はこの方法を選びました。

キーボードの利用者が氏名を入力してTabキーを押すと、「書き直す」に移動します。パッドは飛ばされ、どのキーを押しても何も描けません。「署名して送信」を押すと「枠内に署名してください。」と表示されます。契約に署名できないため、自転車を借りられません。

## 確認できる問題

レベルAAAのWCAG 2.1.3は、例外なく、すべてをキーボードで操作できることを求めています。レベルAの2.1.1には例外が1つあります。始点と終点だけでなく、ポインターの動いた軌跡そのものが必要な機能で、フリーハンドの描画がその例です。手書きの署名も、これに当たる場合があります。W3Cのポインタージェスチャーの解説にも、「署名の入力は本質的に軌跡に基づく場合がある（ただし、何かに同意することや本人であることを確認することは、そうである必要はない）」とあります。

つまり、このパッドは2.1.1の例外に当たる可能性がありますが、レベルAAAには頼れる例外がありません。満たすには、パッドをキーボードで描けるようにするのではなく、契約への署名という機能そのものに、名前の入力という軌跡の要らない方法を用意します。

ブラウザーにはこれを補う仕組みがありません。canvas はただのビットマップで、開発者が付け加えない限り、役割もフォーカスもなく、キー入力も受け取りません。signature_pad などの署名パッドのライブラリーが受け取るのは、ポインター、マウス、タッチのイベントだけです。署名を送る隠しフィールドも描いた内容からしか値が入らないため、ほかの方法では埋められません。

## 利用者への影響

署名は、レンタル、配達の受け取り、同意書、賃貸契約など、手続きの最後に求められることが多いものです。キーボード、スイッチ、音声ですべての欄を入力してきた人が、最後の手順で先に進めなくなります。スクリーンリーダーを有効にしたスマートフォンでは、タッチはまずスクリーンリーダーに渡るため、描くには指の動きをそのままページに渡す特別なジェスチャーが必要で、しかも見えない枠の中に書くことになります。

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

axeなどの自動チェックは、canvas を役割のない要素としてコントロールの対象から外し、「書き直す」も名前の付いたボタンとして通すため、何も報告しません。実際に署名を試すこともないため、描かなければフォームを送れないことには気付きません。AIに署名欄を頼むと、canvas のライブラリーを使い、ほかには何も用意しないことがあります。signature_pad などのライブラリーの例も、パッドだけを置いています。EaseWebの診断では、pointerdown または mousedown を受け取る120×60ピクセル以上の canvas や SVG のうち、フォーカスを受け取らず、同じ fieldset（なければ親要素）の中に入力欄もないものごとに要確認項目を出し、描く内容を入力でも渡せるかを人が確認できるようにしています。

## 修正前：手書きのパッドだけ

```html
<form class="agreement">
  <p class="field">
    <label for="name">氏名</label>
    <input id="name" name="name" autocomplete="name" required>
  </p>
  <div class="signature">
    <p>署名</p>
    <canvas id="pad" width="400" height="140"></canvas>
    <button type="button" id="clear">書き直す</button>
    <input type="hidden" name="signature" id="signature-data">
    <p id="signature-error" class="error" hidden>枠内に署名してください。</p>
  </div>
  <button type="submit">署名して送信</button>
</form>
```

```js
import SignaturePad from 'signature_pad';

const form = document.querySelector('.agreement');
const pad = new SignaturePad(document.getElementById('pad'));
const error = document.getElementById('signature-error');

document.getElementById('clear').addEventListener('click', () => pad.clear());

form.addEventListener('submit', (event) => {
  if (pad.isEmpty()) {
    event.preventDefault();
    error.hidden = false;
    return;
  }
  document.getElementById('signature-data').value = pad.toDataURL();
});
```

氏名の欄とボタンには問題がありません。問題は署名にあります。

- Tabキーは「氏名」から「書き直す」に移動します。パッドには一度も止まらず、どのキーでも描けません。
- 隠しフィールドには描いた内容からしか値が入らないため、ほかに署名する方法がありません。
- 何か描くまで「署名して送信」はフォームを受け付けないため、キーボードの利用者はまったく送信できません。

## 修正後：入力か手書きか

```html
<form class="agreement">
  <p class="field">
    <label for="name">氏名</label>
    <input id="name" name="name" autocomplete="name" required>
  </p>
  <fieldset class="signature">
    <legend>署名</legend>
    <label for="typed-signature">署名として氏名を入力</label>
    <input id="typed-signature" name="typed-signature" autocomplete="name">
    <p>または手書きで署名</p>
    <canvas id="pad" width="400" height="140"></canvas>
    <button type="button" id="clear">手書きを消す</button>
    <input type="hidden" name="signature" id="signature-data">
    <p id="signature-error" class="error" hidden>氏名を入力するか、手書きで署名してください。</p>
  </fieldset>
  <button type="submit">署名して送信</button>
</form>
```

```js
import SignaturePad from 'signature_pad';

const form = document.querySelector('.agreement');
const pad = new SignaturePad(document.getElementById('pad'));
const typed = document.getElementById('typed-signature');
const error = document.getElementById('signature-error');

document.getElementById('clear').addEventListener('click', () => pad.clear());

form.addEventListener('submit', (event) => {
  if (pad.isEmpty() && !typed.value.trim()) {
    event.preventDefault();
    error.hidden = false;
    typed.focus();
    return;
  }
  if (!pad.isEmpty()) {
    document.getElementById('signature-data').value = pad.toDataURL();
  }
});
```

1. 署名を `fieldset` でまとめ、凡例を「署名」にします。2つの署名方法が1つの質問として読み上げられます。
2. 先頭に「署名として氏名を入力」というラベル付きの入力欄を置きます。Tabキーで移動でき、スクリーンリーダーはラベルを読み上げ、音声操作でも入力できます。
3. その後ろに、手で書きたい人のためのパッドを「または手書きで署名」として残します。
4. どちらか一方があればフォームを受け付けます。描いた内容は、描かれているときだけ読み取ります。
5. どちらも空のときは、エラーで両方の方法を伝え、入力欄にフォーカスを移します。

これで修正前の例は直り、2.1.3を満たします。入力した名前が同じ役目を果たすため、署名に軌跡が要らなくなります。手書きは、使いたい人のための近道として残ります。

## 修正後：氏名の入力だけ

```html
<form class="agreement">
  <p class="field">
    <label for="name">氏名</label>
    <input id="name" name="name" autocomplete="name" required>
  </p>
  <div class="signature">
    <label for="typed-signature">署名として氏名を入力</label>
    <input id="typed-signature" name="signature" autocomplete="name">
    <p id="signature-error" class="error" hidden>署名として氏名を入力してください。</p>
  </div>
  <button type="submit">署名して送信</button>
</form>
```

```js
const form = document.querySelector('.agreement');
const typed = document.getElementById('typed-signature');
const error = document.getElementById('signature-error');

form.addEventListener('submit', (event) => {
  if (!typed.value.trim()) {
    event.preventDefault();
    error.hidden = false;
    typed.focus();
  }
});
```

1. パッド、「書き直す」ボタン、隠しフィールドを、「署名として氏名を入力」というラベル付きの入力欄1つに置き換え、その値を署名とします。
2. その欄が空のときだけフォームを受け付けず、エラーを出してその欄にフォーカスを移します。
3. 署名のライブラリーは外します。ページで描くものはもうありません。

これで修正前の例は直り、最も少ないコードで2.1.3を満たします。ページをどの方法で使っていても署名できます。失うのは手書きの署名で、署名するなら手で書くものと考える人もいます。

## どの方法を選ぶか

どちらも基準を満たします。入力か手書きかを選べる方法は、手で署名したい人のためにパッドを残せますが、入力欄が1つ増え、どちらか一方を受け付ける確認が必要です。氏名の入力だけにする方法は、フォームが最も小さく、保守も最も楽で、手書きの署名を使う必要のない事業に向いています。署名を何に使うかで選んでください。事業や規則で手書きの署名を残す必要があるなら、入力欄の隣にパッドを残します。同意の記録だけが目的なら、入力した名前だけで足ります。

## 実装の判断

入力した名前が有効な署名として認められるかは、技術ではなく法律の問題で、契約の内容や国によって答えが変わります。電子署名サービスには、手書きの署名と並べて、名前を入力する署名を用意しているものがいくつかあります。フォームの持ち主が入力の署名をオフにできるサービスもあり、オフにすると、署名の方法として手書きしか残らないことがあります。

「氏名」の欄と入力の署名は、どちらも名前を尋ねます。氏名の欄から署名を自動で埋めると、本人は何も署名していないことになるため、そうしないでください。`autocomplete="name"` を付ければ、ブラウザーが1回の選択で入力できる候補を出します。

署名の画像のアップロードだけでは十分ではありません。ファイルの選択はキーボードでできますが、手で書けない人が署名の画像を持っているとは限りません。

矢印キーでパッドに描けるようにするのも修正になりません。矢印キーで描いた線は署名とは言えず、時間がかかり、キーを押すのに苦労する人には大きな負担になります。スクリーンリーダーの利用者には、描いたものが見えないままです。

フリーハンドの描画、絵を描くこと、軌跡に沿って操作するゲームのように、入力で代われない機能もあります。そうしたコンテンツは2.1.3を満たせず、W3Cの解説もそう述べています。それでも描画ツールは、図形、色、描いたものの移動など、軌跡ではない操作にはキーボードの方法を用意できます。2.1.1が求めているのも、この部分です。

多くのフォームのように、手書きが同意を示すためだけのものなら、署名すること自体には軌跡が要らず、ほかの方法のないパッドはレベルAの2.1.1も満たさない可能性があります。その問題は[Tabキーで到達できないdivのボタン](/ja/learn/native-buttons)とそのトピックで扱います。ここでの修正は、どちらも直します。

## 修正の確認

1. マウスとタッチパッドを使わず、フォームの先頭からTabキーで移動し、キーボードだけですべての欄を入力し、署名して送信する。
2. パッドを空のまま入力欄だけで署名し、エラーなしで送信されるか確認する。
3. スクリーンリーダーでTabキーで署名に移動し、入力欄のラベル（グループの中にある場合はグループ名も）が読み上げられるか確認して、名前を入力して送信する。
4. 音声操作で入力欄のラベルを言い、名前を音声で入力し、ボタンの名前を言って送信する。

OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。フォームや署名のライブラリーを変更した際は、再度確認します。

## 制限

このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。署名をキーボードでも行えるようにする方法を示していますが、入力した名前が契約に対して法的に十分かどうかは判断していません。ドラッグなど、1本の指やポインターで代われないジェスチャーは、[ボタンで代われないピンチ・ドラッグ・傾きのジェスチャー](/ja/learn/gestures)で扱っています。キーボード、スイッチ、音声操作、スクリーンリーダーを使う人と一緒に、手続き全体を確認してください。

---

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