From bc65bb586cd93fef8fc509311ec2401c143efc1f Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 3 Aug 2026 11:55:39 -0500 Subject: [PATCH] Brand mark: scallop ribbon shape, app icon in light / dark / tinted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- Marks.xcodeproj/project.pbxproj | 4 + .../AppIcon.appiconset/AppIcon-Dark.png | Bin 0 -> 14937 bytes .../AppIcon.appiconset/AppIcon-Tinted.png | Bin 0 -> 14158 bytes .../AppIcon.appiconset/AppIcon.png | Bin 9198 -> 13857 bytes .../AppIcon.appiconset/Contents.json | 24 + Marks/Views/Library/MarksMark.swift | 166 +++++++ docs/logo-c1-variants.html | 322 +++++++++++++ docs/logo-variants.html | 447 ++++++++++++++++++ scripts/appicon/appicon.html | 72 +++ scripts/appicon/generate.sh | 38 ++ 10 files changed, 1073 insertions(+) create mode 100644 Marks/Assets.xcassets/AppIcon.appiconset/AppIcon-Dark.png create mode 100644 Marks/Assets.xcassets/AppIcon.appiconset/AppIcon-Tinted.png create mode 100644 Marks/Views/Library/MarksMark.swift create mode 100644 docs/logo-c1-variants.html create mode 100644 docs/logo-variants.html create mode 100644 scripts/appicon/appicon.html create mode 100755 scripts/appicon/generate.sh diff --git a/Marks.xcodeproj/project.pbxproj b/Marks.xcodeproj/project.pbxproj index 9974c22..40ca20f 100644 --- a/Marks.xcodeproj/project.pbxproj +++ b/Marks.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 1F1CB72BBCFFB33B6533D5C9 /* LibraryListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E093C878E702891C64D21FD5 /* LibraryListRow.swift */; }; 212F713DCC289C48087B79AE /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7728D15C17219ABFF3EFFE /* Log.swift */; }; 22C814FD55D29B88D227C987 /* SpotlightBookmarkSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41DDBB04346F3BF06DE233D2 /* SpotlightBookmarkSearch.swift */; }; + 2F80DFB7298B733965FE04F5 /* MarksMark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EC8E834AB663A823D77BCB1 /* MarksMark.swift */; }; 337E8272EEB3B10FD0868F76 /* IngestedSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9BBF1006495D75DE4A232 /* IngestedSource.swift */; }; 3528AF5CB690BBCCF337581B /* Bookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA428181B35885F7D9F4D55 /* Bookmark.swift */; }; 4153FBF538C1D3F4BC96E4C5 /* LibraryKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA5C9BFD9C0DD3876CC32B3A /* LibraryKit.swift */; }; @@ -138,6 +139,7 @@ 23F172EC9977CD5C51B228B9 /* MarksWidget.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = MarksWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 240DBB87940F8D255A812EB2 /* BookmarkActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkActions.swift; sourceTree = ""; }; 2526E22D8EC5453358F0FCF9 /* SpotlightRetrievalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotlightRetrievalTests.swift; sourceTree = ""; }; + 3EC8E834AB663A823D77BCB1 /* MarksMark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarksMark.swift; sourceTree = ""; }; 41DDBB04346F3BF06DE233D2 /* SpotlightBookmarkSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotlightBookmarkSearch.swift; sourceTree = ""; }; 47CB3AAED5B64809B06A9650 /* RecentPodcastsWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentPodcastsWidget.swift; sourceTree = ""; }; 49685B8F3FEC72E8CF75843E /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; @@ -257,6 +259,7 @@ 1BF2B010DADCCFBC282D37F0 /* LibraryGridView.swift */, AA5C9BFD9C0DD3876CC32B3A /* LibraryKit.swift */, E093C878E702891C64D21FD5 /* LibraryListRow.swift */, + 3EC8E834AB663A823D77BCB1 /* MarksMark.swift */, ); path = Library; sourceTree = ""; @@ -600,6 +603,7 @@ 41F00F4E7FFC1C0ACF71E398 /* MarksApp.swift in Sources */, E10B0B4EC9580342830EC0D2 /* MarksAppIntents.swift in Sources */, 8BD3FA025C082654D55374A2 /* MarksAuth.swift in Sources */, + 2F80DFB7298B733965FE04F5 /* MarksMark.swift in Sources */, 14E1B3CE58D36BFF1A2199C1 /* OnboardingView.swift in Sources */, 773C174263D08416248D0675 /* PodcastGenerationManager.swift in Sources */, 0479C0AA16E3AFDBD4414AD0 /* PodcastIndex.swift in Sources */, diff --git a/Marks/Assets.xcassets/AppIcon.appiconset/AppIcon-Dark.png b/Marks/Assets.xcassets/AppIcon.appiconset/AppIcon-Dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c964e63410b77709825b8abd252456e94e4399e4 GIT binary patch literal 14937 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 Yx|D(ueL9Z6C`k4*)^=8fC%y0dA7dElT>t<8 literal 0 HcmV?d00001 diff --git a/Marks/Assets.xcassets/AppIcon.appiconset/AppIcon-Tinted.png b/Marks/Assets.xcassets/AppIcon.appiconset/AppIcon-Tinted.png new file mode 100644 index 0000000000000000000000000000000000000000..d86980f21da0f2d911520d093ba891c9723b8ddf GIT binary patch literal 14158 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�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

$HH9XT-ua8$WUTsf= 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 diff --git a/Marks/Assets.xcassets/AppIcon.appiconset/Contents.json b/Marks/Assets.xcassets/AppIcon.appiconset/Contents.json index cefcc87..e02c1e5 100644 --- a/Marks/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Marks/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -5,6 +5,30 @@ "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "AppIcon-Dark.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "AppIcon-Tinted.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], "info" : { diff --git a/Marks/Views/Library/MarksMark.swift b/Marks/Views/Library/MarksMark.swift new file mode 100644 index 0000000..310d0a2 --- /dev/null +++ b/Marks/Views/Library/MarksMark.swift @@ -0,0 +1,166 @@ +import SwiftUI + +// MARK: - The mark +// +// One path, four tails. +// +// The brand mark is a single closed shape rather than a stack of separate +// ribbons, and that is the whole design decision. A logo gets tested in two +// places that have nothing to do with how it looks on a slide: at 16pt in a +// Spotlight result, and in iOS 26's tinted and monochrome icon modes, where the +// palette is thrown away and only the silhouette survives. Every arrangement of +// N separate ribbons collapses into a blob under both tests, because the shapes +// were only ever told apart by colour. A scalloped baseline is told apart by +// its outline, so it reads the same tinted, monochrome, and 16pt tall. +// +// Geometry is defined in a canonical 88 x 98 box and fitted to whatever rect it +// is handed, so the proportions can't drift with the container. + +struct ScallopMark: Shape { + /// Downward points along the baseline. Four is the drawn default: three + /// reads as a plain banner, and five starts to look like bunting. + var tails: Int = 4 + + /// How far the notches cut up from the baseline, as a fraction of height. + /// Shallower than this and the tails read as damage rather than intent. + var notchDepth: CGFloat = 16.0 / 98.0 + + /// Top-corner radius, as a fraction of width. + var capRadius: CGFloat = 10.0 / 88.0 + + /// Canonical proportions of the drawn mark. Everything else derives from + /// this, including the app icon artwork — see scripts/appicon. + static let aspectRatio: CGFloat = 88.0 / 98.0 + + func path(in rect: CGRect) -> Path { + // Fit the canonical box into `rect` without distorting it, so callers + // can hand this any frame and still get the drawn proportions. + let height = min(rect.height, rect.width / Self.aspectRatio) + let width = height * Self.aspectRatio + let minX = rect.midX - width / 2 + let minY = rect.midY - height / 2 + let maxX = minX + width + let maxY = minY + height + + let radius = min(capRadius * width, min(width, height) / 2) + let depth = notchDepth * height + let points = max(2, tails) + let step = width / CGFloat(points - 1) + + var path = Path() + path.move(to: CGPoint(x: minX + radius, y: minY)) + // Top edge, right cap, then straight down the right side. + path.addArc( + tangent1End: CGPoint(x: maxX, y: minY), + tangent2End: CGPoint(x: maxX, y: minY + radius), + radius: radius + ) + path.addLine(to: CGPoint(x: maxX, y: maxY)) + // The tails, cut right to left so the winding stays consistent with + // the caps above. + for i in 1.. MarksMark { + MarksMark(bands: [color]) + } + + var body: some View { + ScallopMark(tails: tails) + .fill(bandFill) + .aspectRatio(ScallopMark.aspectRatio, contentMode: .fit) + } + + /// Hard-stopped, not blended: the bands are three inks laid side by side, + /// not a gradient. Doubling the stop at each boundary is what kills the + /// ramp SwiftUI would otherwise interpolate. + private var bandFill: LinearGradient { + let stops = bands.enumerated().flatMap { index, color in + [ + Gradient.Stop(color: color, location: Double(index) / Double(bands.count)), + Gradient.Stop(color: color, location: Double(index + 1) / Double(bands.count)), + ] + } + return LinearGradient(stops: stops, startPoint: .leading, endPoint: .trailing) + } +} + +// MARK: - Previews + +#Preview("Size ladder") { + VStack(spacing: 28) { + HStack(alignment: .bottom, spacing: 22) { + ForEach([128, 64, 40, 24, 16], id: \.self) { size in + MarksMark().frame(height: CGFloat(size)) + } + } + // The test the shape was chosen to pass: no colour, same mark. + HStack(alignment: .bottom, spacing: 22) { + ForEach([128, 64, 40, 24, 16], id: \.self) { size in + MarksMark.monochrome().frame(height: CGFloat(size)) + } + } + HStack(spacing: 22) { + ForEach(3..<6) { tails in + MarksMark(tails: tails).frame(height: 72) + } + } + } + .padding(40) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Paper.sheet) +} + +#Preview("Lockup") { + HStack(spacing: 18) { + MarksMark().frame(height: 64) + VStack(alignment: .leading, spacing: 6) { + Text("Marks").font(.system(size: 46, design: .serif)) + Text("EVERYTHING YOU SAVED") + .font(PaperType.micro) + .tracking(3) + .foregroundStyle(Paper.tertiary) + } + } + .foregroundStyle(Paper.ink) + .padding(40) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Paper.sheet) +} diff --git a/docs/logo-c1-variants.html b/docs/logo-c1-variants.html new file mode 100644 index 0000000..277588c --- /dev/null +++ b/docs/logo-c1-variants.html @@ -0,0 +1,322 @@ + + + + +Marks — C1 development + + + + +

+

C1 — three ribbons, developed

+

C1 had the most robust silhouette of the seven and the least ownership: three bookmark ribbons is a shape any read-later app could reach for. Ownability is the thing to attack, so each variant below adds one specific idea — a rhythm, a structure, a colour rule — and the caption says whether it actually buys anything.

+
+ + + +
+ 01 +

Ten developments

+

Shown in the app's own palette (navy / vermilion / yellow from Paper.swatchPairs) rather than the reference cyan-violet, since that was the conclusion of the last sheet. The ownable? line is the honest verdict, not a summary.

+
+
+ +
+ 02 +

Palette

+

The three-swatch column is the interesting one. The app already hashes bookmarks into a twelve-swatch palette — a mark built from three of those swatches is describing the app's actual data model, which is ownership no competitor can copy without copying the system underneath it.

+
+
+ +
+ 03 +

Size ladder

+

128 / 64 / 40 / 24 / 16 px. C1's whole argument is that it survives this; the developments have to survive it too or they've bought ownership with legibility.

+
+
+ +
+ 04 +

App icon

+

Squircle at 118 and 58 px, mark at ~62% of the tile.

+
+
+
+ +
+ 05 +

Lockups

+
+
+ +
+ 06 +

Pattern

+

The test C1 is most likely to win: a repeating field is where a simple silhouette beats a clever one.

+
+
+ + + + + diff --git a/docs/logo-variants.html b/docs/logo-variants.html new file mode 100644 index 0000000..897627d --- /dev/null +++ b/docs/logo-variants.html @@ -0,0 +1,447 @@ + + + + +Marks — logo variants + + + + +
+

Marks — logo variants

+

Five geometries derived from the reference (letter + box files + bookmark ribbon), each drawn once and re-coloured through the app's existing Paper palette. Geometry lives in <symbol>; colour is injected with CSS custom properties, so any cell in the matrix is a real, buildable combination.

+
+ + + + + +
+ 01 +

Seven geometries

+

Shown in the reference palette (cyan #22C7F0 / violet #8B4DEE) with multiply overlap, so they're directly comparable to the original.

+
+ +
+
+

A1 · Binder-B

+

Faithful reconstruction. Ribbon stem carries the notch; three bars go wide / narrow / wide to make the B. Closest to the source.

+
+ +
+
+

A2 · Ribbon-B

+

Every terminal becomes a swallowtail. More internally consistent, but the bowls stop reading as bowls — the B gets weaker.

+
+ +
+
+

B1 · Slab-M, tailed

+

Retargeted to the app's actual name. A real M — counters are wedges, apex at the cap line — with a swallowtail on each of the three feet.

+
+ +
+
+

B2 · Slab-M, flat

+

Same letter, tails removed. Sturdier and more type-like, but only the rounded cap is left carrying the bookmark idea — probably too little.

+
+ +
+
+

B3 · Ribbon knockout

+

Single ribbon, letter knocked out. One colour, one shape — the only variant that's trivially correct in iOS 26 tinted and mono icon modes.

+
+ +
+
+

C1 · Three ribbons

+

Drops the letterform entirely. Stepped heights do the work. Most robust silhouette, least ownable — many apps could use this.

+
+ +
+
+

C2 · Monoline

+

Binder-B as strokes. Sits in the same register as the app's serif/paper styling instead of fighting it. Fails below ~28px.

+
+ +
+
+ + +
+ 02 +

Palette matrix

+

Each geometry through the reference palette and five pairs drawn from Paper.swatchPairs in LibraryKit.swift. The Dark column uses the palette's own dark-mode counterparts, not dimmed light values.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReferenceNavy + Verm.Ink + Verm.Yellow + Verm.Blue + ForestInk onlyDark
A1 Binder-B
A2 Ribbon-B
B1 Slab-M deep
B2 Slab-M shallow
B3 Knockout
C1 Three ribbons
C2 Monoline
+
+ + +
+ 03 +

Size ladder

+

128 / 64 / 40 / 24 / 16 px. 40px is the Settings-row and tab-bar size; 16px is the favicon / Spotlight-result size. Anything that turns to mush at 24 is a wordmark, not a mark.

+ +
+
A1 Binder-B
+
128
+
64
+
40
+
24
+
16
+
+ +
+
B1 Slab-M deep
+
128
+
64
+
40
+
24
+
16
+
+ +
+
B3 Knockout
+
128
+
64
+
40
+
24
+
16
+
+ +
+
C1 Three ribbons
+
128
+
64
+
40
+
24
+
16
+
+ +
+
C2 Monoline
+
128
+
64
+
40
+
24
+
16
+
+
+ + +
+ 04 +

App icon

+

iOS squircle at 120pt and 60pt. The mark is set at ~62% of the tile — Apple's grid wants generous margins, and every one of these was drawn edge-to-edge in its own box.

+
+
A1 · navy
+
B1 · yellow
+
B3 · ink
+
C1 · blue
+
A1 · dark
+
A1 · reference
+
+
+
60pt
+
60pt
+
60pt
+
60pt
+
60pt
+
60pt
+
+
+ + +
+ 05 +

Lockups

+

Wordmark set in the app's own voice — serif for the name, mono letterspaced for the descriptor — rather than the reference's geometric sans, which belongs to a different product.

+ +
+ +
Marks
Everything you saved
+
+ +
+ +
Marks
Everything you saved
+
+ +
+ +
Marks
Everything you saved
+
+ +
+ +
Marks
Everything you saved
+
+
+ + +
+ 06 +

Pattern test

+

The reference proves itself by tiling. Same test here — if the mark only works alone, it isn't a system.

+
+
+ + + + + diff --git a/scripts/appicon/appicon.html b/scripts/appicon/appicon.html new file mode 100644 index 0000000..aedd51e --- /dev/null +++ b/scripts/appicon/appicon.html @@ -0,0 +1,72 @@ + + + + + +Marks app icon + + + +
+ + + diff --git a/scripts/appicon/generate.sh b/scripts/appicon/generate.sh new file mode 100755 index 0000000..81906e2 --- /dev/null +++ b/scripts/appicon/generate.sh @@ -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" -- 2.54.0