apps/readest-app/docs/safe-area-insets.md
The app runs on devices with notches, status bars, and rounded corners (iOS, Android). UI elements near screen edges must account for safe area insets to avoid being obscured.
gridInsets: Insets — Per-view insets derived from view settings (header/footer visibility, margins). Calculated by getViewInsets() in src/utils/insets.ts. Passed as a prop from BooksGrid → child components.statusBarHeight: number — OS status bar height (default 24px). Stored in themeStore.systemUIVisible: boolean — Whether the system UI (status bar, navigation bar) is currently shown. Stored in themeStore.appService?.hasSafeAreaInset — Whether the platform requires safe area handling (mobile devices).For UI elements anchored to the top of the screen (headers, close buttons, overlays):
// When system UI is visible, use the larger of gridInsets.top and statusBarHeight
// When system UI is hidden, use gridInsets.top alone
style={{
marginTop: systemUIVisible
? `${Math.max(gridInsets.top, statusBarHeight)}px`
: `${gridInsets.top}px`,
}}
For containers that need safe area padding at the top:
style={{
paddingTop: appService?.hasSafeAreaInset ? `${gridInsets.top}px` : '0px',
}}
For UI elements anchored to the bottom of the screen (footer bars, controls, progress indicators), use gridInsets.bottom * 0.33 as padding — a fraction of the full inset since bottom bars don't need as much clearance as the home indicator area:
style={{
paddingBottom: appService?.hasSafeAreaInset ? `${gridInsets.bottom * 0.33}px` : 0,
}}
gridInsetsWhen creating overlay components (image viewers, table viewers, zoom controls, etc.), always pass gridInsets as a prop so they can position their controls correctly:
<ImageViewer gridInsets={gridInsets} ... />
<TableViewer gridInsets={gridInsets} ... />
<ZoomControls gridInsets={gridInsets} ... />