Files
coder/site/src/theme
Jake Howell 88c7304e0b feat: add AppearanceProvider to decouple externalImages from theme (#27197)
> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.

## What

Introduces an `AppearanceProvider` / `useAppearance` context that
publishes **user** appearance values derived from the active site theme,
and migrates the current consumers of `theme.externalImages` (`Avatar`,
`ExternalImage`, `IconsPage`) to read from it.

## Why

Today, per-user appearance concerns like `externalImages` are smuggled
onto the Emotion theme object, which forces components to pull in
`useTheme` purely to reach a single appearance value. That couples "how
something looks for this user" to "how the styling engine happens to be
wired", and means every new user-appearance value has to be bolted onto
the theme.

This context gives user appearance a home of its own, decoupled from
Emotion. Components ask for what they actually need (`const {
externalImages } = useAppearance()`) instead of reaching through the
theme.

## Scope: user appearance, not admin appearance

To be explicit, this provider is about **user-level** appearance — the
per-user, theme-derived rendering concerns. It is deliberately *not* the
deployment-level `AppearanceConfig` (application name/logo, service
banners, support/docs links) that admins configure; that is a separate
concern with its own data source and shouldn't be folded in here.

## Future scope

`externalImages` is the **first** value to move here, not the only one.
`Appearance` is deliberately modelled as an open interface so future
*user* appearance state can live in one place without touching every
consumer or overloading the theme again. Likely candidates are other
per-user, theme-derived values, e.g.:

- Terminal font / other typography preferences currently surfaced via
user appearance settings.
- Theme mode and other theme-derived rendering styles that follow the
same "read one value off the theme" pattern as `externalImages`.
- Accessibility-oriented rendering preferences (e.g. reduced motion) as
they're added.

Centralising these behind a single provider keeps consumers stable as
the surface grows and avoids re-litigating the `useTheme` coupling each
time (laziness now, less maintenance later).

## Changes

- Add `site/src/theme/appearance.tsx` (`AppearanceProvider`,
`useAppearance`), defaulting `externalImages` to `forDarkThemes` to
match `DEFAULT_THEME`.
- Wrap children with `AppearanceProvider` in `ThemeOverride` and in the
Storybook preview decorator.
- Migrate `Avatar`, `ExternalImage`, and `IconsPage` off
`theme.externalImages` and onto `useAppearance`.

## Notes

- Kept as a **draft** pending the go-ahead to open for review.
- No behavioural change intended; this is a plumbing/refactor step.
2026-07-27 08:54:28 +00:00
..
2025-08-14 18:25:15 -03:00