Skip to content

🧑‍🎨 feat: Give the Default Avatar and Avatar Placeholder Theme Roles - #16545

Open
berry-13 wants to merge 4 commits into
berry-13/clickui-not-expressiblefrom
berry-13/avatar-theme-roles
Open

berry-13 wants to merge 4 commits into
berry-13/clickui-not-expressiblefrom
berry-13/avatar-theme-roles

Conversation

@berry-13

@berry-13 berry-13 commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The default user avatar filled with a literal rgb(121, 137, 255) in Avatar, the share MessageIcon and the endpoint Icon, and agent and assistant avatars sat on surface-secondary with a dark:surface-tertiary override, so no theme could repaint either and the ClickHouse theme kept LibreChat's purple.

This adds three color roles next to brand-purple: avatar-fill and avatar-text for the default avatar and its glyph, and avatar-placeholder for the backdrop behind an agent or assistant image. Every bundled theme reproduces today's pixels. A theme that sets only its surfaces keeps the placeholder on the surface its mode used before, both through resolveTheme and through the legacy applyTheme path, which now receives the mode. ClickHouse takes Click UI's avatar.color.background.default and avatar.color.text.default and its muted background, and the Click UI parity spec cites those tokens.

avatar-text defaults to each bundled theme's primary text, which is what the sidebar Avatar inked before, and follows rgb-text-primary in a theme that does not set it. The chat and share layouts do not render a user message icon, so the sidebar account avatar is the default avatar users see, and its pixels are unchanged in every bundled theme. The bundled dark default keeps its 2.6:1 glyph contrast; raising it is a design change tracked in berry-13#207.

Closes berry-13#194

Type of change

  • Feature

Testing

Tested environments/configuration: mock e2e harness (Chromium), jest (jsdom), default and ClickHouse themes in light and dark.

Automated tests:

  • New scenarios in clickhouse-theme.spec.ts: default-avatar-keeps-its-fill (rgb(121, 137, 255), the old glyph ink and the old placeholder surface in both modes with no theme) and clickhouse-avatar-follows-click-ui; both pass.
  • packages/client theme suites: 425 passed, including registry and applyTheme fallback specs and the glyph contrast checks.
  • Related client suites: 6341 passed.
  • tsc --noEmit for packages/client, client and packages/data-provider; ESLint, Prettier and import order clean.

Screenshots / recordings

Sidebar account avatar with its image failing, so the default avatar draws. The default theme is pixel-identical before and after; ClickHouse moves from LibreChat's purple to Click UI's avatar fill and glyph.

Before After
Default, light
Default, dark
ClickHouse, light
ClickHouse, dark

The default user avatar filled with a literal rgb(121, 137, 255) in Avatar, MessageIcon and the endpoint Icon, and agent and assistant avatars sat on surface-secondary in light and a dark: surface-tertiary override, so no theme could repaint either.

avatar-fill and avatar-text paint the default avatar and its glyph, and avatar-placeholder paints the backdrop behind an agent or assistant image. Every bundled theme reproduces today's pixels. A theme that sets only its surfaces keeps the placeholder on the surface its mode used before the role existed, both when resolved and through the legacy applyTheme path. ClickHouse takes Click UI's avatar fill and text and its muted background.
The sidebar account avatar falls back to the default avatar when its image fails; with no theme it keeps rgb(121, 137, 255) in both modes and the placeholder keeps the surface its mode used, and under ClickHouse both take the Click UI values.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T06:45:30.087244Z b090466 New commits
🔒 Security Review ✅ Completed 2026-09-30T06:18:08.896066Z 640e393 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

The sidebar Avatar, the only default avatar the chat renders, still inked its glyph in text-primary, so a theme's avatar-text never reached it and ClickHouse drew a dark glyph where Click UI draws white. Avatar now reads avatar-text, which defaults to each bundled theme's primary text and follows rgb-text-primary in a theme that does not set it, through resolveTheme and the legacy applyTheme path, so every rendered default avatar keeps its pixels.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 640e3930e6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

boxShadow: 'rgba(240, 246, 252, 0.1) 0px 0px 0px 1px',
}}
className={`relative flex items-center justify-center rounded-full p-1 text-text-primary ${className}`}
className={`bg-avatar-fill text-text-primary relative flex items-center justify-center rounded-full p-1 ${className}`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the avatar text role to the shared fallback

When the shared Avatar falls back to its default glyph, including the sidebar account avatar exercised by the new e2e scenario, it still uses text-text-primary, so rgb-avatar-text never controls that glyph. ClickHouse therefore renders its light/dark primary text (#161517/#ffffff) rather than the declared avatar text (#ffffff/#1f1f1c), while the new contrast test only checks the unused token pair. Use the dedicated role here, splitting the role or adjusting its defaults if preserving the stock component's existing pixels is required.

AGENTS.md reference: AGENTS.md:L92-L97

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6c64ae0: Avatar now inks its glyph with text-avatar-text, and avatar-text defaults to each bundled theme's primary text (what the glyph painted before) and follows rgb-text-primary in a theme that does not set it. clickhouse-avatar-follows-click-ui asserts the sidebar glyph takes Click UI's avatar text in both modes; the default stays pixel-identical.

Comment on lines +76 to +77
const avatarPlaceholder =
mode === 'dark' ? colors['rgb-surface-tertiary'] : colors['rgb-surface-secondary'];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Infer dark mode for legacy applyTheme callers

For existing consumers of the publicly exported applyTheme(colors, root, base?) API, the new optional mode argument is necessarily omitted and mapColors defaults it to light. On a root already carrying .dark, a legacy theme that defines both surfaces now writes --avatar-placeholder from rgb-surface-secondary, whereas the pre-change dark:bg-surface-tertiary selected the tertiary value. The in-repo provider passes the new argument, but external callers silently regress; derive the default from the target root/base or otherwise preserve the old signature's dark behavior.

AGENTS.md reference: AGENTS.md:L42-L45

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b090466: applyTheme falls back to the root's dark class when no mode is passed; the new spec 'reads the mode off the root when a caller does not pass it' fails without it.

applyTheme is exported, and a caller that applies a legacy palette in dark mode without the new mode argument derived the avatar placeholder from the light surface. It now falls back to the root's dark class.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b09046610c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

mapColors(themeRGB, base).forEach(([property, value]) => {
const activeMode = mode ?? (root.classList.contains('dark') ? 'dark' : 'light');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Infer legacy mode from ancestors or the supplied base

For a legacy three-argument caller that scopes the exported applyTheme(colors, root, darkTheme) to an element beneath <html class="dark">, this check sees no class on root and derives light mode. It consequently writes --avatar-placeholder from rgb-surface-secondary, whereas the former dark:bg-surface-tertiary matched the dark ancestor. The fresh evidence after the prior applyTheme review is that the implemented fallback checks only the target itself, not its ancestors or the already-supplied dark base; infer from those as well when mode is omitted.

AGENTS.md reference: AGENTS.md:L42-L45

Useful? React with 👍 / 👎.

Comment on lines +80 to +81
--avatar-text: 33 33 33;
--avatar-placeholder: 247 247 248;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve CSS-variable fallback links for legacy themes

For consumers that theme through the documented CSS properties—such as overriding --text-primary, --surface-secondary, and --surface-tertiary after importing @librechat/client/theme.css—these hard-coded defaults leave the avatars on LibreChat's stock colors. Before this change the shared avatar read --text-primary and the image backdrops read the surface roles directly, while the new JavaScript fallbacks never run for CSS-only themes. Define these defaults in terms of the legacy source variables, using the tertiary surface in .dark, so existing CSS themes continue to repaint the same elements.

AGENTS.md reference: AGENTS.md:L42-L45

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
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.

1 participant