Skip to content

Adaptive design

foundations specs/foundations/adaptive-design.kmd

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

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Adaptive design

Facet Visual do Koder Design. Material parity: https://m3.material.io/foundations/adaptive-design.

Principle

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

ClassWidthTypical surface
Compact0–599 dpPhone portrait
Medium600–839 dpPhone landscape, small tablet, foldable inner
Expanded840–1199 dpLarge tablet, small desktop, foldable outer
Large≥ 1200 dpDesktop, TV

Breakpoints are density-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:

StrategyBehaviorExamples
ResizeSame element, different dimensionsCard width, font size step
ReflowSame element, different position/orderSidebar moves to bottom on Compact
TransformDifferent element entirelyNavigation 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. Feed — vertical scrollable list, full-width on Compact, multi-column on Expanded
  2. List-detail — split pane on Expanded; stack on Compact
  3. Supporting pane — primary content + auxiliary pane (collapsible)
  4. Custom — 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; long-press is the touch equivalent
  • Drag-and-drop 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) { /* hover-capable styles */ }
@media (pointer: coarse) { /* touch-first sizing */ }

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)

References