screenshot

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Screenshot-Based UI Verification

基于截图的UI视觉验证

Visual verification workflow for UI changes to accelerate code review and catch responsive design issues early.
针对UI变更的视觉验证工作流,用于加速代码评审并提前发现响应式设计问题。

When to Use This Skill

何时使用该Skill

Use this skill when:
  • Making any UI changes (components, styling, layout)
  • Implementing responsive design
  • Creating pull requests with visual changes
  • Want to demonstrate UI behavior without reviewers running code locally
  • Need to document visual state before/after bug fixes
在以下场景使用此Skill:
  • 进行任何UI变更(组件、样式、布局)时
  • 实现响应式设计时
  • 创建包含视觉变更的拉取请求(PR)时
  • 希望无需评审人员本地运行代码即可展示UI行为时
  • 需要记录Bug修复前后的视觉状态时

Why Screenshot Verification Matters

为什么截图验证很重要

Benefits

优势

  • Faster Reviews: Reviewers see changes instantly without local setup
  • Documents Design: Creates visual record of design decisions
  • Visual Changelog: Historical record of UI evolution
  • Catches Responsive Issues: Early detection of mobile/tablet problems
  • Reduces Communication: Less back-and-forth about visual changes
  • Quality Gate: Forces conscious review of visual output
  • 更快的评审速度:评审人员无需本地搭建环境即可立即查看变更
  • 设计文档化:创建设计决策的视觉记录
  • 视觉变更日志:UI演进的历史记录
  • 提前发现响应式问题:尽早检测移动端/平板端问题
  • 减少沟通成本:减少关于视觉变更的来回沟通
  • 质量关卡:强制对视觉输出进行有意识的评审

Problems It Solves

解决的问题

  • ❌ "I can't reproduce the layout issue locally"
  • ❌ "What does this look like on mobile?"
  • ❌ "Is this the intended design?"
  • ❌ "How does this compare to the old version?"
  • ✅ All answered with screenshots in PR
  • ❌ "我无法在本地复现布局问题"
  • ❌ "这在移动端是什么样子?"
  • ❌ "这是预期的设计吗?"
  • ❌ "这和旧版本相比有什么变化?"
  • ✅ 所有问题都可通过PR中的截图得到解答

Required Screenshots

所需截图

For any PR that changes UI, capture all three viewport sizes:
对于任何包含UI变更的PR,需捕获以下三种视口尺寸的截图:

1. Desktop View (1920x1080)

1. 桌面端视图(1920x1080)

  • Full page screenshot
  • Key component close-ups if needed
  • Before and after comparisons (for fixes/refactors)
  • Different states (default, hover, active, error, loading)
  • 全页面截图
  • 必要时添加关键组件特写
  • Bug修复/重构前后的对比图
  • 不同状态(默认、悬停、激活、错误、加载)

2. Tablet View (768x1024)

2. 平板端视图(768x1024)

  • Portrait orientation
  • Verify responsive breakpoints
  • Touch interaction targets visible
  • Menu/navigation in tablet mode
  • 竖屏方向
  • 验证响应式断点
  • 触控交互目标可见
  • 平板模式下的菜单/导航

3. Mobile View (375x667)

3. 移动端视图(375x667)

  • Portrait orientation (iPhone 8/SE size - common minimum)
  • Touch target sizes visible (minimum 44x44px)
  • Scrolling behavior documented
  • Mobile menu states
  • 竖屏方向(iPhone 8/SE尺寸 - 常见最小规格)
  • 触控目标尺寸可见(最小44x44px)
  • 记录滚动行为
  • 移动端菜单状态

How to Capture Screenshots

如何捕获截图

Browser DevTools Method

浏览器开发者工具方法

Chrome/Edge DevTools:
  1. Open DevTools (F12 or Cmd+Option+I)
  2. Toggle device toolbar (Cmd+Shift+M or Ctrl+Shift+M)
  3. Select device preset or custom dimensions:
    • Desktop: 1920 x 1080
    • Tablet: 768 x 1024
    • Mobile: 375 x 667
  4. Capture screenshot:
    • Full page: Cmd+Shift+P → "Capture full size screenshot"
    • Viewport only: Cmd+Shift+P → "Capture screenshot"
Firefox DevTools:
  1. Open DevTools (F12)
  2. Toggle Responsive Design Mode (Cmd+Option+M)
  3. Set dimensions
  4. Click screenshot icon in toolbar
Chrome/Edge DevTools:
  1. 打开开发者工具(F12 或 Cmd+Option+I)
  2. 切换设备工具栏(Cmd+Shift+M 或 Ctrl+Shift+M)
  3. 选择设备预设或自定义尺寸:
    • 桌面端:1920 x 1080
    • 平板端:768 x 1024
    • 移动端:375 x 667
  4. 捕获截图:
    • 全页面:Cmd+Shift+P → "捕获完整尺寸截图"
    • 仅视口:Cmd+Shift+P → "捕获截图"
Firefox DevTools:
  1. 打开开发者工具(F12)
  2. 切换响应式设计模式(Cmd+Option+M)
  3. 设置尺寸
  4. 点击工具栏中的截图图标

CLI Screenshot Tools

命令行截图工具

Using Playwright (recommended for CI):
javascript
// screenshot.js
const { chromium } = require('playwright');

async function captureScreenshots(url) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(url);

  // Desktop
  await page.setViewportSize({ width: 1920, height: 1080 });
  await page.screenshot({
    path: 'screenshots/desktop.png',
    fullPage: true
  });

  // Tablet
  await page.setViewportSize({ width: 768, height: 1024 });
  await page.screenshot({
    path: 'screenshots/tablet.png',
    fullPage: true
  });

  // Mobile
  await page.setViewportSize({ width: 375, height: 667 });
  await page.screenshot({
    path: 'screenshots/mobile.png',
    fullPage: true
  });

  await browser.close();
}

captureScreenshots('http://localhost:3000');
Run:
node screenshot.js
Using Puppeteer:
javascript
// screenshot.js
const puppeteer = require('puppeteer');

async function captureScreenshots(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto(url, { waitUntil: 'networkidle2' });

  // Desktop
  await page.setViewport({ width: 1920, height: 1080 });
  await page.screenshot({
    path: 'screenshots/desktop.png',
    fullPage: true
  });

  // Tablet
  await page.setViewport({ width: 768, height: 1024 });
  await page.screenshot({
    path: 'screenshots/tablet.png',
    fullPage: true
  });

  // Mobile
  await page.setViewport({ width: 375, height: 667 });
  await page.screenshot({
    path: 'screenshots/mobile.png',
    fullPage: true
  });

  await browser.close();
}

captureScreenshots('http://localhost:3000');
使用Playwright(CI环境推荐):
javascript
// screenshot.js
const { chromium } = require('playwright');

async function captureScreenshots(url) {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(url);

  // Desktop
  await page.setViewportSize({ width: 1920, height: 1080 });
  await page.screenshot({
    path: 'screenshots/desktop.png',
    fullPage: true
  });

  // Tablet
  await page.setViewportSize({ width: 768, height: 1024 });
  await page.screenshot({
    path: 'screenshots/tablet.png',
    fullPage: true
  });

  // Mobile
  await page.setViewportSize({ width: 375, height: 667 });
  await page.screenshot({
    path: 'screenshots/mobile.png',
    fullPage: true
  });

  await browser.close();
}

captureScreenshots('http://localhost:3000');
运行:
node screenshot.js
使用Puppeteer:
javascript
// screenshot.js
const puppeteer = require('puppeteer');

async function captureScreenshots(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto(url, { waitUntil: 'networkidle2' });

  // Desktop
  await page.setViewport({ width: 1920, height: 1080 });
  await page.screenshot({
    path: 'screenshots/desktop.png',
    fullPage: true
  });

  // Tablet
  await page.setViewport({ width: 768, height: 1024 });
  await page.screenshot({
    path: 'screenshots/tablet.png',
    fullPage: true
  });

  // Mobile
  await page.setViewport({ width: 375, height: 667 });
  await page.screenshot({
    path: 'screenshots/mobile.png',
    fullPage: true
  });

  await browser.close();
}

captureScreenshots('http://localhost:3000');

PR Description Template

PR描述模板

Use this template to document visual changes:
markdown
undefined
使用以下模板记录视觉变更:
markdown
undefined

Visual Changes

视觉变更

Desktop (1920x1080)

桌面端(1920x1080)

Desktop view
Key changes:
  • Updated header navigation layout
  • Improved spacing between sections
  • Added hover states to buttons
Desktop view
主要变更:
  • 更新了头部导航布局
  • 优化了区块间的间距
  • 为按钮添加了悬停状态

Tablet (768x1024)

平板端(768x1024)

Tablet view
Key changes:
  • Stacked layout for sidebar
  • Touch-friendly button sizes (48x48px)
  • Adjusted typography for readability
Tablet view
主要变更:
  • 侧边栏改为堆叠布局
  • 按钮尺寸适配触控(48x48px)
  • 调整排版提升可读性

Mobile (375x667)

移动端(375x667)

Mobile view
Key changes:
  • Hamburger menu replaces horizontal nav
  • Single column layout
  • Bottom sticky CTA button
Mobile view
主要变更:
  • 汉堡菜单替代横向导航
  • 单列布局
  • 底部固定CTA按钮

Before/After Comparison

前后对比

Before (Bug)

修复前(Bug状态)

Before fix
Issue: Text overflowing container on mobile
Before fix
问题:移动端文本溢出容器

After (Fixed)

修复后(完成状态)

After fix
Fix: Applied word-wrap and max-width constraints
After fix
修复方案:应用自动换行和最大宽度限制

Interaction States

交互状态

Default State

默认状态

Default
Default

Hover State

悬停状态

Hover
Hover

Active/Selected State

激活/选中状态

Active
Active

Error State

错误状态

Error
Error

Loading State

加载状态

Loading
Loading

Responsive Design Notes

响应式设计说明

  • Breakpoints: 768px (tablet), 375px (mobile)
  • All touch targets > 44x44px
  • Text remains readable at all sizes (min 16px body)
  • No horizontal scrolling on any viewport
  • Images scale proportionally
  • 断点:768px(平板端)、375px(移动端)
  • 所有触控目标尺寸>44x44px
  • 所有尺寸下文本保持可读(正文字号最小16px)
  • 所有视口无横向滚动
  • 图片按比例缩放

Accessibility Checks

可访问性检查

  • Keyboard navigation works
  • Focus states visible
  • Color contrast meets WCAG AA (4.5:1)
  • Alt text on images
  • ARIA labels where needed
undefined
  • 键盘导航正常工作
  • 焦点状态可见
  • 颜色对比度符合WCAG AA标准(4.5:1)
  • 图片包含替代文本
  • 必要时添加ARIA标签
undefined

Automated Screenshot Testing

自动化截图测试

Using Playwright Test

使用Playwright Test

javascript
// tests/visual.spec.js
const { test, expect } = require('@playwright/test');

test.describe('Visual Regression', () => {
  test('homepage looks correct on desktop', async ({ page }) => {
    await page.goto('http://localhost:3000');
    await page.setViewportSize({ width: 1920, height: 1080 });
    await expect(page).toHaveScreenshot('homepage-desktop.png');
  });

  test('homepage looks correct on mobile', async ({ page }) => {
    await page.goto('http://localhost:3000');
    await page.setViewportSize({ width: 375, height: 667 });
    await expect(page).toHaveScreenshot('homepage-mobile.png');
  });
});
Run:
npx playwright test --update-snapshots
(first time to generate baselines)
javascript
// tests/visual.spec.js
const { test, expect } = require('@playwright/test');

test.describe('Visual Regression', () => {
  test('homepage looks correct on desktop', async ({ page }) => {
    await page.goto('http://localhost:3000');
    await page.setViewportSize({ width: 1920, height: 1080 });
    await expect(page).toHaveScreenshot('homepage-desktop.png');
  });

  test('homepage looks correct on mobile', async ({ page }) => {
    await page.goto('http://localhost:3000');
    await page.setViewportSize({ width: 375, height: 667 });
    await expect(page).toHaveScreenshot('homepage-mobile.png');
  });
});
运行:
npx playwright test --update-snapshots
(首次运行生成基准截图)

Using Storybook + Chromatic

使用Storybook + Chromatic

For component-level visual testing:
  1. Setup Storybook:
javascript
// Button.stories.jsx
export default {
  title: 'Components/Button',
  component: Button,
};

export const Primary = () => <Button variant="primary">Click me</Button>;
export const Secondary = () => <Button variant="secondary">Click me</Button>;
  1. Integrate Chromatic:
bash
npm install --save-dev chromatic
npx chromatic --project-token=<token>
  1. CI Integration (GitHub Actions):
yaml
undefined
针对组件级别的视觉测试:
  1. 搭建Storybook:
javascript
// Button.stories.jsx
export default {
  title: 'Components/Button',
  component: Button,
};

export const Primary = () => <Button variant="primary">Click me</Button>;
export const Secondary = () => <Button variant="secondary">Click me</Button>;
  1. 集成Chromatic:
bash
npm install --save-dev chromatic
npx chromatic --project-token=<token>
  1. CI集成(GitHub Actions):
yaml
undefined

.github/workflows/chromatic.yml

.github/workflows/chromatic.yml

name: Chromatic on: push jobs: chromatic: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx chromatic --project-token=${{ secrets.CHROMATIC_TOKEN }}
undefined
name: Chromatic on: push jobs: chromatic: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx chromatic --project-token=${{ secrets.CHROMATIC_TOKEN }}
undefined

Screenshot Checklist

截图检查清单

Before submitting PR with UI changes:
提交包含UI变更的PR前,请检查:

Capture Requirements

捕获要求

  • Desktop screenshot (1920x1080) captured
  • Tablet screenshot (768x1024) captured
  • Mobile screenshot (375x667) captured
  • All screenshots uploaded to PR
  • Before/after comparison included (for bug fixes)
  • 已捕获桌面端截图(1920x1080)
  • 已捕获平板端截图(768x1024)
  • 已捕获移动端截图(375x667)
  • 所有截图已上传至PR
  • 包含前后对比图(针对Bug修复)

Quality Checks

质量检查

  • Screenshots show full page (not cut off)
  • No localhost URLs visible in screenshots
  • No sensitive data in screenshots (PII, keys, etc.)
  • Screenshots are clear and readable
  • File names are descriptive (e.g.,
    desktop-homepage.png
    )
  • 截图展示完整页面(无截断)
  • 截图中无localhost URL
  • 截图中无敏感数据(个人信息、密钥等)
  • 截图清晰可读
  • 文件命名具有描述性(例如:
    desktop-homepage.png

Documentation

文档记录

  • Key changes listed for each viewport
  • Responsive behavior described
  • Accessibility notes included
  • Interaction states documented (if applicable)
  • 列出每个视口的主要变更
  • 描述响应式行为
  • 包含可访问性说明
  • 记录交互状态(如适用)

Common Issues and Solutions

常见问题与解决方案

Issue: Screenshots Too Large

问题:截图文件过大

Problem: Screenshots are 5MB+ and slow to load in PR Solution: Compress images before uploading
bash
undefined
问题:截图大小超过5MB,在PR中加载缓慢 解决方案:上传前压缩图片
bash
undefined

Using ImageMagick

使用ImageMagick

convert input.png -quality 85 output.png
convert input.png -quality 85 output.png

Using pngquant

使用pngquant

pngquant input.png --output output.png
undefined
pngquant input.png --output output.png
undefined

Issue: Dynamic Content Changes Between Screenshots

问题:截图间动态内容不一致

Problem: Timestamps, random data make screenshots inconsistent Solution: Mock data or freeze time in tests
javascript
// Mock Date
const mockDate = new Date('2025-01-01T00:00:00Z');
jest.useFakeTimers();
jest.setSystemTime(mockDate);

// Or in Playwright
await page.addInitScript(() => {
  Date.now = () => 1704067200000; // Fixed timestamp
});
问题:时间戳、随机数据导致截图不一致 解决方案:模拟数据或在测试中冻结时间
javascript
// 模拟日期
const mockDate = new Date('2025-01-01T00:00:00Z');
jest.useFakeTimers();
jest.setSystemTime(mockDate);

// 或在Playwright中
await page.addInitScript(() => {
  Date.now = () => 1704067200000; // 固定时间戳
});

Issue: Screenshot Diffs Show Font Rendering Differences

问题:截图差异显示字体渲染不同

Problem: Same code renders differently on different OS Solution: Use Docker for consistent environment
dockerfile
FROM mcr.microsoft.com/playwright:v1.40.0-jammy
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npm", "run", "screenshot"]
问题:同一代码在不同操作系统上渲染效果不同 解决方案:使用Docker保证环境一致性
dockerfile
FROM mcr.microsoft.com/playwright:v1.40.0-jammy
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npm", "run", "screenshot"]

Screenshot Organization

截图管理

Directory Structure

目录结构

screenshots/
├── desktop/
│   ├── homepage.png
│   ├── product-list.png
│   └── checkout.png
├── tablet/
│   ├── homepage.png
│   ├── product-list.png
│   └── checkout.png
└── mobile/
    ├── homepage.png
    ├── product-list.png
    └── checkout.png
screenshots/
├── desktop/
│   ├── homepage.png
│   ├── product-list.png
│   └── checkout.png
├── tablet/
│   ├── homepage.png
│   ├── product-list.png
│   └── checkout.png
└── mobile/
    ├── homepage.png
    ├── product-list.png
    └── checkout.png

Naming Convention

命名规范

  • Use descriptive names:
    desktop-homepage-logged-in.png
  • Include state if relevant:
    mobile-form-error-state.png
  • Version comparisons:
    before-header-fix.png
    ,
    after-header-fix.png
  • 使用描述性名称:
    desktop-homepage-logged-in.png
  • 相关状态需包含在内:
    mobile-form-error-state.png
  • 版本对比:
    before-header-fix.png
    ,
    after-header-fix.png

Integration with CI/CD

与CI/CD集成

GitHub Actions Example

GitHub Actions示例

yaml
name: Visual Testing
on: pull_request

jobs:
  screenshots:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build app
        run: npm run build

      - name: Start app
        run: npm start &

      - name: Wait for app
        run: npx wait-on http://localhost:3000

      - name: Capture screenshots
        run: node scripts/screenshot.js

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        with:
          name: screenshots
          path: screenshots/
yaml
name: Visual Testing
on: pull_request

jobs:
  screenshots:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build app
        run: npm run build

      - name: Start app
        run: npm start &

      - name: Wait for app
        run: npx wait-on http://localhost:3000

      - name: Capture screenshots
        run: node scripts/screenshot.js

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        with:
          name: screenshots
          path: screenshots/

Best Practices

最佳实践

Do's

建议

  • ✅ Capture all three viewport sizes
  • ✅ Include before/after for bug fixes
  • ✅ Document interaction states (hover, active, error)
  • ✅ Compress large images
  • ✅ Use descriptive file names
  • ✅ Annotate screenshots with key changes
  • ✅ 捕获三种视口尺寸的截图
  • ✅ Bug修复需包含前后对比图
  • ✅ 记录交互状态(悬停、激活、错误)
  • ✅ 压缩大尺寸图片
  • ✅ 使用描述性文件名
  • ✅ 为截图标注主要变更

Don'ts

不建议

  • ❌ Skip mobile screenshots ("desktop only" is rare)
  • ❌ Upload screenshots with sensitive data
  • ❌ Use random viewport sizes (stick to standards)
  • ❌ Forget to document responsive breakpoints
  • ❌ Rely solely on screenshots (still need code review)
  • ❌ 跳过移动端截图(“仅桌面端”场景极少)
  • ❌ 上传包含敏感数据的截图
  • ❌ 使用随机视口尺寸(遵循标准)
  • ❌ 忘记记录响应式断点
  • ❌ 仅依赖截图(仍需进行代码评审)

Related Skills

相关Skill

  • universal-verification-pre-merge
    - Pre-merge verification checklist
  • universal-testing-webapp-testing
    - Web application testing patterns
  • toolchains-javascript-testing-playwright
    - Playwright testing framework
  • universal-debugging-verification-before-completion
    - Verification workflows
  • universal-verification-pre-merge
    - 合并前通用验证检查清单
  • universal-testing-webapp-testing
    - Web应用测试模式
  • toolchains-javascript-testing-playwright
    - Playwright测试框架
  • universal-debugging-verification-before-completion
    - 完成前验证工作流