Application patterns

Updated: Sep 24, 2026
Choose the interaction shape that best matches the task, then supply the application’s data and copy. These recurring screen families for Meta Ray-Ban Display glasses are component compositions rather than product templates. Each pattern names the toolkit owner for each part of the screen, shows a representative route, and calls out common pitfalls.
The patterns build on Screen patterns: one App at the root and a clear primary surface for each route. When content scrolls, give the active route or pager page one owner for that axis; static pages need no scroller. These ownership boundaries keep focus and scrolling predictable.
ShapeRoute root(s)Owns the contentOwns the action
Routed list
Page per route
VerticalList of ListItem, then ScrollView + Panel
ButtonRail on the detail route
Tabbed control surface
one SubNavigationPager
two list-based control pages in this example
integrated ListItem controls
Media browser
Page per route
Carousel of Card, then StaticContainer + Panel
the Card itself
Status surface
one Page
ScrollView + Panel
one persistent Button in a ButtonRail
Timed session
one Page
ScrollView + Panel
one phase-derived Button
Work queue with row commands
one Page
VerticalList of ListItem
anchored VerticalMenu
Multi-area workspace
one SubNavigationPager
content chosen for each area
actions scoped to an area

Routed list

Prefer this pattern when a task has one collection, one record detail, durable state, and no more than one primary detail action. It is a safe starting point when requirements do not imply another interaction shape.
Compose the root once so navigation, transitions, and durable state each have a clear owner. Router adapters come from the /react-router subpath, not the root package.
import { App } from '@wearables-ui-toolkit/mrbd';
import {
  ReactRouterNavigationProvider,
  ReactRouterPageTransition,
} from '@wearables-ui-toolkit/mrbd/react-router';
import { BrowserRouter, Route, Routes } from 'react-router-dom';

function Root() {
  return (
    <BrowserRouter>
      <ReactRouterNavigationProvider>
        <App>
          <RecordProvider>
            <ReactRouterPageTransition>
              {({ location }) => (
                <Routes location={location}>
                  <Route path="/" element={<CollectionPage />} />
                  <Route path="/item/:id" element={<DetailPage />} />
                </Routes>
              )}
            </ReactRouterPageTransition>
          </RecordProvider>
        </App>
      </ReactRouterNavigationProvider>
    </BrowserRouter>
  );
}
Keep the durable state provider outsideReactRouterPageTransition so route changes preserve application state. ReactRouterPageTransition supplies its transition state through a render callback; pass its location to Routes.
Use Page > VerticalList > ListItem for the collection route. This direct composition gives the list one scroll and focus owner, without an additional ScrollView, summary, section heading, Panel, footer, gap, divider, or action rail.
import { ListItem, Page, VerticalList } from '@wearables-ui-toolkit/mrbd';

<Page headerText="Items">
  <VerticalList insetForHeader ariaLabel="Items">
    {records.map(record => (
      <ListItem
        key={record.id}
        title={record.title}
        subtitle={record.subtitle}
        timestamp={formatPointInTime(record.timestamp)}
        onClick={() => navigate(`/item/${record.id}`)}
      />
    ))}
  </VerticalList>
</Page>
The detail route uses the action shell: one ScrollView, one edge-to-edge Panel as its direct child, and a ButtonRail as the shell’s second track.
Recommended composition:
  • Use Page header props for route identity instead of a standalone Header.
  • Use VerticalList with ListItem for the collection.
  • Give static routes a ScrollView with tabIndex={0} and a concise ariaLabel, so directional input can scroll without a child focus target.
  • Use one Panel with width="100%" as the detail information backdrop.
  • Build the text stack with TextView, TextStyle, and TextColor.
  • Put the single detail action in one Button within a ButtonRail, then use Toast to confirm its result.
Common pitfalls:
  • Avoid in-app Back, Home, Close, or Cancel-return controls. System back owns route return, and PageTransition restores the focused row and scroll offset.
  • Keep rows adjacent instead of wrapping them in cards or adding chevrons and gaps.
  • Prefer one detail Panel to a panel per field, a second panel, or a field grid.
  • Keep the record and its row mounted while that row is the restoration target so focus has a valid owner.

Tabbed control surface

This example uses two peer control categories with no routed detail. SubNavigationPager does not require a fixed number or type of pages: every navigation item maps to one React child, and that child can render any content appropriate for the destination. Prefer it to Page as the primary surface for a tabbed route; keep rails and groups as action surfaces rather than using them as tabs.
import { ListItem, SubNavigationPager, VerticalList } from '@wearables-ui-toolkit/mrbd';

<SubNavigationPager
  items={[
    { label: 'Controls', icon: '/icons/sliders.svg' },
    { label: 'Modes', icon: '/icons/modes.svg' },
  ]}
  currentPageIndex={pageIndex}
  onPageChange={setPageIndex}
  homeIndex={0}
  ariaLabel="Device controls"
>
  <VerticalList insetForHeader ariaLabel="Controls">
    {controls.map(control => (
      <ListItem
        key={control.id}
        title={control.title}
        subtitle={control.subtitle}
        showSwitch
        checked={control.enabled}
        onCheckedChange={checked => setControl(control.id, checked)}
      />
    ))}
  </VerticalList>
  <VerticalList insetForHeader ariaLabel="Modes">
    {modes.map(mode => (
      <ListItem
        key={mode.id}
        title={mode.title}
        subtitle={mode.subtitle}
        showRadioButton
        checked={mode.id === activeModeId}
        onCheckedChange={checked => {
          if (checked) {
            setActiveMode(mode.id);
          }
        }}
      />
    ))}
  </VerticalList>
</SubNavigationPager>
Recommended practices:
  • Control the pager and keep its index above it so state updates do not reset the visible tab.
  • Prefer a one-word tab label when it identifies the destination clearly; use a concise two-word label when needed. Give every SubNavigationItem its required icon.
  • Match each navigation item with one child page. A child can contain any React content; this control-surface example uses VerticalList because its pages are lists of settings.
  • When a child scrolls, give that page one owner for the scrolling axis. Set insetForHeader on the VerticalList or ScrollView that should clear the overlaid tab strip; static pages need neither a scroller nor this inset.
  • Let each row own showSwitch or showRadioButton. A standalone Switch or RadioButton beside a separately focusable label creates two targets for one setting.
  • Exactly one radio row must remain selected. A false callback must not clear the current selection because the control requires a valid value.
  • Update durable state directly for routine control changes. Reserve Toast for outcomes that need transient confirmation rather than every toggle.

Media browser

Horizontal browsing of a small set of media destinations, then one detail route.
  • Collection: Page > one edge-to-edge ScrollView > one direct Carousel > interactive Card destinations.
  • Detail: Page > one ScrollView with tabIndex={0} > one non-interactive StaticContainer media region > one edge-to-edge Panel.
Recommended composition:
  • Use Card with bottomScrim or topScrim, CardBelowScrim for the image layer, and CardAboveScrim for compact copy.
  • Seed Carousel with initialIndex from durable state and persist changes through onItemCentered. Avoid restoring position with a timeout or effect.
  • Use StaticContainer with clipContent for detail media. Card is always focusable and represents a destination, so it is not a decorative hero.
  • Put reading information below the media in a Panel with width="100%".
Recommended practices:
  • Give each Card geometry through its public props: a responsive width bounded by both axes and either an aspect-ratio or explicit height. Positioned layer wrappers cannot establish card geometry.
  • Every Card must have an onClick and a complete ariaLabel because it is a focusable destination.
  • Keep Carousel as a direct ScrollView child. A padded wrapper breaks its edge-to-edge frame and pagination placement.
  • Limit in-card copy to a title and one short context line.
Common pitfalls:
  • Put a scrim or material beneath text over media to preserve legibility on the additive-light display.
  • Avoid a second same-axis scroller within the carousel route.
  • Customize Card, StaticContainer, and Panel through public root props rather than implementation details.

Status surface

One monitored subject, one primary state, one reversible action. No routes, no collection, no tabs.
import { useState } from 'react';
import {
  Button,
  ButtonRail,
  Page,
  Panel,
  ScrollView,
  TextColor,
  TextStyle,
  TextView,
  Toast,
} from '@wearables-ui-toolkit/mrbd';

export function StatusPage() {
  const [acknowledged, setAcknowledged] = useState(false);

  const handleToggle = () => {
    setAcknowledged(current => !current);
    Toast.show(acknowledged ? 'Reopened' : 'Acknowledged');
  };

  return (
    <Page headerText="Cold store">
      <div className="action-page-shell">
        <ScrollView insetForHeader tabIndex={0} ariaLabel="Cold store status">
          <Panel width="100%">
            <div className="content-inset">
              <TextView as="p" textStyle={TextStyle.BODY2_EMPHASIZED}>
                {acknowledged ? 'Door event acknowledged' : 'Door event recorded'}
              </TextView>
              <TextView as="p" textStyle={TextStyle.BODY2}>
                Temperature monitoring continues without interruption.
              </TextView>
              <TextView
                as="p"
                textStyle={TextStyle.LABEL}
                textColor={TextColor.SECONDARY}
              >
                STATUS
              </TextView>
              <TextView as="p" textStyle={TextStyle.META1}>
                {acknowledged ? 'Acknowledged' : 'Needs review'}
              </TextView>
            </div>
          </Panel>
        </ScrollView>
        <div className="action-dock">
          <ButtonRail>
            <Button
              title={acknowledged ? 'Reopen' : 'Acknowledge'}
              onClick={handleToggle}
            />
          </ButtonRail>
        </div>
      </div>
    </Page>
  );
}
Recommended practices:
  • Keep the Button mounted across state changes so focus retains its owner. Derive its title, the status value, the summary, and the toast from the same state.
  • Keep the Panel to a small, fixed set of text roles: a BODY2_EMPHASIZED summary, one BODY2 supporting sentence, an all-caps LABEL eyebrow with TextColor.SECONDARY, and the current value in META1. Prefer body, label, and metadata styles for routine content.
  • Start panel content with useful status information instead of repeating the page header.
  • Never offer “stop monitoring” for a safety-relevant subject. Model the workflow state instead and describe continued monitoring in every branch; this protects the safety boundary.

Timed session

A status surface with time: a session that runs, pauses, completes, and can restart. Same shell as above, plus one phase model.
type SessionPhase = 'running' | 'paused' | 'complete';
Recommended practices:
  • Keep one phase state and one non-negative remaining-time state. One effect that depends only on phase should own the running interval and transition to complete at zero.
  • Derive the summary, supporting copy, status value, action title, and toast from phase. A running label or pause action must not remain at zero because it would expose an invalid command.
  • Keep the action Button mounted in every phase so completion preserves focus and replaces obsolete commands in place.
  • Use ProgressIndicator, ProgressRing, or CircularProgressBar for a determinate value, and IndeterminateLoader when completion time is unknown. Prefer these accessible components to authored progress markup.
  • Treat duration and tick interval as domain values, not design values, and keep CSS values tokenized.

Work queue with row commands

A compact collection whose rows open temporary commands for that same record. No detail route, no permanent action panel, no second scroll owner.
import { useRef, useState } from 'react';
import {
  ListItem,
  Page,
  Toast,
  TooltipMode,
  VerticalList,
  VerticalMenu,
  VerticalMenuButton,
  VerticalMenuCorner,
  getVerticalMenuAnchorProps,
} from '@wearables-ui-toolkit/mrbd';

export function QueuePage() {
  const [openRecordId, setOpenRecordId] = useState<string | null>(null);
  const rowRefs = useRef(new Map<string, HTMLDivElement>());

  const closeAndRestoreFocus = (id: string) => {
    setOpenRecordId(null);
    requestAnimationFrame(() => {
      rowRefs.current.get(id)?.focus({ preventScroll: true });
    });
  };

  return (
    <Page headerText="Queue">
      <VerticalList insetForHeader ariaLabel="Queue">
        {records.map(record => (
          <ListItem
            key={record.id}
            ref={node => {
              if (node == null) {
                rowRefs.current.delete(record.id);
              } else {
                rowRefs.current.set(record.id, node);
              }
            }}
            title={record.title}
            subtitle={record.subtitle}
            ariaLabel="Open actions"
            onClick={() => setOpenRecordId(record.id)}
            tooltipMode={
              openRecordId === record.id ? TooltipMode.ALWAYS : TooltipMode.NONE
            }
            tooltipContent={
              <VerticalMenu
                autoFocusFirstItem
                onDismissRequest={() => closeAndRestoreFocus(record.id)}
              >
                <VerticalMenuButton
                  text="Assign"
                  onClick={event => {
                    event.stopPropagation();
                    assign(record.id);
                    closeAndRestoreFocus(record.id);
                    Toast.show('Assigned');
                  }}
                />
                <VerticalMenuButton
                  text="Snooze"
                  onClick={event => {
                    event.stopPropagation();
                    snooze(record.id);
                    closeAndRestoreFocus(record.id);
                    Toast.show('Snoozed');
                  }}
                />
              </VerticalMenu>
            }
            {...getVerticalMenuAnchorProps(VerticalMenuCorner.ABOVE_RIGHT)}
          />
        ))}
      </VerticalList>
    </Page>
  );
}
Recommended practices:
  • Make the row the trigger and present the menu through its tooltip portal. Opening the menu must not change list size, row position, or scroll extent; stable geometry protects focus and scrolling.
  • Set VerticalMenuButton labels with text, not title, and keep them to one line.
  • For each command, stop propagation, update state, clear the open row, restore focus to that row, and then report the result with Toast.
  • Run the same clear-and-restore path from onDismissRequest. VerticalMenuDismissReason reports whether Escape or navigation dismissed the menu.
  • Keep at least one meaningful enabled command in every reachable record state, and keep the record mounted so focus has a stable host.
  • Consider ContextMenu with ButtonContextMenuItemView or EmojiContextMenuItemView for horizontal, icon-led commands. Its dismiss callback is onDismiss(reason), with reasons from DismissReason.
  • Consider SwipeToReveal when every row has the same one to three icon-only commands. It wraps one focusable child and reveals an ordered actions tray, with each action shaped as { icon, contentDescription, onClick }; the shell is not another focus stop.

Multi-area workspace

This example uses three peer areas in one SubNavigationPager: a compact overview with bottom actions, an actionable list, and a read-only surface. Other pager pages can use any content and composition appropriate for their area.
Recommended practices:
  • Use one SubNavigationPager as the primary route surface and provide one child for each navigation item. If a child scrolls, give it one vertical scroll owner and apply insetForHeader to that owner; a static child needs neither.
  • Keep an area’s action shell inside that child: place the scroller in the flexible track and ButtonRail in the auto track. A rail beside the tab bar would mix area navigation and local actions.
  • Keep peer areas at the same hierarchy. When a record needs its own screen, add a route and retain the pager as one destination rather than turning one area into an embedded detail.
  • Store the active index above the pager so state updates and route returns preserve it.

Loading and replacement

import { Shimmer, ShimmerItem, ShimmerItemCornerRadius } from '@wearables-ui-toolkit/mrbd';

<Shimmer showShimmer={isLoading}>
  <ShimmerItem
    width="100%"
    cornerRadius={ShimmerItemCornerRadius.MEDIUM}
    className="row-skeleton"
  />
</Shimmer>
Recommended practices:
  • Use Shimmer with ShimmerItem when the pending structure is known, and IndeterminateLoader when completion time is unknown.
  • Preserve layout and focus ownership during asynchronous transitions. Keep the focused parent mounted while status, icon, or loading content changes.
  • Let press feedback finish even when application state replaces the icon. ButtonHandle.animateActionTransition({ endingIcon, endingTitle }) supports this send-then-sent case.
  • Memoize material and shape-provider instances so renders reuse them.
  • Prefer declarative state changes so components run their built-in transitions.
  • If loaded content must replace the focused target, move focus to an equivalent valid target first.

Empty, not-found, and education states

Recommended practices:
  • In a Page or pager child, render an empty state as ordinary inset ScrollView content: a short BODY2_EMPHASIZED line, one BODY2 sentence, and one Button only when a real recovery path exists. Set tabIndex={0} and an ariaLabel so the scroller remains operable.
  • Keep empty copy out of ListItem; a fabricated row creates a false focus target. Likewise, avoid inventing an add or import action the application does not support.
  • Use Modal for structured education content on a dedicated route when the task must pause. Choose its icon, bannerSrc, logoSrc, avatarSrc, or listItems slot and place a ButtonGroup in buttons. Add a leading visual only when it explains the state. Modal does not provide overlay or dismissal behavior itself.
  • Send recovery to the collection rather than one hardcoded record.

Error and permission flows

Recommended composition:
  • Use a dedicated route containing Modal for a blocking requirement. Use separate presentation infrastructure only when it explicitly owns the backdrop, focus containment, and dismissal behavior that Modal does not provide.
  • Use Toast for non-blocking feedback.
  • Use ListItem for actionable settings rows.
  • Put actions in ButtonGroup or a docked ButtonRail.
Recommended practices:
  • State what changed and what the user can do, with the action outside body copy.
  • A presenter that overlays a modal must keep focus within the presented content or on its primary action while obscured content remains visible. Modal does not enforce that boundary; a dedicated route avoids exposing background controls.
  • Let Back or Escape dismiss the dedicated decision route or the topmost presenter-owned transient surface before returning to earlier route content.
  • When an action is unavailable, remove its focus target or use the component’s disabled state. Do not leave a focusable callback that returns without feedback.
  • Separate destructive bulk actions from routine commands and require explicit confirmation that names their scope.