Insurance — Coverage Selector
An auto-insurance coverage builder with toggleable modules for liability, collision, comprehensive, roadside and rental, each with amount or deductible selectors that recalculate a running monthly premium and update a live coverage summary panel with included items and totals.
MCP
Code
:root {
--blue: #2563eb;
--blue-d: #1d4ed8;
--green: #15a06b;
--blue-50: #eef4ff;
--ink: #0f1a2b;
--muted: #5a6b82;
--bg: #f3f7fc;
--white: #ffffff;
--line: #e1e9f4;
--radius: 16px;
--shadow: 0 1px 2px rgba(15, 26, 43, .04), 0 8px 28px rgba(37, 99, 235, .07);
--font: 'Inter', system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font);
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
padding: 32px 18px;
}
.quote {
max-width: 980px;
margin: 0 auto;
}
/* Header */
.quote__head {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 22px;
}
.brand {
display: flex;
align-items: center;
gap: 9px;
font-weight: 800;
font-size: 17px;
letter-spacing: -.01em;
}
.brand__mark {
display: grid;
place-items: center;
width: 34px;
height: 34px;
border-radius: 10px;
background: var(--blue);
color: var(--white);
}
.quote__vehicle {
font-size: 13px;
color: var(--muted);
font-weight: 500;
background: var(--white);
border: 1px solid var(--line);
padding: 7px 13px;
border-radius: 999px;
}
/* Layout */
.quote__grid {
display: grid;
grid-template-columns: 1fr 340px;
gap: 22px;
align-items: start;
}
/* Modules column */
.modules h1 {
font-size: 24px;
font-weight: 800;
letter-spacing: -.02em;
}
.modules__lead {
color: var(--muted);
font-size: 14px;
margin: 5px 0 18px;
}
.module-list { list-style: none; display: flex; flex-direction: column; gap: 14px; }
.module {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
transition: border-color .18s, box-shadow .18s;
}
.module.is-active {
border-color: rgba(37, 99, 235, .35);
}
.module.is-off { opacity: .72; }
.module__main {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 14px;
padding: 16px 18px;
}
.module__icon {
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 12px;
background: var(--blue-50);
color: var(--blue);
flex-shrink: 0;
}
.module.is-off .module__icon { background: #eef1f6; color: var(--muted); }
.module__title {
font-weight: 700;
font-size: 15.5px;
display: flex;
align-items: center;
gap: 8px;
}
.module__desc {
font-size: 13px;
color: var(--muted);
margin-top: 2px;
}
.tag {
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .04em;
padding: 2px 7px;
border-radius: 999px;
}
.tag--req { background: #e9f7f0; color: var(--green); }
/* Toggle */
.toggle { position: relative; display: inline-flex; cursor: pointer; }
.toggle input {
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
margin: 0;
cursor: pointer;
}
.toggle input:disabled { cursor: not-allowed; }
.toggle__track {
width: 46px;
height: 27px;
border-radius: 999px;
background: #cdd7e6;
transition: background .2s;
display: inline-flex;
align-items: center;
padding: 3px;
}
.toggle__thumb {
width: 21px;
height: 21px;
border-radius: 50%;
background: var(--white);
box-shadow: 0 1px 3px rgba(15, 26, 43, .25);
transition: transform .2s;
}
.toggle input:checked + .toggle__track { background: var(--green); }
.toggle input:checked + .toggle__track .toggle__thumb { transform: translateX(19px); }
.toggle input:disabled:checked + .toggle__track { background: #9cc7b3; }
.toggle input:focus-visible + .toggle__track {
outline: 3px solid rgba(37, 99, 235, .35);
outline-offset: 2px;
}
/* Options */
.module__opts {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
padding: 14px 18px;
border-top: 1px dashed var(--line);
background: #fafcff;
}
.module__opts--flat { justify-content: space-between; }
.opts__label {
font-size: 12px;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: .04em;
}
.opts__price { font-weight: 700; color: var(--green); font-size: 14px; }
.chips { display: flex; gap: 8px; flex-wrap: wrap; }
.chip {
font-family: inherit;
font-size: 13px;
font-weight: 600;
color: var(--ink);
background: var(--white);
border: 1.5px solid var(--line);
border-radius: 10px;
padding: 7px 12px;
cursor: pointer;
transition: border-color .15s, background .15s, color .15s, transform .08s;
}
.chip:hover { border-color: rgba(37, 99, 235, .4); }
.chip:active { transform: scale(.97); }
.chip.is-on {
background: var(--blue);
border-color: var(--blue);
color: var(--white);
}
.chip:focus-visible {
outline: 3px solid rgba(37, 99, 235, .35);
outline-offset: 2px;
}
/* Summary */
.summary { position: sticky; top: 24px; }
.summary__card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 22px;
}
.summary__card h2 {
font-size: 13px;
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
letter-spacing: .05em;
}
.summary__total {
display: flex;
align-items: baseline;
gap: 2px;
margin: 6px 0 2px;
color: var(--ink);
}
.summary__currency { font-size: 24px; font-weight: 700; }
#totalMonthly { font-size: 48px; font-weight: 800; letter-spacing: -.03em; line-height: 1; }
.summary__per { font-size: 17px; font-weight: 600; color: var(--muted); margin-left: 3px; }
.summary__annual { font-size: 13px; color: var(--muted); }
.summary__annual strong { color: var(--ink); }
.summary__divider { height: 1px; background: var(--line); margin: 18px 0; }
.summary__sub {
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .05em;
color: var(--muted);
margin-bottom: 10px;
}
.summary__list { list-style: none; display: flex; flex-direction: column; gap: 9px; }
.summary__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
font-size: 13.5px;
animation: fade .22s ease;
}
.summary__row .name { display: flex; align-items: center; gap: 7px; font-weight: 600; }
.summary__row .name svg { color: var(--green); flex-shrink: 0; }
.summary__row .meta { color: var(--muted); font-weight: 400; font-size: 12px; }
.summary__row .cost { font-weight: 700; white-space: nowrap; }
.summary__empty { font-size: 13px; color: var(--muted); font-style: italic; }
.cta {
width: 100%;
margin-top: 18px;
font-family: inherit;
font-size: 15px;
font-weight: 700;
color: var(--white);
background: var(--blue);
border: none;
border-radius: 12px;
padding: 13px;
cursor: pointer;
transition: background .15s, transform .08s;
}
.cta:hover { background: var(--blue-d); }
.cta:active { transform: scale(.99); }
.cta:focus-visible { outline: 3px solid rgba(37, 99, 235, .4); outline-offset: 2px; }
.cta.is-done { background: var(--green); }
.summary__note {
display: flex;
align-items: center;
gap: 6px;
justify-content: center;
margin-top: 12px;
font-size: 12px;
color: var(--muted);
}
.summary__note svg { color: var(--green); }
@keyframes fade {
from { opacity: 0; transform: translateY(-3px); }
to { opacity: 1; transform: translateY(0); }
}
/* Responsive */
@media (max-width: 820px) {
.quote__grid { grid-template-columns: 1fr; }
.summary { position: static; }
}
@media (max-width: 520px) {
body { padding: 20px 12px; }
.module__main { grid-template-columns: auto 1fr; row-gap: 12px; }
.module__main .toggle { grid-column: 1 / -1; justify-self: end; }
.modules h1 { font-size: 21px; }
#totalMonthly { font-size: 42px; }
.quote__vehicle { width: 100%; text-align: center; }
}(function () {
"use strict";
var LABELS = {
liability: "Liability",
collision: "Collision",
comprehensive: "Comprehensive",
roadside: "Roadside assistance",
rental: "Rental reimbursement",
};
var modules = Array.prototype.slice.call(document.querySelectorAll(".module"));
var summaryList = document.getElementById("summaryList");
var totalMonthlyEl = document.getElementById("totalMonthly");
var totalAnnualEl = document.getElementById("totalAnnual");
var continueBtn = document.getElementById("continueBtn");
var checkIcon =
'<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" ' +
'stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
'<path d="M20 6L9 17l-5-5"/></svg>';
function fmt(n) {
return n.toLocaleString("en-US");
}
// Returns { price, metaText } for one module's current state.
function readModule(mod) {
var flat = mod.querySelector(".opts__price");
if (flat) {
return { price: parseInt(flat.dataset.price, 10) || 0, meta: "24/7 nationwide" };
}
var active = mod.querySelector(".chip.is-on");
if (!active) return { price: 0, meta: "" };
var price = parseInt(active.dataset.price, 10) || 0;
var label = mod.querySelector(".opts__label");
var meta = (label ? label.textContent.trim() + ": " : "") + active.textContent.trim();
return { price: price, meta: meta };
}
function recalc() {
var total = 0;
var rows = [];
modules.forEach(function (mod) {
var input = mod.querySelector('input[type="checkbox"]');
var on = input.checked;
mod.classList.toggle("is-active", on);
mod.classList.toggle("is-off", !on);
var opts = mod.querySelector(".module__opts");
if (opts) opts.style.display = on ? "" : "none";
if (!on) return;
var id = mod.dataset.id;
var info = readModule(mod);
total += info.price;
rows.push({ id: id, price: info.price, meta: info.meta });
});
// Render summary
summaryList.innerHTML = "";
if (rows.length === 0) {
var li = document.createElement("li");
li.className = "summary__empty";
li.textContent = "No coverage selected yet.";
summaryList.appendChild(li);
} else {
rows.forEach(function (r) {
var li = document.createElement("li");
li.className = "summary__row";
li.innerHTML =
'<span class="name">' + checkIcon +
"<span>" + LABELS[r.id] +
(r.meta ? '<br><span class="meta">' + r.meta + "</span>" : "") +
"</span></span>" +
'<span class="cost">$' + r.price + "</span>";
summaryList.appendChild(li);
});
}
animateNumber(totalMonthlyEl, total);
totalAnnualEl.textContent = "$" + fmt(total * 12);
// reset CTA state if user changes things after "confirming"
if (continueBtn.classList.contains("is-done")) {
continueBtn.classList.remove("is-done");
continueBtn.textContent = "Continue to checkout";
}
}
// Small count-up so the big number feels responsive.
function animateNumber(el, target) {
var current = parseInt(el.textContent.replace(/[^0-9]/g, ""), 10) || 0;
if (current === target) {
el.textContent = fmt(target);
return;
}
var steps = 12;
var step = (target - current) / steps;
var i = 0;
clearInterval(el._timer);
el._timer = setInterval(function () {
i++;
current += step;
if (i >= steps) {
clearInterval(el._timer);
current = target;
}
el.textContent = fmt(Math.round(current));
}, 16);
}
// Toggle handlers
modules.forEach(function (mod) {
var input = mod.querySelector('input[type="checkbox"]');
if (!input.disabled) {
input.addEventListener("change", recalc);
}
// Chip (limit / deductible) handlers
var chipGroup = mod.querySelector(".chips");
if (chipGroup) {
chipGroup.addEventListener("click", function (e) {
var chip = e.target.closest(".chip");
if (!chip) return;
chipGroup.querySelectorAll(".chip").forEach(function (c) {
c.classList.remove("is-on");
c.setAttribute("aria-checked", "false");
});
chip.classList.add("is-on");
chip.setAttribute("aria-checked", "true");
recalc();
});
// Arrow-key support within a radiogroup
chipGroup.addEventListener("keydown", function (e) {
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
var chips = Array.prototype.slice.call(chipGroup.querySelectorAll(".chip"));
var idx = chips.indexOf(document.activeElement);
if (idx === -1) return;
e.preventDefault();
var next = e.key === "ArrowRight" ? idx + 1 : idx - 1;
next = (next + chips.length) % chips.length;
chips[next].focus();
chips[next].click();
});
}
});
continueBtn.addEventListener("click", function () {
continueBtn.classList.add("is-done");
continueBtn.textContent = "Coverage saved ✓";
});
recalc();
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Coverage Selector</title>
<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@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="quote" aria-labelledby="quote-title">
<header class="quote__head">
<div class="brand">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</span>
<span>Northbridge Auto</span>
</div>
<div class="quote__vehicle">2023 Toyota RAV4 · Policy #NB-48217</div>
</header>
<div class="quote__grid">
<!-- Coverage modules -->
<section class="modules" aria-label="Coverage modules">
<h1 id="quote-title">Build your coverage</h1>
<p class="modules__lead">Toggle the protections you want. Your monthly premium updates as you go.</p>
<ul class="module-list" id="moduleList">
<li class="module" data-id="liability" data-required="true">
<div class="module__main">
<div class="module__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="module__text">
<div class="module__title">Liability
<span class="tag tag--req">Required</span>
</div>
<p class="module__desc">Covers injury and property damage you cause to others.</p>
</div>
<label class="toggle">
<input type="checkbox" id="t-liability" checked disabled aria-label="Liability coverage (required)" />
<span class="toggle__track" aria-hidden="true"><span class="toggle__thumb"></span></span>
</label>
</div>
<div class="module__opts">
<span class="opts__label" id="lbl-liability">Coverage limit</span>
<div class="chips" role="radiogroup" aria-labelledby="lbl-liability" data-target="liability">
<button type="button" class="chip" role="radio" aria-checked="false" data-value="50000" data-price="38">50k / 100k</button>
<button type="button" class="chip is-on" role="radio" aria-checked="true" data-value="100000" data-price="52">100k / 300k</button>
<button type="button" class="chip" role="radio" aria-checked="false" data-value="250000" data-price="71">250k / 500k</button>
</div>
</div>
</li>
<li class="module" data-id="collision">
<div class="module__main">
<div class="module__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 13l2-5a3 3 0 0 1 2.8-2h8.4A3 3 0 0 1 19 8l2 5"/><path d="M5 13h14v5H5z"/><circle cx="7.5" cy="18.5" r="1.5"/><circle cx="16.5" cy="18.5" r="1.5"/></svg>
</div>
<div class="module__text">
<div class="module__title">Collision</div>
<p class="module__desc">Repairs your car after an accident, regardless of fault.</p>
</div>
<label class="toggle">
<input type="checkbox" id="t-collision" checked aria-label="Collision coverage" />
<span class="toggle__track" aria-hidden="true"><span class="toggle__thumb"></span></span>
</label>
</div>
<div class="module__opts">
<span class="opts__label" id="lbl-collision">Deductible</span>
<div class="chips" role="radiogroup" aria-labelledby="lbl-collision" data-target="collision">
<button type="button" class="chip" role="radio" aria-checked="false" data-value="250" data-price="46">$250</button>
<button type="button" class="chip is-on" role="radio" aria-checked="true" data-value="500" data-price="34">$500</button>
<button type="button" class="chip" role="radio" aria-checked="false" data-value="1000" data-price="24">$1,000</button>
</div>
</div>
</li>
<li class="module" data-id="comprehensive">
<div class="module__main">
<div class="module__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 15a4 4 0 0 1 4-4 5 5 0 0 1 9.6-1.5A3.5 3.5 0 1 1 18 16H7a4 4 0 0 1-4-1z"/></svg>
</div>
<div class="module__text">
<div class="module__title">Comprehensive</div>
<p class="module__desc">Theft, hail, fire, vandalism and other non-collision damage.</p>
</div>
<label class="toggle">
<input type="checkbox" id="t-comprehensive" checked aria-label="Comprehensive coverage" />
<span class="toggle__track" aria-hidden="true"><span class="toggle__thumb"></span></span>
</label>
</div>
<div class="module__opts">
<span class="opts__label" id="lbl-comprehensive">Deductible</span>
<div class="chips" role="radiogroup" aria-labelledby="lbl-comprehensive" data-target="comprehensive">
<button type="button" class="chip is-on" role="radio" aria-checked="true" data-value="250" data-price="19">$250</button>
<button type="button" class="chip" role="radio" aria-checked="false" data-value="500" data-price="13">$500</button>
<button type="button" class="chip" role="radio" aria-checked="false" data-value="1000" data-price="9">$1,000</button>
</div>
</div>
</li>
<li class="module" data-id="roadside">
<div class="module__main">
<div class="module__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 11l1.5-4.5A2 2 0 0 1 8.4 5h7.2a2 2 0 0 1 1.9 1.5L19 11"/><path d="M4 11h16v6H4z"/><path d="M9 17v2"/><path d="M15 17v2"/></svg>
</div>
<div class="module__text">
<div class="module__title">Roadside assistance</div>
<p class="module__desc">Towing, jump-starts, lockouts and flat-tire help, 24/7.</p>
</div>
<label class="toggle">
<input type="checkbox" id="t-roadside" aria-label="Roadside assistance coverage" />
<span class="toggle__track" aria-hidden="true"><span class="toggle__thumb"></span></span>
</label>
</div>
<div class="module__opts module__opts--flat">
<span class="opts__label">Flat add-on</span>
<span class="opts__price" data-price="7">+$7 / mo</span>
</div>
</li>
<li class="module" data-id="rental">
<div class="module__main">
<div class="module__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="6" rx="1"/><path d="M6 11V8a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v3"/><circle cx="7.5" cy="18" r="1.3"/><circle cx="16.5" cy="18" r="1.3"/></svg>
</div>
<div class="module__text">
<div class="module__title">Rental reimbursement</div>
<p class="module__desc">Pays for a rental car while yours is being repaired.</p>
</div>
<label class="toggle">
<input type="checkbox" id="t-rental" aria-label="Rental reimbursement coverage" />
<span class="toggle__track" aria-hidden="true"><span class="toggle__thumb"></span></span>
</label>
</div>
<div class="module__opts">
<span class="opts__label" id="lbl-rental">Daily limit</span>
<div class="chips" role="radiogroup" aria-labelledby="lbl-rental" data-target="rental">
<button type="button" class="chip is-on" role="radio" aria-checked="true" data-value="35" data-price="6">$35 / day</button>
<button type="button" class="chip" role="radio" aria-checked="false" data-value="50" data-price="9">$50 / day</button>
<button type="button" class="chip" role="radio" aria-checked="false" data-value="75" data-price="13">$75 / day</button>
</div>
</div>
</li>
</ul>
</section>
<!-- Summary -->
<aside class="summary" aria-labelledby="summary-title" aria-live="polite">
<div class="summary__card">
<h2 id="summary-title">Your monthly premium</h2>
<div class="summary__total">
<span class="summary__currency">$</span><span id="totalMonthly">0</span><span class="summary__per">/mo</span>
</div>
<p class="summary__annual">That's <strong id="totalAnnual">$0</strong> per year, billed monthly.</p>
<div class="summary__divider"></div>
<h3 class="summary__sub">Included coverage</h3>
<ul class="summary__list" id="summaryList"></ul>
<button type="button" class="cta" id="continueBtn">Continue to checkout</button>
<p class="summary__note">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg>
Locked-in rate for 6 months. Cancel anytime.
</p>
</div>
</aside>
</div>
</main>
<script src="script.js"></script>
</body>
</html>Coverage Selector
A reassuring auto-insurance coverage builder. Each coverage module — liability, collision, comprehensive, roadside assistance and rental reimbursement — has a toggle and, where it applies, a coverage-limit or deductible selector. Flipping a toggle or changing a limit instantly recalculates the running monthly premium and refreshes the summary panel on the right.
The summary lists every included coverage with its individual cost, shows a confident big number for the monthly total, and notes the equivalent annual figure. Required coverages (liability) stay locked on, while optional add-ons can be switched freely. All pricing math is vanilla JS — no libraries.
Calm blues and a trust-green accent, rounded cards, clear deductible chips and accessible labels and focus styles make the choices feel safe and easy to scan.
Illustrative UI only — not a real insurance quote.