Skip to content

Swap in the vector Delta branding, and fix Top Contributors - #184

Merged
ssavutu merged 2 commits into
mainfrom
housekeeping
Aug 5, 2026
Merged

Swap in the vector Delta branding, and fix Top Contributors#184
ssavutu merged 2 commits into
mainfrom
housekeeping

Conversation

@ssavutu

@ssavutu ssavutu commented Aug 5, 2026

Copy link
Copy Markdown
Member

Two unrelated frontend cleanups that came up together.

Vector Delta branding

The sidebar wordmark was a raster logo.png. It is now the SVG artwork, with the triangle mark shown when the sidebar is collapsed, and a real favicon — the app was previously serving The Triangle's blue "T", not Delta's own mark.

The supplied SVGs sit in a 377x347 canvas that the artwork occupies a thin band of, so each viewBox is tightened to the actual bounding box; otherwise object-contain shrinks the wordmark to about a fifth of its intended size. Sizing moved from a fixed w-30 h-30 square to height-anchored, since the new mark is 5.3:1 where the PNG was 2.8:1.

Sizes were picked by rendering rather than by guessing:

  • The collapsed mark is 40px, not 28px to match the wordmark. Below ~36px its two concentric rings merge into a single muddy band.
  • The favicon drops those rings entirely — at 16px there is no resolving them.

Two things in the favicon that are load-bearing and easy to undo by accident:

  • No CSS custom properties. A first pass used var(--mark); rendering caught that librsvg silently drops it, so fill fell back to black and stroke: var(--mark) failed outright, deleting the crossbar. Browsers are fine with it, but favicons pass through enough non-browser pipelines that plain class rules are safer.
  • Brand blue is the base color, not white or navy. Safari ignores prefers-color-scheme in favicons, so the base has to survive both light and dark tab strips. The media queries sharpen it to white or navy where they are honored. The fallback path was rendered and checked.

triangle.ico stays registered for browsers without SVG favicon support. logo.png is now unreferenced but left in the tree.

Top Contributors

The card mapped over users — a Set built from the event feed — so it listed every person who had ever done anything, ordered by whenever their name first appeared. Unsorted, uncapped, with someone sitting at the bottom on a single event. Each row also recomputed its count with a full events.filter(), making the card O(users × events).

It now counts into a Map in one pass, sorts by count descending with a name tiebreak, and caps at five — the same shape as the actionEntries memo directly above it, which was already correct. Capped rather than thresholded on purpose: a minimum-count filter would blank the card on a quiet install.

Reviewer note: users is still deliberately unsorted. It is what pins a person to an avatar color, and the feed indexes into the same array. Sorting it would recolor the feed and give the same person two different colors across the two panels. Both paths now go through a single avatarColor helper — that is the thing to preserve if this code moves.

Verification

tsc --noEmit and eslint clean, vite build succeeds, favicon confirmed to land in dist/. The ranking logic was run against the numbers from the actual broken screenshot and produces the expected order. Rendering checks were done with rsvg-convert at real display sizes on both background colors.

Not visually verified in a running browser — authenticated CMS pages have no login-free path, so that needs a manual look after deploy.

🤖 Generated with Claude Code

ssavutu and others added 2 commits August 5, 2026 00:58
Replaces the raster logo.png wordmark with the SVG artwork, adds the
triangle mark for the collapsed sidebar, and gives the app a favicon that
is actually the product's own logo rather than The Triangle's blue "T".

All three source files ship with the artwork sitting in a 377x347 canvas
that the mark occupies a small band of, so each viewBox is tightened to
the real bounding box. Left alone, object-contain would have scaled the
wordmark to roughly a fifth of its intended size.

Sizing changed with it. The new wordmark is 5.3:1 where the PNG was
2.8:1, so the old fixed w-30 h-30 square rendered it at half the visual
height; it is now height-anchored. max-w-full replaces shrink-0 so it
cannot spill past the 60px rail, which the PNG did.

The collapsed mark is 40px rather than matching the wordmark's 28px
because its two concentric rings merge into one muddy band below ~36px.
Its rings are dropped entirely from the favicon, where 16px leaves no
chance of resolving them.

The favicon deliberately avoids CSS custom properties. A first pass used
var(--mark) and rendering caught that librsvg drops it -- fill fell back
to black and stroke: var() failed outright, silently deleting the
crossbar. Browsers handle it, but favicons pass through enough non-browser
pipelines that plain class rules are the safer choice.

Its base color is brand blue because Safari ignores prefers-color-scheme
in favicons; blue stays legible on both light and dark tab strips, and
the media queries sharpen it to white or navy where they are honored.
Verified the fallback path renders correctly. triangle.ico stays
registered for browsers with no SVG favicon support.

logo.png is left in place for now; it is no longer referenced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The card mapped over `users`, a Set built from the event feed, so it
listed everyone who had ever done anything in whatever order their name
first showed up. In practice that meant an unsorted list with someone
sitting at the bottom on a single event -- it read as broken, because it
was. Each row also recomputed its own count with a full events.filter(),
so the card was O(users * events) for a number the same pass could have
produced once.

topContributors now counts into a Map in one pass, sorts by count
descending with a name tiebreak, and caps at five. It is the same shape
as the actionEntries memo directly above it, which was already doing this
correctly.

Capped rather than thresholded on purpose. A minimum-count filter would
blank the card on a quiet install; a cap drops the single-event case
while still showing everyone when there are only a few contributors.

`users` stays deliberately unsorted, and that is the trap here: it is
what pins a person to an avatar color, and the feed picks its colors from
the same array by index. Sorting it in place would have recolored the
feed and, worse, given the same person two different colors across the
two panels. Both now go through one avatarColor helper.

Also adds the empty state the Actions Breakdown card already had.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ssavutu
ssavutu merged commit b6e1a49 into main Aug 5, 2026
6 checks passed
@ssavutu
ssavutu deleted the housekeeping branch August 5, 2026 05:19
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