app-store-screenshots-generator

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

App Store Screenshots Generator

App Store 截图生成工具

Skill by ara.so — Daily 2026 Skills collection.
A skill for AI coding agents (Claude Code, Cursor, Windsurf, Codex, etc.) that generates production-ready App Store screenshots for iOS apps. It scaffolds a Next.js project, designs advertisement-style slides, and exports PNGs at all required Apple resolutions.
ara.so 开发的Skill — 属于Daily 2026 Skills合集。
这是一款面向AI编码Agent(Claude Code、Cursor、Windsurf、Codex等)的Skill,用于生成可直接用于生产的iOS应用App Store截图。它会快速搭建一个Next.js项目,设计广告风格的幻灯片,并导出符合Apple所有要求分辨率的PNG图片。

What This Skill Does

该Skill的功能

  • Asks about your app's brand, features, and style preferences before building anything
  • Scaffolds a minimal Next.js + TypeScript + Tailwind project
  • Designs each screenshot as an advertisement (not a UI showcase)
  • Writes compelling copy using proven App Store copywriting patterns
  • Renders screenshots with a built-in iPhone mockup frame
  • Exports PNGs at all 4 Apple-required sizes (6.9", 6.5", 6.3", 6.1")
  • Supports multi-locale screenshot sets, RTL-aware layouts, and reusable theme presets
  • 在开始构建前,询问你的应用品牌、功能和风格偏好
  • 快速搭建一个极简的Next.js + TypeScript + Tailwind项目
  • 将每张截图设计为广告形式(而非UI展示)
  • 使用经过验证的App Store文案撰写模式创作有吸引力的文案
  • 借助内置的iPhone样机框架渲染截图
  • 导出符合Apple要求的4种尺寸的PNG图片(6.9"、6.5"、6.3"、6.1")
  • 支持多语言截图集、RTL布局和可复用的主题预设

Install

安装

Using npx skills (recommended)

使用npx skills(推荐)

bash
undefined
bash
undefined

Install for current project

为当前项目安装

npx skills add ParthJadhav/app-store-screenshots
npx skills add ParthJadhav/app-store-screenshots

Install globally (available across all projects)

全局安装(可在所有项目中使用)

npx skills add ParthJadhav/app-store-screenshots -g
npx skills add ParthJadhav/app-store-screenshots -g

Install for a specific agent

为特定Agent安装

npx skills add ParthJadhav/app-store-screenshots -a claude-code
undefined
npx skills add ParthJadhav/app-store-screenshots -a claude-code
undefined

Manual install

手动安装

bash
git clone https://github.com/ParthJadhav/app-store-screenshots ~/.claude/skills/app-store-screenshots
bash
git clone https://github.com/ParthJadhav/app-store-screenshots ~/.claude/skills/app-store-screenshots

Usage

使用方法

Once installed, just describe what you need:
> Build App Store screenshots for my habit tracker.
  The app helps people stay consistent with simple daily routines.
  I want 6 slides, clean/minimal style, warm neutrals, and a calm premium feel.
The agent will ask clarifying questions about brand colors, fonts, features, style direction, number of slides, and locales before generating anything.
安装完成后,只需描述你的需求即可:
> 为我的习惯追踪应用构建App Store截图。
  该应用帮助人们坚持简单的日常习惯。
  我需要6张幻灯片,简洁极简风格,温暖中性色调,营造沉稳高端的质感。
Agent会先询问品牌颜色、字体、功能、风格方向、幻灯片数量和语言区域等问题,之后才会开始生成内容。

Project Structure

项目结构

The skill scaffolds this layout:
project/
├── public/
│   ├── mockup.png              # iPhone frame with transparent screen area
│   ├── app-icon.png            # Your app icon
│   ├── screenshots/            # App screenshots (optionally nested by locale)
│   │   ├── en/
│   │   ├── de/
│   │   └── ar/
│   └── screenshots-ipad/       # Optional iPad screenshots
├── src/app/
│   ├── layout.tsx              # Font setup
│   └── page.tsx                # Entire screenshot generator (single file)
├── package.json
└── next.config.ts
The entire generator lives in a single
page.tsx
file.
Run the dev server, open the browser, and click any screenshot to export it as a PNG.
该Skill会搭建以下项目结构:
project/
├── public/
│   ├── mockup.png              # 带有透明屏幕区域的iPhone框架图
│   ├── app-icon.png            # 你的应用图标
│   ├── screenshots/            # 应用截图(可按语言区域嵌套)
│   │   ├── en/
│   │   ├── de/
│   │   └── ar/
│   └── screenshots-ipad/       # 可选的iPad截图
├── src/app/
│   ├── layout.tsx              # 字体配置
│   └── page.tsx                # 完整的截图生成器(单文件)
├── package.json
└── next.config.ts
整个生成器都在单个
page.tsx
文件中。
启动开发服务器,打开浏览器,点击任意截图即可将其导出为PNG格式。

Core page.tsx Pattern

核心page.tsx模式

tsx
// src/app/page.tsx — minimal scaffold pattern
"use client";

import { toPng } from "html-to-image";
import { useRef, useState } from "react";

// --- Design tokens / theme presets ---
const THEMES = {
  "clean-light": {
    bg: "#F6F1EA",
    fg: "#171717",
    accent: "#5B7CFA",
    font: "Inter",
  },
  "dark-bold": {
    bg: "#0B1020",
    fg: "#F8FAFC",
    accent: "#8B5CF6",
    font: "Inter",
  },
  "warm-editorial": {
    bg: "#F7E8DA",
    fg: "#2B1D17",
    accent: "#D97706",
    font: "Playfair Display",
  },
} as const;

type ThemeKey = keyof typeof THEMES;

// --- Export sizes (width x height in px) ---
const EXPORT_SIZES = {
  "6.9": { w: 1320, h: 2868 },
  "6.5": { w: 1284, h: 2778 },
  "6.3": { w: 1206, h: 2622 },
  "6.1": { w: 1125, h: 2436 },
} as const;

// --- Slide data ---
const SLIDES = [
  {
    id: 1,
    headline: "Track Every Habit,\nEvery Day",
    subheadline: "Build streaks that stick",
    screenshot: "/screenshots/en/home.png",
    layout: "phone-right", // varies per slide: phone-left | phone-right | phone-center
  },
  {
    id: 2,
    headline: "See Your Progress\nAt a Glance",
    subheadline: "Beautiful weekly summaries",
    screenshot: "/screenshots/en/stats.png",
    layout: "phone-left",
  },
  // ... more slides
];

// --- Screenshot canvas (designed at 6.9" — 1320x2868) ---
function ScreenshotSlide({
  slide,
  theme,
  slideRef,
}: {
  slide: (typeof SLIDES)[0];
  theme: (typeof THEMES)[ThemeKey];
  slideRef: React.RefObject<HTMLDivElement>;
}) {
  return (
    <div
      ref={slideRef}
      style={{
        width: 1320,
        height: 2868,
        background: theme.bg,
        color: theme.fg,
        fontFamily: theme.font,
        position: "relative",
        overflow: "hidden",
      }}
    >
      {/* Headline */}
      <div
        style={{
          position: "absolute",
          top: 180,
          left: 80,
          right: 80,
          fontSize: 96,
          fontWeight: 800,
          lineHeight: 1.1,
          whiteSpace: "pre-line",
        }}
      >
        {slide.headline}
      </div>

      {/* Subheadline */}
      <div
        style={{
          position: "absolute",
          top: 520,
          left: 80,
          fontSize: 48,
          color: theme.accent,
          fontWeight: 500,
        }}
      >
        {slide.subheadline}
      </div>

      {/* iPhone mockup + app screenshot */}
      <div
        style={{
          position: "absolute",
          bottom: 0,
          right: slide.layout === "phone-right" ? 0 : "auto",
          left: slide.layout === "phone-left" ? 0 : "auto",
        }}
      >
        {/* App screenshot inside mockup */}
        <div style={{ position: "relative", width: 660, height: 1430 }}>
          <img
            src={slide.screenshot}
            style={{
              position: "absolute",
              top: 28,
              left: 24,
              width: 612,
              borderRadius: 52,
            }}
            alt=""
          />
          {/* Mockup frame sits on top */}
          <img
            src="/mockup.png"
            style={{ position: "absolute", inset: 0, width: "100%" }}
            alt=""
          />
        </div>
      </div>
    </div>
  );
}

// --- Export helper ---
async function exportSlide(
  ref: React.RefObject<HTMLDivElement>,
  slideId: number,
  size: keyof typeof EXPORT_SIZES,
  locale: string,
  themeName: string
) {
  if (!ref.current) return;

  const { w, h } = EXPORT_SIZES[size];
  const scale = w / 1320; // designed at 1320px wide

  const dataUrl = await toPng(ref.current, {
    width: w,
    height: h,
    style: { transform: `scale(${scale})`, transformOrigin: "top left" },
  });

  const link = document.createElement("a");
  link.download = `slide-${slideId}-${locale}-${themeName}-${size}in.png`;
  link.href = dataUrl;
  link.click();
}

// --- Main page ---
export default function ScreenshotGenerator() {
  const [activeTheme, setActiveTheme] = useState<ThemeKey>("clean-light");
  const [activeLocale, setActiveLocale] = useState("en");
  const slideRefs = useRef<React.RefObject<HTMLDivElement>[]>(
    SLIDES.map(() => ({ current: null } as React.RefObject<HTMLDivElement>))
  );

  const theme = THEMES[activeTheme];

  return (
    <div style={{ padding: 40, background: "#111", minHeight: "100vh" }}>
      {/* Controls */}
      <div style={{ display: "flex", gap: 16, marginBottom: 40 }}>
        {(Object.keys(THEMES) as ThemeKey[]).map((t) => (
          <button
            key={t}
            onClick={() => setActiveTheme(t)}
            style={{
              padding: "8px 16px",
              background: activeTheme === t ? "#fff" : "#333",
              color: activeTheme === t ? "#000" : "#fff",
              border: "none",
              borderRadius: 8,
              cursor: "pointer",
            }}
          >
            {t}
          </button>
        ))}

        {/* Bulk export all slides at all sizes */}
        <button
          onClick={async () => {
            for (const [i, slide] of SLIDES.entries()) {
              for (const size of Object.keys(EXPORT_SIZES) as (keyof typeof EXPORT_SIZES)[]) {
                await exportSlide(
                  slideRefs.current[i],
                  slide.id,
                  size,
                  activeLocale,
                  activeTheme
                );
              }
            }
          }}
          style={{
            padding: "8px 16px",
            background: theme.accent,
            color: "#fff",
            border: "none",
            borderRadius: 8,
            cursor: "pointer",
            marginLeft: "auto",
          }}
        >
          Export All
        </button>
      </div>

      {/* Slides */}
      <div style={{ display: "flex", flexDirection: "column", gap: 40 }}>
        {SLIDES.map((slide, i) => (
          <div key={slide.id}>
            <ScreenshotSlide
              slide={slide}
              theme={theme}
              slideRef={slideRefs.current[i]}
            />
            {/* Per-slide export buttons */}
            <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
              {(Object.keys(EXPORT_SIZES) as (keyof typeof EXPORT_SIZES)[]).map(
                (size) => (
                  <button
                    key={size}
                    onClick={() =>
                      exportSlide(
                        slideRefs.current[i],
                        slide.id,
                        size,
                        activeLocale,
                        activeTheme
                      )
                    }
                    style={{
                      padding: "6px 12px",
                      background: "#222",
                      color: "#fff",
                      border: "1px solid #444",
                      borderRadius: 6,
                      cursor: "pointer",
                      fontSize: 13,
                    }}
                  >
                    Export {size}"
                  </button>
                )
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}
tsx
// src/app/page.tsx — 极简脚手架模式
"use client";

import { toPng } from "html-to-image";
import { useRef, useState } from "react";

// --- 设计令牌 / 主题预设 ---
const THEMES = {
  "clean-light": {
    bg: "#F6F1EA",
    fg: "#171717",
    accent: "#5B7CFA",
    font: "Inter",
  },
  "dark-bold": {
    bg: "#0B1020",
    fg: "#F8FAFC",
    accent: "#8B5CF6",
    font: "Inter",
  },
  "warm-editorial": {
    bg: "#F7E8DA",
    fg: "#2B1D17",
    accent: "#D97706",
    font: "Playfair Display",
  },
} as const;

type ThemeKey = keyof typeof THEMES;

// --- 导出尺寸(宽 x 高,单位px) ---
const EXPORT_SIZES = {
  "6.9": { w: 1320, h: 2868 },
  "6.5": { w: 1284, h: 2778 },
  "6.3": { w: 1206, h: 2622 },
  "6.1": { w: 1125, h: 2436 },
} as const;

// --- 幻灯片数据 ---
const SLIDES = [
  {
    id: 1,
    headline: "Track Every Habit,\nEvery Day",
    subheadline: "Build streaks that stick",
    screenshot: "/screenshots/en/home.png",
    layout: "phone-right", // 每张幻灯片可不同:phone-left | phone-right | phone-center
  },
  {
    id: 2,
    headline: "See Your Progress\nAt a Glance",
    subheadline: "Beautiful weekly summaries",
    screenshot: "/screenshots/en/stats.png",
    layout: "phone-left",
  },
  // ... 更多幻灯片
];

// --- 截图画布(基于6.9"设计 — 1320x2868) ---
function ScreenshotSlide({
  slide,
  theme,
  slideRef,
}: {
  slide: (typeof SLIDES)[0];
  theme: (typeof THEMES)[ThemeKey];
  slideRef: React.RefObject<HTMLDivElement>;
}) {
  return (
    <div
      ref={slideRef}
      style={{
        width: 1320,
        height: 2868,
        background: theme.bg,
        color: theme.fg,
        fontFamily: theme.font,
        position: "relative",
        overflow: "hidden",
      }}
    >
      {/* 标题 */}
      <div
        style={{
          position: "absolute",
          top: 180,
          left: 80,
          right: 80,
          fontSize: 96,
          fontWeight: 800,
          lineHeight: 1.1,
          whiteSpace: "pre-line",
        }}
      >
        {slide.headline}
      </div>

      {/* 副标题 */}
      <div
        style={{
          position: "absolute",
          top: 520,
          left: 80,
          fontSize: 48,
          color: theme.accent,
          fontWeight: 500,
        }}
      >
        {slide.subheadline}
      </div>

      {/* iPhone样机 + 应用截图 */}
      <div
        style={{
          position: "absolute",
          bottom: 0,
          right: slide.layout === "phone-right" ? 0 : "auto",
          left: slide.layout === "phone-left" ? 0 : "auto",
        }}
      >
        {/* 应用截图置于样机内部 */}
        <div style={{ position: "relative", width: 660, height: 1430 }}>
          <img
            src={slide.screenshot}
            style={{
              position: "absolute",
              top: 28,
              left: 24,
              width: 612,
              borderRadius: 52,
            }}
            alt=""
          />
          {/* 样机框架置于顶层 */}
          <img
            src="/mockup.png"
            style={{ position: "absolute", inset: 0, width: "100%" }}
            alt=""
          />
        </div>
      </div>
    </div>
  );
}

// --- 导出辅助函数 ---
async function exportSlide(
  ref: React.RefObject<HTMLDivElement>,
  slideId: number,
  size: keyof typeof EXPORT_SIZES,
  locale: string,
  themeName: string
) {
  if (!ref.current) return;

  const { w, h } = EXPORT_SIZES[size];
  const scale = w / 1320; // 基于1320px宽度设计

  const dataUrl = await toPng(ref.current, {
    width: w,
    height: h,
    style: { transform: `scale(${scale})`, transformOrigin: "top left" },
  });

  const link = document.createElement("a");
  link.download = `slide-${slideId}-${locale}-${themeName}-${size}in.png`;
  link.href = dataUrl;
  link.click();
}

// --- 主页面 ---
export default function ScreenshotGenerator() {
  const [activeTheme, setActiveTheme] = useState<ThemeKey>("clean-light");
  const [activeLocale, setActiveLocale] = useState("en");
  const slideRefs = useRef<React.RefObject<HTMLDivElement>[]>(
    SLIDES.map(() => ({ current: null } as React.RefObject<HTMLDivElement>))
  );

  const theme = THEMES[activeTheme];

  return (
    <div style={{ padding: 40, background: "#111", minHeight: "100vh" }}>
      {/* 控制面板 */}
      <div style={{ display: "flex", gap: 16, marginBottom: 40 }}>
        {(Object.keys(THEMES) as ThemeKey[]).map((t) => (
          <button
            key={t}
            onClick={() => setActiveTheme(t)}
            style={{
              padding: "8px 16px",
              background: activeTheme === t ? "#fff" : "#333",
              color: activeTheme === t ? "#000" : "#fff",
              border: "none",
              borderRadius: 8,
              cursor: "pointer",
            }}
          >
            {t}
          </button>
        ))}

        {/* 批量导出所有尺寸的所有幻灯片 */}
        <button
          onClick={async () => {
            for (const [i, slide] of SLIDES.entries()) {
              for (const size of Object.keys(EXPORT_SIZES) as (keyof typeof EXPORT_SIZES)[]) {
                await exportSlide(
                  slideRefs.current[i],
                  slide.id,
                  size,
                  activeLocale,
                  activeTheme
                );
              }
            }
          }}
          style={{
            padding: "8px 16px",
            background: theme.accent,
            color: "#fff",
            border: "none",
            borderRadius: 8,
            cursor: "pointer",
            marginLeft: "auto",
          }}
        >
          导出全部
        </button>
      </div>

      {/* 幻灯片区域 */}
      <div style={{ display: "flex", flexDirection: "column", gap: 40 }}>
        {SLIDES.map((slide, i) => (
          <div key={slide.id}>
            <ScreenshotSlide
              slide={slide}
              theme={theme}
              slideRef={slideRefs.current[i]}
            />
            {/* 单张幻灯片导出按钮 */}
            <div style={{ display: "flex", gap: 8, marginTop: 12 }}>
              {(Object.keys(EXPORT_SIZES) as (keyof typeof EXPORT_SIZES)[]).map(
                (size) => (
                  <button
                    key={size}
                    onClick={() =>
                      exportSlide(
                        slideRefs.current[i],
                        slide.id,
                        size,
                        activeLocale,
                        activeTheme
                      )
                    }
                    style={{
                      padding: "6px 12px",
                      background: "#222",
                      color: "#fff",
                      border: "1px solid #444",
                      borderRadius: 6,
                      cursor: "pointer",
                      fontSize: 13,
                    }}
                  >
                    导出 {size}"
                  </button>
                )
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Multi-Locale Setup

多语言区域配置

Organize screenshots under locale folders and swap the base path:
public/screenshots/
├── en/home.png
├── de/home.png
└── ar/home.png   ← RTL locale
tsx
// Locale-aware copy dictionary
const COPY: Record<string, Record<number, { headline: string; subheadline: string }>> = {
  en: {
    1: { headline: "Track Every Habit,\nEvery Day", subheadline: "Build streaks that stick" },
    2: { headline: "See Your Progress\nAt a Glance", subheadline: "Beautiful weekly summaries" },
  },
  de: {
    1: { headline: "Jede Gewohnheit\nIm Blick", subheadline: "Baue Serien auf, die halten" },
    2: { headline: "Fortschritt auf\neinen Blick", subheadline: "Wöchentliche Übersichten" },
  },
  ar: {
    1: { headline: "تتبع كل عادة\nكل يوم", subheadline: "ابنِ سلاسل تدوم" },
    2: { headline: "اطّلع على تقدمك\nدفعة واحدة", subheadline: "ملخصات أسبوعية جميلة" },
  },
};

// RTL-aware canvas wrapper
function LocaleCanvas({ locale, children }: { locale: string; children: React.ReactNode }) {
  const isRTL = ["ar", "he", "fa"].includes(locale);
  return (
    <div dir={isRTL ? "rtl" : "ltr"} style={{ textAlign: isRTL ? "right" : "left" }}>
      {children}
    </div>
  );
}
将截图按语言区域文件夹组织,并切换基础路径:
public/screenshots/
├── en/home.png
├── de/home.png
└── ar/home.png   ← RTL语言区域
tsx
// 支持多语言的文案字典
const COPY: Record<string, Record<number, { headline: string; subheadline: string }>> = {
  en: {
    1: { headline: "Track Every Habit,\nEvery Day", subheadline: "Build streaks that stick" },
    2: { headline: "See Your Progress\nAt a Glance", subheadline: "Beautiful weekly summaries" },
  },
  de: {
    1: { headline: "Jede Gewohnheit\nIm Blick", subheadline: "Baue Serien auf, die halten" },
    2: { headline: "Fortschritt auf\neinen Blick", subheadline: "Wöchentliche Übersichten" },
  },
  ar: {
    1: { headline: "تتبع كل عادة\nكل يوم", subheadline: "ابنِ سلاسل تدوم" },
    2: { headline: "اطّلع على تقدمك\nدفعة واحدة", subheadline: "ملخصات أسبوعية جميلة" },
  },
};

// 支持RTL的画布包装器
function LocaleCanvas({ locale, children }: { locale: string; children: React.ReactNode }) {
  const isRTL = ["ar", "he", "fa"].includes(locale);
  return (
    <div dir={isRTL ? "rtl" : "ltr"} style={{ textAlign: isRTL ? "right" : "left" }}>
      {children}
    </div>
  );
}

Dependencies (package.json)

依赖(package.json)

json
{
  "dependencies": {
    "next": "^14.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "html-to-image": "^1.11.11"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "@types/react": "^18.0.0",
    "@types/node": "^20.0.0",
    "tailwindcss": "^3.4.0",
    "autoprefixer": "^10.4.0",
    "postcss": "^8.4.0"
  }
}
json
{
  "dependencies": {
    "next": "^14.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "html-to-image": "^1.11.11"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "@types/react": "^18.0.0",
    "@types/node": "^20.0.0",
    "tailwindcss": "^3.4.0",
    "autoprefixer": "^10.4.0",
    "postcss": "^8.4.0"
  }
}

Starting the Dev Server

启动开发服务器

The skill auto-detects your package manager (bun preferred):
bash
undefined
该Skill会自动检测你的包管理器(推荐使用bun):
bash
undefined

bun (preferred)

bun(推荐)

bun install && bun dev
bun install && bun dev

pnpm

pnpm

pnpm install && pnpm dev
pnpm install && pnpm dev

yarn

yarn

yarn && yarn dev
yarn && yarn dev

npm

npm

npm install && npm run dev

Then open [http://localhost:3000](http://localhost:3000) to view the screenshot generator.
npm install && npm run dev

然后打开 [http://localhost:3000](http://localhost:3000) 即可查看截图生成器。

Export Sizes Reference

导出尺寸参考

DisplayWidthHeightNotes
6.9"1320px2868pxDesign base size
6.5"1284px2778pxScale: 0.973
6.3"1206px2622pxScale: 0.914
6.1"1125px2436pxScale: 0.852
Note: Use a 6.1" simulator to capture your initial app screenshots — this avoids resolution adjustments later.
显示屏宽度高度说明
6.9"1320px2868px设计基准尺寸
6.5"1284px2778px缩放比例: 0.973
6.3"1206px2622px缩放比例: 0.914
6.1"1125px2436px缩放比例: 0.852
注意: 使用6.1"模拟器捕获初始应用截图 — 这样后续无需调整分辨率。

Design Principles the Agent Follows

Agent遵循的设计原则

  1. Screenshots are ads, not docs — each slide sells one idea
  2. One-second rule — headline must be readable at App Store thumbnail size
  3. Vary layouts — no two adjacent slides share the same phone placement (
    phone-left
    /
    phone-right
    /
    phone-center
    )
  4. Style is user-driven — no hardcoded colors; everything flows from theme tokens
  5. First slide = strongest benefit — not the most complex feature
  1. 截图是广告,不是文档 — 每张幻灯片传递一个核心卖点
  2. 一秒原则 — 标题在App Store缩略图尺寸下必须清晰可读
  3. 布局多样化 — 相邻幻灯片的手机放置位置不同(
    phone-left
    /
    phone-right
    /
    phone-center
  4. 风格由用户驱动 — 无硬编码颜色;所有样式均来自主题令牌
  5. 第一张幻灯片=最强卖点 — 而非最复杂的功能

Quality Checklist

质量检查清单

Before exporting, verify each slide:
  • Headline communicates exactly one idea in ~1 second
  • First slide leads with the strongest user benefit (not a feature)
  • Adjacent slides use different phone placement layouts
  • Decorative elements support the message, not obscure the UI
  • Text and framing look correct after scaling to smallest export size (6.1")
  • RTL locales have
    dir="rtl"
    set and layouts feel native (not mechanically mirrored)
导出前,请验证每张幻灯片:
  • 标题在约1秒内清晰传达一个核心观点
  • 第一张幻灯片以用户最强受益点开头(而非功能)
  • 相邻幻灯片使用不同的手机放置布局
  • 装饰元素为内容增色,而非遮挡UI
  • 缩放到最小导出尺寸(6.1")后,文字和框架显示正常
  • RTL语言区域已设置
    dir="rtl"
    ,布局符合原生习惯(而非机械镜像)

Example Prompts

示例提示词

text
undefined
text
undefined

Habit tracker

习惯追踪应用

Build App Store screenshots for my habit tracker. The app helps people stay consistent with simple daily routines. I want 6 slides, clean/minimal style, warm neutrals, and a calm premium feel.
为我的习惯追踪应用构建App Store截图。 该应用帮助人们坚持简单的日常习惯。 我需要6张幻灯片,简洁极简风格,温暖中性色调,营造沉稳高端的质感。

Finance app

理财应用

Generate App Store screenshots for my personal finance app. Main strengths: fast expense capture, clear monthly trends, shared budgets. Sharp modern style, high contrast, 7 slides.
为我的个人理财应用生成App Store截图。 核心优势:快速记账、清晰月度趋势、共享预算。 鲜明现代风格,高对比度,7张幻灯片。

Multi-locale + themes

多语言+多主题

Build App Store screenshots for my language learning app. I need English, German, and Arabic screenshot sets. Use two themes: clean-light and dark-bold. Make Arabic slides feel RTL-native, not just translated.
undefined
为我的语言学习应用构建App Store截图。 我需要英文、德文和阿拉伯文截图集。 使用两种主题:clean-light和dark-bold。 让阿拉伯文幻灯片符合RTL原生体验,而非仅翻译文字。
undefined

Troubleshooting

故障排除

html-to-image exports blank or white images
  • Ensure all images are served from
    /public
    (same origin). Cross-origin images block canvas export.
  • Add
    crossOrigin="anonymous"
    to
    <img>
    tags if loading from a CDN.
  • Check the browser console for CORS errors.
Mockup frame not aligning with screenshot
  • The included
    mockup.png
    has pre-measured transparent screen area. Use
    position: absolute
    with the app screenshot behind the frame, not inside it.
  • Use
    top: 28px, left: 24px
    as the starting offset for a 660px-wide mockup.
Export scaling looks blurry
  • Always design at 1320×2868 (6.9" base) and scale down — never design small and scale up.
  • Pass explicit
    width
    and
    height
    to
    toPng()
    with the correct scale transform.
Font not loading in exports
  • Load fonts via
    next/font
    or a
    <style>
    tag inside the canvas div, not just in
    layout.tsx
    .
  • Call
    document.fonts.ready
    before triggering
    toPng()
    .
tsx
// Wait for fonts before export
await document.fonts.ready;
const dataUrl = await toPng(ref.current, { width: w, height: h });
Simulator screenshots wrong resolution
  • Always capture from the 6.1" simulator as the starting point to minimize later adjustments.
html-to-image导出空白或白色图片
  • 确保所有图片均从
    /public
    目录加载(同源)。跨域图片会阻止画布导出。
  • 如果从CDN加载图片,为
    <img>
    标签添加
    crossOrigin="anonymous"
    属性。
  • 检查浏览器控制台是否有CORS错误。
样机框架与截图对齐异常
  • 内置的
    mockup.png
    带有预测量的透明屏幕区域。使用
    position: absolute
    将应用截图置于框架下方,而非内部。
  • 对于660px宽的样机,使用
    top: 28px, left: 24px
    作为起始偏移量。
导出缩放后模糊
  • 始终以1320×2868(6.9"基准)尺寸设计,再缩小导出 — 切勿以小尺寸设计再放大。
  • toPng()
    传入明确的
    width
    height
    参数,并设置正确的缩放变换。
导出中未加载字体
  • 通过
    next/font
    或画布div内的
    <style>
    标签加载字体,而非仅在
    layout.tsx
    中加载。
  • 触发
    toPng()
    前调用
    document.fonts.ready
tsx
// 等待字体加载完成后再导出
await document.fonts.ready;
const dataUrl = await toPng(ref.current, { width: w, height: h });
模拟器截图分辨率错误
  • 始终从6.1"模拟器捕获初始截图,以减少后续调整工作。

Requirements

要求

  • Node.js 18+
  • One of: bun, pnpm, yarn, or npm
  • Claude Code, Cursor, Windsurf, Codex, or any agent that reads skill files
  • Node.js 18+
  • 以下包管理器之一:bun、pnpm、yarn或npm
  • Claude Code、Cursor、Windsurf、Codex或任何支持读取Skill文件的Agent