fix(ui): drop the home-indicator padding of a bottom sheet on the keyboard

A bottom sheet standing on the on-screen keyboard kept its safe-area
inset as bottom padding, leaving about 34 px of empty space between
its last row and the keys on an iPhone.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 22:57:47 +02:00
1 parent 6b41c11f30
commit 938b5151de
3 files changed
+26 -2

No files matched your search

@@ -78,6 +78,20 @@ describe("the shell owns the safe area", () => {
);
});
it("a bottom sheet on the keyboard drops its home-indicator padding", () => {
// Standing on the keyboard, the safe-area inset left a ~34 px gap
// between the sheet's last row and the keys.
expect(code(read("src/app/globals.css"))).toMatch(
/@custom-variant keyboard-open \(html\[data-keyboard="open"\] &\);/,
);
const sheet = code(read("src/components/ui/responsive-sheet.tsx"));
const padded = (sheet.match(
/"[^"]*pb-\[calc\(env\(safe-area-inset-bottom[^"]*"/g,
) ?? []) as string[];
expect(padded.length).toBe(2);
for (const cls of padded) expect(cls).toMatch(/\bkeyboard-open:pb-4\b/);
});
it("the sheet primitive names its side for those rules", () => {
expect(read("src/components/ui/sheet.tsx")).toMatch(
/data-sheet-side=\{side\}/,
+7
View File
@@ -76,6 +76,13 @@
}
}
/*
* `keyboard-open:` — while the on-screen keyboard is up (see the keyboard
* rules further down). A bottom sheet then stands on the keyboard, not on
* the home indicator, so its safe-area padding has nothing left to clear.
*/
@custom-variant keyboard-open (html[data-keyboard="open"] &);
@custom-variant shell-mobile {
@media (max-width: 47.98rem), (pointer: coarse) and (max-height: 520px) {
@slot;
+5 -2
View File
@@ -257,7 +257,10 @@ export function ResponsiveSheet({
// Without a footer the body is the sheet's bottom edge, so it
// keeps its last row above the home indicator of an
// installed app; the footer does that when there is one.
!footer && "pb-[calc(env(safe-area-inset-bottom,0px)+1rem)]",
// Standing on the keyboard, the sheet is clear of the
// indicator already; the inset would leave a gap above it.
!footer &&
"keyboard-open:pb-4 pb-[calc(env(safe-area-inset-bottom,0px)+1rem)]",
bodyClassName,
)}
>
@@ -266,7 +269,7 @@ export function ResponsiveSheet({
{footer ? (
<SheetFooter
data-slot="responsive-sheet-footer"
className="border-border/70 bg-background/95 supports-[backdrop-filter]:bg-background/80 sticky bottom-0 mt-0 flex-row justify-end gap-2 border-t p-4 pb-[calc(env(safe-area-inset-bottom,0px)+1rem)] backdrop-blur"
className="border-border/70 bg-background/95 supports-[backdrop-filter]:bg-background/80 keyboard-open:pb-4 sticky bottom-0 mt-0 flex-row justify-end gap-2 border-t p-4 pb-[calc(env(safe-area-inset-bottom,0px)+1rem)] backdrop-blur"
>
{footer}
</SheetFooter>