Bootstrap 5 Survey Form with Rating Scale
A compact two-column survey form with required fields, a 1 to 10 recommendation scale, selectable improvement chips and a free-text comment box.
<section class="wdw-stage">
<div class="container" style="max-width:860px">
<form id="surveyForm" class="wdw-card overflow-hidden" novalidate>
<header class="wdw-aurora px-4 px-md-5 py-4">
<div class="d-flex flex-wrap justify-content-between align-items-end gap-2 mb-3">
<div>
<span class="wdw-eyebrow text-white-50 mb-1">2 minute survey</span>
<h1 class="h3 fw-bold text-white mb-0">Help us improve the platform</h1>
</div>
<span class="small text-white-50" id="svCount" aria-live="polite">0 of 4 required answered</span>
</div>
<div class="progress bg-white bg-opacity-25" style="height:6px" role="progressbar" aria-label="Survey progress" aria-valuemin="0" aria-valuemax="4" aria-valuenow="0" id="svBar"><div class="progress-bar bg-white" style="width:0"></div></div>
</header>
<div class="p-4 p-md-5">
<div class="row g-3">
<div class="col-md-6">
<label for="svName" class="form-label fw-semibold">Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="svName" placeholder="Enter your name" autocomplete="name" required aria-describedby="svNameErr">
<div class="invalid-feedback" id="svNameErr">Please enter your name.</div>
</div>
<div class="col-md-6">
<label for="svEmail" class="form-label fw-semibold">Email <span class="text-danger">*</span></label>
<input type="email" class="form-control" id="svEmail" placeholder="[email protected]" autocomplete="email" required aria-describedby="svEmailErr">
<div class="invalid-feedback" id="svEmailErr">Enter a valid email.</div>
</div>
<div class="col-md-4">
<label for="svAge" class="form-label fw-semibold">Age <span class="text-danger">*</span></label>
<input type="number" class="form-control" id="svAge" min="1" max="125" placeholder="Age" required aria-describedby="svAgeErr">
<div class="invalid-feedback" id="svAgeErr">Enter an age from 1 to 125.</div>
</div>
<div class="col-md-4 col-6">
<label for="svRole" class="form-label fw-semibold">Current role</label>
<select class="form-select" id="svRole"><option value="" selected disabled>Select</option><option>Student</option><option>Full-time job</option><option>Full-time learner</option><option>Prefer not to say</option><option>Other</option></select>
</div>
<div class="col-md-4 col-6">
<label for="svLike" class="form-label fw-semibold">Like most</label>
<select class="form-select" id="svLike"><option value="" selected disabled>Select</option><option>Challenges</option><option>Projects</option><option>Community</option><option>Open source</option></select>
</div>
<div class="col-12">
<fieldset aria-required="true" id="svNpsSet">
<legend class="form-label fw-semibold fs-6">How likely are you to recommend us to a friend or colleague? <span class="text-danger">*</span></legend>
<div class="nps-grid" id="svNps"><input type="radio" class="btn-check" name="rating" id="nps1" value="1" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps1">1</label><input type="radio" class="btn-check" name="rating" id="nps2" value="2" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps2">2</label><input type="radio" class="btn-check" name="rating" id="nps3" value="3" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps3">3</label><input type="radio" class="btn-check" name="rating" id="nps4" value="4" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps4">4</label><input type="radio" class="btn-check" name="rating" id="nps5" value="5" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps5">5</label><input type="radio" class="btn-check" name="rating" id="nps6" value="6" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps6">6</label><input type="radio" class="btn-check" name="rating" id="nps7" value="7" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps7">7</label><input type="radio" class="btn-check" name="rating" id="nps8" value="8" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps8">8</label><input type="radio" class="btn-check" name="rating" id="nps9" value="9" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps9">9</label><input type="radio" class="btn-check" name="rating" id="nps10" value="10" required aria-describedby="svNpsErr"><label class="nps-btn" for="nps10">10</label></div>
<div class="d-flex justify-content-between small wdw-muted mt-2"><span>Not likely</span><span>Very likely</span></div>
<div class="small text-danger d-none mt-1" id="svNpsErr" tabindex="-1">Please pick a score from 1 to 10.</div>
</fieldset>
</div>
<div class="col-12">
<fieldset>
<legend class="form-label fw-semibold fs-6">What should we improve? <span class="fw-normal wdw-muted">Select all that apply</span></legend>
<div class="d-flex flex-wrap gap-2"><input type="checkbox" class="btn-check" name="improve" id="imp0" value="Front-end projects"><label class="survey-chip" for="imp0"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Front-end projects</label><input type="checkbox" class="btn-check" name="improve" id="imp1" value="Back-end projects"><label class="survey-chip" for="imp1"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Back-end projects</label><input type="checkbox" class="btn-check" name="improve" id="imp2" value="Data visualization"><label class="survey-chip" for="imp2"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Data visualization</label><input type="checkbox" class="btn-check" name="improve" id="imp3" value="Challenges"><label class="survey-chip" for="imp3"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Challenges</label><input type="checkbox" class="btn-check" name="improve" id="imp4" value="Open source community"><label class="survey-chip" for="imp4"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Open source community</label><input type="checkbox" class="btn-check" name="improve" id="imp5" value="Helper rooms"><label class="survey-chip" for="imp5"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Helper rooms</label><input type="checkbox" class="btn-check" name="improve" id="imp6" value="Meetups"><label class="survey-chip" for="imp6"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Meetups</label><input type="checkbox" class="btn-check" name="improve" id="imp7" value="Wiki"><label class="survey-chip" for="imp7"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Wiki</label><input type="checkbox" class="btn-check" name="improve" id="imp8" value="Forum"><label class="survey-chip" for="imp8"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Forum</label><input type="checkbox" class="btn-check" name="improve" id="imp9" value="Additional courses"><label class="survey-chip" for="imp9"><svg class="chip-ck" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>Additional courses</label></div>
</fieldset>
</div>
<div class="col-12">
<label for="svComment" class="form-label fw-semibold">Any comment or suggestion?</label>
<textarea class="form-control" id="svComment" rows="2" placeholder="Enter your comment here..."></textarea>
</div>
<div class="col-12 d-flex flex-wrap align-items-center gap-3">
<button type="submit" class="btn btn-primary rounded-pill px-4">Submit survey</button>
<span id="svOk" class="text-success fw-semibold d-none" role="status">Thank you, your answers were sent.</span>
</div>
</div>
</div>
</form>
</div>
</section>
/* Web Designer Wall library style — shared base for every Bootstrap 5 snippet.
Indigo accent, Plus Jakarta Sans, soft cards, pill buttons. Works in light and dark. */
@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap");
:root,
[data-bs-theme="light"] {
--wdw-font: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--bs-body-font-family: var(--wdw-font);
--bs-primary: #4f46e5;
--bs-primary-rgb: 79, 70, 229;
--bs-primary-text-emphasis: #3730a3;
--bs-primary-bg-subtle: #eef2ff;
--bs-primary-border-subtle: #c7d2fe;
--bs-link-color: #4f46e5;
--bs-link-color-rgb: 79, 70, 229;
--bs-link-hover-color: #4338ca;
--bs-link-hover-color-rgb: 67, 56, 202;
--bs-focus-ring-color: rgba(79, 70, 229, 0.25);
--bs-border-radius: 0.75rem;
--bs-border-radius-lg: 1rem;
--bs-border-radius-xl: 1.25rem;
--wdw-radius-card: 1.25rem;
--wdw-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 16px 40px -16px rgba(16, 24, 40, 0.22);
--wdw-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06), 0 4px 12px -4px rgba(16, 24, 40, 0.12);
--wdw-stage: #f5f6fb;
}
[data-bs-theme="dark"] {
--bs-primary: #818cf8;
--bs-primary-rgb: 129, 140, 248;
--bs-primary-text-emphasis: #c7d2fe;
--bs-primary-bg-subtle: rgba(129, 140, 248, 0.14);
--bs-primary-border-subtle: rgba(129, 140, 248, 0.35);
--bs-link-color: #a5b4fc;
--bs-link-color-rgb: 165, 180, 252;
--bs-link-hover-color: #c7d2fe;
--bs-link-hover-color-rgb: 199, 210, 254;
--bs-focus-ring-color: rgba(129, 140, 248, 0.35);
--wdw-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 16px 40px -16px rgba(0, 0, 0, 0.7);
--wdw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 12px -4px rgba(0, 0, 0, 0.5);
--wdw-stage: #0f1117;
}
body { font-family: var(--wdw-font); -webkit-font-smoothing: antialiased; }
/* Bootstrap compiles its blue into these components; point them at the accent. */
.btn-primary {
--bs-btn-bg: var(--bs-primary); --bs-btn-border-color: var(--bs-primary);
--bs-btn-hover-bg: #4338ca; --bs-btn-hover-border-color: #4338ca;
--bs-btn-active-bg: #3730a3; --bs-btn-active-border-color: #3730a3;
--bs-btn-disabled-bg: var(--bs-primary); --bs-btn-disabled-border-color: var(--bs-primary);
--bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}
[data-bs-theme="dark"] .btn-primary { --bs-btn-color: #0f1117; --bs-btn-hover-bg: #a5b4fc; --bs-btn-hover-border-color: #a5b4fc; --bs-btn-hover-color: #0f1117; --bs-btn-active-color: #0f1117; }
.btn-outline-primary {
--bs-btn-color: var(--bs-primary); --bs-btn-border-color: var(--bs-primary);
--bs-btn-hover-bg: var(--bs-primary); --bs-btn-hover-border-color: var(--bs-primary);
--bs-btn-active-bg: var(--bs-primary); --bs-btn-active-border-color: var(--bs-primary);
--bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}
.btn { --bs-btn-font-weight: 600; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: .5rem; }
.btn-sm { min-height: 36px; }
.form-control, .form-select { min-height: 44px; border-radius: .75rem; }
.form-control:focus, .form-select:focus, .form-check-input:focus { border-color: rgba(var(--bs-primary-rgb), .6); box-shadow: 0 0 0 .25rem var(--bs-focus-ring-color); }
.form-check-input:checked { background-color: var(--bs-primary); border-color: var(--bs-primary); }
.nav-pills { --bs-nav-pills-link-active-bg: var(--bs-primary); }
.nav { --bs-nav-link-color: var(--bs-secondary-color); --bs-nav-link-hover-color: var(--bs-emphasis-color); }
.pagination { --bs-pagination-active-bg: var(--bs-primary); --bs-pagination-active-border-color: var(--bs-primary); --bs-pagination-color: var(--bs-body-color); --bs-pagination-focus-box-shadow: 0 0 0 .25rem var(--bs-focus-ring-color); }
.progress, .progress-stacked { --bs-progress-bar-bg: var(--bs-primary); }
.list-group { --bs-list-group-active-bg: var(--bs-primary); --bs-list-group-active-border-color: var(--bs-primary); }
.dropdown-menu { --bs-dropdown-link-active-bg: var(--bs-primary); border-radius: .875rem; box-shadow: var(--wdw-shadow-sm); }
.accordion { --bs-accordion-active-bg: var(--bs-primary-bg-subtle); --bs-accordion-active-color: var(--bs-primary-text-emphasis); --bs-accordion-btn-focus-box-shadow: 0 0 0 .25rem var(--bs-focus-ring-color); }
.badge { font-weight: 600; }
/* Library building blocks — use these instead of re-inventing them per snippet. */
.wdw-stage { min-height: 100vh; display: flex; align-items: center; background: var(--wdw-stage); padding-block: 3rem; }
.wdw-card { background: var(--bs-body-bg); border: 1px solid var(--bs-border-color-translucent); border-radius: var(--wdw-radius-card); box-shadow: var(--wdw-shadow); }
.wdw-card-sm { background: var(--bs-body-bg); border: 1px solid var(--bs-border-color-translucent); border-radius: 1rem; box-shadow: var(--wdw-shadow-sm); }
.wdw-aurora { position: relative; color: #fff; background: radial-gradient(60% 55% at 100% 0, #818cf8 0, transparent 70%), radial-gradient(65% 60% at 0 100%, #ec4899 0, transparent 65%), #1e1b4b; }
.wdw-eyebrow { display: inline-block; font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--bs-primary); }
.wdw-heading { font-weight: 800; letter-spacing: -.02em; color: var(--bs-emphasis-color); }
.wdw-muted { color: var(--bs-secondary-color); }
.wdw-icon { width: 2.75rem; height: 2.75rem; flex: none; display: inline-flex; align-items: center; justify-content: center; border-radius: .875rem; background: var(--bs-primary-bg-subtle); color: var(--bs-primary); }
/* Component */
.nps-grid { display: grid; grid-template-columns: repeat(10, 1fr); border: 1px solid var(--bs-border-color); border-radius: 50rem; overflow: hidden; background: var(--bs-body-bg); }
.nps-btn { display: flex; align-items: center; justify-content: center; min-height: 44px; font-weight: 600; cursor: pointer; color: var(--bs-body-color); border-left: 1px solid var(--bs-border-color); transition: background-color .15s ease, color .15s ease; }
.nps-grid > .nps-btn:first-of-type { border-left: 0; }
.nps-btn:hover { background: var(--bs-tertiary-bg); }
.btn-check:checked + .nps-btn { background: var(--bs-primary); color: #fff; }
[data-bs-theme="dark"] .btn-check:checked + .nps-btn { color: #0f1117; }
.btn-check:focus-visible + .nps-btn, .btn-check:focus-visible + .survey-chip { outline: 2px solid var(--bs-primary); outline-offset: -2px; }
.survey-chip { display: inline-flex; align-items: center; gap: .35rem; min-height: 36px; padding: .25rem .9rem; font-size: .875rem; font-weight: 600; border: 1px solid var(--bs-border-color); border-radius: 50rem; color: var(--bs-body-color); cursor: pointer; transition: background-color .15s ease, border-color .15s ease; }
.survey-chip:hover { background: var(--bs-tertiary-bg); }
.survey-chip .chip-ck { display: none; }
.btn-check:checked + .survey-chip { background: var(--bs-primary-bg-subtle); color: var(--bs-primary-text-emphasis); border-color: var(--bs-primary); }
.btn-check:checked + .survey-chip .chip-ck { display: inline; }
#surveyForm textarea.form-control { min-height: 4rem; height: auto; }
#svBar .progress-bar { transition: width .25s ease; }
@media (max-width: 575.98px) {
.nps-grid { grid-template-columns: repeat(5, 1fr); border-radius: 1rem; }
.nps-btn:nth-of-type(5n+1) { border-left: 0; }
.nps-btn:nth-of-type(n+6) { border-top: 1px solid var(--bs-border-color); }
}
@media (prefers-reduced-motion: reduce) { .nps-btn, .survey-chip, #svBar .progress-bar { transition: none; } }
(function () {
function initSurvey() {
var form = document.getElementById('surveyForm');
if (!form) return;
var ok = document.getElementById('svOk');
var npsErr = document.getElementById('svNpsErr');
var set = document.getElementById('svNpsSet');
var bar = document.getElementById('svBar');
var count = document.getElementById('svCount');
var required = ['svName', 'svEmail', 'svAge'];
function updateProgress() {
var n = required.filter(function (id) { return document.getElementById(id).checkValidity() && document.getElementById(id).value; }).length;
if (form.querySelector('input[name="rating"]:checked')) n++;
bar.setAttribute('aria-valuenow', n);
bar.firstElementChild.style.width = (n / 4 * 100) + '%';
count.textContent = n + ' of 4 required answered';
}
form.addEventListener('input', updateProgress);
form.addEventListener('change', function (e) {
if (e.target.name === 'rating') { npsErr.classList.add('d-none'); set.removeAttribute('aria-invalid'); }
updateProgress();
});
form.addEventListener('submit', function (e) {
e.preventDefault();
form.classList.add('was-validated');
var scored = !!form.querySelector('input[name="rating"]:checked');
npsErr.classList.toggle('d-none', scored);
if (!scored) set.setAttribute('aria-invalid', 'true');
if (!form.checkValidity() || !scored) {
ok.classList.add('d-none');
var bad = form.querySelector('.form-control:invalid, .form-select:invalid');
if (bad) bad.focus(); else npsErr.focus();
return;
}
ok.classList.remove('d-none');
form.reset();
form.classList.remove('was-validated');
updateProgress();
});
}
initSurvey();
})();
Built on Bootstrap 5.3.8 with plain JavaScript — no jQuery. Supports light and dark mode via data-bs-theme.