Skip to content

chore(ui): prune the unused vendored color tokens - #183

Merged
renefloor merged 5 commits into
feat/update-color-tokensfrom
chore/prune-unused-tokens
Sep 11, 2026
Merged

chore(ui): prune the unused vendored color tokens#183
renefloor merged 5 commits into
feat/update-color-tokensfrom
chore/prune-unused-tokens

Conversation

@renefloor

@renefloor renefloor commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #182review and merge that first; this PR's base is feat/update-color-tokens, so its diff is only the cleanup.

stream_tokens.dart carried 502 constants of which 163 were read. This PR removes the 159 unread color constants (158 in light/, 159 in dark/ — see the asymmetry note below). The dimension constants are deliberately left alone — see the note at the bottom.

The unread constants were unreachable as well as unread: the files sit under internal/, are excluded from the barrel contract, and are imported by exactly two files — stream_colors.dart (light only) and stream_color_scheme.dart (both). Nothing public is touched.

One commit per category, so a bisect stays meaningful:

Commit Dropped Why it was dead
c46b2da 4 backgroundElevationElevation0..3 — unread, and the names no longer exist upstream after the core/chat/video namespace split.
1f12b4b 124 Derived component tokens (badge, button, avatar, control, chat, input, reaction, presence). Dead by architecture: only root semantics are vendored, and a component re-derives its values from a StreamColorScheme field. StreamBadgeNotification reads colorScheme.accentError, not a badgeBgError constant.
c662af3 31 Root semantics whose field resolves through the generated swatch instead (textPrimary ??= chrome.shade900). Verified against the resolved swatch shade in both modes before deleting — redundancy, not a competing value.
38f7062 Docs only: refreshes the two skill claims this prune invalidates.
3a85f80 −5 Adds back five transparent primitives — see below.

Every StreamColors primitive is now token-backed

Review asked whether white30 and black70 should survive the prune. Checking why they had no reader turned up a larger inconsistency: three members of StreamColors were baked hexes, and each exactly equals an upstream token nobody had vendored.

StreamColors value upstream token
white50 0x80FFFFFF baseTransparentWhite50
black50 0x80000000 baseTransparentBlack50
black75 0xBF000000 baseTransparentBlack75

Every other member already read a token, so these three were the baked-hex hazard one layer up, in the public palette. They read their tokens now, and white30 / black70 come back as public members rather than unread constants — the vendored files are internal, so a constant left there unreferenced is not "available to users", it is invisible to everyone. Exposing them is what makes them reachable and keeps them referenced.

Five tokens added to both modes; no baked hexes remain in StreamColors.

Not done: vendoring all 19 transparent primitives upstream publishes. That widens the public API permanently, which is the owner's call rather than part of a cleanup.

Also fixes a light/dark asymmetry

dark/ carried borderUtilityFocus (0x4078A8FF) with no counterpart in light/ — 501 light vs 502 dark. Upstream has only border/utility/**focused** ({brand.150}), which generates #C3D9FF light / #19418D dark, exactly what borderFocus ??= brand.shade150 already resolves to. So it was a stale name carrying a stale value, and nothing read it.

Both files now declare 348 identical constants. That symmetry is what lets the check:tokens guard in the follow-up PR pass.

Verification

analyze is the load-bearing check here: these are static const members reached only through a prefixed import, so a deleted-but-used constant is an undefined_getter compile error, not a lint. It passes across all four packages, and --ignore="*example*" does not exclude the gallery, so that is analyzed too.

The deletion is exactly minimal rather than approximate: 168 color constants remain and all 168 have a reader. No deleted name has a reference anywhere in the repo — including apps/, scripts/, tests, and the tracked generated output.

format:verify and check:barrels pass. flutter test is +397 -56, but that is a null result rather than evidence: no test, golden, or source file changes, so the count could not have moved. The 56 are the pre-existing macOS-golden drift.

Note if you grep this yourself: a bare-name search for accentPrimary or textPrimary returns ~1100 hits, because those are also StreamColorScheme field names. Every hit is colorScheme.X; the only reference form for a token is StreamTokens.X.

Scope: colors only

The "every constant has a reader" property now holds for colors. 180 dimension constants (size*, space*, radius*, font*, lineHeight*, typography*, and the component dimension groups) remain unread, deliberately.

An earlier revision of this branch deleted those too, on the grounds that the package does not sync dimensions from upstream. That was wrong — those values mirror upstream exactly, ordering included, and typography specifically comes from the web flavor, the only one carrying the Geist family this package ships.

They are unreferenced for a different reason: StreamSpacing, StreamRadius and StreamTokensTypography hard-code the same numbers instead of reading the constants. That is the swatch-versus-baked-hex hazard one layer up — nothing ties the class to the constant it mirrors, so the two can drift silently.

Deleting them would remove the evidence of that gap rather than the gap, so it is handled separately by wiring those classes to the constants. The check_tokens.dart guard that fails CI on an unreferenced token moved to that PR too, since it can only pass once the dimensions are wired.

This also resolves an apparent tension in the skill: "do not vendor button/*" governs button colors, which this PR drops, while the dimensions carve-out keeps buttonPaddingX* and friends.

No changelog entry

Deliberate. The policy is conditional on changing package behavior, and nothing read these constants. StreamTokens is internal — private constructor, under internal/, listed in check_barrels.yaml's internal_dirs, and exported from no barrel — so the removals are outside the deprecation policy too.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: e87065f5-b37a-4154-8d92-2757e324039c


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.

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.57%. Comparing base (b71e4ff) to head (90d3b2e).

Additional details and impacted files
@@                    Coverage Diff                    @@
##           feat/update-color-tokens     #183   +/-   ##
=========================================================
  Coverage                     68.57%   68.57%           
=========================================================
  Files                           210      210           
  Lines                          8593     8593           
=========================================================
  Hits                           5893     5893           
  Misses                         2700     2700           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@renefloor
renefloor force-pushed the chore/prune-unused-tokens branch from 18f6421 to f735efa Compare September 10, 2026 12:00
@renefloor renefloor changed the title chore(ui): prune the unused vendored design tokens, and guard against regrowth chore(ui): prune the unused vendored color tokens Sep 10, 2026
@renefloor
renefloor added this pull request to stack #185 September 10, 2026 12:29
@renefloor
renefloor force-pushed the chore/prune-unused-tokens branch 3 times, most recently from b1c76eb to c662af3 Compare September 10, 2026 14:39
@renefloor
renefloor marked this pull request as ready for review September 11, 2026 08:24
@renefloor
renefloor requested a review from a team as a code owner September 11, 2026 08:24
static const baseTransparent0 = Color(0x00FFFFFF);
static const baseTransparentWhite10 = Color(0x1AFFFFFF);
static const baseTransparentWhite20 = Color(0x33FFFFFF);
static const baseTransparentWhite30 = Color(0x4DFFFFFF);

@xsahil03x xsahil03x Sep 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we not want white 30 and black 70 in stream colors? I think its good to have the primitive colors even if we are not using them in themes. Or maybe better to only have these and remove the different ones from StreamColors

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and the second half of it turned out to be the bigger one.

Agreed on keeping the primitives — but the token file is the wrong home for that. internal/tokens/ is excluded from the barrels and the class has a private constructor, so a constant left there unreferenced is not "available even though themes don't use it", it is unreachable by anyone. StreamColors is the public primitive palette, so that is where a primitive has to surface. Added white30 and black70 there, which also makes them referenced, so check:tokens stays honest without an allowlist entry.

On "remove the different ones" — they are not different, which is what makes this worth doing. All three baked hexes in StreamColors match an upstream token exactly:

member value upstream token
white50 0x80FFFFFF baseTransparentWhite50
black50 0x80000000 baseTransparentBlack50
black75 0xBF000000 baseTransparentBlack75

Every other member of the class already read a token. So these three were the same baked-hex-instead-of-a-token hazard the skill warns about for component defaults, one layer up in the public palette — the values happened to be right, with nothing keeping them right. They read their tokens now.

Net: five tokens added to both modes, StreamColors has no Color(0x…) literals left, and light/ and dark/ stay in agreement at 348 each.

One thing I did not do: vendor all 19 transparent primitives upstream publishes. That widens the public API permanently and felt like your call rather than something to fold into a prune — happy to add the rest if you want the full set.

renefloor and others added 5 commits September 11, 2026 13:08
backgroundElevationElevation0..3 are unread, and the names no longer
exist upstream — the core/chat/video namespace split renamed them to
background/core/elevation-N, which generates as
backgroundCoreElevation0..3.

Carrying an unread constant under a name the source of truth has
retired is worse than not carrying it: it reads as the current name to
anyone checking whether a token is vendored. The colorScheme fields
that mirror them (backgroundElevation0..3) resolve through chrome[0]
and are unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
124 unread color constants for badge, button, avatar, control, chat,
input, reaction and presence components. They are dead by
architecture, not by neglect: this package vendors only the root
semantics, and a component's values are re-derived from those in its
own defaults. StreamBadgeNotification reads
colorScheme.accentError rather than a badgeBgError constant, so
upstream's derived tokens have no counterpart here on purpose.

Keeping them invited the opposite reading — that a component should
reach for a token constant — which is the one thing a component must
not do, since a constant bypasses the seedable color scheme and a
custom brand or chrome would stop applying.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
31 unread constants whose StreamColorScheme field resolves through the
generated brand or chrome swatch instead — textPrimary through
chrome.shade900, accentPrimary through brand.shade500,
backgroundCoreApp through chrome[0].

That indirection is deliberate: the swatches are regenerated from a
seed color, so resolving a field through the swatch is what lets
StreamColorScheme.light(brand: ...) actually repaint the SDK, where a
baked hex would silently ignore it. The constant is therefore
redundant by construction, and the field is the single source.

Verified before deleting that this is redundancy and not a competing
value: all 29 mechanically resolvable pairs hold the same color as the
swatch shade their field reads, in both light and dark.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The sync-state note named `backgroundElevationElevation0` and `avatarPaletteBg1`
as constants that survive only here, and said `light/` and `dark/` do not hold
the same set. This PR deletes the first two and makes the sets identical, so
both claims are now false.

The relationship they described is also simpler than it was: with the unread
constants gone, the vendored set is a strict subset of upstream — every name
here exists upstream under the same spelling. That is worth stating, since it
is the property a future sync can actually check.

The naming table had the same problem one layer down: it taught the group-segment
transform using `borderUtilityWarning` and `backgroundCoreOnAccent`, both deleted
here. Swapped for `borderUtilitySelected` and `backgroundCoreHighlight`, which
survive precisely because a field reads them — so the table now demonstrates the
rule it sits next to rather than contradicting it.

Also two American-English fixes: `recognise` (mine, from the previous PR) and
`Multicolour`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review asked whether `white30` and `black70` should survive the prune. Looking
at why they had no reader turned up a larger inconsistency: three members of
`StreamColors` were baked hexes, and each one exactly equals an upstream token
nobody had vendored.

  white50  0x80FFFFFF  = baseTransparentWhite50
  black50  0x80000000  = baseTransparentBlack50
  black75  0xBF000000  = baseTransparentBlack75

Every other member of that class already reads a token, so these three were the
baked-hex hazard the skill warns about, sitting in the public palette. They now
read their tokens.

`white30` and `black70` come back for the same reason, as public members rather
than as unread constants — the vendored files are internal, so a constant left
there unreferenced is not "available to users", it is invisible to everyone.
Exposing them through `StreamColors` is what makes them reachable, and it keeps
them referenced, so the check stays honest without an allowlist entry.

Five tokens added to both modes, ordered with their neighbours. No baked hexes
remain in `StreamColors`; light/ and dark/ stay in agreement at 348 each.

Not done: vendoring all 19 transparent primitives upstream publishes. That
widens the public API permanently, which is the owner's call rather than
something to fold into a cleanup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@renefloor
renefloor force-pushed the chore/prune-unused-tokens branch from 3a85f80 to 90d3b2e Compare September 11, 2026 11:09
@renefloor
renefloor merged commit 7043b27 into main Sep 11, 2026
16 of 18 checks passed
@renefloor
renefloor deleted the chore/prune-unused-tokens branch September 11, 2026 11:15
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.

2 participants