パスワード表示切替
目のアイコンをクリックするとパスワードの表示・非表示を切り替えられる。入力ミスを確認しやすい。
プレビュー
操作実際に入力して試せます。
仕様書
- 向く場面
- ログイン・登録フォームのパスワード欄
- スマホで入力ミスを確認させたい認証画面
- ユーザー自身の操作でだけ一時的に表示したい欄
- 避ける場面
- 共有端末など覗き見が前提の画面で既定表示にする
- 元々見える確認コード(type=text にする)
- 構成要素
- ラベル
- パスワード入力欄
- 目のトグルボタン
- ヒント
- 持っている状態
- 通常
- ホバー
- フォーカス
- キーボード
Tab で入力欄→目のボタン、Enter/Space で表示・非表示を切替。フォーカスはボタンに残る
- 支援技術
button type=button に aria-pressed(表示中=true)と aria-label(「表示」⇔「隠す」)を切替。svg は aria-hidden。:focus-visible でリング。autocomplete=current-password でパスワード管理と連携
- 動き
アイコン色・枠色 200ms(ホバー/フォーカス)。reduced-motion で無効
デザインの要点
- 状態は input の type 属性そのものに持つ。JS の変数を別に持つと、二重管理でずれる
- ボタンは type=button を明示する。フォームの中で省くと submit になり、押した瞬間に送信される
- aria-label は「表示」⇔「隠す」で次に起きることを書く。aria-pressed は「いま表示中か」を伝える。両方要る
- 既定は非表示のまま。覗き見のリスクは利用者の任意操作でだけ引き受けさせる
使いどころ
ログインフォームやアカウント作成フォームのように、ユーザーが自分で入力した文字列を確認したい場面に向く。特にモバイルでの入力ミスは訂正コストが高いため、目のアイコンで表示に切り替えられる本実装は入力の信頼性を上げる効果が大きい。逆に、他人の画面を覗き見される可能性が高い共有端末や公共の場での利用が前提のプロダクトでは、デフォルトで表示に倒す運用は避け、あくまでユーザー自身の任意操作(このボタン)でのみ一時的に表示させる今の設計を保つべきである。また、確認コードやワンタイムパスワードのように元々見える前提の入力欄には表示切替自体が不要で、typeを最初からtextにしておく方がボタンを1つ減らせて簡潔になる。パスワード生成・強度チェックの機能とは独立した部品なので、それらと組み合わせる場合は別コンポーネントとして追加する設計になる。
仕組み
input要素のtype属性を"password"と"text"の間で直接書き換えるだけの実装で、状態はDOMのtype属性そのものが単一の情報源になっている(専用のJS変数を持たない)。ボタンクリックのたびにpwInput.type === "password"を判定し、目の開いたアイコン(eye-on)と閉じたアイコン(eye-off)をstyle.displayの"none"切り替えで入れ替え、同時にaria-labelを"パスワードを表示"⇔"パスワードを隠す"へ書き換える。2つのsvgアイコンを両方DOMに置いておき表示/非表示を切り替える方式は、アイコンを1つだけ用意してpathを書き換えるより実装が単純だが、常に2つ分のsvgノードがDOMに存在し続けるトレードオフがある。inputにはautocomplete="current-password"が指定されており、ブラウザのパスワードマネージャーとの連携(候補表示・自動入力)を妨げない配慮がされている。
実装の注意
type属性を書き換える実装は、ブラウザによってはtype変更時にカーソル位置や選択範囲がリセットされることがある(Safari系で報告例がある挙動)。入力途中の文字列の中間でこのボタンを押すと、カーソルが末尾に飛んで続きを打ちにくくなる場合があるため、厳密に対応するならselectionStart/selectionEndを変更前に保存し、type変更後に再設定する処理を足すとよい。次に、type="text"に切り替えている間はブラウザの自動入力候補や画面録画・肩越しの覗き見に対して無防備になるため、フォームを離れる(blur)タイミングやモーダルを閉じるタイミングでtype="password"へ戻す保険を入れる実装も検討に値する。現状はクリックし続けない限り表示のままなので、確認が終わったら手動で戻す前提のUIになっている点は利用者への説明(ヒント文言)でも補うとよい。
アクセシビリティ
ボタンのaria-labelがクリックのたびに"パスワードを表示"⇔"パスワードを隠す"へ動的に書き換えられており、スクリーンリーダー利用者にも次に押すとどうなるかが正しく伝わる設計になっている。あわせて aria-pressed を true/false で切り替えているので、「いま表示中か」という現在の状態も読み上げに乗る(ラベルは次の操作、pressed は現在の状態という役割分担)。2つの svg には aria-hidden="true" を付けて装飾として除外している。ボタンはbutton要素にtype="button"を明示しているため、フォーム内でsubmitとして誤って扱われることもない(button要素はtype省略時にsubmit扱いになる仕様への配慮が既にされている)。focus-visibleに対するoutlineも用意されており、キーボード操作時の可視性は確保できている。切替直後にボタンからフォーカスを外さない実装なので、aria-pressed の変化はその場で読み上げられる。
調整して使う
調整しやすいのは3つ。まずアイコンの出し分け方式で、現状は2つのsvgをDOMに並べてdisplayで切り替えているが、1つのsvgのd属性(パス)をJSで書き換える方式に変えればDOMノード数を減らせる。次に自動的にpasswordへ戻すタイミングで、一定時間(例えば10秒)経過後に自動で非表示へ戻すタイマーを足すと、表示したまま画面を離れてしまうケースの安全性が上がる(ただしタイマー中に読み終わらないと再度押す手間が増えるトレードオフがある)。最後に、確認用パスワード欄が並ぶ登録フォームでは、2つの入力欄それぞれに個別のトグルを付けるか、1つの操作で両方まとめて表示/非表示にするかという設計判断が生じる。個別トグルの方が誤操作は少ないが、まとめて切り替えられる方が確認の手間は減るため、フォームの複雑さで選ぶとよい。
コード(コピーして使えます)
<div class="pw-wrap">
<label class="pw-label" for="pw-input">パスワード</label>
<div class="pw-field">
<input
id="pw-input"
class="pw-input"
type="password"
value="MySecret123"
autocomplete="current-password"
/>
<button class="pw-toggle" id="pw-btn" type="button" aria-label="パスワードを表示" aria-pressed="false">
<svg class="eye-icon" id="eye-on" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<svg class="eye-icon" id="eye-off" style="display:none" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
</button>
</div>
<p class="pw-hint">ボタンで表示/非表示を切替</p>
</div>