Open demo

CSS code

HTML
<dialog class="lf-dialog" id="lf-signin" aria-labelledby="lf-dtitle" aria-describedby="lf-dsub" closedby="none">
  <p class="lf-tag">Session expired</p>
  <h1 id="lf-dtitle">Sign in to continue</h1>
  <p class="lf-sub" id="lf-dsub">Your work is saved. Sign in again to get back to the Q3 rollout board.</p>
  <form onsubmit="event.preventDefault()">
    <label class="lf-label" for="mmail">Email address</label>
    <input class="lf-field" id="mmail" name="username" type="email" value="[email protected]" autocomplete="username" readonly>
    <label class="lf-label" for="mpw">Password</label>
    <input class="lf-field" id="mpw" name="password" type="password" autocomplete="current-password" placeholder="••••••••" required autofocus>
    <button class="lf-btn" type="submit">Unlock</button>
    <button class="lf-ghost" type="button">Sign in as someone else</button>
  </form>
</dialog>
CSS
/* Session-expired sign-in in a native <dialog>. showModal() makes the page behind inert, traps focus and
   paints ::backdrop, which dims and blurs your app. closedby="none" and the cancel handler keep Escape
   from dismissing it: the only ways out are signing in or switching account. */
.lf-dialog{--lf-brand:#4f46e5;--lf-brand-dark:#4338ca;--lf-ink:#111827;--lf-muted:#6b7280;--lf-edge:#868c99;
  box-sizing:border-box;width:calc(100% - 40px);max-width:394px;border:0;border-radius:18px;padding:34px 32px;
  background:#fff;color:var(--lf-ink);line-height:normal;box-shadow:0 26px 70px rgba(12,18,38,.34);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
.lf-dialog::backdrop{background:rgba(17,22,38,.34);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px)}
.lf-dialog *,.lf-dialog *::before,.lf-dialog *::after{box-sizing:border-box}
.lf-dialog :where(h1,p,form){margin:0}
.lf-dialog :where(input,button){font:inherit}
.lf-dialog :focus-visible{outline:2px solid var(--lf-brand);outline-offset:2px}
.lf-tag{display:inline-flex;align-items:center;gap:7px;font-size:.78rem;font-weight:700;color:#92400e;
  background:#fffbeb;border:1px solid #fde68a;padding:5px 11px;border-radius:999px;margin-bottom:16px}
.lf-dialog h1{font-size:1.32rem;letter-spacing:-.02em;margin-bottom:6px}
.lf-sub{color:var(--lf-muted);font-size:.91rem;margin-bottom:24px;line-height:1.55}
.lf-label{display:block;font-size:.84rem;font-weight:600;margin-bottom:6px}
.lf-field{width:100%;padding:12px 14px;border:1px solid var(--lf-edge);border-radius:10px;font-size:.95rem;margin-bottom:18px;
  background:#fff;color:inherit;transition:border-color .15s,box-shadow .15s}
.lf-field::placeholder{color:var(--lf-muted)}
.lf-field:focus{outline:none;border-color:var(--lf-brand);box-shadow:0 0 0 3px rgba(79,70,229,.15)}
.lf-btn{width:100%;padding:13px;border:0;border-radius:10px;background:var(--lf-brand);color:#fff;
  font-size:.96rem;font-weight:700;cursor:pointer;transition:background .15s}
.lf-btn:hover{background:var(--lf-brand-dark)}
.lf-ghost{width:100%;padding:11px;margin-top:10px;border:0;background:none;color:var(--lf-muted);
  font-size:.89rem;font-weight:600;cursor:pointer;border-radius:10px;transition:background .15s}
.lf-ghost:hover{background:#f4f5fa}
@media (prefers-reduced-motion:reduce){.lf-field,.lf-btn,.lf-ghost{transition:none}}
JavaScript
(() => {
  const dlg = document.getElementById('lf-signin');
  if (!dlg || typeof dlg.showModal !== 'function') return;
  dlg.addEventListener('cancel', e => e.preventDefault()); // Escape must not reveal the expired session
  if (!dlg.open) dlg.showModal(); // call this when your app detects the expired session
})();

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<div class="modal lf-modal" id="lf-signin" tabindex="-1" data-bs-backdrop="false" data-bs-keyboard="false" aria-labelledby="lf-dtitle" aria-describedby="lf-dsub">
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <div class="modal-body">
        <p class="lf-tag d-inline-flex align-items-center rounded-pill">Session expired</p>
        <h1 id="lf-dtitle">Sign in to continue</h1>
        <p class="lf-sub text-body-secondary" id="lf-dsub">Your work is saved. Sign in again to get back to the Q3 rollout board.</p>
        <form onsubmit="event.preventDefault()">
          <label class="form-label" for="mmail">Email address</label>
          <input class="form-control" id="mmail" name="username" type="email" value="[email protected]" autocomplete="username" readonly>
          <label class="form-label" for="mpw">Password</label>
          <input class="form-control" id="mpw" name="password" type="password" autocomplete="current-password" placeholder="••••••••" required>
          <button class="btn btn-brand w-100" type="submit">Unlock</button>
          <button class="btn btn-ghost w-100" type="button">Sign in as someone else</button>
        </form>
      </div>
    </div>
  </div>
</div>
CSS
/* Bootstrap .modal (bundle JS) with a static backdrop and the keyboard close switched off, so signing in or
   switching account are the only ways out. The .modal element itself dims and blurs your app. */
.lf-modal{--lf-brand:#4f46e5;--lf-brand-dark:#4338ca;--lf-ink:#111827;--lf-muted:#6b7280;--lf-edge:#868c99;
  --bs-body-color:var(--lf-ink);--bs-secondary-color:var(--lf-muted);--bs-border-color:var(--lf-edge);
  --bs-modal-width:394px;--bs-modal-margin:20px;--bs-modal-border-width:0;--bs-modal-border-radius:18px;--bs-modal-padding:34px 32px;
  --bs-modal-box-shadow:0 26px 70px rgba(12,18,38,.34);
  background:rgba(17,22,38,.34);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);line-height:normal}
.lf-modal .modal-content{box-shadow:var(--bs-modal-box-shadow)}
.lf-modal :focus-visible{outline:2px solid var(--lf-brand);outline-offset:2px}
.lf-tag{gap:7px;font-size:.78rem;font-weight:700;color:#92400e;background:#fffbeb;border:1px solid #fde68a;padding:5px 11px;margin-bottom:16px}
.lf-modal h1{font-size:1.32rem;font-weight:700;line-height:normal;letter-spacing:-.02em;margin-bottom:6px}
.lf-sub{font-size:.91rem;margin-bottom:24px;line-height:1.55}
.lf-modal .form-label{font-size:.84rem;font-weight:600;margin-bottom:6px}
.lf-modal .form-control{padding:12px 14px;border-radius:10px;font-size:.95rem;line-height:normal;margin-bottom:18px;transition:border-color .15s,box-shadow .15s}
.lf-modal .form-control[readonly]{background-color:#fff}
.lf-modal .form-control:focus{border-color:var(--lf-brand);box-shadow:0 0 0 3px rgba(79,70,229,.15);outline:none}
.lf-modal .form-control::placeholder{color:var(--lf-muted)}
.lf-modal .btn{--bs-btn-line-height:normal;--bs-btn-focus-box-shadow:none;--bs-btn-border-radius:10px;--bs-btn-border-width:0;transition:background-color .15s}
.lf-modal .btn-brand{--bs-btn-padding-y:13px;--bs-btn-font-size:.96rem;--bs-btn-font-weight:700;
  --bs-btn-bg:var(--lf-brand);--bs-btn-color:#fff;--bs-btn-hover-bg:var(--lf-brand-dark);--bs-btn-hover-color:#fff;
  --bs-btn-active-bg:var(--lf-brand-dark);--bs-btn-active-color:#fff}
.lf-modal .btn-ghost{--bs-btn-padding-y:11px;--bs-btn-font-size:.89rem;--bs-btn-font-weight:600;
  --bs-btn-color:var(--lf-muted);--bs-btn-hover-bg:#f4f5fa;--bs-btn-hover-color:var(--lf-muted);--bs-btn-active-bg:#f4f5fa;--bs-btn-active-color:var(--lf-muted);margin-top:10px}
@media (prefers-reduced-motion:reduce){.lf-modal .form-control,.lf-modal .btn{transition:none}}
JavaScript
(() => {
  const el = document.getElementById('lf-signin');
  if (!el || !window.bootstrap) return;
  // Bootstrap focuses the modal itself; move focus on to the password field once it is shown.
  el.addEventListener('shown.bs.modal', () => document.getElementById('mpw').focus());
  bootstrap.Modal.getOrCreateInstance(el).show(); // call this when your app detects the expired session
})();

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<dialog class="m-auto w-[calc(100%-40px)] max-w-[394px] rounded-[18px] bg-white px-8 py-[34px] font-[-apple-system,BlinkMacSystemFont,'Segoe_UI',Roboto,Helvetica,Arial,sans-serif] leading-[normal] text-ink shadow-[0_26px_70px_rgba(12,18,38,.34)] backdrop:bg-[rgba(17,22,38,.34)] backdrop:backdrop-blur-[7px]" id="lf-signin" aria-labelledby="lf-dtitle" aria-describedby="lf-dsub" closedby="none">
  <p class="mb-4 inline-flex items-center gap-[7px] rounded-full border border-[#fde68a] bg-[#fffbeb] px-[11px] py-[5px] text-[.78rem] font-bold text-[#92400e]">Session expired</p>
  <h1 class="mb-1.5 text-[1.32rem] font-bold tracking-[-.02em]" id="lf-dtitle">Sign in to continue</h1>
  <p class="mb-6 text-[.91rem] leading-[1.55] text-muted" id="lf-dsub">Your work is saved. Sign in again to get back to the Q3 rollout board.</p>
  <form onsubmit="event.preventDefault()">
    <label class="mb-1.5 block text-[.84rem] font-semibold" for="mmail">Email address</label>
    <input class="mb-[18px] w-full rounded-[10px] border border-edge bg-white px-3.5 py-3 text-[.95rem] transition-[border-color,box-shadow] duration-150 focus:border-brand focus:shadow-[0_0_0_3px_rgba(79,70,229,.15)] focus:outline-none motion-reduce:transition-none"
           id="mmail" name="username" type="email" value="[email protected]" autocomplete="username" readonly>
    <label class="mb-1.5 block text-[.84rem] font-semibold" for="mpw">Password</label>
    <input class="mb-[18px] w-full rounded-[10px] border border-edge bg-white px-3.5 py-3 text-[.95rem] transition-[border-color,box-shadow] duration-150 placeholder:text-muted focus:border-brand focus:shadow-[0_0_0_3px_rgba(79,70,229,.15)] focus:outline-none motion-reduce:transition-none"
           id="mpw" name="password" type="password" autocomplete="current-password" placeholder="••••••••" required autofocus>
    <button class="w-full cursor-pointer rounded-[10px] bg-brand p-[13px] text-[.96rem] font-bold text-white transition-[background-color,border-color,color] duration-150 hover:bg-brand-dark focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand motion-reduce:transition-none" type="submit">Unlock</button>
    <button class="mt-2.5 w-full cursor-pointer rounded-[10px] p-[11px] text-[.89rem] font-semibold text-muted transition-[background-color,border-color,color] duration-150 hover:bg-[#f4f5fa] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand motion-reduce:transition-none" type="button">Sign in as someone else</button>
  </form>
</dialog>
CSS
/* A native <dialog> opened with showModal(): the page behind is inert, focus is trapped, and the backdrop:
   utilities dim and blur your app. closedby="none" and the cancel handler keep Escape from dismissing it. */
@theme {
  --color-brand: #4f46e5;
  --color-brand-dark: #4338ca;
  --color-ink: #111827;
  --color-muted: #6b7280;
  --color-edge: #868c99;
}
JavaScript
(() => {
  const dlg = document.getElementById('lf-signin');
  if (!dlg || typeof dlg.showModal !== 'function') return;
  dlg.addEventListener('cancel', e => e.preventDefault()); // Escape must not reveal the expired session
  if (!dlg.open) dlg.showModal(); // call this when your app detects the expired session
})();

About this set

A sign-in dialog that opens over the application when a session expires, so people can re-authenticate without losing their place. The dialog shows a Session expired tag, a heading, a line saying the work is saved, the account email as a read-only field, a password field that receives focus, an Unlock button and a quieter Sign in as someone else button. Behind it the app is dimmed and blurred. In plain CSS and Tailwind it is a native dialog element opened with showModal(), which makes the rest of the page inert, traps focus inside and paints the ::backdrop that does the dimming and blur. Escape is blocked with closedby=”none” and a cancel handler, because closing would show an expired session; signing in or switching account are the ways out. The Bootstrap version uses the .modal component and bundle JavaScript with the backdrop and keyboard close switched off, and moves focus to the password field once shown. The blurred dashboard in the demo is a stand-in for your own app and is not part of the snippet.

What’s included

  • Native dialog with showModal(), inert background and focus trap
  • Backdrop dims and blurs whatever page sits behind
  • Escape cannot dismiss an expired-session prompt
  • Read-only account email with the password field focused
  • Bootstrap version built on .modal with a static setup

Accessibility

The dialog is labelled by its heading and described by the saved-work line. Focus starts in the password field and stays inside the dialog; Tab reaches Unlock and Sign in as someone else, both with a 2px outline, and the fields show a brand ring. The page behind is inert while the dialog is open, and transitions are removed under prefers-reduced-motion.

Customise it

Change --lf-brand, --lf-muted and --lf-edge on .lf-dialog (plain CSS) or .lf-modal (Bootstrap), or the --color-* theme values (Tailwind). The dim and blur are the ::backdrop rule, the .lf-modal background, or the backdrop: utilities.