Phase 9b · Tier A

RN-universal Dialog pilot

Scaffolded

RN Modal-backed dialog with backdrop, fade-in, and a compound API that mirrors shadcn. Both controlled (open + onOpenChange) and uncontrolled (defaultOpen) modes are supported, so AI agents can drive the primitive declaratively.

Side-by-side: confirm action

shadcn (Radix)
RN

Controlled (agent-driven)

Drive the dialog from external state — exactly how an A2UI agent would: it sends a JSON descriptor with open: true and a list of actions, and the ThemeProvider renders the dialog. No imperative ref needed.

Semantics

AspectshadcnRNNotes
RootRadix Root<RnDialog> context providerTracks open state and trigger ref
TriggerRadix Trigger (asChild)<RnDialogTrigger> PressableOpens on press; restores focus on close
ContentPortal + role="dialog"RN Modal + role="dialog"Modal renders fixed overlay on web; OS modal on native
Backdropfixed bg-black/50 + animate-outAnimated.View opacity 0→0.5Tap to dismiss; same on both
TitleRadix Title<RnDialogTitle> Textaria-labelledby wires automatically
DescriptionRadix Description<RnDialogDescription> Textaria-describedby wires automatically
Close (esc)Radix focus trap + escModal onRequestCloseWeb: esc; Android: hardware back
Focus trapRadix maintainsNot implemented in v1Modal native focus + ARIA is enough for WCAG AA

Use with agents (A2UI)

A2UI-style descriptor → component:

{
  "type": "dialog",
  "open": true,
  "title": "Submit application?",
  "description": "Once submitted, you can no longer edit responses.",
  "actions": [
    { "id": "cancel", "kind": "secondary", "label": "Cancel" },
    { "id": "submit", "kind": "primary",   "label": "Submit" }
  ]
}

The renderer maps open directly to the prop, and actions to RnDialogClose + RnButton children.