Adaptive design
Koder UI is adaptive by default — single codebase serves phones, tablets, foldables, desktops, TVs, and web. Defines the principles, the 4 window-size classes, and the rules each layout uses to reflow. Material parity (`/foundations/adaptive-design`); complement to `app-layout/safe-area.kmd` (which covers insets) and `window-size-classes.kmd` (which covers the canonical breakpoints).
Spec — Adaptive design
Facet *isual*do Koder Design. Material parity: https://m3.material.io/foundations/adaptive-design.
Principle
*ne codebase. Every surface. No "responsive afterthought".*A Koder UI must work on the smallest expected surface from day 1; any larger surface is an enhancement, never a special-case rewrite.
This applies equally to:
- Flutter apps (mobile + desktop + web + TV)
- Web apps (browser desktop + mobile breakpoint)
- Landing pages (covered by
web-apps/responsiveness.kmd) - CLIs (TTY width adaptation — yes, even CLIs adapt)
R1 — 4 window-size classes (canonical)
Aligned with Material 3 window-size classes. Full spec in app-layout/window-size-classes.kmd; summary here:
| Class | Width | Typical surface |
|---|---|---|
| *ompact* | 0–599 dp | Phone portrait |
| *edium* | 600–839 dp | Phone landscape, small tablet, foldable inner |
| *xpanded* | 840–1199 dp | Large tablet, small desktop, foldable outer |
| *arge* | ≥ 1200 dp | Desktop, TV |
Breakpoints are *ensity-independent pixels (dp)*so Flutter, Web, and Android share the mental model.
R2 — 3 adaptive strategies per element
Every UI element pick ONE of three strategies at each breakpoint:
| Strategy | Behavior | Examples |
|---|---|---|
| *esize* | Same element, different dimensions | Card width, font size step |
| *eflow* | Same element, different position/order | Sidebar moves to bottom on Compact |
| *ransform* | Different element entirely | Navigation bar → drawer; full picker → dropdown |
Anti-pattern: mixing all three within one component without documentation — leads to unpredictable behavior.
R3 — Canonical layouts
Material defines 4 canonical layouts (full spec in canonical-layouts.kmd):
- *eed*— vertical scrollable list, full
width on Compact, multicolumn on Expanded - *ist-detail*— split pane on Expanded; stack on Compact
- *upporting pane*— primary content + auxiliary pane (collapsible)
- *ustom*— when the above don't fit
Pick one per screen and stick with it across breakpoints.
R4 — Touch + pointer parity
The same surface must work with touch (Compact/Medium) and pointer (Expanded/Large). Implications:
- All controls satisfy 48×48 dp touch target (
safe-area.kmd) - Hover states are enhancements, never required for primary flow
- Right
click is enhancement; longpress is the touch equivalent - Drag
anddrop must have a non-drag alternative (button, menu)
R5 — Input modality detection
// Flutter
final canHover = MediaQuery.of(context).platformBrightness != null
&& Theme.of(context).platform.supportsMouse;
// Web (CSS)
@media (hover: hover) { }
@media (pointer: coarse) { }Don't gate primary functionality on hover. Use it for refinement.
R6 — Foldable support
Foldable devices switch class mid-session. Koder UIs:
- Listen to size changes via
MediaQuery/ResizeObserver - Re-pick canonical layout on class change
- Preserve scroll position + form state through the transition
R7 — TV (10-foot UI)
Class Large at high DPI (TV) requires:
- Focus indication 2× normal stroke (TV viewing distance)
- Touch target 64×64 dp minimum (D-pad navigation imprecision)
- Color contrast AAA (not AA) — viewing angle variance
See koder-app/behaviors.kmd for full TV adaptation rules.
Cross-link
window-size-classes.kmd— full breakpoint speccanonical-layouts.kmd— 4 layout templatessafe-area.kmd— touch targets + insetsweb-apps/responsiveness.kmd— landing page subsetnavigation/back-behavior.kmd— adapts per class (drawer vs bar)