solo-apple-app-icon · v1.0.0 · 2026-08-20 · sha256 d3a79ecc60a6e572
solo-apple-app-icon v1.0.0A
Immutable. This exact content is served forever at /api/v1/blob/d3a79ecc60a6e572.
---
name: solo-apple-app-icon
description: Build an iOS 26+ layered app icon (.icon / Icon Composer) from a designer's SVG, generated by script rather than drawn by hand, and check every appearance without launching the app. Use when the user says "иконка приложения", "сделай иконку", "app icon", "Icon Composer", "layered icon", "иконка не как в фигме", "тёмная иконка выглядит плохо", or when an app still ships a folder of flattened PNGs. Do NOT use for in-app icons and SF Symbols (that is symbol work), or for the App Store screenshot set.
license: MIT
metadata:
author: fortunto2
version: "1.0.0"
openclaw:
emoji: "🟪"
---
# apple-app-icon — the icon as a build artefact, not a drawing
On iOS 26 an app icon is no longer a flattened PNG: it is a small bundle of
**layers** the system lights, tints and renders as Liquid Glass. Icon Composer
draws them by hand; this skill generates the same document from the brand
artwork, so the icon is *rebuilt* when the brand changes rather than redrawn
from memory.
Reach for it when the icon and the app disagree — different mark, different
gradient, or nine PNGs whose source nobody can find.
## Workflow
### 1. Find the real artwork before drawing anything
Two attempts went the wrong way on one project before the file turned up:
rebuilding the mark from the app's own button geometry (beautiful, and a
different mark), then tracing it off a 1024 PNG by measuring pixels (the right
mark, at second-hand accuracy).
```bash
find ~ -maxdepth 7 \( -iname "*logo*" -o -iname "*icon*" \) \
\( -iname "*.svg" -o -iname "*.ai" -o -iname "*.sketch" \) 2>/dev/null
mdfind -name "logo" # useless if Spotlight is off — check `mdutil -s /`
```
Look in abandoned web projects and old zips; a Figma export usually survives
somewhere even when the designer's archive link is dead. **What you want is one
artboard, already composed**: the mark on its tile, with the background
gradient in a `<linearGradient>`. That file is the source of truth — copy it
into the repo (`Resources/logo-source.svg`) so the icon has a provenance.
### 2. Generate
```bash
python3 "${SKILL_DIR}/scripts/make_icon.py" \
--source Resources/logo-source.svg \
--icon Resources/App.icon --render
```
It reads the stops, the viewBox and the paths out of the artwork, splits the
paths into layers, writes `icon.json`, and exports all six appearances through
`ictool`. Nothing is retyped — a hex copied into a script is a second source of
truth that drifts on the next export.
### 3. Look at all six, not at one
```
Default · Dark · TintedLight · TintedDark · ClearLight · ClearDark
```
The dark and tinted appearances are where a mark falls apart: the system
re-colours the layers, and thin white lines with dark gaps — the shape of most
marks — are exactly what a translucent treatment fills in. Rendering is three
seconds; guessing costs a release.
### 4. Then see it among other icons
```bash
xcrun simctl install <UDID> path/to/App.app
xcrun simctl io <UDID> screenshot home.png # after `axe button home`
```
A tile that looks confident at 512 points can be mush at 60 next to Photos and
Safari. That is the only test that matters.
## Wiring it into the project
```yaml
# project.yml (XcodeGen)
sources:
- path: Resources/App.icon
settings:
base:
ASSETCATALOG_COMPILER_APPICON_NAME: App # the .icon's name, no extension
```
Keep the old `AppIcon.appiconset` beside it while the deployment target is
below 26 — older systems fall back to it.
## Gotchas
Each of these cost a render, and none is documented — the full set, with the
measurements, is in `references/icon-json.md`.
- **Icon Composer fills contours and ignores strokes.** `<circle fill="none"
stroke="…">` arrives as a filled disc: the first build of one icon came out
as a white blob. Hand it outlines — a ring is an annulus with `evenodd`, an
arc is a band with rounded ends.
- **Groups are drawn front to back.** The background written first covers
everything; it goes **last**.
- **`fill` belongs to the layer.** On a group it is accepted and silently
ignored — `ictool` returns 0 either way.
- **`fill-specializations` does nothing from JSON** (Icon Composer 1.6). On a
group it parses and changes nothing; on a layer the document stops loading.
Per-appearance colours are a thing the GUI writes — if dark or monochrome
must differ, open the `.icon` and set them there, then stop regenerating it.
- **A colour is `display-p3:r,g,b,a`, all four components.** Three fail with
"Expected four comma separated color components"; a bare hex with "missing
':' delimiter", which names the delimiter and not the prefix.
- **`linear-gradient` takes exactly two colours.** A three-stop brand gradient
has to be drawn as a background *layer* — at the cost that the system can no
longer dim it for dark. Two colours it can dim; three it cannot touch. Pick
which matters more and say so in a comment.
- **The artboard is already composed.** Scaling it to "72% of the canvas, so it
breathes" shrinks the mark twice — the designer's margin plus yours. Scale
1:1; use ~0.9 only if watchOS is claimed, because a circular mask eats the
corners of a square's margin.
- **Do not claim platforms you do not ship.** `supported-platforms` with
`circles: [watchOS]` puts a round preview in front of you that no user will
ever see, and tempts you into re-composing the icon for it.
## Don't
- **Don't redraw the mark from the app's own code.** The button inside the app
and the icon on the home screen are two different objects; making the icon
"match the code" produces a mark nobody recognises.
- **Don't trace a PNG when a vector exists.** Measuring pixels gets the radii
right and the end caps wrong.
- **Don't ship without looking at Dark.** It is the appearance most likely to
be ugly and the one nobody checks.
- **Don't leave the probes in the repo.** Scripts written to interrogate the
format belong in the commit message or in `references/`, not in `scripts/`.