Focus Trap

포커스 트랩

The technique that confines Tab cycling to a modal's own focusable elements while it's open, so focus never leaks to the page behind it.

···
html
<div class="stage">
  <div class="bg">
    <div class="ghost"></div><div class="ghost"></div><div class="ghost"></div>
  </div>
  <div class="modal">
    <div class="mh"></div>
    <div class="field f1"></div>
    <div class="row">
      <button class="f2"></button>
      <button class="f3"></button>
    </div>
  </div>
</div>
css
.stage{position:relative;width:94%;height:88%}
.bg{position:absolute;inset:0;display:flex;gap:10%;padding:10%;filter:blur(1.5px);opacity:.35}
.ghost{flex:1;border-radius:10px;background:var(--muted)}
.modal{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:70%;height:76%;border-radius:14px;border:1px solid var(--line);background:var(--surface);box-shadow:0 12px 30px rgba(0,0,0,.18);padding:9%;display:flex;flex-direction:column;gap:10%}
.mh{width:50%;height:12%;border-radius:5px;background:var(--fg);opacity:.7}
.field{height:20%;border-radius:8px;border:1px solid var(--line);background:var(--bg)}
.row{display:flex;gap:8%;flex:1}
.row button{flex:1;border-radius:8px;border:1px solid var(--line);background:var(--bg)}
.ring{box-shadow:0 0 0 3px var(--accent)}
js
const order = ['.f1', '.f2', '.f3'];
let i = 0;
function tick() {
  order.forEach(function (sel) { document.querySelector(sel).classList.remove('ring'); });
  document.querySelector(order[i % order.length]).classList.add('ring');
  i++;
}
tick();
setInterval(tick, 900);

Open a modal, keep pressing Tab from its last element, and with no handling in place, focus slides right off into the page content hidden behind it — invisible on screen but still read aloud by a screen reader, a deeply confusing state. A focus trap wires the modal's last element back to its first, so Tab only ever cycles within it.

Despite the name, the goal isn't to imprison the user — it's to make the **one way out** unmistakable, via Escape or a close button, always reachable. A trap with no way out is itself a violation of 2.1.2 (No Keyboard Trap).

The demo auto-cycles Tab through three elements inside a modal: from the last one it wraps back to the first, and focus never crosses into the dimmed background.

When to use

Prefer a battle-tested library (focus-trap, Radix) or the native <dialog> element's showModal() over hand-rolling it — <dialog> gives you the trap and Escape-to-close for free.