Layout — Window size classes
4 canonical window-size classes (Compact / Medium / Expanded / Large) that Koder UIs adapt to. Defines breakpoints in dp, detection per surface (Flutter, Web, native Android), and the expected layout responses at each class. Material parity (`/foundations/layout/applying-layout/window-size-classes`).
Spec — Window size classes
Facet *isual*do Koder Design. Material parity: https://m3.material.io/foundations/layout/applying-layout/window-size-classes.
The 4 classes
| Class | Width (dp) | Typical surfaces | Columns (grid) |
|---|---|---|---|
| *ompact* | 0–599 | Phone portrait, watch | 4 |
| *edium* | 600–839 | Phone landscape, small tablet, foldable inner | 8 |
| *xpanded* | 840–1199 | Large tablet, small laptop, foldable outer | 12 |
| *arge* | ≥ 1200 | Desktop, ultrawide, TV | 12+ |
Breakpoints are in *ensity-independent pixels (dp)*so Flutter, Web, and Android share the mental model. Web converts to CSS px 1:1 below high-DPI; above HDPI it follows window.devicePixelRatio.
R1 — Detect class per surface
Flutter
WindowSizeClass classOf(BuildContext context) {
final w = MediaQuery.of(context).size.width;
if (w < 600) return WindowSizeClass.compact;
if (w < 840) return WindowSizeClass.medium;
if (w < 1200) return WindowSizeClass.expanded;
return WindowSizeClass.large;
}koder_kit exposes KoderWindowClass.of(context) (planned).
Web
:root {
--wc-compact: 599px;
--wc-medium: 839px;
--wc-expanded: 1199px;
}
@media (max-width: 599px) { }
@media (min-width: 600px) and (max-width: 839px) { }
@media (min-width: 840px) and (max-width: 1199px) { }
@media (min-width: 1200px) { }koder_web_kit exposes data-window-class="compact|medium|..." attribute on <html> (planned).
Native Android
WindowSizeClass from Jetpack androidx.window.core.layout (Android 12+). Mapping is 1:1 with the above thresholds.
CLI / TUI
TTY columns (terminfo). Adapt at:
- Compact: < 80 cols → single column, abbreviate labels
- Medium: 80–119 cols → 2 columns, full labels
- Expanded: 120–159 cols → 3 columns, descriptions
- Large: ≥ 160 cols → full table layouts, side panes
R2 — Layout response per class
What changes at each class (canonical):
| Element | Compact | Medium | Expanded | Large |
|---|---|---|---|---|
| Navigation | Bottom bar | Bottom bar | Nav rail | Nav drawer (persistent) |
| Container padding | 16dp | 16dp | 24dp | 32dp |
| Card/grid columns | 1 | 2 | 3 | 4+ |
| List density | Comfortable | Comfortable | Comfortable | Compact (optional) |
| Dialog | Full-screen sheet | Full-screen | Centered modal | Centered modal |
| Date picker | Calendar full-screen | Calendar full-screen | Inline + popup | Inline + popup |
| Search | Full-width bar in app bar | Bar in app bar | Inline search field | Inline + always-visible |
R3 — Multiwindow / splitscreen
Foldables and desktops support split-screen. Koder UIs:
- Listen for class changes (
MediaQueryrebuild,ResizeObserver) - Pick canonical layout fresh per class change
- Preserve state across transitions (scroll, form input, selection)
Antipattern: hardcoding "isPhone" — use class detection so the same code works on tablet split-screen Compact.
R4 — Orientation independence
Window class is determined by *idth* not orientation. A landscape phone is Medium, not Compact. Avoid Orientation queries except for truly orientation-sensitive UI (camera viewfinder, AR).
R5 — Type scale
Type scale adjusts per class:
| Class | Display | Headline | Body |
|---|---|---|---|
| Compact | -1 step | -1 step | base |
| Medium | base | base | base |
| Expanded | +1 step | base | base |
| Large | +1 step | +1 step | base |
(Concrete sizes in future typography.kmd.)
R6 — Forbidden patterns
- ❌ Single hardcoded breakpoint (
if width < 768) without class taxonomy - ❌ Orientation-only checks (
portraitvslandscape) - ❌ Different code paths for "mobile" vs "desktop" with no class
- ❌ Pixel hardcoding (
width: 320px); use dp + class
Cross-link
adaptive-design.kmd— strategies (resizereflowtransform)canonical-layouts.kmd— 4 layouts per class behaviorsafe-area.kmd— insets at every class