Layout
Layout controls how Senco screens are structured and how content reflows across screen sizes.
Senco is mobile-first in the literal sense: the phone layout is the design, and the desktop layout is the phone layout given more room. That ordering is what keeps the two from drifting into different products, and it is why the transfer flow looks the same shape on a 1440px monitor as it does on a 360px handset.
Grid system
Column-based responsive grid that adapts across breakpoints. Content reflows — it never shrinks.
| Breakpoint | Width | Columns | Gutter | Margin |
|---|---|---|---|---|
| Mobile | < 600px | 4 | 16px | 16px |
| Tablet | 600–1023px | 8 | 16px | 24px |
| Desktop | 1024–1439px | 12 | 24px | 32px |
| Wide | ≥ 1440px | 12 | 24px | auto (max 1400px) |
Breakpoint tokens: --ds-bp-mobile (600px), --ds-bp-tablet (1024px), --ds-bp-desktop (1440px).
Reflow, never shrink
Content moving to a smaller screen changes how many columns it spans. It does not scale down. A 16px label stays 16px at every breakpoint; what changes is that a three-column card row becomes a single stacked column.
Shrinking is how a design system ends up with 13px text on the exact devices whose users need it largest.
Column spans by breakpoint
| Content | Mobile (4) | Tablet (8) | Desktop (12) |
|---|---|---|---|
| Full-width section | 4 | 8 | 12 |
| Card in a row of three | 4 (stacked) | 4 (two per row) | 4 (three per row) |
| Transfer flow column | 4 | 6 centered | 4 centered |
| Dashboard main / side | 4 / 4 stacked | 8 / 8 stacked | 8 / 4 |
| Settings nav / panel | 4 (tabs on top) | 8 (tabs on top) | 3 / 9 |
The transfer flow narrows as the screen widens — from 4 of 4 columns to 4 of 12. This is deliberate and is the clearest expression of the mobile-first rule: more space does not mean a wider form, it means more margin around the same form.
Breakpoints are for layout, not for components
A component never queries the viewport. It fills the container it is given, and the layout decides how wide that container is. A card that reads @media (max-width: 600px) breaks the moment it is placed in a sidebar on desktop.
Where a component genuinely needs to respond to its own width — a transaction row that drops its secondary line when narrow — it uses a container query, not a media query.
Containers
| Type | Behavior | Usage |
|---|---|---|
| Fluid | Fills available width | Full-width sections |
| Constrained | Max-width with centered content | Default content pages |
| Full-bleed | Edge-to-edge, ignores page padding | Hero banners, backgrounds |
Constrained widths come from the sizing tokens — --ds-container-xs through --ds-container-max. See Sizing.
Choosing a container
| Screen | Container | Why |
|---|---|---|
| Transfer flow | sm (480px) | Single-column sequence; wider invites out-of-order scanning |
| Settings | lg (960px) | Two-column nav + panel on desktop |
| Dashboard | lg (960px) | Main column plus a side rail |
| Documentation / content | md (640px) | Keeps line length inside 45–75 characters |
| Marketing hero | Full-bleed | Background extends past the content column |
Full-bleed with constrained content
A full-bleed section is edge-to-edge in background only. Its text still sits in a constrained container, because a headline running 1400px wide is unreadable regardless of how good the background looks.
Content centered in a constrained container with margins. Comfortable reading.
Content stretches edge-to-edge. Line lengths become unreadable on wide screens.
Layout primitives
| Primitive | Purpose |
|---|---|
| Container | Constrain content width and apply horizontal padding |
| Grid | Arrange content in responsive columns |
| Stack | Vertical sequence with a single consistent gap |
| Row | Horizontal sequence with a single consistent gap, wrapping by default |
Stack and Row exist so that spacing lives in one place. A screen built from Stacks has exactly one gap value per group and no margins on children — which is the rule from Spacing enforced by structure rather than by review.
Header and Footer are template-level regions — see Templates > Basic page.
Safe areas
Mobile layouts respect env(safe-area-inset-*). The bottom tab bar and the persistent action bar both sit above the home indicator; without the inset the primary transfer action lands underneath the gesture bar, where roughly a third of taps are swallowed by the OS.
Responsive principles
- Mobile-first approach — base styles are mobile, enhance with breakpoints
- Navigation adapts: bottom tabs on mobile, sidebar on desktop
- Touch targets meet the 48px minimum on mobile
- Column spanning changes per breakpoint (e.g., 3-col cards on desktop stack to single column on mobile)
- Content reflows at 320px width and 200% zoom without horizontal scrolling (WCAG 1.4.10)
- Layout is defined in
remand fractional units, so OS text scaling reflows rather than clips
Do and don't
| Do | Don't |
|---|---|
| Let the transfer flow stay 480px on a wide screen | Stretch a single-column form to fill 1200px |
| Reflow a three-card row into a stack | Scale three cards down to fit side by side on mobile |
| Use a container query for component-level response | Put a viewport media query inside a component |
| Apply safe-area insets to fixed bottom chrome | Pin an action bar to bottom: 0 and lose it under the home indicator |
Keep gaps on the parent via gap | Add margins to grid children to fake a gutter |
Design and accessibility
- Use semantic landmarks (
header,main,footer,nav) - Grid does not break reading order for screen readers — visual order and DOM order must match
orderandrow-reverseare prohibited where they would separate the two- Container does not trap keyboard focus
- One
<main>per page, and the skip link targets it - Landmarks are labelled when repeated (two
<nav>elements need distinguishingaria-labels)
Known gaps
| Item | Status |
|---|---|
| Stack / Row primitives | Documented here; not implemented as shipped components |
| Container queries | Documented as the preferred mechanism; not yet used in any component |
| Grid column tokens | Column counts and gutters are documented but not defined as --ds- variables |
| Safe-area handling | Applied on prototype screens; not systematized as a layout primitive |