diff --git a/assets/backgrounds/squircle-mac-dark.svg b/assets/backgrounds/squircle-mac-dark.svg
new file mode 100644
index 0000000000..9db218fbf7
--- /dev/null
+++ b/assets/backgrounds/squircle-mac-dark.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/backgrounds/squircle-mac-light.svg b/assets/backgrounds/squircle-mac-light.svg
new file mode 100644
index 0000000000..3d0cff9657
--- /dev/null
+++ b/assets/backgrounds/squircle-mac-light.svg
@@ -0,0 +1,6 @@
+
diff --git a/scripts/generate_icons.py b/scripts/generate_icons.py
index cceb850e37..49f60b7c5c 100644
--- a/scripts/generate_icons.py
+++ b/scripts/generate_icons.py
@@ -14,11 +14,16 @@ Sources of truth — two axes, composed per target:
Backgrounds (per platform surface, light/dark):
assets/backgrounds/squircle-light.svg white rounded
assets/backgrounds/squircle-dark.svg #0d1117 rounded
+ assets/backgrounds/squircle-mac-light.svg mac HIG grid
+ assets/backgrounds/squircle-mac-dark.svg mac HIG grid
The master SVGs (assets/icon-master.svg light, assets/icon-master-dark.svg
dark) are GENERATED artifacts — squircle background + girl nested into the
824px HIG content safe zone. The light master drives every squircle target;
- the dark master drives the dark-appearance targets.
+ the dark master drives the dark-appearance targets. macOS is the exception:
+ its icns targets render from an in-memory mac master that puts the same
+ squircle on Apple's 824x824 (r=185.4) grid — centered in 1024 with 100px
+ margins — so the icon matches the size of Apple-template neighbors.
The girl is nested via its art bbox as viewBox, so it always lands centered in
the box (824 safe zone for squircles / height-fitted for the marks) without
@@ -98,11 +103,15 @@ DARK_HEX = "#0d1117"
DARK_RGB = (13, 17, 23)
# Girl placement per background: (x, y, w, h) in that background's coordinate
-# space. Squircle backgrounds put the girl in the 824px HIG content safe zone
-# (centered, 100px pad on a 1024 canvas). Marks reuse the same squircles.
+# space. Full-bleed squircles put the girl in the 824px HIG content safe zone
+# (centered, 100px pad on a 1024 canvas); the mac-grid squircle is itself 824
+# on 1024, so the girl box scales by 824/1024 to keep the same relative size
+# inside the shape. Marks reuse the full-bleed squircles.
GIRL_BOXES = {
"squircle-light.svg": (100, 100, 824, 824),
"squircle-dark.svg": (100, 100, 824, 824),
+ "squircle-mac-light.svg": (180.5, 180.5, 663, 663),
+ "squircle-mac-dark.svg": (180.5, 180.5, 663, 663),
}
# The brand-kit SVG canvas (both girl svgs share this viewBox).
GIRL_VIEWBOX = 5487.0615
@@ -187,6 +196,10 @@ class IconArt:
self.bboxes: dict[str, tuple[float, float, float, float]] = {}
self.master = compose_svg(self, "black", "squircle-light.svg")
self.master_dark = compose_svg(self, "white", "squircle-dark.svg")
+ # macOS icons sit on Apple's 824-on-1024 grid, not the full-bleed
+ # squircle: same art, mac-grid backgrounds, icns targets only.
+ self.master_mac = compose_svg(self, "black", "squircle-mac-light.svg")
+ self.master_mac_dark = compose_svg(self, "white", "squircle-mac-dark.svg")
def girl_path(art: IconArt, girl: str) -> str:
@@ -335,11 +348,11 @@ def target_bytes(art: IconArt, kind: str, arg: object) -> bytes:
img = render(art.master_dark, max(arg))
img.save(buf, format="ICO", sizes=[(s, s) for s in arg])
elif kind == "icns":
- img = render(art.master, 1024)
+ img = render(art.master_mac, 1024)
frames = [img.resize((s, s), Image.LANCZOS) for s in (16, 32, 64, 128, 256, 512, 1024)]
img.save(buf, format="ICNS", append_images=frames[1:])
elif kind == "icns_dark":
- img = render(art.master_dark, 1024)
+ img = render(art.master_mac_dark, 1024)
frames = [img.resize((s, s), Image.LANCZOS) for s in (16, 32, 64, 128, 256, 512, 1024)]
img.save(buf, format="ICNS", append_images=frames[1:])
elif kind == "wide":