Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(web): seal the seam between attached banners and the composer by vitalyiegorov · Pull Request #8551 · pingdotgg/t3code · GitHub
Skip to content

fix(web): seal the seam between attached banners and the composer - #8551

Closed
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam
Closed

fix(web): seal the seam between attached banners and the composer#8551
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/composer-banner-seam

Conversation

@vitalyiegorov

@vitalyiegorovvitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes#8546.

What Changed

At the join between an attached banner and the composer, a row one device pixel tall was painted by neither surface, so the chat behind it showed through crisp and unblurred. The drawer's mask cut-off now bleeds one pixel past the seam, so the drawer paints that row itself.

One rule, one file. Stacked banners get the same repair for free, since they share the mask.

Why

The lost row is the drawer's own last masked row — measured at DPR 1.25 it sits at device offset −1.00 relative to the composer's top edge. Chromium drops the final device-pixel row of a filtered backdrop when the mask cut-off lands off the pixel grid, and the composer's surface begins exactly at that cut-off, so nothing covers it.

That rules out anything that paints at offset ≥ 0. Measured over 224 configurations (7 device pixel ratios × 8 sub-pixel phases × ancestor transform × light/dark):

approachleaking configs
main today48
border-top, or box-shadow: inset 0 1px 0, on the composer48 — paints below the lost row
a strip built from the same tokens + backdrop-filter30 — its own top row is dropped the same way
mask bleed 1px0

This finishes #8083, which fixed the same symptom on this same mask by dropping before:mask-none so the overlap is masked again. One device row survived that fix — the cut-off row itself — and that is what this closes.

The - 1px is absolute rather than scaled with the 1rem in --chat-composer-attachment-overlap: it repairs a device-pixel rounding artefact, not a layout distance, so it must not grow with interface font size. 2px is no better (also 0 leaks), so 1px is the minimum.

Cost: surface parity is ≤7/255 worst case (dark, --glass-opacity: 40%), ≤4 at the 80% default, 0 at 100%. Rows above the seam are bit-identical.

UI Changes

Reported on the macOS desktop app — a line of chat text visible through the seam:

Resume with less context banner with chat text showing through the seam above the composer

Re-verified against main @ 053affbed, 34 commits after this branch's base: the fix cherry-picks with no conflict and every selector it relies on is unchanged. Measured in the running app at DPR 2, dark, with a real attached banner and high-contrast content behind the seam, toggling only the mask cut-off on one page instance:

device rowbeforeafter
14mean 50.5 / peak 77mean 38.0 / peak 44
15mean 50.7 / peak 77mean 38.0 / peak 44
every other rowbyte-identical

The mean-50.5-against-peak-77 spread is the artifact: bright content punching through. Afterwards those rows read flat. Exactly two device rows change, which is the 1 CSS px at DPR 2.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes — not applicable, nothing moves

Built with Claude Fable 5 and Claude Opus 5 in the Claude Code harness.

🤖 Generated with Claude Code


Note

Low Risk
Single-file CSS mask tweak for composer chrome; no logic, data, or security surface changes.

Overview
Fixes a 1 device-pixel leak at the join between attached chat banners (drawers) and the composer, where unblurred chat content could show through because neither the masked glass pseudo-element nor the composer surface painted that row.

The attached drawer ::before mask cut-off now uses calc(var(--chat-composer-attachment-overlap) - 1px) instead of the overlap value directly, so the masked backdrop bleeds 1px past the seam and Chromium still paints the last filtered row. A short comment documents the browser rounding behavior. Stacked banners pick up the same fix via the shared selector in index.css.

Reviewed by Cursor Bugbot for commit 7a8c97e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix 1px gap at composer-attachment seam in backdrop mask

Shifts the -webkit-mask-image and mask-image linear-gradient cut-offs by 1px using calc(var(--chat-composer-attachment-overlap) - 1px) in index.css. This creates a 1px bleed so the backdrop covers a device-pixel row that Chromium was leaving transparent at the seam.

Macroscope summarized 7a8c97e.

@coderabbitai

coderabbitaiBot commented Aug 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94dd926e-6bf2-4e07-8c9b-7d4fb061ca95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 28, 2026
@macroscopeapp

macroscopeappBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a narrowly scoped one-file CSS seam fix, but the 1px bleed changes production rendering where configurable glass opacity and alert tint variants may differ across the overlap. The unresolved visual-consistency concern needs human validation across those states.

No code changes detected at 7a8c97e. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new attachment-seal strip: its fill does not follow the same surface composition as the drawer it seals, so the sealed row can read as a band rather than a continuation of the drawer. Geometry, the shoulder-tab exclusion, and the --chat-composer-drawer-inset inheritance (via [data-chat-composer-form="true"]) all check out.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
Comment on lines +1137 to +1142
background:
linear-gradient(
var(--chat-composer-attachment-seal-shade),
var(--chat-composer-attachment-seal-shade)
),
var(--chat-composer-glass-surface);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The strip paints the glass surface fully opaque and untinted, but the surface it is sealing is color-mix(in srgb, <surface> var(--glass-opacity), transparent) plus --chat-composer-attached-tint. Since bottom: calc(100% - 1px) puts 2 of the 3px on top of the drawer's own surface, the mismatch is visible there:

  • --glass-opacity is a runtime user setting (40-100%, default 80; routes/__root.tsx:171). Every other composer/drawer surface derives from it, and the only other opaque paints of --chat-composer-glass-surface are inside @supports not (backdrop-filter) fallbacks. Below 100% the seal reads as a solid strip over translucent glass.
  • The tint is dropped: the internal top drawer always sets data-variant="info"|"warning" (ChatComposer.tsx:2979) and banner-stack alerts set error/warning/success, each of which layers --chat-composer-attached-tint onto the drawer surface.

Consider composing the strip from the same tokens - the glass-opacity mix plus the drawer tint - and giving it its own backdrop-filter, so any row it loses at its own top edge lands on the drawer's already-blurred surface, keeping the opaque paint only for the @supports not fallback. The tint currently lives on the drawer element, so it needs hoisting to the shell (next to --chat-composer-attachment-seal-shade) for the strip to read it.

- var(--chat-composer-glass-surface);+ color-mix(in srgb, var(--chat-composer-glass-surface) var(--glass-opacity), transparent);+ -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));+ backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturation));

Posted via Macroscope — UI Consistency

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 69cbd4c to cb2663dCompareAugust 28, 2026 15:01
@macroscopeapp
macroscopeappBot dismissed their stale reviewAugust 28, 2026 15:01

Dismissing prior approval to re-evaluate cb2663d

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the attachment seal: the repainted strip does not reproduce the glass composition of the surfaces it covers, so it becomes visible as an opaque band under non-default glass opacity and on tinted banner variants.

Posted via Macroscope — UI Consistency

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from cb2663d to 36fe1b9CompareAugust 28, 2026 15:07
@github-actionsgithub-actionsBot added size:S 10-29 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 28, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36fe1b9. Configure here.

Comment threadapps/web/src/index.css Outdated
@vitalyiegorov

vitalyiegorov commented Aug 28, 2026

Copy link
Copy Markdown
ContributorAuthor

Verified in the real app, not just the harness — same build, dark theme, DPR 2, with the actual ComposerBannerStack attached above the real composer and bright content behind it. The only difference between the two frames is the seal being disabled and enabled via an injected style; same page instance, same DOM, same clip box, no reload.

Per-row brightness across the clip (1448 device px wide), rows relative to the seam:

device rowbefore mean / peakafter mean / peak
0–5identicalidentical
621.1 / 6820.1 / 68
724.4 / 7320.1 / 73
838.6 / 7620.1 / 76
939.2 / 7020.1 / 68
1042.1 / 7720.0 / 29
1141.4 / 7720.0 / 29
12–19identicalidentical

Rows 10–11 are the leak: peak 77 against a ~60–68 surround, carrying crisp glyph tops. After the fix they read a flat 29. Exactly six device rows change — the 3 CSS px strip — and every row outside it is identical, so nothing else moved.

Real app before and after at 6x magnification: the leaking row at the seam disappears with the fix

Every attached banner hits this seam: .chat-composer-drawer-surface is what ComposerBannerStack renders, and externalComposerDrawerAttached is composerBannerItems.length > 0.

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 36fe1b9 to b6a13d1CompareAugust 28, 2026 17:10
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

On the open visual-consistency question — configurable glass opacity and the alert tint variants across the overlap.

The bleed does not introduce a new surface. It extends the drawer's own masked surface one CSS px further down, into a strip the composer's surface already covers, so the two overlap there instead of leaving a row painted by neither. That means the only thing that can differ is a double composite of two semi-transparent surfaces over one CSS px.

Measured every state that can vary across the overlap — glass opacity 100 / 80 (default) / 40 %, light and dark, and the default / info / warning / error banner tints:

glass opacityworst-case per-channel delta in the seam strip
100 %0 / 255
80 % (default)≤ 4 / 255
40 % (minimum)≤ 7 / 255

Rows above the seam are bit-identical in every combination. Only the two device rows at the seam change, and there they go from showing chat text through the gap to the surface colour. The residual at 40 % is the expected double composite, and it lands strictly closer to the intended surface colour than the leak it replaces.

Worth stating plainly: this gives back 1 px of the 17 px the overlap lost in #8083. That is the cost of the fix, and I did not find a way to seal the seam without it — a border, an inset shadow, and a token-matched strip all still leaked, and an outset shadow sealed it but broke surface parity far worse (178–222 / 255).

@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from b6a13d1 to 2497405CompareAugust 29, 2026 16:38
A row about one device pixel tall at the join between an attached
banner and the composer was painted by neither surface, so the chat
behind it showed through crisp and unblurred.
The row is the drawer's own last masked row. Chromium drops the final
device-pixel row of a filtered backdrop when the mask cut-off lands off
the pixel grid, and the composer's surface begins exactly at that
cut-off, so nothing covers it. Bleeding the cut-off one pixel past the
seam lets the drawer paint that row itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorovforce-pushed the fix/composer-banner-seam branch from 2497405 to 7a8c97eCompareAugust 30, 2026 17:13
@vitalyiegorov

Copy link
Copy Markdown
ContributorAuthor

Closing — no longer reproducible on recent nightlies. If the seam leak resurfaces, the root-cause analysis and per-DPR measurements in this thread should spare the next person the diagnosis.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat text shows through the seam between an attached composer banner and the composer

1 participant

@vitalyiegorov