PIN Pad — Secure Keypad HTML CSS JS Snippet

PIN Pad · Forms · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Masked filling dots
Four dots fill (and scale) as digits are entered, showing progress without ever revealing the PIN.
Auto-submit on length
Entering the fourth digit auto-validates after a short delay so the last dot fills before the check runs.
Shake-on-error
A wrong PIN plays a horizontal shake keyframe (re-triggered via forced reflow) — the universal "incorrect" cue.
Unlock success state
A correct PIN turns the dots and lock icon green, updates the title to "✓ Unlocked", and locks further input.
Backspace support
del removes the last digit and unfills its dot, on-screen (⌫) or via the Backspace key.
Full keyboard input
A keydown listener routes number keys and Backspace through the same pressKey/del as the pad.
Tactile keypad
A clean 3×4 grid with press scale/colour states gives satisfying physical feedback on tap.
Export-safe feedback
Colour states use transition and the shake uses a transform keyframe, so feedback renders identically across frameworks.

About this UI Snippet

PIN Pad — Filling Dots, Shake-on-Error & Unlock Success State

Screenshot of the PIN Pad snippet rendered live

A PIN pad is the focused, secure-feeling way to take a short numeric code — lock screens, payment confirmation, parental gates, 2FA backup. Unlike a free-text field, it shows masked dots that fill as you type, validates automatically at the right length, and gives unmistakable feedback: a shake for wrong, a colour shift for correct. This snippet implements all of that in plain HTML, CSS, and vanilla JavaScript, with mouse, touch, and physical-keyboard input.

Masked dots that fill

Four dots represent the PIN. pressKey appends a digit to the pin string (capped at four) and render fills the corresponding dots — filled dots scale up slightly and take the accent colour. The actual digits are never shown, only the count, which is the privacy expectation for a PIN. Backspace (del) removes the last digit and unfills its dot.

Auto-submit and validation

There is no submit button — entering the fourth digit auto-checks after a short 180ms delay (so the final dot visibly fills before validation). check compares the pin to the correct value: a match locks the pad and switches to a success state (green dots, green lock icon, "✓ Unlocked"); a mismatch triggers feedback and clears.

Shake-on-error

A wrong PIN plays a horizontal pp-shake keyframe on the whole card — the universally understood "nope" gesture from iOS and macOS lock screens. It's re-triggered each time with the remove-class / force-reflow / add-class pattern so it fires on every failed attempt, the hint updates to "Wrong PIN — try again", and the entry clears after the shake so the user can retry. A locked flag blocks further input once unlocked.

Three input methods

The on-screen keypad is a clean 3×4 grid (1–9, blank, 0, backspace) with tactile press states. A keydown listener mirrors it so physical number keys and Backspace work too — every path routes through the same pressKey/del, so they can't diverge. The success/colour states use transition on colour and background, and the shake uses a transform keyframe — both export cleanly.

In production the check would call your backend (never compare a real PIN client-side), and you'd rate-limit attempts. Pair this with an OTP input for emailed codes, a pattern lock for gesture entry, or an auth login card for full sign-in.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to trace the reflow trick or the auto-submit timing by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the shake animation on wrong entry needs the remove-class, read offsetWidth, re-add-class sequence to replay correctly, and why check runs on a short setTimeout after the fourth digit rather than immediately. The same assistant can help optimize it, for example checking whether the same pressKey and del logic could be shared more cleanly between the on-screen button clicks and the document keydown listener, or whether the locked flag correctly blocks every input path once the pad is unlocked. It's also useful for extending the effect: ask it to support a 6-digit PIN instead of 4, add a shuffled keypad layout to defeat shoulder-surfing, or wire the check function to a real backend call with rate-limiting instead of a hardcoded client-side comparison. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a numeric PIN entry pad in plain HTML, CSS, and vanilla JavaScript with masked progress dots, a shake-on-error animation, and a success state — no frameworks.

Requirements:
- A row of exactly four dot indicators that visually fill (change color and scale slightly) one at a time as digits are entered, without ever displaying the actual digits typed.
- A 3x4 on-screen keypad of buttons for digits 0 through 9 plus a delete/backspace button, where every button press appends or removes a digit from an in-memory PIN string capped at exactly 4 characters.
- The moment the fourth digit is entered, wait a short delay (so the final dot visibly finishes filling first) and then automatically compare the entered PIN against a stored correct value — there must be no explicit submit button.
- On a correct match, switch the whole card into a visually distinct success state (for example recoloring the dots, an icon, and the title text to a success color) and permanently disable further input until reset.
- On an incorrect match, replay a horizontal shake animation on the card every single time it fails (even on consecutive failures), using the standard remove-class, force a synchronous reflow by reading an element's offsetWidth, then re-add-class technique so the CSS animation restarts from frame zero instead of being skipped by the browser, then clear the entered PIN after the shake finishes so the user can retry.
- Mirror every on-screen keypad action with a document-level keydown listener so physical number keys and the Backspace key drive the exact same append/delete logic as the on-screen buttons, with no divergent code paths.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA dark PIN card appears with a lock icon, four empty dots, and a 3×4 numeric keypad.
  2. 2
    Enter the PINTap digits — each fills the next dot. Entering the fourth digit auto-validates after a brief beat.
  3. 3
    Try a wrong PINEnter anything but 1 9 3 7 — the card shakes, the hint says "Wrong PIN", and the dots clear so you can retry.
  4. 4
    Enter the correct PINType 1 9 3 7 — the dots and lock icon turn green and the title becomes "✓ Unlocked".
  5. 5
    Use backspaceTap ⌫ (or press Backspace) to remove the last digit before submitting.
  6. 6
    Type on your keyboardPhysical number keys and Backspace work too, routed through the same logic as the on-screen pad.

Real-world uses

Common Use Cases

Lock and unlock screens
App or kiosk lock screens where a quick PIN beats a password. Pair with a pattern lock as an alternative gesture.
Payment and transaction confirmation
Confirm a payment or transfer with a PIN; combine with a checkout payment form for the full flow.
2FA backup and verification
Numeric verification entry; for emailed/SMS codes use a longer OTP input instead.
Parental gates and kid modes
A simple barrier before sensitive settings or purchases in family apps.
POS and kiosk access
Staff PIN entry on shared point-of-sale or kiosk devices where a numeric pad suits touch screens.
Vault and secure sections
Gate a private area of an app; combine with an auth login card for account sign-in.

Got questions?

Frequently Asked Questions

Never compare the real PIN on the client. Send the entered PIN to your backend over HTTPS and verify it there (against a salted hash), returning success/failure. Keep the client-side check only for demos. Always rate-limit attempts server-side and lock the account or add a cooldown after several failures to prevent brute-forcing a 4-digit space.

Add two more .pp-dot elements and change the length checks from 4 to 6 in pressKey and the auto-submit condition. Everything else — fill rendering, shake, success — scales automatically since it's driven by pin.length and the number of dot elements.

The dots already mask digits. For stronger privacy, optionally shuffle the keypad layout each session (randomise the 0–9 positions) so observers can't infer the PIN from finger positions. For brute force, enforce server-side attempt limits and exponential backoff; the client locked flag is only a UX guard, not security.

The keys are real <button>s, so they're keyboard- and screen-reader operable, and a keydown handler adds physical number-key entry. Add an aria-label to each key, announce remaining digits and errors via an aria-live="polite" region (e.g. "3 of 4 entered", "incorrect PIN"), and ensure the success/error states are conveyed by text and icon, not colour alone.

In React, hold pin and a status ('idle' | 'ok' | 'error') in useState; pressKey appends and triggers validation in an effect when length hits 4, and the shake is a class keyed off status. In Vue, use refs and a watcher on pin.length. In Angular, track pin on the component. The dot-fill and shake CSS port unchanged.