Brand mark: scallop ribbon shape, app icon in light / dark / tinted (#11)
CI / build-and-deploy (push) Successful in 18s

This commit was merged in pull request #11.
This commit is contained in:
2026-08-03 17:00:52 +00:00
parent b3d8719874
commit c932814166
10 changed files with 1073 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
<!doctype html>
<!--
App icon artwork for Marks. Rendered to PNG by generate.sh — do not edit the
PNGs in AppIcon.appiconset directly, they are build output.
The path below is the same geometry as ScallopMark in
Marks/Views/Library/MarksMark.swift, in the same canonical 88 x 98 box. If you
change one, change the other; there is no shared source between Swift and SVG.
Three variants, per Apple's iOS 18+ app icon model:
light — full-bleed artwork on the paper ground, flattened (icons may not
carry an alpha channel).
dark — artwork only, on transparency. The system supplies the dark
background, so the bands are the palette's dark counterparts,
which are lifted rather than dimmed.
tinted — greyscale on transparency. The system maps luminance to the user's
chosen tint, so the three bands are carried as three greys whose
relative luminance matches the colour version. Flattening them to
one grey would throw away the band reading in exactly the mode
that has nothing else left.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<title>Marks app icon</title>
<style>
html,body{margin:0;padding:0;background:transparent}
#tile{width:1024px;height:1024px;display:flex;align-items:center;justify-content:center}
svg{display:block}
</style>
</head>
<body>
<div id="tile"></div>
<script>
// 88 x 98 canonical box. 4 tails => 3 steps of 88/3.
const W = 88, H = 98, R = 10, DEPTH = 16, TAILS = 4
const step = W / (TAILS - 1)
let d = `M${R} 0 H${W - R} A${R} ${R} 0 0 1 ${W} ${R} V${H}`
for (let i = 1; i < TAILS; i++) {
d += ` L${(W - step * (i - 0.5)).toFixed(3)} ${H - DEPTH}`
d += ` L${(W - step * i).toFixed(3)} ${H}`
}
d += ` V${R} A${R} ${R} 0 0 1 ${R} 0 Z`
const VARIANTS = {
// navy, vermilion, yellow on the paper sheet
light: {ground: '#F8F5EF', bands: ['#001A55', '#ED663F', '#FECD00']},
// the palette's dark counterparts, lifted so they carry on a dark ground
dark: {ground: null, bands: ['#5B8FD0', '#E07A5C', '#D8AD10']},
// greys ordered by the colour version's relative luminance
tinted: {ground: null, bands: ['#737373', '#B8B8B8', '#F2F2F2']},
}
const v = VARIANTS[new URLSearchParams(location.search).get('v') || 'light']
// Mark occupies 62% of the tile's height, which lands it inside Apple's grid
// with the margin the squircle mask wants.
const markH = 1024 * 0.62
const svg = [`<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">`]
if (v.ground) svg.push(`<rect width="1024" height="1024" fill="${v.ground}"/>`)
svg.push(`<defs><clipPath id="mark"><path d="${d}"/></clipPath></defs>`)
svg.push(`<g transform="translate(${512 - markH * W / H / 2} ${512 - markH / 2}) scale(${markH / H})">`)
svg.push(`<g clip-path="url(#mark)">`)
v.bands.forEach((color, i) => {
svg.push(`<rect x="${(W / 3 * i).toFixed(3)}" y="0" width="${(W / 3).toFixed(3)}" height="${H}" fill="${color}"/>`)
})
svg.push(`</g></g></svg>`)
document.getElementById('tile').innerHTML = svg.join('')
</script>
</body>
</html>
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Renders the three app-icon variants from appicon.html into the asset catalog.
#
# scripts/appicon/generate.sh
#
# Chrome is the rasteriser because it is the only thing on a stock Mac that
# renders SVG the same way the browser preview sheets in docs/ do. The PNGs it
# writes are build output — edit appicon.html, not them.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
OUT="$HERE/../../Marks/Assets.xcassets/AppIcon.appiconset"
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
[ -x "$CHROME" ] || { echo "Google Chrome not found at $CHROME" >&2; exit 1; }
command -v magick >/dev/null || { echo "ImageMagick (magick) not found" >&2; exit 1; }
render() {
local variant=$1 name=$2
"$CHROME" --headless=new --disable-gpu --hide-scrollbars \
--default-background-color=00000000 \
--screenshot="$OUT/$name" --window-size=1024,1024 \
--virtual-time-budget=2000 \
"file://$HERE/appicon.html?v=$variant" >/dev/null 2>&1
echo " $name"
}
echo "rendering app icon ->"
render light AppIcon.png
render dark AppIcon-Dark.png
render tinted AppIcon-Tinted.png
# The light icon is the one shipped as the home-screen artwork, and iOS rejects
# an alpha channel there. The dark and tinted variants are composited by the
# system over a background it supplies, so they must keep theirs.
magick "$OUT/AppIcon.png" -background '#F8F5EF' -alpha remove -alpha off "$OUT/AppIcon.png"
echo "done"