Chapter 04

The silent dialogue

A dialog is an interruption that asks permission. When it really must speak, it does so with the keyboard in hand: focus where it belongs, Escape that mends, motion that enters like a sigh and never like a shout.

7 min read

There’s a silent cruelty in badly made dialogs: they steal focus mid-thought, they make you hunt for the “Cancel” button with your mouse, they slam the door on the context underneath. The best dialog does the opposite — it knows it’s an interruption and behaves like a guest: it knocks, it waits for you to be ready, and it leaves the room in the state it found it.

Behind a dialog hide ten responsibilities. Where does focus go on open? Where does it return on close? Is the keyboard really trapped inside, or does a clumsy Tab leak out? Can you exit with Escape? Is the background really non-interactive, even to a screen reader? Does clicking the backdrop close it — always, never, or sometimes? The answer to each question is a small decision of respect.

We open six different dialogs — from the friendliest (an overlay that explains its own language) to the strictest (the confirmation of an irreversible action) — and we show how each one stays quiet in a different way.

The overlay that explains its own language

Keyboard shortcuts are a gift you make only to those who discover them. But discovery shouldn’t be archaeology: a good interface always has a ? that calls an overlay with every combination, grouped, legible, and that closes with Escape like a guide who’s done speaking.

04.a

Premi ? ovunque (fuori dai campi di testo) per aprire la legenda delle scorciatoie.

Scorciatoie

gh
Vai alla home
gi
Vai all'indice dei capitoli
t
Cambia tema chiaro/scuro
l
Cambia lingua IT / EN
?
Apri / chiudi questa legenda
Esc
Chiudi qualsiasi dialog aperto

Shortcuts overlay · `?` opens, Esc closes

The overlay opens with <dialog>.showModal() — a native HTML element that gives you focus trap, Escape handling, top-layer rendering, and backdrop dismiss. No libraries, no manual focus traps. The global listener listens for ? only when focus is not in an input — because someone typing “why” doesn’t want to see the shortcuts.

Entry lasts 220 ms, exit 160 ms — golden rule of motion: things leave faster than they arrive. It matches how physical objects behave (they enter view slowly, get whisked away) and what users already expect. Reduced motion drops everything to 1 ms; the overlay still works.

The confirmation that works to tell you no

A destructive-action confirmation has a strange job: it must allow the action, but its real work is to prevent it from happening by accident. A good confirmation dialog puts the safe default under the user’s hand — focus on Cancel, Enter confirms Cancel, clicking the backdrop doesn’t close. The machine works to tell you no.

04.b
  1. Pensieri di domenica

    aggiornato il 12 maggio

  2. Lista letture

    8 voci

  3. Frammenti per il colofone

    3 voci

Eliminare questa nota?

L'azione non si annulla. La nota verrà rimossa subito.

Destructive confirm · safe default on Cancel

Three notes in a list. Click the trash can to open the dialog: honest title (“Delete this note?”), description that recalls the irreversibility (“This can’t be undone.”), two buttons — Cancel (visual primary, autofocus, accent-ink) and Delete (text only, desaturated red, secondary). Enter cancels, Esc cancels, clicking the backdrop doesn’t close. Confirming the deletion takes a deliberate act: Tab + Enter or an explicit click.

aria-labelledby points to the title, aria-describedby to the description — a screen reader knows immediately what the dialog is about and what’s about to happen. When the user cancels, an aria-live=“polite” whispers “Cancelled” — not to celebrate, but to close the sentence that started. It’s a small invisible detail, but when it’s missing you can tell.

The sheet that comes up from below

The same content on a phone and on a desktop demands two different shapes: at the top centre, a dialog behaves like a poster; at the bottom full-width, it behaves like a drawer pulling open. A well-made bottom sheet on mobile becomes a centred dialog on desktop without changing markup — only its geometry adapts to thumb or cursor.

04.c

Su mobile esce dal basso; su desktop al centro.

Condividi questo capitolo

Bottom sheet · up from the bottom on mobile, centred on desktop

Click “Share” opens a native <dialog>. On &lt; 640px the styling makes it come from the bottom edge with a 320 ms slide-up (gentle spring preset); on ≥ 640px it becomes a standard centred dialog. The drag-handle at the top lets you close it with a swipe-down (PointerEvent, 60 px threshold) — a natural gesture for mobile users, invisible for everyone else.

Three options: copy link, email, other. No avatars, no superfluous icons — it’s a sheet that does one thing, does it well, and gets out. Tap outside closes (state is recoverable, no work lost). Reduced motion replaces the slide with an instant fade: the sheet just appears, without moving through space.

A dialog inside a dialog, without stumbling

The hardest case is also more common than you’d think: a dialog open that requires another. Account settings asking for confirmation to disable themselves. The wrong answer is to close the first and open the second — the user loses context. The right answer is to stack the dialogs, mark the previous as inert, and stitch focus back on close.

04.d

Impostazioni account

Email
anna@altrove.it
Piano
Studio · annuale
Notifiche
Solo settimanali

Disattivare l'account?

Sospendiamo l'accesso e archiviamo i tuoi capitoli. Potrai riattivare l'account entro 30 giorni, poi i dati vengono eliminati.

Nested dialog · one inside the other, without trapping

Click “Account settings” opens the first dialog. Inside, “Disable account” opens a second dialog over the first. The first stays visible underneath but receives the inert attribute: Tab, click, and screen readers skip its elements entirely. The second dialog’s backdrop has a darker tone (perceptual sum): you can clearly see it’s an extra layer.

The focus restoration pattern is a manual stack: I save the active element before each open, restore it on the matching close. Closing the nested, focus returns to the “Disable account” button inside the first dialog (not to the original trigger). Closing the first, it returns to the trigger in the stage. It’s a small choreography that, done well, feels natural. Done badly, it’s the fastest way to lose trust.

When the dialog is too much: confirm in place

The most common mistake with dialogs is… opening too many. A confirmation for every small action is a constant toll: the user stops reading and clicks through. The best confirmation, when the action is reversible or small, is in place — the message turns into its own “are you sure?” without moving context, without trapping focus, without disturbing the background.

04.e
  1. «Ho letto il capitolo nuovo — la voce del vuoto è davvero un'idea felice.»

    Anna · 14:02

  2. «Domani ci vediamo per i bottoni del capitolo successivo?»

    Marco · 13:48

  3. «Ho fatto una piccola lista di skeleton da sperimentare 👇»

    Lorenzo · 11:25

Inline confirm · when the modal is too much

Three messages in a fake conversation. Hover on a message reveals a trash can. Click doesn’t open a dialog: the message turns into an inline panel — “Delete?” + two small CTAs, Cancel (default, focus, accent) and Yes. A 5-second auto-timeout restores the original state. Esc cancels. It’s the dialog without the dialog.

The voice in aria-live=“polite” announces “Confirm deletion” to screen readers — a micro-interruption that doesn’t break the thread. It’s the dialog anti-pattern: it shows that you don’t always need to open one. When the action is small, when the context is already visible, when the error is reversible — a dialog is wasted attention. The inline confirm does the same work, in silence.

The popover that stays where it’s needed

The last case is the non-modal dialog: a small contextual window that opens near its trigger, doesn’t block the background, and closes itself when the user clicks away. It’s the right pattern for filters, quick choices, micro-forms. The rule: if the information is contextual, don’t move it to the centre of the screen.

04.f

Cliccando fuori si applicano i filtri; Esc annulla.

Anchored popover · non-modal, doesn't interrupt

A “Filters” button opens an anchored popover below. Three checkboxes and an “Apply” CTA. No backdrop, no blocking: the background stays clickable, and clicking outside closes the popover (committing the filters). Esc cancels (the filters revert to their previous state). Tab inside cycles between the options — but it’s a soft focus trap: after the last element, Tab leaves and closes the popover. The user isn’t trapped.

Technically we use the native Popover API (popover=“auto” + popovertarget) — a recent web addition that gives light-dismiss and Escape handling for free. Anchoring under the trigger uses CSS Anchor Positioning (anchor-name / position-anchor) where supported, with a JS-computed position: absolute fallback for older browsers. aria-expanded on the trigger always reflects the state.

What we take away

A dialog is an interruption. When it’s necessary, it must make itself small. The six demos above show six different ways to “interrupt well”:

  1. The tool overlay that explains its own language (and leaves with the same shortcut that called it).
  2. The destructive confirm that works to tell you no — safe default, armoured backdrop.
  3. The adaptive sheet that changes geometry between mobile and desktop without changing markup.
  4. The nested dialog that stacks context without breaking it.
  5. The anti-dialog, inline — showing when not to open a modal.
  6. The anchored popover, non-modal, staying where the user is already looking.

Six kinds of interruption, one philosophy: respect the thread of thought of whoever’s using the interface. The next chapter changes scale — the scroll that tells a story — because after learning to interrupt well, it’s time to learn how not to interrupt at all, letting the user’s gesture carry the story forward.