Skip to content

Keyboard

<Sheet.Content className="sheet">
<input placeholder="Card number" />
{/* stays clear of the on-screen keyboard automatically */}
</Sheet.Content>

iOS resizes visualViewport, not the layout viewport, when the keyboard opens. scrollsheet listens to it and writes the inset to --scrollsheet-keyboard, no prop required. The panel overdraws about 120vh past its bottom edge, so a stale frame never shows a gap while the keyboard pushes the content up.

A sheet resting at a short peek detent has no room to show a field the keyboard covers, so keyboardExpands on Sheet.Root promotes to the tallest detent the moment the keyboard actually appears, and restores the peek detent when it closes, unless a drag or a controlled change moved the sheet in between. The gate is a measured nonzero inset, never focus alone: a desktop click into an input expands nothing.

Top, left, and right sheets are pinned to their edge, so the panel itself never moves for the keyboard. Their content still can, though: the same inset becomes bottom clearance on [data-scrollsheet-body] plus scroll-padding on the panel, so a focused field near the bottom of a side drawer scrolls clear instead of sitting under the keyboard.

<Sheet.Content fill> stretches the body to the panel (flex column) so a fixed header plus an independently scrolling list needs no CSS chain of your own:

<Sheet.Content fill className="sheet">
<div className="search">{/* fixed */}</div>
<div className="results">{/* flex: 1; min-height: 0; overflow-y: auto */}</div>
</Sheet.Content>

The list then sizes to whatever the sheet has left, including when the keyboard caps the panel at the visual viewport. fill is off by default because it changes how the content detent measures: with it on, the detent and content-morph both switch from the body’s own box to its first child.

Doing it by hand instead, same result:

.sheet { display: flex; flex-direction: column; }
.sheet [data-scrollsheet-body] { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.sheet .results { flex: 1; min-height: 0; overflow-y: auto; }

[data-scrollsheet-body] is the wrapper the library already renders around your content; fill just sets these two rules for you off one attribute.