Focus Trap

Restricts focus within an active dialog.

Background page (inert while dialog is open)

html

<div class="ft-demo" style="--accent:#E8A020;">
  <div class="ft-demo__page">Background page (inert while dialog is open)</div>
  <div class="ft-demo__dialog" style="border-radius:12px;" role="dialog" aria-modal="true">
    <p class="ft-demo__title">Delete file?</p>
    <div class="ft-demo__row">
      <button type="button" class="ft-demo__btn">Cancel</button>
      <button type="button" class="ft-demo__btn ft-demo__btn--primary">Delete</button>
    </div>
    <p class="ft-demo__loop">&#8635; Tab from Delete wraps back to Cancel</p>
    {{unless trapped}}<p class="ft-demo__leak">&#8594; Tab from Delete escapes to the page behind</p>

tailwind

<div style="width:320px;font-family:system-ui,sans-serif;color:#241430">
  <div class="p-3.5 rounded-lg mb-3 text-xs" style="background:#24143010;color:#241430aa">Background page (inert while dialog is open)</div>
  <div role="dialog" aria-modal="true" class="p-4" style="border-radius:12px;background:#FAF7F2;box-shadow:0 12px 32px rgba(36,20,48,.22)">
    <p class="m-0 mb-3 font-bold">Delete file?</p>
    <div class="flex gap-2">
      <button type="button" class="px-4 py-2 rounded-md border" style="border-color:#24143033">Cancel</button>
      <button type="button" class="px-4 py-2 rounded-md font-semibold border" style="background:#E8A020;border-color:#E8A020">Delete</button>
    </div>
    <p class="mt-3 text-xs" style="color:#1a7a44">&#8635; Tab from Delete wraps back to Cancel</p>
    {{unless trapped}}<p class="mt-3 text-xs" style="color:#b3261e">&#8594; Tab from Delete escapes to the page behind</p>

react

export function FocusTrap({
  title = "Delete file?",
  trapped = true,
  accent = "#E8A020",
  radius = 12,
}) {
  return (
    <div style={{ width: 320, fontFamily: "system-ui, sans-serif", fontSize: 14, color: "#241430" }}>
      <div style={{ padding: 14, borderRadius: 10, background: "#24143010", color: "#241430aa", fontSize: 12, marginBottom: 12 }}>Background page (inert while dialog is open)</div>
      <div role="dialog" aria-modal="true" style={{ padding: 16, background: "#FAF7F2", boxShadow: "0 12px 32px rgba(36,20,48,.22)", borderRadius: radius }}>
        <p style={{ margin: "0 0 12px", fontWeight: 700 }}>{title}</p>
        <div style={{ display: "flex", gap: 8 }}>
          <button type="button" style={{ padding: "9px 16px", borderRadius: 7, border: "1.5px solid #24143033", background: "transparent", cursor: "pointer" }}>Cancel</button>
          <button type="button" style={{ padding: "9px 16px", borderRadius: 7, border: "1.5px solid " + accent, background: accent, fontWeight: 600, cursor: "pointer" }}>Delete</button>
        </div>
        {trapped
          ? <p style={{ margin: "12px 0 0", fontSize: 12, color: "#1a7a44" }}>&#8635; Tab from Delete wraps back to Cancel</p>
          : <p style={{ margin: "12px 0 0", fontSize: 12, color: "#b3261e" }}>&#8594; Tab from Delete escapes to the page behind</p>}
      </div>
    </div>
  );
}