Design Lounge
Install

Nº 322 of 520

One-time code

Six digits from a letter. Three are filled, so the line reads 3 of 6. The check accepts only 184219 and otherwise names that code.

Preview

Open full size
1280 × 800 · 4.0 KB · 90 lines

Use it

Free to use · credit appreciated

1 Once, in your project

npx skills add SusanAcharya/Design-Lounge

2 Then ask

Using the Design Lounge skill, build "One-time code" (piece otp-code) in my stack. Match its brief; restyle it onto my product's theme.

No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.

Brief

177 lines

Build brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them. When a kit is locked, each cell uses --control width at least 40px and the family’s radius. A password is password-field. A link in an email is auth-magic-link-sent.

What it is

Six cells for a code from a letter. The label is “Code from the letter.” The first three cells are 1, 8, and 4. The last three are empty. The status line reads “3 of 6.” A digit moves focus to the next cell. Backspace on an empty cell moves to the previous. Paste of digits fills from the start. Check the code stays disabled until six digits. The code that passes is 184219. Any other six digits turns the borders danger and shows “The code is 184219.” A pass writes “Code accepted.” and disables the button. Check is the one solid primary.

Reference behaviour

  1. Cells 1, 8, 4 are filled. Cells 4, 5, and 6 are empty. The status line is “3 of 6.” Check is disabled.
  2. Typing a digit keeps one character, strips anything that is not a digit, and focuses the next cell.
  3. Backspace on an empty cell focuses the previous cell.
  4. Pasting 184219 fills all six and enables Check.
  5. Check with 184219 hides the error, writes “Code accepted.”, and disables the button.
  6. Check with any other six digits shows the error and sets aria-invalid on the cells.
  7. There is no animation. Focus ring is 2px --focus, offset 2px on a cell and offset 3px on the button.

Structure

padding 48px 64px
Sign in                      12px
Code from the letter         12px label
[ 1 ] [ 8 ] [ 4 ] [ ] [ ] [ ]    each 40 by 48
[ Check the code ]           40px, disabled until six
3 of 6.                      or the error, or Code accepted.
  • The six inputs are a group labelled by the sentence.
  • Each input has its own name: Digit 1 through Digit 6.
  • The first input may use autocomplete="one-time-code".

Tokens

:root {
  --bg: #f6f4ef;
  --surface: #ffffff;
  --ink: #161513;
  --ink-2: #5a554c;
  --line-strong: #cfc6b8;
  --primary: #1f4d3a;
  --primary-ink: #fffdf8;
  --danger: #9b2c2c;
  --focus: #1f4d3a;
  --sans: "IBM Plex Sans", system-ui, sans-serif;
}

Cell radius is 2px. The family replaces it. Do not join the six cells into one rounded track unless the family is already a pill, and even then keep a gap of 8px so they stay six targets.

Typography

RoleFamilySizeWeightColour
Wheresans12px500--ink-2
Labelsans12px500--ink-2
Digitsans20px500--ink
Buttonsans13px500--primary-ink
Statussans14px400--ink
Errorsans14px400--danger

Digits are centered and tabular. The where-line letter-spacing is 0.04em.

Motion

None. Focus moves in one frame. Reduced motion has nothing to remove. Do not flip the cells.

ThingTriggerWhat changes
Digitinputthe cell, focus moves forward
Backspaceempty cellfocus moves back
Pastedigitsthe cells from the start
Checksix digitsaccepted, or the error

States

  • Cell resting: 40px wide, 48px tall, radius 2px, 1px --line-strong, surface, text centered.
  • Cell invalid: border --danger, only after a failed check.
  • Check disabled: opacity 0.4, until six digits, and again after a pass.
  • Check enabled: solid --primary, text --primary-ink.
  • Status: “n of 6.” while the code is short. Empty while a result is showing.
  • Error: “The code is 184219.” This demo names the code so the piece can be tested. A product says the code was not the one in the letter, and does not print the real code.
  • Focus-visible: 2px outline, offset 2px on a cell.

Accessibility

  • The group is labelled “Code from the letter.”
  • Each cell has its own name, Digit 1 through Digit 6.
  • inputmode="numeric" opens a digit keyboard.
  • A failed check sets aria-invalid on the cells. The error is text under them.
  • Check is disabled until the code is complete, so an empty submit cannot happen.
  • Hit targets are at least 40 by 48. On a phone, at least 44px.
  • Contrast of the digits on white and the error on the paper clears 4.5.
  • Do not use one input and pretend it is six. The caret and the paste behaviour depend on the cells.

Responsive rules

  • At 1280 the row is six cells with an 8px gap, padding 48px 64px.
  • Below 640 the cells stay 40px and the gap may drop to 6px. They stay on one row. Do not wrap a code.
  • On a phone the cell height is at least 44px.
  • Do not replace the cells with a password field.

Acceptance checklist

  • Six cells. The first three are 1, 8, 4. The status line is “3 of 6.”
  • Check is disabled until six digits.
  • A digit focuses the next cell. Backspace on an empty cell focuses the previous.
  • Paste fills from the first cell.
  • 184219 is accepted. The status line is “Code accepted.”
  • Another six-digit code shows “The code is 184219.” and danger borders.
  • Cells are 40 by 48, radius 2px, digit 20px.
  • Check is the only solid button.
  • Focus ring is 2px.
  • There is no animation.

Implementation notes

Keep one character. Move focus yourself.

box.value = box.value.replace(/\D/g, '').slice(-1);
if (box.value && i < 5) boxes[i + 1].focus();

Paste reads text, keeps digits, and fills from the first cell.

Common mistakes:

  • One long input. The person cannot see which digit is missing.
  • Accepting letters.
  • Enabling Check at three digits.
  • Printing the real code in a product error. This demo prints 184219 so the test is visible. A product does not.
  • A second primary beside Check.
  • Cells shorter than 40px.
  • A countdown that resends on its own. A resend is a separate outline button, not in this piece.

Where it sits in a product:

  1. Use it after a letter or a message has sent a code. The sent state is auth-magic-link-sent when the method is a link.
  2. A password the person chose is password-field.
  3. Check is the one primary.
  4. Cell size and radius follow the family, and the cell stays at least 40px.
  5. When a theme is locked, Check uses --primary and --primary-ink.
  6. Six digits is this letter. A product of four digits uses four cells and says so in the count.
  7. The where-line Sign in is the screen name.
  8. Do not also ask for the password on the same view unless the product truly needs both, and then the password is the other control.
  9. The opening digits 1, 8, 4 match the start of 184219 so a tester can finish the code.
  10. Keep the credit line on the token block.

Rebuild order:

  1. Set the paper and IBM Plex Sans.
  2. Place the label and six cells, with 1, 8, 4 filled.
  3. Place Check, disabled, and the status line.
  4. Wire input, backspace, and paste.
  5. Wire Check against 184219.
  6. Map the button and the radius onto the kit.

Copy you keep:

  1. Sign in.
  2. Code from the letter.
  3. Digit 1 through Digit 6.
  4. 3 of 6. as the start.
  5. Check the code.
  6. The code is 184219.
  7. Code accepted.
  8. The passing code 184219.

Details

Palette
Type
IBM Plex Sans
Motion
Static
Build
One session
Designed bySusan AcharyaKathmandu

Studied

Igloo

A product with one job. Do not invent a homepage they did not ship.

All sources