name: ddg-drk-add-icon description: Invoke ONLY when the user explicitly runs /ddg-drk-add-icon or names this skill by name (e.g. "use ddg-drk-add-icon to add these SVGs"). Do NOT auto-invoke from symptom/intent matching — the skill mutates the apple-browsers asset catalog and Swift files and creates a git commit, so it must be user-initiated. If the user asks about adding icons or Swift package assets without naming this skill, answer directly instead. Inputs are a list of local paths and/or HTTP URLs (one entry per icon); category (Glyph/Color/Recolorable) and size are derived from each filename. Handles imageset/symbolset creation, Contents.json variants, and the camelCase accessor in the matching DesignSystemImages+*.swift file, in a single batch commit.
ddg-drk-add-icon
Overview
Imports one or more SVGs into the DesignResourcesKitIcons Swift package at SharedPackages/Infrastructure/DesignResourcesKitIcons. For each icon, three things must always change together:
- An
*.imageset/(or*.symbolset/for Recolorable) directory underDesignSystemImages.xcassets/{Category}/{Size}/containing the SVG. - A
Contents.jsonin that directory — format differs by category. - A
public static varaccessor in the matchingDesignSystemImages+Glyphs.swift/+Color.swift/+Recolorable.swift, inserted alphabetically inside the rightSizeNenum.
Forgetting any of the three leaves the icon unusable.
Inputs
- Required: one or more local paths and/or
http(s)URLs to SVGs. A single icon is just a list of length one — treat the singular and batch cases the same way. - Optional, only if a filename doesn't follow convention: category, size, accessor name (per-icon).
Inputs may mix local paths and URLs in the same invocation, and may span multiple categories/sizes — they don't have to be homogeneous.
Filename convention (drives everything else)
| Category | Filename | Example |
|---|---|---|
| Glyph | Title-Case-{size}.svg |
Pin-Remove-12.svg |
| Color | Title-Case-Color-{size}.svg |
Chat-Color-12.svg |
| Recolorable | lower-case-recolorable-{size}.svg |
check-recolorable-24.svg |
Allowed sizes: Glyph 12/16/20/24, Color 12/16/24/32/42/72/96/128, Recolorable 24.
If the file you were given doesn't match, rename it to match before continuing. Don't invent new size buckets — confirm with the user instead.
Steps
Run steps 1–6 for each input icon in the list (they're independent — fetch all first, then process). Step 7 is a single verification pass at the end.
Get the file locally. If input is a URL,
curl -fL -o /tmp/<basename> <url>. If local, just note the path. When given multiple URLs, you may run thecurls in parallel.Parse the filename to derive:
- Category (Glyph / Color / Recolorable — by presence of
-Color-or-Recolorable-). - Size (trailing number before
.svg). - Base name (everything before the category/size suffix).
- Accessor name = base name lowerCamelCased. For Color, drop the
Colorsuffix from the variable name but keep it in the resource name (e.g.Chat-Color-12.svg→ accessorchat, resource.chatColor12). For Recolorable, same shape — dropRecolorablefrom the variable name but keep it in the resource (e.g.check-recolorable-24.svg→ accessorcheck, resource.checkRecolorable24). For Glyph, no suffix to drop — the whole base name becomes the camelCased variable and the resource is<variable><size>(e.g.Pin-Remove-12.svg→ accessorpinRemove, resource.pinRemove12).
- Category (Glyph / Color / Recolorable — by presence of
Create the imageset/symbolset directory:
- Glyph:
SharedPackages/Infrastructure/DesignResourcesKitIcons/Sources/DesignResourcesKitIcons/DesignSystemImages.xcassets/Glyphs/{size}px/{Filename-without-.svg}.imageset/ - Color: same root,
Color/{size}px/...imageset/— exception:Color/32x/(nop, matches existing repo state). - Recolorable:
Recolorable/{size}px/{Title-Cased-filename-without-.svg}.symbolset/— the directory is Title-Case (e.g.Check-Recolorable-24.symbolset/) to match how Glyph and Color imageset directories are cased. (The existingcheck-recolorable-24.svgSVG predates this convention; new icons should follow it.)
- Glyph:
Copy the SVG into that directory. If needed, rename the SVG to match the imageset/symbolset directory name (e.g. drop
Check-Recolorable-24.svginsideCheck-Recolorable-24.symbolset/). hefilenamefield inContents.json(next step) must reference the SVG file name exactly.Write
Contents.jsonin that same directory. Use the variant for the category:Glyph:
{ "images" : [{ "filename" : "Pin-Remove-12.svg", "idiom" : "universal" }], "info" : { "author" : "xcode", "version" : 1 }, "properties" : { "template-rendering-intent" : "template" } }Glyph with
Recolorablein the filename (e.g.Check-Recolorable-16.svg) — same category and folder layout as a regular Glyph, but omit thepropertiesblock entirely so the asset is treated as "Default image" (preserves the SVG's own colors) instead of "Template image" (tinted by foreground color):{ "images" : [{ "filename" : "Check-Recolorable-16.svg", "idiom" : "universal" }], "info" : { "author" : "xcode", "version" : 1 } }The accessor still keeps the full base name (including
Recolorable), e.g.checkRecolorable: DesignSystemImage { .init(resource: .checkRecolorable16) }. Only theRecolorable(.symbolset) category drops the suffix from the accessor — Glyphs do not.Color (no
properties):{ "images" : [{ "filename" : "Chat-Color-12.svg", "idiom" : "universal" }], "info" : { "author" : "xcode", "version" : 1 } }Recolorable (
symbolsarray,.symbolset, hierarchical rendering):{ "info" : { "author" : "xcode", "version" : 1 }, "properties" : { "symbol-rendering-intent" : "hierarchical" }, "symbols" : [{ "filename" : "Check-Recolorable-24.svg", "idiom" : "universal" }] }Add the Swift accessor to the right file in
SharedPackages/Infrastructure/DesignResourcesKitIcons/Sources/DesignResourcesKitIcons/:- Glyph →
DesignSystemImages+Glyphs.swift - Color →
DesignSystemImages+Color.swift - Recolorable →
DesignSystemImages+Recolorable.swift
Find the
public enum Size{N}block matching the icon's size and insert a new line in alphabetical order by accessor name:public static var pinRemove: DesignSystemImage { .init(resource: .pinRemove12) }The
.pinRemove12reference is auto-generated by Xcode at build time from the imageset folder name — do not try to define it manually.- Glyph →
Verify (once, after all icons are processed):
git statusshould show, per icon, one new imageset/symbolset directory with two files (the SVG andContents.json), plus modifications to theDesignSystemImages+*.swiftfile(s) for each category touched. The number of modified Swift files equals the number of distinct categories in the batch (1–3). Build the package in Xcode (or rely on the user's next build) — if any accessor still red-underlines, the corresponding imageset folder name is mistyped.
Worked example (this just happened on dominik/macos-26-menu-icons)
Input: /Users/ayoy/Downloads/Pin-Remove-12.svg
- Category: Glyph (no
-Color-/-Recolorable-). Size: 12. Base:Pin-Remove. Accessor:pinRemove. - New dir:
.../Glyphs/12px/Pin-Remove-12.imageset/withPin-Remove-12.svgand the GlyphContents.json(template intent). - Inserted in
DesignSystemImages+Glyphs.swiftSize12 enum betweenpinandplatform:public static var pinRemove: DesignSystemImage { .init(resource: .pinRemove12) }
Commit landed as 41d4ea6e95 Add Pin-Remove and Window-Duplicate 12px icons — five files: two SVGs, two Contents.json, one Swift edit.
Common mistakes
- Wrong Contents.json for category. Glyph without
template-rendering-intentrenders as filled black — not tinted. Color withtemplate-rendering-intentstrips the colors. Exception: Glyphs whose filename containsRecolorable(e.g.Check-Recolorable-16.svg) must omittemplate-rendering-intentso they render as Default and keep their own colors. .imagesetfor Recolorable. Recolorable must be.symbolsetwithsymbols(notimages) — Xcode silently ignores it otherwise.Color/32px/instead ofColor/32x/. The 32-size directory is32xin this repo. All other sizes are{N}px.- Alphabetical insertion drift. Several existing files (notably
+Color.swiftSize32 and Size128) aren't strictly alphabetical. Don't replicate the disorder — insert your line in the correct alphabetical position and leave neighbours alone. - Recolorable casing. The
.symbolsetdirectory is Title-Case (e.g.Check-Recolorable-24.symbolset/) like the Glyph and Color imageset directories. The SVG inside should ideally follow the directory's casing (Title-Case) — note that the existingcheck-recolorable-24.svguses lowercase historically; either works, but match the directory for new icons. Thefilenamefield inContents.jsonmust reference whatever the SVG is actually named. - Manually editing
ImageResource. The.pinRemove12,.chatColor12, etc. accessors are generated by Xcode from the asset catalog. Don't grep for them — they only exist post-build.
Batching notes
When the input list has more than one icon:
- One commit for the batch. Don't make a commit per icon. Commit message convention from recent history:
Add <Title> and <Title> {size}px icons(two icons, same size) orAdd <N> icons/Add <Title>, <Title>, and <Title> iconswhen sizes/categories are mixed — match the style of nearby commits ingit logfor the asset catalog. - Swift edits coalesce. All Glyphs land in
DesignSystemImages+Glyphs.swift, all Colors in+Color.swift, all Recolorables in+Recolorable.swift. Open each file at most once; insert every new accessor in its correctSize{N}enum (alphabetically) before moving on. - Same size + same category = same enum. When several icons share a size and category, insert them all into the same
Size{N}block in one pass so you don't re-locate the block per icon. - Independent failures. If one input is malformed (bad filename, wrong size bucket), stop and confirm with the user — don't silently skip it and ship the rest, since the user is expecting the full set.
- Don't parallelise file writes. Asset-catalog directory creation and Swift edits are cheap and ordering-sensitive (alphabetical insertion); run them sequentially. Only the initial
curlfetches are worth parallelising.