
UI/UX
公開 読了まで 約15分

Figma で丁寧に作ったボタンが、実装されて戻ってきたら、押したあとの見た目がなかった。キーボードで操作すると、どこを選んでいるのか分からない。送信中にボタンの幅がふっと縮む。AI に画面を組ませたら、無効のボタンが薄い灰色で、なぜ押せないのか誰にも分からない。ひとりでデザインを担当していると、こうした「デザインしていない部分」を、毎回あとから見つけて直すことになりがちです。
先に結論です。ボタンや入力欄の「状態」は、画面の一枚とは別に、状態の一覧と短い仕様文で渡します。とくに抜けやすいのは、フォーカス(キーボードで選んだとき)、無効、読み込み中、エラーの4つです。この記事では、その4つを NG と OK の作例で比べ、根拠になる WCAG の基準と、そのまま使える仕様の書き方をまとめました。
Figma の画面は、ある瞬間を止めた一枚です。ボタンには、カーソルを乗せたとき、キーボードで選んだとき、処理を待つ間など、いくつもの姿がありますが、画面に描かれるのはふつう1つだけです。実装する人は、描かれていない姿を推測で埋めます。忙しければブラウザやライブラリの初期値のまま進みますし、AI に組ませれば、書かれていない部分は AI がそれらしく決めてしまいます。
道具の側も変わりつつあります。Figma は2026年10月の Config India で、チームのデザインの決まりを文章で書いて、人とエージェント(指示を受けて作業を進める AI)が参照できる「Specs」を発表しました。有料プランでのオープンベータ(試験提供)です。公開されたデザインライブラリにエージェント向けの指針を付ける「Guidelines」もオープンベータで始まっています。どの道具を使うにしても、状態の決まりを文字で残しておくことが、これまで以上に効いてきます。
まず、どの状態を作るかを決めます。ボタンと入力欄なら、次の表が出発点になります。「必ず作る」と「必要なときに作る」を分けておくと、作業量を抑えられます。
| 状態 | いつ | 作るか | 見た目で決めること |
|---|---|---|---|
| 通常(default) | 何もしていないとき | 必ず | 部品だと分かる形。見分けるのに必要な線や面は、周りと 3:1 以上 |
| ホバー(hover) | カーソルを乗せたとき | 必ず(パソコン) | 少しの変化でよい。スマートフォンにはない |
| フォーカス(focus-visible) | キーボードで選んだとき | 必ず | はっきり見える印。ホバーと別の見た目にする |
| 押している間(pressed) | クリック・タップの間 | 必ず | 押せたと分かる変化 |
| 読み込み中(loading) | 押したあと、処理を待つ間 | 送信・保存のボタン | 幅を変えない。何をしているかを文字で残す |
| 無効(disabled) | 今は押せないとき | なるべく作らない | 使うなら、理由をすぐ近くに書く |
| エラー(error) | 入力欄の値に問題があるとき | 入力欄は必ず | 色だけに頼らず、文字で何が違うかを書く |

変化の強さの目安がほしいときは、Google の Material Design 3 の値が参考になります。公式のデザイントークン(色や大きさなどの決まりを名前と値にしたもの)では、部品の上に重ねる色の膜(state layer)の不透明度を、ホバー 8%、フォーカス 10%、押している間 10%、ドラッグ中 16% にしています。無効の state layer の値は 38% です。
なお、CSS の :focus と :focus-visible は違います。:focus-visible は、ブラウザが「フォーカスの印を見せるべき」と判断したときだけ当たります。Chromium 153 で試したところ、キーボードの Tab キーで選んだボタンには当たり、マウスでクリックしたボタンには当たりませんでした。デザインでも「キーボードで選んだときの見た目」として、ホバーとは別に作っておくと、実装とずれません。
キーボードで選んだときに出る枠を、フォーカスの印(フォーカスリング)と呼びます。よくあるのが、ブラウザの初期の枠を消して、代わりに薄い影や淡い色を付けるやり方です。デザイン上はきれいに見えますが、地の色との差が小さいと、キーボードで操作する人には、今どこにいるのかが分かりません。
WCAG 2.2 の 2.4.7「フォーカスの可視化」(レベル AA)は、キーボードのフォーカスの印が見えることを求めています。1.4.11「非テキストのコントラスト」(AA)の解説では、フォーカスの印も、隣り合う色と 3:1 以上の差が必要な例として扱われています。さらに上の段階の 2.4.13「フォーカスの外観」(AAA)は、2px の太さの枠に相当する広さがあり、フォーカスの前と後で 3:1 以上の差があることを求めています。
OK の作例では、墨色の 2px の線を、ボタンから少し離して外側に引いています。ボタンの色に左右されず、白い地の上でははっきり見えます。

必須の項目が埋まるまで送信ボタンを薄い灰色にして押せなくする作りはよく見かけますが、押せない理由が書かれていないと、使う人はそこで止まってしまいます。
デジタル庁デザインシステムは、ボタンを無効にする設計を考えるときは「そもそも無効化する必要があるかを注意深く確認してください」としたうえで、送信ボタンは無効にせず、押されたときにエラーメッセージでやるべきことを案内するよう勧めています。どうしても無効にするなら、すぐ近くに、なぜ無効なのか、どうすれば押せるようになるのかを具体的に書く、としています。
無効の部品は、WCAG の 1.4.3 と 1.4.11 のコントラストの決まりから外れます。決まりの上では薄くしてもかまわない、ということですが、薄いほど読めなくなるのも事実です。デジタル庁デザインシステムは、背景との差を 3:1 以上にすると無効に見えないことがあり、3:1 未満だとそもそも見えない可能性がある、という矛盾を指摘しています。見た目の工夫だけで解決しにくいので、次の節の「無効を作らない」選び方が効いてきます。

押したあと、ボタンの文字をスピナー(くるくる回る印)に置き換えると、文字の分だけボタンの幅が縮み、周りの部品がずれます。文字が消えるので、何を待っているのかも分からなくなります。
OK の作例では、ボタンの幅を最初の幅のまま固定し、回る印を文字の左に足して、文字を「送信中…」に変えています。GitHub のデザインシステム Primer も、アイコンの付いたボタンでは、読み込み中に回る印がアイコンの位置だけを置き換え、ボタンの文字は見えたままにする、と説明しています。読み上げソフトを使う人には見た目の変化が伝わらないので、「送信しています」のような文を、読み上げられる場所(role="status" の要素など)に出すことも仕様に書いておきます。

枠の色を赤に変えるだけのエラー表示は、色の違いが分かりにくい人には伝わりません。WCAG の 1.4.1「色の用途」(A)は、色だけで情報を伝えないことを求めています。3.3.1「エラーの特定」(A)は、入力のエラーを自動で見つけたときに、その箇所を特定し、何が違うのかを文字で説明することを求めています。
OK の作例では、枠を赤くしたうえで、入力欄のすぐ下に印と文で「@ のあとが入っていません」と、何が違うのかを書いています。直し方まで一言で書けるなら、「例:[email protected]」のように添えるとさらに親切です。赤い色を使うこと自体は問題ありません。色に加えて、形と文字でも伝えるのがポイントです。

無効の扱いについて、次の4つのデザインシステムは、いずれも「なるべく使わない」としています。
disabledReason を検討するinactive を別に用意している理由のひとつは、HTML の disabled 属性を付けたボタンが、Tab キーで移動する順番から外れることです。W3C の APG(WAI-ARIA の作り方の手引き)のキーボード操作のページは、フォーカスできない無効の部品は、読み上げソフトを使う人に見つけてもらいにくい、と説明しています。見つけてもらう必要があるときは、aria-disabled="true" を使う方法があります。こちらはフォーカスできるまま「無効」と伝えますが、押されたときの動きを止めるのは作り手の仕事です。とはいえ、まず検討したいのは無効にしない作りです。
デザイナーが仕様に書くのは、「押されたらどうするか」です。たとえば、ボタンは押せるままにして、未入力のまま押されたら案内を出し、最初の未入力の欄へ移動する。この動きをコードにすると次のようになります(HTML と JavaScript。このコードのまま Chromium 153 で、Tab キーでボタンを選べること、押すと案内が出てメールアドレスの欄へ移動すること、入力後に押すと「送信しています」と出ることを確かめました。未入力だけを確かめる短い例で、メールアドレスの形式の確認と、欄ごとのエラー表示(aria-invalid と aria-describedby)は省いています)。
<form id="contact-form" novalidate>
<label for="email">メールアドレス(必須)</label>
<input id="email" name="email" type="email" required>
<button class="button" type="submit" aria-describedby="send-note">送信する</button>
<p id="send-note">必須の項目を入力すると送信できます</p>
<p id="form-status" role="status"></p>
</form>
<script>
const form = document.getElementById("contact-form");
const statusEl = document.getElementById("form-status");
form.addEventListener("submit", (e) => {
e.preventDefault();
const firstEmpty = [...form.querySelectorAll("[required]")]
.find((el) => !el.value.trim());
if (firstEmpty) {
statusEl.textContent = "必須の項目が入力されていません";
firstEmpty.focus();
return;
}
statusEl.textContent = "送信しています";
});
</script>
フォーカスリングは CSS の :focus-visible に書きます。白い地の上に置くボタンの例です。暗い面や写真の上にも置くなら、内側に白い線を足すなど、2色の線にしておくと安心です。
.button {
min-width: 10rem; /* 160px。読み込み中も幅を変えない */
}
.button:focus-visible {
outline: 2px solid #111112; /* 白い地との差 3:1 以上 */
outline-offset: 3px;
}
状態の一覧を Figma で並べたら、それとは別に、短い仕様を文字で書いて添えます。長い文書はいりません。部品ごとに、状態と「見た目」「動き」「伝え方」を並べるだけで、渡す相手が人でも AI でも、推測で埋める部分が減ります。Markdown(見出しや箇条書きを記号で書く書き方)で書いておけば、Figma の Specs にも、エンジニアのリポジトリの説明ファイルにも、AI への指示にも、そのまま貼れます。
## 送信ボタン(Button / Primary)
- 幅:最小 160px。読み込み中も幅を変えない
- hover:地を #2A2A2E に。パソコンだけ
- focus-visible:墨 #111112 の 2px の線、ボタンから 3px 外側。hover と別の見た目
- pressed:地を #3A3A3F に
- loading:文字を「送信中…」にし、左に回る印。role="status" に「送信しています」
- disabled:使わない。未入力のまま押されたら、エラーを出して最初の未入力の欄へ移動
- 例外:送信の二重押しを防ぐため、loading の間だけ押しても何もしない
## メールアドレスの入力欄(TextField)
- error:枠を赤 #D93025・2px にし、欄の下に印と文。文は「何が違うか」。書けるなら「どう直すか」も
- error の文は aria-describedby で欄と結ぶ
- 文の例:「@ のあとが入っていません。例:[email protected]」
コツは、数値と文言を具体的に書くことです。「分かりやすいフォーカス」ではなく「2px の線、3px 外側」と書いた部分ほど、実装でぶれません。
全部を一度にそろえなくても大丈夫です。まずは送信ボタンとメールアドレスの入力欄の2つから、一覧と仕様を書いてみてください。
MODS BASE では、デザインシステムの整備や、デザインと実装をつなぐ進め方のご相談も受けています。チームでの決まりづくりに迷ったときは、お気軽にお声がけください。
MODS BASE
MODS BASEは、長野県上伊那郡辰野町を拠点に、WEB 制作、WEB 集客の運用代行、UI/UX デザイン・プロダクト開発、ブランディング・グラフィック、AI 導入支援を手がけるデザイン・開発スタジオです。全国のお客様にオンラインで対応しています。
WEB 制作
何を伝え、どう動いてもらうか。UX の設計から考えるホームページ制作。
( Related )