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.
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-mockCopies 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.jsonWorks, 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.
| Prop | Type | Default |
|---|---|---|
childrenrequiredThe content shown on the screen. | node | none |
deviceWhich frame to draw. | "phone" | "tablet" | "laptop" | "desktop" | "watch" | "browser" | "phone" |
orientationTurns a phone or a tablet on its side. Other devices ignore it. | "portrait" | "landscape" | "portrait" |
toneBezel color. | "dark" | "light" | "dark" |
labelAccessible name of the group, for example Mobile preview. Without it the frame adds nothing to the accessibility tree. | string | none |
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