Size classes, hinge posture, reserved regions, and window segments
These hooks and readers are named imports from one. They let a layout follow
the window’s size class, a foldable’s hinge, and regions of the screen that
content should avoid.
useSizeClass() returns the window’s horizontal and vertical size
classes (compact, regular, or unspecified) and re-renders when they change.
On iOS they are the window scene’s UIUserInterfaceSizeClass values, so a
layout can branch on the trait iOS uses rather than on the screen’s width.
On a foldable such as iPhone Duo the outer display is compact and the inner
display regular in both axes. On Android they come from the activity window’s
WindowMetrics: width below 600dp is compact, otherwise regular; height below
480dp is compact, otherwise regular. They update on configuration and
window-metrics changes.
The native hook seeds its first render from a synchronous window reading. After its last subscriber unmounts, the next mount reads that seed again. A delayed initial read cannot replace a newer native event or update a later subscription.
useHinge() reports the hinge’s status (closed, partiallyOpen,
fullyOpen, or unknown) and angle in radians. getSizeClass() returns
Promise<SizeClass> and getHinge() returns Promise<HingeState | null>;
both read the current value once. onHingeChange(callback) subscribes outside
React, passes the current HingeState | null to each change callback, and
returns an unsubscribe function. Call it when the subscription’s owner unmounts.
The first render already has the current hinge, so a hinge-keyed layout does
not render its no-hinge branch first. On iOS the state comes from UIHinge. On Android the status comes from the
Jetpack FoldingFeature (FLAT is fullyOpen, HALF_OPENED is partiallyOpen)
and the angle from the hinge sensor; a hinge sensor with no folding feature in
the current window reads closed only on an official 0° sensor reading and
unknown otherwise, preserving the sensor angle; a device with no hinge reads
null.
On web, both size-class readers return regular for each axis. Both hinge
readers return null, and onHingeChange returns an unsubscribe function
without emitting events.
| Props | Type | Description |
|---|---|---|
horizontalUserInterfaceSizeClassHorizontal window size class: compact, regular, or unspecified. | UserInterfaceSizeClass | Horizontal window size class: compact, regular, or unspecified. |
verticalUserInterfaceSizeClassVertical window size class: compact, regular, or unspecified. | UserInterfaceSizeClass | Vertical window size class: compact, regular, or unspecified. |
| Props | Type | Description |
|---|---|---|
statusHingeStatusHinge posture: unknown, closed, partiallyOpen, or fullyOpen. | HingeStatus | Hinge posture: unknown, closed, partiallyOpen, or fullyOpen. |
anglenumberHinge angle in radians. | number | Hinge angle in radians. |
Safe area insets say how far to pull content in from each edge; they cannot
describe a fold across the middle of the screen or a camera on the inner
display. One.UI.ReservedRegions.Provider is a view that reports the regions
reserved inside its own bounds, and useReservedRegions() reads
them from the nearest provider. A division is a fold that content should not
straddle; an occlusion is a camera cutout or system control that hides
content. Each region has an id, a frame in the provider’s points (margins
included), the margins kept around it for interactive content, and
isActive. useReservedRegions() returns active regions unless you pass
{ includeInactive: true }, and { kind: 'division' } narrows to one kind.
useReservedRegionsReady() turns true after the provider’s first native
reading, including an empty reading. Until then, useWindowSegments() returns
an empty array.
useWindowSegments() returns the unobstructed parts of the provider when
an active full-span division separates it. useSpanning() reports whether
that division splits the provider. The provider must fill the window to read
window segments.
On iOS 27.1 the regions come from UIKit’s reservedRegions and update as the
hinge moves. On Android a fold from Jetpack WindowManager is a division,
active while it separates the display, and each display cutout
is an occlusion; Android reports no margins. The web reports none.
import { One, useReservedRegions } from 'one'
function Canvas() { const [fold] = useReservedRegions({ kind: 'division' }) if (!fold) return <Board /> return ( <> <Board style={{ height: fold.frame.y }} /> <Palette style={{ top: fold.frame.y + fold.frame.height }} /> </> )}
<One.UI.ReservedRegions.Provider style={{ flex: 1 }}> <Canvas /></One.UI.ReservedRegions.Provider>One.iOS.ArrangementView uses SwiftUI’s ArrangementView on iOS 27.1 or later.
It accepts primary and secondary panes, with automatic, split, and overlay
styles. With splitArrangementLayoutRatio={0.5} on a closed iPhone Duo, the
first two styles stack equal panes;
overlay gives both panes the full arrangement frame. Open and folded Duo
postures have not yet been verified in One’s simulator fixture.
import { One, useSizeClass } from 'one'
function Workspace() { const sizeClass = useSizeClass() if (sizeClass.horizontal !== 'regular') return <Chat /> return ( <One.iOS.ArrangementView style={{ flex: 1 }} arrangementViewStyle="split" splitArrangementLayoutRatio={0.5} primary={<Chat />} secondary={<Preview />} /> )}Edit this page on GitHub.