Nonprofit — Giving Toggle
A warm, donor-ready giving toggle that lets supporters switch between one-time and monthly gifts with a single segmented control. Choosing monthly surfaces a most-impact badge, rewrites the supporting copy, relabels every preset amount, and projects an annual total alongside a friendly impact line. Preset chips and a custom amount field stay in sync, an animated progress thermometer anchors the campaign, and trust badges plus recent donors reinforce transparency — all self-contained vanilla HTML, CSS and JavaScript.
MCP
Code
:root {
--brand: #1f7a6d;
--brand-d: #155e54;
--accent: #e8743b;
--accent-d: #cc5d28;
--ink: #2a2722;
--ink-2: #524d44;
--muted: #7a7368;
--bg: #faf6f0;
--surface: #ffffff;
--line: rgba(42, 39, 34, 0.1);
--line-2: rgba(42, 39, 34, 0.18);
--ok: #2f9e6f;
--warn: #d98a2b;
--danger: #d4503e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--sh-sm: 0 1px 2px rgba(42, 39, 34, 0.06), 0 2px 8px rgba(42, 39, 34, 0.05);
--sh-md: 0 8px 28px rgba(42, 39, 34, 0.1);
--sh-lg: 0 18px 50px rgba(42, 39, 34, 0.16);
}
* { box-sizing: border-box; }
html { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: var(--ink);
background:
radial-gradient(1200px 600px at 85% -10%, rgba(232, 116, 59, 0.08), transparent 60%),
radial-gradient(900px 500px at -5% 110%, rgba(31, 122, 109, 0.08), transparent 55%),
var(--bg);
min-height: 100vh;
padding: clamp(16px, 4vw, 48px);
display: flex;
align-items: center;
justify-content: center;
}
h1, h2, h3 {
font-family: "Fraunces", Georgia, serif;
font-weight: 600;
color: var(--ink);
line-height: 1.15;
margin: 0;
}
.give {
width: 100%;
max-width: 960px;
display: grid;
grid-template-columns: 1fr 0.92fr;
gap: clamp(16px, 2.4vw, 28px);
align-items: stretch;
}
/* ---------- Impact rail ---------- */
.rail {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-md);
overflow: hidden;
display: flex;
flex-direction: column;
}
.rail__photo {
position: relative;
min-height: 200px;
background:
linear-gradient(150deg, var(--brand) 0%, var(--brand-d) 55%, #0f4a42 100%);
display: flex;
align-items: flex-end;
}
.rail__photo::before {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(180px 180px at 78% 28%, rgba(232, 196, 120, 0.5), transparent 70%),
radial-gradient(220px 160px at 18% 75%, rgba(255, 255, 255, 0.18), transparent 70%);
mix-blend-mode: screen;
}
.rail__caption {
position: relative;
margin: 14px;
padding: 6px 12px;
font-size: 0.78rem;
font-weight: 600;
color: #fff;
background: rgba(21, 94, 84, 0.55);
backdrop-filter: blur(4px);
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 999px;
}
.rail__body { padding: clamp(18px, 2.4vw, 26px); display: flex; flex-direction: column; gap: 14px; }
.eyebrow {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--brand);
}
.rail h1 { font-size: clamp(1.45rem, 3.2vw, 1.95rem); }
.lede { margin: 0; color: var(--ink-2); font-size: 0.96rem; }
/* thermometer */
.thermo { background: #fbf7f1; border: 1px solid var(--line); border-radius: var(--r-md); padding: 14px; }
.thermo__head { display: flex; align-items: baseline; gap: 8px; }
.thermo__head strong { font-family: "Fraunces", serif; font-size: 1.35rem; color: var(--brand-d); }
.thermo__head span { color: var(--muted); font-size: 0.85rem; }
.thermo__track {
margin: 9px 0 6px;
height: 12px;
border-radius: 999px;
background: rgba(42, 39, 34, 0.08);
overflow: hidden;
}
.thermo__fill {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, var(--brand), var(--ok));
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.thermo__meta { display: flex; justify-content: space-between; font-size: 0.8rem; color: var(--muted); font-weight: 600; }
.impacts { list-style: none; margin: 2px 0 0; padding: 0; display: grid; gap: 8px; }
.impacts li {
display: flex;
align-items: baseline;
gap: 10px;
padding: 9px 12px;
background: #fbf7f1;
border: 1px solid var(--line);
border-radius: var(--r-sm);
font-size: 0.88rem;
}
.impacts strong { font-family: "Fraunces", serif; color: var(--accent-d); white-space: nowrap; }
.impacts span { color: var(--ink-2); }
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.badge {
font-size: 0.7rem;
font-weight: 600;
color: var(--brand-d);
background: rgba(31, 122, 109, 0.08);
border: 1px solid rgba(31, 122, 109, 0.2);
padding: 4px 9px;
border-radius: 999px;
}
/* ---------- Donation card ---------- */
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-lg);
padding: clamp(18px, 2.6vw, 28px);
display: flex;
flex-direction: column;
gap: 16px;
}
.card__head h2 { font-size: clamp(1.3rem, 2.8vw, 1.6rem); }
.card__head p { margin: 5px 0 0; color: var(--ink-2); font-size: 0.92rem; }
/* segmented control */
.seg {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
padding: 5px;
background: #f3ede4;
border: 1px solid var(--line);
border-radius: 999px;
}
.seg__pill {
position: absolute;
top: 5px;
left: 5px;
width: calc(50% - 5px - 2px);
height: calc(100% - 10px);
background: var(--surface);
border-radius: 999px;
box-shadow: var(--sh-sm);
transition: transform 0.32s cubic-bezier(0.34, 1.4, 0.5, 1);
z-index: 0;
}
.seg.is-once [data-pill] { transform: translateX(calc(100% + 4px)); }
.seg__opt {
position: relative;
z-index: 1;
appearance: none;
border: 0;
background: transparent;
cursor: pointer;
font: inherit;
font-weight: 600;
color: var(--ink-2);
padding: 11px 8px;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: color 0.25s;
}
.seg__opt.is-active { color: var(--brand-d); }
.seg__opt:focus-visible { outline: 3px solid rgba(31, 122, 109, 0.4); outline-offset: 2px; }
.seg__badge {
font-size: 0.64rem;
font-weight: 700;
letter-spacing: 0.03em;
text-transform: uppercase;
color: #fff;
background: var(--accent);
padding: 3px 7px;
border-radius: 999px;
box-shadow: 0 2px 6px rgba(232, 116, 59, 0.4);
transition: transform 0.25s, opacity 0.25s;
}
.seg.is-once .seg__badge { opacity: 0.45; transform: scale(0.92); }
.freqcopy {
margin: -4px 0 0;
font-size: 0.88rem;
color: var(--ink-2);
background: rgba(31, 122, 109, 0.06);
border-left: 3px solid var(--brand);
padding: 9px 12px;
border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
/* amount presets */
.amounts { border: 0; margin: 0; padding: 0; }
.amounts legend {
padding: 0;
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 9px;
}
.amounts__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.amt {
appearance: none;
cursor: pointer;
font: inherit;
background: var(--surface);
border: 1.5px solid var(--line-2);
border-radius: var(--r-md);
padding: 12px 6px;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
transition: transform 0.15s, border-color 0.2s, background 0.2s, box-shadow 0.2s;
}
.amt:hover { border-color: var(--brand); transform: translateY(-2px); box-shadow: var(--sh-sm); }
.amt:active { transform: translateY(0); }
.amt.is-active {
border-color: var(--brand);
background: rgba(31, 122, 109, 0.07);
box-shadow: 0 0 0 3px rgba(31, 122, 109, 0.14);
}
.amt:focus-visible { outline: 3px solid rgba(31, 122, 109, 0.4); outline-offset: 2px; }
.amt__val { font-family: "Fraunces", serif; font-weight: 600; font-size: 1.15rem; color: var(--ink); }
.amt__sub { font-size: 0.68rem; color: var(--muted); }
.amt-custom {
margin-top: 9px;
display: flex;
align-items: center;
border: 1.5px solid var(--line-2);
border-radius: var(--r-md);
padding: 0 12px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.amt-custom:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(31, 122, 109, 0.14); }
.amt-custom__sym { font-family: "Fraunces", serif; font-weight: 600; color: var(--muted); }
.amt-custom input {
flex: 1;
border: 0;
outline: 0;
background: transparent;
font: inherit;
font-weight: 600;
padding: 11px 8px;
color: var(--ink);
}
/* impact line */
.impactline {
margin: 0;
font-size: 0.92rem;
color: var(--brand-d);
font-weight: 500;
}
.impactline strong { color: var(--accent-d); }
/* annual preview */
.preview {
background: linear-gradient(160deg, #fbf7f1, #f6efe5);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 14px 16px;
display: grid;
gap: 7px;
}
.preview__row { display: flex; justify-content: space-between; align-items: baseline; font-size: 0.9rem; color: var(--ink-2); }
.preview__row strong { color: var(--ink); font-weight: 600; }
.preview__row--total {
border-top: 1px dashed var(--line-2);
padding-top: 9px;
margin-top: 1px;
}
.preview__row--total span { font-weight: 600; color: var(--ink); }
.preview__row--total strong {
font-family: "Fraunces", serif;
font-size: 1.3rem;
color: var(--brand-d);
}
.preview__note { font-size: 0.8rem; color: var(--muted); }
/* donate CTA */
.donate {
appearance: none;
cursor: pointer;
border: 0;
font: inherit;
font-weight: 700;
font-size: 1.05rem;
color: #fff;
background: linear-gradient(180deg, var(--accent), var(--accent-d));
padding: 15px 18px;
border-radius: var(--r-md);
box-shadow: 0 8px 22px rgba(232, 116, 59, 0.35);
transition: transform 0.15s, box-shadow 0.2s, filter 0.2s;
}
.donate:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(232, 116, 59, 0.42); filter: brightness(1.03); }
.donate:active { transform: translateY(0); }
.donate:focus-visible { outline: 3px solid rgba(232, 116, 59, 0.5); outline-offset: 3px; }
.reassure {
margin: -6px 0 0;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-size: 0.76rem;
color: var(--muted);
text-align: center;
}
.reassure svg { color: var(--brand); flex: none; }
/* donors */
.donors { border-top: 1px solid var(--line); padding-top: 14px; }
.donors__title {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
.donors ul { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 7px; }
.donors li { display: flex; align-items: center; gap: 9px; font-size: 0.86rem; color: var(--ink-2); }
.donors em { font-style: normal; color: var(--brand-d); font-weight: 600; margin-left: auto; }
.av {
width: 26px;
height: 26px;
border-radius: 50%;
background: var(--c, var(--brand));
color: #fff;
display: grid;
place-items: center;
font-size: 0.78rem;
font-weight: 700;
flex: none;
}
/* toast */
.toast {
position: fixed;
left: 50%;
bottom: 24px;
transform: translate(-50%, 130%);
background: var(--ink);
color: #fff;
font-size: 0.88rem;
font-weight: 500;
padding: 12px 18px;
border-radius: 999px;
box-shadow: var(--sh-lg);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.34, 1.4, 0.5, 1), opacity 0.3s;
z-index: 50;
max-width: 88vw;
text-align: center;
}
.toast.is-show { transform: translate(-50%, 0); opacity: 1; }
/* ---------- Responsive ---------- */
@media (max-width: 820px) {
.give { grid-template-columns: 1fr; max-width: 480px; }
}
@media (max-width: 520px) {
body { padding: 14px; }
.amounts__grid { grid-template-columns: repeat(2, 1fr); }
.rail h1 { font-size: 1.4rem; }
.seg__opt { flex-direction: column; gap: 3px; padding: 9px 6px; }
.preview__row--total strong { font-size: 1.15rem; }
}(function () {
"use strict";
// ---- State ----
var state = {
freq: "monthly", // "monthly" | "once"
amount: 25,
};
// ---- Elements ----
var seg = document.querySelector(".seg");
var freqOpts = Array.prototype.slice.call(document.querySelectorAll(".seg__opt"));
var freqCopyEl = document.querySelector("[data-freqcopy]");
var amountBtns = Array.prototype.slice.call(document.querySelectorAll(".amt"));
var subEls = Array.prototype.slice.call(document.querySelectorAll("[data-sub]"));
var customInput = document.querySelector("[data-custom]");
var impactEl = document.querySelector("[data-impact]");
var previewEach = document.querySelector("[data-preview-each]");
var previewAnnual = document.querySelector("[data-preview-annual]");
var previewLabel = document.querySelector("[data-preview-label]");
var previewNote = document.querySelector("[data-preview-note]");
var donateAmtEl = document.querySelector("[data-donate-amt]");
var donateBtn = document.querySelector("[data-donate]");
var toastEl = document.querySelector("[data-toast]");
// $25/mo funds clean water for ~1 family/year. Annual gift / 25 ≈ families reached.
var FAMILY_PER_YEAR = 300; // $ per family per year
var copy = {
monthly:
"Monthly gifts keep wells maintained year-round — you can pause or cancel anytime.",
once:
"A one-time gift makes an immediate splash. Consider monthly to fund the upkeep that keeps water flowing.",
};
// ---- Helpers ----
function fmt(n) {
return "$" + Number(n).toLocaleString("en-US", { maximumFractionDigits: 0 });
}
function getAmount() {
var n = parseFloat(state.amount);
return isNaN(n) || n <= 0 ? 0 : n;
}
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("is-show");
}, 3200);
}
// ---- Render ----
function render() {
var monthly = state.freq === "monthly";
var amt = getAmount();
// segmented control visual + a11y
seg.classList.toggle("is-once", !monthly);
freqOpts.forEach(function (b) {
var active = b.getAttribute("data-freq") === state.freq;
b.classList.toggle("is-active", active);
b.setAttribute("aria-checked", active ? "true" : "false");
});
// frequency copy
freqCopyEl.textContent = copy[state.freq];
// preset sub-labels ("per month" vs "one-time")
subEls.forEach(function (el) {
el.textContent = monthly ? "per month" : "one-time";
});
// impact line
if (amt <= 0) {
impactEl.innerHTML = "Enter an amount to see your impact.";
} else if (monthly) {
var litres = Math.round((amt / 25) * 9000);
impactEl.innerHTML =
"Provides <strong>" +
litres.toLocaleString("en-US") +
" litres</strong> of clean water every month.";
} else {
impactEl.innerHTML =
"Funds <strong>" +
Math.max(1, Math.round((amt / 25) * 380)).toLocaleString("en-US") +
" litres</strong> toward the next community well.";
}
// annual projection
var annual = monthly ? amt * 12 : amt;
previewLabel.textContent = monthly ? "Your monthly gift" : "Your one-time gift";
previewEach.textContent = monthly ? fmt(amt) + " / month" : fmt(amt) + " once";
var totalRow = previewAnnual.closest(".preview__row");
if (totalRow) {
totalRow.querySelector("span").textContent = monthly
? "Projected over 12 months"
: "Total gift";
}
previewAnnual.textContent = fmt(annual);
var families = annual / FAMILY_PER_YEAR;
if (amt <= 0) {
previewNote.textContent = "Choose an amount to see your yearly reach.";
} else if (monthly) {
previewNote.textContent =
"That's clean water for ~" +
(families < 1 ? "<1" : Math.round(families)) +
" famil" +
(Math.round(families) === 1 ? "y" : "ies") +
" for a full year.";
} else {
previewNote.textContent =
"Roughly " +
Math.max(1, Math.round(annual / 65)) +
" people gain safe water access today.";
}
// donate button
donateAmtEl.textContent = monthly ? fmt(amt) + "/mo" : fmt(amt);
donateBtn.disabled = amt <= 0;
donateBtn.style.opacity = amt <= 0 ? "0.55" : "";
donateBtn.style.cursor = amt <= 0 ? "not-allowed" : "";
}
// ---- Events: frequency ----
function setFreq(freq) {
if (state.freq === freq) return;
state.freq = freq;
render();
}
freqOpts.forEach(function (btn) {
btn.addEventListener("click", function () {
setFreq(btn.getAttribute("data-freq"));
});
// arrow-key navigation for the radiogroup
btn.addEventListener("keydown", function (e) {
if (e.key === "ArrowRight" || e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "ArrowDown") {
e.preventDefault();
var other = freqOpts.find(function (b) { return b !== btn; });
if (other) { other.focus(); setFreq(other.getAttribute("data-freq")); }
}
});
});
// ---- Events: presets ----
function selectPreset(btn) {
amountBtns.forEach(function (b) {
var on = b === btn;
b.classList.toggle("is-active", on);
b.setAttribute("aria-pressed", on ? "true" : "false");
});
state.amount = parseFloat(btn.getAttribute("data-amount"));
if (customInput) customInput.value = "";
render();
}
amountBtns.forEach(function (btn) {
btn.addEventListener("click", function () { selectPreset(btn); });
});
// ---- Events: custom amount ----
if (customInput) {
customInput.addEventListener("input", function () {
var v = parseFloat(customInput.value);
if (customInput.value === "") {
// fall back to last active preset, or default
var active = amountBtns.find(function (b) { return b.classList.contains("is-active"); });
state.amount = active ? parseFloat(active.getAttribute("data-amount")) : 25;
render();
return;
}
amountBtns.forEach(function (b) {
b.classList.remove("is-active");
b.setAttribute("aria-pressed", "false");
});
state.amount = isNaN(v) ? 0 : v;
render();
});
}
// ---- Events: donate ----
donateBtn.addEventListener("click", function () {
var amt = getAmount();
if (amt <= 0) { toast("Please enter an amount to continue."); return; }
var msg =
state.freq === "monthly"
? "Thank you! Your " + fmt(amt) + "/month gift is ready to confirm 💧"
: "Thank you! Your one-time " + fmt(amt) + " gift is ready to confirm 💧";
toast(msg);
});
// ---- Init ----
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Bright Wells Foundation — Make a Gift</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=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="give">
<!-- Left: mission / impact rail -->
<aside class="rail" aria-label="Campaign impact">
<div class="rail__photo" role="img" aria-label="Volunteers installing a clean-water pump in a rural village">
<span class="rail__caption">Naila's village now drinks clean water — 2025</span>
</div>
<div class="rail__body">
<span class="eyebrow">Clean Water Campaign</span>
<h1>Give the gift of water that never runs dry.</h1>
<p class="lede">A recurring gift funds the wells, repairs and caretakers that keep water flowing — long after a single donation would run out.</p>
<div class="thermo" role="group" aria-label="Campaign progress">
<div class="thermo__head">
<strong>$184,200</strong>
<span>raised of $250,000</span>
</div>
<div class="thermo__track" role="progressbar" aria-valuenow="74" aria-valuemin="0" aria-valuemax="100" aria-label="74 percent funded">
<div class="thermo__fill" style="width:74%"></div>
</div>
<div class="thermo__meta"><span>74% funded</span><span>2,914 donors</span></div>
</div>
<ul class="impacts">
<li><strong>1 well</strong><span>serves 380 people</span></li>
<li><strong>$25/mo</strong><span>= clean water for a family, all year</span></li>
<li><strong>96¢</strong><span>of every $1 reaches the field</span></li>
</ul>
<div class="badges">
<span class="badge">★ Registered Charity #84-2910</span>
<span class="badge">Tax-deductible</span>
<span class="badge">Top-rated transparency</span>
</div>
</div>
</aside>
<!-- Right: donation card -->
<section class="card" aria-label="Donation form">
<header class="card__head">
<h2>Choose how you'll give</h2>
<p>Monthly donors provide the steady funding our field teams plan around.</p>
</header>
<!-- Frequency segmented control -->
<div class="seg" role="radiogroup" aria-label="Giving frequency">
<span class="seg__pill" data-pill aria-hidden="true"></span>
<button class="seg__opt is-active" role="radio" aria-checked="true" data-freq="monthly" id="freq-monthly">
Monthly
<span class="seg__badge">★ Most impact</span>
</button>
<button class="seg__opt" role="radio" aria-checked="false" data-freq="once" id="freq-once">
One-time
</button>
</div>
<p class="freqcopy" data-freqcopy aria-live="polite">
Monthly gifts keep wells maintained year-round — you can pause or cancel anytime.
</p>
<!-- Amount presets -->
<fieldset class="amounts">
<legend>Select an amount</legend>
<div class="amounts__grid" data-amounts>
<button class="amt" data-amount="10"><span class="amt__val">$10</span><span class="amt__sub" data-sub>per month</span></button>
<button class="amt is-active" data-amount="25" aria-pressed="true"><span class="amt__val">$25</span><span class="amt__sub" data-sub>per month</span></button>
<button class="amt" data-amount="50"><span class="amt__val">$50</span><span class="amt__sub" data-sub>per month</span></button>
<button class="amt" data-amount="100"><span class="amt__val">$100</span><span class="amt__sub" data-sub>per month</span></button>
</div>
<label class="amt-custom">
<span class="amt-custom__sym">$</span>
<input type="number" inputmode="decimal" min="1" step="1" placeholder="Other amount" data-custom aria-label="Custom amount" />
</label>
</fieldset>
<!-- Dynamic impact line -->
<p class="impactline" data-impact aria-live="polite"></p>
<!-- Annual projection preview -->
<div class="preview" data-preview>
<div class="preview__row">
<span data-preview-label>Your gift</span>
<strong data-preview-each>$25 / month</strong>
</div>
<div class="preview__row preview__row--total">
<span>Projected over 12 months</span>
<strong data-preview-annual>$300</strong>
</div>
<div class="preview__note" data-preview-note>That's ~12 families with clean water for a year.</div>
</div>
<button class="donate" data-donate>
Donate <span data-donate-amt>$25/mo</span>
</button>
<p class="reassure">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 2l8 3v6c0 5-3.4 9-8 11-4.6-2-8-6-8-11V5l8-3z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>
Secure giving · Cancel a recurring gift anytime · 100% transparency promise
</p>
<div class="donors">
<span class="donors__title">Recent donors</span>
<ul>
<li><span class="av" style="--c:#1f7a6d">M</span> Marisol G. — <em>$25/mo</em></li>
<li><span class="av" style="--c:#e8743b">T</span> Theo R. — <em>$100/mo</em></li>
<li><span class="av" style="--c:#2f9e6f">A</span> Aanya P. — <em>$50 once</em></li>
</ul>
</div>
</section>
</main>
<div class="toast" data-toast role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Giving Toggle
A donation card for the fictional Bright Wells Foundation built around a single, high-stakes decision: give once or give monthly. The frequency control is a pill-style segmented switch with a sliding indicator and a “Most impact” badge pinned to the monthly option. Flipping it rewrites the supporting copy, relabels every preset amount (“per month” vs “one-time”), recomputes the dynamic impact line, and refreshes the donate button label — so the consequences of the choice are always visible.
Four preset amounts and a custom input stay perfectly in sync: picking a chip clears the custom field, typing a custom amount deselects the chips, and clearing the field gracefully falls back to the last preset. The annual-projection panel multiplies a monthly amount by twelve to preview the real yearly commitment, then translates it into human terms (“clean water for ~12 families for a full year”). A campaign thermometer, registered-charity badges, and a recent-donors list round out the trust signals.
Everything is keyboard-usable and announced via aria-live regions, the radiogroup supports arrow-key navigation, and a small toast() helper confirms the gift on submit. No frameworks, no build step — just three files.
Illustrative UI only — fictional organization, not a real charity or donation system.