Skip to content

Device Mock

A drawn device frame (phone, tablet, laptop, desktop, watch or browser window) around your content, which becomes the screen. The frame is plain CSS decoration with neutral bezels; the screen is a real container, so everything inside keeps its own semantics and focus order.

Motionv0.1.0experimentalWCAG 2.2 AAView spec

Optional, and kept apart.

Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.

Good morning

Three tasks are due today.

import { DeviceMock } from "@rdloom/react";

export default function DeviceMockBasicExample() {
  return (
    <div className="flex w-full justify-center">
      <DeviceMock device="phone" label="Phone preview" className="max-w-[14rem]">
        <div className="flex size-full flex-col items-center justify-center gap-2 p-4 text-center">
          <h3 className="m-0 text-base font-semibold">Good morning</h3>
          <p className="m-0 text-sm text-[var(--rd-color-text-muted)]">Three tasks are due today.</p>
        </div>
      </DeviceMock>
    </div>
  );
}

Installation

npx rdloom add device-mock

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/device-mock.json

Works, but without upgrade tracking.

Usage

import { DeviceMock } from "@rdloom/react";

<div className="flex w-full justify-center">
  <DeviceMock device="phone" label="Phone preview" className="max-w-[14rem]">
    <div className="flex size-full flex-col items-center justify-center gap-2 p-4 text-center">
      <h3 className="m-0 text-base font-semibold">Good morning</h3>
      <p className="m-0 text-sm text-[var(--rd-color-text-muted)]">Three tasks are due today.</p>
    </div>
  </DeviceMock>
</div>

Devices

Laptop

Sample screen

Desktop

Sample screen

Browser

Sample screen

Watch

Sample screen

import { DeviceMock } from "@rdloom/react";

const screen = (title: string) => (
  <div className="flex size-full min-h-24 flex-col items-center justify-center gap-1 p-3 text-center">
    <h3 className="m-0 text-sm font-semibold">{title}</h3>
    <p className="m-0 text-xs text-[var(--rd-color-text-muted)]">Sample screen</p>
  </div>
);

export default function DeviceMockDevicesExample() {
  return (
    <div className="flex w-full flex-wrap items-end justify-center gap-6">
      <DeviceMock device="laptop" label="Laptop preview" className="max-w-[20rem]">
        {screen("Laptop")}
      </DeviceMock>
      <DeviceMock device="desktop" tone="light" label="Desktop preview" className="max-w-[20rem]">
        {screen("Desktop")}
      </DeviceMock>
      <DeviceMock device="browser" label="Browser preview" className="max-w-[20rem]">
        {screen("Browser")}
      </DeviceMock>
      <DeviceMock device="watch" label="Watch preview" className="max-w-[7rem]">
        {screen("Watch")}
      </DeviceMock>
    </div>
  );
}

Tablet landscape

Weekly summary

The button below is a real, focusable button.

import { Button, DeviceMock } from "@rdloom/react";

export default function DeviceMockTabletLandscapeExample() {
  return (
    <div className="flex w-full justify-center">
      <DeviceMock device="tablet" orientation="landscape" tone="light" label="Tablet preview" className="max-w-[26rem]">
        <div className="flex size-full flex-col items-center justify-center gap-3 p-4 text-center">
          <h3 className="m-0 text-base font-semibold">Weekly summary</h3>
          <p className="m-0 text-sm text-[var(--rd-color-text-muted)]">The button below is a real, focusable button.</p>
          <Button>View report</Button>
        </div>
      </DeviceMock>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
childrenrequired

The content shown on the screen.

nodenone
device

Which frame to draw.

"phone" | "tablet" | "laptop" | "desktop" | "watch" | "browser""phone"
orientation

Turns a phone or a tablet on its side. Other devices ignore it.

"portrait" | "landscape""portrait"
tone

Bezel color.

"dark" | "light""dark"
label

Accessible name of the group, for example Mobile preview. Without it the frame adds nothing to the accessibility tree.

stringnone

Accessibility

Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab: moves through the focusable content inside the screen as if the frame were not there

Screen readers announce

  • Announced as a group with the label when given, then the screen content in order

What your code must do

  • The frame, notch, camera dots, stand and browser toolbar are aria-hidden decoration
  • The screen is a real div holding the children, so headings, forms and links inside keep their semantics
  • With label the root is a group named by it; without it the root is a plain div
  • Static: nothing moves, so there is no still switch
  • The frame scales down with its parent (width 100 percent up to a maximum) and never causes horizontal scroll
  • Forced colors: bezels keep a visible system-color border

Guidelines

Use it when

  • Showing how a screen looks on a phone, tablet, laptop or in a browser window
  • Marketing and documentation previews

Avoid it when

  • The device shape carries meaning the user must act on
  • Pixel-exact device simulation

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-border-default
  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-radius-overlay