From 61148fe31120cbeac00f574249f869334d7bf834 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 15 Sep 2026 09:27:05 -0700 Subject: [PATCH 1/2] test(chat-ui): avatar palette resolves through theme tokens --- packages/chat-ui/src/avatar.test.tsx | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/chat-ui/src/avatar.test.tsx b/packages/chat-ui/src/avatar.test.tsx index bb6bed6931..cd8a12ae33 100644 --- a/packages/chat-ui/src/avatar.test.tsx +++ b/packages/chat-ui/src/avatar.test.tsx @@ -8,6 +8,7 @@ import { CORBIT_VISOR_COLOR, CorbitAvatar, avatarClassForPrincipal, + avatarColorClass, avatarColorForPrincipal, resolveAvatarFill, } from "./avatar"; @@ -57,6 +58,25 @@ describe("avatarClassForPrincipal", () => { }); }); +describe("avatar pastel tokens", () => { + test("the palette is token references, not hardcoded hex", () => { + expect(AVATAR_COLORS).toHaveLength(4); + for (const color of AVATAR_COLORS) { + expect(color.startsWith("--avatar-")).toBe(true); + expect(color).not.toContain("#"); + } + }); + + test("every class pairs its token with the readable ink", () => { + for (const color of AVATAR_COLORS) { + const cls = avatarColorClass[color]; + expect(cls).toContain(`bg-(${color})`); + expect(cls).toContain("text-black"); + expect(cls).not.toContain("#"); + } + }); +}); + describe("resolveAvatarFill", () => { test("a principal with no explicit image gets the generated fill", () => { const fill = resolveAvatarFill("prn_alice"); @@ -81,6 +101,19 @@ describe("resolveAvatarFill", () => { }); describe("CorbitAvatar", () => { + test("paints its field from the identity token, never a hardcoded hex", () => { + const html = renderToStaticMarkup(); + expect(html).toContain(`fill:var(${CORBIT_DEFAULT_COLOR})`); + expect(html).not.toContain("#C5D2DE"); + expect(html).not.toContain("#C1D1BE"); + expect(html).not.toContain("#F7EAD5"); + expect(html).not.toContain("#F2B277"); + }); + + test("a chosen palette token paints the field", () => { + const html = renderToStaticMarkup(); + expect(html).toContain("fill:var(--avatar-3)"); + }); test("renders an SVG with an accessible name and no visible label", () => { const html = renderToStaticMarkup( , From 37e9eb46b48390eae3f831e49f1ddba350bebf03 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 15 Sep 2026 09:34:41 -0700 Subject: [PATCH 2/2] feat(chat-ui): resolve avatar identity through theme tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avatar pastels move from hardcoded hex to --avatar-1..4 tokens defined once in chat-ui's stylesheet :root — the proposed upstream contract for react-ui's theme. Class map, CorbitAvatar field, and the create-agent swatches all consume the tokens by reference; DESIGN.md's color-exception section now describes the token rule. No hex remains in the identity path. Fixes CL-7478 --- DESIGN.md | 12 ++++---- apps/web/src/pages/create-agent-panel.tsx | 4 +-- packages/chat-ui/src/avatar.test.tsx | 18 ++++-------- packages/chat-ui/src/avatar.tsx | 34 ++++++++++++++++------- packages/chat-ui/src/styles.css | 10 +++++++ 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 10e4bef784..783e361c16 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -163,11 +163,13 @@ and the rest of its semantic palette. Never hardcode a hex value or an arbitrary Tailwind color class in product code; if a needed token doesn't exist yet, add it in react-ui, not locally. -**Avatar identity** is the one deliberate color exception. A person without -an explicit image receives deterministic initials from the approved pastel -palette. `@corbits/chat-ui` owns the finite Tailwind background/text class -map and resolves a principal to one class pair; consumers never use CSS -variables or inline color styles. Agents use `CorbitAvatar`: a circular +**Avatar identity** resolves through theme tokens, not local hex. A person without +an explicit image receives deterministic initials from the `--avatar-1` through +`--avatar-4` pastel tokens, defined once in `@corbits/chat-ui`'s stylesheet +`:root` (the proposed upstream contract for `@corbits/react-ui`'s theme). +`@corbits/chat-ui` owns the finite token-to-class map and resolves a principal +to one class pair; consumers use the resolver's classes, never their own color +styles. Agents use `CorbitAvatar`: a circular field with a dark lower visor and sensor glint. The two forms stay distinct at a glance in every collaborative surface. diff --git a/apps/web/src/pages/create-agent-panel.tsx b/apps/web/src/pages/create-agent-panel.tsx index 2d3734974a..2f5532f56f 100644 --- a/apps/web/src/pages/create-agent-panel.tsx +++ b/apps/web/src/pages/create-agent-panel.tsx @@ -434,11 +434,11 @@ export function CreateAgentPanel({ size="lg" />
- {AVATAR_COLORS.map((color) => ( + {AVATAR_COLORS.map((color, index) => (