Mobile Rendering Model and Responsive Behavior
This guide explains how mobile behavior works in SimplyPages and what is automatic versus app-owned.
Mental Model
SimplyPages mobile behavior is primarily CSS-driven, with small framework-generated HTML/JS hooks:
ShellBuilderemits shell structure and mobile nav toggle markup when a sidebar exists.- Shell layout defaults to full-width content when no sidebar is configured.
framework.cssapplies responsive breakpoints and stacking/wrapping behavior.framework.jshandles shared shell collapse/drawer behavior and the native Page disclosure lifecycle.- Layout primitives (
Page,Row,Column) provide stable class/markup contracts that CSS targets.
What the Framework Handles Automatically
When you use ShellBuilder with a sidebar:
- Full/template documents include viewport meta:
width=device-width, initial-scale=1.0. Body-only fragments omit document/head/chrome/assets/scripts; their host loads framework assets once. - Shared framework JS initializes existing and later direct/HTMX shell fragments idempotently.
Collapsible desktop buttons are named native non-submit controls; desktop preference persistence
through
sidebarCollapsedis best effort, with document-local operation when storage is denied. - Mobile sidebar toggle button is rendered.
- Sidebar uses off-canvas mobile behavior via
.main-sidebar.mobile-openat widths through 768px. - Closed shell navigation is inert and has
aria-hidden="true"; the named toggle tracks expanded state. - Opening focuses a navigation control; Escape returns focus to the toggle, and outside clicks dismiss.
- Resize restores desktop usability without discarding the desktop collapsed preference. That preference does not narrow or prevent opening the mobile drawer.
- The floating toggle cannot overlay or activate through an active modal. CSS owns modal base layering
(
--sp-modal-layer, default 1100); JS adds only relative stack offsets.
When you use Row/Column:
- Columns collapse to full-width stacks on mobile breakpoints.
- Column/module text wrapping guards are applied to prevent overflow.
When you use Page.withStickySidebar(...):
- One native
details/firstsummarydisclosure starts closed; mobile uses stacked layout. Fresh mobile disclosures remain closed until opened. - Framework JS keeps desktop (769px and above) natively open and restores each element's remembered observable native/application open preference through 768px. Desktop closes update that preference before reopening; ordinary nested details are untouched.
- New/replacement elements initialize from their own native state. Reinserted/reparented elements retain preference; detached elements receive no responsive writes or focus transfer.
- A closing disclosure with contained focus transfers to its usable summary. Desktop transfers a
focused summary to usable content or the generated
tabindex="-1"aside fallback. If the caller supplied a detailstabindex, JS uses the details as fallback instead; it does not overwrite caller attributes or application focus. Covered/background Pages do not take focus or scroll away from the top modal. - Without JS, the disclosure starts closed at every width and the native summary is keyboard-operable. An open desktop summary remains displayed while itself focused for native keyboard recovery. Visibility follows native open state, not forced boxes.
- This Page lifecycle is separate from ShellBuilder's off-canvas drawer. Check both presentations in the application; custom policies remain application-owned.
When you use TabsModule:
- Tab navigation stacks vertically through 768px; desktop navigation stays horizontal.
- Framework JS matches
aria-orientationand keyboard arrows to the computed flex axis: Up/Down vertically, Left/Right horizontally, with wrapping/automatic activation and Home/End. - Initialization, resize and dynamic insertion synchronize orientation without resetting focus, selection or nested tabs. Perpendicular arrows and modified keys retain native behavior.
- SSR markup uses a horizontal fallback until the framework assets apply the viewport layout.
Breakpoints
Framework defaults currently use:
@media (max-width: 768px):- shell becomes single-column
- sidebar off-canvas mode
- row/column stacking and sticky-sidebar mobile mode
@media (max-width: 480px):- tighter typography/spacing
- InfoBox icon/value scaling
Where Mobile Is Configured
- Shell/layout markup:
ShellBuilderandPage - Responsive behavior:
static/css/framework.css - Interactive shell and native Page lifecycle:
static/js/framework.js
There is no separate "mobile mode" API flag to enable.
Consumer Responsibilities
Framework defaults do not replace product-level mobile design decisions. App teams still own:
- page/module-specific responsive polish
- domain-specific navigation labels and density
- QA across real target devices
Use custom CSS layering via withCustomCss(...)/addCustomCss(...) for app-specific behavior.
Practical Usage Pattern
- Build shell with
ShellBuilder. - Build content with
Page+Row+Column. - Keep module sizing in layout (not module internals).
- Add app CSS overrides for product-specific mobile tuning.
Mobile Release Checklist
- Verify no horizontal overflow at phone/tablet widths.
- Verify sidebar toggle opens/closes correctly on mobile.
- Verify multi-column rows stack to readable blocks.
- Verify sticky sidebar content remains accessible and compact on mobile.
- Verify long text/code/table content remains usable (wrap or horizontal scroll).