Open demo

CSS code

HTML
<div><span class="lbl">Inline band</span>
<section class="nl nl-band" aria-labelledby="sc-title">
  <div>
    <p class="nl-kicker">The Sunday Crate</p>
    <h2 id="sc-title">Five records worth your Sunday</h2>
    <p>One short email a week: new pressings, reissues and one deep cut from the shop floor.</p>
  </div>
  <div>
    <form class="nl-form" action="#" method="post" data-subscribe>
      <label class="nl-label" for="sc-email">Email address</label>
      <div class="nl-pill">
        <input class="nl-input" id="sc-email" name="email" type="email" autocomplete="email" required aria-describedby="sc-hint">
        <button class="nl-btn" type="submit">Subscribe</button>
      </div>
      <p class="nl-hint" id="sc-hint">Required. Unsubscribe from any email in one click.</p>
    </form>
    <div class="nl-done" tabindex="-1" data-done hidden>
      <span class="nl-check" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
      <h3>You're on the list</h3>
      <p>The first crate lands in <span data-email>your inbox</span> on Sunday morning.</p>
    </div>
  </div>
</section>
</div>
<div class="demo-grid">
<div><span class="lbl">Waitlist card</span>
<section class="nl nl-card" aria-labelledby="od-title">
  <span class="nl-badge">Shipping spring 2026</span>
  <h2 id="od-title">Orbit Desk waitlist</h2>
  <p>A standing desk that remembers your heights and nudges you to stand.</p>
  <form class="nl-form" action="#" method="post" data-subscribe>
    <label class="nl-label" for="od-email">Email address</label>
    <div class="nl-row">
      <input class="nl-input" id="od-email" name="email" type="email" autocomplete="email" required aria-describedby="od-hint">
      <button class="nl-btn" type="submit">Join waitlist</button>
    </div>
    <p class="nl-hint" id="od-hint">Required. One email when your order window opens, nothing else.</p>
  </form>
  <div class="nl-done" tabindex="-1" data-done hidden>
    <span class="nl-check" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
    <h3>You're on the waitlist</h3>
    <p>We'll write to <b data-email>you</b> when your order window opens.</p>
  </div>
  <div class="nl-people"><span class="nl-faces" aria-hidden="true"><span>AK</span><span>JM</span><span>RS</span></span>2,140 people are waiting</div>
</section>
</div>
<div><span class="lbl">Waitlist card, confirmed</span>
<section class="nl nl-card" aria-labelledby="od2-title">
  <span class="nl-badge">Shipping spring 2026</span>
  <h2 id="od2-title">Orbit Desk waitlist</h2>
  <p>A standing desk that remembers your heights and nudges you to stand.</p>
  <div class="nl-done" tabindex="-1">
    <span class="nl-check" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
    <h3>You're on the waitlist</h3>
    <p>We'll write to <b>[email protected]</b> when your order window opens.</p>
  </div>
  <div class="nl-people"><span class="nl-faces" aria-hidden="true"><span>AK</span><span>JM</span><span>RS</span></span>2,141 people are waiting</div>
</section>
</div>
</div>
CSS
/* Newsletter and waitlist sign-ups: one visible label, one email field, one button. The browser validates the
   address; a few lines of script then swap the form for a confirmation and move focus to it. */
.nl{--nl-ink:#18181b;--nl-muted:#52525b;--nl-line:#71717a;--nl-accent:#18181b;--nl-accent-text:#fff;--nl-ring:rgba(24,24,27,.2);--nl-error:#b91c1c;
  color:var(--nl-ink);font-size:.92rem;line-height:1.45}
.nl-label{display:block;font-size:.84rem;font-weight:600;margin-bottom:6px}
.nl-input{display:block;width:100%;min-width:0;font:inherit;color:inherit;background:#fff;border:1px solid var(--nl-line);border-radius:10px;padding:11px 14px;
  transition:border-color .15s,box-shadow .15s}
.nl-input:focus{outline:none;border-color:var(--nl-accent);box-shadow:0 0 0 3px var(--nl-ring)}
.nl-input:user-invalid{border-color:var(--nl-error)}
.nl-btn{flex:none;font:inherit;font-weight:650;color:var(--nl-accent-text);background:var(--nl-accent);border:0;border-radius:10px;padding:12px 20px;cursor:pointer;
  transition:filter .15s,transform .15s}
.nl-btn:hover{filter:brightness(1.12)}
.nl-btn:active{transform:translateY(1px)}
.nl-btn:focus-visible{outline:2px solid var(--nl-accent);outline-offset:3px}
.nl-hint{font-size:.8rem;color:var(--nl-muted);margin-top:8px}
.nl-done{outline:none}
.nl-done:focus-visible{outline:2px solid var(--nl-accent);outline-offset:6px;border-radius:8px}
.nl-check{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#dcfce7;color:#15803d;margin-bottom:10px}
/* 1. inline band: pitch on the left, field and button joined in one pill */
.nl-band{display:grid;grid-template-columns:1fr 1.1fr;gap:28px;align-items:center;background:#18181b;color:#fafafa;border-radius:22px;padding:32px 34px;
  --nl-accent:#facc15;--nl-accent-text:#18181b;--nl-ring:rgba(250,204,21,.35);--nl-muted:#c4c4cc}
.nl-band .nl-kicker{font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:#facc15}
.nl-band h2{font-size:1.45rem;font-weight:750;letter-spacing:-.02em;line-height:1.2;margin:4px 0 6px}
.nl-band p{color:#c4c4cc}
.nl-pill{display:flex;gap:6px;background:#fff;border-radius:999px;padding:5px;border:1px solid #fff}
.nl-pill:focus-within{box-shadow:0 0 0 3px var(--nl-ring)}
.nl-pill .nl-input,.nl-pill .nl-input:focus{border:0;box-shadow:none;border-radius:999px;color:var(--nl-ink);padding:10px 14px}
.nl-pill .nl-btn{border-radius:999px}
.nl-band .nl-label{color:#fafafa}
.nl-band .nl-done h3{font-size:1.1rem}
.nl-band .nl-check{background:#facc15;color:#18181b}
/* 2. waitlist card */
.nl-card{background:linear-gradient(160deg,#fff 40%,#f3efff);border-radius:22px;padding:30px 30px 26px;text-align:center;
  box-shadow:0 1px 2px rgba(24,24,27,.06),0 16px 36px -16px rgba(76,29,149,.28);--nl-accent:#5b21b6;--nl-ring:rgba(91,33,182,.22)}
.nl-badge{display:inline-block;font-size:.74rem;font-weight:700;color:#5b21b6;background:#ede9fe;border-radius:999px;padding:4px 11px}
.nl-card h2{font-size:1.4rem;font-weight:750;letter-spacing:-.02em;line-height:1.2;margin:12px 0 6px}
.nl-card>p,.nl-card .nl-form>p{color:var(--nl-muted)}
.nl-card .nl-form{margin-top:18px;text-align:left}
.nl-row{display:flex;gap:8px}
.nl-row .nl-input{flex:1 1 0}
.nl-people{display:flex;align-items:center;justify-content:center;gap:10px;margin-top:16px;font-size:.8rem;color:var(--nl-muted)}
.nl-faces{display:flex}
.nl-faces span{width:26px;height:26px;border-radius:50%;border:2px solid #fff;margin-left:-8px;display:grid;place-items:center;font-size:.62rem;font-weight:700;color:#fff}
.nl-faces span:first-child{margin-left:0;background:#7c3aed}.nl-faces span:nth-child(2){background:#0e7490}.nl-faces span:nth-child(3){background:#be123c}
.nl-card .nl-done{margin-top:18px;display:flex;flex-direction:column;align-items:center}
.nl-done h3{font-size:1.05rem;font-weight:700}
.nl-done p{color:var(--nl-muted);margin-top:4px}
.nl-card .nl-done p b{color:var(--nl-ink)}
/* keep hidden states hidden whatever the layout rules say */
.nl .nl-done[hidden],.nl .nl-form[hidden]{display:none}
@media (max-width:640px){.nl-band{grid-template-columns:1fr;padding:26px 22px;gap:20px}}
@media (max-width:420px){.nl-card{padding:24px 18px 22px}.nl-row{flex-direction:column}.nl-pill .nl-btn{padding:10px 14px}}
@media (prefers-reduced-motion:reduce){.nl-input,.nl-btn{transition:none}}
JavaScript
// Swap the form for its confirmation once the browser has validated the address. Demo only: nothing is sent;
// replace the body of the handler with your fetch() call. Without the script the form posts normally.
document.querySelectorAll('form[data-subscribe]').forEach(function (form) {
  var done = form.parentElement.querySelector('[data-done]');
  form.addEventListener('submit', function (e) {
    e.preventDefault();
    done.querySelector('[data-email]').textContent = form.elements.email.value;
    form.hidden = true;
    done.hidden = false;
    done.focus();
  });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Inline band</span>
<section class="nl nl-band" aria-labelledby="sc-title">
  <div>
    <p class="nl-kicker">The Sunday Crate</p>
    <h2 id="sc-title">Five records worth your Sunday</h2>
    <p>One short email a week: new pressings, reissues and one deep cut from the shop floor.</p>
  </div>
  <div>
    <form class="nl-form" action="#" method="post" data-subscribe>
      <label class="form-label" for="sc-email">Email address</label>
      <div class="input-group">
        <input class="form-control" id="sc-email" name="email" type="email" autocomplete="email" required aria-describedby="sc-hint">
        <button class="btn nl-btn" type="submit">Subscribe</button>
      </div>
      <p class="form-text" id="sc-hint">Required. Unsubscribe from any email in one click.</p>
    </form>
    <div class="nl-done" tabindex="-1" data-done hidden>
      <span class="nl-check" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
      <h3>You're on the list</h3>
      <p>The first crate lands in <span data-email>your inbox</span> on Sunday morning.</p>
    </div>
  </div>
</section>
</div>
<div class="demo-grid">
<div><span class="lbl">Waitlist card</span>
<section class="card nl nl-card" aria-labelledby="od-title">
  <span class="badge rounded-pill">Shipping spring 2026</span>
  <h2 id="od-title">Orbit Desk waitlist</h2>
  <p>A standing desk that remembers your heights and nudges you to stand.</p>
  <form class="nl-form" action="#" method="post" data-subscribe>
    <label class="form-label" for="od-email">Email address</label>
    <div class="d-flex gap-2 nl-row">
      <input class="form-control" id="od-email" name="email" type="email" autocomplete="email" required aria-describedby="od-hint">
      <button class="btn nl-btn" type="submit">Join waitlist</button>
    </div>
    <p class="form-text" id="od-hint">Required. One email when your order window opens, nothing else.</p>
  </form>
  <div class="nl-done" tabindex="-1" data-done hidden>
    <span class="nl-check" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
    <h3>You're on the waitlist</h3>
    <p>We'll write to <b data-email>you</b> when your order window opens.</p>
  </div>
  <div class="nl-people"><span class="nl-faces" aria-hidden="true"><span>AK</span><span>JM</span><span>RS</span></span>2,140 people are waiting</div>
</section>
</div>
<div><span class="lbl">Waitlist card, confirmed</span>
<section class="card nl nl-card" aria-labelledby="od2-title">
  <span class="badge rounded-pill">Shipping spring 2026</span>
  <h2 id="od2-title">Orbit Desk waitlist</h2>
  <p>A standing desk that remembers your heights and nudges you to stand.</p>
  <div class="nl-done" tabindex="-1">
    <span class="nl-check" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
    <h3>You're on the waitlist</h3>
    <p>We'll write to <b>[email protected]</b> when your order window opens.</p>
  </div>
  <div class="nl-people"><span class="nl-faces" aria-hidden="true"><span>AK</span><span>JM</span><span>RS</span></span>2,141 people are waiting</div>
</section>
</div>
</div>
CSS
/* Bootstrap .input-group, .form-control, .btn, .card and .badge, recoloured through Bootstrap's CSS variables.
   The browser validates the address; a few lines of script then swap the form for a confirmation and focus it. */
.nl{--nl-ink:#18181b;--nl-muted:#52525b;--nl-line:#71717a;--nl-accent:#18181b;--nl-accent-text:#fff;--nl-ring:rgba(24,24,27,.2);--nl-error:#b91c1c;
  --bs-border-color:var(--nl-line);--bs-border-radius:10px;color:var(--nl-ink);font-size:.92rem;line-height:1.45}
.nl p,.nl h2,.nl h3{margin:0}
.nl .form-label{display:block;font-size:.84rem;font-weight:600;margin-bottom:6px}
.nl .form-control{font-size:inherit;line-height:inherit;color:var(--nl-ink);padding:11px 14px;transition:border-color .15s,box-shadow .15s}
.nl .form-control:focus{border-color:var(--nl-accent);box-shadow:0 0 0 3px var(--nl-ring)}
.nl .form-control:user-invalid{border-color:var(--nl-error)}
.nl .nl-btn{--bs-btn-color:var(--nl-accent-text);--bs-btn-bg:var(--nl-accent);--bs-btn-border-color:var(--nl-accent);--bs-btn-hover-color:var(--nl-accent-text);
  --bs-btn-hover-bg:var(--nl-accent);--bs-btn-hover-border-color:var(--nl-accent);--bs-btn-active-color:var(--nl-accent-text);--bs-btn-active-bg:var(--nl-accent);
  --bs-btn-active-border-color:var(--nl-accent);--bs-btn-focus-box-shadow:none;--bs-btn-font-weight:650;--bs-btn-font-size:.92rem;
  --bs-btn-padding-x:20px;--bs-btn-padding-y:11px;--bs-btn-border-radius:10px;line-height:1.45;flex:none;transition:filter .15s,transform .15s}
.nl .nl-btn:hover{filter:brightness(1.12)}
.nl .nl-btn:active{transform:translateY(1px)}
.nl .nl-btn:focus-visible{outline:2px solid var(--nl-accent);outline-offset:3px}
.nl .form-text{font-size:.8rem;color:var(--nl-muted);margin-top:8px}
.nl .nl-done{outline:none}
.nl .nl-done:focus-visible{outline:2px solid var(--nl-accent);outline-offset:6px;border-radius:8px}
.nl .nl-check{display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#dcfce7;color:#15803d;margin-bottom:10px}
/* 1. inline band: .input-group drawn as one pill */
.nl-band{display:grid;grid-template-columns:1fr 1.1fr;gap:28px;align-items:center;background:#18181b;color:#fafafa;border-radius:22px;padding:32px 34px;
  --nl-accent:#facc15;--nl-accent-text:#18181b;--nl-ring:rgba(250,204,21,.35);--nl-muted:#c4c4cc}
.nl-band .nl-kicker{font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:#facc15}
.nl-band h2{font-size:1.45rem;font-weight:750;letter-spacing:-.02em;line-height:1.2;margin:4px 0 6px}
.nl-band p{color:#c4c4cc}
.nl-band .input-group{gap:6px;background:#fff;border-radius:999px;padding:5px;border:1px solid #fff}
.nl-band .input-group:focus-within{box-shadow:0 0 0 3px var(--nl-ring)}
.nl-band .input-group .form-control,.nl-band .input-group .form-control:focus{border:0;box-shadow:none;border-radius:999px!important;padding:10px 14px}
.nl-band .input-group .nl-btn{border-radius:999px!important}
.nl-band .form-label{color:#fafafa}
.nl-band .nl-done h3{font-size:1.1rem}
.nl-band .nl-check{background:#facc15;color:#18181b}
/* 2. waitlist card */
.nl-card{--bs-card-border-width:0;--bs-card-border-radius:22px;background:linear-gradient(160deg,#fff 40%,#f3efff);padding:30px 30px 26px;text-align:center;
  box-shadow:0 1px 2px rgba(24,24,27,.06),0 16px 36px -16px rgba(76,29,149,.28);--nl-accent:#5b21b6;--nl-ring:rgba(91,33,182,.22)}
.nl-card .badge{--bs-badge-font-size:.74rem;--bs-badge-padding-x:11px;--bs-badge-padding-y:4px;color:#5b21b6;background:#ede9fe;align-self:center;line-height:1.45}
.nl-card h2{font-size:1.4rem;font-weight:750;letter-spacing:-.02em;line-height:1.2;margin:12px 0 6px}
.nl-card>p,.nl-card .nl-form>p{color:var(--nl-muted)}
.nl-card .nl-form{margin-top:18px;text-align:left}
.nl-card .nl-row .form-control{flex:1 1 0;min-width:0}
.nl-card .nl-people{display:flex;align-items:center;justify-content:center;gap:10px;margin-top:16px;font-size:.8rem;color:var(--nl-muted)}
.nl-card .nl-faces{display:flex}
.nl-card .nl-faces span{width:26px;height:26px;border-radius:50%;border:2px solid #fff;margin-left:-8px;display:grid;place-items:center;font-size:.62rem;font-weight:700;color:#fff}
.nl-card .nl-faces span:first-child{margin-left:0;background:#7c3aed}.nl-card .nl-faces span:nth-child(2){background:#0e7490}.nl-card .nl-faces span:nth-child(3){background:#be123c}
.nl-card .nl-done{margin-top:18px;display:flex;flex-direction:column;align-items:center}
.nl .nl-done h3{font-size:1.05rem;font-weight:700}
.nl .nl-done p{color:var(--nl-muted);margin-top:4px}
.nl-card .nl-done p b{color:var(--nl-ink)}
.nl .nl-done[hidden],.nl .nl-form[hidden]{display:none}
.card.nl-card{display:block} /* plain block flow, so vertical margins match the CSS version */
@media (max-width:640px){.nl-band{grid-template-columns:1fr;padding:26px 22px;gap:20px}}
@media (max-width:420px){.nl-card{padding:24px 18px 22px}.nl-card .nl-row{flex-direction:column}.nl-band .input-group .nl-btn{--bs-btn-padding-x:14px;--bs-btn-padding-y:9px}}
@media (prefers-reduced-motion:reduce){.nl .form-control,.nl .nl-btn{transition:none}}
JavaScript
// Swap the form for its confirmation once the browser has validated the address. Demo only: nothing is sent;
// replace the body of the handler with your fetch() call. Without the script the form posts normally.
document.querySelectorAll('form[data-subscribe]').forEach(function (form) {
  var done = form.parentElement.querySelector('[data-done]');
  form.addEventListener('submit', function (e) {
    e.preventDefault();
    done.querySelector('[data-email]').textContent = form.elements.email.value;
    form.hidden = true;
    done.hidden = false;
    done.focus();
  });
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5e5a52] uppercase">Inline band</span>
<section class="grid grid-cols-[1fr_1.1fr] items-center gap-7 rounded-[22px] bg-[#18181b] px-[34px] py-8 text-[.92rem] leading-[1.45] text-[#fafafa] [--nl-accent:#facc15] [--nl-accent-text:#18181b] [--nl-muted:#c4c4cc] [--nl-ring:rgba(250,204,21,.35)] max-[640px]:grid-cols-1 max-[640px]:gap-5 max-[640px]:px-[22px] max-[640px]:py-[26px]" aria-labelledby="sc-title">
  <div>
    <p class="text-[.72rem] font-bold tracking-[.12em] text-[#facc15] uppercase">The Sunday Crate</p>
    <h2 class="mt-1 mb-1.5 text-[1.45rem] leading-[1.2] font-[750] tracking-[-.02em]" id="sc-title">Five records worth your Sunday</h2>
    <p class="text-[#c4c4cc]">One short email a week: new pressings, reissues and one deep cut from the shop floor.</p>
  </div>
  <div>
    <form action="#" method="post" data-subscribe>
      <label class="mb-1.5 block text-[.84rem] font-semibold" for="sc-email">Email address</label>
      <div class="flex gap-1.5 rounded-full border border-white bg-white p-[5px] focus-within:shadow-[0_0_0_3px_var(--nl-ring)]">
        <input class="block w-full min-w-0 rounded-full bg-transparent px-3.5 py-2.5 text-nl-ink focus:outline-none" id="sc-email" name="email" type="email" autocomplete="email" required aria-describedby="sc-hint">
        <button class="flex-none cursor-pointer rounded-[10px] bg-(--nl-accent) px-5 py-3 font-[650] text-(--nl-accent-text) transition-[filter,translate] duration-150 hover:brightness-112 active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-(--nl-accent) motion-reduce:transition-none rounded-full max-[420px]:px-3.5 max-[420px]:py-2.5" type="submit">Subscribe</button>
      </div>
      <p class="mt-2 text-[.8rem] text-(--nl-muted)" id="sc-hint">Required. Unsubscribe from any email in one click.</p>
    </form>
    <div class="focus-visible:rounded-lg focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-(--nl-accent)" tabindex="-1" data-done hidden>
      <span class="mb-2.5 grid size-10 place-items-center rounded-full bg-[#facc15] text-[#18181b]" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
      <h3 class="text-[1.1rem] font-bold">You're on the list</h3>
      <p class="mt-1 text-(--nl-muted)">The first crate lands in <span data-email>your inbox</span> on Sunday morning.</p>
    </div>
  </div>
</section>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] items-start gap-[34px]">
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5e5a52] uppercase">Waitlist card</span>
<section class="rounded-[22px] bg-[linear-gradient(160deg,#fff_40%,#f3efff)] px-[30px] pt-[30px] pb-[26px] text-center shadow-[0_1px_2px_rgba(24,24,27,.06),0_16px_36px_-16px_rgba(76,29,149,.28)] [--nl-accent:#5b21b6] [--nl-accent-text:#fff] [--nl-ring:rgba(91,33,182,.22)] [--nl-muted:#52525b] max-[420px]:px-[18px] max-[420px]:pt-6 max-[420px]:pb-[22px] text-[.92rem] leading-[1.45] text-nl-ink" aria-labelledby="od-title">
  <span class="inline-block rounded-full bg-[#ede9fe] px-[11px] py-1 text-[.74rem] font-bold text-[#5b21b6]">Shipping spring 2026</span>
  <h2 class="mt-3 mb-1.5 text-[1.4rem] leading-[1.2] font-[750] tracking-[-.02em]" id="od-title">Orbit Desk waitlist</h2>
  <p class="text-(--nl-muted)">A standing desk that remembers your heights and nudges you to stand.</p>
  <form class="mt-[18px] text-left" action="#" method="post" data-subscribe>
    <label class="mb-1.5 block text-[.84rem] font-semibold" for="od-email">Email address</label>
    <div class="flex gap-2 max-[420px]:flex-col">
      <input class="block w-full min-w-0 rounded-[10px] border border-nl-line bg-white px-3.5 py-[11px] text-nl-ink transition-[border-color,box-shadow] duration-150 focus:border-(--nl-accent) focus:shadow-[0_0_0_3px_var(--nl-ring)] focus:outline-none user-invalid:border-nl-error motion-reduce:transition-none flex-1 basis-0" id="od-email" name="email" type="email" autocomplete="email" required aria-describedby="od-hint">
      <button class="flex-none cursor-pointer rounded-[10px] bg-(--nl-accent) px-5 py-3 font-[650] text-(--nl-accent-text) transition-[filter,translate] duration-150 hover:brightness-112 active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-(--nl-accent) motion-reduce:transition-none" type="submit">Join waitlist</button>
    </div>
    <p class="mt-2 text-[.8rem] text-(--nl-muted)" id="od-hint">Required. One email when your order window opens, nothing else.</p>
  </form>
  <div class="mt-[18px] flex flex-col items-center focus-visible:rounded-lg focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-(--nl-accent)" tabindex="-1" data-done hidden>
    <span class="mb-2.5 grid size-10 place-items-center rounded-full bg-[#dcfce7] text-[#15803d]" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
    <h3 class="text-[1.05rem] font-bold">You're on the waitlist</h3>
    <p class="mt-1 text-(--nl-muted)">We'll write to <b class="text-nl-ink" data-email>you</b> when your order window opens.</p>
  </div>
  <div class="mt-4 flex items-center justify-center gap-2.5 text-[.8rem] text-(--nl-muted)"><span class="flex" aria-hidden="true"><span class="grid size-[26px] place-items-center rounded-full border-2 border-white bg-[#7c3aed] text-[.62rem] font-bold text-white">AK</span><span class="-ml-2 grid size-[26px] place-items-center rounded-full border-2 border-white bg-[#0e7490] text-[.62rem] font-bold text-white">JM</span><span class="-ml-2 grid size-[26px] place-items-center rounded-full border-2 border-white bg-[#be123c] text-[.62rem] font-bold text-white">RS</span></span>2,140 people are waiting</div>
</section>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5e5a52] uppercase">Waitlist card, confirmed</span>
<section class="rounded-[22px] bg-[linear-gradient(160deg,#fff_40%,#f3efff)] px-[30px] pt-[30px] pb-[26px] text-center shadow-[0_1px_2px_rgba(24,24,27,.06),0_16px_36px_-16px_rgba(76,29,149,.28)] [--nl-accent:#5b21b6] [--nl-accent-text:#fff] [--nl-ring:rgba(91,33,182,.22)] [--nl-muted:#52525b] max-[420px]:px-[18px] max-[420px]:pt-6 max-[420px]:pb-[22px] text-[.92rem] leading-[1.45] text-nl-ink" aria-labelledby="od2-title">
  <span class="inline-block rounded-full bg-[#ede9fe] px-[11px] py-1 text-[.74rem] font-bold text-[#5b21b6]">Shipping spring 2026</span>
  <h2 class="mt-3 mb-1.5 text-[1.4rem] leading-[1.2] font-[750] tracking-[-.02em]" id="od2-title">Orbit Desk waitlist</h2>
  <p class="text-(--nl-muted)">A standing desk that remembers your heights and nudges you to stand.</p>
  <div class="mt-[18px] flex flex-col items-center focus-visible:rounded-lg focus-visible:outline-2 focus-visible:outline-offset-6 focus-visible:outline-(--nl-accent)" tabindex="-1">
    <span class="mb-2.5 grid size-10 place-items-center rounded-full bg-[#dcfce7] text-[#15803d]" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>
    <h3 class="text-[1.05rem] font-bold">You're on the waitlist</h3>
    <p class="mt-1 text-(--nl-muted)">We'll write to <b class="text-nl-ink">[email protected]</b> when your order window opens.</p>
  </div>
  <div class="mt-4 flex items-center justify-center gap-2.5 text-[.8rem] text-(--nl-muted)"><span class="flex" aria-hidden="true"><span class="grid size-[26px] place-items-center rounded-full border-2 border-white bg-[#7c3aed] text-[.62rem] font-bold text-white">AK</span><span class="-ml-2 grid size-[26px] place-items-center rounded-full border-2 border-white bg-[#0e7490] text-[.62rem] font-bold text-white">JM</span><span class="-ml-2 grid size-[26px] place-items-center rounded-full border-2 border-white bg-[#be123c] text-[.62rem] font-bold text-white">RS</span></span>2,141 people are waiting</div>
</section>
</div>
</div>
CSS
@theme {
  --color-nl-ink: #18181b;
  --color-nl-line: #71717a;
  --color-nl-error: #b91c1c;
}
JavaScript
// Swap the form for its confirmation once the browser has validated the address. Demo only: nothing is sent;
// replace the body of the handler with your fetch() call. Without the script the form posts normally.
document.querySelectorAll('form[data-subscribe]').forEach(function (form) {
  var done = form.parentElement.querySelector('[data-done]');
  form.addEventListener('submit', function (e) {
    e.preventDefault();
    done.querySelector('[data-email]').textContent = form.elements.email.value;
    form.hidden = true;
    done.hidden = false;
    done.focus();
  });
});

About this set

Single-field sign-ups for newsletters and launch waitlists, each with its confirmation state. Inline band is a dark strip for a record shop’s weekly email: a pitch on the left and, on the right, a visible Email address label above a white pill that joins the field and a yellow Subscribe button. Waitlist card is a centred card for a standing desk launch with a shipping badge, a one-line pitch, the email field and Join waitlist button side by side, and a row of initials with a waiting count. Waitlist card, confirmed shows the same card after submitting, so you can style the success state without filling the form in. Use the band in page footers or between articles and the card on launch and coming-soon pages. The browser validates the email address first; a few lines of script then hide the form, write the address into the confirmation, reveal it and move focus to it so screen readers read the result. Replace the body of the submit handler with your own request. Without JavaScript the form posts normally. On narrow phones the waitlist field and button stack.

What’s included

  • Inline band with a joined pill input and a centred waitlist card
  • Confirmation state with the submitted address, also shown on its own
  • Visible label on every single-field form, no placeholder-as-label
  • Focus moves to the confirmation after a successful submit
  • Bootstrap version uses .input-group, .card, .badge and .form-text

Accessibility

Each email field has a visible label, autocomplete="email" and a hint linked with aria-describedby that says the field is required. After submit, the confirmation receives focus through tabindex="-1" so its heading and text are read, and it shows a focus outline. The avatar initials are aria-hidden. Buttons and fields have visible focus styles, and transitions switch off under prefers-reduced-motion.

Customise it

Each layout sets --nl-accent, --nl-accent-text and --nl-ring: yellow on black in .nl-band, violet in .nl-card. Change those and the band background. In Bootstrap the button is a .btn restyled through --bs-btn-* variables; in Tailwind edit the [--nl-accent:…] properties on each section.