Skip to content

feat(brand): add --shadow-overlay, the theme-inverting lift for covering surfaces - #90

Merged
Tjemmmic merged 4 commits into
mainfrom
feat/shadow-overlay-token
Aug 12, 2026
Merged

feat(brand): add --shadow-overlay, the theme-inverting lift for covering surfaces#90
Tjemmmic merged 4 commits into
mainfrom
feat/shadow-overlay-token

Conversation

@Tjemmmic

Copy link
Copy Markdown
Contributor

Problem

@tangle-network/agent-app ships React surfaces that reach for a --shadow-overlay token — AssistantDock (the floating dock and its drawer) and web-react's exported OVERLAY_SHADOW constant. That token is defined only in agent-app's own theme/tokens.css, and the apps that render those components do not import that stylesheet: they compose the design system through @tangle-network/sandbox-ui/globals.css, which re-ships brand's tokens.

The result is that the assistant dock renders with no elevation at all in products/platform/web (14 imports of agent-app/assistant) and products/sandbox/web (3). A drawer that covers the page reads as flat against it.

There are two failure modes behind that, not one. shadow-[var(--shadow-overlay)] resolves to nothing because the variable is undefined, and the bare shadow-overlay utility does not exist at all, because those apps do not consume agent-app's Tailwind preset.

Solution

Add --shadow-overlay to brand: the elevation step above --shadow-dropdown, for a surface that covers the page rather than sitting beside it — a drawer, a modal, a floating dock. Two layers, a tight contact shadow plus a wide ambient one.

It is declared in tokens.css and in the @theme block, so Tailwind emits a shadow-overlay utility as well as the variable. That closes both failure modes with one token.

Alone among brand's shadows it derives from --hsl-foreground rather than a fixed rgba, so it inverts with the theme. That is what makes it work in dark, where a black shadow on a dark canvas renders as nothing — the same observation already recorded at the top of tokens.css. Because the ramp does the inverting, one declaration serves both themes and the light block does not restate it:

  • dark → rgba(227, 230, 237, 0.1) / 0.16 — a soft halo
  • light → rgba(43, 44, 62, 0.1) / 0.16 — dark ink

The alpha pair was chosen by rendering three candidates against brand's own surfaces rather than porting agent-app's numbers, which are tuned to a different ramp. The conservative candidate barely separated the drawer from the page; the strong one read as a glow rather than elevation.

Validation

  • 224 tests pass across 20 files
  • The token resolves and inverts as shown above, confirmed by reading computed style in a browser for both themes
  • Verified through the full consumer chain rather than in isolation: rebuilt sandbox-ui against this change (its validate-built-css gates pass) so dist/globals.css carries the token, rebuilt products/platform/web against that, and confirmed the built app stylesheet contains --shadow-overlay: 0 2px 6px hsl(var(--hsl-foreground) / .1), 0 16px 40px hsl(var(--hsl-foreground) / .16)
  • Rendered AssistantDock's literal class strings against that built stylesheet, before and after, in both themes: the drawer edge gains a halo in dark and a soft lift in light

Note for whoever picks up the rollout: sandbox-ui/dist/globals.css inlines brand's tokens at build time, so this reaches apps only after sandbox-ui rebuilds and publishes against the released brand.

@changeset-bot

changeset-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8be0e7d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@tangle-network/brand Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Tjemmmic

This comment has been minimized.

@Tjemmmic

This comment has been minimized.

@Tjemmmic

Tjemmmic commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

🤖 AI Code Review (ensemble)

Resolution (8be0e7d): 1 addressed · 1 not an issue.

Summary

The PR adds a --shadow-overlay token derived from --hsl-foreground that inverts automatically with the theme, correctly duplicated in both tokens.css (for runtime) and theme.css (for Tailwind utility emission). The CSS implementation is solid, but the newly added parity test suite has critical coverage gaps: the .light block is parsed but never guarded for content or directly asserted against, allowing regressions in that block to pass silently.

Issues Found

2 total — 1 P1 (blocking) · 0 P2 (should fix) · 1 P3 (nice to have)

❗ P1 — Parity test suite never guards .light block contentAddressed

Note: Guard added in 8be0e7d: expect(light.size).toBeGreaterThan(5) sits beside the existing count check, so the absence assertions can no longer hold against an empty map.

One correction to the mechanism, since it matters for anyone reading this later: a selector rename would not pass vacuously — blockIn throws missing theme block when nothing matches, so that case fails loudly at load. Verified by pointing the helper at .light-renamed, which aborts the file rather than running it. The risk that IS real is the one blocksIn documents itself — one selector can head more than one rule, so a first-match read can slice the wrong region and yield a small, non-empty map. That is what the guard catches. Mutation-checked by substituting an empty map: × finds the self-inverting tokens to check — expected 0 to be greater than 5.

  • File: packages/brand/src/styles/theme-parity.test.ts:29-46
  • Problem: The test suite parses the .light spine (line 30) but only checks shared.length > 5 for the .dark spine. If a selector rename or parsing issue empties the .light block, the self-inverting tests will pass vacuously (as light.has(token) returns false on an empty map). The contract that tokens.css and theme.css agree is only explicitly verified against the .dark runtime values.
  • Fix: Add an explicit content guard for the .light map alongside the existing shared.length check, e.g.: expect(light.size).toBeGreaterThan(5);.

ℹ️ P3 — Changeset omits mention of new test suiteNot an issue

Note: The changeset feeds packages/brand/CHANGELOG.md, which is consumer-facing. Every entry there describes a token or design change and its rationale — the 1.4.0 entry covers the dark ladder re-spacing, a contrast fix and four new @tangle-network/ui primitives; 1.3.0 the missing named theme; 1.2.0 the spine retune and its upgrade note. None mentions a test suite, because a test is not something a consumer of the published package can observe. Adding one here would break that convention rather than follow it.

  • File: .changeset/shadow-overlay-token.md:7
  • Problem: The changeset accurately describes the new token and its dual declarations but fails to mention the addition of the theme-parity.test.ts suite. Reviewers and consumers tracking test coverage via changesets will miss this structural addition.
  • Fix: Append a brief note to the changeset, such as: 'A theme-parity test suite guards the duplication so a future edit to one file cannot drift from the other.'

❌ CHANGES REQUESTEDResolved — no blocking concerns remain

Status: the P1 guard is in (8be0e7d); the P3 is a convention mismatch with the published changelog. Nothing blocking remains.

The CSS additions are flawless, but the new test suite introduced to guard them has a structural blind spot. By asserting the .light block's contents without verifying the block actually yielded declarations, the suite could pass during a future regression that breaks .light parsing entirely. Adding a simple light.size guard resolves the issue and ensures the test actually validates its intended contract.

Quick Reference

  • P1: Parity test suite never guards .light block content
  • P3: Changeset omits mention of new test suite

Synthesized by Sokuza AI from multiple independent reviewers

@Tjemmmic

Copy link
Copy Markdown
Contributor Author

🤖 AI Code Review (ensemble)

Summary

The PR introduces a --shadow-overlay CSS token and corresponding Tailwind @theme entry, with a comprehensive parity test to ensure consistency between tokens.css and theme.css. The implementation is clean and the architectural choice to derive the shadow from --hsl-foreground for theme inversion is well-documented and clever.

No Issues Found

✅ APPROVE

The changes are functionally sound, well-reasoned, and include excellent guardrails. The new --shadow-overlay token correctly leverages --hsl-foreground to invert between light and dark themes, and the accompanying test file ensures that tokens.css and theme.css remain in sync. The code comments clearly explain the rationale, and the changeset is present.


Synthesized by Sokuza AI from multiple independent reviewers

@Tjemmmic
Tjemmmic marked this pull request as ready for review August 12, 2026 23:06
@Tjemmmic
Tjemmmic merged commit 532a6d8 into main Aug 12, 2026
1 check passed
@tangletools tangletools mentioned this pull request Aug 12, 2026
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