Open demo

CSS code

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="fm-i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/></symbol>
  <symbol id="fm-i-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3.5h8.5L19 8v12.5H6z"/><path d="M14.5 3.5V8H19M9 12.5h7M9 16h4.5"/></symbol>
  <symbol id="fm-i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15.5" r="3.8"/><path d="M10.8 12.8 19.5 4M16 7.5l2.5 2.5M13.8 9.8l2 2"/></symbol>
  <symbol id="fm-i-star" viewBox="0 0 24 24"><path d="M12 3.2l2.62 5.53 6.06.78-4.45 4.19 1.13 6.01L12 16.8l-5.36 2.91 1.13-6.01-4.45-4.19 6.06-.78z"/></symbol>
  <symbol id="fm-i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 6v12M6 12h12"/></symbol>
  <symbol id="fm-i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5.5 12.5l4.2 4.2 8.8-9.2"/></symbol>
</svg>

<div><span class="lbl">Login / sign up</span>
<div class="btns"><button class="fm-btn is-primary" type="button" data-open="fm-signin" aria-haspopup="dialog">Sign in</button></div></div>

<div><span class="lbl">Newsletter</span>
<div class="btns"><button class="fm-btn" type="button" data-open="fm-news" aria-haspopup="dialog">Subscribe popup</button></div></div>

<div><span class="lbl">Feedback with rating</span>
<div class="btns"><button class="fm-btn" type="button" data-open="fm-feedback" aria-haspopup="dialog">Rate your call</button></div></div>

<!-- Login / sign up -->
<dialog class="fm is-auth" id="fm-signin" aria-labelledby="fm-signin-title">
  <form class="fm-form" method="dialog">
    <button class="fm-x" value="close" formnovalidate aria-label="Close"><svg aria-hidden="true"><use href="#fm-i-x"/></svg></button>
    <div class="fm-auth-head">
      <span class="fm-logo" aria-hidden="true"><svg><use href="#fm-i-logo"/></svg></span>
      <h2 class="fm-title" id="fm-signin-title">Welcome back to Fieldnote</h2>
      <p class="fm-sub">Sign in to pick up your notes where you left them.</p>
    </div>
    <div class="fm-body">
      <button class="fm-btn is-block" type="button"><svg aria-hidden="true"><use href="#fm-i-key"/></svg>Continue with SSO</button>
      <p class="fm-or">or</p>
      <div class="fm-field">
        <label class="fm-label" for="fm-signin-email">Email</label>
        <input class="fm-input" id="fm-signin-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required autofocus>
      </div>
      <div class="fm-field">
        <label class="fm-label" for="fm-signin-password">Password</label>
        <input class="fm-input" id="fm-signin-password" name="password" type="password" autocomplete="current-password" minlength="8" required>
      </div>
      <div class="fm-row">
        <label class="fm-check"><input type="checkbox" name="remember" checked>Remember me</label>
        <a class="fm-link" href="#">Forgot password?</a>
      </div>
      <button class="fm-btn is-primary is-block" value="signin">Sign in</button>
    </div>
    <p class="fm-note">New to Fieldnote? <a class="fm-link" href="#">Create an account</a></p>
  </form>
</dialog>

<!-- Newsletter -->
<dialog class="fm is-news" id="fm-news" aria-labelledby="fm-news-title" aria-describedby="fm-news-desc">
  <form class="fm-form fm-news" method="dialog">
    <div class="fm-news-media"><img src="https://images.unsplash.com/photo-1517842645767-c639042777db?auto=format&fit=crop&w=640&h=820&q=70" width="640" height="820" alt="An open notebook with a handwritten Notes heading, a fountain pen and reading glasses on a wooden desk"></div>
    <div class="fm-news-body">
      <p class="fm-eyebrow">The Friday Field Notes</p>
      <h2 class="fm-title" id="fm-news-title">One useful idea, every Friday</h2>
      <p class="fm-lead" id="fm-news-desc">A short letter on reading, note-taking and research habits, with one method you can try before Monday.</p>
      <label class="fm-label" for="fm-news-email">Email address</label>
      <div class="fm-inline">
        <input class="fm-input" id="fm-news-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required autofocus>
        <button class="fm-btn is-primary" value="subscribe">Subscribe</button>
      </div>
      <p class="fm-fine">12,400 readers. One email a week. Unsubscribe any time.</p>
    </div>
    <button class="fm-x is-chip" value="close" formnovalidate aria-label="Close"><svg aria-hidden="true"><use href="#fm-i-x"/></svg></button>
  </form>
</dialog>

<!-- Feedback with rating -->
<dialog class="fm is-feedback" id="fm-feedback" aria-labelledby="fm-feedback-title">
  <form class="fm-form" method="dialog">
    <button class="fm-x" value="close" formnovalidate aria-label="Close"><svg aria-hidden="true"><use href="#fm-i-x"/></svg></button>
    <div class="fm-head">
      <img class="fm-avatar" src="https://images.unsplash.com/photo-1580489944761-15a19d654956?auto=format&fit=crop&crop=faces&w=96&h=96&q=70" width="48" height="48" alt="Priya Raman">
      <div><h2 class="fm-title" id="fm-feedback-title">How was your onboarding call with Priya?</h2><p class="fm-sub">30 minutes · Tuesday 22 September</p></div>
    </div>
    <div class="fm-body">
      <fieldset class="fm-set">
        <legend class="fm-label">Rate the call <span class="fm-hint">1 is poor, 5 is excellent</span></legend>
        <div class="fm-stars">
          <label class="fm-star"><input class="fm-sr" type="radio" name="rating" value="1" required><span class="fm-sr">1 star</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="fm-star"><input class="fm-sr" type="radio" name="rating" value="2"><span class="fm-sr">2 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="fm-star"><input class="fm-sr" type="radio" name="rating" value="3"><span class="fm-sr">3 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="fm-star"><input class="fm-sr" type="radio" name="rating" value="4"><span class="fm-sr">4 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="fm-star"><input class="fm-sr" type="radio" name="rating" value="5"><span class="fm-sr">5 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
        </div>
      </fieldset>
      <fieldset class="fm-set">
        <legend class="fm-label">What went well? <span class="fm-opt">(optional)</span></legend>
        <div class="fm-chips">
          <label class="fm-chip"><input class="fm-sr" type="checkbox" name="good" value="clear"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>Clear answers</label>
          <label class="fm-chip"><input class="fm-sr" type="checkbox" name="good" value="on-time"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>On time</label>
          <label class="fm-chip"><input class="fm-sr" type="checkbox" name="good" value="examples"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>Useful examples</label>
          <label class="fm-chip"><input class="fm-sr" type="checkbox" name="good" value="friendly"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>Friendly</label>
        </div>
      </fieldset>
      <div class="fm-field">
        <label class="fm-label" for="fm-feedback-better">Anything we could do better? <span class="fm-opt">(optional)</span></label>
        <textarea class="fm-input" id="fm-feedback-better" name="better" rows="4" placeholder="For example: more time on tagging and saved searches"></textarea>
      </div>
    </div>
    <div class="fm-foot">
      <button class="fm-btn" value="later" formnovalidate>Not now</button>
      <button class="fm-btn is-primary" value="send">Send feedback</button>
    </div>
  </form>
</dialog>
CSS
/* Form modals on the native <dialog> element. Each dialog holds a <form method="dialog">: the browser's own
   validation (required, type="email") must pass before the submit button closes it; Cancel and X use formnovalidate. */
.fm-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.fm-btn{--fm-accent:#0f766e;display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:10px 18px;border:1px solid #d4cdbf;border-radius:10px;
  background:#fff;color:#0f172a;font-family:inherit;font-size:.9rem;font-weight:600;line-height:1.2;text-decoration:none;cursor:pointer;transition:background-color .15s,border-color .15s,box-shadow .15s}
.fm-btn:hover{background:#f7f4ee;border-color:#a8a090}
.fm-btn:focus-visible{outline:2px solid var(--fm-accent);outline-offset:2px}
.fm-btn.is-primary{background:var(--fm-accent);border-color:var(--fm-accent);color:#fff;box-shadow:0 6px 16px -8px rgba(15,118,110,.75)}
.fm-btn.is-primary:hover{background:#115e59;border-color:#115e59}
.fm-btn.is-block{width:100%}
.fm-btn svg{width:18px;height:18px;flex:none}

.fm{--fm-accent:#0f766e;--fm-accent-dark:#115e59;--fm-ink:#0f172a;--fm-muted:#475569;--fm-line:#e8e3d9;--fm-soft:#faf8f4;--fm-field:#848e9d;--fm-ring:rgba(15,118,110,.24);
  --fm-radius:18px;--fm-w:440px;
  width:min(var(--fm-w),calc(100% - 32px));max-width:none;max-height:calc(100dvh - 56px);margin:auto;padding:0;border:0;border-radius:var(--fm-radius);
  background:#fff;color:var(--fm-ink);font-size:.95rem;line-height:1.55;overflow:hidden;
  box-shadow:0 0 0 1px rgba(15,23,42,.06),0 28px 64px -18px rgba(15,23,42,.45)}
.fm[open]{display:flex;flex-direction:column;animation:fm-in .22s cubic-bezier(.2,.8,.2,1)}
.fm::backdrop{background:rgba(15,23,42,.55);backdrop-filter:blur(2px)}
.fm[open]::backdrop{animation:fm-fade .22s ease-out}
@keyframes fm-in{from{opacity:0;transform:translateY(10px) scale(.97)}}
@keyframes fm-fade{from{opacity:0}}
html:has(.fm:modal){overflow:hidden} /* stop the page scrolling behind an open modal */

.fm-form{display:flex;flex-direction:column;flex:1;min-height:0;margin:0}
.fm-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:22px 32px 26px}
.fm-title{margin:0;font-size:1.3rem;font-weight:700;letter-spacing:-.015em;line-height:1.3}
.fm-sub{margin:4px 0 0;color:var(--fm-muted);font-size:.9rem}
.fm-x{position:absolute;top:12px;right:12px;z-index:1;display:grid;place-items:center;width:36px;height:36px;padding:0;border:0;border-radius:10px;background:transparent;color:#475569;cursor:pointer;
  transition:background-color .15s,color .15s}
.fm-x:hover{background:#f3efe7;color:var(--fm-ink)}
.fm-x:focus-visible{outline:2px solid var(--fm-accent);outline-offset:1px}
.fm-x svg{width:20px;height:20px}
/* Over a photo the X sits on a solid, slightly blurred chip so it keeps its contrast */
.fm-x.is-chip{border-radius:999px;background:rgba(255,255,255,.9);color:#0f172a;box-shadow:0 1px 3px rgba(15,23,42,.2);backdrop-filter:blur(6px)}
.fm-x.is-chip:hover{background:#fff}
.fm-foot{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px;padding:16px 24px;border-top:1px solid var(--fm-line);background:var(--fm-soft)}

/* Fields: 44px tall, 10px radius, the label always above the field */
.fm-field{display:flex;flex-direction:column;gap:6px;margin:0 0 16px}
.fm-label{color:var(--fm-ink);font-size:.85rem;font-weight:600;line-height:1.3}
.fm-opt{color:#5b6474;font-weight:400}
.fm-input{width:100%;height:44px;margin:0;padding:0 14px;border:1px solid var(--fm-field);border-radius:10px;background:#fff;color:var(--fm-ink);font-family:inherit;font-size:.95rem;
  transition:border-color .15s,box-shadow .15s}
.fm-input::placeholder{color:#6b7280;opacity:1}
.fm-input:hover{border-color:#5f6b7b}
.fm-input:focus-visible{outline:none;border-color:var(--fm-accent);box-shadow:0 0 0 3px var(--fm-ring)}
.fm-input:user-invalid{border-color:#b91c1c}
.fm-input:user-invalid:focus-visible{box-shadow:0 0 0 3px rgba(185,28,28,.2)}
textarea.fm-input{display:block;height:auto;min-height:104px;padding:10px 14px;line-height:1.5;resize:vertical}
.fm-check{display:inline-flex;align-items:center;gap:8px;color:#334155;font-size:.875rem;cursor:pointer}
.fm-check input{appearance:none;flex:none;width:18px;height:18px;margin:0;border:1.5px solid var(--fm-field);border-radius:5px;background:#fff center/12px no-repeat;cursor:pointer;
  transition:background-color .15s,border-color .15s}
.fm-check input:checked{border-color:var(--fm-accent);background-color:var(--fm-accent);
  background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}
.fm-check input:focus-visible{outline:2px solid var(--fm-accent);outline-offset:2px}
.fm-link{border-radius:4px;color:var(--fm-accent);font-weight:600;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}
.fm-link:hover{color:var(--fm-accent-dark);text-decoration-thickness:2px}
.fm-link:focus-visible{outline:2px solid var(--fm-accent);outline-offset:2px}

/* 1. Sign in */
.fm.is-auth{--fm-w:420px}
.fm-auth-head{padding:34px 32px 0;text-align:center}
.fm-logo{display:grid;place-items:center;width:48px;height:48px;margin:0 auto 14px;border-radius:14px;background:linear-gradient(145deg,#14b8a6,#0f766e);color:#fff;
  box-shadow:0 8px 18px -8px rgba(15,118,110,.8)}
.fm-logo svg{width:26px;height:26px}
.fm-or{display:flex;align-items:center;gap:12px;margin:18px 0;color:#5b6474;font-size:.75rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase}
.fm-or::before,.fm-or::after{content:"";flex:1;height:1px;background:var(--fm-line)}
.fm-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 16px;margin:0 0 24px;font-size:.875rem}
.fm-note{margin:0;padding:16px 32px;border-top:1px solid var(--fm-line);background:var(--fm-soft);color:var(--fm-muted);font-size:.875rem;text-align:center}

/* 2. Newsletter: photo left, form right; stacked with a short photo band on phones */
.fm.is-news{--fm-w:760px}
.fm-form.fm-news{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr)}
.fm-news-media{position:relative;min-height:180px;margin:0;background:#d9d2c3}
.fm-news-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.fm-news-body{align-self:center;padding:48px 40px 40px}
.fm-eyebrow{margin:0 0 10px;color:var(--fm-accent);font-size:.75rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.fm-news .fm-title{font-size:1.6rem;line-height:1.2;letter-spacing:-.02em}
.fm-lead{margin:10px 0 22px;color:var(--fm-muted)}
.fm-inline{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.fm-inline .fm-input{flex:1 1 190px;width:auto;min-width:0}
.fm-fine{margin:12px 0 0;color:#5b6474;font-size:.8rem}

/* 3. Feedback with a star rating */
.fm.is-feedback{--fm-w:500px}
.fm-head{display:flex;align-items:center;gap:14px;padding:24px 60px 4px 24px}
.fm-avatar{flex:none;width:48px;height:48px;border-radius:50%;object-fit:cover;box-shadow:0 0 0 3px #fff,0 0 0 4px var(--fm-line)}
.fm-head .fm-title{font-size:1.1rem;line-height:1.35}
.fm-head .fm-sub{margin-top:2px;font-size:.85rem}
.fm.is-feedback .fm-body{padding:20px 24px 22px}
.fm-set{min-width:0;margin:0 0 24px;padding:0;border:0}
.fm-set legend{display:block;margin:0 0 8px;padding:0}
.fm-hint{margin-left:6px;color:#5b6474;font-size:.8rem;font-weight:400}
/* The stars: visually hidden radios inside labels. --fm-rate (checked) and --fm-hover (hovered) set how
   many stars are filled; each star fills when its number --i is within that count. */
.fm-stars{--fm-show:var(--fm-hover,var(--fm-rate,0));display:flex;margin-left:-6px}
.fm-stars:has(input[value="1"]:checked){--fm-rate:1}
.fm-stars:has(input[value="2"]:checked){--fm-rate:2}
.fm-stars:has(input[value="3"]:checked){--fm-rate:3}
.fm-stars:has(input[value="4"]:checked){--fm-rate:4}
.fm-stars:has(input[value="5"]:checked){--fm-rate:5}
.fm-stars:has(.fm-star:nth-child(1):hover){--fm-hover:1}
.fm-stars:has(.fm-star:nth-child(2):hover){--fm-hover:2}
.fm-stars:has(.fm-star:nth-child(3):hover){--fm-hover:3}
.fm-stars:has(.fm-star:nth-child(4):hover){--fm-hover:4}
.fm-stars:has(.fm-star:nth-child(5):hover){--fm-hover:5}
.fm-star{display:grid;place-items:center;width:46px;height:46px;border-radius:10px;cursor:pointer}
.fm-star:nth-child(2){--i:2}.fm-star:nth-child(3){--i:3}.fm-star:nth-child(4){--i:4}.fm-star:nth-child(5){--i:5}
.fm-star svg{width:34px;height:34px;fill:#f59e0b;fill-opacity:clamp(0,calc(var(--fm-show) - var(--i,1) + 1),1);stroke:#b45309;stroke-width:1.4;stroke-linejoin:round;
  transition:fill-opacity .15s,transform .15s}
.fm-star:hover svg{transform:scale(1.12)}
.fm-star:has(:focus-visible){outline:2px solid var(--fm-accent);outline-offset:-2px}
/* Optional chips: checkbox pills */
.fm-chips{display:flex;flex-wrap:wrap;gap:8px}
.fm-chip{display:inline-flex;align-items:center;gap:6px;min-height:38px;padding:6px 14px 6px 11px;border:1px solid #d4cdbf;border-radius:999px;background:#fff;color:#334155;
  font-size:.85rem;font-weight:500;cursor:pointer;transition:background-color .15s,border-color .15s,color .15s}
.fm-chip svg{width:16px;height:16px;flex:none}
.fm-chip .is-on{display:none}
.fm-chip:hover{border-color:#a8a090;background:var(--fm-soft)}
.fm-chip:has(:checked){border-color:var(--fm-accent);background:#e6f4f1;color:var(--fm-accent-dark)}
.fm-chip:has(:checked) .is-off{display:none}
.fm-chip:has(:checked) .is-on{display:block}
.fm-chip:has(:focus-visible){outline:2px solid var(--fm-accent);outline-offset:2px}
.fm.is-feedback .fm-field{margin:0}

@media (max-width:767.98px){
  .fm-form.fm-news{grid-template-columns:minmax(0,1fr)}
  .fm-news-media{height:140px;min-height:0}
  .fm-news-media img{object-position:center 62%}
  .fm-news-body{padding:22px 20px 24px}
  .fm-news .fm-title{font-size:1.35rem}
}
@media (max-width:600px){
  .fm{max-height:calc(100dvh - 32px)}
  .fm-auth-head{padding:30px 20px 0}
  .fm-body{padding:20px 20px 22px}
  .fm-note{padding:14px 20px}
  .fm-head{padding:20px 56px 2px 18px}
  .fm.is-feedback .fm-body{padding:18px 18px 20px}
  .fm-foot{padding:14px 18px}
  .fm-foot .fm-btn,.fm-inline .fm-btn{flex:1 1 auto}
}
@media (prefers-reduced-motion:reduce){
  .fm[open],.fm[open]::backdrop{animation:none}
  .fm-btn,.fm-x,.fm-input,.fm-check input,.fm-chip,.fm-star svg{transition:none}
  .fm-star:hover svg{transform:none}
}
JavaScript
// Open a dialog from any button with data-open="dialog-id". showModal() traps focus, closes on Esc
// and makes the page behind inert. Buttons inside <form method="dialog"> close it with no script;
// the browser checks required and type="email" first, so an invalid form stays open.
document.querySelectorAll('[data-open]').forEach(function (btn) {
  var dialog = document.getElementById(btn.getAttribute('data-open'));
  if (!dialog) return;
  btn.addEventListener('click', function () {
    dialog.showModal();
    dialog.addEventListener('close', function () { btn.focus(); }, { once: true }); // focus back to the trigger
  });
  // A click on the backdrop lands on the <dialog> itself (its form fills the box), so close it
  dialog.addEventListener('click', function (e) { if (e.target === dialog) dialog.close(); });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="fm-i-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3.5h8.5L19 8v12.5H6z"/><path d="M14.5 3.5V8H19M9 12.5h7M9 16h4.5"/></symbol>
  <symbol id="fm-i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15.5" r="3.8"/><path d="M10.8 12.8 19.5 4M16 7.5l2.5 2.5M13.8 9.8l2 2"/></symbol>
  <symbol id="fm-i-star" viewBox="0 0 24 24"><path d="M12 3.2l2.62 5.53 6.06.78-4.45 4.19 1.13 6.01L12 16.8l-5.36 2.91 1.13-6.01-4.45-4.19 6.06-.78z"/></symbol>
  <symbol id="fm-i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 6v12M6 12h12"/></symbol>
  <symbol id="fm-i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5.5 12.5l4.2 4.2 8.8-9.2"/></symbol>
</svg>

<div><span class="lbl">Login / sign up</span>
<div class="d-flex flex-wrap gap-2"><button class="btn fm-btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#fm-signin">Sign in</button></div></div>

<div><span class="lbl">Newsletter</span>
<div class="d-flex flex-wrap gap-2"><button class="btn fm-btn" type="button" data-bs-toggle="modal" data-bs-target="#fm-news">Subscribe popup</button></div></div>

<div><span class="lbl">Feedback with rating</span>
<div class="d-flex flex-wrap gap-2"><button class="btn fm-btn" type="button" data-bs-toggle="modal" data-bs-target="#fm-feedback">Rate your call</button></div></div>

<!-- Login / sign up -->
<div class="modal fade fm fm-auth" id="fm-signin" tabindex="-1" aria-labelledby="fm-signin-title" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      <form>
        <div class="modal-header">
          <span class="fm-logo" aria-hidden="true"><svg><use href="#fm-i-logo"/></svg></span>
          <h2 class="modal-title" id="fm-signin-title">Welcome back to Fieldnote</h2>
          <p class="fm-sub">Sign in to pick up your notes where you left them.</p>
        </div>
        <div class="modal-body">
          <button class="btn fm-btn w-100" type="button"><svg aria-hidden="true"><use href="#fm-i-key"/></svg>Continue with SSO</button>
          <p class="fm-or">or</p>
          <div class="mb-3">
            <label class="form-label" for="fm-signin-email">Email</label>
            <input class="form-control" id="fm-signin-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required autofocus>
          </div>
          <div class="mb-3">
            <label class="form-label" for="fm-signin-password">Password</label>
            <input class="form-control" id="fm-signin-password" name="password" type="password" autocomplete="current-password" minlength="8" required>
          </div>
          <div class="d-flex flex-wrap align-items-center justify-content-between column-gap-3 row-gap-2 mb-4">
            <div class="form-check">
              <input class="form-check-input" type="checkbox" id="fm-signin-remember" name="remember" checked>
              <label class="form-check-label" for="fm-signin-remember">Remember me</label>
            </div>
            <a class="fm-link" href="#">Forgot password?</a>
          </div>
          <button class="btn fm-btn btn-primary w-100" type="submit">Sign in</button>
        </div>
        <div class="modal-footer">
          <p>New to Fieldnote? <a class="fm-link" href="#">Create an account</a></p>
        </div>
      </form>
    </div>
  </div>
</div>

<!-- Newsletter -->
<div class="modal fade fm fm-news" id="fm-news" tabindex="-1" aria-labelledby="fm-news-title" aria-describedby="fm-news-desc" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <form class="row g-0">
        <div class="col-md-5 fm-news-media"><img src="https://images.unsplash.com/photo-1517842645767-c639042777db?auto=format&fit=crop&w=640&h=820&q=70" width="640" height="820" alt="An open notebook with a handwritten Notes heading, a fountain pen and reading glasses on a wooden desk"></div>
        <div class="col-md-7 align-self-center">
          <div class="fm-news-body">
            <p class="fm-eyebrow">The Friday Field Notes</p>
            <h2 class="modal-title" id="fm-news-title">One useful idea, every Friday</h2>
            <p class="fm-lead" id="fm-news-desc">A short letter on reading, note-taking and research habits, with one method you can try before Monday.</p>
            <label class="form-label" for="fm-news-email">Email address</label>
            <div class="d-flex flex-wrap gap-2">
              <input class="form-control" id="fm-news-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required autofocus>
              <button class="btn fm-btn btn-primary" type="submit">Subscribe</button>
            </div>
            <p class="fm-fine">12,400 readers. One email a week. Unsubscribe any time.</p>
          </div>
        </div>
      </form>
      <button type="button" class="btn-close fm-chip-x" data-bs-dismiss="modal" aria-label="Close"></button>
    </div>
  </div>
</div>

<!-- Feedback with rating -->
<div class="modal fade fm fm-feedback" id="fm-feedback" tabindex="-1" aria-labelledby="fm-feedback-title" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      <form>
        <div class="modal-header">
          <img class="fm-avatar" src="https://images.unsplash.com/photo-1580489944761-15a19d654956?auto=format&fit=crop&crop=faces&w=96&h=96&q=70" width="48" height="48" alt="Priya Raman">
          <div><h2 class="modal-title" id="fm-feedback-title">How was your onboarding call with Priya?</h2><p class="fm-sub">30 minutes · Tuesday 22 September</p></div>
        </div>
        <div class="modal-body">
          <fieldset class="mb-4">
            <legend class="form-label">Rate the call <span class="fm-hint">1 is poor, 5 is excellent</span></legend>
            <div class="fm-stars">
              <label class="fm-star"><input class="visually-hidden" type="radio" name="rating" value="1" required><span class="visually-hidden">1 star</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
              <label class="fm-star"><input class="visually-hidden" type="radio" name="rating" value="2"><span class="visually-hidden">2 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
              <label class="fm-star"><input class="visually-hidden" type="radio" name="rating" value="3"><span class="visually-hidden">3 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
              <label class="fm-star"><input class="visually-hidden" type="radio" name="rating" value="4"><span class="visually-hidden">4 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
              <label class="fm-star"><input class="visually-hidden" type="radio" name="rating" value="5"><span class="visually-hidden">5 stars</span><svg aria-hidden="true"><use href="#fm-i-star"/></svg></label>
            </div>
          </fieldset>
          <fieldset class="mb-4">
            <legend class="form-label">What went well? <span class="fm-opt">(optional)</span></legend>
            <div class="d-flex flex-wrap gap-2">
              <input type="checkbox" class="btn-check" id="fm-good-clear" name="good" value="clear" autocomplete="off"><label class="btn fm-chip" for="fm-good-clear"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>Clear answers</label>
              <input type="checkbox" class="btn-check" id="fm-good-on-time" name="good" value="on-time" autocomplete="off"><label class="btn fm-chip" for="fm-good-on-time"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>On time</label>
              <input type="checkbox" class="btn-check" id="fm-good-examples" name="good" value="examples" autocomplete="off"><label class="btn fm-chip" for="fm-good-examples"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>Useful examples</label>
              <input type="checkbox" class="btn-check" id="fm-good-friendly" name="good" value="friendly" autocomplete="off"><label class="btn fm-chip" for="fm-good-friendly"><svg class="is-off" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="is-on" aria-hidden="true"><use href="#fm-i-check"/></svg>Friendly</label>
            </div>
          </fieldset>
          <label class="form-label" for="fm-feedback-better">Anything we could do better? <span class="fm-opt">(optional)</span></label>
          <textarea class="form-control" id="fm-feedback-better" name="better" rows="4" placeholder="For example: more time on tagging and saved searches"></textarea>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn fm-btn" data-bs-dismiss="modal">Not now</button>
          <button type="submit" class="btn fm-btn btn-primary">Send feedback</button>
        </div>
      </form>
    </div>
  </div>
</div>
CSS
/* Bootstrap's Modal, .form-control, .form-check and .btn-check, restyled through --bs-* variables. */
.fm-btn{--bs-btn-padding-x:18px;--bs-btn-padding-y:10px;--bs-btn-font-size:.9rem;--bs-btn-font-weight:600;--bs-btn-line-height:1.2;--bs-btn-border-radius:10px;
  --bs-btn-bg:#fff;--bs-btn-color:#0f172a;--bs-btn-border-color:#d4cdbf;--bs-btn-hover-bg:#f7f4ee;--bs-btn-hover-color:#0f172a;--bs-btn-hover-border-color:#a8a090;
  --bs-btn-active-bg:#efeae0;--bs-btn-active-color:#0f172a;--bs-btn-active-border-color:#a8a090;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px}
.fm-btn.btn-primary{--bs-btn-bg:#0f766e;--bs-btn-color:#fff;--bs-btn-border-color:#0f766e;--bs-btn-hover-bg:#115e59;--bs-btn-hover-color:#fff;--bs-btn-hover-border-color:#115e59;
  --bs-btn-active-bg:#134e4a;--bs-btn-active-color:#fff;--bs-btn-active-border-color:#134e4a;box-shadow:0 6px 16px -8px rgba(15,118,110,.75)}
.fm-btn:focus-visible{outline:2px solid #0f766e;outline-offset:2px;box-shadow:none}
.fm-btn svg{width:18px;height:18px;flex:none}

.fm{--bs-modal-width:440px;--bs-modal-margin:1rem;--bs-modal-color:#0f172a;--bs-modal-bg:#fff;--bs-modal-border-width:0;--bs-modal-border-radius:18px;--bs-modal-inner-border-radius:18px;
  --bs-modal-header-border-width:0;--bs-modal-title-line-height:1.3;--bs-modal-footer-bg:#faf8f4;--bs-modal-footer-border-color:#e8e3d9;
  font-size:.95rem;line-height:1.55}
@media (min-width:576px){.fm{--bs-modal-margin:1.75rem}}
.fm .modal-content{overflow:hidden;box-shadow:0 0 0 1px rgba(15,23,42,.06),0 28px 64px -18px rgba(15,23,42,.45)}
.fm .modal-body{padding:22px 32px 26px}
.fm .modal-title{font-size:1.3rem;font-weight:700;letter-spacing:-.015em}
.fm-sub{margin:4px 0 0;color:#475569;font-size:.9rem}
.fm .modal-footer{gap:10px;padding:16px 24px}
.fm .modal-footer>*{margin:0}
.fm .btn-close{position:absolute;top:12px;right:12px;z-index:2;width:36px;height:36px;padding:0;border-radius:10px;background-size:13px;opacity:.75}
.fm .btn-close:hover{background-color:#f3efe7;opacity:1}
.fm .btn-close:focus-visible{outline:2px solid #0f766e;outline-offset:1px;box-shadow:none;opacity:1}
/* Over a photo the X sits on a solid, slightly blurred chip so it keeps its contrast */
.fm .btn-close.fm-chip-x{border-radius:50%;background-color:rgba(255,255,255,.9);opacity:1;box-shadow:0 1px 3px rgba(15,23,42,.2);backdrop-filter:blur(6px)}
.fm .btn-close.fm-chip-x:hover{background-color:#fff}
/* Same entrance as the plain version: a short rise and scale instead of Bootstrap's 50px drop */
.fm.fade .modal-dialog{transform:translateY(10px) scale(.97);transition:transform .22s cubic-bezier(.2,.8,.2,1)}
.fm.show .modal-dialog{transform:none}
.modal-backdrop{--bs-backdrop-bg:#0f172a;--bs-backdrop-opacity:.55;backdrop-filter:blur(2px)}

/* Fields: 44px tall, 10px radius, the label always above the field */
.fm .form-label{display:block;margin-bottom:6px;color:#0f172a;font-size:.85rem;font-weight:600;line-height:1.3}
.fm legend.form-label{float:none;width:auto;margin-bottom:8px;font-size:.85rem}
.fm-opt,.fm-hint{color:#5b6474;font-weight:400}
.fm-hint{margin-left:6px;font-size:.8rem}
.fm .form-control{height:44px;padding:0 14px;border-color:#848e9d;border-radius:10px;color:#0f172a;font-size:.95rem;transition:border-color .15s,box-shadow .15s}
.fm .form-control::placeholder{color:#6b7280}
.fm .form-control:hover{border-color:#5f6b7b}
.fm .form-control:focus{border-color:#0f766e;box-shadow:0 0 0 3px rgba(15,118,110,.24)}
.fm .form-control:user-invalid{border-color:#b91c1c}
.fm .form-control:user-invalid:focus{box-shadow:0 0 0 3px rgba(185,28,28,.2)}
.fm textarea.form-control{height:auto;min-height:104px;padding:10px 14px;line-height:1.5} /* undo the 44px height and Bootstrap's one-line min-height */
.fm .form-check{display:flex;align-items:center;gap:8px;min-height:0;margin:0;padding:0;color:#334155}
.fm .form-check-input{float:none;flex:none;width:18px;height:18px;margin:0;border:1.5px solid #848e9d;border-radius:5px;background-size:12px;cursor:pointer}
.fm .form-check-input:checked{background-color:#0f766e;border-color:#0f766e}
.fm .form-check-input:focus{border-color:#848e9d;box-shadow:none}
.fm .form-check-input:checked:focus{border-color:#0f766e}
.fm .form-check-input:focus-visible{outline:2px solid #0f766e;outline-offset:2px}
.fm .form-check-label{cursor:pointer}
.fm-link{border-radius:4px;color:#0f766e;font-weight:600;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}
.fm-link:hover{color:#115e59;text-decoration-thickness:2px}
.fm-link:focus-visible{outline:2px solid #0f766e;outline-offset:2px}

/* 1. Sign in */
.fm-auth{--bs-modal-width:420px}
.fm-auth .modal-header{display:block;padding:34px 32px 0;text-align:center}
.fm-logo{display:grid;place-items:center;width:48px;height:48px;margin:0 auto 14px;border-radius:14px;background:linear-gradient(145deg,#14b8a6,#0f766e);color:#fff;
  box-shadow:0 8px 18px -8px rgba(15,118,110,.8)}
.fm-logo svg{width:26px;height:26px}
.fm-or{display:flex;align-items:center;gap:12px;margin:18px 0;color:#5b6474;font-size:.75rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase}
.fm-or::before,.fm-or::after{content:"";flex:1;height:1px;background:#e8e3d9}
.fm-auth .form-check,.fm-auth .fm-link{font-size:.875rem}
.fm-auth .modal-footer{justify-content:center;padding:16px 32px;color:#475569;font-size:.875rem;text-align:center}

/* 2. Newsletter: photo left, form right; stacked with a short photo band on phones */
.fm-news{--bs-modal-width:760px}
.fm-news-media{position:relative;min-height:180px;background:#d9d2c3}
.fm-news-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.fm-news-body{padding:48px 40px 40px}
.fm-eyebrow{margin:0 0 10px;color:#0f766e;font-size:.75rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.fm-news .modal-title{font-size:1.6rem;line-height:1.2;letter-spacing:-.02em}
.fm-lead{margin:10px 0 22px;color:#475569}
.fm-news .form-control{flex:1 1 190px;width:auto;min-width:0}
.fm-fine{margin:12px 0 0;color:#5b6474;font-size:.8rem}

/* 3. Feedback with a star rating */
.fm-feedback{--bs-modal-width:500px}
.fm-feedback .modal-header{gap:14px;padding:24px 60px 4px 24px}
.fm-avatar{flex:none;width:48px;height:48px;border-radius:50%;object-fit:cover;box-shadow:0 0 0 3px #fff,0 0 0 4px #e8e3d9}
.fm-feedback .modal-title{font-size:1.1rem;line-height:1.35}
.fm-feedback .fm-sub{margin-top:2px;font-size:.85rem}
.fm-feedback .modal-body{padding:20px 24px 22px}
/* The stars: visually hidden radios inside labels. --fm-rate (checked) and --fm-hover (hovered) set how
   many stars are filled; each star fills when its number --i is within that count. */
.fm-stars{--fm-show:var(--fm-hover,var(--fm-rate,0));display:flex;margin-left:-6px}
.fm-stars:has(input[value="1"]:checked){--fm-rate:1}
.fm-stars:has(input[value="2"]:checked){--fm-rate:2}
.fm-stars:has(input[value="3"]:checked){--fm-rate:3}
.fm-stars:has(input[value="4"]:checked){--fm-rate:4}
.fm-stars:has(input[value="5"]:checked){--fm-rate:5}
.fm-stars:has(.fm-star:nth-child(1):hover){--fm-hover:1}
.fm-stars:has(.fm-star:nth-child(2):hover){--fm-hover:2}
.fm-stars:has(.fm-star:nth-child(3):hover){--fm-hover:3}
.fm-stars:has(.fm-star:nth-child(4):hover){--fm-hover:4}
.fm-stars:has(.fm-star:nth-child(5):hover){--fm-hover:5}
.fm-star{display:grid;place-items:center;width:46px;height:46px;border-radius:10px;cursor:pointer}
.fm-star:nth-child(2){--i:2}.fm-star:nth-child(3){--i:3}.fm-star:nth-child(4){--i:4}.fm-star:nth-child(5){--i:5}
.fm-star svg{width:34px;height:34px;fill:#f59e0b;fill-opacity:clamp(0,calc(var(--fm-show) - var(--i,1) + 1),1);stroke:#b45309;stroke-width:1.4;stroke-linejoin:round;
  transition:fill-opacity .15s,transform .15s}
.fm-star:hover svg{transform:scale(1.12)}
.fm-star:has(:focus-visible){outline:2px solid #0f766e;outline-offset:-2px}
/* Optional chips: .btn-check checkboxes styled as pills */
.fm-chip{--bs-btn-padding-x:14px;--bs-btn-padding-y:6px;--bs-btn-font-size:.85rem;--bs-btn-font-weight:500;--bs-btn-border-radius:999px;
  --bs-btn-bg:#fff;--bs-btn-color:#334155;--bs-btn-border-color:#d4cdbf;--bs-btn-hover-bg:#faf8f4;--bs-btn-hover-color:#334155;--bs-btn-hover-border-color:#a8a090;
  --bs-btn-active-bg:#e6f4f1;--bs-btn-active-color:#115e59;--bs-btn-active-border-color:#0f766e;
  display:inline-flex;align-items:center;gap:6px;min-height:38px;padding-left:11px}
.fm-chip svg{width:16px;height:16px;flex:none}
.fm-chip .is-on,.btn-check:checked+.fm-chip .is-off{display:none}
.btn-check:checked+.fm-chip .is-on{display:block}
.btn-check:focus-visible+.fm-chip{outline:2px solid #0f766e;outline-offset:2px;box-shadow:none}

@media (max-width:767.98px){
  .fm-news-media{height:140px;min-height:0}
  .fm-news-media img{object-position:center 62%}
  .fm-news-body{padding:22px 20px 24px}
  .fm-news .modal-title{font-size:1.35rem}
}
@media (max-width:600px){
  .fm-auth .modal-header{padding:30px 20px 0}
  .fm .modal-body{padding:20px 20px 22px}
  .fm-auth .modal-footer{padding:14px 20px}
  .fm-feedback .modal-header{padding:20px 56px 2px 18px}
  .fm-feedback .modal-body{padding:18px 18px 20px}
  .fm .modal-footer{padding-left:18px;padding-right:18px}
  .fm-feedback .modal-footer .fm-btn,.fm-news .fm-btn{flex:1 1 auto}
}
@media (prefers-reduced-motion:reduce){
  .fm.fade .modal-dialog,.fm-btn,.fm .form-control,.fm-star svg{transition:none}
  .fm-star:hover svg{transform:none}
}
JavaScript
// An invalid form never fires submit: the browser stops it and shows its own message (required, type="email").
// A valid one would reload the page, so stop that and close the modal instead (send the data with fetch here).
document.querySelectorAll('.fm form').forEach(function (form) {
  form.addEventListener('submit', function (e) {
    if (!form.checkValidity()) return; // let the browser report the problem
    e.preventDefault();
    bootstrap.Modal.getOrCreateInstance(form.closest('.modal')).hide();
  });
});
// Bootstrap focuses the modal box itself when it opens; move focus to the field marked autofocus.
document.querySelectorAll('.fm.modal').forEach(function (modal) {
  modal.addEventListener('shown.bs.modal', function () {
    var field = modal.querySelector('[autofocus]');
    if (field) field.focus();
  });
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="fm-i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11"/></symbol>
  <symbol id="fm-i-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3.5h8.5L19 8v12.5H6z"/><path d="M14.5 3.5V8H19M9 12.5h7M9 16h4.5"/></symbol>
  <symbol id="fm-i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15.5" r="3.8"/><path d="M10.8 12.8 19.5 4M16 7.5l2.5 2.5M13.8 9.8l2 2"/></symbol>
  <symbol id="fm-i-star" viewBox="0 0 24 24"><path d="M12 3.2l2.62 5.53 6.06.78-4.45 4.19 1.13 6.01L12 16.8l-5.36 2.91 1.13-6.01-4.45-4.19 6.06-.78z"/></symbol>
  <symbol id="fm-i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 6v12M6 12h12"/></symbol>
  <symbol id="fm-i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5.5 12.5l4.2 4.2 8.8-9.2"/></symbol>
</svg>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#5b6474] uppercase">Login / sign up</span>
<div class="flex flex-wrap gap-2.5"><button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#0f766e] bg-[#0f766e] text-white shadow-[0_6px_16px_-8px_rgba(15,118,110,.75)] hover:border-[#115e59] hover:bg-[#115e59]" type="button" data-open="fm-signin" aria-haspopup="dialog">Sign in</button></div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#5b6474] uppercase">Newsletter</span>
<div class="flex flex-wrap gap-2.5"><button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#d4cdbf] bg-white text-[#0f172a] hover:border-[#a8a090] hover:bg-[#f7f4ee]" type="button" data-open="fm-news" aria-haspopup="dialog">Subscribe popup</button></div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#5b6474] uppercase">Feedback with rating</span>
<div class="flex flex-wrap gap-2.5"><button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#d4cdbf] bg-white text-[#0f172a] hover:border-[#a8a090] hover:bg-[#f7f4ee]" type="button" data-open="fm-feedback" aria-haspopup="dialog">Rate your call</button></div></div>

<!-- Login / sign up -->
<dialog class="m-auto max-h-[calc(100dvh-56px)] w-[min(420px,calc(100%-32px))] max-w-none overflow-hidden rounded-[18px] border-0 bg-white p-0 text-[.95rem] leading-[1.55] text-[#0f172a] shadow-[0_0_0_1px_rgba(15,23,42,.06),0_28px_64px_-18px_rgba(15,23,42,.45)] backdrop:bg-[rgba(15,23,42,.55)] backdrop:backdrop-blur-[2px] open:flex open:flex-col motion-safe:open:animate-[fm-in_.22s_cubic-bezier(.2,.8,.2,1)] motion-safe:open:backdrop:animate-[fm-fade_.22s_ease-out] max-[600px]:max-h-[calc(100dvh-32px)]" id="fm-signin" aria-labelledby="fm-signin-title">
  <form class="m-0 flex min-h-0 flex-1 flex-col" method="dialog">
    <button class="absolute top-3 right-3 z-[1] grid size-9 cursor-pointer place-items-center border-0 p-0 transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0f766e] motion-reduce:transition-none rounded-[10px] bg-transparent text-[#475569] hover:bg-[#f3efe7] hover:text-[#0f172a]" value="close" formnovalidate aria-label="Close"><svg class="size-5" aria-hidden="true"><use href="#fm-i-x"/></svg></button>
    <div class="px-8 pt-[34px] text-center max-[600px]:px-5 max-[600px]:pt-[30px]">
      <span class="mx-auto mb-3.5 grid size-12 place-items-center rounded-[14px] bg-[linear-gradient(145deg,#14b8a6,#0f766e)] text-white shadow-[0_8px_18px_-8px_rgba(15,118,110,.8)]" aria-hidden="true"><svg class="size-[26px]"><use href="#fm-i-logo"/></svg></span>
      <h2 class="m-0 text-[1.3rem] leading-[1.3] font-bold tracking-[-.015em]" id="fm-signin-title">Welcome back to Fieldnote</h2>
      <p class="mt-1 mb-0 text-[.9rem] text-[#475569]">Sign in to pick up your notes where you left them.</p>
    </div>
    <div class="min-h-0 flex-auto overflow-y-auto overscroll-contain px-8 pt-[22px] pb-[26px] max-[600px]:px-5 max-[600px]:pt-5 max-[600px]:pb-[22px]">
      <button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#d4cdbf] bg-white text-[#0f172a] hover:border-[#a8a090] hover:bg-[#f7f4ee] w-full" type="button"><svg class="size-[18px] flex-none" aria-hidden="true"><use href="#fm-i-key"/></svg>Continue with SSO</button>
      <p class="my-[18px] flex items-center gap-3 text-[.75rem] font-semibold tracking-[.08em] text-[#5b6474] uppercase before:h-px before:flex-1 before:bg-[#e8e3d9] after:h-px after:flex-1 after:bg-[#e8e3d9]">or</p>
      <div class="mb-4 flex flex-col gap-1.5">
        <label class="text-[.85rem] leading-[1.3] font-semibold text-[#0f172a]" for="fm-signin-email">Email</label>
        <input class="w-full rounded-[10px] border border-[#848e9d] bg-white px-3.5 text-[.95rem] text-[#0f172a] transition-[border-color,box-shadow] duration-150 placeholder:text-[#6b7280] hover:border-[#5f6b7b] focus-visible:border-[#0f766e] focus-visible:shadow-[0_0_0_3px_rgba(15,118,110,.24)] focus-visible:outline-none user-invalid:border-[#b91c1c] user-invalid:focus-visible:shadow-[0_0_0_3px_rgba(185,28,28,.2)] motion-reduce:transition-none h-11" id="fm-signin-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required autofocus>
      </div>
      <div class="mb-4 flex flex-col gap-1.5">
        <label class="text-[.85rem] leading-[1.3] font-semibold text-[#0f172a]" for="fm-signin-password">Password</label>
        <input class="w-full rounded-[10px] border border-[#848e9d] bg-white px-3.5 text-[.95rem] text-[#0f172a] transition-[border-color,box-shadow] duration-150 placeholder:text-[#6b7280] hover:border-[#5f6b7b] focus-visible:border-[#0f766e] focus-visible:shadow-[0_0_0_3px_rgba(15,118,110,.24)] focus-visible:outline-none user-invalid:border-[#b91c1c] user-invalid:focus-visible:shadow-[0_0_0_3px_rgba(185,28,28,.2)] motion-reduce:transition-none h-11" id="fm-signin-password" name="password" type="password" autocomplete="current-password" minlength="8" required>
      </div>
      <div class="mb-6 flex flex-wrap items-center justify-between gap-x-4 gap-y-2 text-[.875rem]">
        <label class="inline-flex cursor-pointer items-center gap-2 text-[#334155]"><input class="m-0 size-[18px] flex-none cursor-pointer appearance-none rounded-[5px] border-[1.5px] border-[#848e9d] bg-white bg-[length:12px_12px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#0f766e] checked:bg-[#0f766e] checked:bg-[url(data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20viewBox=%270%200%2020%2020%27%3e%3cpath%20fill=%27none%27%20stroke=%27%23fff%27%20stroke-linecap=%27round%27%20stroke-linejoin=%27round%27%20stroke-width=%273%27%20d=%27m6%2010%203%203%206-6%27/%3e%3c/svg%3e)] focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none" type="checkbox" name="remember" checked>Remember me</label>
        <a class="rounded font-semibold text-[#0f766e] underline decoration-1 underline-offset-[3px] hover:text-[#115e59] hover:decoration-2 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2" href="#">Forgot password?</a>
      </div>
      <button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#0f766e] bg-[#0f766e] text-white shadow-[0_6px_16px_-8px_rgba(15,118,110,.75)] hover:border-[#115e59] hover:bg-[#115e59] w-full" value="signin">Sign in</button>
    </div>
    <p class="m-0 border-t border-[#e8e3d9] bg-[#faf8f4] px-8 py-4 text-center text-[.875rem] text-[#475569] max-[600px]:px-5 max-[600px]:py-3.5">New to Fieldnote? <a class="rounded font-semibold text-[#0f766e] underline decoration-1 underline-offset-[3px] hover:text-[#115e59] hover:decoration-2 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2" href="#">Create an account</a></p>
  </form>
</dialog>

<!-- Newsletter -->
<dialog class="m-auto max-h-[calc(100dvh-56px)] w-[min(760px,calc(100%-32px))] max-w-none overflow-hidden rounded-[18px] border-0 bg-white p-0 text-[.95rem] leading-[1.55] text-[#0f172a] shadow-[0_0_0_1px_rgba(15,23,42,.06),0_28px_64px_-18px_rgba(15,23,42,.45)] backdrop:bg-[rgba(15,23,42,.55)] backdrop:backdrop-blur-[2px] open:flex open:flex-col motion-safe:open:animate-[fm-in_.22s_cubic-bezier(.2,.8,.2,1)] motion-safe:open:backdrop:animate-[fm-fade_.22s_ease-out] max-[600px]:max-h-[calc(100dvh-32px)]" id="fm-news" aria-labelledby="fm-news-title" aria-describedby="fm-news-desc">
  <form class="m-0 grid min-h-0 flex-1 grid-cols-1 md:grid-cols-[minmax(0,5fr)_minmax(0,7fr)]" method="dialog">
    <div class="relative h-[140px] bg-[#d9d2c3] md:h-auto md:min-h-[180px]"><img class="absolute inset-0 size-full object-cover object-[center_62%] md:object-center" src="https://images.unsplash.com/photo-1517842645767-c639042777db?auto=format&fit=crop&w=640&h=820&q=70" width="640" height="820" alt="An open notebook with a handwritten Notes heading, a fountain pen and reading glasses on a wooden desk"></div>
    <div class="self-center px-5 pt-[22px] pb-6 md:px-10 md:pt-12 md:pb-10">
      <p class="mt-0 mb-2.5 text-[.75rem] font-bold tracking-[.1em] text-[#0f766e] uppercase">The Friday Field Notes</p>
      <h2 class="m-0 text-[1.35rem] leading-[1.2] font-bold tracking-[-.02em] md:text-[1.6rem]" id="fm-news-title">One useful idea, every Friday</h2>
      <p class="mt-2.5 mb-[22px] text-[#475569]" id="fm-news-desc">A short letter on reading, note-taking and research habits, with one method you can try before Monday.</p>
      <label class="text-[.85rem] leading-[1.3] font-semibold text-[#0f172a]" for="fm-news-email">Email address</label>
      <div class="mt-1.5 flex flex-wrap gap-2">
        <input class="w-full rounded-[10px] border border-[#848e9d] bg-white px-3.5 text-[.95rem] text-[#0f172a] transition-[border-color,box-shadow] duration-150 placeholder:text-[#6b7280] hover:border-[#5f6b7b] focus-visible:border-[#0f766e] focus-visible:shadow-[0_0_0_3px_rgba(15,118,110,.24)] focus-visible:outline-none user-invalid:border-[#b91c1c] user-invalid:focus-visible:shadow-[0_0_0_3px_rgba(185,28,28,.2)] motion-reduce:transition-none h-11 w-auto min-w-0 flex-[1_1_190px]" id="fm-news-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required autofocus>
        <button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#0f766e] bg-[#0f766e] text-white shadow-[0_6px_16px_-8px_rgba(15,118,110,.75)] hover:border-[#115e59] hover:bg-[#115e59] max-[600px]:flex-auto" value="subscribe">Subscribe</button>
      </div>
      <p class="mt-3 mb-0 text-[.8rem] text-[#5b6474]">12,400 readers. One email a week. Unsubscribe any time.</p>
    </div>
    <button class="absolute top-3 right-3 z-[1] grid size-9 cursor-pointer place-items-center border-0 p-0 transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0f766e] motion-reduce:transition-none rounded-full bg-white/90 text-[#0f172a] shadow-[0_1px_3px_rgba(15,23,42,.2)] backdrop-blur-[6px] hover:bg-white" value="close" formnovalidate aria-label="Close"><svg class="size-5" aria-hidden="true"><use href="#fm-i-x"/></svg></button>
  </form>
</dialog>

<!-- Feedback with rating -->
<dialog class="m-auto max-h-[calc(100dvh-56px)] w-[min(500px,calc(100%-32px))] max-w-none overflow-hidden rounded-[18px] border-0 bg-white p-0 text-[.95rem] leading-[1.55] text-[#0f172a] shadow-[0_0_0_1px_rgba(15,23,42,.06),0_28px_64px_-18px_rgba(15,23,42,.45)] backdrop:bg-[rgba(15,23,42,.55)] backdrop:backdrop-blur-[2px] open:flex open:flex-col motion-safe:open:animate-[fm-in_.22s_cubic-bezier(.2,.8,.2,1)] motion-safe:open:backdrop:animate-[fm-fade_.22s_ease-out] max-[600px]:max-h-[calc(100dvh-32px)]" id="fm-feedback" aria-labelledby="fm-feedback-title">
  <form class="m-0 flex min-h-0 flex-1 flex-col" method="dialog">
    <button class="absolute top-3 right-3 z-[1] grid size-9 cursor-pointer place-items-center border-0 p-0 transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0f766e] motion-reduce:transition-none rounded-[10px] bg-transparent text-[#475569] hover:bg-[#f3efe7] hover:text-[#0f172a]" value="close" formnovalidate aria-label="Close"><svg class="size-5" aria-hidden="true"><use href="#fm-i-x"/></svg></button>
    <div class="flex items-center gap-3.5 pt-6 pr-[60px] pb-1 pl-6 max-[600px]:pt-5 max-[600px]:pr-14 max-[600px]:pb-0.5 max-[600px]:pl-[18px]">
      <img class="size-12 flex-none rounded-full object-cover shadow-[0_0_0_3px_#fff,0_0_0_4px_#e8e3d9]" src="https://images.unsplash.com/photo-1580489944761-15a19d654956?auto=format&fit=crop&crop=faces&w=96&h=96&q=70" width="48" height="48" alt="Priya Raman">
      <div><h2 class="m-0 text-[1.1rem] leading-[1.35] font-bold tracking-[-.015em]" id="fm-feedback-title">How was your onboarding call with Priya?</h2><p class="mt-0.5 mb-0 text-[.85rem] text-[#475569]">30 minutes · Tuesday 22 September</p></div>
    </div>
    <div class="min-h-0 flex-auto overflow-y-auto overscroll-contain px-6 pt-5 pb-[22px] max-[600px]:px-[18px] max-[600px]:pt-[18px] max-[600px]:pb-5">
      <fieldset class="m-0 mb-6 min-w-0 border-0 p-0">
        <legend class="mb-2 block p-0 text-[.85rem] leading-[1.3] font-semibold text-[#0f172a]">Rate the call <span class="ml-1.5 text-[.8rem] font-normal text-[#5b6474]">1 is poor, 5 is excellent</span></legend>
        <div class="-ml-1.5 flex [--fm-show:var(--fm-hover,var(--fm-rate,0))] has-[[value='1']:checked]:[--fm-rate:1] has-[[value='2']:checked]:[--fm-rate:2] has-[[value='3']:checked]:[--fm-rate:3] has-[[value='4']:checked]:[--fm-rate:4] has-[[value='5']:checked]:[--fm-rate:5] has-[label:nth-child(1):hover]:[--fm-hover:1] has-[label:nth-child(2):hover]:[--fm-hover:2] has-[label:nth-child(3):hover]:[--fm-hover:3] has-[label:nth-child(4):hover]:[--fm-hover:4] has-[label:nth-child(5):hover]:[--fm-hover:5]">
          <label class="group grid size-[46px] cursor-pointer place-items-center rounded-[10px] has-focus-visible:outline-2 has-focus-visible:-outline-offset-2 has-focus-visible:outline-[#0f766e] [--i:1]"><input class="sr-only" type="radio" name="rating" value="1" required><span class="sr-only">1 star</span><svg class="size-[34px] fill-[#f59e0b] [fill-opacity:clamp(0,calc(var(--fm-show)_-_var(--i)_+_1),1)] stroke-[#b45309] [stroke-width:1.4] [stroke-linejoin:round] transition-[fill-opacity,scale] duration-150 motion-safe:group-hover:scale-[1.12] motion-reduce:transition-none" aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="group grid size-[46px] cursor-pointer place-items-center rounded-[10px] has-focus-visible:outline-2 has-focus-visible:-outline-offset-2 has-focus-visible:outline-[#0f766e] [--i:2]"><input class="sr-only" type="radio" name="rating" value="2"><span class="sr-only">2 stars</span><svg class="size-[34px] fill-[#f59e0b] [fill-opacity:clamp(0,calc(var(--fm-show)_-_var(--i)_+_1),1)] stroke-[#b45309] [stroke-width:1.4] [stroke-linejoin:round] transition-[fill-opacity,scale] duration-150 motion-safe:group-hover:scale-[1.12] motion-reduce:transition-none" aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="group grid size-[46px] cursor-pointer place-items-center rounded-[10px] has-focus-visible:outline-2 has-focus-visible:-outline-offset-2 has-focus-visible:outline-[#0f766e] [--i:3]"><input class="sr-only" type="radio" name="rating" value="3"><span class="sr-only">3 stars</span><svg class="size-[34px] fill-[#f59e0b] [fill-opacity:clamp(0,calc(var(--fm-show)_-_var(--i)_+_1),1)] stroke-[#b45309] [stroke-width:1.4] [stroke-linejoin:round] transition-[fill-opacity,scale] duration-150 motion-safe:group-hover:scale-[1.12] motion-reduce:transition-none" aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="group grid size-[46px] cursor-pointer place-items-center rounded-[10px] has-focus-visible:outline-2 has-focus-visible:-outline-offset-2 has-focus-visible:outline-[#0f766e] [--i:4]"><input class="sr-only" type="radio" name="rating" value="4"><span class="sr-only">4 stars</span><svg class="size-[34px] fill-[#f59e0b] [fill-opacity:clamp(0,calc(var(--fm-show)_-_var(--i)_+_1),1)] stroke-[#b45309] [stroke-width:1.4] [stroke-linejoin:round] transition-[fill-opacity,scale] duration-150 motion-safe:group-hover:scale-[1.12] motion-reduce:transition-none" aria-hidden="true"><use href="#fm-i-star"/></svg></label>
          <label class="group grid size-[46px] cursor-pointer place-items-center rounded-[10px] has-focus-visible:outline-2 has-focus-visible:-outline-offset-2 has-focus-visible:outline-[#0f766e] [--i:5]"><input class="sr-only" type="radio" name="rating" value="5"><span class="sr-only">5 stars</span><svg class="size-[34px] fill-[#f59e0b] [fill-opacity:clamp(0,calc(var(--fm-show)_-_var(--i)_+_1),1)] stroke-[#b45309] [stroke-width:1.4] [stroke-linejoin:round] transition-[fill-opacity,scale] duration-150 motion-safe:group-hover:scale-[1.12] motion-reduce:transition-none" aria-hidden="true"><use href="#fm-i-star"/></svg></label>
        </div>
      </fieldset>
      <fieldset class="m-0 mb-6 min-w-0 border-0 p-0">
        <legend class="mb-2 block p-0 text-[.85rem] leading-[1.3] font-semibold text-[#0f172a]">What went well? <span class="font-normal text-[#5b6474]">(optional)</span></legend>
        <div class="flex flex-wrap gap-2">
          <label class="group inline-flex min-h-[38px] cursor-pointer items-center gap-1.5 rounded-full border border-[#d4cdbf] bg-white py-1.5 pr-3.5 pl-[11px] text-[.85rem] font-medium text-[#334155] transition-colors duration-150 hover:border-[#a8a090] hover:bg-[#faf8f4] has-checked:border-[#0f766e] has-checked:bg-[#e6f4f1] has-checked:text-[#115e59] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-[#0f766e] motion-reduce:transition-none"><input class="sr-only" type="checkbox" name="good" value="clear"><svg class="size-4 flex-none group-has-checked:hidden" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="hidden size-4 flex-none group-has-checked:block" aria-hidden="true"><use href="#fm-i-check"/></svg>Clear answers</label>
          <label class="group inline-flex min-h-[38px] cursor-pointer items-center gap-1.5 rounded-full border border-[#d4cdbf] bg-white py-1.5 pr-3.5 pl-[11px] text-[.85rem] font-medium text-[#334155] transition-colors duration-150 hover:border-[#a8a090] hover:bg-[#faf8f4] has-checked:border-[#0f766e] has-checked:bg-[#e6f4f1] has-checked:text-[#115e59] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-[#0f766e] motion-reduce:transition-none"><input class="sr-only" type="checkbox" name="good" value="on-time"><svg class="size-4 flex-none group-has-checked:hidden" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="hidden size-4 flex-none group-has-checked:block" aria-hidden="true"><use href="#fm-i-check"/></svg>On time</label>
          <label class="group inline-flex min-h-[38px] cursor-pointer items-center gap-1.5 rounded-full border border-[#d4cdbf] bg-white py-1.5 pr-3.5 pl-[11px] text-[.85rem] font-medium text-[#334155] transition-colors duration-150 hover:border-[#a8a090] hover:bg-[#faf8f4] has-checked:border-[#0f766e] has-checked:bg-[#e6f4f1] has-checked:text-[#115e59] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-[#0f766e] motion-reduce:transition-none"><input class="sr-only" type="checkbox" name="good" value="examples"><svg class="size-4 flex-none group-has-checked:hidden" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="hidden size-4 flex-none group-has-checked:block" aria-hidden="true"><use href="#fm-i-check"/></svg>Useful examples</label>
          <label class="group inline-flex min-h-[38px] cursor-pointer items-center gap-1.5 rounded-full border border-[#d4cdbf] bg-white py-1.5 pr-3.5 pl-[11px] text-[.85rem] font-medium text-[#334155] transition-colors duration-150 hover:border-[#a8a090] hover:bg-[#faf8f4] has-checked:border-[#0f766e] has-checked:bg-[#e6f4f1] has-checked:text-[#115e59] has-focus-visible:outline-2 has-focus-visible:outline-offset-2 has-focus-visible:outline-[#0f766e] motion-reduce:transition-none"><input class="sr-only" type="checkbox" name="good" value="friendly"><svg class="size-4 flex-none group-has-checked:hidden" aria-hidden="true"><use href="#fm-i-plus"/></svg><svg class="hidden size-4 flex-none group-has-checked:block" aria-hidden="true"><use href="#fm-i-check"/></svg>Friendly</label>
        </div>
      </fieldset>
      <div class="flex flex-col gap-1.5">
        <label class="text-[.85rem] leading-[1.3] font-semibold text-[#0f172a]" for="fm-feedback-better">Anything we could do better? <span class="font-normal text-[#5b6474]">(optional)</span></label>
        <textarea class="w-full rounded-[10px] border border-[#848e9d] bg-white px-3.5 text-[.95rem] text-[#0f172a] transition-[border-color,box-shadow] duration-150 placeholder:text-[#6b7280] hover:border-[#5f6b7b] focus-visible:border-[#0f766e] focus-visible:shadow-[0_0_0_3px_rgba(15,118,110,.24)] focus-visible:outline-none user-invalid:border-[#b91c1c] user-invalid:focus-visible:shadow-[0_0_0_3px_rgba(185,28,28,.2)] motion-reduce:transition-none block h-auto min-h-[104px] resize-y py-2.5 leading-[1.5]" id="fm-feedback-better" name="better" rows="4" placeholder="For example: more time on tagging and saved searches"></textarea>
      </div>
    </div>
    <div class="flex flex-wrap justify-end gap-2.5 border-t border-[#e8e3d9] bg-[#faf8f4] px-6 py-4 max-[600px]:px-[18px] max-[600px]:py-3.5">
      <button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#d4cdbf] bg-white text-[#0f172a] hover:border-[#a8a090] hover:bg-[#f7f4ee] max-[600px]:flex-auto" value="later" formnovalidate>Not now</button>
      <button class="inline-flex min-h-11 cursor-pointer items-center justify-center gap-2 rounded-[10px] border px-[18px] py-2.5 text-[.9rem] leading-[1.2] font-semibold no-underline transition-[background-color,border-color,box-shadow] duration-150 focus-visible:outline-2 focus-visible:outline-[#0f766e] focus-visible:outline-offset-2 motion-reduce:transition-none border-[#0f766e] bg-[#0f766e] text-white shadow-[0_6px_16px_-8px_rgba(15,118,110,.75)] hover:border-[#115e59] hover:bg-[#115e59] max-[600px]:flex-auto" value="send">Send feedback</button>
    </div>
  </form>
</dialog>
CSS
/* Entrance keyframes for the dialog and its backdrop (used through motion-safe:open:animate-[…]) */
@keyframes fm-in { from { opacity: 0; transform: translateY(10px) scale(.97); } }
@keyframes fm-fade { from { opacity: 0; } }
/* Stop the page scrolling behind an open modal */
html:has(dialog:modal) { overflow: hidden; }
JavaScript
// Open a dialog from any button with data-open="dialog-id". showModal() traps focus, closes on Esc
// and makes the page behind inert. Buttons inside <form method="dialog"> close it with no script;
// the browser checks required and type="email" first, so an invalid form stays open.
document.querySelectorAll('[data-open]').forEach(function (btn) {
  var dialog = document.getElementById(btn.getAttribute('data-open'));
  if (!dialog) return;
  btn.addEventListener('click', function () {
    dialog.showModal();
    dialog.addEventListener('close', function () { btn.focus(); }, { once: true }); // focus back to the trigger
  });
  // A click on the backdrop lands on the <dialog> itself (its form fills the box), so close it
  dialog.addEventListener('click', function (e) { if (e.target === dialog) dialog.close(); });
});

About this set

Form Modals is three dialogs for a note-taking app called Fieldnote, each built around a real form. Login / sign up is a centred sign-in card with a Continue with SSO button, an or divider, labelled email and password fields, Remember me, a Forgot password link and a Create an account footer. Newsletter is a two-column subscribe popup with a photo on the left that turns into a short photo band on phones, with an inline email field and Subscribe button. Feedback with rating asks how an onboarding call went, with a five-star rating, optional checkbox chips and a comment box. Use the first behind a sign-in button, the second for an opt-in prompt and the third after a finished task. In the plain CSS and Tailwind versions each dialog holds a form with method=dialog, so the browser’s own required and type=email checks must pass before the submit button closes it, while Not now and the X use formnovalidate. The Bootstrap version uses the Modal plugin with .form-control, .form-check and .btn-check, plus a few lines that hide the modal only after a valid submit. The stars are visually hidden radios: :has() works out how many are checked or hovered, and each star’s fill-opacity follows that number, with no script.

What’s included

  • Native validation: required and type=email keep the dialog open until the form is valid
  • 44px fields with labels above them, a visible focus ring and a red border on invalid input
  • Five-star rating from radio inputs, filled on hover and on check with CSS only
  • Checkbox pill chips (.btn-check in Bootstrap) that swap a plus icon for a check mark
  • Newsletter layout that stacks into a photo band on phones, with a close button on a solid chip

Accessibility

Each dialog is named by its visible heading through aria-labelledby, and the newsletter is also described by its intro sentence. Every field has a visible label and the right autocomplete token; the rating is a fieldset with a legend whose visually hidden radios are named 1 star to 5 stars, so arrow keys move through them and the focused star gets a ring. Focus goes to the email field when the sign-in and newsletter dialogs open (in Bootstrap through a shown.bs.modal listener) and back to the trigger when any dialog closes. The entrance animation, transitions and the star hover scale are switched off under prefers-reduced-motion.

Customise it

In the plain version change --fm-accent, --fm-field (the input border) and --fm-w on .fm. In Bootstrap edit --bs-modal-width and the --bs-btn-* values on .fm-btn and .fm-chip, plus the .form-control rules in the style block; in Tailwind replace #0f766e and the w-[min(…)] width in the class lists.