rememberFormFactor

Android is the one target where the surface genuinely varies at runtime — the same codebase runs on a phone, a Wear OS watch and an Android TV. uiMode's type bits are the platform's own answer, and reading them through LocalConfiguration means a fold, a display switch or a move to an external monitor recomposes with the right tokens.

ponytail: UI_MODE_TYPE_DESK and _CAR fall through to Handheld. Desk mode is a docked phone at arm's length (Handheld metrics are right), and nothing in this family ships to Auto — which needs driver-distraction rules, not a token scale. Add them when a consumer actually targets them.

The platform's own answer for which surface this is. Android is the only target that genuinely varies at runtime (the same APK runs on a phone, a watch and a television), so it reads the system UI mode; the other targets are known at compile time.

Composable rather than a plain function because the Android actual reads the configuration, which changes on a fold/unfold or a display switch and must recompose.

Override it at the call site — AdaptiveTheme(formFactor = FormFactor.Tv) — for previews, screenshot tests, and desktop builds that render a leanback UI.

iOS and iPadOS are both arm's-length touch surfaces, so the width axis alone separates iPhone from iPad — no runtime probe needed.

tvOS and watchOS are deliberately absent: Compose Multiplatform renders neither, so this module has no such target. Apple's 10-foot and wrist UIs are SwiftUI, consuming the non-UI toolkit modules (offline-outbox already targets watchos*, as does Mileway's sharedWatch). The FormFactor.Tv and FormFactor.Watch sets still matter here — they drive Android TV and Wear OS, and a desktop build can opt into either explicitly via AdaptiveTheme(formFactor = ...).

The browser is treated as Desktop. That is the posture, not the screen size — a phone browser is still a ~360dp viewport and resolves to WindowType.Compact, which is what actually drives the layout. Desktop and Handheld share a token ladder precisely so this call doesn't have to be right about the device, only about the viewing distance.