ForgeWP

Practice: Build an Arrival Dialog

Add an accessible arrival-information dialog to the navigation demonstration Page.

Start with Build a Mobile Menu Island. Add a separate island that opens arrival information in a modal dialog.

A modal dialog temporarily keeps keyboard interaction inside its content. The browser's dialog.showModal() supplies this behavior, including Escape-to-close. Setting an open attribute alone does not create the same modal behavior.

Your task

  1. Create src/components/ArrivalDialog.tsx with a named ArrivalDialog export.
  2. Show an Arrival information button. Its click handler should call showModal() on a dialog reference.
  3. Give the dialog a visible heading and connect it with aria-labelledby. Include arrival time and a clearly labelled Close button.
  4. Keep the dialog closed in the initial server-rendered HTML. Access browser methods only in event handlers.
  5. Return focus to the opening button when the dialog closes. Support both the Close button and the browser's Escape action.
  6. Mount it in NavigationDemo with its own <Hydrate trigger="load"> wrapper. Preserve the existing mobile menu.

Use useRef() for DOM references and useId() for the heading identifier. Place autoFocus on the Close button to make initial focus predictable. Give the dialog a viewport-aware maximum height with internal scrolling so it also works on small screens.

Acceptance checklist

  • Opening the dialog places focus on Close; Tab and Shift+Tab remain within the modal.
  • Close and Escape both dismiss it and return focus to Arrival information.
  • A screen reader announces the dialog's title.
  • At a narrow width and at 200% zoom, the text and Close control remain reachable.
  • There is no animation to suppress when reduced motion is enabled.
  • After export, both islands work on WordPress, and the console shows no hydration errors.

The dialog opener becomes interactive when its JavaScript loads. Keep essential arrival details available in ordinary page content too if visitors need them without JavaScript.

Try it first, then compare with the worked solution.