app-store-screenshots-generator
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApp 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
undefinedbash
undefinedInstall 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
undefinednpx skills add ParthJadhav/app-store-screenshots -a claude-code
undefinedManual install
手动安装
bash
git clone https://github.com/ParthJadhav/app-store-screenshots ~/.claude/skills/app-store-screenshotsbash
git clone https://github.com/ParthJadhav/app-store-screenshots ~/.claude/skills/app-store-screenshotsUsage
使用方法
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.tsThe entire generator lives in a single file. Run the dev server, open the browser, and click any screenshot to export it as a PNG.
page.tsx该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整个生成器都在单个文件中。 启动开发服务器,打开浏览器,点击任意截图即可将其导出为PNG格式。
page.tsxCore 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 localetsx
// 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
undefinedbun (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
导出尺寸参考
| Display | Width | Height | Notes |
|---|---|---|---|
| 6.9" | 1320px | 2868px | Design base size |
| 6.5" | 1284px | 2778px | Scale: 0.973 |
| 6.3" | 1206px | 2622px | Scale: 0.914 |
| 6.1" | 1125px | 2436px | Scale: 0.852 |
Note: Use a 6.1" simulator to capture your initial app screenshots — this avoids resolution adjustments later.
| 显示屏 | 宽度 | 高度 | 说明 |
|---|---|---|---|
| 6.9" | 1320px | 2868px | 设计基准尺寸 |
| 6.5" | 1284px | 2778px | 缩放比例: 0.973 |
| 6.3" | 1206px | 2622px | 缩放比例: 0.914 |
| 6.1" | 1125px | 2436px | 缩放比例: 0.852 |
注意: 使用6.1"模拟器捕获初始应用截图 — 这样后续无需调整分辨率。
Design Principles the Agent Follows
Agent遵循的设计原则
- Screenshots are ads, not docs — each slide sells one idea
- One-second rule — headline must be readable at App Store thumbnail size
- Vary layouts — no two adjacent slides share the same phone placement (/
phone-left/phone-right)phone-center - Style is user-driven — no hardcoded colors; everything flows from theme tokens
- First slide = strongest benefit — not the most complex feature
- 截图是广告,不是文档 — 每张幻灯片传递一个核心卖点
- 一秒原则 — 标题在App Store缩略图尺寸下必须清晰可读
- 布局多样化 — 相邻幻灯片的手机放置位置不同(/
phone-left/phone-right)phone-center - 风格由用户驱动 — 无硬编码颜色;所有样式均来自主题令牌
- 第一张幻灯片=最强卖点 — 而非最复杂的功能
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 set and layouts feel native (not mechanically mirrored)
dir="rtl"
导出前,请验证每张幻灯片:
- 标题在约1秒内清晰传达一个核心观点
- 第一张幻灯片以用户最强受益点开头(而非功能)
- 相邻幻灯片使用不同的手机放置布局
- 装饰元素为内容增色,而非遮挡UI
- 缩放到最小导出尺寸(6.1")后,文字和框架显示正常
- RTL语言区域已设置,布局符合原生习惯(而非机械镜像)
dir="rtl"
Example Prompts
示例提示词
text
undefinedtext
undefinedHabit 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原生体验,而非仅翻译文字。
undefinedTroubleshooting
故障排除
html-to-image exports blank or white images
- Ensure all images are served from (same origin). Cross-origin images block canvas export.
/public - Add to
crossOrigin="anonymous"tags if loading from a CDN.<img> - Check the browser console for CORS errors.
Mockup frame not aligning with screenshot
- The included has pre-measured transparent screen area. Use
mockup.pngwith the app screenshot behind the frame, not inside it.position: absolute - Use as the starting offset for a 660px-wide mockup.
top: 28px, left: 24px
Export scaling looks blurry
- Always design at 1320×2868 (6.9" base) and scale down — never design small and scale up.
- Pass explicit and
widthtoheightwith the correct scale transform.toPng()
Font not loading in exports
- Load fonts via or a
next/fonttag inside the canvas div, not just in<style>.layout.tsx - Call before triggering
document.fonts.ready.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
导出中未加载字体
- 通过或画布div内的
next/font标签加载字体,而非仅在<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