UI Components Easy
Register Page
Sign-up page with name, email, password, and confirm password fields, real-time strength meter, terms checkbox, and validation. No dependencies.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
/* Reuse base from login-page, extend for register */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #0f1117;
--surface: #16181f;
--surface2: #1e2130;
--border: #2a2d3a;
--text: #e2e8f0;
--text-muted: #64748b;
--accent: #818cf8;
--accent-hover: #a5b4fc;
--red: #f87171;
--green: #34d399;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
}
.auth-layout {
display: grid;
grid-template-columns: 1fr 1fr;
min-height: 100vh;
}
.auth-left {
background: linear-gradient(160deg, #0f160e 0%, #0f1117 50%, #0e1020 100%);
border-right: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
padding: 48px;
}
.auth-left-inner {
max-width: 360px;
}
.brand {
font-size: 1.4rem;
font-weight: 800;
color: var(--accent);
text-decoration: none;
display: block;
margin-bottom: 40px;
}
.feature-list {
list-style: none;
display: flex;
flex-direction: column;
gap: 18px;
}
.feature-item {
display: flex;
align-items: center;
gap: 14px;
font-size: 0.9rem;
color: var(--text-muted);
}
.feature-icon {
font-size: 1.2rem;
}
.auth-right {
display: flex;
align-items: center;
justify-content: center;
padding: 32px;
background: var(--bg);
overflow-y: auto;
}
.auth-card {
width: 100%;
max-width: 420px;
}
.auth-header {
margin-bottom: 24px;
}
.auth-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 6px;
}
.auth-subtitle {
font-size: 0.875rem;
color: var(--text-muted);
}
.oauth-group {
margin-bottom: 18px;
}
.oauth-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
padding: 10px 16px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 9px;
color: var(--text);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
font-family: inherit;
transition: background .15s;
}
.oauth-btn:hover {
background: var(--surface2);
}
.auth-divider {
position: relative;
text-align: center;
margin: 18px 0;
}
.auth-divider::before,
.auth-divider::after {
content: "";
position: absolute;
top: 50%;
width: calc(50% - 18px);
height: 1px;
background: var(--border);
}
.auth-divider::before {
left: 0;
}
.auth-divider::after {
right: 0;
}
.auth-divider span {
font-size: 0.75rem;
color: var(--text-muted);
background: var(--bg);
padding: 0 8px;
}
.auth-form {
display: flex;
flex-direction: column;
gap: 16px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.field {
display: flex;
flex-direction: column;
gap: 5px;
}
.field-label {
font-size: 0.82rem;
font-weight: 600;
color: var(--text);
}
.field-input-wrap {
position: relative;
}
.field-input {
width: 100%;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 9px;
color: var(--text);
font-size: 0.875rem;
padding: 10px 14px;
outline: none;
font-family: inherit;
transition: border-color .15s;
}
.field-input::placeholder {
color: var(--text-muted);
}
.field-input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.1);
}
.field-input.invalid {
border-color: var(--red);
}
.field-input-wrap .field-input {
padding-right: 40px;
}
.pwd-toggle {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
padding: 2px;
transition: color .15s;
}
.pwd-toggle:hover {
color: var(--text);
}
.field-error {
font-size: 0.78rem;
color: var(--red);
}
/* Strength meter */
.strength-meter {
display: flex;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.strength-bar-wrap {
flex: 1;
height: 4px;
background: var(--surface2);
border-radius: 2px;
overflow: hidden;
}
.strength-bar {
height: 100%;
width: 0;
border-radius: 2px;
transition: width .3s, background .3s;
}
.strength-bar.s1 {
background: var(--red);
width: 25%;
}
.strength-bar.s2 {
background: #fb923c;
width: 50%;
}
.strength-bar.s3 {
background: #facc15;
width: 75%;
}
.strength-bar.s4 {
background: var(--green);
width: 100%;
}
.strength-label {
font-size: 0.72rem;
color: var(--text-muted);
white-space: nowrap;
}
.remember-row {
display: flex;
align-items: flex-start;
gap: 8px;
cursor: pointer;
font-size: 0.82rem;
color: var(--text-muted);
line-height: 1.5;
}
.remember-row input {
accent-color: var(--accent);
width: 14px;
height: 14px;
margin-top: 2px;
flex-shrink: 0;
}
.terms-link {
color: var(--accent);
text-decoration: none;
}
.terms-link:hover {
text-decoration: underline;
}
.auth-submit {
width: 100%;
background: var(--accent);
border: none;
border-radius: 9px;
color: #fff;
font-size: 0.9rem;
font-weight: 600;
padding: 11px;
cursor: pointer;
font-family: inherit;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: background .15s, opacity .15s;
}
.auth-submit:hover {
background: var(--accent-hover);
}
.auth-submit:disabled {
opacity: 0.6;
cursor: not-allowed;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.btn-spinner svg {
animation: spin .8s linear infinite;
}
.auth-switch {
margin-top: 18px;
text-align: center;
font-size: 0.82rem;
color: var(--text-muted);
}
.auth-switch-link {
color: var(--accent);
text-decoration: none;
font-weight: 600;
}
/* Success */
.auth-success {
text-align: center;
padding: 16px 0;
}
.success-check {
font-size: 3rem;
background: rgba(52, 211, 153, 0.15);
color: var(--green);
width: 72px;
height: 72px;
border-radius: 50%;
display: grid;
place-items: center;
margin: 0 auto 20px;
font-size: 2rem;
border: 2px solid rgba(52, 211, 153, 0.3);
}
.auth-success h2 {
font-size: 1.4rem;
margin-bottom: 10px;
}
.auth-success p {
color: var(--text-muted);
font-size: 0.875rem;
line-height: 1.6;
margin-bottom: 24px;
}
@media (max-width: 700px) {
.auth-layout {
grid-template-columns: 1fr;
}
.auth-left {
display: none;
}
.form-row {
grid-template-columns: 1fr;
}
}const pwdEl = document.getElementById("password");
const confirmEl = document.getElementById("confirm");
const submitBtn = document.getElementById("registerSubmit");
const bar = document.getElementById("strengthBar");
const label = document.getElementById("strengthLabel");
const meter = document.getElementById("strengthMeter");
function passwordStrength(pwd) {
let score = 0;
if (pwd.length >= 8) score++;
if (pwd.length >= 12) score++;
if (/[A-Z]/.test(pwd)) score++;
if (/[0-9]/.test(pwd)) score++;
if (/[^A-Za-z0-9]/.test(pwd)) score++;
return Math.min(4, score);
}
const strengthLabels = ["", "Weak", "Fair", "Good", "Strong"];
pwdEl?.addEventListener("input", () => {
const s = passwordStrength(pwdEl.value);
meter.hidden = !pwdEl.value;
bar.className = "strength-bar s" + s;
label.textContent = strengthLabels[s] || "";
});
confirmEl?.addEventListener("input", () => {
const errEl = document.getElementById("confirm-error");
if (confirmEl.value && confirmEl.value !== pwdEl.value) {
errEl.textContent = "Passwords do not match.";
errEl.hidden = false;
confirmEl.classList.add("invalid");
} else {
errEl.hidden = true;
confirmEl.classList.remove("invalid");
}
});
// Pwd toggle
document.getElementById("pwdToggle")?.addEventListener("click", () => {
pwdEl.type = pwdEl.type === "password" ? "text" : "password";
});
function showError(id, msg) {
const el = document.getElementById(id);
if (el) {
el.textContent = msg;
el.hidden = false;
}
}
function clearError(id) {
const el = document.getElementById(id);
if (el) el.hidden = true;
}
document.getElementById("registerForm")?.addEventListener("submit", async (e) => {
e.preventDefault();
let valid = true;
if (!document.getElementById("firstName").value.trim()) {
showError("firstName-error", "First name is required.");
valid = false;
} else clearError("firstName-error");
const email = document.getElementById("email").value;
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
showError("email-error", "Enter a valid email.");
valid = false;
} else clearError("email-error");
if (pwdEl.value.length < 8) {
showError("password-error", "Password must be at least 8 characters.");
valid = false;
} else clearError("password-error");
if (confirmEl.value !== pwdEl.value) {
showError("confirm-error", "Passwords do not match.");
valid = false;
}
if (!document.getElementById("terms").checked) {
showError("terms-error", "You must accept the terms.");
valid = false;
} else clearError("terms-error");
if (!valid) return;
submitBtn.disabled = true;
submitBtn.querySelector(".btn-text").hidden = true;
submitBtn.querySelector(".btn-spinner").hidden = false;
await new Promise((r) => setTimeout(r, 1400));
document.getElementById("formState").hidden = true;
document.getElementById("successState").hidden = false;
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Create Account</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="auth-layout">
<div class="auth-left">
<div class="auth-left-inner">
<a href="#" class="brand">✦ StealThis</a>
<ul class="feature-list">
<li class="feature-item">
<span class="feature-icon">⚡</span>
<span>300+ copy-paste components</span>
</li>
<li class="feature-item">
<span class="feature-icon">🔧</span>
<span>MCP server for AI integration</span>
</li>
<li class="feature-item">
<span class="feature-icon">🎨</span>
<span>Web animations & concept pages</span>
</li>
<li class="feature-item">
<span class="feature-icon">🚀</span>
<span>Always free · MIT License</span>
</li>
</ul>
</div>
</div>
<div class="auth-right">
<div class="auth-card">
<!-- Success state (hidden) -->
<div class="auth-success" id="successState" hidden>
<div class="success-check">✓</div>
<h2>Account created!</h2>
<p>We've sent a verification email to your inbox. Check it to activate your account.</p>
<a href="#" class="auth-submit">Go to sign in</a>
</div>
<!-- Form state -->
<div id="formState">
<div class="auth-header">
<h1 class="auth-title">Create account</h1>
<p class="auth-subtitle">Join thousands of developers already using StealThis.</p>
</div>
<div class="oauth-group">
<button type="button" class="oauth-btn">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
<path
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
fill="#4285F4" />
<path
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
fill="#34A853" />
<path
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"
fill="#FBBC05" />
<path
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
fill="#EA4335" />
</svg>
Sign up with Google
</button>
</div>
<div class="auth-divider"><span>or</span></div>
<form id="registerForm" class="auth-form" novalidate>
<div class="form-row">
<div class="field">
<label for="firstName" class="field-label">First name</label>
<input type="text" id="firstName" class="field-input" placeholder="Alex"
autocomplete="given-name" />
<p class="field-error" id="firstName-error" role="alert" hidden></p>
</div>
<div class="field">
<label for="lastName" class="field-label">Last name</label>
<input type="text" id="lastName" class="field-input" placeholder="Chen"
autocomplete="family-name" />
</div>
</div>
<div class="field">
<label for="email" class="field-label">Email address</label>
<input type="email" id="email" class="field-input" placeholder="you@example.com"
autocomplete="email" />
<p class="field-error" id="email-error" role="alert" hidden></p>
</div>
<div class="field">
<label for="password" class="field-label">Password</label>
<div class="field-input-wrap">
<input type="password" id="password" class="field-input" placeholder="Min. 8 characters"
autocomplete="new-password" />
<button type="button" class="pwd-toggle" id="pwdToggle" aria-label="Toggle password">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2">
<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>
</button>
</div>
<!-- Strength meter -->
<div class="strength-meter" id="strengthMeter" hidden>
<div class="strength-bar-wrap">
<div class="strength-bar" id="strengthBar"></div>
</div>
<span class="strength-label" id="strengthLabel"></span>
</div>
<p class="field-error" id="password-error" role="alert" hidden></p>
</div>
<div class="field">
<label for="confirm" class="field-label">Confirm password</label>
<input type="password" id="confirm" class="field-input" placeholder="Repeat your password"
autocomplete="new-password" />
<p class="field-error" id="confirm-error" role="alert" hidden></p>
</div>
<label class="remember-row">
<input type="checkbox" id="terms" />
<span class="remember-text">I agree to the <a href="#" class="terms-link">Terms of
Service</a> and <a href="#" class="terms-link">Privacy Policy</a></span>
</label>
<p class="field-error" id="terms-error" role="alert" hidden></p>
<button type="submit" class="auth-submit" id="registerSubmit">
<span class="btn-text">Create account</span>
<span class="btn-spinner" hidden>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2.5">
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
</span>
</button>
</form>
<p class="auth-switch">Already have an account? <a href="#" class="auth-switch-link">Sign in</a></p>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Register Page
A complete account registration page with full name, email, password, and confirm-password fields, live password strength meter, terms & conditions checkbox, and inline validation.
Features
- Full name + email + password + confirm-password fields
- Real-time password strength meter (Weak / Fair / Good / Strong) based on length, symbols, numbers, and uppercase
- Password match validation in real-time on the confirm field
- “Accept Terms” checkbox with link to terms page
- Google and GitHub OAuth alternatives
- Client-side validation on submit with inline error messages
- Success state with animated checkmark
How it works
passwordStrength()scores a password 0–4 and updates meter bar width + labelconfirmPasswordfield turns red/green as the user types to match- Submit handler validates all fields before simulating account creation with a loader