@crossplatformai/skills
Version:
Reusable Agent Skills for CrossPlatform.ai projects.
224 lines (168 loc) • 10.5 kB
Markdown
# React Native Web To DOM
This is a Post.Build.Ship reference contract, not a standalone skill.
Use this reference when `post`, `build`, or `ship` is already active and an
approved task adds, migrates, or reviews UI that might be owned by React Native,
React Native Web, browser DOM, or an Electron renderer.
This contract supplements Post.Build.Ship. It preserves Authorizing User
authority, observable acceptance, platform boundaries, resolver and accessibility proof,
proportionate QA, ownership, and Ship safety. It adds no mandatory review or presentation fields.
## Decide Ownership Before Syntax
Classify the UI by every runtime that renders it:
- UI rendered only by a web client, web SSR shell, or Electron renderer is
DOM-first.
- UI also rendered by native mobile remains React Native by default. Migrate it
only through **Shared contract with platform-selected implementations** or
**Platform-owned renderer/composition** below.
- A cosmetic fix to existing React Native Web does not trigger a migration
decision. Apply the smallest RNW-compatible repair and keep the owning
boundary unchanged.
- A raw DOM child must not be inserted directly into an RNW tree. Use an
approved platform primitive or migrate the owning boundary separately.
Define the boundary by platform ownership, not by whether a file happens to be
edited. Shared data does not require shared rendering, and a shared source file
does not make browser-only interaction native-owned.
Existing React Native Web aliases and the server-emitted RNW stylesheet are
intentionally retained transitional infrastructure. Remove either only after
the affected runtime no longer renders RNW and its replacement has equivalent
resolution, SSR, reset, and hydration proof.
## Prove Every Resolution Context
Inventory every active resolver before selecting a seam. At minimum, record the
intentional result for:
| Resolution context | Question to prove |
| ------------------ | ---------------------------------------------------------------------------------- |
| Web client | Which browser or default condition selects the implementation? |
| Web SSR or Node | Which export, default, or `main` entry executes on the server? |
| Electron renderer | Which browser or default condition selects the renderer implementation? |
| Native Metro | Does the explicit `react-native` condition select the native implementation? |
| Expo Web | Which Metro or Expo condition actually executes, and which reset stylesheet loads? |
| Tests | Which resolver condition did the test process execute? |
In the conditional-export shape proven by this repository's worked example,
DOM owns `browser`, `default`, and `main`, while React Native is selected by the
explicit `react-native` condition. This is not a universal package-layout rule.
Use it only when the target package uses that conditional-export shape.
Do not claim platform coverage by calling the same imported implementation
under different platform labels. Tests must prove that each resolver context
actually selected and executed its intended entry. When Expo Web does not share
the web client's alias, condition, or reset path, record the gap and plan it as
a separate runtime follow-up instead of silently classifying Expo Web as either
native mobile or the existing browser client.
Temporary resolver aliases are migration scaffolding. Declare the condition
that removes each alias, such as support for package conditional exports in the
owning resolver, and treat conditional exports as the completion target when
that resolver supports them.
## Use One Of Three Patterns
Only three implementation patterns are approved by this reference. Choose the
smallest seam whose invariants fit the owning runtimes.
### Shared Contract With Platform-Selected Implementations
Use one conceptual API when browser or Electron and native need the same
capability but require different primitives.
Required invariants:
1. The public contract contains only semantics every implementation supports,
or it exposes an explicit capability result that lets an implementation
decline.
2. DOM and native entries are selected by tested resolver conditions rather
than runtime platform checks inside the shared implementation.
3. Every accepted prop is honored, rejected, or represented by an explicit
capability decline. No implementation accepts and silently ignores a prop.
A method returning `false` for an unsupported operation can be an explicit
capability decline. A platform implementation that accepts `sticky`,
`animated`, or another behavior prop and then ignores it is the anti-example:
split the contract, reject the value, or expose support explicitly.
Keep DOM structural resets with the DOM implementation and native behavior with
the native implementation. If a temporary app alias selects the entries,
record its removal criterion.
### Platform-Owned Renderer Or Composition
Use this when shared code owns data and structure but interaction semantics
belong to the rendering platform.
Required invariants:
1. The renderer callback or composition slot is required.
2. Shared item and section types exclude platform interaction semantics such
as `href`, `target`, `onPress`, router objects, and native gesture props.
3. The shared layer retains no fallback link, pressable, button, or other
interaction primitive.
The caller may extend a structural shared item type with platform-owned fields,
then render an anchor, router link, pressable, or native navigation primitive.
Do not make the renderer optional and retain an RNW interaction fallback; that
leaves platform ownership ambiguous.
### Isolated DOM-Only Subpath
Use this for browser behavior that should live near shared domain code but must
never enter native bundles.
This repository's worked isolation mechanism has four parts:
1. export browser modules through explicit, discoverably named subpaths;
2. keep them out of the mobile-facing root barrel;
3. allow DOM or browser APIs only through targeted lint-boundary
configuration; and
4. keep functions hook-free when consumers call them as plain functions.
An equivalent repository-native boundary mechanism is acceptable when it
enforces the same isolation. Browser components may use hooks when they are
rendered as components; a plain adapter or factory must not hide hook calls
behind ordinary function invocation.
Prefer names such as `browser-*`, `dom-*`, or an explicit `/browser-*` or
`/dom-*` export. A generic root export that happens to touch `window`,
`document`, anchors, or DOM events is not isolated.
## Bound Shipping Exceptions
When browser-owned UI would remain or expand RNW solely to ship faster, ask the
user to choose between:
1. the recommended DOM slice; or
2. a bounded RNW exception for the current commit.
Record all of the following before accepting the exception:
- named files and owning surface;
- why the DOM slice cannot ship in the current commit;
- behavioral proof the RNW implementation must preserve;
- any unavoidable public or mobile-facing API exposure; and
- the user's explicit decision.
The exception expires with that commit unit. It cannot silently widen to
another file, surface, or commit. A public or mobile-facing API is allowed only
when the handoff discloses it specifically and the user approves it.
## Own Resets And The Cascade
Keep DOM resets structural: box sizing, display model, intrinsic minimums,
default margin, padding, border, list, and text-decoration normalization.
Behavior-specific styling such as sticky offsets, animation, feature geometry,
or route state stays with the feature.
Load structural resets below application utilities so app utilities own the
final cascade. Preserve the target repository's layer system; do not introduce
a second reset hierarchy merely to imitate RNW. A platform primitive's
contract tests should prove its reset class or selector, and feature tests
should prove the utilities that intentionally override it.
When existing RNW styling is still owned by mobile or an approved exception,
keep using RNW and Uniwind computed-style evidence. Do not turn a spacing or
class-precedence repair into an unapproved DOM migration.
## Verification Obligations
Every migration or new seam verifies:
- resolver selection for web client, web SSR or Node, Electron renderer,
native Metro, Expo Web, and tests when those contexts exist;
- native and mobile-facing exports, including the absence of browser-only
modules from mobile-facing barrels;
- SSR output, hydration, and retained RNW stylesheet behavior for any
remaining RNW tree;
- rendered DOM semantics rather than component names alone; and
- reset ownership, CSS layer order, utility precedence, and unexpected
computed-style changes.
For navigation or scroll behavior, verification is unconditional whenever the
behavior is touched. Prove:
- the navigation target receives focus;
- repeated navigation produces a repeat-safe live-region announcement;
- reduced-motion preference selects non-animated behavior; and
- the page records zero page errors, console errors, and failed requests.
When visual parity is claimed, compare both implementations at two
representative viewports. Record:
- rendered tag and structural reset class;
- a fixed, declared set of computed-style properties; and
- relevant geometry such as bounding box, alignment, and spacing.
Do not require visual-parity evidence when parity is not claimed. Do require
semantic and resolver proof for every affected context.
## Learn A Pattern Explicitly
When none of the three approved patterns fits, emit a `Pattern Candidate`
instead of establishing local precedent. It must contain:
- repository evidence and the owning runtimes;
- why each approved pattern fails to fit;
- the proposed invariant;
- appropriate and inappropriate uses;
- resolver and CSS/reset implications;
- accessibility requirements;
- counterexamples and failure modes; and
- exact QA and user-visible proof.
Ask for separate Authorizing User approval and a materially revised Post.Build.Ship packet before
changing this skill or reference. If the candidate is rejected, use the
closest approved pattern or stop for direction. Never encode a fourth pattern
silently in application code, a local agent file, or an unreviewed exception.