Modifier Architecture
1. Scope
This document owns the boundary and runtime rules shared by Modifier, scoped parent data,
component NodeSpec, and Theme/Defaults. The generated
Capability Reference owns the exhaustive public-symbol
inventory; this page does not maintain a second list.
2. Ownership model
Use the first matching layer:
Modifierowns stable outer decoration and behavior that can apply to most nodes: layout, appearance, drawing, visibility, interaction, focus, semantics, testing, gestures, shared content, nested scroll, shadows, and layout animation.- A typed parent scope owns data meaningful only to that parent, such as
RowScope/ColumnScope.weight, scoped alignment, or ConstraintLayout child constraints. - Component parameters and
NodeSpecown component semantics such as text styling, image content scale, button variants, and text-field editing state. Theme, design-system recipes, andDefaultssupply defaults; renderers consume resolved values and do not invent business defaults.
Collection reuse and motion remain container policy. Focused-editor visibility belongs to the nearest real scroll owner. A pager owns discrete page selection only, so a page that may be hidden by the IME supplies its own page-local scrolling.
3. Runtime contracts
3.1 Ordering, layout, and appearance
Modifier is an immutable ordered chain rooted at Modifier. Renderer resolution preserves that
order. Later declarations replace earlier values for complete physical/relative spacing families,
background and shape families, visibility, click handling, and other documented single-value
properties. Accumulating properties such as zIndex keep their explicit accumulation contract.
Physical padding, margin, offset, and inset selectors never change meaning in RTL. Their
Relative counterparts resolve start/end from the VNode's captured direction on every bind. The
renderer has one native-padding writer: container content padding, resolved Modifier padding, and
selected insets are composed before the View is updated.
Maximum dimensions and aspect ratio are portable constraints, not raw Android setters. Android Renderer folds them into one synthetic layout boundary around the complete node. Exact incoming constraints remain authoritative; invalid finite/positive values or contradictory declared bounds fail before rendering. Constraint parent data is meaningful only on ConstraintLayout children.
A drawable background takes precedence over a packed color and follows resource qualifiers from the View context. Shape and legacy corner-radius declarations replace one another in chain order. Clipping, platform elevation, exact shadows, and sibling order remain separate concerns.
3.2 Drawing, interaction, semantics, and shared content
Drawing callbacks execute in Modifier order. Behind callbacks run before wrapped content; content-aware callbacks decide whether and when to forward content; cache builders use renderer-owned caches. Visibility controls drawing and layout participation independently of draw callback registration.
Interaction indication describes visual feedback only; it does not make a node clickable or
enabled. High-level components resolve design-system feedback before installing it. Accessibility
state travels through renderer-neutral semantics, with logical collection indexes unchanged by RTL.
testTag is diagnostic identity, not a globally unique application key.
Shared-content markers publish endpoint identity and mode. Pairing, snapshots, and fallback belong to a shared-aware host; outside such a host the marker has no visual effect.
3.3 Focus, keys, gestures, and nested scroll
UI Contract owns normalized focus, key, gesture, and nested-scroll values. UI Foundation exposes session services; Gesture contributes recognizer descriptions; Android Renderer attaches mounted targets without upper layers retaining Views.
Preview keys travel root-to-target and unconsumed keys bubble target-to-root. Explicit focus destinations win before native search. Gesture modifiers describe pointer, click, drag, anchored drag, transform, and arbitration policy; the renderer owns platform timing, slop, pointer streams, velocity, cancellation, and callback delivery.
Nested-scroll pre phases travel outer-to-inner and post phases inner-to-outer. Every finite result
is bounded by the remaining offered value. Framework scrollers and native children implementing
Android nested scrolling join the chain directly; other AndroidView children require an attached
dispatcher. The legacy native fling bridge can report only Boolean consumption, while the
ViewCompose chain preserves exact partial velocity.
3.4 Exact-shadow routing
UI Contract owns renderer-neutral shadow layers and order. Android Renderer owns before/after
decoration planes without depending on a raster backend. The optional Shadow Android artifact
resolves shapes and density, rasterizes layers, and replays them. Without it, exact-shadow requests
are no-ops and do not affect layout, input, elevation, or zIndex.
See the advanced-shadow guide and Shadow Android module manual for application and backend details.
4. Generated capability ownership
The source scanner discovers application-facing public/protected DSL, Modifier, component, host, integration, and tooling entries from published production source sets. Each entry must resolve to exactly one capability, artifact/version state, generated reference owner, sample decision, module manual, and versioned API root. Internal, test, Demo, generated, and renderer-only helpers are excluded.
The website and governance gate consume the same committed model. Refresh it with
./gradlew updateDocumentationCapabilityReference; stale output, duplicate ownership, or a new
orphan fails verification. Raw signatures and KDoc/Javadoc remain in the
versioned API Reference.
5. Hard boundaries and change gate
Do not place component semantics in general Modifier, parent-specific data in global Modifier,
theme defaults in renderers, or first-party durable contracts in an untyped dynamic map. Do not
add a second handwritten symbol inventory beside the generated Reference.
A Modifier-boundary change must update this architecture owner, cover the affected contract and renderer path, provide compiled Q3 samples and public documentation required by its Q level, and add Demo or device evidence when behavior is visual or interactive. Follow the development workflow.