feat: consume brand 1.1 canonical palette and expose its named themes#175
Conversation
🤖 AI Code Review (ensemble)SummaryThis PR bumps @tangle-network/brand from 1.0.0 to 1.1.0 (including peer dependencies) and imports the brand's named themes stylesheet into the library's global CSS. The code changes are entirely limited to dependency metadata, lockfile resolution, and a stylesheet import, with no new runtime logic, API changes, or violations of existing repository patterns. Issues Found1 total — 0 P1 (blocking) · 0 P2 (should fix) · 1 P3 (nice to have) ℹ️ P3 — Comment lacks available theme names
✅ APPROVEThe change is a low-risk dependency bump and stylesheet composition. Reviewer A's P2 findings are discounted because the diff shows the Quick Reference
Synthesized by Sokuza AI from multiple independent reviewers |
tangletools
left a comment
There was a problem hiding this comment.
🟡 Value Audit — sound-with-nits
| Verdict | sound-with-nits |
| Concerns | 2 (1 medium-concern, 1 weak-concern) |
| Heuristic | 0.0s |
| Duplication | 0.0s |
| Interrogation | 293.2s (2 bridge agents) |
| Total | 293.2s |
💰 Value — sound-with-nits
Adds a one-line brand CSS import + bumps the brand floor so named themes resolve through sandbox-ui's composed globals.css; correct and in-grain, but the package's own useBrandThemeSync hook will clobber any data-theme="" an app sets, so the opt-in path this enables is unreachable through the
- What it does: Two changes: (1) bumps @tangle-network/brand peer + dev floor from ^1.0.0 to ^1.1.0 in package.json (lines 126, 181), and (2) adds
@import "@tangle-network/brand/styles/named-themes.css";to src/styles/globals.css:21, right after the existing tokens.css import. At build time, scripts/copy-styles.mjs:37-44 already resolves bare-specifier @imports via Node package-exports and inlines them through - Goals it achieves: Make a consuming app that sets
data-theme="<name>"(e.g. brand 1.1'sintelligence) resolve through sandbox-ui's single composed stylesheet, instead of the app having to reach into@tangle-network/brand/styles/named-themes.cssfor a second CSS file. Secondary: raise the brand floor so a consumer resolving brand 1.0.0 can't end up with an @import that points at a non-existent export (named-the - Assessment: The change is correct, minimal, and squarely in the grain of the existing tokens.css pattern — same import location, same resolution mechanism in copy-styles.mjs, same single-source-of-truth rationale documented in CLAUDE.md:62. The peer-floor bump is justified: the new @import would fail to resolve against brand 1.0.0. The validate-built-css.mjs guard still passes (it only blocks URL @imports and
- Better / existing approach: none for the CSS layer — extending the existing tokens.css import pattern is the right approach. BUT the package's own useBrandThemeSync hook (src/dashboard/use-brand-theme-sync.ts:24-31) unconditionally force-writes data-theme to 'dark' or 'light' on mount and on every class mutation, and is mounted by both DashboardLayout (dashboard-layout.tsx:218) and SidebarLayout (sidebar-layout.tsx:225). Its
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 2
- Bridge warning: opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content
🎯 Usefulness — sound
A 2-line in-grain change that inlines brand 1.1's named-theme scopes into the composed CSS entry so a consuming app's data-theme="<name>" actually resolves; no better approach, no competing pattern.
- Integration: Reachable and correctly wired. The new
@import "@tangle-network/brand/styles/named-themes.css"(src/styles/globals.css:21) sits directly beneath the identical existing@import "@tangle-network/brand/styles/tokens.css"(line 16) and is resolved by the same mechanism: scripts/copy-styles.mjs:37-44 routes bare specifiers through Node's package-exports resolver, then postcss-import inlines the fil - Fit with existing patterns: Follows the established pattern exactly. tokens.css was already imported the same way for the same reason (single composed entry point so apps don't reach into brand for a second stylesheet). Positioning is also correct: after tokens.css (named-themes overrides spine tokens declared there) and before
@import "tailwindcss"(so Tailwind's utility scan sees the themed values). The named-themes.css - Real-world viability: Holds up. The @import is inlined at build time (no runtime resolution risk), the source package's exports map is the only dependency, and the validator (scripts/validate-built-css.mjs) blocks any URL @import leak that would break downstream chain-imports — so the failure mode that motivated this whole pipeline (CHANGELOG 0.10.9) stays guarded. One real edge worth a heads-up: src/dashboard/use-bran
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 1
💰 Value Audit
🟠 Named themes exposed in CSS are clobbered by this package's own useBrandThemeSync hook [maintenance] ``
src/dashboard/use-brand-theme-sync.ts:29-30 computes
next = dark ? 'dark' : 'light'and writes data-theme=next unconditionally; src/dashboard/dashboard-layout.tsx:218 and src/dashboard/sidebar-layout.tsx:225 mount this hook in every dashboard/sidebar consumer. So an app that sets data-theme='intelligence' (the opt-in path this PR exists to enable) will have it overwritten to data-theme='dark' on mount and on every subsequent light/dark toggle. The PR body's stated win — 'without this, an opt-i
🎯 Usefulness Audit
🟡 useBrandThemeSync will clobber a named data-theme in dashboard-mounted apps [integration] ``
src/dashboard/use-brand-theme-sync.ts:30 writes
data-themeto 'dark' or 'light' based solely on the.darkclass, and is mounted unconditionally by DashboardLayout (dashboard-layout.tsx:218) and SidebarLayout (sidebar-layout.tsx:225). An app that opts into a brand named theme via<html data-theme="intelligence">AND mounts either layout will have its theme reset to the neutral spine on mount. Not a defect of this CSS change — the surface it adds is reachable and correct — but the dashboard
What this audit checks
It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.
| Pass | What it asks |
|---|---|
| Heuristic | Vague title? Whitespace-only or cruft-bearing diff? (content signals only) |
| Duplication | Do added function/class names already exist elsewhere in the repo? |
| Value Audit | What does it do? What goal does it achieve? Is it good? Better architecture or already-exists? |
| Usefulness Audit | Does it integrate and fit? Will it hold up in real use and actually get used? |
Findings are concerns, not blocks — the human reviewer decides what to do with them.
✅ No Blockers —
|
tangletools
left a comment
There was a problem hiding this comment.
✅ Approved — 3 non-blocking findings — 6d85b825
Full multi-shot audit completed 3/3 planned shots over 3 changed files. Global verifier still owns final merge decision.
Full immutable report for this review: trace
Summary comment for this run: full summary
tangletools · 2026-07-14T05:38:16Z · immutable trace
Why
@tangle-network/brand@1.1.0ships the canonical Tangle palette — an indigo-cast surface ladder replacing the flat neutral spine (tangle-network/brand#74). This package is what actually delivers it:dist/globals.cssinlines brand's tokens at build time, so a brand release changes nothing for any app until sandbox-ui is rebuilt against it.Change
^1.1.0(devDependency and peerDependency). The floor matters:^1.0.0would let a consumer resolve 1.0.0, which has neither the canonical palette nor the./styles/named-themes.cssexport the@importbelow depends on.globals.cssimports brand's named themes, so an app that opts intodata-theme="<name>"resolves it through this one composed entry point instead of reaching into brand for a second stylesheet. Without this, an opt-in silently resolves to nothing — the app keeps the base spine and the theme simply never applies.0.79.0.Verified in the built output
The point of this PR is what lands in
dist/globals.css, so that is what was checked:--hsl-background: 240 33% 6%(was the flat240 7% 9%)--hsl-background: 223 23% 94%— white paper on a tinted canvas, not white-on-white.dark[data-theme="intelligence"]present and reachabledist/tokens.cssbyte-identical to brand 1.1.0'stokens.csstypecheck clean; build clean; the built-CSS validator passes.
Known state
src/dashboard/sidebar-collapse.test.tsxfails (11 tests) — pre-existing onmain, verified by running it on a clean checkout ofmainwith identical results. Untouched by this PR, which changes only CSS imports and the manifest.Downstream
Merging this publishes
0.79.0, which unblocks tangle-network/agent-dev-container#3564 (the app-side unification: platform, sandbox and intelligence stop hand-writing palettes and inherit brand's).