ワンタイムコード入力One-time code input
SMS やメールで届いた6桁ほどの数字を入れる欄。1桁ずつ四角い枠が並んだ見た目にすることが多い。ログインや登録の本人確認で使う。
表示例
SMS で届いた6桁の数字
別名・英語表記
- 認証コード入力
- OTP入力
- 確認コード入力
- ワンタイムパスワード入力
- 2段階認証の入力欄
- SMS認証コードの欄
- One-time code input
実装例
<div class="x-otp-input">
<label class="x-otp-input__l" for="x-otp-input-i">確認コード</label>
<p class="x-otp-input__h" id="x-otp-input-h">SMS で届いた6桁の数字</p>
<!-- 入力欄は1つ。6つの枠は後ろに敷いた飾りで、読み上げからは外す。
欄を6つに分けないので、貼り付けと自動入力がそのまま全桁に入る -->
<div class="x-otp-input__f">
<span class="x-otp-input__bs" aria-hidden="true">
<span class="x-otp-input__b"></span><span class="x-otp-input__b"></span><span class="x-otp-input__b"></span>
<span class="x-otp-input__b"></span><span class="x-otp-input__b"></span><span class="x-otp-input__b"></span>
</span>
<input class="x-otp-input__i" id="x-otp-input-i" type="text" name="code" value="4829"
inputmode="numeric" autocomplete="one-time-code" spellcheck="false"
aria-describedby="x-otp-input-h">
</div>
<button class="x-otp-input__re" type="button">コードを再送する</button>
</div>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-otp-input,
.x-otp-input *:not([class*="fa-"]),
.x-otp-input *:not([class*="fa-"])::before,
.x-otp-input *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-otp-input {
width: 260px;
color: #1b2028;
}
.x-otp-input__l {
display: block;
font-size: 12px;
font-weight: 700;
}
.x-otp-input__h {
margin: 2px 0 8px;
color: #5b6472;
font-size: 11px;
}
/* 枠と入力欄は同じ等幅の書体・大きさにし、幅を ch で測る。
書体が何に落ちても、1文字の幅と枠の幅が同じ単位で決まるので桁と枠がずれない */
.x-otp-input .x-otp-input__f,
.x-otp-input .x-otp-input__b,
.x-otp-input .x-otp-input__i {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
font-size: 22px;
}
.x-otp-input__f {
position: relative;
width: 18ch;
height: 46px;
overflow: hidden;
border-radius: 6px;
}
.x-otp-input__bs {
display: flex;
height: 100%;
}
.x-otp-input__b {
width: calc(3ch - 8px);
height: 100%;
margin: 0 4px;
border: 1px solid #7f8a99;
border-radius: 6px;
background: #fff;
}
/* 1桁ぶんの幅 3ch = 文字 1ch + 字間 2ch。左に 1ch 空けて文字を枠の中央に置く。
6桁目のあとのキャレットで横にスクロールしないよう、欄は1枠ぶん広くして外側で切る */
.x-otp-input__i {
position: absolute;
top: 0;
left: 0;
width: 21ch;
height: 100%;
padding: 0 0 0 1ch;
border: 0;
outline: none;
background: none;
color: #1b2028;
letter-spacing: 2ch;
font-variant-numeric: tabular-nums;
}
.x-otp-input__f:focus-within {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
.x-otp-input__f:focus-within .x-otp-input__b {
border-color: #1d4ed8;
}
.x-otp-input__re {
margin-top: 10px;
padding: 4px 0;
border: 0;
background: none;
color: #1d4ed8;
font-size: 12px;
text-decoration: underline;
cursor: pointer;
}
.x-otp-input__re:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
AIへの指示文
「ワンタイムコード入力(One-time code input)」を実装してください。
## これは何か
SMS やメールで届いた6桁ほどの数字を入れる欄。1桁ずつ四角い枠が並んだ見た目にすることが多い。ログインや登録の本人確認で使う。
## 使いどころ
- SMS・メール・認証アプリで届いた確認コードを入れてもらうとき
- 2段階認証のログインや、電話番号・メールアドレスの確認
## 実装の要件
- label + 補足文 + input[type=text][inputmode=numeric][autocomplete=one-time-code][maxlength=6]。後ろに aria-hidden の枠6つを敷く
- 枠と入力欄を同じ等幅の書体にし、幅と字間を ch で決めると、書体が変わっても桁と枠がずれない
- maxlength="6" を付けると、空白やハイフンを含むコードを貼ったときにブラウザが末尾を切り詰める。paste イベントで数字だけ取り出してから入れるか、maxlength を付けずに送信時に桁数を確かめる
- 枠を6つの input に分けると、フォーカスの自動移動・Backspace での戻り・貼り付けの振り分け・自動入力をすべて自前で作ることになり、どれかが壊れやすい。入力欄は1つにして、枠は見た目だけで描くほうが確実(GOV.UK Design System も1つの欄を使う)
- 欄を分けた場合、枠ごとに「6桁中1桁目」のような名前が要り、読み上げでは欄が6つ続く
- type="number" にしない。先頭の 0 が消え、スクロールや上下キーで値が増減する。type="text" に inputmode="numeric" を付ける
- コピーしたときに混ざる空白やハイフンは受け止めて取り除く。桁数が合わないときは「あと2桁足りません」のように何が違うかを伝える
- 有効期限と再送の手段をそばに置く。届かない人がそこで行き詰まる
## アクセシビリティの要件
- 欄全体に見える label を1つ付ける(「確認コード」)。何桁で、どこに届いたかは補足文にして aria-describedby で結ぶ
- autocomplete="one-time-code" を付ける。Safari などは SMS やメールで届いたコードを入力候補に出し、1回で全桁が入る
- 枠の飾りは aria-hidden="true" にして読み上げから外す。読むのは input の値だけにする
- 6桁そろったら自動で送信するなら、その動きを入力の前に文言で予告する(WCAG 3.2.2)
- WCAG 2.2 の 3.3.8 アクセシブルな認証(最低限)に沿い、貼り付けと自動入力を妨げない
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<div class="x-otp-input">
<label class="x-otp-input__l" for="x-otp-input-i">確認コード</label>
<p class="x-otp-input__h" id="x-otp-input-h">SMS で届いた6桁の数字</p>
<!-- 入力欄は1つ。6つの枠は後ろに敷いた飾りで、読み上げからは外す。
欄を6つに分けないので、貼り付けと自動入力がそのまま全桁に入る -->
<div class="x-otp-input__f">
<span class="x-otp-input__bs" aria-hidden="true">
<span class="x-otp-input__b"></span><span class="x-otp-input__b"></span><span class="x-otp-input__b"></span>
<span class="x-otp-input__b"></span><span class="x-otp-input__b"></span><span class="x-otp-input__b"></span>
</span>
<input class="x-otp-input__i" id="x-otp-input-i" type="text" name="code" value="4829"
inputmode="numeric" autocomplete="one-time-code" spellcheck="false"
aria-describedby="x-otp-input-h">
</div>
<button class="x-otp-input__re" type="button">コードを再送する</button>
</div>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-otp-input,
.x-otp-input *:not([class*="fa-"]),
.x-otp-input *:not([class*="fa-"])::before,
.x-otp-input *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
.x-otp-input {
width: 260px;
color: #1b2028;
}
.x-otp-input__l {
display: block;
font-size: 12px;
font-weight: 700;
}
.x-otp-input__h {
margin: 2px 0 8px;
color: #5b6472;
font-size: 11px;
}
/* 枠と入力欄は同じ等幅の書体・大きさにし、幅を ch で測る。
書体が何に落ちても、1文字の幅と枠の幅が同じ単位で決まるので桁と枠がずれない */
.x-otp-input .x-otp-input__f,
.x-otp-input .x-otp-input__b,
.x-otp-input .x-otp-input__i {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
font-size: 22px;
}
.x-otp-input__f {
position: relative;
width: 18ch;
height: 46px;
overflow: hidden;
border-radius: 6px;
}
.x-otp-input__bs {
display: flex;
height: 100%;
}
.x-otp-input__b {
width: calc(3ch - 8px);
height: 100%;
margin: 0 4px;
border: 1px solid #7f8a99;
border-radius: 6px;
background: #fff;
}
/* 1桁ぶんの幅 3ch = 文字 1ch + 字間 2ch。左に 1ch 空けて文字を枠の中央に置く。
6桁目のあとのキャレットで横にスクロールしないよう、欄は1枠ぶん広くして外側で切る */
.x-otp-input__i {
position: absolute;
top: 0;
left: 0;
width: 21ch;
height: 100%;
padding: 0 0 0 1ch;
border: 0;
outline: none;
background: none;
color: #1b2028;
letter-spacing: 2ch;
font-variant-numeric: tabular-nums;
}
.x-otp-input__f:focus-within {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
.x-otp-input__f:focus-within .x-otp-input__b {
border-color: #1d4ed8;
}
.x-otp-input__re {
margin-top: 10px;
padding: 4px 0;
border: 0;
background: none;
color: #1d4ed8;
font-size: 12px;
text-decoration: underline;
cursor: pointer;
}
.x-otp-input__re:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- SMS・メール・認証アプリで届いた確認コードを入れてもらうとき
- 2段階認証のログインや、電話番号・メールアドレスの確認
注意点
- 枠を6つの input に分けると、フォーカスの自動移動・Backspace での戻り・貼り付けの振り分け・自動入力をすべて自前で作ることになり、どれかが壊れやすい。入力欄は1つにして、枠は見た目だけで描くほうが確実(GOV.UK Design System も1つの欄を使う)
- 欄を分けた場合、枠ごとに「6桁中1桁目」のような名前が要り、読み上げでは欄が6つ続く
- type="number" にしない。先頭の 0 が消え、スクロールや上下キーで値が増減する。type="text" に inputmode="numeric" を付ける
- コピーしたときに混ざる空白やハイフンは受け止めて取り除く。桁数が合わないときは「あと2桁足りません」のように何が違うかを伝える
- 有効期限と再送の手段をそばに置く。届かない人がそこで行き詰まる
アクセシビリティの要点
- 欄全体に見える label を1つ付ける(「確認コード」)。何桁で、どこに届いたかは補足文にして aria-describedby で結ぶ
- autocomplete="one-time-code" を付ける。Safari などは SMS やメールで届いたコードを入力候補に出し、1回で全桁が入る
- 枠の飾りは aria-hidden="true" にして読み上げから外す。読むのは input の値だけにする
- 6桁そろったら自動で送信するなら、その動きを入力の前に文言で予告する(WCAG 3.2.2)
- WCAG 2.2 の 3.3.8 アクセシブルな認証(最低限)に沿い、貼り付けと自動入力を妨げない