Bottom Sheet
A panel that slides up from the bottom of the screen. Learn modal and standard sheets, detents and drag handles, every way to dismiss one, and the scroll, keyboard and lost-choice bugs testers find in them.
You’re about to order dinner and tap Delivery time. A panel slides up from the bottom with time slots. You pick 19:30, scroll to see later slots… and the whole panel slides away. Did it save 19:30 or not? You can’t tell. That panel is a bottom sheet, and that moment of doubt is a bug.
Bottom sheets are everywhere on phones: sort and filter options, share menus, maps, music players, checkout choices. They feel natural because they sit where your thumb is. But they mix two gestures in one place (scroll the content, drag the sheet), they have many exits, and they often hold unsaved choices. Let’s learn them properly.
What is a bottom sheet?
A bottom sheet is a surface that slides up from the bottom edge and shows extra content or options related to the current screen. It can usually be dragged to different heights and swiped down to close.
There are two kinds, and testers must know which one they’re looking at:
- Modal bottom sheet: sits on top of the page behind a dim scrim, blocks everything else and must be closed before you continue. It’s a modalModalA window that appears on top of the page and blocks the content behind it until it is closed.Open in glossary → that happens to come from the bottom. Example: Sort by, Share, Choose delivery time.
- Standard (persistent) bottom sheet: lives next to the main content, without a scrim. You can still use the page behind it. Example: the list of places under a map, the mini player in a music app.
Anatomy
iOS sheet at the medium detent
- 1
Scrim
Dims and blocks the page behind a modal sheet. Tapping it means “cancel”, never “confirm”.
- 2
Container
The sheet itself, attached to the bottom edge with rounded top corners.
- 3
Grabber / drag handle
Shows the sheet can be dragged. Must also work with a tap or screen reader.
- 4
Header
Title plus a visible way out: Cancel, Done or an X.
- 5
Content
Scrolls inside the sheet. Dragging it must scroll first, and move the sheet only at the top.
- 6
Detents
The heights the sheet snaps to, such as medium (about half) and large (almost full).
- 7
Footer action
The main button, pinned so it stays visible, even above the keyboard.
How it differs from similar components
| Component | Blocks the page? | Can resize? | Typical use |
|---|---|---|---|
| Modal bottom sheet | Yes (scrim) | Often (detents) | Options, filters, a short task |
| Standard bottom sheet | No | Yes (peek / half / full) | Results under a map, a mini player |
| iOS action sheet | Yes | No | A few actions about one item, plus Cancel |
| Dialog | Yes | No | A short decision in the middle of the screen |
| Full-screen page | Replaces it | No | Long forms and multi-step tasks |
Dev note
Apple calls them sheets. They can stop at heights called detents (medium is about half the screen, large is almost full), and the little bar on top is the grabber. The short list of actions with a separate Cancel is an action sheet (now built as a confirmation dialog). Material Design says bottom sheet (modal or standard) and drag handle. On the web you’ll hear bottom sheet, drawer or bottom drawer. In bug reports, use your team’s name and say which kind it is.
Designs & variants
Each demo below is fully working. Open it, drag it, and close it every way you can.
Every demo works. Open each sheet and try every way out: the buttons, tapping outside, Esc, and dragging the handle down. Drag the handles up too.
Flights
Sheet with detents
iOSOpens at medium height. Drag the grabber (or the list when it's at the top) to large, or down to close. Cancel is always there for people who can't swipe.
Restaurants
Modal bottom sheet
AndroidMaterial 3: drag handle, rounded 28 dp top corners and a scrim. The page behind is blocked. Back, tapping the scrim and dragging down all close it without changing anything.
7 cafés nearby
- Café Mila · 4.7 · 200 m
- Bean There · 4.5 · 350 m
- Kaffeehaus · 4.6 · 400 m
- Roastery 9 · 4.8 · 650 m
- Little Cup · 4.3 · 700 m
- Oat & Co · 4.4 · 900 m
- Brew Lab · 4.6 · 1.1 km
Height: peek
Standard (persistent) bottom sheet
AndroidLives next to the main content instead of on top of it: no scrim, and the map still works. Drag between peek, half and full. It can't be dismissed, only collapsed.
Photo
Action sheet
iOSA short list of actions about one thing, with a separate Cancel. Not draggable. Destructive actions are red. Tapping outside cancels.
T-shirts · 42
Filter sheet on mobile web
WebA dialog that slides up on small screens (often a side panel or centered dialog on desktop). It needs a visible close button, Esc, a focus trap and a clear apply button.
Modal or standard?
Ask one question: can I still use the page behind it?
- If no, it’s modal: there must be a scrim, taps must not reach the page, focus must stay inside, and there must be a clear way out.
- If yes, it’s standard: there’s no scrim, the page behind must keep working (zoom the map, tap a pin), and it usually can’t be dismissed, only collapsed to a “peek” height.
A sheet that dims the page but still lets taps through, or that blocks the page without dimming it, is mixing the two. That’s a bug, or at least a design finding.
When to use it
A bottom sheet is a good choice when:
- The task is short and related to the current screen: pick a sort order, choose a time slot, share a link.
- The user benefits from seeing the screen behind (a map, a product) while choosing.
- There are a few options or one small form that fit comfortably at the first height.
- The content should be within thumb reach on a phone.
Good and bad uses
Short, focused choice
One decision, all options visible, and Cancel in reach.
A long form in a sheet
Fields are cut off, the keyboard will cover half of them, and one accidental swipe throws everything away. Use a full screen.
A visible way out
Swipe down works, but so do Cancel and Done, for anyone who can’t swipe.
Gestures only
No buttons at all. Is the choice saved? How do switch control or keyboard users close it?
How a good bottom sheet behaves
Heights and dragging
A sheet opens at a defined height (often medium, or just tall enough for its content) and can snap between a few heights, called detents. Dragging the grabber up expands it; dragging down collapses it to the next height, or closes it from the lowest one. When you let go between heights, it should snap to the nearest one. A fast flick should go one step in the flick’s direction.
It should also reopen at its default height every time, and never show up cut off, empty, or taller than the screen.
Scroll vs drag
This is where most sheet bugs live. A sheet with a long list has two things that move with the same finger movement. The rule real apps follow:
- Dragging up on the content first expands the sheet to its largest height, then scrolls the list.
- Dragging down on the content scrolls the list back up. Only when the list is at the very top does a downward drag start moving the sheet down.
- The grabber and header always move the sheet, never the list.
If dragging the list moves the sheet even when it’s scrolled down, users can’t scroll, and they close the sheet when they only wanted to see earlier items.
Every way to close a modal sheet
| Exit | Where | What must happen |
|---|---|---|
| Cancel / X / Close | Everywhere | Closes, changes nothing |
| Swipe down | iOS, Android, often mobile web | Same as Cancel |
| Tap the scrim | Everywhere | Same as Cancel. The tap must not reach the page behind |
| System Back | Android (button or gesture) | Closes the sheet first, not the screen behind |
| Esc key | Web; hardware keyboards on phones and tablets | Same as Cancel |
| Screen reader escape | VoiceOver two-finger Z, TalkBack Back | Same as Cancel |
| Done / Apply / Confirm | Everywhere | The only exit that saves the choice |
The keyboard
When a field inside the sheet gets focus, the keyboard takes up about 40% of the screen. The sheet must move up so the focused field and the main button stay visible. If the sheet is tall, its content should shrink and scroll rather than push the header off the top of the screen. Test in portrait and landscape, where very little space is left.
Small screens, landscape and large text
Test on the smallest supported phone, in landscape, and at the largest text size (Dynamic TypeDynamic TypeThe phone setting that lets users make text bigger or smaller across apps.Open in glossary → on iOS, Font size on Android). The header and the main button must stay reachable, the content must scroll, and the sheet must respect the safe areaSafe areaThe part of the screen not covered by the notch, Dynamic Island, rounded corners or home indicator.Open in glossary → so the home indicator doesn’t cover the last button.
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Scroll fights drag | Dragging the list moves the sheet; you can’t scroll, or you close it by accident | High |
| 2 | Keyboard covers the field | The text field or Save button is hidden behind the keyboard | High |
| 3 | Dismiss loses or saves silently | A swipe throws away a half-filled form, or applies a choice you never confirmed | High |
| 4 | Taps go through the scrim | Tapping the dimmed area also presses a button on the page behind | Medium–High |
| 5 | Gesture-only closing | No Cancel or X; Esc, Back and screen reader escape don’t work | High (for affected users) |
| 6 | Wrong height | Reopens half-collapsed, cut off, or taller than the screen on small phones | Medium |
| 7 | Page behind scrolls | Scrolling inside the sheet also scrolls the page under it | Medium |
| 8 | Focus stays behind | The screen reader keeps reading the page, not the sheet | High (for affected users) |
| 9 | Stuck or jumpy animation | The sheet stops between heights or bounces after a fast flick | Low |
| 10 | Double sheets | A double tap opens two sheets, or the sheet reopens after closing | Medium |
Try it: the sheet lab
Open the sheet, change the option, then try to get out every way. Drag on the list, not only on the grabber. Tap the note field to bring up the keyboard. Then switch one good behavior off at a time and repeat.
Checkout
Tap “Delivery” to open the sheet.
Open the sheet, change the option, then try to close it every way. Drag the list, not just the grabber. Tap the note field to bring up the keyboard. Then switch a behavior off and repeat.
What happened
- Nothing yet.
Find the bug
This iOS food delivery checkout has a Delivery time sheet with six bugs. Explore it like a tester:
- Open the sheet and drag the list of time slots (with the mouse or your finger).
- Pick a slot, then close the sheet in different ways and check the checkout row each time.
- Tap the dimmed area over the buttons behind the sheet.
- Tap the note field. Try to close it with Esc or the device panel buttons.
🐞 Bug hunt: Delivery time sheet
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
Checkout
Burger Lab
- Classic burger€9.501
- Sweet potato fries€4.201
- Rua Augusta 12, Lisbon
- Visa •• 4242
Device
Event log
- Sheet events appear here…
- 1
Hint: Try to scroll the time slots by dragging them.
- 2
Hint: Tap “Note for the courier”.
- 3
Hint: Pick a different slot, then swipe the sheet down. Check the checkout row. Then do the same but tap outside.
- 4
Hint: With the sheet open, tap the dimmed + next to an item.
- 5
Hint: Look for a Cancel or close button. Try Esc and the VoiceOver escape gesture in the device panel.
- 6
Hint: Swipe the sheet down to close it, then open it again.
How to test it
Checklist
Bottom sheet test checklist
0 of 13 checked
Example test cases
TC-BS-001Confirming a delivery slotpositivehigh
Steps
- Open Checkout
- Tap "Delivery time"
- Tap "12:30 – 12:45"
- Tap "Confirm"
Expected result
The sheet closes and the checkout row shows "12:30 – 12:45". Focus returns to the Delivery time row.
TC-BS-002Every dismissal path cancelsnegativehigh
Preconditions
- Delivery time is "ASAP"
Steps
- Open the sheet and tap "12:30 – 12:45"
- Close it with: swipe down, tap outside, Cancel, Esc (hardware keyboard), System Back (Android)
- Check the checkout row after each attempt
Expected result
After every path the checkout still shows "ASAP".
TC-BS-003Scrolling the list does not move the sheetpositivehigh
Steps
- Open the sheet and drag it to the large height
- Drag up on the list to scroll to "15:00"
- Drag down on the list a little
Expected result
The list scrolls in both directions and the sheet stays at the large height. Only when the list is back at the top does a further downward drag collapse the sheet.
TC-BS-004Keyboard does not cover the note fieldedgehigh
Preconditions
- Smallest supported phone (e.g. iPhone SE)
- Portrait, then landscape
Steps
- Open the sheet
- Tap "Note for the courier" and type "Ring twice"
Expected result
The field and the Confirm button stay visible above the keyboard in both orientations, and the typed text is readable.
TC-BS-005Tapping the scrim does not trigger the page behindnegativemedium
Steps
- Open the sheet
- Tap the dimmed area exactly over the "+" button of an item
- Tap the dimmed area over "Place order" (if visible)
Expected result
The sheet closes. The quantity, the total and the order are unchanged.
TC-BS-006Unsaved form input is protectednegativemedium
Steps
- Open a sheet with a form (e.g. "Add a note")
- Type some text
- Swipe the sheet down
Expected result
The sheet stays open and asks "Discard changes?". "Keep editing" keeps the text; "Discard" closes it.
TC-BS-007Reopening starts at the default heightedgemedium
Steps
- Open the sheet
- Drag it down slowly until it closes
- Open it again
- Repeat after dragging to the large height and tapping outside
Expected result
Each time, the sheet reopens fully visible at the medium height.
TC-BS-008Screen reader can use and close the sheetpositivemedium
Preconditions
- VoiceOver (or TalkBack) is on
Steps
- Activate "Delivery time"
- Swipe right through the sheet
- Perform the escape gesture (VoiceOver: two-finger Z; TalkBack: Back)
Expected result
Focus starts in the sheet, the title is read, swiping never reaches the page behind, and the escape gesture closes it without changes.
Tip: click a test case to expand it.
Tools
- iOS Simulator: I/O › Keyboard › Toggle Software Keyboard (Cmd+K) shows the on-screen keyboard. With Connect Hardware Keyboard on, the software keyboard may never appear, so you’d miss bug #2. Turn on VoiceOver in Settings › Accessibility or use the Accessibility Inspector.
- Android Emulator: test Back with the button and with gesture navigation, rotate with the toolbar, and raise Font size and Display size in Settings. Turn on TalkBack from Settings › Accessibility.
- Web: use the device toolbar in DevTools for small viewports, but test the keyboard on a real phone: desktop emulation doesn’t show the virtual keyboard. Check
role="dialog",aria-modaland focus in the Accessibility pane.
Accessibility
- Announce it. A modal sheet is a dialog: the screen readerScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary → should say its title and that it’s a dialog when it opens. See Modal for the full set of dialog rules.
Dev note
On the web, userole="dialog"witharia-modal="true"andaria-labelledby(or a native<dialog>). - Focus moves in, stays in, comes back. Focus goes to the sheet when it opens, a focus trapFocus trapKeeping keyboard focus inside an open modal so users can't tab into the hidden page behind it.Open in glossary → keeps Tab inside a modal sheet, and focus returns to the trigger when it closes.
- Never gesture-only. Every gestureGestureA touch action such as tap, long press, swipe, pinch or drag.Open in glossary → needs a non-gesture alternative (see Gestures): a visible Cancel or Close button, Esc, Android Back and the VoiceOver escape gesture. WCAGWCAGThe Web Content Accessibility Guidelines, the main international standard for accessibility.Open in glossary → 2.2 asks that anything done by dragging can also be done with a simple tap or click (success criterion 2.5.7, Dragging Movements), so resizing should also work by tapping or activating the grabber.
- Grabber with a name. If the grabber is focusable, it needs a label and action, e.g. “Sheet grabber, double-tap to expand”. iOS and Material’s drag handles provide this; custom ones often don’t.
- Touch targetsTouch targetThe area of the screen that responds to a tap, which should be large enough for a finger.Open in glossary →: Cancel, X and every option at least 44×44 pt (iOS) / 48×48 dp (Android). A thin grabber alone is hard to hit, so the whole header should be draggable.
- Large text and contrast: content scrolls rather than clips at the biggest text size, text has 4.5:1 contrast and the sheet’s edge stands out from the page (3:1, or a clear scrim).
Mobile vs web
- Sheets: rounded card with an optional grabber, swipe down to close.
Dev note
UISheetPresentationController/ SwiftUI.sheetwith.presentationDetents([.medium, .large]). - Apps can block swipe-to-dismiss when there are unsaved changes and ask first. Test that it’s on where it should be.
Dev note
.interactiveDismissDisabled()/isModalInPresentation. - A sheet can leave the page behind usable at smaller heights (like Maps). Then it behaves like a standard sheet: no dimming, page still works.
- Action sheets (confirmation dialogs) are not draggable and always have Cancel. On iPad, sheets and action sheets look different (centered or as a popover), so test both.
- Material 3 modal bottom sheets have a scrim, rounded 28 dp top corners and a drag handle. They can open partially expanded, then expand fully.
- Standard bottom sheets stay on screen next to the content, often with a peek height.
Dev note
Compose:ModalBottomSheetandBottomSheetScaffold. - System Back (button or gesture) must close a modal sheet first. With predictive back, the sheet should animate along with the Back swipe and only close when you release.
- Test rotation and process death: the sheet may vanish, duplicate or lose its selection. Test TalkBack, Font size and Display size.
- On desktop the sheet is often a side drawer or centered dialog. Test both breakpoints.
Dev note
There’s no native “bottom sheet” element. It’s a<dialog>or a customdivstyled as a sheet. - Check Esc, the close button, click outside, the focus trap and scroll lock (the page behind must not scroll, especially on iOS Safari).
- On a real phone, check the virtual keyboard: mobile browsers don’t always resize the page, so a sheet stuck to the bottom can end up behind it.
- Decide what browser Back does while the sheet is open. Either way, it must not lose the user’s input.
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.You picked a new delivery slot in a sheet, then swiped the sheet down without tapping Confirm. What should happen?
2.The list in a sheet is scrolled halfway down. You drag down on the list. What should move?
3.Which one is a standard (persistent) bottom sheet?
4.A sheet can only be closed by swiping down. Why is that a problem?
5.Which test is MOST likely to show that the keyboard covers a field in a sheet?
Key takeaways
- A modal sheet blocks the page with a scrim; a standard sheet sits beside the content and keeps the page usable. Know which one you’re testing.
- Sheets snap between detents. They should always reopen at their default height, fully visible.
- Scroll before drag: the content scrolls first, and the sheet moves only from the top of the list.
- Every exit except Confirm means cancel. Swipes must never silently save a choice or silently throw away a form.
- Always test the keyboard, small screens and landscape, taps on the scrim, and closing without gestures (Cancel, Esc, Back, screen reader).
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡