Repository navigation
UI Consistency: 1 issue found
One finding.
apps/web/src/index.css(contrast role block, commented at L1909) /apps/web/src/browser/annotationTheme.ts— the desktop preview annotation overlay still snapshots the raw semantic roles (--foreground,--popover-foreground,--muted-foreground,--accent-foreground,--border,--input) and ships them to the guest webview, where they style the annotation toolbar, editor popover, labels, and outlines. That app chrome therefore stays on stock contrast while the rest of the interface follows the new setting, which is the adjusted/unadjusted split this check asks to be audited. Suggested fix: read the--contrast-*roles for those six fields (surface roles unchanged); the IPC schema accepts the substitutedcolor-mix()strings, so no contract change is required.
Everything else checked out: the Tailwind bridge, global chrome rules, markdown chrome, sidebar palette, toolbar roles, and the imperative context-menu fallback all read the shared --contrast-* custom properties; filter is no longer used on the app root; and the border/input derivations soften toward transparent with a modest foreground mix for stronger borders, so translucent hairlines keep their alpha. Code and terminal roles are deliberately left unadjusted (user media), which matches the guidance.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Methodology
- Confirmed prior findings were addressed at head: the root
filterapproach is gone in favor of--contrast-*custom properties;--toolbar-foreground/--toolbar-border/--toolbar-control-foregroundnow have adjusted variants consumed by thehtml[data-theme-id]chrome rules; the t3-chat inline-code override reads--contrast-message-foreground; the theme inspector hover label reads--contrast-popover-foreground; the no-opdata-appearance-contrastattribute was dropped. - Audited literal role reads: grepped
apps/web/src/index.cssand allapps/web/src/**/*.{ts,tsx}forvar(--foreground|border|muted-foreground|popover-foreground|card-foreground|accent-foreground|secondary-foreground|message-foreground|sidebar-*|input|placeholder|secondary-label|icon-muted). Remaining matches inindex.cssare only the token definitions and the derivation block; the only TSX match isFilePreviewPanel.tsx(--code-foregroundfallback, intentionally unadjusted). - Audited imperative/computed-style consumers via
getPropertyValue(and"--role"string reads:ThreadTerminalDrawer.tsxreads only terminal tokens (correctly excluded);browser/annotationTheme.tsreads chrome roles raw (the reported finding). - Cascade check:
--contrast-*are declared on:root, [data-app-sidebar], and the sidebar is the only element scope that redeclares the semantic palette (index.cssL1500-1542, L1836-1861), so derivations resolve against the correct local surfaces.--sidebar-icon-colorkeeps its pre-existing root-scoped resolution. - Alpha check:
--contrast-border/--contrast-input/--contrast-sidebar-borderscale the original translucent token towardtransparentwhen softening and add a(contrast-100)/4 %foreground mix when boosting, so a 6-8% dark-mode hairline no longer inverts into an opaque line. - Not re-reported (already flagged in an earlier run, code unchanged): the third duplicated
settings-slidercall site inSettingsPanels.tsx(ratio math plus--settings-slider-progress/--settings-slider-fill-offsetpair) that would be better owned by a small shared slider primitive.
🔍 Agent activity — 23 tool calls
💭 I want to check the current PR head to see what's changed since my prior comments—it looks like the filter was removed and contrast custom properties with alpha-preserving mixes were introduced as I'd suggested. Now I need to check for any remaining literal reads of variables like
var(--foreground)orvar(--border)in the web source that might still split contrast, starting with index.css at head.
1. git_tools — var(--(foreground|border|muted-foreground|popover-foreground|card-foreground|accent-foreground|secondary-foreground|message-foreground|sidebar-foreground|sidebar-muted-foreground|sidebar-border|input|placeholder|secondary-label|icon-muted|toolbar-foreground|toolbar-border|toolbar-control-foreground))
2. git_tools — 24ad866..d7b49ad
💭 The CSS grep confirms literal role reads are mostly cleaned up in index.css, with toolbar contrast and message foreground overrides now properly consuming contrast tokens. Next I want to check the TSX files in apps/web for remaining direct var(--foreground) or var(--border) reads that weren't updated but could still cause inconsistencies.
3. git_tools — var(--(foreground|border|muted-foreground|popover-foreground|card-foreground|accent-foreground|secondary-foreground|message-foreground|sidebar-foreground|sidebar-muted-foreground|sidebar-border|input|placeholder|secondary-label|icon-muted))
4. git_tools — var(--(foreground|border|muted-foreground|popover-foreground|card-foreground|accent-foreground|secondary-foreground|message-foreground|sidebar-foreground|sidebar-muted-foreground|sidebar-border|input|placeholder|secondary-label|icon-muted))
💭 That fallback is intentional since code/terminal roles aren't meant to adjust for user media. Now I'm reviewing the new CSS sections across several line ranges to verify correctness, including checking whether the sidebar block redeclares the contrast variable under its own selector. I'm tracing whether custom property substitution happens at the point of declaration versus inheritance — since --contrast-* variables reference --foreground/--background computed at :root, any theme block with higher specificity that redefines --foreground locally wouldn't cause those contrast variables to re-derive, since they were already resolved where declared. I need to check whether the higher-specificity theme selectors actually override --foreground too.
5. browse_code — apps/web/src/index.css
6. browse_code — apps/web/src/index.css
💭 I'm checking the contrast block declared for the sidebar element against the earlier token overrides, confirming it resolves using the sidebar's own local foreground and border variables. I also need to compare specificity between the sidebar selector and the root selector since they appear equal, which could affect which rule wins. Since var() resolution happens at computed-value time using whichever declaration actually wins the cascade, the ordering of declarations doesn't matter unless they're truly competing on the same element — so I'm now scanning the theme-mapped sidebar block for any higher-specificity override of foreground variables, then moving on to verify the color-mix logic used for contrast and border boosting. I should check for other scopes that might override these semantic role declarations too, like message-related containers or chat headers, and search the codebase for other
--foreground,--card, and--popoverdeclarations to catch any similar edge cases.
7. git_tools — ^\s*--(foreground|background|card|popover|accent|secondary|muted-foreground|border|input|message-surface|message-foreground|sidebar-foreground|sidebar|toolbar-foreground|toolbar-border|toolbar-control-foreground|card-foreground|popover-foreground|accent-foreground|secondary-foreground|placeholder|secondary-label|icon-muted|sidebar-muted-foreground|sidebar-border):
💭 I'm trying to pin down the exact selector, likely something like
html[data-theme-id] [data-app-sidebar], and need to check the surrounding lines to confirm. I'm also tracing how--sidebar-row-hoverand--contrast-foregroundare scoped between:rootand[data-app-sidebar]to make sure the color-mix resolves against the right value on the same element.
8. browse_code — apps/web/src/index.css
💭 Checking that no remaining code still references the old raw role variables—the grep confirms only definitions remain. Now I'm tracing how
--sidebar-icon-colorat root resolves through--contrast-sidebar-muted-foregroundand--sidebar, since it's only declared at root, meaning sidebar elements inherit that root-level resolution rather than a scoped one. That's consistent with the prior behavior before this change, so no regression there. I'm now checking whether the[data-app-sidebar]contrast redeclaration conflicts with thehtml[data-theme-id]selector's specificity — it doesn't, since contrast variables aren't declared in that block, and I'm also verifying that popover-related contrast variables resolve correctly against themed overrides at root. Beyond that, I want to make sure no remaining tsx code still reads raw role variables directly rather than through the bridge, and confirm the scope stays limited to apps/web. I also need to check whether the new contrast variables are referenced anywhere outside index.css, particularly in contexts like mobile apps or iframes where the root document differs, since those wouldn't inherit the same root-level declarations.
9. git_tools — --contrast-
💭 I'm noticing that button.tsx swaps the icon color variable to reference the contrast muted-foreground token instead of the raw one, which keeps it consistent since the bridge variable now maps to contrast anyway. Now I want to check whether there's an actual bug in how alpha values are handled here. Tracing through the softening and boosting math for the contrast border color, I see softening just scales the original border alpha down proportionally while boosting mixes in a foreground percentage that grows the effective opacity noticeably in dark mode—both seem intentional per the design guidance. I also note that contrast-foreground uses background as its softening anchor, though foreground itself gets used across many surfaces like cards and popovers, but those seem to be handled separately already. Checking related concerns: variable declaration order in the CSS doesn't affect resolution, so the sidebar icon color referencing a later-declared muted foreground variable is fine. I'm also considering whether themes could be light while the dark variant class is applied independently, but that's likely already handled correctly, and the way the appearance contrast values get written as inline styles on the document root means the sidebar block correctly inherits them. I'm now weighing whether accent-colored foregrounds like primary, destructive, and message-action foregrounds should also get contrast adjustment, but these seem intentionally excluded since the guidance only calls out card, popover, accent, secondary, and message foregrounds -- all of which are already covered. Now I'm tracing whether the sidebar row hover/active/selected states in the dark block, which reference the contrast foreground variable, were changed from a plain foreground reference in the diff -- checking where exactly those lines shifted. Both the sidebar's contrast-foreground and its usage are declared on the same element, so it resolves correctly using the sidebar's own value. The trees foreground override in the file browser panel also looks fine. Now I'm considering whether the settings slider duplication I flagged in a previous pass -- where the contrast ratio math and classes are inlined redundantly -- is still present in the current code, since that issue was raised before and doesn't appear to have been addressed since. Since guidance says not to re-flag issues I already raised unless the code changed in direct response, I won't repeat that comment. Instead, I'll check whether a shared SettingsSlider component has since been introduced, and scan other CSS files in the web app for similar foreground/alpha patterns outside the sidebar scope.
10. browse_code — apps/web/src/**/*.css
11. git_tools — settings-slider|--settings-slider-progress|SettingsSlider
💭 I still need to check the settings-slider CSS to confirm the contrast slider's fill-offset behavior works correctly, and verify whether the glass opacity and volume sliders have consistent min/max ranges. Let me look at the SettingsPanels code around lines 960-1110 for the slider implementation details.
12. browse_code — apps/web/src/components/settings/SettingsPanels.tsx
13. git_tools — settings-slider
💭 Everything looks consistent so far, so I'm double-checking the contrast token derivations against their base variables — message foreground against message surface, accent foreground against accent — and now looking at whether secondary label, placeholder, and muted icon contrast tokens might have any inconsistency given they're only declared at root and the sidebar scope. That checks out too. I'm now realizing the important subtlety is that since Tailwind's
@theme inlineinlines the value directly ascolor: var(--contrast-muted-foreground), resolution happens at the actual element and correctly inherits from either root or the sidebar's redeclared values depending on scope — which confirms why redeclaring these on the sidebar matters. I still want to check whether any other component locally overrides--foregroundor--bordervia inline styles or Tailwind arbitrary properties, so I'm going to search more broadly for those patterns without anchoring to line starts.
14. git_tools — [--(foreground|border|card|popover|accent|muted-foreground|input|sidebar-foreground):
15. git_tools — "--(foreground|border|card|popover|accent|muted-foreground|input)"
💭 I'm looking at annotationTheme.ts, which pulls raw CSS variables like
--foreground,--muted-foreground,--border, and--inputfor the browser annotation overlay — this falls under the apps/web/src include pattern, so I need to check whether these role reads stay consistent with the Tailwind bridge and global CSS conventions.
16. browse_code — apps/web/src/browser/annotationTheme.ts
💭 I'm weighing whether this annotation overlay, since it lives in a separate preview document that doesn't inherit the main CSS, should still respect the contrast setting as app chrome. I'm unsure whether reading
--contrast-foregroundvia getComputedStyle would actually resolve to the adjusted color-mix value or just return the raw substituted string, since that behavior differs between registered and unregistered custom properties. Since this check defaults to failure, I want to dig harder for genuine cross-view findings rather than settle for the marginal preview case. I'm now checking whether other places, like a shared UI package, read the raw foreground/role variables directly, and specifically tracing where--control-icon-colorfalls back across components beyond the button variant.
17. git_tools — control-icon-color
💭 I'm checking that the color-mix formula is valid CSS syntax, and confirming that mixing with 0% of black should just return the original color converted through oklab, modulo rounding. Tracing through the alpha handling in color-mix, the border cases with 0% boost, and how the contrast-base variables get set inline by JS versus their CSS defaults — everything seems to be resolving correctly and inheriting properly through the sidebar element. For dark themes, I want to confirm the target variable still tracks correctly since it's derived from the theme's text color and the dark class toggle rather than being set by JS directly, which checks out. The desktop app's settings sync and duplicate CSS are out of scope here, so I'm narrowing back down to searching the web app source for any other imperative style-setting code that might still need splitting out. I've grepped for foreground variable usage across the codebase and found only two candidates: the file preview panel's code-foreground reference and the annotation theme file's string reference. Since annotationTheme.ts is the remaining candidate worth evaluating, I want to trace where its preview theme reader gets consumed to decide if it needs changes.
18. browse_code — In apps/web/src/browser, how is readPreviewAnnotationTheme (annotationTheme.ts) used? Which UI does DesktopPreviewAnnotationTheme style — is it app chrome (annotation toolbar/labels drawn over a desktop preview webview) or user content? Show the consumers and how the theme values are applied (e.g. injected CSS into a separate webview document).
💭 The annotation overlay's chrome lives in a guest webview and syncs its theme via a MutationObserver watching the host documentElement's inline style — the same attribute applyAppearanceContrast mutates. That means the observer will fire on contrast changes, but since it reads raw role values rather than contrast-adjusted ones, the overlay chrome won't actually follow the contrast setting even though the rest of the app does. The check rule explicitly calls out this exact scenario: literal reads of
--foreground,--border, and related roles causing menus and utilities to split into adjusted vs. unadjusted colors. Since annotationTheme.ts reads precisely those roles now split by the contrast adjustment, this looks like a real, confirmed gap — and the fix is straightforward, just switching those reads to the--contrast-*equivalents with matching fallbacks. The catch is that inline review comments can only be posted on lines actually in the diff, so I'd need to attach this finding to a nearby diff line (like appearanceContrast.ts or the contrast CSS block) while referencing annotationTheme.ts. Before committing to this, I want to check if there's a stronger finding elsewhere, so I'm considering whether other raw-role reads exist in adjacent app chrome like theme inspector, terminal, or chart colors that might be more relevant than this one.
19. git_tools — getPropertyValue(
💭 ThreadTerminalDrawer seems to reference user media/terminal tokens, which I'm intentionally excluding, but I want to double check exactly which variables it reads, like whether it also uses --foreground for the drawer chrome.
20. browse_code — apps/web/src/components/ThreadTerminalDrawer.tsx
💭 The terminal case is intentionally excluded since the filter would wrongly affect terminals, so that's fine. That leaves annotationTheme.ts as a candidate finding — but I'm weighing whether this is a legitimate boundary case, since it's a cross-process theme snapshot exporting a raw palette rather than the kind of imperative style string or menu the customer's rule was targeting. I also notice the guest side has its own separate Tailwind bridge defined in Annotation.css, distinct from the host's, and since the annotation chrome sits inside the preview it could count as part of the interface subject to the contrast preference. But before suggesting a fix involving reading contrast variables, I need to check whether the IPC schema validating the theme payload restricts color string format or length — if color-mix strings would break that schema, my proposed fix could be unsafe to recommend.
21. browse_code — Show the definition of DesktopPreviewAnnotationTheme and DesktopPreviewAnnotationThemeInputSchema in packages/contracts (or wherever defined): what validation/constraints are applied to each color field (max length, allowed characters/format)? Would a long nested color-mix(in oklab, color-mix(...), black 0%) string pass validation?
💭 This does seem like a real, actionable inconsistency—the annotation overlay chrome would stay unadjusted while the app chrome around it follows the contrast setting, and that's distinct from the "don't affect previews" guidance since it's chrome, not content. Still, I'm weighing whether this is significant enough to fail on, given it's a fairly narrow surface and I want to avoid flagging something that might be intentional or a false positive. I should also verify the contrast changes actually trigger a re-render, since if the observer's dedupe logic short-circuits identical pushed values, nothing visibly changes and this remains a confirmed static split. I'll flag it with an inline comment, though I need to figure out the best diff location to attach it to since the actual bug lives in a file outside the diff. Looking at index.css, the
--contrast-foregrounddeclaration around line 1909 falls within the added diff range, so that's a valid target for the comment — noting the adjusted roles never reach the desktop preview overlay because the snapshot logic captures raw values. Before I finalize, I want to double check the exact surrounding lines and do one more pass over the diff for anything else I might have missed.
22. git_tools — glassOpacity|applyAppearance|AppearanceSync|documentElement.style
💭 I'm settling on a single finding for the annotationTheme.ts split, and weighing whether it's substantial enough to warrant a failure state versus just a suggestion—thinking through whether the guest annotation overlay really counts as core app chrome that needs to be flagged. The overlay reads raw role names and applies them imperatively via style.setProperty, which fits the check's rule about auditing imperative style strings, so I'm leaning toward treating this as a legitimate finding rather than dismissing it as intentional. Since the check defaults to failure when findings exist and explicit guidance points that direction, I'll go with failure and a single inline comment rather than neutral. Before finalizing, I'm double-checking a few other spots for genuine issues -- the settings-slider math duplication (already flagged, skip), the chat markdown heading styles (fine), the contrast-secondary-foreground derivation against secondary (fine), and whether any elements locally override card/popover roles outside the expected root/sidebar/theme blocks (none found). I also confirm message-surface isn't overridden elsewhere, and that the sidebar block doesn't redeclare popover, secondary, placeholder, or message roles, which matters for how contrast values cascade. Now I'm tracing through custom property resolution more carefully: since the sidebar doesn't declare --popover-foreground, its --contrast-popover-foreground computes identically to root's since both --popover and --popover-foreground are inherited unchanged. But since the sidebar does declare --card and --card-foreground locally, --contrast-card-foreground there resolves correctly using those local values. I'm now checking whether placeholder, secondary-label, and icon-muted -- which alias to muted-foreground at root -- behave consistently when inherited into the sidebar context, since the sidebar redefines muted-foreground itself. Since these unspecified variables inherit root's already-computed values rather than re-deriving locally, there's a minor mismatch where contrast-placeholder mixes root's muted-foreground with the sidebar's own background, but this mirrors an existing pattern and isn't significant enough to flag. The same reasoning applies to contrast-icon-muted and the secondary/secondary-foreground pairs, which also fall back to root values without issue. I'm moving on to check the toolbar-related contrast variables declared at the root and sidebar selector level. Now I'm deciding where to anchor a code comment about this — I'll place it near the contrast-foreground color-mix declaration around line 1909, since that's the specific role illustrating the sharing behavior worth documenting. Since the serialized payload is identical, the overlay never picks up the adjusted values, so I should suggest reading the adjusted roles for those six fields instead, since they're just fully substituted color-mix strings that the IPC schema accepts as plain strings. No diff is needed since the fix lives in a different file. I'm double-checking the exact line numbers for each field in annotationTheme.ts and confirming the CSS variable declaration at line 1909 before posting the review comment. Reconsidering the severity once more — is this a clear enough consistency gap to warrant a failure, or should I flag it as neutral to avoid noise? Given the explicit rule that runtime-adjusted roles must be shared ordinary custom properties across the Tailwind bridge, global CSS, and imperative style strings, this does look like a genuine violation, so I'll mark it as a failure and post the review.
23. modify_pr — One remaining adjusted/unadjusted split: the desktop preview annotation overlay still snapshots the raw semantic roles, so that app chrome does not follow the new contrast setting. Everything else in the audit looks consistent — the Tailwind bridge, global chrome rules, markdown chrome, toolbar roles, and the imperative context-menu fallback all read the shared --contrast-* properties, and the border/input derivations now soften toward transparent so translucent hairlines keep their alpha.
Agent Credits: 369 credits