gentelella
Version:
Gentelella v4 — free admin template. 60 pages, 20 chart variants, fully interactive inbox & kanban, live theme generator, component playground, PWA-ready. Vite 8, vanilla JS, no Bootstrap, no jQuery.
595 lines (564 loc) • 28.2 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forms | Gentelella 2026 v4</title>
<link rel="icon" href="../images/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script type="module" src="/src/main-v4.js"></script>
</head>
<body data-shell="admin" data-page="forms" data-breadcrumb="Home > Forms > General">
<main class="main">
<div class="page-wrapper">
<div class="page-header">
<div class="page-header-row">
<div>
<div class="page-pretitle">UI</div>
<h1 class="page-title">Forms</h1>
</div>
<div class="page-actions">
<a class="btn btn-outline" href="form_advanced.html">Advanced</a>
<a class="btn btn-outline" href="form_validation.html">Validation</a>
<a class="btn btn-outline" href="form_wizards.html">Wizard</a>
<a class="btn btn-outline" href="form_upload.html">Upload</a>
</div>
</div>
</div>
<!-- ── Section 1: Project form (full-width with side help) ── -->
<div class="row col-8-4">
<div class="card">
<div class="card-header">
<div>
<div class="card-title">New project</div>
<div class="card-subtitle">Tell us about the project you're starting.</div>
</div>
</div>
<div class="card-body">
<form>
<div class="form-group">
<label class="form-label" for="projectName">Project name <span class="required">*</span></label>
<input type="text" id="projectName" class="form-control" placeholder="e.g. Acme website redesign">
<div class="form-help">A short, recognizable name. You can change it later.</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" for="client">Client</label>
<select id="client" class="form-control">
<option>Acme Corp</option>
<option>Globex Inc</option>
<option>Initech</option>
<option>Umbrella Co</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="budget">Budget (USD)</label>
<div class="input-affix">
<span class="affix prefix">$</span>
<input type="number" id="budget" class="form-control" placeholder="25000">
<span class="affix">USD</span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" for="startDate">Start date</label>
<input type="date" id="startDate" class="form-control">
</div>
<div class="form-group">
<label class="form-label" for="endDate">End date</label>
<input type="date" id="endDate" class="form-control">
</div>
</div>
<div class="form-group">
<label class="form-label" for="description">Description</label>
<textarea id="description" class="form-control" placeholder="What problem are we solving?"></textarea>
<div class="form-help">Markdown is supported.</div>
</div>
<div class="form-group">
<label class="form-label">Visibility</label>
<div class="segmented" role="radiogroup">
<label><input type="radio" name="vis" checked><span>Private</span></label>
<label><input type="radio" name="vis"><span>Team</span></label>
<label><input type="radio" name="vis"><span>Public</span></label>
</div>
<div class="form-help">Private — only invited members. Public — anyone with the link.</div>
</div>
<div class="form-group">
<label class="form-label" for="tagInput">Tags</label>
<div class="tag-input" id="tagInput" tabindex="0">
<span class="tag-pill">Web<button type="button" aria-label="Remove">×</button></span>
<span class="tag-pill">Design<button type="button" aria-label="Remove">×</button></span>
<span class="tag-pill">Q2<button type="button" aria-label="Remove">×</button></span>
<input type="text" placeholder="Add tag…" aria-label="Add tag">
</div>
<div class="form-help">Press Enter to add a tag.</div>
</div>
<div class="form-actions right">
<button type="reset" class="btn btn-outline">Save as draft</button>
<button type="submit" class="btn btn-primary">Create project</button>
</div>
</form>
</div>
</div>
<div style="display:flex;flex-direction:column;gap:16px">
<div class="card">
<div class="card-header"><div class="card-title">Tips</div></div>
<div class="card-body">
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:10px;font-size:12.5px;color:var(--text-secondary);line-height:1.5">
<li><strong style="color:var(--text)">Be specific</strong> with your project name — it shows up in URLs and notifications.</li>
<li><strong style="color:var(--text)">Set a realistic budget</strong>. You can adjust it as scope changes.</li>
<li><strong style="color:var(--text)">Tags</strong> help filter and group projects across your workspace.</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header"><div class="card-title">Need help?</div></div>
<div class="card-body" style="font-size:12.5px;color:var(--text-secondary);line-height:1.5">
Read our <a href="faq.html">project guide</a> or reach out to <a href="#">support</a> — we usually reply within an hour.
</div>
</div>
</div>
</div>
<!-- ── Section 2: Profile + avatar ── -->
<div class="row col-2">
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Profile</div>
<div class="card-subtitle">Avatar upload, names, bio.</div>
</div>
</div>
<div class="card-body">
<div style="display:flex;gap:20px;align-items:center;margin-bottom:18px">
<label class="avatar-upload" for="avatar">
A
<span class="overlay">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 7a2 2 0 012-2h2l2-2h6l2 2h2a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V7z"/><circle cx="12" cy="12" r="3.5"/></svg>
</span>
<input type="file" id="avatar" accept="image/*" hidden>
</label>
<div>
<div style="font-size:14px;font-weight:600;color:var(--text);margin-bottom:4px">Aigars Silkalns</div>
<div style="font-size:12.5px;color:var(--text-muted);margin-bottom:8px">Owner · aigars@example.com</div>
<button class="btn btn-outline btn-sm">Upload new photo</button>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label" for="firstName">First name</label>
<input type="text" id="firstName" class="form-control" value="Aigars">
</div>
<div class="form-group">
<label class="form-label" for="lastName">Last name</label>
<input type="text" id="lastName" class="form-control" value="Silkalns">
</div>
</div>
<div class="form-group">
<label class="form-label" for="username">Username</label>
<div class="input-affix">
<span class="affix prefix">colorlib.com/</span>
<input type="text" id="username" class="form-control" value="aigars">
</div>
</div>
<div class="form-group">
<label class="form-label" for="bio">Bio</label>
<textarea id="bio" class="form-control" placeholder="A few words about yourself…">Founder of Colorlib. Building Gentelella v4.</textarea>
</div>
</div>
</div>
<!-- Account security -->
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Account security</div>
<div class="card-subtitle">Password, 2FA, password strength.</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="currentPwd">Current password</label>
<div class="input-group">
<svg class="input-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5 7V5a3 3 0 016 0v2"/></svg>
<input type="password" id="currentPwd" class="form-control" value="••••••••">
</div>
</div>
<div class="form-group">
<label class="form-label" for="newPwd">New password</label>
<div class="input-group">
<svg class="input-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5 7V5a3 3 0 016 0v2"/></svg>
<input type="password" id="newPwd" class="form-control" placeholder="At least 12 characters" value="Goat-Stapler99!">
</div>
<div class="password-strength s4"><div class="seg"></div><div class="seg"></div><div class="seg"></div><div class="seg"></div></div>
<div class="password-strength-label">Strength: <strong>Strong</strong> · 12+ chars, mixed case, number, symbol</div>
</div>
<div class="form-group">
<label class="form-label">Two-factor authentication</label>
<label class="switch">
<input type="checkbox" checked>
<span class="track"></span>
<span class="switch-label">Enabled — codes from authenticator app</span>
</label>
</div>
<div class="form-group">
<label class="form-label">Verify with one-time code</label>
<div class="otp-grid">
<input class="otp-input" aria-label="Digit 1 of 6" type="text" inputmode="numeric" pattern="[0-9]" maxlength="1" placeholder="·">
<input class="otp-input" aria-label="Digit 2 of 6" type="text" inputmode="numeric" pattern="[0-9]" maxlength="1" placeholder="·">
<input class="otp-input" aria-label="Digit 3 of 6" type="text" inputmode="numeric" pattern="[0-9]" maxlength="1" placeholder="·">
<input class="otp-input" aria-label="Digit 4 of 6" type="text" inputmode="numeric" pattern="[0-9]" maxlength="1" placeholder="·">
<input class="otp-input" aria-label="Digit 5 of 6" type="text" inputmode="numeric" pattern="[0-9]" maxlength="1" placeholder="·">
<input class="otp-input" aria-label="Digit 6 of 6" type="text" inputmode="numeric" pattern="[0-9]" maxlength="1" placeholder="·">
</div>
</div>
</div>
</div>
</div>
<!-- ── Section 3: Preferences (sliders, switches, color, segmented) ── -->
<div class="row col-2">
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Preferences</div>
<div class="card-subtitle">Range sliders, switches, segmented controls.</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="volume">Notification volume</label>
<div class="slider-row">
<input type="range" id="volume" class="slider" min="0" max="100" value="62">
<span class="slider-value" data-slider-display="volume">62%</span>
</div>
</div>
<div class="form-group">
<label class="form-label" for="font-size">Font size</label>
<div class="slider-row">
<input type="range" id="font-size" class="slider" min="12" max="20" step="1" value="14">
<span class="slider-value" data-slider-display="font-size">14px</span>
</div>
</div>
<div class="form-group">
<label class="form-label">Theme</label>
<div class="segmented" role="radiogroup">
<label><input type="radio" name="pref-theme"><span>Light</span></label>
<label><input type="radio" name="pref-theme"><span>Dark</span></label>
<label><input type="radio" name="pref-theme" checked><span>System</span></label>
</div>
</div>
<div class="form-group">
<label class="form-label">Density</label>
<div class="segmented" role="radiogroup">
<label><input type="radio" name="pref-density" checked><span>Cozy</span></label>
<label><input type="radio" name="pref-density"><span>Comfortable</span></label>
<label><input type="radio" name="pref-density"><span>Compact</span></label>
</div>
</div>
<div class="form-group" style="display:flex;flex-direction:column;gap:8px">
<label class="switch"><input type="checkbox" checked><span class="track"></span><span class="switch-label">Send weekly summary email</span></label>
<label class="switch"><input type="checkbox"><span class="track"></span><span class="switch-label">Beta features</span></label>
<label class="switch"><input type="checkbox" checked><span class="track"></span><span class="switch-label">Sounds for incoming messages</span></label>
</div>
</div>
</div>
<!-- Color + rating + stepper -->
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Project meta</div>
<div class="card-subtitle">Color tag, priority, team size, rating.</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label">Project color</label>
<div class="color-grid" role="radiogroup">
<span class="color-swatch active" style="background:var(--primary)" data-color="primary"></span>
<span class="color-swatch" style="background:var(--azure)" data-color="azure"></span>
<span class="color-swatch" style="background:var(--blue)" data-color="blue"></span>
<span class="color-swatch" style="background:var(--purple)" data-color="purple"></span>
<span class="color-swatch" style="background:var(--pink)" data-color="pink"></span>
<span class="color-swatch" style="background:var(--red)" data-color="red"></span>
<span class="color-swatch" style="background:var(--orange)" data-color="orange"></span>
<span class="color-swatch" style="background:var(--yellow)" data-color="yellow"></span>
<span class="color-swatch" style="background:var(--green)" data-color="green"></span>
<span class="color-swatch" style="background:var(--cyan)" data-color="cyan"></span>
</div>
</div>
<div class="form-group">
<label class="form-label" for="priority">Priority</label>
<select id="priority" class="form-control">
<option>Low</option>
<option selected>Medium</option>
<option>High</option>
<option>Critical</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Team size</label>
<div class="stepper">
<button type="button" data-step="-1">−</button>
<input type="number" value="6" min="1" max="50" aria-label="Team size">
<button type="button" data-step="1">+</button>
</div>
</div>
<div class="form-group">
<label class="form-label">Project rating</label>
<div class="rating" data-rating="4">
<button type="button" class="on" aria-label="1 star"><svg viewBox="0 0 16 16"><path d="M8 1l2 5 5 .5-4 3.5 1 5-4-2.5-4 2.5 1-5-4-3.5 5-.5z"/></svg></button>
<button type="button" class="on" aria-label="2 stars"><svg viewBox="0 0 16 16"><path d="M8 1l2 5 5 .5-4 3.5 1 5-4-2.5-4 2.5 1-5-4-3.5 5-.5z"/></svg></button>
<button type="button" class="on" aria-label="3 stars"><svg viewBox="0 0 16 16"><path d="M8 1l2 5 5 .5-4 3.5 1 5-4-2.5-4 2.5 1-5-4-3.5 5-.5z"/></svg></button>
<button type="button" class="on" aria-label="4 stars"><svg viewBox="0 0 16 16"><path d="M8 1l2 5 5 .5-4 3.5 1 5-4-2.5-4 2.5 1-5-4-3.5 5-.5z"/></svg></button>
<button type="button" aria-label="5 stars"><svg viewBox="0 0 16 16"><path d="M8 1l2 5 5 .5-4 3.5 1 5-4-2.5-4 2.5 1-5-4-3.5 5-.5z"/></svg></button>
</div>
</div>
<div class="form-group">
<label class="form-label">Cover image</label>
<label class="file-input" for="cover">
<span class="file-input-trigger">Choose file</span>
<span class="file-input-name" id="cover-name">cover-redesign-final.jpg</span>
<input id="cover" type="file" accept="image/*">
</label>
</div>
</div>
</div>
</div>
<!-- ── Section 4: Search with suggestions, validation states ── -->
<div class="row col-2">
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Search with suggestions</div>
<div class="card-subtitle">Live autocomplete pattern.</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="searchInput">Search clients</label>
<div class="input-group">
<svg class="input-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="7" cy="7" r="5"/><path d="M11 11l3.5 3.5"/></svg>
<input type="text" id="searchInput" class="form-control" value="acm">
</div>
<div class="search-suggest">
<div class="search-suggest-row"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="6"/></svg><span><mark>Acm</mark>e Corp</span><span class="meta">Pro · NYC</span></div>
<div class="search-suggest-row"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="6"/></svg><span><mark>Acm</mark>e Industries</span><span class="meta">Business · Berlin</span></div>
<div class="search-suggest-row"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="6"/></svg><span><mark>Acm</mark>e Studio</span><span class="meta">Starter · Tokyo</span></div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Validation states</div>
<div class="card-subtitle">Helper, error, disabled, and required.</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="email-ok">Email <span class="required">*</span></label>
<input type="email" id="email-ok" class="form-control" value="aigars@example.com">
<div class="form-help">We'll never share your email.</div>
</div>
<div class="form-group">
<label class="form-label" for="email-bad">Backup email</label>
<input type="email" id="email-bad" class="form-control is-invalid" value="not-an-email">
<div class="form-error">Please enter a valid email address.</div>
</div>
<div class="form-group">
<label class="form-label" for="readonly-input">Account ID (read-only)</label>
<input type="text" id="readonly-input" class="form-control" value="acct_8f3k29df3lkj" disabled>
<div class="form-help">Generated automatically. Cannot be changed.</div>
</div>
<div class="form-group">
<label class="form-label" for="char-count">Tagline (max 80)</label>
<textarea id="char-count" class="form-control" maxlength="80" data-char-counter>The free Bootstrap admin template, redesigned for 2026.</textarea>
<div class="form-help"><span data-char-display="char-count">56</span>/80 characters</div>
</div>
</div>
</div>
</div>
<!-- ── Section 5: Advanced controls (date-range, multi-select, rich text) ── -->
<div class="row col-2">
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Date range</div>
<div class="card-subtitle">Two-month picker · presets · keyboard nav</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="dr-input">Report period</label>
<div class="date-range" data-date-range>
<input id="dr-input" class="form-control" placeholder="Pick a date range" aria-label="Date range">
</div>
<div class="form-help">Click the input to open the picker. Built from scratch — no library.</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Multi-select with chips</div>
<div class="card-subtitle">Type to filter · Enter to add · Backspace to remove</div>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label">Project tags</label>
<div class="multi-select" data-multi-select>
<select multiple hidden>
<option value="Design" selected>Design</option>
<option value="Engineering" selected>Engineering</option>
<option value="Product">Product</option>
<option value="Marketing">Marketing</option>
<option value="Support">Support</option>
<option value="Sales">Sales</option>
<option value="Operations">Operations</option>
<option value="Legal">Legal</option>
<option value="Finance">Finance</option>
<option value="HR">HR</option>
</select>
</div>
<div class="form-help">Bound to a real <code><select multiple></code> so it submits with any form.</div>
</div>
</div>
</div>
</div>
<div class="row col-1">
<div class="card">
<div class="card-header">
<div>
<div class="card-title">Rich text editor</div>
<div class="card-subtitle">Bold · italic · headings · lists · links · code · keyboard shortcuts</div>
</div>
</div>
<div class="card-body">
<div class="form-group" style="margin-bottom:0">
<label class="form-label" for="rt-bio">About</label>
<div class="rich-text" data-rich-text>
<textarea id="rt-bio" name="bio" hidden><p>Click any button in the toolbar — <strong>bold</strong>, <em>italic</em>, lists, links, code blocks. Try <kbd>⌘B</kbd> / <kbd>⌘I</kbd> / <kbd>⌘K</kbd>. The hidden <code>&lt;textarea&gt;</code> stays in sync so a parent form picks it up on submit.</p></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<script type="module">
// Slider value display
document.querySelectorAll('input[type="range"].slider').forEach((r) => {
const display = document.querySelector(`[data-slider-display="${r.id}"]`);
if (!display) return;
const suffix = r.id === 'volume' ? '%' : (r.id === 'font-size' ? 'px' : '');
r.addEventListener('input', () => { display.textContent = r.value + suffix; });
});
// Color swatch selection
document.querySelectorAll('.color-grid').forEach((grid) => {
grid.addEventListener('click', (e) => {
const sw = e.target.closest('.color-swatch');
if (!sw) return;
grid.querySelectorAll('.color-swatch').forEach((s) => s.classList.remove('active'));
sw.classList.add('active');
});
});
// Stepper +/-
document.querySelectorAll('.stepper').forEach((step) => {
const input = step.querySelector('input');
step.querySelectorAll('button[data-step]').forEach((btn) => {
btn.addEventListener('click', () => {
const delta = parseInt(btn.dataset.step, 10);
const min = parseInt(input.min || '-Infinity', 10);
const max = parseInt(input.max || 'Infinity', 10);
const next = Math.max(min, Math.min(max, (parseInt(input.value, 10) || 0) + delta));
input.value = next;
input.dispatchEvent(new Event('input', { bubbles: true }));
});
});
});
// Tag input
document.querySelectorAll('.tag-input').forEach((wrap) => {
const input = wrap.querySelector('input');
wrap.addEventListener('click', (e) => {
if (e.target === wrap) input.focus();
const close = e.target.closest('.tag-pill button');
if (close) close.closest('.tag-pill').remove();
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && input.value.trim()) {
e.preventDefault();
const pill = document.createElement('span');
pill.className = 'tag-pill';
pill.innerHTML = input.value.trim().replace(/[<>&]/g, '') + '<button type="button" aria-label="Remove">×</button>';
wrap.insertBefore(pill, input);
input.value = '';
} else if (e.key === 'Backspace' && !input.value) {
const last = wrap.querySelector('.tag-pill:last-of-type');
if (last) last.remove();
}
});
});
// Rating
document.querySelectorAll('.rating').forEach((rating) => {
const stars = [...rating.querySelectorAll('button')];
stars.forEach((star, i) => {
star.addEventListener('click', () => {
stars.forEach((s, j) => s.classList.toggle('on', j <= i));
rating.dataset.rating = String(i + 1);
});
});
});
// File input — show selected name
document.querySelectorAll('.file-input input[type="file"]').forEach((inp) => {
const display = inp.closest('.file-input').querySelector('.file-input-name');
if (!display) return;
inp.addEventListener('change', () => {
if (inp.files?.[0]) display.textContent = inp.files[0].name;
});
});
// OTP — auto-advance
document.querySelectorAll('.otp-grid').forEach((grid) => {
const inputs = [...grid.querySelectorAll('.otp-input')];
inputs.forEach((inp, i) => {
inp.addEventListener('input', () => {
if (inp.value && i < inputs.length - 1) inputs[i + 1].focus();
});
inp.addEventListener('keydown', (e) => {
if (e.key === 'Backspace' && !inp.value && i > 0) inputs[i - 1].focus();
});
});
});
// Char counter
document.querySelectorAll('[data-char-counter]').forEach((el) => {
const display = document.querySelector(`[data-char-display="${el.id}"]`);
if (!display) return;
const update = () => { display.textContent = String(el.value.length); };
el.addEventListener('input', update);
update();
});
// Password strength
const newPwd = document.getElementById('newPwd');
if (newPwd) {
const meter = newPwd.closest('.form-group')?.querySelector('.password-strength');
const label = newPwd.closest('.form-group')?.querySelector('.password-strength-label strong');
const score = (v) => {
let s = 0;
if (v.length >= 8) s++;
if (v.length >= 12) s++;
if (/[A-Z]/.test(v) && /[a-z]/.test(v)) s++;
if (/\d/.test(v) && /[^A-Za-z0-9]/.test(v)) s++;
return s;
};
const labels = ['Empty', 'Weak', 'Fair', 'Good', 'Strong'];
newPwd.addEventListener('input', () => {
const s = score(newPwd.value);
if (meter) meter.className = `password-strength s${s}`;
if (label) label.textContent = labels[s];
});
}
</script>
</body>
</html>