For AI agents: the complete documentation index is available at https://a3s-lab.github.io/UI/v0.4.1/en/llms.txt, the full documentation bundle is available at https://a3s-lab.github.io/UI/v0.4.1/en/llms-full.txt, and this page is available as Markdown at https://a3s-lab.github.io/UI/v0.4.1/en/components/device-simulator.md.

Device Simulator

Device Simulator switches between phone, tablet, and desktop viewports inside a bounded workbench and renders the matching hardware shell for each class. Browser mode uses a real-size iframe; native mode hands safe arguments to a trusted host that can launch a3s-webview.

Live preview
Device preview
Preview ready at 393 × 852.a3s-webview --url …

The phone shell includes a metal rail, sensor area, and bottom gesture region, while the tablet shell includes a narrow bezel and camera. Laptops add a display bezel, hinge, and base; desktop monitors add a chin and stand. Every shell scales with its device to fit the workbench, while the iframe retains the exact selected CSS viewport dimensions.

The simulator does not spoof the User-Agent, device pixel ratio, or touch capability, inject safe-area variables into the target, or simulate network throttling or operating-system browser behavior. The target must also allow iframe embedding; pages restricted by X-Frame-Options or CSP frame-ancestors should use native preview instead.

Usage

Load the runtime and Device Simulator controller. The complete style pack already includes the structural and Harness visual layers.

<section
  class="device-simulator"
  data-device="phone"
  data-orientation="portrait"
>
  <header>
    <select data-device-simulator-select aria-label="Device preset">
      <option
        value="phone"
        data-kind="phone"
        data-orientation="portrait"
        data-width="393"
        data-height="852"
      >
        Phone · 393 × 852
      </option>
      <option
        value="desktop"
        data-kind="desktop"
        data-orientation="landscape"
        data-width="1440"
        data-height="900"
      >
        Desktop · 1440 × 900
      </option>
      <option value="custom">Custom</option>
    </select>
    <input
      type="number"
      min="240"
      max="4000"
      value="393"
      data-device-simulator-width
    />
    <input
      type="number"
      min="180"
      max="3000"
      value="852"
      data-device-simulator-height
    />
    <div
      role="group"
      aria-label="Orientation"
      data-device-simulator-orientation
    >
      <button
        type="button"
        aria-pressed="true"
        data-device-simulator-orientation-value="portrait"
      >
        Portrait
      </button>
      <button
        type="button"
        aria-pressed="false"
        data-device-simulator-orientation-value="landscape"
      >
        Landscape
      </button>
    </div>
    <form data-device-simulator-navigation>
      <input
        type="text"
        inputmode="url"
        value="https://example.com"
        data-device-simulator-url
      />
      <button type="submit">Open</button>
      <button type="button" data-device-simulator-refresh>Refresh</button>
    </form>
  </header>
  <div data-device-simulator-workspace>
    <div data-device-simulator-canvas>
      <div data-device-simulator-frame>
        <iframe
          title="Application preview"
          src="https://example.com"
          data-device-simulator-preview
        ></iframe>
        <span hidden data-device-simulator-screen-status></span>
      </div>
    </div>
  </div>
  <footer>
    <output aria-live="polite" data-device-simulator-status></output>
    <code data-device-simulator-command></code>
    <button type="button" data-device-simulator-copy-command>
      Copy native command
    </button>
    <button type="button" data-device-simulator-native>
      Open native preview
    </button>
  </footer>
</section>
<script type="module">
  import "@a3s-lab/ui/runtime";
  import "@a3s-lab/ui/device-simulator";
</script>

The complete runtime package is equivalent to import "@a3s-lab/ui/all".

Parameters

ParameterValuesDefaultPurpose
Preset data-width240–4000—Preset CSS viewport width
Preset data-height180–3000—Preset CSS viewport height
Preset data-kindphone, tablet, desktopInferred from widthSelects the phone, tablet, or computer hardware shell
Preset data-orientationportrait, landscapeKeeps current orientationSynchronizes a preset's natural orientation when selected
Root data-orientationportrait, landscapeportraitCurrent screen orientation
Root data-fitactual or omittedAutomatic fitUses a 1:1 scale or shrinks the viewport into the workspace
Root data-device-titleStringiframe titleNative window title
--device-simulator-workspace-min-heightCSS length38remWorkspace minimum height
--device-simulator-workspace-max-heightCSS length44remWorkspace maximum height

Custom dimensions are clamped to the same range as a3s-webview. Changing either custom input selects the option whose value is custom and emits a size-change event.

JavaScript API

The root element exposes these methods after initialization:

MethodPurpose
setDevice(value)Selects a preset and returns false when it does not exist
setSize(width, height)Switches to custom dimensions and clamps the values
setOrientation(value)Sets portrait or landscape
navigate(url?)Validates and opens an HTTP, HTTPS, file, or relative URL
refreshPreview()Reloads the current iframe URL
openNativePreview()Emits a native preview request, then copies the command when unhandled

Dimension changes emit a3s:device-change; URL navigation emits a3s:device-navigate. Native preview uses the cancelable, bubbling, and composed a3s:device-preview-request event. Its detail contains { executable, args, url, width, height, title, command }.

A trusted host should call preventDefault() to claim the request and use the structured args directly. Do not reparse the display-and-copy-only command string.

const simulator = document.querySelector(".device-simulator");

simulator.addEventListener("a3s:device-preview-request", async (event) => {
  if (!window.trustedPreviewHost) return;
  event.preventDefault();

  const { executable, args } = event.detail;
  await window.trustedPreviewHost.launch({ executable, args });
});

The web controller never executes a local process directly. Without a trusted host, Native preview only copies the equivalent command to the clipboard.

States and variants

  • data-state="loading" exposes progress while the iframe navigates.
  • data-state="ready" means the dimensions or preview are synchronized.
  • data-state="error" reports an invalid URL or copy failure and names the recovery in the status output.
  • Phones, tablets, laptops, and desktop monitors use distinct bezels, sensors, bases, or stands. The iframe always uses the real CSS-pixel dimensions in the controls.
  • A file: URL can be handed to a native host. Browser security policy decides whether an iframe may load a local file.

Accessibility

Give the root, preset selector, orientation group, URL input, and iframe accessible names. Orientation buttons use aria-pressed, and status output uses aria-live="polite". The workspace remains an independent scroll owner, and scaling the visual frame does not reorder keyboard focus inside the iframe. Custom dimension controls must retain numeric min, max, and clear labels.