Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/docs/configuration/dotenv.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3796,7 +3796,7 @@ Properly setting cache headers is crucial for optimizing the performance and eff

**Behaviour:**

- Every color token in the theme engine can be set this way: drop the `rgb-` prefix from the token name and upper-snake-case the rest, so `rgb-surface-submit` becomes `REACT_APP_THEME_SURFACE_SUBMIT` and `rgb-status-error-border` becomes `REACT_APP_THEME_STATUS_ERROR_BORDER`. The token list is the `IThemeRGB` interface in [`packages/client/src/theme/types/index.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/client/src/theme/types/index.ts).
- Every color token in the theme engine can be set this way: drop the `rgb-` prefix from the token name and upper-snake-case the rest, so `rgb-surface-submit` becomes `REACT_APP_THEME_SURFACE_SUBMIT` and `rgb-status-error-border` becomes `REACT_APP_THEME_STATUS_ERROR_BORDER`. The token list is the `IThemeRGB` interface in [`packages/client/src/theme/types/index.ts`](https://github.com/LibreChat-AI/LibreChat/blob/dev/packages/client/src/theme/types/index.ts).
- Values are inlined into the client when it is built, so the client has to be rebuilt after changing them. Setting them on a prebuilt Docker image has no effect.
- The same colors apply in light and dark mode, and they only change colors, not shape.
- [`interface.theme`](/docs/configuration/librechat_yaml/object_structure/theme) in `librechat.yaml` outranks these colors, and the high-contrast modes outrank both. `interface.theme` also needs no rebuild and can change shape, so prefer it for new deployments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ description: Apply a bundled or custom theme to every user with interface.theme

`interface.theme` sets the deployment theme: the colors, shape, typography, shadows and motion that every user sees, in both light and dark mode. It takes either the name of a theme bundled with LibreChat or an inline theme definition.

<Callout type="info" title="Availability">
`interface.theme` and the appearance scales described on this page are on LibreChat's `canary`
branch and are not part of a tagged release yet.
<Callout type="important" title="Pending: not yet released">
`interface.theme` and the appearance scales on this page are merged to LibreChat's `dev` branch
and are not in a tagged release yet; v0.8.8 and earlier do not read them.
</Callout>

```yaml filename="interface / theme"
Expand All @@ -20,7 +20,7 @@ interface:

When `interface.theme` is unset, LibreChat behaves as before: users see the default LibreChat theme, or whatever build-time colors or stored theme apply to them.

This page covers what an operator sets in `librechat.yaml`. The theme engine itself, including the full token list and how each token maps to Tailwind utilities, is documented in the [theme README](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/client/src/theme/README.md) in the LibreChat repository.
This page covers what an operator sets in `librechat.yaml`. The theme engine itself, including the full token list and how each token maps to Tailwind utilities, is documented in the [theme README](https://github.com/LibreChat-AI/LibreChat/blob/dev/packages/client/src/theme/README.md) in the LibreChat repository.

## How the theme is chosen

Expand Down Expand Up @@ -54,10 +54,11 @@ A name that is not one of these is ignored: the server logs it and drops `interf

`interface.theme: clickhouse` changes both color and shape:

- **Palette.** Every color token is defined in both modes from Click UI's light and dark tokens, so nothing falls back to the LibreChat palette. A few values are moved along their Click UI ramps where the verbatim value missed WCAG AA contrast.
- **Palette.** Every color token is defined in both modes from Click UI's light and dark tokens, so nothing falls back to the LibreChat palette. Values are Click UI's own, even where a pairing falls below WCAG AA contrast. Users who need more contrast can pick a high-contrast mode, which always uses the built-in accessible palette (see [How the theme is chosen](#how-the-theme-is-chosen)).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Wait for these changes to reach the documented canary branch

The page says it describes LibreChat's canary branch, but the checked canary sources still contain the contrast-adjusted ClickHouse palette and none of the newly documented appearance tokens (for example, menuPanelRadius, buttonHeightXs, or chromeBorderAlpha); these changes currently exist only in pending/dev work. Consequently, canary operators are told the opposite palette behavior and any configurations using the new rows are silently logged and ignored. Merge the corresponding LibreChat changes into canary first, or clearly mark this entire update as dev-only.

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 b3e6d03: the page no longer claims canary; it opens with the Pending: not yet released callout saying these keys are on dev only (v0.8.8 does not read interface.theme), and the source links point at dev.

- **Accent.** Near-black `#151515` in light mode and the ClickHouse yellow `#faff69` in dark mode, used for the accent and submit button. The focus ring is Click UI's outline color: blue `#437eef` in light mode, `#faff69` in dark.
- **Buttons.** The primary button is `#302e32` in light mode and `#faff69` in dark, with Click UI's hover steps.
- **Radii.** A tighter scale taken from Click UI's `border.radii`: controls, menus, tooltips, tabs and `sm` through `lg` at `0.25rem`, surfaces and `xl`/`2xl` at `0.5rem`, large surfaces and `3xl` at `0.75rem`.
- **Radii.** A tighter scale taken from Click UI's `border.radii`: controls, menus, popovers, tooltips, tabs, the composer's action buttons and `sm` through `lg` at `0.25rem`, surfaces and `xl`/`2xl` at `0.5rem`, large surfaces and `3xl` at `0.75rem`.
- **Borders.** Decorative outlines on controls and chips and the hairlines inside stroked surfaces are hidden (`chromeBorderAlpha` and `insetBorderAlpha` at `0`), so surfaces are separated by fill; field edges and the neutral button's border stay. Destructive actions use the `soft` style.
- **Shadows.** Click UI's single elevation shadow on every raised surface, menus and dragged items, at 0.15 opacity in light mode and 0.6 in dark, with a hairline shadow for the `2xs` through `sm` steps. Tooltips have no shadow.
- **Controls.** Buttons and fields are 2rem tall, controls are padded `1rem` with a `0.5rem` icon gap and a regular (`400`) label weight. Fields are filled (`fieldFillStyle: fill`) and swap their edge color on focus (`fieldFocusStyle: border`); labels are `0.75rem` at weight `500`. Disabled controls use the `fill` style with their own disabled colors, the switch is a compact 2rem by 1rem, and table rows get a 1px rule.
- **Dialogs.** A 1px edge, `2rem` side padding, and titles at `1.25rem`, weight `700`, in the Inter stack. Every dialog scrim is at 0.75 opacity.
Expand Down Expand Up @@ -143,7 +144,7 @@ The browser applies the same rules to the theme it receives and logs `[Deploymen

### Colors

Colors use the same token names as the theme engine: `rgb-` followed by the token, such as `rgb-surface-primary`, `rgb-text-primary`, `rgb-border-medium`, `rgb-accent-primary` or `rgb-status-error-subtle`. The full list of 130 tokens is `themeColorTokens` in [`packages/data-provider/src/theme.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/data-provider/src/theme.ts), which the server and the browser both read, and each token is described in the `IThemeRGB` interface in [`packages/client/src/theme/types/index.ts`](https://github.com/LibreChat-AI/LibreChat/blob/canary/packages/client/src/theme/types/index.ts).
Colors use the same token names as the theme engine: `rgb-` followed by the token, such as `rgb-surface-primary`, `rgb-text-primary`, `rgb-border-medium`, `rgb-accent-primary` or `rgb-status-error-subtle`. The full list of 130 tokens is `themeColorTokens` in [`packages/data-provider/src/theme.ts`](https://github.com/LibreChat-AI/LibreChat/blob/dev/packages/data-provider/src/theme.ts), which the server and the browser both read, and each token is described in the `IThemeRGB` interface in [`packages/client/src/theme/types/index.ts`](https://github.com/LibreChat-AI/LibreChat/blob/dev/packages/client/src/theme/types/index.ts).

Beyond the surface, text, border, status and syntax palettes, these roles let a theme restyle specific interaction states and components:

Expand Down Expand Up @@ -172,7 +173,7 @@ A few tokens follow a related token you did set when you leave them out, so a pa

### Appearance

Appearance values are set **per mode**, and a mode without them uses the defaults below. The defaults are the same in both modes except `menuShadow` and `tooltipShadow`, which are heavier in dark mode. To change shape in both modes, repeat the values under `light` and `dark`, as in the example above.
Appearance values are set **per mode**, and a mode without them uses the defaults below. The defaults are the same in both modes except `menuShadow` and `tooltipShadow`, which are heavier in dark mode, and `buttonNeutralBorderOpacity`, which is opaque in dark mode. To change shape in both modes, repeat the values under `light` and `dark`, as in the example above.

| Key | Controls | Default |
| --- | --- | --- |
Expand All @@ -181,8 +182,17 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `surfaceRadius` | Radius of surfaces such as cards and menus | `1rem` |
| `largeSurfaceRadius` | Radius of large surfaces such as dialogs | `1.5rem` |
| `menuRadius` | Corner radius of menu panels | `0.7rem` |
| `menuPanelRadius` | Corner radius of the model selector menu, hover cards and the composer's menus; follows `radiusXl` when unset | `0.75rem` |
| `popoverRadius` | Corner radius of the composer's popovers, such as the prompts, skills and mention lists; follows `radius2xl` when unset | `1rem` |
| `composerActionRadius` | Corner radius of the composer's send and stop buttons and its other round action buttons; follows `roundControlRadius` when unset | `9999px` |
| `tooltipRadius` | Corner radius of tooltips | `0.275rem` |
| `tooltipPaddingX` | Inline padding of tooltips | `0.5rem` |
| `tooltipPaddingY` | Vertical padding of tooltips | `0.25rem` |
| `tooltipTextSize` | Font size of tooltip text | `1rem` |
| `tabRadius` | Corner radius of tab triggers | `0.185rem` |
| `tabMinWidth` | Minimum width of a tab trigger | `100px` |
| `listMinWidth` | Minimum width of a Select list; `0` sizes it by its trigger | `8rem` |
| `listMaxHeight` | Height after which a Select list scrolls | `24rem` |
| `radiusSm` | The `rounded-sm` step used across the app | `calc(0.5rem - 4px)` |
| `radiusMd` | The `rounded-md` step | `calc(0.5rem - 2px)` |
| `radiusLg` | The `rounded-lg` step | `0.5rem` |
Expand All @@ -191,11 +201,20 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `radius3xl` | The `rounded-3xl` step | `1.5rem` |
| `controlHeight` | Height of standard controls | `2.25rem` |
| `controlPaddingX` | Inline padding of theme-sized controls; follows `spaceNormal` when unset | `0.75rem` |
| `buttonPaddingX` | Inline padding of the default Button size | `1rem` |
| `controlGap` | Gap between a control's icon and label; follows `spaceCompact` when unset | `0.375rem` |
| `iconSize` | Size of standard icons (`size-theme-icon`) | `1rem` |
| `iconSizeMd` | Size of medium icons (`size-theme-icon-md`) | `1.25rem` |
| `iconSizeLg` | Size of large icons, such as a dialog's close button (`size-theme-icon-lg`) | `1.5rem` |
| `controlFontWeight` | Label weight of theme-sized controls | `500` |
| `buttonHeight` | Height of the default Button | `2.5rem` |
| `buttonHeightSm` | Height of the `sm` Button | `2.25rem` |
| `buttonHeightXs` | Height of the `xs` Button | `1.75rem` |
| `buttonHeightLg` | Height of the `lg` Button | `2.75rem` |
| `buttonHeightCompact` | Height of compact Buttons | `2rem` |
| `iconButtonSizeSm` | Width and height of the `icon-sm` Button | `2rem` |
| `fieldHeight` | Height of form fields | `2.5rem` |
| `fieldHeightLg` | Height of large form fields, such as title inputs | `3rem` |
| `fieldPaddingY` | Vertical padding of form fields | `0.5rem` |
| `fieldFocusStyle` | How a focused field shows focus: `ring` draws the focus ring, `border` swaps the field's edge to `rgb-border-field-focus` | `ring` |
| `fieldFillStyle` | Whether fields stay `transparent` or paint `rgb-field-fill` (`fill`) | `transparent` |
Expand All @@ -206,6 +225,7 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `focusRingOffset` | Distance of the focus outline from the element's edge | `2px` |
| `switchWidth` | Width of the switch | `2.75rem` |
| `switchHeight` | Height of the switch; the knob is this minus the track's 4px border | `1.5rem` |
| `checkboxSize` | Size of the checkbox and its check mark | `1rem` |
| `tableCellSpaceY` | Vertical padding of table cells | `1rem` |
| `tableRowStroke` | Thickness of the rule between table rows | `0px` |
| `spaceCompact` | Compact spacing step | `0.375rem` |
Expand All @@ -220,12 +240,19 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `textLg` | The `text-lg` size | `1.125rem` |
| `textXl` | The `text-xl` size | `1.25rem` |
| `text2xl` | The `text-2xl` size | `1.5rem` |
| `text3xl` | The `text-3xl` size | `1.875rem` |
| `text3xs` | The `text-3xs` size (10px by default) | `0.625rem` |
| `text2xs` | The `text-2xs` size (11px by default) | `0.6875rem` |
| `text1xs` | The `text-1xs` size (13px by default) | `0.8125rem` |
| `text1sm` | The `text-1sm` size (15px by default) | `0.9375rem` |
| `leadingXs` | Line height paired with `text-xs` | `calc(1 / 0.75)` |
| `leadingSm` | Line height paired with `text-sm` | `calc(1.25 / 0.875)` |
| `leadingBase` | Line height paired with `text-base` | `calc(1.5 / 1)` |
| `leadingLg` | Line height paired with `text-lg` | `calc(1.75 / 1.125)` |
| `leadingXl` | Line height paired with `text-xl` | `calc(1.75 / 1.25)` |
| `leading2xl` | Line height paired with `text-2xl` | `calc(2 / 1.5)` |
| `leading3xl` | Line height paired with `text-3xl` | `calc(2.25 / 1.875)` |
| `inlineCodeWeight` | Weight of inline code in messages | `600` |
| `dialogStroke` | Width of the dialog's edge stroke | `0px` |
| `dialogPaddingX` | Inline padding of dialogs | `1.5rem` |
| `dialogHeaderGap` | Gap between a dialog's title and description | `0.375rem` |
Expand All @@ -236,6 +263,7 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `scrimOpacity` | Strength of the scrim behind standard dialogs | `0.8` |
| `alertScrimOpacity` | Strength of the scrim behind confirmation dialogs | `0.9` |
| `modalScrimOpacity` | Strength of the scrim behind other modal dialogs | `0.65` |
| `buttonNeutralBorderOpacity` | Opacity of the neutral button's border | Light: `0.1`; dark: `1` |
| `elevationSurface` | Shadow of raised theme surfaces | `0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)` |
| `elevationDrag` | Shadow of a badge while it is dragged | `0 10px 25px rgb(0 0 0 / 0.1)` |
| `shadow2xs` | The `shadow-2xs` step | `0 1px rgb(0 0 0 / 0.05)` |
Expand All @@ -249,28 +277,35 @@ Appearance values are set **per mode**, and a mode without them uses the default
| `tooltipShadow` | Shadow of tooltips | Light: `0 2px 4px 0 rgb(0 0 0 / 0.25)`; dark: `0 1px 2px 0 rgb(0 0 0 / 0.35)` |
| `motionFast` | Duration of fast transitions | `150ms` |
| `motionNormal` | Duration of normal transitions | `200ms` |
| `chromeBorderAlpha` | How much of the border color the outlines of controls and chips keep; `0` hides them without moving anything | `1` |
| `insetBorderAlpha` | How much of the border color the hairlines inside a stroked surface keep; `0` hides them | `1` |
| `destructiveStyle` | How destructive actions are painted: `fill` uses the solid destructive surface, `soft` a tint of it | `fill` |

The defaults reproduce LibreChat's look, so a theme that sets none of these keys changes no shape.

Accepted values:

- **Radii, `controlHeight`, `controlPaddingX`, `controlGap`, button and field sizes, spacing, text and label sizes, and the dialog lengths (`dialogStroke`, `dialogPaddingX`, `dialogHeaderGap`, `dialogTitleSize`):** `0`, or a number in `px`, `rem` or `em` (such as `0.25rem`), or a single `calc()` of two such lengths (such as `calc(0.5rem - 2px)`).
- **Radii, `controlHeight`, `controlPaddingX`, `buttonPaddingX`, `controlGap`, `buttonHeight`, `buttonHeightSm`, `fieldHeight`, `fieldPaddingY`, spacing, tooltip padding and text size, text and label sizes, and the dialog lengths (`dialogStroke`, `dialogPaddingX`, `dialogHeaderGap`, `dialogTitleSize`):** `0`, or a number in `px`, `rem` or `em` (such as `0.25rem`), or a single `calc()` of two such lengths (such as `calc(0.5rem - 2px)`).
- **`buttonHeightXs`, `buttonHeightLg`, `buttonHeightCompact`, `iconButtonSizeSm` and `fieldHeightLg`:** a length in `px` or `rem` of at least `24px` (`1.5rem`), the minimum pointer target size.
- **`iconSize`:** `12px` to `20px` (`0.75rem` to `1.25rem`). **`iconSizeMd`:** `20px` to `24px`. **`iconSizeLg`:** `16px` to `32px`. **`checkboxSize`:** `16px` to `24px`. **`listMaxHeight`:** `128px` to `640px` (`8rem` to `40rem`). Each is a length in `px` or `rem`.
- **`tabMinWidth` and `listMinWidth`:** `0`, or a length in `px` or `rem`.
- **`switchWidth` and `switchHeight`:** a positive length in `px` or `rem`. Both must use the same unit (a side you leave out uses its `rem` default), the width must exceed the height so the knob can travel, and the height must clear the 4px track border (more than `4px`, or at least `0.5rem`).
- **`tableCellSpaceY` and `tableRowStroke`:** `0`, or a length in `px` or `rem`.
- **`focusRingWidth`:** a positive length in `px` or `rem`, so the focus indicator never disappears.
- **`focusRingOffset`:** `0`, or a length in `px` or `rem` that may be negative (drawing the outline inside the element's edge).
- **`disabledStyle`:** `dim` or `fill`.
- **`fieldFocusStyle`:** `ring` or `border`.
- **`fieldFillStyle`:** `transparent` or `fill`.
- **Font weights (`controlFontWeight`, `dialogTitleFontWeight`):** a whole number from `1` to `1000`. `labelFontWeight` also accepts `inherit`.
- **Font weights (`controlFontWeight`, `dialogTitleFontWeight`, `inlineCodeWeight`):** a whole number from `1` to `1000`. `labelFontWeight` also accepts `inherit`.
- **Line heights (the `leading*` steps, `labelLeading` and `dialogTitleLeading`):** a unitless number (such as `1.5`), a single `calc()` dividing two numbers (such as `calc(1.25 / 0.875)`), or a length.
- **Scrim opacities:** a number from `0` to `1`.
- **Scrim opacities, `buttonNeutralBorderOpacity`, `chromeBorderAlpha` and `insetBorderAlpha`:** a number from `0` to `1`.
- **`destructiveStyle`:** `fill` or `soft`.
- **Font families:** any non-empty `font-family` list without `;`, `{` or `}`. The font must be available to the browser: LibreChat bundles Inter, Roboto Mono and Inconsolata, so any other family has to be installed on the viewer's machine or served by your deployment, or the next family in the list is used.
- **Shadow steps (`shadow2xs` through `shadow2xl`), `menuShadow`, `tooltipShadow` and `elevationDrag`:** a concrete `box-shadow` list, or `none`. `var()`, `env()`, `attr()` and `url()` are rejected.
- **`elevationSurface`:** any non-empty `box-shadow` value without `;`, `{`, `}` or `url()`.
- **Motion:** a duration in `ms` or `s`, such as `120ms`.

Every appearance value must be a string. Quote numbers such as font weights, line heights and scrim opacities (`'500'`, `'1.5'`, `'0.8'`); an unquoted YAML number is rejected.
Every appearance value must be a string. Quote numbers such as font weights, line heights and opacities (`'500'`, `'1.5'`, `'0.8'`); an unquoted YAML number is rejected.

### Brands

Expand Down
Loading