Function
The saved position to offer, in seconds
It is a dialog by role, not by mechanism. There is no backdrop, nothing
outside it is marked aria-hidden, and it is not portalled — it is an
ordinary child of the player box, so it covers the video and nothing else.
That is the whole point of the surface: the decision is about the video, so
it stays within the video's bounds and leaves the rest of the lesson page
live behind it.
Two accessibility affordances survive the loss of the Dialog primitive
and are implemented here by hand: focus moves to the primary action when
the overlay appears, so a keyboard or screen-reader user is taken to the
choice rather than left hunting for it, and Escape closes it. Focus is
not trapped — trapping is a modal behavior, and this is not modal.
Every exit means "play". Resume and Restart are the two answers; Escape
and the close control both route to onRestart, because the learner
already asked for playback and the only open question was where from.
It is purely a decision surface: it does not read or write storage, does
not touch the player, and does not decide whether it should be shown.
useResumeOnFirstPlay owns that and acts on the answer.
Below sm the prose gives way, not the choice. Being bounded by the
player means being bounded by a 16:9 box about 200px tall on a phone, and a
card laid out for a desktop column overflowed it at both ends — where the
lesson wrapper's overflow: hidden cut off the heading and the first line of
the description. The heading and the description are what go, because the
timestamped action already carries what they say; the timestamp and the two
actions are the choice itself and survive at every size.
They go to sr-only, never to hidden and never out of the tree, because
they are the dialog's aria-labelledby and aria-describedby targets: a
screen-reader user is offered exactly what a sighted one is. max-h-full
with overflow-y-auto is the backstop for a translation long enough to
overflow even the compact form — it scrolls inside the card rather than
being clipped by an ancestor.
The "Resume from MM:SS · Restart from beginning" choice, drawn inside the player over the video frame.