Skip to content

Give the app icon a tile of its own - #10

Merged
mikeallisonJS merged 1 commit into
mainfrom
feat/app-icon-tile
Aug 15, 2026
Merged

Give the app icon a tile of its own#10
mikeallisonJS merged 1 commit into
mainfrom
feat/app-icon-tile

Conversation

@mikeallisonJS

Copy link
Copy Markdown
Owner

Follow-up to #7, as planned there. The tray mark and the app icon have different jobs and had been sharing one drawing: a 2px-stroke silhouette on transparency is right in a tray full of monochrome glyphs, and reads as a placeholder at 512px in a Dock full of filled shapes.

So the exe, the installer, Add/Remove Programs, the Start menu, the taskbar button, the .app bundle and the DMG's volume now carry the mark knocked out of a rounded tile in white. The tray keeps the bare mark, unchanged.

The tile

  • Rounded square, rx 224 on the 1024 master — 21.9%, between Apple's continuous corner and Windows 11's near-square. That midpoint is the point of having one tile rather than one per platform.
  • 135° gradient, #8f78ff → #3f22b5. The only gradient in the product: an app icon is conventionally read as a lit surface, and nothing else here is. The tray mark stays flat because the OS picks its ink.
  • Mark at 62.5% of the canvas, placed by its 24-unit grid box rather than its ink — the ink sits right of centre (the flow lines run out of the mic), and centring that pushes the mic off-centre, which is what the eye actually tracks.

Composited, not authored as one file

app-tile.svg is the tile alone; build_icons.py renders mark-ready.svg over it. A tile file holding its own copy of the mark geometry is exactly the parallel-copy drift ADR 0006 just finished undoing — and the copy nobody would notice going stale, since that file is opened only when the app icon is being changed. A test asserts the tile source draws nothing but the tile.

Below 32px, margin gives way to glyph

0.80 of the canvas instead of 0.625. Proportional scaling gives a 16px entry ten pixels for the whole mark and the mic stops being a mic. Compared before choosing — at 16 and 24 the larger fill is decisively more legible, and at 32 the smaller one already reads while 0.80 starts crowding the corners. Every platform's own icon set does this; the step is invisible because nothing shows 24 and 32 together.

app_icon() now reads cadent.ico

On Windows a running app's taskbar button takes its icon from setWindowIcon, not from the exe — so pointing it anywhere else makes the button disagree with the Start-menu entry that launched it, which is the same class of mismatch that started this work. Reading the container also means one artefact rather than a parallel PNG set; Qt's ICO support is read-only, which is all this needs.

BRAND_INK goes with it, since nothing paints the bare mark in brand purple any more.

One thing I measured and did not act on

The tile's gradient floor has little separation from a dark taskbar:

vs dark #1f1f1f vs light #f3f3f3
tile top #8f78ff 4.94 3.00
tile floor #3f22b5 1.65 8.98

That is the extreme bottom-right corner only, and rendered on both polarities the icon's mass and the white mark carry it — it reads fine. Flagging it rather than quietly changing an agreed design. If it does bother you, the fix that preserves the gradient exactly is a subtle lighter rim on the tile.

Relatedly, the brand token and its both-polarity taskbar audit are left in place untouched: the thing that now lands on an unknown taskbar is the tile, not the mark, so what that audit should measure is a real question — and one worth asking rather than answering in passing.

Testing

1004 passed, 3 skipped, ruff clean. New coverage: the app icon is the tile (rounded corner transparent, centre filled, gradient running the right way, mark knocked out in white), it exposes every container size, the tile source carries no copy of the mark, and the small-size fill really is the larger one.

🤖 Generated with Claude Code

The tray mark and the app icon have different jobs and had been sharing one
drawing. A 2px-stroke silhouette on transparency is right in a tray full of
monochrome glyphs; at 512px in a Dock full of filled shapes it reads as a
placeholder. So the exe, the installer, Add/Remove Programs, the Start menu,
the taskbar button, the .app bundle and the DMG's volume now carry the mark
knocked out of a rounded tile in white, and the tray keeps the bare mark.

The tile is a rounded square, rx 224 on the 1024 master — 21.9%, between
Apple's continuous corner and Windows 11's near-square, which is the whole
point of having one tile rather than one per platform. It carries a 135°
gradient from #8f78ff to #3f22b5, the only gradient in the product: an app
icon is conventionally read as a lit surface and nothing else here is.

Composited, not authored as one file. app-tile.svg is the tile alone and
build_icons.py renders mark-ready.svg over it, so the geometry keeps the
single source ADR 0006 just finished giving it — a tile file holding its own
copy is the drift that ADR exists to undo, and the copy nobody would notice
going stale, since it is opened only when the app icon changes. A test asserts
the tile source draws nothing but the tile.

Below 32px the tile trades margin for glyph, 0.80 of the canvas against 0.625.
Proportional scaling leaves a 16px entry ten pixels for the whole mark and the
mic stops being a mic; every platform's own set does this, and nothing ever
shows 24 and 32 together for the step to be visible.

icons.app_icon() now reads cadent.ico rather than painting the bare mark. On
Windows a *running* app's taskbar button takes its icon from setWindowIcon,
not from the exe, so anything else there disagrees with the Start-menu entry
that launched it — the same class of mismatch this line of work started from.
Reading the container also means one artefact instead of a parallel PNG set;
Qt's ICO support is read-only, which is all this needs.

The DMG gets .VolumeIcon.icns and the custom-icon bit, so the mounted volume
stops being a generic disk. A missing SetFile costs the icon, not the release.

BRAND_INK goes with it: nothing paints the bare mark in brand purple any more.
The `brand` token and its both-polarity taskbar audit are deliberately left in
place — the tile is what lands on an unknown taskbar now, and what that audit
should measure is a question worth asking rather than answering in passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@mikeallisonJS, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0404074c-ba8f-4e32-86fb-5ef890cff990

📥 Commits

Reviewing files that changed from the base of the PR and between a0b43c4 and d2d3eea.

⛔ Files ignored due to path filters (2)
  • packaging/icons/app-tile.svg is excluded by !**/*.svg
  • packaging/icons/cadent.ico is excluded by !**/*.ico
📒 Files selected for processing (7)
  • cadent/icons.py
  • changelog.d/10.feature.md
  • docs/specs/m4-ui-modernization-spec.md
  • packaging/icons/cadent.icns
  • scripts/build_dmg.py
  • scripts/build_icons.py
  • tests/test_icons.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mikeallisonJS
mikeallisonJS merged commit 2ec9bc3 into main Aug 15, 2026
4 checks passed
@mikeallisonJS
mikeallisonJS deleted the feat/app-icon-tile branch August 15, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant