[FE Feat] Dark Mode#4502
Conversation
Token-variable foundation + antd darkAlgorithm + hex codemod. - ThemeContextProvider: darkAlgorithm + brand seed (primary = logo yellow #f2f25c, dark button text); strips light color tokens from the antd config so they don't leak into dark. - theme-variables.css: :root/.dark CSS-variable layer for antd semantic tokens, zinc/gray/ag-gray scales, and codemod hex vars. Light values are byte-identical to the prior hardcoded values; only .dark differs. - tailwind.config: darkMode "selector"; named scales point at var(--ag-*). - Codemod routed ~165 files' arbitrary [#hex] + bg-white through CSS vars. - Dark variants for editor/code/eval CSS + globals; JSS literals -> theme.*; inline-style fixes; resize-handle + footer + workflow type chips. - FOUC inline script in _document; Light/System/Dark toggle in the top bar.
Enable antd v6 CSS-variable mode and derive the dark schema from antd's
design tokens, so the whole app tunes from one source.
- ThemeContextProvider: enable cssVar {key: "agenta"} (both modes) + add the
`agenta` root class on <html> so the global --ant-* tokens resolve app-wide.
- theme-variables.css: .dark values now reference var(--ant-color-*) (lossless;
the hand-authored dark values equalled antd's). :root/light untouched.
- Chip/badge palette maps routed to antd preset palette vars (var(--ant-*-1/6/3)),
which adapt to dark automatically: TypeChip, observability spanTypeStyles,
PRESET_COLOR_MAP (fixes PlaygroundHeader + evaluator label chips), and
referenceColors (mapped to nearest antd preset — minor light hue shift).
- Footer: add top border to separate it from content (colorBorderSecondary). - Breadcrumb top bar: borderBottom #eaeff5 -> colorBorderSecondary so the divider is subtle in dark instead of a bright white line. - "Have a question?" help cards: set color: colorText so the <a> text + icons stop inheriting antd's colorLink (blue in dark). - referenceColors: route reference-tag tones through per-tone CSS vars (light = exact Untitled-UI hex, dark = matching antd preset) — now light-lossless and dark-adaptive. - ThemeContextProvider: rename darkSeed -> DARK_TOKEN_OVERRIDES with guidance as the single dark-schema tuning knob.
Import-graph reachability analysis over oss/src + ee/src (from page entries) found these unreachable: - TestcasesTableNew/components/TestcaseEditDrawer/useTreeStyles.ts — orphan createUseStyles hook, imported by nothing. - TestcasesTableNew/components/TestcaseEditDrawerContent.tsx — backwards-compat re-export shim; the live consumer imports .../TestcaseEditDrawer/index directly.
Convert 15 small createUseStyles components to Tailwind classes (per the JSS-to-Tailwind inventory). Visual output preserved in light + dark: theme color tokens -> var-backed token classes (bg-colorBgContainer, text-colorText, border-colorBorder, etc.), antd token sizing -> exact Tailwind/arbitrary values, and `& .ant-*`/nth-child selectors -> arbitrary variants. 6 now-empty assets/styles.ts deleted. CustomTreeComponent skipped (pseudo-element tree connectors — Tier 2). tsc unchanged (541 src baseline); eslint clean.
23 of 29 Tier-2 createUseStyles components converted to Tailwind, preserving light + dark output: antd-component overrides -> `[&_.ant-*]:` arbitrary variants, prop-driven styles -> clsx conditionals, theme tokens -> var-backed token classes. 6 standalone assets/styles.ts deleted. Skipped (left as JSS, for a follow-up): SetupTracingModal, NewEvaluation, auth, DeploymentCard styles (reference antd tokens with no --ag-* var yet), app-management/assets (prop-driven non-token pair), CustomTreeComponent (::before tree-connector pseudo-elements). tsc unchanged (oss 541 / ee 541 baselines); eslint clean.
…ror-text/primary-text)
Clears the 4 Tier-2 files that were skipped for lack of a theme-aware token. - Add 5 missing --ag-* token vars (colorError, colorErrorBorder, colorBgContainerDisabled, colorInfoBg, controlItemBgActive) plus a var-backed boxShadowTertiary exposed as the shadow-tertiary utility. - Migrate SetupTracingModal, NewEvaluation, auth (SendOTP/ShowErrorMessage) and DeploymentCard from createUseStyles to Tailwind; delete 3 styles files. - SetupTracingModal exports its modal-chrome class constants; SetupEvaluationModal and GetStarted consume them instead of the removed useStyles. Light output preserved; oss tsc src baseline unchanged (541).
EnvironmentTag set its background/text/border via inline hex from environmentColors, so the pills stayed bright in dark mode. Route them through new --ag-env-* CSS vars (light = the same hex, dark = matching antd presets) so they adapt like the reference-tag chips. Light output unchanged.
The dark ConfigProvider passed only colorant Button overrides and dropped the ~60 per-component structural overrides from antd-themeConfig.json that light mode spreads into its token. Components fell back to derived globals, so tags, tabs, selects, badges etc. rendered smaller in dark than light (e.g. Tag at fontSizeSM 10 instead of the configured 12). Spread the same component overrides into the dark token, color-stripped per component, so structural sizing matches light while darkAlgorithm still owns dark colors.
CodeBlock hardcoded the github-light Shiki theme, whose per-token inline colors render as bright boxed tokens on the dark surface (e.g. the "How to use API" drawer and the endpoints page). Select github-dark when the app theme is dark and re-key the composer so Shiki re-tokenizes on toggle.
Follow-up to the previous commit: selecting github-dark for the CodeBlock was not enough because the Shiki tokenizer reads the theme from the CodeNode (getTheme()), not from the loaded registry. The node was created without a theme, so tokens kept the light palette — whose #fff background renders as the white boxes behind each token in dark mode. Pass the theme into $createCodeNode(language, theme) so tokenization uses it. Verified via shiki: github-light bg=#fff, github-dark bg=#24292e.
TokenNode paints placeholder chips ({{...}}, Jinja blocks/comments, invalid
state) via inline JS style, which the hex codemod can't reach, so the light
backgrounds (#e2e8f0, #fef2f2) stayed bright in dark mode. Route them through
--ag-c-* vars; add the four missing hexes (A855F7, FEF2F2, B91C1C, F87171) to
theme-variables.css with dark-adapted values. Light output unchanged.
In dark mode the sidebar and content area both resolved to ~#141414, so the layout read as one flat panel separated only by borders. Add a dedicated --ag-sidebar-bg (light #fff, unchanged; dark #0d0d0d, one step darker than the content surface) and point the Sider at it, so the nav rail recedes as chrome. Light mode untouched.
Follow-up to the darker sidebar: the antd Menu painted its own colorBgContainer (#141414), which now read as a lighter band against the #0d0d0d sidebar. Make the menu (and inline submenus) transparent so it inherits the sidebar surface. No-op in light mode (sidebar is white behind it).
Pre-auth routes render straight on <body>, which had no background, so in dark mode they showed white behind dark antd controls. Add a dark <body> background (scoped to .dark, so light is untouched) which covers all bare routes, and make the auth page's logo and panel shadow theme-aware (swap to the dark-accent logo and a neutral shadow in dark). Product-mockup/grid imagery stays light as intentional showcase art. Light mode unchanged.
New visitors now follow their OS color-scheme preference instead of always starting in light. Update both the useLocalStorage default in ThemeContextProvider and the pre-paint FOUC script in _document so they agree on the empty-storage case (no flash). Users who explicitly pick Light/Dark are unaffected.
The Skip button used antd type="link" default (blue colorLink), which reads as off-brand on the dark get-started screen. Style it with neutral theme-aware text (colorTextSecondary, hover colorText) so it's a quiet secondary action in both themes.
…et (dark) The get-started guide sections used bg-colorInfoBg, whose dark value was antd's blue info tint — but the project treats colorInfoBg as a neutral grey (#f5f7fa ≈ zinc-1), so the cards rendered navy. Map colorInfoBg's dark value to bg-elevated (neutral), which also fixes the NewEvaluation tab hover. Also make the section Progress theme-aware (strokeColor -> colorPrimary, trail -> colorFill) so it isn't an invisible navy bar on dark. Light unchanged.
The widget panel uses a black drop-shadow for elevation, which is invisible on the dark page, so it blended into the background. Add a dark-only border (colorBorder) so the floating panel has a defined edge. Light keeps its shadow, unchanged.
Skeleton cells returned a bare 12px <SkeletonLine> bar, while real cell content
wraps in `h-full flex items-center` — so the bars sat at the top of each row
instead of centered. Make SkeletonLine self-center by default (both the
@agenta/ui copy and the oss copy), with a `center={false}` opt-out for the one
place that stacks two lines (RunNameCells), which now centers them in its own
flex-col. Not dark-mode specific.
The evaluations "Application" cell styled the variant slug, row, and version chip with hardcoded navy rgba (rgba(15,23,42,.85) etc.) in evaluations.css, so they were near-invisible on dark. Route them through theme-aware --ag-app-variant-* vars (light = exact prior rgba, dark = adapted). Also fix the variant version Tag in VariantDetails (rgba(5,23,41,.06) -> colorFillSecondary, byte-identical in light). Light unchanged.
Links in light use the navy primary, but in dark the colorLink/colorInfo seeds are stripped so darkAlgorithm fell back to a harsh default blue — the off-brand blue seen on card CTAs and other links. Pin colorLink/Hover/Active in DARK_TOKEN_OVERRIDES to a softer, dark-tuned blue (#58a6ff family), which retunes every antd link app-wide in one place instead of per-component patches. Light unchanged.
Removed the forked oss copy of SkeletonLine and repointed all 11 consumers
(EvaluationRunsTablePOC + References cells) to the single source of truth,
`import {SkeletonLine} from "@agenta/ui/table"`. No behavior change — the two
copies were already identical after the centering fix.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 300 files, which is 150 over the limit of 150. To get a review, narrow the scope: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (300)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…f-height mount useContainerResize initialised to height 0 and only updated via a post-paint useEffect + requestAnimationFrame. For infinite-scroll (autoHeight) tables the scroll viewport is derived from the measured container height, so the first painted frame fell back to useScrollConfig's 360px default (~50% of a typical viewport) and then grew to full height a frame later — visible on every mount/navigation. Measure the container synchronously in a layout effect (useLayoutEffect on the client, useEffect on the server) so the initial paint already has the real height. Applied to both InfiniteVirtualTable copies: the @agenta/ui package one (evaluators) and the oss-local one (evaluations, testsets, eval-run scenarios, playground testset preview).
…eeze routeChangeComplete fires with (url) but routeChangeError fires with (err, url), so the onSettle handler received the error object as its first argument and crashed on url.includes(runId). Guard with a typeof string check before calling string methods.
…n dark mode --ag-c-98A2B3 and --ag-c-94A3B8 are used exclusively as muted secondary TEXT (e.g. the uppercase OUTPUTS / WITH-REASONING section labels in the eval scenario drawer), but the codemod mapped both to #2f2f2f — a dark surface value that is near-invisible on the #141414 drawer. Point them at colorTextTertiary so they read as quiet, legible labels. Light values untouched.
The Expand/Collapse/Show/Hide all buttons are antd default buttons whose bg (colorBgContainer #141414) is darker than the elevated popover surface (#242424), so in dark they read as heavy recessed boxes. Flush their bg to the popover surface so they render as clean outlined buttons — matching the light look where the button bg already equals the popover bg. Scoped dark: only, both the package and oss copies. Light unchanged.
… mode The deploy modal's instruction callout used bg-zinc-50 + border-zinc-200. Only zinc-1..10 are var-backed in the Tailwind config; the default zinc 50-950 scale is static light, so the callout rendered as a white box with the theme-aware (light) text on it — invisible in dark. Add dark: overrides routing to the white-alpha surface vars. Same pattern fixed in the other surfaces the sweep surfaced: the tool-selector popover selected/hover rows + icon placeholders, the annotation empty-state card, and the scenario filter count chip. All scoped dark: only — light unchanged.
The light-restore edit from the earlier commit was dropped by the pre-commit patch-restore and never landed. Re-apply: keep static green-7/orange-6/blue-7 for light, scope the dark brightening with dark: variants so light is byte-identical.
…iagnostics Remove the ee + oss evaluators/playground/index.tsx pages. Add temporary scroll-height diagnostics to InfiniteVirtualTable useScrollConfig.
The PageLayout header used h-11 but no flex-shrink:0. In the flex column, a title-only header (little intrinsic content) compresses below 44px while a tabbed header resists shrinking (tabs have a taller min-content height), producing different header heights and a layout shift when navigating between tabbed and non-tabbed table pages. Add shrink-0 so the header is always h-11.
Default (and dashed) antd buttons derive defaultBg from colorBgContainer (#141414), which is darker than most card/elevated surfaces in dark, so they read as heavy recessed boxes wherever they sit on a raised surface (cards, popovers, modals — e.g. View members / Open billing portal). Override the Button component token defaultBg to transparent in dark (with subtle white-alpha hover/active), so default buttons render flush-outlined like light. Remove the now-redundant per-component bg overrides from the column-visibility popover (both copies) since the token fix covers them. Light unchanged.
- RunOverlay: the loading scrim used bg-white/90 with no dark handling, washing the panel white in dark. Add dark:bg-[#141414]/90. - Metric/Frequency chart tooltips: bg-white/95 with text-gray-900. gray-900 inverts to colorText (light) in dark, so the tooltip was a white box with invisible light text. Add dark elevated bg + subtle ring; the text then reads correctly. Light unchanged.
Crisp renders in a cross-origin iframe with no runtime light/dark API, so the chatbox theme can't be bound to our in-app toggle from code. Wire the one lever the SDK exposes — setColorTheme — to the app theme: a dark (Black) accent in dark mode, dashboard default in light, so the launcher/accent is less out-of-place. Includes a TODO to enable chatbox dark mode in the Crisp dashboard for the chat window itself (system-scheme based; not bindable to our toggle).



Summary
Primary Change
Layout QoL Improvements
Testing
QA follow-up
Checklist
Contributor Resources