# ChatWidget

## 2026-07-21 — Brand color now derives a contrast-safe foreground

**Prompted by:** Hellen — widget color customization (theme color vs custom pick)

### What changed
- `ChatWidgetWindow` previously set only `{ "--primary": brandColor }` as a raw hex. It now applies `resolveBrandVars(brandColor)`, which emits `--primary`, `--primary-foreground` and `--ring` — all in `oklch()`.
- `--primary-foreground` is computed from the brand color via `getContrastText()`, so header text and the user message bubble flip to dark text on light brand colors.
- Values are emitted as `oklch()` rather than raw hex, matching what `ThemeProvider` injects, so the two theming paths are now interchangeable.
- An unparseable hex is treated as "no brand color" instead of throwing — a half-typed value in a color input cannot blank out the widget.

### Why
- Setting `--primary` without recomputing `--primary-foreground` left text using the tenant's foreground token, producing white-on-light (unreadable) for any light brand color.
- This is the component-side prerequisite for the Widget Appearance settings feature, where an admin can pick an arbitrary color.

### Affected tokens / files
- `packages/shadcn/src/components/ui/chat-widget.tsx`
- `packages/shadcn/src/lib/colors.ts` — new `resolveBrandVars()`
- `gradientFrom` / `gradientTo` are unchanged and remain supported, but are not exposed in the settings UI (solid color only, per PO).
