đŸŽ¯

mobile-emulation

đŸŽ¯Skill

from adaptationio/skrillz

VibeIndex|
What it does

mobile-emulation skill from adaptationio/skrillz

đŸ“Ļ

Part of

adaptationio/skrillz(191 items)

mobile-emulation

Installation

Add MarketplaceAdd marketplace to Claude Code
/plugin marketplace add adaptationio/Skrillz
Install PluginInstall plugin from marketplace
/plugin install skrillz@adaptationio-Skrillz
Claude CodeAdd plugin in Claude Code
/plugin enable skrillz@adaptationio-Skrillz
Add MarketplaceAdd marketplace to Claude Code
/plugin marketplace add /path/to/skrillz
Install PluginInstall plugin from marketplace
/plugin install skrillz@local

+ 4 more commands

📖 Extracted from docs: adaptationio/skrillz
1Installs
3
-
Last UpdatedJan 16, 2026

Skill Details

SKILL.md

Mobile device emulation and responsive testing with Playwright. Use when testing mobile layouts, touch interactions, device-specific features, or responsive breakpoints.

Overview

# Mobile Emulation Testing with Playwright

Test responsive designs and mobile-specific features using Playwright's device emulation capabilities.

Quick Start

```typescript

import { test, expect, devices } from '@playwright/test';

test.use(devices['iPhone 14']);

test('mobile navigation works', async ({ page }) => {

await page.goto('/');

// Mobile menu should be visible

await page.getByRole('button', { name: 'Menu' }).click();

await expect(page.getByRole('navigation')).toBeVisible();

});

```

Configuration

Project-Based Device Testing

playwright.config.ts:

```typescript

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({

projects: [

// Desktop browsers

{

name: 'Desktop Chrome',

use: { ...devices['Desktop Chrome'] },

},

{

name: 'Desktop Firefox',

use: { ...devices['Desktop Firefox'] },

},

{

name: 'Desktop Safari',

use: { ...devices['Desktop Safari'] },

},

// Mobile devices

{

name: 'iPhone 14',

use: { ...devices['iPhone 14'] },

},

{

name: 'iPhone 14 Pro Max',

use: { ...devices['iPhone 14 Pro Max'] },

},

{

name: 'Pixel 7',

use: { ...devices['Pixel 7'] },

},

{

name: 'Galaxy S23',

use: { ...devices['Galaxy S III'] }, // Closest available

},

// Tablets

{

name: 'iPad Pro',

use: { ...devices['iPad Pro 11'] },

},

{

name: 'iPad Mini',

use: { ...devices['iPad Mini'] },

},

],

});

```

Custom Device Configuration

```typescript

{

name: 'Custom Mobile',

use: {

viewport: { width: 390, height: 844 },

userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...',

deviceScaleFactor: 3,

isMobile: true,

hasTouch: true,

defaultBrowserType: 'webkit',

},

},

```

Available Devices

Popular Devices

```typescript

import { devices } from '@playwright/test';

// iPhones

devices['iPhone 14']

devices['iPhone 14 Plus']

devices['iPhone 14 Pro']

devices['iPhone 14 Pro Max']

devices['iPhone 13']

devices['iPhone 12']

devices['iPhone SE']

// Android Phones

devices['Pixel 7']

devices['Pixel 5']

devices['Galaxy S III']

devices['Galaxy S5']

devices['Galaxy Note 3']

devices['Nexus 5']

// Tablets

devices['iPad Pro 11']

devices['iPad Pro 11 landscape']

devices['iPad Mini']

devices['iPad (gen 7)']

devices['Galaxy Tab S4']

// Desktop

devices['Desktop Chrome']

devices['Desktop Firefox']

devices['Desktop Safari']

devices['Desktop Edge']

```

List All Devices

```typescript

import { devices } from '@playwright/test';

console.log(Object.keys(devices));

// Outputs all available device names

```

Responsive Breakpoint Testing

Test Multiple Viewports

```typescript

const breakpoints = [

{ name: 'mobile', width: 375, height: 667 },

{ name: 'tablet', width: 768, height: 1024 },

{ name: 'desktop', width: 1280, height: 720 },

{ name: 'wide', width: 1920, height: 1080 },

];

for (const bp of breakpoints) {

test(layout at ${bp.name}, async ({ page }) => {

await page.setViewportSize({ width: bp.width, height: bp.height });

await page.goto('/');

await expect(page).toHaveScreenshot(layout-${bp.name}.png);

});

}

```

Dynamic Viewport Changes

```typescript

test('responsive navigation', async ({ page }) => {

await page.goto('/');

// Desktop - horizontal nav

await page.setViewportSize({ width: 1280, height: 720 });

await expect(page.locator('.desktop-nav')).toBeVisible();

await expect(page.locator('.mobile-menu-button')).not.toBeVisible();

// Tablet - may show hamburger

await page.setViewportSize({ width: 768, height: 1024 });

// Mobile - hamburger menu

await page.setViewportSize({ width: 375, height: 667 });

await expect(page.locator('.desktop-nav')).not.toBeVisible();

await expect(page.locator('.mobile-menu-button')).toBeVisible();

});

```

Touch Interactions

Tap

```typescript

test('tap interaction', async ({ page }) => {

await page.goto('/');

// Tap is equivalent to click on touch devices

await page.getByRole('button').tap();

});

```

Swipe

```typescript

test('swipe carousel', async ({ page }) => {

await page.goto('/gallery');

const carousel = page.locator('.carousel');

const box = await carousel.boundingBox();

if (box) {

// Swipe left

await page.mouse.move(box.x + box.width - 50, box.y + box.height / 2);

await page.mouse.down();

await page.mouse.move(box.x + 50, box.y + box.height / 2, { steps: 10 });

await page.mouse.up();

}

await expect(page.locator('.slide-2')).toBeVisible();

});

```

Pinch to Zoom

```typescript

test('pinch to zoom map', async ({ page }) => {

await page.goto('/map');

const map = page.locator('#map');

const box = await map.boundingBox();

if (box) {

const centerX = box.x + box.width / 2;

const centerY = box.y + box.height / 2;

// Simulate pinch out (zoom in)

await page.touchscreen.tap(centerX, centerY);

// Note: Multi-touch pinch requires custom implementation

}

});

```

Long Press

```typescript

test('long press context menu', async ({ page }) => {

await page.goto('/');

const element = page.locator('.long-press-target');

const box = await element.boundingBox();

if (box) {

await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);

await page.mouse.down();

await page.waitForTimeout(1000); // Hold for 1 second

await page.mouse.up();

}

await expect(page.locator('.context-menu')).toBeVisible();

});

```

Orientation Testing

Portrait vs Landscape

```typescript

test('orientation change', async ({ page }) => {

// Portrait

await page.setViewportSize({ width: 390, height: 844 });

await page.goto('/video');

await expect(page.locator('.video-container')).toHaveCSS('width', '390px');

// Landscape

await page.setViewportSize({ width: 844, height: 390 });

await expect(page.locator('.video-container')).toHaveCSS('width', '844px');

});

```

Using Device Landscape Variants

```typescript

test.use(devices['iPad Pro 11 landscape']);

test('tablet landscape layout', async ({ page }) => {

await page.goto('/dashboard');

// Sidebar should be visible in landscape

await expect(page.locator('.sidebar')).toBeVisible();

});

```

Geolocation Testing

```typescript

test.use({

geolocation: { latitude: 40.7128, longitude: -74.0060 }, // NYC

permissions: ['geolocation'],

});

test('shows nearby locations', async ({ page }) => {

await page.goto('/locations');

await page.getByRole('button', { name: 'Find Nearby' }).click();

await expect(page.getByText('New York')).toBeVisible();

});

```

Change Location During Test

```typescript

test('location change', async ({ page, context }) => {

await context.setGeolocation({ latitude: 51.5074, longitude: -0.1278 }); // London

await page.goto('/weather');

await expect(page.getByText('London')).toBeVisible();

await context.setGeolocation({ latitude: 35.6762, longitude: 139.6503 }); // Tokyo

await page.reload();

await expect(page.getByText('Tokyo')).toBeVisible();

});

```

Network Conditions

Slow 3G

```typescript

test('works on slow network', async ({ page, context }) => {

// Emulate slow 3G

const client = await context.newCDPSession(page);

await client.send('Network.emulateNetworkConditions', {

offline: false,

downloadThroughput: (500 * 1024) / 8, // 500kb/s

uploadThroughput: (500 * 1024) / 8,

latency: 400, // 400ms

});

await page.goto('/');

// Should show skeleton loaders

await expect(page.locator('.skeleton')).toBeVisible();

// Eventually loads

await expect(page.locator('.content')).toBeVisible({ timeout: 30000 });

});

```

Offline Mode

```typescript

test('offline functionality', async ({ page, context }) => {

await page.goto('/');

// Cache page, then go offline

await context.setOffline(true);

await page.reload();

// Should show offline message or cached content

await expect(page.getByText(/offline/i)).toBeVisible();

});

```

Device-Specific Features

Notch/Safe Areas

```typescript

test('respects safe areas', async ({ page }) => {

// iPhone with notch

test.use(devices['iPhone 14 Pro']);

await page.goto('/');

// Header should account for notch

const header = page.locator('header');

const paddingTop = await header.evaluate(el =>

window.getComputedStyle(el).paddingTop

);

// Should have safe area inset

expect(parseInt(paddingTop)).toBeGreaterThan(20);

});

```

Dark Mode

```typescript

test.use({

colorScheme: 'dark',

});

test('dark mode styling', async ({ page }) => {

await page.goto('/');

const body = page.locator('body');

const bgColor = await body.evaluate(el =>

window.getComputedStyle(el).backgroundColor

);

// Should have dark background

expect(bgColor).toBe('rgb(0, 0, 0)'); // or dark color

});

```

Reduced Motion

```typescript

test.use({

reducedMotion: 'reduce',

});

test('respects reduced motion', async ({ page }) => {

await page.goto('/');

const animated = page.locator('.animated-element');

const animationDuration = await animated.evaluate(el =>

window.getComputedStyle(el).animationDuration

);

// Should have no animation

expect(animationDuration).toBe('0s');

});

```

Visual Regression Across Devices

```typescript

const testDevices = [

'iPhone 14',

'Pixel 7',

'iPad Pro 11',

'Desktop Chrome',

];

for (const deviceName of testDevices) {

test.describe(Visual: ${deviceName}, () => {

test.use(devices[deviceName]);

test('homepage', async ({ page }) => {

await page.goto('/');

await expect(page).toHaveScreenshot(homepage-${deviceName}.png);

});

test('product page', async ({ page }) => {

await page.goto('/products/1');

await expect(page).toHaveScreenshot(product-${deviceName}.png);

});

});

}

```

Best Practices

  1. Test real devices too - Emulation is good but not perfect
  2. Cover major breakpoints - 375px, 768px, 1024px, 1280px minimum
  3. Test both orientations - Portrait and landscape
  4. Test touch vs click - Some interactions differ
  5. Test slow networks - Mobile users often have poor connectivity
  6. Test safe areas - Account for notches, home indicators

References

  • references/device-list.md - Complete device list with specs
  • references/touch-patterns.md - Touch gesture implementations