The mark is one closed path with a scalloped baseline — four tails, rounded cap — rather than a stack of separate ribbons. That is the whole design decision. A logo gets tested at 16pt in a Spotlight result and in iOS 26's tinted and monochrome icon modes, where the palette is discarded and only the silhouette survives. Arrangements of N separate ribbons collapse into a blob under both, because the shapes were only ever told apart by colour; a scalloped outline is told apart by its own edge, so it reads the same tinted, monochrome, and 16pt tall. ScallopMark defines geometry in a canonical 88x98 box and fits it to whatever rect it is handed, so proportions cannot drift with the container. MarksMark adds the three bands as a hard-stopped gradient, keeping it a single fillable shape. The bands are Paper.swatchPairs entries rather than bespoke brand colours: the app already hashes every bookmark into that palette, so the mark is made of the same ink as the content. App icon ships all three variants per Apple's iOS 18+ model — light flattened (iOS rejects an alpha channel there), dark and tinted on transparency for the system to composite. The tinted variant carries three greys ordered by the colour version's relative luminance rather than one flat grey, so the band reading survives the one mode that has nothing else left. The PNGs are build output: edit scripts/appicon/appicon.html and re-run generate.sh. docs/logo-variants.html and docs/logo-c1-variants.html are the exploration the choice came out of. Verified: SwiftUI shape rendered through ImageRenderer matches the SVG at every size from 160pt to 16pt; assetutil confirms all three renditions in the compiled catalog with the correct appearance tags; installed on a simulator and checked against the home screen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
73 lines
3.1 KiB
HTML
73 lines
3.1 KiB
HTML
<!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>
|