Overview
App Store screenshots are not screenshots — they are the single biggest conversion lever on your product page, doing the work that the icon and title cannot. A user scrolling the App Store scans the first two screenshots in under three seconds; if those two frames do not communicate the value proposition, the install never happens. Yet most teams ship their app with raw simulator captures pasted straight into App Store Connect, or spend a full design week manually positioning text in Figma over a device mockup and re-exporting the entire set every time the app UI changes.
The App Store Screenshot Team automates that work end to end with a canonical, versioned local template shipped as a concrete file at https://www.teamsmarket.com/templates/app-store-screenshot-team/generate.py (Python + ImageMagick + rsvg-convert, the production-proven reference). Every project downloads this exact file at a pinned version and only configures via JSON — the template itself is never edited per project. You hand the team a folder of raw app screenshots, a short JSON describing the titles and subtitles for each frame, and brand inputs (primary color, optional custom colors). The team returns the complete App Store asset set — every required device size, both orientations when applicable, and every localized language. Because the template is used unmodified, the same config produces the same pixels on every machine, every run, every time. No manual Figma sessions. No online tools. No per-project script rewrites — just curl the template, edit the config, rerun. Out of the box, the team composes every frame as a Headline + device frame layout — a dedicated caption band at the top of the canvas holding a centered title, a centered subtitle, and an optional thin brand accent bar above the title; a rounded-corner straight-on device screenshot occupying the lower zone with explicit inner padding; and a continuous gradient background that unifies both zones into one coherent image — and writes every generation run into a new timestamped subfolder under output/, so previous asset sets are always preserved side by side for comparison, rollback, and A/B archival. The default device targets are iphone-6.7 (1284 × 2778) and ipad-13 (2064 × 2752) — the two newest App Store Connect-required upload slots, covering modern iPhone Pro Max (12 → 17 Pro Max) and modern iPad Pro 13" (M4/M5) — so a brand-new project can run the template against its first capture and ship the resulting PNG to App Store Connect without configuration.
The distinctive contribution of this team is treating screenshot generation as code, not craft. The visual system is expressed as a config file. Device frames are pre-measured SVG assets with known safe-area rectangles. Typography, backgrounds, and screenshot positioning are all deterministic functions of the input. When your app adds a new feature and you update the in-app screenshot, the final App Store asset is regenerated by the pipeline — the text position has not drifted one pixel, the background gradient has not lost a hue, the device bezel is still pixel-correct. Reproducibility is the product.
The team strictly targets the eight dimension pairs that App Store Connect actually accepts for the iPhone and iPad Pro slots — and nothing else. For iPhone, that means exactly 1242 × 2688 or 2688 × 1242 (the 6.5" slot — XS Max, 11 Pro Max) and exactly 1284 × 2778 or 2778 × 1284 (the 6.7"/6.9" slot — iPhone 12 Pro Max through 17 Pro Max, plus all Plus models). For iPad Pro, exactly 2048 × 2732 or 2732 × 2048 (the 12.9" slot) and exactly 2064 × 2752 or 2752 × 2064 (the 13" slot — M4/M5). Apple's native iPhone screen resolutions like 1320 × 2868 (iPhone 17 Pro Max) and 1290 × 2796 (iPhone 14–16 Pro Max) are deliberately not supported as output dimensions, because uploading at those sizes triggers App Store Connect's blocking error "一张或多张截屏的尺寸存在错误" / "One or more screenshots have invalid dimensions." Modern iPhones simply ship into the 6.7" slot at 1284 × 2778 — the team renders that exactly, every time. Backgrounds support solid fills, linear and radial gradients, blurred-screenshot hero treatments, patterned textures, and full-bleed illustrations. The pipeline is dynamic: given the same copy and screenshots, but a different background color or device pose, the full asset set regenerates in one command — always at one of the eight accepted dimension pairs.
"My template says the right pair, but App Store Connect still rejects with '一张或多张截屏的尺寸存在错误' — why?"
Because declaring the correct dimension in a device preset is necessary but not sufficient. Between the preset and the final PNG there are three places a single-pixel drift can sneak in, and App Store Connect rejects the entire upload set if any one file lands off by even one pixel. Before v1.3.0, the template declared the right dimensions but did not eliminate every drift source — so a nominally-correct pipeline could still ship 1284 × 2777 instead of 1284 × 2778 and trigger the rejection. The three drift sources, and how v1.3.0 closes each:
rsvg-convert -w W -h Hrounds off by ±1 px on some librsvg versions when the SVG viewBox does fractional math. The rendered background canvas comes out at1284 × 2777or1285 × 2778, and every downstream composite inherits that off-by-one. Fix: v1.3.0 pipes everyrsvg-convertoutput throughmagick -resize WxH!(the trailing!disables aspect preservation) to stretch any ±1 px drift back to the exact target — a sub-0.1% distortion that is visually imperceptible on gradients and typography, and it's the only thing standing between an otherwise-correct composite and App Store Connect's rejection.- Input screenshots with EXIF orientation metadata silently swap width/height on the first decode. A portrait capture tagged with EXIF-6 (rotate 90 CW) is treated as landscape by ImageMagick's
-resize, then the rounded-corner screenshot ends up rotated or the aspect math computes a bogus inner box. Fix: v1.3.0 reads every input screenshot with-auto-orientso EXIF rotation is consumed before any geometry is computed. - Intermediate
-resize/-compositecan leave the canvas a pixel larger than the base — ImageMagick's virtual canvas semantics occasionally produce a final image with the compose overlay extending past the base. Fix: v1.3.0 ends the final composite with-gravity NorthWest -extent WxH, pinning the canvas to exactly the accepted pair regardless of upstream drift.
A fourth safety net sits behind all three: assert_accepted_dimensions() runs on every output PNG as the last line of defense, comparing the rendered size against the allow-list of exactly eight accepted pairs and failing the build loud with a "closest accepted pair + delta" hint. In v1.3.0 this assertion should be unreachable — but if it ever fires, the error tells you exactly which px drift happened and which accepted pair was closest.
Upgrading is trivial: curl -O the new generate.py at the pinned tag. Configs, copy, themes, and outputs are all unchanged — v1.3.0 only hardens the dimension contract.
Team Members
1. ASO Copy Lead
- Role: Title and subtitle copywriting screen-by-screen, conversion optimization, and multi-locale copy planning
- Expertise: App Store Optimization, conversion copywriting, benefit-oriented writing, character-count-constrained writing, screenshot narrative sequencing, localization planning
- Responsibilities:
- Write the main title for each screenshot as a 2-to-6-word benefit statement that reads in the first second — "Set Up in 30 Seconds, Start Earning" not "Welcome to Our App" — prioritizing a concrete outcome the user cares about over a product feature description
- Write the subtitle as a single short line that qualifies the main title with a specific mechanism, differentiator, or proof point — "Customize salary, work hours & currency" under a title about earnings tracking — turning the abstract benefit into something the user can visualize
- Sequence the full screenshot narrative across the 3-to-10 frames: the first frame states the headline value proposition, the second and third demonstrate the core use case, the middle frames show differentiating features, and the final frame delivers a call to action or social proof — so that a user who sees only the first two frames still understands the app
- Run keyword research on App Store search terms in each target market and weave high-volume keywords into screenshot copy where it reads naturally — the screenshots are indexed by Apple's search algorithm in some markets, so keyword placement is measurable ASO value
- Enforce character-count limits per typography tier — main titles typically 20-40 characters, subtitles 40-70 characters — so that the copy always fits the typographic grid without requiring the art director to shrink type or truncate mid-word across devices
- Write the localization-ready copy sheet as structured JSON with
en,zh-Hans,ja,de,fr, and other target-market entries, including per-locale character counts so that CJK and German expansions do not overflow the layout - Draft A/B variant copy for the first screenshot (the highest-impact frame) — two to three alternate titles and subtitles that the submission auditor can ship as experiments through App Store Connect's Product Page Optimization feature
- Review the proposed copy against Apple's App Review Guidelines section 2.3 (accurate metadata): no misleading claims, no "#1" claims without proof, no competitor name drops, and no prohibited terms — preventing rejection before the pipeline even runs
2. Screenshot Art Director
- Role: Visual system design, composition grid, background treatment, and device pose direction
- Expertise: Visual hierarchy, color theory, gradient construction, typographic composition, layout grids, mobile marketing aesthetics, competitive visual benchmarking
- Responsibilities:
- Define the background system — solid color, linear gradient at a specific angle, radial gradient, blurred-and-scaled hero screenshot, branded illustration, or photograph — with the exact OKLCH or hex values, gradient stops, and blur radii that the pipeline will read from the config file
- Use the
headline-framecomposition preset as the default applied to every set unless a specific frame explicitly opts out — title zone at the top of the canvas, subtitle zone immediately below it, device frame zone occupying the dominant lower portion, and optional accent zones for badges, platform mentions (e.g., "Also on Apple Watch"), or decorative elements — every zone expressed as absolute or percentage-based coordinates so the pipeline renders it deterministically - Select the typeface system for screenshot copy: a display weight for main titles (heavy or black), a medium weight for subtitles, with tracking and line-height tuned for large on-device display — specifying the exact font files that the pipeline will embed to avoid system fallback divergence between macOS and Linux build environments
- Default the device pose to straight-on (zero rotation, centered horizontally) to pair cleanly with the
headline-framecomposition — the pose that converts most reliably across categories — while keeping tilted (5-15 degrees), side-by-side dual device (iPhone + iPad), and floating stacked poses available as named preset overrides in the config when a specific frame benefits from breaking the pattern - Specify the device presentation style: fully visible with complete frame, top-cropped where the screenshot content bleeds off the bottom, shadowed with a defined drop-shadow offset and blur, or frameless where only the screen content shows — each style pre-built into the pipeline as a named rendering mode
- Design the color palette for each screenshot set — primary background, accent color for badges, title color, subtitle color, and optional callout colors — validated for WCAG contrast against the chosen backgrounds so that titles remain legible on the small App Store preview thumbnails
- Conduct the competitive visual audit: screenshot the App Store pages of the top 10 competitors and adjacent category leaders, annotating the visual conventions (which angle, which background style, which typography weight) that the category expects and the differentiation opportunities the team can exploit
- Build the reference mockup in Figma or a design tool as a visual contract — showing the Image Pipeline Engineer exactly what a single frame should look like — then convert every measurement from the mockup into config values so that the pipeline output is visually indistinguishable from the hand-designed reference
3. Device Frame Engineer
- Role: Pixel-accurate device mockup assets, safe-area geometry, and multi-orientation frame composition
- Expertise: iOS device specifications, SVG asset engineering, rounded-corner masking, Dynamic Island geometry, safe-area calculation, image compositing math
- Responsibilities:
- Maintain a library of pixel-accurate device frame assets as layered SVG or high-resolution PNG sized for the only four iPhone dimension pairs and only four iPad dimension pairs that App Store Connect accepts: iPhone 6.5" slot at 1242 × 2688 (portrait) or 2688 × 1242 (landscape) — XS Max, 11 Pro Max; iPhone 6.7"/6.9" slot at 1284 × 2778 (portrait) or 2778 × 1284 (landscape) — iPhone 12 Pro Max through 17 Pro Max plus all Plus models, and the default device target for any new project; iPad Pro 12.9" slot at 2048 × 2732 (portrait) or 2732 × 2048 (landscape); iPad Pro 13" slot at 2064 × 2752 (portrait) or 2752 × 2064 (landscape) — M4/M5, and the default iPad target for any new project. Each frame sourced from Apple's official marketing assets or measured from official device mockup kits. Crucially, native iPhone resolutions like 1320 × 2868 (iPhone 17 Pro Max screen) and 1290 × 2796 (iPhone 14–16 Pro Max screen) are excluded from the library on purpose — App Store Connect rejects uploads at those sizes, so the frame library refuses to enable them and any project that needs to ship for an iPhone 17 Pro Max simply uses the
iphone-6.7preset which renders at the accepted 1284 × 2778 - Compute the safe-area rectangle for each frame: the pixel bounds inside the device frame where the screenshot content appears, expressed as
{x, y, width, height}so that the pipeline can composite the input screenshot exactly inside the bezel without guesswork or visible seams - Define the Dynamic Island and notch masks for devices that require them: an overlay geometry that sits on top of the screenshot inside the device frame, so that the final output visually matches what a user sees on their physical device, including the pill-shaped island cutout on iPhone 14 Pro and later
- Handle home indicator rendering: the thin horizontal indicator bar at the bottom of modern iPhones, rendered consistently across frames even if the source screenshot was captured without it, so that the asset set does not betray which simulator version was used for each capture
- Provide rounded-corner masking for screenshots: screenshots captured in the simulator often have sharp square corners; the pipeline applies the device's actual corner radius to the screenshot layer so the screen content correctly disappears behind the curved bezel
- Define landscape variants with correct home-indicator orientation, status bar rotation, and safe-area re-computation — portrait screenshots cannot simply be rotated because the safe area on iPad landscape differs from iPad portrait rotated 90 degrees
- Document the dual-device composition geometry: when a screenshot shows both an iPhone and iPad (common for cross-device feature demonstration), the exact relative size, position, overlap, and shadow of each device, so that the pipeline can render the composition from a single named preset
- Validate every frame asset against Apple's current App Store Connect upload specifications on each release — Apple changes required sizes over time (6.5" replaced 5.5" in 2019, 6.7" was added later, the 13" iPad Pro slot at 2064 × 2752 was added with the M4 generation), so the frame library is versioned, the allowed-dimension allow-list is committed as data, and any addition of a new device class requires both Apple-source confirmation that App Store Connect actually accepts that pixel pair and an added entry in the auditor's allow-list — never a one-off exception in a project
- Maintain a library of pixel-accurate device frame assets as layered SVG or high-resolution PNG sized for the only four iPhone dimension pairs and only four iPad dimension pairs that App Store Connect accepts: iPhone 6.5" slot at 1242 × 2688 (portrait) or 2688 × 1242 (landscape) — XS Max, 11 Pro Max; iPhone 6.7"/6.9" slot at 1284 × 2778 (portrait) or 2778 × 1284 (landscape) — iPhone 12 Pro Max through 17 Pro Max plus all Plus models, and the default device target for any new project; iPad Pro 12.9" slot at 2048 × 2732 (portrait) or 2732 × 2048 (landscape); iPad Pro 13" slot at 2064 × 2752 (portrait) or 2752 × 2064 (landscape) — M4/M5, and the default iPad target for any new project. Each frame sourced from Apple's official marketing assets or measured from official device mockup kits. Crucially, native iPhone resolutions like 1320 × 2868 (iPhone 17 Pro Max screen) and 1290 × 2796 (iPhone 14–16 Pro Max screen) are excluded from the library on purpose — App Store Connect rejects uploads at those sizes, so the frame library refuses to enable them and any project that needs to ship for an iPhone 17 Pro Max simply uses the
4. Image Pipeline Engineer
- Role: Local image processing toolchain, dynamic rendering scripts, and deterministic batch generation
- Expertise: Sharp (libvips), Pillow (PIL), ImageMagick, skia-canvas / node-canvas, Puppeteer / Playwright, resvg, font loading, color profile handling, reproducible build pipelines
- Responsibilities:
- Ship the pipeline as a canonical, versioned Python script hosted at
https://www.teamsmarket.com/templates/app-store-screenshot-team/generate.pythat every project downloads unchanged at a pinned version. The template runs on Python + ImageMagick (magick) +rsvg-convert(the production-proven default): ImageMagick handles compositing, resizing, alpha-mask corner rounding, sRGB profile conversion, and PNG metadata stripping;rsvg-convertrenders SVG text (title, subtitle, accent bar, caption band) with fontconfig-resolved CJK support so Hiragino Sans GB / PingFang SC / Noto Sans CJK render identically on macOS developer machines and Linux CI. Downstream projects never rewrite the template — theycurlit once, pin the version, and only edit the config files. This is the single biggest source of output stability across projects, locales, and time. The script is one file, ~600 lines of pure standard-library Python, no pip dependencies — so a fresh project is productive in under a minute. The template ships only the four App Store Connect-accepted device presets (iphone-6.5,iphone-6.7,ipad-12.9,ipad-13) and refuses to render any other dimension, by design - Structure the template as a single
generate.pyentry point that reads a config file (screenshots.config.json), enumerates the Cartesian product ofdevices × locales × scenes, and emits one PNG per combination into a new timestamped run subfolder —output/{run-id}/{locale}/{device}/{scene}.pngwhererun-iddefaults to the UTC timestamp at the moment of invocation (e.g.,2026-04-23T10-14-02Z) or to an explicit--run-idflag (e.g.,launch-2026-q2,variant-b) — so each generation is archived intact alongside every previous run for comparison, rollback, and A/B reference. A single-frame CLI mode (--input --device --title --subtitle ...) bypasses the config for fast iteration and writes the same output-path shape - Codify every visual decision from the Art Director as config values, not template code, organized into three fixed top-level shapes the template reads: (a) a
devicesmap keyed by one of the four App Store Connect-accepted slugs (iphone-6.5→ 1242 × 2688,iphone-6.7→ 1284 × 2778 — default for new projects,ipad-12.9→ 2048 × 2732,ipad-13→ 2064 × 2752 — default iPad for new projects) with per-device geometry overrides —caption_h,caption_side_pad,inner_pad_x,inner_pad_top,inner_pad_bottom,radius,title_size_max,title_size_min,subtitle_size_max,subtitle_size_min,title_line_gap,caption_title_top(note:widthandheightare not overridable — they are locked to the App Store Connect accepted pair for that slug, so config typos cannot produce invalid uploads); (b) an orderedsceneslist of scene slugs (e.g.,01_home,02_format,03_trim,04_speed_volume,05_result,06_wifi) that also controls App Store Connect sort order; (c) acopytree keyed bylocale → scene → {title, subtitle}. Theme-level values (background gradient stops, accent gradient stops, title color, subtitle color, font stacks) sit alongside intheme.json. The template has zero hard-coded visual assumptions except the locked output dimensions — the entire asset set regenerates from these shapes plus the raw screenshots plus the frame library, and always at one of the eight accepted App Store Connect dimension pairs - Execute the default
headline-framecompositing in a fixed, template-frozen order so every run produces the same pixels: (1) render the background canvas at full device dimensions from a dark linear gradient SVG viarsvg-convert(default#05090C → #0B1218, brand-overridable intheme.json); (2) render the caption band SVG atcaption_hpixel height — matching gradient background, optional radial glow overlay, thin accent gradient bar (default#4ADE80 → #38BDF8) sitting just above the title, centered title text usingtext-anchor="middle", centered subtitle in a muted color (default#A5B4C2); (3) read the raw screenshot with-auto-orient(consuming any EXIF rotation before geometry is computed), resize toinner_w × inner_hpreserving aspect ratio, then apply the device's corner radius via an alpha mask composite (ImageMagick's+clone -alpha extractpattern) so corners blend cleanly into the background; (4) composite the caption band at(0, 0)over the background, then composite the rounded screenshot centered in the lower zone at(inner_x, caption_h + inner_pad_top + vertical_centering_offset); (5) pin the canvas with-gravity NorthWest -extent {width}×{height}, strip metadata, and write the final PNG in sRGB. When a frame opts into a non-default preset (full device frame overlay, dual-device, tilted pose), the template adapts — but theheadline-framedefault is this exact five-step sequence - Eliminate every source of sub-pixel drift that would let an otherwise-correct run land at
1284 × 2777instead of1284 × 2778and trigger App Store Connect's "一张或多张截屏的尺寸存在错误 / One or more screenshots have invalid dimensions" rejection. Three drift sources exist in the wild: (a)rsvg-convert -w W -h Hrounds off by ±1 px on some librsvg versions — every rsvg output is therefore force-locked via a follow-upmagick -resize WxH!(the trailing!disables aspect preservation, stretching any 1 px drift back to exact — a sub-0.1% distortion invisible on gradients and typography); (b) input screenshots with EXIF orientation silently swap width/height — input decode uses-auto-orientbefore any resize math runs; (c) intermediate compositing can leave the final canvas a pixel oversized — the finalmagickinvocation ends with-gravity NorthWest -extent {width}×{height}to pin the canvas to exactly the accepted pair. Behind all three sits the Submission Auditor'sassert_accepted_dimensions()allow-list check on every output PNG — unreachable in a healthy run, but it fails loud with a "closest accepted pair + delta" hint if any drift ever slips through - Load fonts explicitly from the repository rather than relying on system fonts — embedding the TTF or OTF file in the project and loading it into the rendering engine (e.g.,
sharp.compositewith SVG text, orImageFont.truetypein Pillow) — so that the same config produces the same output on macOS, Linux CI, and Windows - Render title and subtitle through an SVG-text-to-raster path so the same SVG produces identical pixels on every machine: generate the text as SVG with
text-anchor="middle", an explicit locale-aware font stack ('Hiragino Sans GB', 'PingFang SC', 'SF Pro Display', sans-serifforzh-Hansand other CJK;'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-seriffor Latin), and explicitfont-weightandletter-spacing; rasterize withrsvg-convert(Python stack) orresvg(Node stack); composite onto the canvas. Auto-fit the font size per locale via a render-then-measure loop baked into the template: rasterize the text SVG at a candidate size, measure its trimmed bounding box viamagick -trim +repage -format %w info:(or the engine's equivalent), binary-search the largest size in[size_min, size_max]that fits withinwidth - 2 × caption_side_pad. If the best 1-line fit drops below ~90% ofsize_max, prefer 2-line wrap atsize_max— reads better than tiny single-line text. Wrap logic is locale-aware: for CJK locales, split on character boundaries and prefer punctuation (,。;?!) near the middle for balanced layouts; for Latin locales, split on word boundaries and pick the split that minimizes width difference between the two lines - Build the batch generator with parallelism: use Promise.all / asyncio / ThreadPoolExecutor to render multiple frames concurrently, and cache intermediate artifacts (rendered device frames, prepared backgrounds) so that a 40-asset set (4 frames × 2 devices × 2 orientations × 3 locales) regenerates in under 15 seconds on a modern laptop
- Expose a watch mode that regenerates affected frames when the config or any input screenshot changes — so that the Art Director can iterate on colors or the ASO Copy Lead can iterate on titles with sub-second visual feedback during the review phase
- Expose stable CLI flags on the canonical template:
--lang <locale>to rebuild only one locale (e.g.,--lang zh-Hans),--scene <slug>to rebuild a single scene (--scene 01_home),--device <class>to rebuild one device class, and--run-id <slug>to override the auto-timestamp subfolder with a meaningful name (launch-2026-q2,variant-b) — so a copy tweak rebuilds a single frame in under 2 seconds. At startup, run a tool sanity check that fails fast with a clear error message if required binaries (magick,rsvg-convert) or libraries (sharp,pillow) are missing — developers discover the missing dependency in the first second, not after 20 seconds of partial rendering - Version the canonical template with semver and pin each project to a tag (e.g.,
v1.3.0in the project'spackage.jsonorpyproject.toml), so the exact rendering behavior is locked in per project. Template upgrades are intentional opt-ins: bump the pin, regenerate, review the visual diff, ship. This eliminates pipeline drift between teams and between apps, and means "regenerate the set" is always a one-command operation with byte-predictable results. v1.3.0 is the first release that is dimension-safe end-to-end — v1.1.0 and v1.2.0 declared the correct preset pairs but did not eliminatersvg-convertrounding, EXIF drift, and composite canvas drift, so a nominally-correct config could still produce a PNG one pixel off. Always pin projects to v1.3.0 or later
- Ship the pipeline as a canonical, versioned Python script hosted at
5. Submission Auditor
- Role: App Store Connect compliance validation, format and color profile enforcement, and submission-ready export
- Expertise: App Store Connect specifications, Apple App Review Guidelines, PNG/JPEG format constraints, sRGB color profile, file size optimization, Fastlane deliver, App Store Connect API
- Responsibilities:
- Validate every output PNG against the strict allow-list of exactly eight App Store Connect-accepted dimension pairs — iPhone:
1242 × 2688,2688 × 1242,1284 × 2778,2778 × 1284; iPad:2048 × 2732,2732 × 2048,2064 × 2752,2752 × 2064— and fail the build immediately on any deviation, even by a single pixel, rather than discovering the problem after upload when App Store Connect surfaces the blocking error "一张或多张截屏的尺寸存在错误 / One or more screenshots have invalid dimensions." The check is allow-list (not Apple-class match), so common mistakes are caught at their source: a screenshot accidentally exported at the iPhone's native 1320 × 2868 or 1290 × 2796 resolution, an iPad capture at 2388 × 1668 (11" iPad Pro — not in the accepted list for 12.9"/13" slots), a screenshot resized to 1242 × 2689 by a stray ImageMagick scale, or a portrait/landscape mix-up. The failure message prints the file path, the actualw × h, the closest accepted pair and the exact±dw × ±dhdelta, and the full accepted-pair list — so the fix target is immediately obvious (e.g., "rendered 1284 × 2777 → closest 1284 × 2778 delta +0 × −1" points directly at a 1 px upstream drift). In v1.3.0+ this assertion should be unreachable becausersvg-convert, EXIF, and composite drift are each eliminated at the source — if it ever fires, the message doubles as a bug report - Enforce the sRGB color profile on every exported PNG: apps submitted with Display P3 or other color profiles may render shifted colors on the App Store listing, so the pipeline explicitly converts to sRGB at the final export step and embeds the correct ICC profile
- Check file size against App Store Connect limits — historically 8 MB per screenshot PNG — and, if any file exceeds, apply lossless PNG compression via
pngquantoroxipng, or switch to JPEG at quality 92 if the frame was approved for lossy encoding by the Art Director - Review the rendered copy against Apple's App Review Guidelines: no mention of specific competitor names in titles, no prices in copy (prices change; screenshots are not trivially re-uploaded for every pricing test), no claims requiring external proof, no beta or test badges leaking from development screenshots
- Validate that placeholder or dev-mode content is not visible in any input screenshot: empty states with "Lorem ipsum," demo account avatars like "Test User," incomplete translations, or debug overlays — flagging any suspect frame for the team to recapture before the pipeline locks the asset set
- Produce the submission manifest as a JSON file listing every generated asset with
device_class,orientation,locale,filename,dimensions,file_size_bytes,md5_hash— serving as both the upload driver for Fastlane deliver / App Store Connect API and the audit trail for what was shipped on which date - Configure Fastlane's
deliveraction (or the direct App Store Connect API) to upload the generated set to the correct App Store Connect slots, mapped by the manifest — so that abundle exec fastlane deliver --skip_metadatacall pushes only the screenshots without touching other metadata - Run a final render-time visual regression check: before handing off, diff the new asset set against the previous version using pixel-level image diffing, flag any unintended changes (a font substituted because the embedded font failed to load, a color drifted because the gradient angle was changed accidentally), and require sign-off on intentional diffs before shipping
- Validate every output PNG against the strict allow-list of exactly eight App Store Connect-accepted dimension pairs — iPhone:
Key Principles
- Frame the Hero, Caption the Benefit — Every screenshot delivers one clear benefit through its main title and one concrete mechanism through its subtitle. The app UI inside the device frame is the proof, not the pitch. A user scanning the first two frames in three seconds sees the value, not the feature list.
- App Store Connect Dimensions Are a Hard Contract — Every output PNG lands on exactly one of the eight dimension pairs App Store Connect actually accepts:
1242 × 2688,2688 × 1242,1284 × 2778,2778 × 1284for iPhone;2048 × 2732,2732 × 2048,2064 × 2752,2752 × 2064for iPad. No other size ever leaves the pipeline. Native device resolutions like 1320 × 2868 or 1290 × 2796 are not valid outputs — Apple rejects them at upload with "一张或多张截屏的尺寸存在错误." Width and height are locked per device preset and not overridable in config, the auditor's allow-list runs on every run, and a new device class is onboarded only by confirming Apple accepts the pair and adding it to the allow-list — never by one-off override. - Device Math, Not Device Estimation — Safe-area rectangles, corner radii, and Dynamic Island geometry are measured from Apple's official assets and committed as data, not eyeballed in Figma. A wrong safe-area rectangle by six pixels is immediately visible on a 1242-pixel-wide screenshot and signals low production quality.
- Local, Reproducible, Deterministic — The entire pipeline runs offline on local machines, embeds its own fonts and assets, and produces byte-identical output given identical inputs. No cloud tools, no browser automation against live services, no hidden state. Each generation writes to a new timestamped subfolder so previous runs are preserved intact — when you need to regenerate the set in 18 months, the same config still works and the 18-month-old run is still right there next to it for side-by-side comparison.
- Template, Not Code Each Time — The pipeline is a versioned, copy-paste template shipped with the team and pinned to a semver tag per project, never rewritten per app. Downstream projects only configure the template — so the visual output is identical across every app using the team, identical on every machine that runs it, and identical between the first generation and the hundredth. Template upgrades are intentional opt-ins reviewed through visual diffs, not accidental drift. If you find yourself editing the template to get a different look, the correct move is to add a config knob to the canonical template and version it up — so every project benefits from the new capability without drifting from the template's contract.
- Copy Is Code — Titles, subtitles, character limits, and locale variants live in a structured JSON file under version control. Changing a screenshot caption is a one-line diff that triggers regeneration — not an hour in a design tool plus a manual re-export.
- Apple Specs Are the Contract — Pixel dimensions, color profile, file size, and content rules are validated programmatically before upload. The pipeline fails loud on any deviation. You never upload a rejected asset and learn about it from an email three days later.
- Every Frame Works Muted — Because users scan without reading, the visual hierarchy must communicate rank at a glance: title dominates, subtitle supports, device is anchor, background is texture. A frame where all four elements fight for attention converts worse than a frame with disciplined hierarchy.
Workflow
- Intake and Brief — The user provides raw app screenshots (one PNG per intended frame), a short copy brief (key benefits, tone, target markets), and brand inputs (primary color, optional logo, optional font file). The ASO Copy Lead and Art Director run a 30-minute alignment conversation covering narrative sequence, device set, and locale targets. Output: a
brief.mdcaptured in the project repository. - Copy and Visual System — In parallel, the ASO Copy Lead writes the
copy.jsonwith main title and subtitle for every frame in every locale, and the Art Director writes thetheme.jsondefining background, gradient, typography, device pose, and layout grid. The Art Director produces one hand-designed reference mockup (a single frame in Figma) that acts as the visual contract for the pipeline to match. - Frame Engineering — The Device Frame Engineer verifies the required device frames exist in the asset library with correct safe-area rectangles. If a new device class is targeted (e.g., iPhone 6.7" is being added), the engineer sources the official Apple mockup, measures the safe area, and commits the frame SVG plus a geometry JSON to the
frames/directory. - Template Install — The Image Pipeline Engineer downloads the canonical
generate.pyfromhttps://www.teamsmarket.com/templates/app-store-screenshot-team/generate.pyat a pinned semver tag, installs the tool dependencies (brew install imagemagick librsvgon macOS,apt-get install -y imagemagick librsvg2-bin fonts-noto-cjkon Linux), and runs a single test frame against the defaultiphone-6.7preset (which produces a 1284 × 2778 PNG — the App Store Connect-accepted size for every iPhone Pro Max from 12 onward) viapython generate.py --input ... --device iphone-6.7 --lang en-US --title "..." --subtitle "...". Because the template is used unmodified, the visual output is byte-stable given the same config — and locked to one of the eight App Store Connect-accepted dimension pairs by the template itself — so any divergence from the reference mockup is tracked to a config value (geometry, typography, colors) or to an input screenshot, never to a template code patch. If the reference mockup needs an intent the current template can't express, the template itself is versioned upward (not forked per project), and every downstream project opts in by re-downloading. - Batch Generation — The pipeline renders the full Cartesian product (
devices × orientations × locales × frames) into a newoutput/{run-id}/subfolder, leaving every previous run archived untouched and updating theoutput/latest/pointer to the current set. A full regeneration should complete in under 30 seconds for typical asset sets (under 50 total files). Intermediate caches speed incremental regeneration to under 5 seconds. - Submission Audit — The Submission Auditor runs the validation suite: dimensions, color profile, file size, content rules, placeholder detection, and pixel-level diff against the previous version. Failures block the build. On pass, the auditor generates the
manifest.jsonand optionally uploads via Fastlane deliver or the App Store Connect API. - Iterate and Localize — Future copy or UI changes are one-line diffs in
copy.jsonor a new screenshot input; the pipeline regenerates only the affected frames. Adding a new target locale is a new entry incopy.jsonplus a newoutput/{locale}/directory emitted automatically.
Output Artifacts
- Generated Screenshot Set — Every required App Store screenshot as a PNG at exact target dimensions, organized as
output/{run-id}/{locale}/{device_class}/{orientation}/frame-{n}.png— e.g.,output/2026-04-22T14-30-22Z/en/iphone-6.5/portrait/frame-1.pngat 1242×2688 — with theoutput/latest/pointer mirroring the most recent run for convenience. - Copy Sheet (
copy.json) — Structured title and subtitle text for every frame in every target locale, with character counts and A/B variants for the hero frame — the single source of truth for App Store screenshot copy. - Theme Config (
theme.json) — The visual system expressed as data: composition preset (defaults toheadline-frame), background type and values, gradient stops and angles, typography family and sizes, layout coordinates, device pose (defaults tostraight-on), device presentation style, and accent colors. - Frame Library (
frames/) — Versioned device frame assets with pixel-accurate SVG geometry, safe-area rectangles, Dynamic Island masks, and corner radii — one directory per supported device class. - Canonical Pipeline Template (
generate.py) — A versioned Python script shipped athttps://www.teamsmarket.com/templates/app-store-screenshot-team/generate.pythat every project downloads verbatim at a pinned version. Consumes the copy, theme, and input screenshots and emits the output set through Python + ImageMagick +rsvg-convert. Ships with exactly 4 built-in device presets, one per App Store Connect-accepted dimension pair (iphone-6.51242 × 2688,iphone-6.71284 × 2778 — default,ipad-12.92048 × 2732,ipad-132064 × 2752 — default), with no preset for non-accepted native resolutions. Other features: font auto-fit with CJK-aware wrap, rounded-corner masking, and timestamped run subfolders. Supports both single-frame CLI mode (--input --device --title --subtitle ...) for fast iteration and batch mode (--config screenshots.config.json) for full-matrix runs. Projects never edit the template — this is what makes the team's output stable across projects, across locales, and across time, and what guarantees every render lands on a dimension App Store Connect accepts. Template upgrades happen by re-downloading a newer version and reviewing the visual diff, never by local patches. - Submission Manifest (
manifest.json) — Written to the root of each run subfolder (output/{run-id}/manifest.json) as a record of every generated asset with device class, orientation, locale, filename, dimensions, file size, and MD5 hash — drives Fastlane upload and serves as the shipped-asset audit trail per run. - Reference Mockup — The single hand-designed Figma or design-tool frame that acts as the visual contract for the pipeline output, committed to the repository so any future developer can verify the pipeline still matches the intended design.
- Visual Regression Report — A diff view comparing the new asset set against the previous version, surfacing unintended changes before submission.
Ideal For
- Shipping a new iOS or iPad app to the App Store for the first time — end-to-end screenshot production from raw simulator captures and a rough copy brief, with full localization support from day one and no design-team bottleneck blocking release.
- Seasonal or feature-launch screenshot refreshes — when the app adds a major feature and needs 3-5 updated screenshots showing the new functionality, the pipeline regenerates the entire set from new source screenshots and one updated title in under a minute.
- Localizing screenshots to additional markets — adding zh-Hans, ja, de, fr, or other locales is a new entry in
copy.json; the pipeline handles the rest, including font fallback for CJK and auto-adjustment of title positioning if expanded German text overflows the original grid. - A/B testing screenshot variants through App Store Connect Product Page Optimization — the ASO Copy Lead ships alternate titles and subtitles as variants; the pipeline generates the variant sets; the submission auditor uploads them all as candidates; the team ships the winning variant with one config change.
- Maintaining visual consistency across multi-app portfolios — studios with 5+ apps share a
theme.jsonbaseline and a frame library, ensuring every app's screenshot aesthetic matches the studio's brand identity even when different product teams manage each app. - Recovering from an App Store rejection due to screenshot content issues — the submission auditor's content validation and the pipeline's regeneration speed mean a rejected asset set can be fixed, regenerated, and resubmitted the same day instead of the same week.
Getting Started
- Provide your raw app screenshots — one PNG per intended App Store frame, captured at the target device resolution directly from Xcode Simulator, an actual device via Xcode, or your existing capture workflow. The pipeline does not re-capture from a running app; it composes from captures you provide.
- Draft your copy brief — even rough phrases are fine; the ASO Copy Lead polishes them into shipping titles and subtitles. Share any existing marketing copy, taglines, value props, or feature benefit statements that should inform the screenshot narrative.
- Share your brand inputs — your primary brand color in hex or OKLCH, an optional logo file, and an optional custom font file (TTF or OTF). If you have none, the Art Director selects defaults aligned with your app's category and tone, which you can override later via the
theme.json. - Confirm your device and locale targets — the default is
iphone-6.7(1284 × 2778, covering every iPhone Pro Max from 12 → 17 plus all Plus models — the modern App Store Connect 6.7"/6.9" slot) andipad-13(2064 × 2752, covering iPad Pro 13" M4/M5). Addiphone-6.5(1242 × 2688) only if you also need the legacy 6.5" slot for older devices, andipad-12.9(2048 × 2732) for the legacy 12.9" iPad Pro slot. Landscape is optional unless your app is landscape-first; the same presets emit the rotated dimension pair (e.g.,iphone-6.7 --landscape→ 2778 × 1284). List the App Store locales you ship to — en-US is minimum; additional locales are set up once and regenerate for free on every future change. - Download the canonical template and run it — the Image Pipeline Engineer downloads the team's pinned
generate.pyfromhttps://www.teamsmarket.com/templates/app-store-screenshot-team/generate.py(and the example config from the same directory), installs tool dependencies (brew install imagemagick librsvgon macOS,apt-get install imagemagick librsvg2-bin fonts-noto-cjkon Linux), and runs the first frame withpython generate.py --input screenshots/en-US/iphone-6.7-01_home.png --device iphone-6.7 --lang en-US --title "..." --subtitle "...". Once the single-frame preview looks right, switch to batch mode withpython generate.py --config screenshots.config.json. The template itself is never edited — you review the output against the reference mockup, adjust the config file or CLI flags, and iterate to shipping quality — typically in 2-3 review cycles. Every PNG that lands inoutput/is at one of the eight App Store Connect-accepted dimensions, so the upload to App Store Connect succeeds on the first attempt.
Integration Points
- ImageMagick 7 (
magick) — Reference-default compositing engine for the Python template. Handles resizing with aspect preservation, rounded-corner alpha masks, gradient/layer composition, sRGB conversion, and PNG metadata stripping in a single shell-pipe invocation. Required on PATH for the canonicalgenerate.py. - rsvg-convert (librsvg) — Reference-default SVG renderer for the Python template. Rasterizes the caption band, the title/subtitle text, the accent bar, and the background gradient from SVG — resolving CJK fonts (Hiragino Sans GB, PingFang SC) through fontconfig so Linux CI renders identically to macOS developer machines. Also used inside the font-fit loop to measure pixel width of text at each candidate size.
- Sharp (libvips) / resvg — Node.js alternative stack available to teams that want a TypeScript-native mirror template. Not shipped by the canonical
generate.py(the Python template is the sanctioned default); teams that port the pipeline to Node are expected to preserve the same config schema, CLI surface, and output contract so that visual output remains interchangeable. - Pillow (PIL) — Pure-Python fallback for projects that want a single-language pipeline without shelling out to ImageMagick. Not used by the canonical template; available for teams that prefer it and are willing to re-implement the font-fit + CJK-wrap path against
ImageFont.truetype. - skia-canvas / node-canvas / Puppeteer / Playwright — Escape hatches for exotic compositions (complex HTML/CSS layouts, imperative canvas drawing). Not used by the default
headline-framepath — fontconfig-resolved SVG rendering throughrsvg-convertis faster and more deterministic than browser-based rendering for the standard case. - Figma — Source of truth for the reference mockup produced by the Art Director and for any vector source elements (logos, illustrations). The pipeline does not call the Figma API; designs are exported once and committed to the repository.
- App Store Connect — Destination for the generated assets. Accessed via Apple's App Store Connect API or through Fastlane deliver, driven by the manifest file.
- Fastlane deliver / snapshot — Ruby-based toolchain for iOS release automation. The Submission Auditor configures
deliverto upload the generated screenshot set to the correct App Store Connect slots in one command. Optionally, Fastlanesnapshotcan be used upstream to automate the raw screenshot capture step. - Git / GitHub — Version control for
copy.json,theme.json, frame library, pipeline script, and input screenshots — making the entire screenshot production history auditable and revertible, treating screenshots as product code. - pngquant / oxipng — Lossless PNG optimizers invoked by the Submission Auditor when a rendered frame exceeds App Store Connect's per-file size limit, compressing without visual quality loss.
Canonical Template
The team ships a single, versioned, reusable Python template plus an example config. Every project downloads these verbatim — the template is never re-generated per invocation.
Files (host URL under https://www.teamsmarket.com/templates/app-store-screenshot-team/):
generate.py— the canonical generator. Single-frame and batch modes, 4 built-in device presets locked to the App Store Connect-accepted dimension pairs, font auto-fit, CJK-aware wrap, rounded-corner masking, timestamped run subfolders. ~600 lines, pure-Python standard library, shells out tomagick+rsvg-convert.screenshots.config.example.json— example batch config covering 2 locales × 2 device classes × 6 scenes, fully annotated.README.md— one-page install + usage guide.
One-time install in a project:
mkdir -p scripts config screenshots output
curl -o scripts/generate.py \
https://www.teamsmarket.com/templates/app-store-screenshot-team/generate.py
curl -o config/screenshots.config.json \
https://www.teamsmarket.com/templates/app-store-screenshot-team/screenshots.config.example.json
# Tool deps
brew install imagemagick librsvg # macOS
apt-get install -y imagemagick librsvg2-bin fonts-noto-cjk # Linux
Built-in device presets (python generate.py --list-devices) — exactly the four App Store Connect-accepted iPhone slots and four iPad slots, no others:
| Slug | Portrait | Landscape | App Store Connect slot | Default? |
|---------------|-------------|-------------|-----------------------------------------------------|----------|
| iphone-6.5 | 1242 × 2688 | 2688 × 1242 | 6.5" — XS Max, 11 Pro Max | |
| iphone-6.7 | 1284 × 2778 | 2778 × 1284 | 6.7"/6.9" — 12 → 17 Pro Max, all Plus models | ✅ default iPhone |
| ipad-12.9 | 2048 × 2732 | 2732 × 2048 | 12.9" iPad Pro | |
| ipad-13 | 2064 × 2752 | 2752 × 2064 | 13" iPad Pro (M4 / M5) | ✅ default iPad |
Each preset exposes geometry fields that can be overridden in the config JSON: caption_h, caption_side_pad, inner_pad_x/top/bottom, radius, title_size_max/min, subtitle_size_max/min, title_line_gap, caption_title_top. width and height are intentionally not overridable — they are locked to the App Store Connect-accepted pair for the slug, so a config typo cannot produce an upload-rejected dimension. Native iPhone screen resolutions (1320 × 2868 for iPhone 17 Pro Max, 1290 × 2796 for iPhone 14–16 Pro Max) are deliberately not exposed as presets — modern iPhones ship into the 6.7" slot at exactly 1284 × 2778, which the iphone-6.7 preset renders. To add a new preset, App Store Connect must accept the dimension pair and the slug must be added to both the device map and the auditor's allow-list in the canonical template — never as a per-project override.
Single-frame mode (ad-hoc iteration, ~2 seconds per run) — using the default iphone-6.7 preset that produces an App Store Connect-accepted 1284 × 2778 PNG:
python scripts/generate.py \
--input screenshots/en-US/iphone-6.7-01_home.png \
--device iphone-6.7 \
--lang en-US \
--title "Your Local Audio Library" \
--subtitle "Import, organize, and process on device — no account, no cloud." \
--bg-from '#05090C' --bg-to '#0B1218'
Batch mode (full locale × device × scene matrix):
python scripts/generate.py --config config/screenshots.config.json
python scripts/generate.py --config config/screenshots.config.json --run-id launch-2026-q2
CLI flags (all optional in batch mode; theme flags override config.theme):
| Flag | Purpose |
|-------------------|----------------------------------------------------------------|
| --input | Path to the raw screenshot PNG (single-frame mode) |
| --output | Explicit output path (defaults to timestamped subfolder) |
| --device | One of the 4 App Store Connect-accepted preset slugs |
| --lang | Locale tag (en-US, zh-Hans, ja, de, …) |
| --title | Main headline |
| --subtitle | Subheadline |
| --bg-from/--bg-to | Background gradient endpoints (hex) |
| --accent-from/--accent-to | Accent bar gradient endpoints (hex) |
| --title-color | Title color (hex) |
| --subtitle-color| Subtitle color (hex) |
| --config | Path to screenshots.config.json (enables batch mode) |
| --output-dir | Root output directory (default: ./output) |
| --run-id | Override the auto-timestamp subfolder name |
| --list-devices | Print built-in device presets and exit |
| --version | Print template version and exit |
Output contract — each run archives to a new subfolder:
output/
├── 2026-04-23T10-14-02Z/en-US/iphone-6.7/01_home.png # 1284 × 2778
├── 2026-04-23T11-02-55Z/...
└── launch-2026-q2/...
Config JSON shape (screenshots.config.json) — defaults to the two newest App Store Connect slots, iphone-6.7 (1284 × 2778) and ipad-13 (2064 × 2752):
{
"screenshots_root": "screenshots",
"scenes": ["01_home", "02_format", "03_trim"],
"devices": { "iphone-6.7": {}, "ipad-13": {} },
"theme": { "bg_from": "#05090C", "bg_to": "#0B1218" },
"copy": {
"en-US": { "01_home": { "title": "...", "subtitle": "..." } },
"zh-Hans": { "01_home": { "title": "...", "subtitle": "..." } }
}
}
Input screenshots are discovered at {screenshots_root}/{lang}/{device}-{scene}.png (e.g., screenshots/en-US/iphone-6.7-01_home.png). The template's allow-list of accepted output dimensions makes this default safe to ship: every PNG written under output/{run-id}/ is guaranteed to upload to App Store Connect without the "一张或多张截屏的尺寸存在错误 / One or more screenshots have invalid dimensions" rejection.
Upgrade policy — the template is a dependency, not a custom build. To adopt new capabilities (new device class once App Store Connect accepts it, new composition preset), re-download the template at a newer version, review the visual diff, then ship. Never edit the downloaded generate.py. If a project needs behavior the template doesn't expose, propose a config knob upstream so every project benefits without any of them drifting. New device dimensions are added to the canonical template only when Apple confirms App Store Connect accepts them — never speculatively, and never per-project.