fix(ui): derive the sidebar brand accent from the logo instead of chart-1

The dark sidebar reused `--chart-1` (`oklch(0.488 0.243 264.376)`, a
saturated violet) as `--sidebar-primary`, and left `--sidebar-accent` at
zero chroma. That combination is what made the active nav row read as a
flat grey pill with a clashing stripe, and it rendered the Enterprise
usage icon at 2.58:1 against its own `bg-sidebar-primary/10` chip, under
the 3:1 floor for icons.

Both themes now sit on hue 225, taken from the logo (`#87ceea`). The
binding constraint is that `--sidebar-primary` is drawn as text and icons
on a 10%-alpha tint of itself, not that it fills the 3px stripe, so the
two themes need different lightness: dark takes the logo value directly
at 8.83:1, while light needs `oklch(0.5 0.094 225)` at 5.05:1 because the
logo blue on a near-white chip is only 1.66:1. The light value sits on the
sRGB gamut edge, so raising its lightness or chroma clips.

Dark's `--sidebar-primary-foreground` flips to dark, since the fill it
pairs with is now light and near-white on it was 1.57:1.

`--sidebar-accent` picks up only a trace of the same hue. It backs hover
as well as active, so saturating it would make every row look selected.
This commit is contained in:
mubashir1osmani 2026-08-23 14:03:18 -04:00
parent f005afa146
commit 3f698be62f
2 changed files with 32 additions and 20 deletions

View file

@ -109,9 +109,9 @@
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(1 0 0);
--sidebar-foreground: oklch(0.13 0.028 261.692);
--sidebar-primary: oklch(0.21 0.034 264.665);
--sidebar-primary: oklch(0.5 0.094 225);
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
--sidebar-accent: oklch(0.967 0.003 264.542);
--sidebar-accent: oklch(0.967 0.016 225);
--sidebar-accent-foreground: oklch(0.21 0.034 264.665);
--sidebar-border: oklch(0.928 0.006 264.531);
--sidebar-ring: oklch(0.707 0.022 261.325);
@ -151,9 +151,9 @@
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.187 0 0);
--sidebar-foreground: oklch(0.964 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.964 0 0);
--sidebar-accent: oklch(0.309 0 0);
--sidebar-primary: oklch(0.814 0.081 225);
--sidebar-primary-foreground: oklch(0.187 0 0);
--sidebar-accent: oklch(0.309 0.024 225);
--sidebar-accent-foreground: oklch(0.964 0 0);
--sidebar-border: oklch(0.187 0 0);
--sidebar-ring: oklch(0.569 0 0);

View file

@ -22,21 +22,23 @@ Never hardcode hex values. Use these CSS variables via Tailwind classes — all
### Colors
| Token | Tailwind class | Use |
| ----------------------------- | ------------------------------------- | -------------------------------------------------------------- |
| `--background` | `bg-background` | Main content area, input surfaces |
| `--foreground` | `text-foreground` | Primary text |
| `--card` | `bg-card` | Popover/dialog surfaces (no `<Card>` component yet — see gaps) |
| `--muted` | `bg-muted` | Table header rows, subtle fills |
| `--muted-foreground` | `text-muted-foreground` | Secondary/helper text, timestamps |
| `--border` | `border-border` (or bare `border`) | All 1px separators |
| `--primary` | `bg-primary` / `text-primary` | Send button, checkmarks, active links |
| `--destructive` | `bg-destructive` / `text-destructive` | Delete, error actions |
| `--sidebar` | `bg-sidebar` | **Left sidebar background — use this, not `bg-secondary`** |
| `--sidebar-foreground` | `text-sidebar-foreground` | Sidebar text |
| `--sidebar-accent` | `bg-sidebar-accent` | Active/hover nav item fill |
| `--sidebar-accent-foreground` | `text-sidebar-accent-foreground` | Active nav item text |
| `--sidebar-border` | `border-sidebar-border` | Sidebar's own dividers/right border |
| Token | Tailwind class | Use |
| ------------------------------ | --------------------------------------------- | -------------------------------------------------------------------- |
| `--background` | `bg-background` | Main content area, input surfaces |
| `--foreground` | `text-foreground` | Primary text |
| `--card` | `bg-card` | Popover/dialog surfaces (no `<Card>` component yet — see gaps) |
| `--muted` | `bg-muted` | Table header rows, subtle fills |
| `--muted-foreground` | `text-muted-foreground` | Secondary/helper text, timestamps |
| `--border` | `border-border` (or bare `border`) | All 1px separators |
| `--primary` | `bg-primary` / `text-primary` | Send button, checkmarks, active links |
| `--destructive` | `bg-destructive` / `text-destructive` | Delete, error actions |
| `--sidebar` | `bg-sidebar` | **Left sidebar background — use this, not `bg-secondary`** |
| `--sidebar-foreground` | `text-sidebar-foreground` | Sidebar text |
| `--sidebar-accent` | `bg-sidebar-accent` | Active/hover nav item fill |
| `--sidebar-accent-foreground` | `text-sidebar-accent-foreground` | Active nav item text |
| `--sidebar-primary` | `bg-sidebar-primary` / `text-sidebar-primary` | Sidebar brand accent: active nav stripe, menu badge, usage-card icon |
| `--sidebar-primary-foreground` | `text-sidebar-primary-foreground` | Text on a solid `bg-sidebar-primary` fill |
| `--sidebar-border` | `border-sidebar-border` | Sidebar's own dividers/right border |
**Known gotcha — verified in `globals.css`:** `--accent`, `--secondary`, and `--muted` all resolve to the _identical_ OKLCH value in both light and dark themes. Using `bg-accent` for a "selected" state against a `bg-secondary` container is **invisible** — there is zero contrast. This bit us repeatedly in this exact sidebar. Rules:
@ -44,6 +46,16 @@ Never hardcode hex values. Use these CSS variables via Tailwind classes — all
- Active/selected nav item: `bg-sidebar-accent text-sidebar-accent-foreground`, never `bg-accent` on its own inside the sidebar.
- Anywhere else `bg-accent`/`bg-muted` is used for hover/selected state, confirm the container isn't _also_ `bg-accent`/`bg-secondary`/`bg-muted` before shipping — check `globals.css` values, don't assume.
**The sidebar brand hue is 225, taken from the LiteLLM logo (`#87ceea`).** `--sidebar-primary` used to be a near-black in light mode and a copy of `--chart-1` (a saturated violet, `oklch(0.488 0.243 264.376)`) in dark mode, which is why the active nav stripe and the Enterprise usage icon clashed with everything around them. Both themes now sit on hue 225 and differ only in lightness and chroma, because the same value cannot serve both:
- `--sidebar-primary` is rendered as _text and icons on a 10%-alpha tint of itself_ (`bg-sidebar-primary/10`, used by `SidebarMenuBadge` and `SidebarUsageCard`). That pairing is the binding contrast constraint, not the stripe.
- Dark mode takes the logo value directly, `oklch(0.814 0.081 225)`, giving 8.83:1 on its own chip. The old violet gave 2.58:1, below the 3:1 floor for icons.
- Light mode cannot reuse it: the logo sky blue as text on a near-white chip is 1.66:1. Light mode uses `oklch(0.5 0.094 225)` instead, 5.05:1 on its own chip, and it is at the sRGB gamut edge, so raising either lightness or chroma clips.
- `--sidebar-primary-foreground` must stay opposite in lightness to `--sidebar-primary`. Dark mode's is dark (`oklch(0.187 0 0)`) precisely because the fill it sits on is light.
- `--sidebar-accent` carries only a trace of the same hue (chroma 0.016 light, 0.024 dark). It is the fill behind both hover and active, so saturating it makes every row look selected. Keep the saturation in `--sidebar-primary`.
Hover and active currently share `bg-sidebar-accent` and are distinguished only by the stripe. That is pre-existing, not a regression from the retune.
### Status colors (semantic — don't substitute)
| State | Class | Use |