For version 1.1 of my journal app Lanternly, I needed an image for two new places in App Store Connect: the product page header and search results. There was no new design for it, and I didn't want to reshoot the app either. What I did have were the published iPhone screenshots in English and Russian. I built a single 5244 × 2950 file from them that works in both places.
I've already covered Apple's sizes, formats and rules in the article about Creative Assets, so I won't repeat them here. This article is about the build and about a mistake I found in my own cover after I'd already uploaded it.
Why screenshots#
The screenshots have passed review and show exactly what's in the app. If the screens come from them, the cover won't promise anything extra. It also doesn't need to be drawn separately for each language: the screens are already localized, so only the headline and the one line under it need translating.
The composition is simple: headline and subheading at the top center, three phones below them ("Today" in the middle, the chat with Luna and the calendar on the sides), and Luna the mascot above the center phone. Nothing but iPhones appears in the frame, and that's deliberate.
There's a story behind that. The first version of the same script also made frames for iPhone Duo: it took the iPad screenshots and cropped them to the Duo size. That's not allowed. A device slot has to hold a capture from that device, otherwise the screenshot shows something other than what the person will get. I deleted those sets from App Store Connect. The Duo slot stays empty until I can capture frames in a Duo simulator.
How the file is built#
A Python script does it in three steps.
It cuts the phones out of the screenshots. On an App Store artboard the phone sits in a dark frame on a light background. The script looks for the first row with a noticeable number of dark pixels: that's the top of the frame. From that row it finds the left and right edges and cuts the phone out down to the bottom of the artboard. It covers the rounded corners with a mask whose radius is 12.5% of the width, so there's a transparent background behind them instead of a piece of the old artboard.
It builds an HTML page. A background, a soft spot of light, three phones with a shadow and a ±6° rotation, the headline in Lora and a line in Inter, the same fonts as in the app. Luna is a vector SVG from the sticker sources.
It renders the page in headless Chrome. The page is laid out at 2622 × 1475 CSS pixels and captured at a scale factor of 2:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless=new --hide-scrollbars --allow-file-access-from-files \
--virtual-time-budget=3000 --force-device-scale-factor=2 \
--window-size=2622,1475 --screenshot=header-en.png \
file:///path/to/_header-en.htmlThe output is exactly 5244 × 2950. --virtual-time-budget gives the fonts and SVG time to load before the capture, otherwise the image ends up with a fallback font.
One small but important detail: the PNG must have no alpha channel, because Apple doesn't accept transparency. A Chrome screenshot of a page with an opaque background contains no alpha, but after any post-processing I re-save the file as RGB.
The mistake I found after uploading#
Apple only describes cropping in general terms:
Be sure your focal point artwork is within the center of your composition to prevent any unwanted clipping.
There's no exact geometry. If you assume the universal 16:9 asset is cropped around the center, you get this:
| Where | Ratio | What's left of 5244 × 2950 | What's cut off |
|---|---|---|---|
| Header | 21:9 | full width, 2247 px of height | 351 px from top and bottom |
| Search | 3:2 | full height, 4425 px of width | 410 px from left and right |
I overlaid these two frames on my cover and saw that the headline starts at pixel 240 from the top. In the 21:9 header, the top edge runs along pixel 351. If the crop is centered, the headline "A journal that stays yours" gets cut in half.
I had uploaded the first version to App Store Connect before checking. It didn't go to review: version 1.1 hasn't been submitted yet.
Now I run the check before uploading. Here's the script that draws both frames over the cover and saves a downscaled preview:
#!/usr/bin/env python3
"""21:9 and 3:2 crop frames on a 16:9 asset: python3 crop_check.py header.png preview.png"""
import sys
from PIL import Image, ImageDraw
src, preview = sys.argv[1], sys.argv[2]
im = Image.open(src).convert("RGB")
W, H = im.size
d = ImageDraw.Draw(im)
h21 = round(W * 9 / 21) # header: full width
top = (H - h21) // 2
d.rectangle((0, top, W - 1, top + h21), outline=(255, 0, 0), width=14)
w32 = round(H * 3 / 2) # search: full height
left = (W - w32) // 2
d.rectangle((left, 0, left + w32, H - 1), outline=(0, 0, 255), width=14)
im.resize((W // 4, H // 4)).save(preview)
print(f"21:9 y {top}..{top + h21}, 3:2 x {left}..{left + w32}")The red frame is the header, the blue one is search. Everything that needs to be readable has to fall inside the intersection of the two frames.
How I fixed it without a rebuild#
By then I had already deleted the source artboards, so there was nothing to rebuild the cover from. What helped was that the top of the composition is empty background. I shifted the whole image down by 260 pixels and filled the freed strip at the top with the original first 260 rows of the same background:
from PIL import Image
im = Image.open("header-en.png").convert("RGB")
W, H = im.size
D = 260
out = Image.new("RGB", (W, H))
out.paste(im.crop((0, 0, W, D)), (0, 0)) # background strip at the top
out.paste(im.crop((0, 0, W, H - D)), (0, D)) # whole composition shifted down by D
out.save("header-en-v2.png", optimize=True)The headline now starts at pixel 500, inside both frames. At the bottom, 260 pixels of the lower parts of the phones are lost, but the screens already continue past the edge of the frame, so nothing breaks. The seam at row 260 isn't visible: there's a soft gradient with no detail there.
This trick only works if the edge you shift away from has a uniform background. If there were a pattern or a photo at the top, I'd have had to rebuild.
What I don't know#
- How exactly Apple crops the asset. A centered crop is my assumption, backed by the phrase about the "center of your composition". Only the Preview in App Store Connect will show the real picture, and it has to be checked for every locale, because headline length differs between languages.
- What the asset looks like on iPad, Mac and Vision. The Preview shows iPhone and iPad. I haven't seen it on the other devices.
- Whether the cover will perform better than screenshots in search results. Only the statistics after the version ships will show that.
If you're making a similar cover, overlay the 21:9 and 3:2 frames before your first upload. It takes a minute, and it would have saved me an extra round. How I translated the rest of the listing fields into 39 languages is covered in a separate article.



