Skip to content

fix(ui): restore hover feedback and dark-mode variants lost in the token migration - #37579

Merged
yuneng-berri merged 3 commits into
litellm_internal_stagingfrom
litellm_/relaxed-pasteur-5b9529
Aug 20, 2026
Merged

fix(ui): restore hover feedback and dark-mode variants lost in the token migration#37579
yuneng-berri merged 3 commits into
litellm_internal_stagingfrom
litellm_/relaxed-pasteur-5b9529

Conversation

@yuneng-berri

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • 116 hover states changed nothing when hovered
  • Seven files stayed light-coloured in dark mode
  • Nothing stopped the next migration repeating it

How it solves it:

  • Hover gets a distinct value, following the shadcn primitives
  • Indigo maps onto info, amber onto warning
  • A new eslint rule fails an identical hover pair

User Flow

Before: an admin picking an MCP transport gets no feedback from anything they point at, and the panel above stays bright white once they switch to dark mode

  1. They open http://localhost:4000/ui/?page=mcp-servers and click the Connect tab
  2. They point at the "OpenAI platform" link under API Key Setup; the link does not change at all, so they cannot tell it is clickable
  3. They point at the date range control on http://localhost:4000/ui/?page=usage; its border does not change either
  4. They switch the browser to dark mode; the "OpenAI Responses API Integration" banner stays a near-white blue block on the dark page
  5. They open the policies page and start a new policy; the selected mode card keeps a pale indigo fill that glares against the dark form

After: every control they point at responds, and the same screens follow the dark theme

  1. They open http://localhost:4000/ui/?page=mcp-servers and click the Connect tab
  2. They point at the "OpenAI platform" link; the text visibly lightens, so the link reads as clickable
  3. They point at the date range control on http://localhost:4000/ui/?page=usage; its border deepens
  4. They switch the browser to dark mode; the "OpenAI Responses API Integration" banner is now a dark blue tint that sits on the page instead of glaring off it
  5. They open the policies page and start a new policy; the selected mode card picks up the dark theme's accent instead of the pale indigo fill

Relevant issues

Linear ticket

Pre-Submission checklist

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Setup: run the dashboard dev server against a local proxy so the screens below render live

python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload --use_v2_migration_resolver 2>&1 | tee litellm.log
cd ui/litellm-dashboard && npm run dev

Dark mode has no in-app toggle yet, so switch it on from the browser console on each page:

document.documentElement.classList.add('dark')

Before (47a7e17)

Hover feedback

  1. Open http://localhost:3000/mcp-servers/ and click the Connect tab, OpenAI API sub-tab
  2. Hover the "OpenAI platform" link under API Key Setup; capture the link, which does not change
  3. Open http://localhost:3000/usage/ and hover the date range button at the top right; capture the button, whose border does not change

Dark mode

  1. Open http://localhost:3000/mcp-servers/, click the Connect tab, turn dark mode on with the console line above
  2. Capture the OpenAI API sub-tab: the "OpenAI Responses API Integration" banner is a near-white block
  3. Click the Cursor sub-tab and capture it: the banner is a near-white block too
  4. Open http://localhost:3000/policies/, click Add New Policy, turn dark mode on, and capture the mode picker: the selected card is pale indigo

After (e96074f)

Hover feedback

  1. Open http://localhost:3000/mcp-servers/ and click the Connect tab, OpenAI API sub-tab
  2. Hover the "OpenAI platform" link under API Key Setup; capture the link, whose text lightens
  3. Open http://localhost:3000/usage/ and hover the date range button at the top right; capture the button, whose border deepens

Dark mode

  1. Open http://localhost:3000/mcp-servers/, click the Connect tab, turn dark mode on with the console line above
  2. Capture the OpenAI API sub-tab: the banner is a dark blue tint
  3. Click the Cursor sub-tab and capture it: the banner is a dark purple tint
  4. Open http://localhost:3000/policies/, click Add New Policy, turn dark mode on, and capture the mode picker: the selected card uses the dark accent

Type

🐛 Bug Fix

Caveats (if any)

  • Purple has no semantic token, so those keep dark: classes
  • Indigo maps onto info, which is a slightly different hue
  • Eleven pre-existing dead hovers fixed so the rule needs no baseline
  • Hover uses an alpha step, matching the shadcn primitives

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…ken migration

PR #37576 mapped hardcoded Tailwind palette classes onto semantic tokens. Two-tone
hover pairs collapsed onto a single token, so 116 hover utilities across 49 files
became identical to their base class and produced no visible feedback, and in seven
files a dark: variant was dropped while its hardcoded light partner survived, leaving
those elements stuck light in dark mode.

Hover states now follow the alpha-step idiom the shadcn primitives already use
(hover:bg-primary/80, hover:bg-success/20): a duplicated hover:text-X or hover:bg-X
becomes /80, hover:border-border becomes hover:border-ring, and a duplicate is
dropped where another hover utility on the element already carries the change. One
transition-colors that no longer animated anything is removed.

For the dark-mode gaps, indigo maps onto info and amber onto warning. There is no
purple token in globals.css, so the purple sites keep their palette classes and get
their dark: partner back.
…its base

The token migration collapsed two-tone hover pairs by hand, so nothing catches
the next one. `local/no-noop-hover-variant` reads every string literal and
template chunk and errors when a `hover:X` sits alongside a bare `X`, which is
exactly the shape that renders no hover feedback. It ships at error with no
suppression baseline, so the eleven sites that already carried a dead hover
before the migration are fixed here too.

The rule reads one class string at a time, so a base class supplied by a
different ternary branch than its hover partner is left alone: a selected row
whose resting colour already matches its hover colour is deliberate, not a bug.
@yuneng-berri
yuneng-berri enabled auto-merge (squash) August 20, 2026 05:51

@tin-berri tin-berri 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.

done — #37579 approved. Purely presentational fix (66 files, mostly one-line Tailwind class swaps): dead hover: variants where the hover value matched the base (e.g. hover:text-destructivehover:text-destructive/80) and hardcoded light-mode colors (indigo-*, near-white blues) swapped for semantic tokens (info, warning) or explicit dark: variants where no semantic token exists (purple, called out in the PR's own caveats). The new no-noop-hover-variant eslint rule is a solid regression guard — it flags exactly this class of bug (hover utility identical to the base) going forward, and correctly scoped out of the rule's own test fixtures. No auth/billing/security surface, no logic changes. CI green aside from non-blocking pending checks (semgrep, code-quality, external Veria bot review).

@yuneng-berri
yuneng-berri merged commit 93c1461 into litellm_internal_staging Aug 20, 2026
66 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_/relaxed-pasteur-5b9529 branch August 20, 2026 05:52
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR restores dashboard hover feedback, adds dark-mode variants and semantic color tokens, and introduces an ESLint regression guard.

  • Replaces identical base/hover utility pairs with distinct hover colors, borders, or backgrounds.
  • Adds dark-theme styling to previously light-only purple and informational surfaces.
  • Adds and enables a custom ESLint rule with focused rule tests.

Confidence Score: 4/5

The PR appears safe to merge, with one non-blocking destructive-button hover inconsistency worth correcting.

The semantic tokens and dark variants are supported by the dashboard theme, and the new lint rule has focused coverage; the only accepted issue is a remove button inheriting the generic ghost foreground color on hover.

Files Needing Attention: ui/litellm-dashboard/src/components/team/LoggingSettings.tsx

Important Files Changed

Filename Overview
ui/litellm-dashboard/scripts/eslint-rules/no-noop-hover-variant.mjs Adds a narrowly scoped lexical check for identical bare and hover-prefixed utilities; no concrete false positive was established.
ui/litellm-dashboard/tests/eslint-rules/no-noop-hover-variant.test.ts Covers valid distinct hover values, static strings, template elements, and multiple no-op utility categories.
ui/litellm-dashboard/src/components/team/LoggingSettings.tsx Removes a redundant-looking hover class, but this exposes the ghost button’s generic foreground hover color and weakens the destructive affordance.
ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx Adds matching dark-mode backgrounds, borders, and text colors to selected purple tool states.
ui/litellm-dashboard/src/app/(dashboard)/policies/_components/add_policy_form.tsx Replaces fixed indigo selection styling with semantic info tokens that resolve in both themes.
ui/litellm-dashboard/src/components/shared/advanced_date_picker.tsx Replaces no-op border hover states with the distinct ring border token.

Reviews (1): Last reviewed commit: "fix(ui): add an eslint rule that fails a..." | Re-trigger Greptile

onClick={() => removeLoggingConfig(index)}
size="sm"
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
className="text-destructive hover:bg-destructive/10"

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.

P2 Destructive hover color is lost

Removing the explicit hover text color exposes the ghost button’s hover:text-foreground style, so the red remove icon changes to the normal foreground color on hover and weakens the destructive affordance.

Suggested change
className="text-destructive hover:bg-destructive/10"
className="text-destructive hover:bg-destructive/10 hover:text-destructive/80"

Knowledge Base Used: Admin dashboard (ui/litellm-dashboard)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

yuneng-berri added a commit that referenced this pull request Aug 20, 2026
…cleanup (#37580)

PR #37579 read `text-X hover:text-X` on a shadcn Button as dead weight and
removed the hover half. On the ghost and outline variants it was not dead: both
carry their own `hover:text-foreground`, and the duplicate in the className was
the thing displacing it through tailwind-merge. Dropping it handed the hover
back to the variant, so the Remove button in a team's logging settings, the
chat storage banner's dismiss control, and the collapsed enterprise-usage rail
all lose their colour the moment you point at them.

Each of the three now carries a distinct hover value, following the alpha-step
idiom the rest of that migration used, which restores the colour and keeps
`local/no-noop-hover-variant` satisfied.

Every other hover utility that PR dropped sits on a plain element or a variant
with no competing `hover:text-`, so those stay as they are.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants