A passcode login form built with pure HTML and CSS that allows users to enter a numeric or short code for authentication. It enhances secure input clarity and minimal credential interaction in modern login flows.
Usage
Use this component when interfaces require PIN or code based authentication, such as mobile apps, secure dashboards, OTP verification screens, or device unlock interfaces.
Implementation
The layout is implemented using segmented input fields or grouped code inputs, styled with CSS for spacing, focus states, and alignment. Responsive rules ensure proper usability across screen sizes.
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Passcode Login</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@700;800&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<div class="card">
<div class="icon-wrap">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
</svg>
</div>
<h1>Enter Passcode</h1>
<p class="subtitle">4-digit secure access code</p>
<div class="dots">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
<div class="inputs">
<input class="digit-input" type="password" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="·" autofocus autocomplete="off"/>
<input class="digit-input" type="password" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="·" autocomplete="off"/>
<input class="digit-input" type="password" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="·" autocomplete="off"/>
<input class="digit-input" type="password" inputmode="numeric" maxlength="1" pattern="[0-9]" placeholder="·" autocomplete="off"/>
</div>
<div class="numpad">
<div class="key">1</div>
<div class="key">2</div>
<div class="key">3</div>
<div class="key">4</div>
<div class="key">5</div>
<div class="key">6</div>
<div class="key">7</div>
<div class="key">8</div>
<div class="key">9</div>
<div class="key key-zero">0</div>
<div class="key key-del">DEL</div>
</div>
<button class="btn-unlock" type="button">Unlock</button>
<div class="footer-links">
<span>Forgot passcode?</span>
<a href="#">Reset access</a>
</div>
</div>
</body>
</html>CSS
*::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #0a0a0f;
--card: #111118;
--border: #2a2a3a;
--accent: #7c6aff;
--accent-glow: rgba(124, 106, 255, 0.35);
--accent-soft: rgba(124, 106, 255, 0.12);
--text: #e8e6ff;
--muted: #6b6880;
--dot-empty: #1e1e2e;
--dot-filled: #7c6aff;
--red: #ff6a6a;
}
body {
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: 'DM Mono', monospace;
overflow: hidden;
position: relative;
}
body::before,
body::after {
content: '';
position: fixed;
border-radius: 50%;
filter: blur(120px);
pointer-events: none;
opacity: 0.18;
}
body::before {
width: 500px;
height: 500px;
background: #7c6aff;
top: -100px;
right: -100px;
}
body::after {
width: 400px;
height: 400px;
background: #3b2fff;
bottom: -80px;
left: -80px;
}
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 28px;
padding: 48px 44px 44px;
width: 360px;
position: relative;
box-shadow:
0 0 0 1px rgba(124, 106, 255, 0.08),
0 32px 80px rgba(0, 0, 0, 0.6),
inset 0 1px 0 rgba(255, 255, 255, 0.04);
animation: cardIn 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes cardIn {
from { opacity: 0; transform: translateY(24px) scale(0.97); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 24px;
right: 24px;
height: 1px;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
border-radius: 1px;
}
.icon-wrap {
width: 56px;
height: 56px;
background: var(--accent-soft);
border: 1px solid rgba(124, 106, 255, 0.3);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
animation: iconPop 0.5s 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes iconPop {
from { opacity: 0; transform: scale(0.6); }
to { opacity: 1; transform: scale(1); }
}
.icon-wrap svg {
width: 26px;
height: 26px;
color: var(--accent);
}
h1 {
font-family: 'Syne', sans-serif;
font-weight: 800;
font-size: 1.55rem;
color: var(--text);
text-align: center;
letter-spacing: -0.02em;
animation: fadeUp 0.5s 0.3s both;
}
.subtitle {
font-size: 0.78rem;
color: var(--muted);
text-align: center;
margin-top: 6px;
letter-spacing: 0.04em;
animation: fadeUp 0.5s 0.35s both;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.dots {
display: flex;
justify-content: center;
gap: 14px;
margin: 32px 0 28px;
animation: fadeUp 0.5s 0.4s both;
}
.dot {
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--dot-empty);
border: 1.5px solid var(--border);
transition: background 0.2s, box-shadow 0.2s, transform 0.2s;
}
.inputs {
display: flex;
gap: 10px;
justify-content: center;
margin-bottom: 28px;
animation: fadeUp 0.5s 0.45s both;
}
.digit-input {
width: 56px;
height: 64px;
background: rgba(255, 255, 255, 0.03);
border: 1.5px solid var(--border);
border-radius: 14px;
color: var(--text);
font-family: 'Syne', sans-serif;
font-size: 1.6rem;
font-weight: 700;
text-align: center;
outline: none;
caret-color: var(--accent);
transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
-webkit-text-security: disc;
text-security: disc;
appearance: none;
-webkit-appearance: none;
}
.digit-input:focus {
border-color: var(--accent);
background: var(--accent-soft);
box-shadow: 0 0 0 3px var(--accent-glow);
}
.digit-input:valid:not(:placeholder-shown) {
border-color: rgba(124, 106, 255, 0.5);
}
.numpad {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 20px;
animation: fadeUp 0.5s 0.5s both;
}
.key {
background: rgba(255, 255, 255, 0.03);
border: 1.5px solid var(--border);
border-radius: 14px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-family: 'Syne', sans-serif;
font-size: 1.25rem;
font-weight: 700;
color: var(--text);
text-decoration: none;
user-select: none;
transition: background 0.15s, border-color 0.15s, transform 0.1s, box-shadow 0.15s;
position: relative;
overflow: hidden;
}
.key:hover {
background: rgba(124, 106, 255, 0.1);
border-color: rgba(124, 106, 255, 0.4);
}
.key:active {
transform: scale(0.93);
background: var(--accent-soft);
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-glow);
}
.key.key-zero {
grid-column: 2;
}
.key.key-del {
grid-column: 3;
grid-row: 4;
font-size: 0.7rem;
font-family: 'DM Mono', monospace;
font-weight: 400;
letter-spacing: 0.06em;
color: var(--muted);
}
.key.key-del:hover {
color: var(--red);
border-color: rgba(255, 106, 106, 0.4);
}
.key.key-del:active {
background: rgba(255, 106, 106, 0.1);
}
.btn-unlock {
display: block;
width: 100%;
height: 54px;
background: var(--accent);
border: none;
border-radius: 14px;
color: #fff;
font-family: 'Syne', sans-serif;
font-size: 0.95rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
position: relative;
overflow: hidden;
transition: background 0.2s, transform 0.15s, box-shadow 0.2s;
box-shadow: 0 4px 24px rgba(124, 106, 255, 0.4);
animation: fadeUp 0.5s 0.55s both;
}
.btn-unlock::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent);
transition: left 0.4s;
}
.btn-unlock:hover {
background: #9b8dff;
box-shadow: 0 6px 32px rgba(124, 106, 255, 0.55);
}
.btn-unlock:hover::before {
left: 100%;
}
.btn-unlock:active {
transform: scale(0.97);
box-shadow: 0 2px 12px rgba(124, 106, 255, 0.3);
}
.footer-links {
display: flex;
justify-content: center;
gap: 6px;
margin-top: 20px;
font-size: 0.72rem;
color: var(--muted);
animation: fadeUp 0.5s 0.6s both;
}
.footer-links a {
color: var(--accent);
text-decoration: none;
transition: color 0.2s;
}
.footer-links a:hover {
color: #a593ff;
}Notes
- Built with pure HTML and CSS
- No JavaScript required
- Uses segmented code input styling
- Enhances secure input visibility and clarity
- Fully responsive across breakpoints
- Suitable for OTP and PIN authentication
- Easy to customize input spacing and size
Preview styles shown. Production customization recommended.
Browse More UI Components
Explore hundreds of reusable HTML & CSS UI components built for modern web projects.
Discover buttons, cards, loaders, animations, layouts, and more all with live previews and clean, copy-paste code.
