Adaptive design

mandatory

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):

  1. *eed*— vertical scrollable list, fullwidth on Compact, multicolumn on Expanded
  2. *ist-detail*— split pane on Expanded; stack on Compact
  3. *upporting pane*— primary content + auxiliary pane (collapsible)
  4. *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
  • Rightclick is enhancement; longpress is the touch equivalent
  • Draganddrop 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.

  • window-size-classes.kmd — full breakpoint spec
  • canonical-layouts.kmd — 4 layout templates
  • safe-area.kmd — touch targets + insets
  • web-apps/responsiveness.kmd — landing page subset
  • navigation/back-behavior.kmd — adapts per class (drawer vs bar)

Source: ../home/koder/dev/koder/meta/docs/stack/specs/foundations/adaptive-design.kmd