Layout — Window size classes
app-layout specs/app-layout/window-size-classes.kmd
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`).
When this spec applies
Primary triggers
- Implement responsive layout in any Koder surface
All triggers
- Detect current window-size class in a Koder UI
- Decide a breakpoint switch (layout transform, density change)
- Build a new component that adapts across classes
Specification body
Spec — Window size classes
Facet Visual 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) |
|---|---|---|---|
| Compact | 0–599 | Phone portrait, watch | 4 |
| Medium | 600–839 | Phone landscape, small tablet, foldable inner | 8 |
| Expanded | 840–1199 | Large tablet, small laptop, foldable outer | 12 |
| Large | ≥ 1200 | Desktop, ultrawide, TV | 12+ |
Breakpoints are in density-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) { /* Compact */ }
@media (min-width: 600px) and (max-width: 839px) { /* Medium */ }
@media (min-width: 840px) and (max-width: 1199px) { /* Expanded */ }
@media (min-width: 1200px) { /* Large */ }
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 — Multi-window / split-screen
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)
Anti-pattern: hard-coding "isPhone" — use class detection so the same code works on tablet split-screen Compact.
R4 — Orientation independence
Window class is determined by width, 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 (resize/reflow/transform)canonical-layouts.kmd— 4 layouts per class behaviorsafe-area.kmd— insets at every class
References
specs/foundations/adaptive-design.kmdspecs/app-layout/canonical-layouts.kmdspecs/app-layout/safe-area.kmd