Global variables
Global variables set the colors, fonts, corner radius, shadows and spacing of the whole chat UI. Each one has a role, and many components use it. For example, --str-chat__text-primary colors message text, titles and most labels. Change it once and every component that uses it follows.
Global variables point at the palette. Component variables point at global variables, so a change here also reaches the components that build on it.
The variables fall into two groups:
- Theme: colors, typography, corner radius and shadows.
- Layout: spacing and sizes.
Overriding global variables
Set the light value on .str-chat and the dark value on .str-chat__theme-dark, in a CSS layer after the SDK's:
@layer stream, stream-overrides;
@import "stream-chat-react/dist/css/index.css" layer(stream);
@layer stream-overrides {
.str-chat {
--str-chat__accent-primary: #6d28d9;
--str-chat__text-link: #6d28d9;
}
.str-chat__theme-dark {
--str-chat__accent-primary: #a78bfa;
--str-chat__text-link: #c4b5fd;
}
}Primary buttons, switches, checkboxes, mentions and the read status turn purple, and so do links. The user's own message bubbles stay blue: they use the brand scale, not the accent. To change them too, set the outgoing message variables.
Three things to know:
- Put overrides on
.str-chat. The SDK declares every variable on.str-chat, so a value set on:rootor on a wrapper element never reaches the UI. - Put the dark rule after the light one. Both selectors have the same specificity, so the later rule wins. A value set only on
.str-chatapplies to both themes. - Override on
.str-chat, not on an element inside it. A variable that points at another one gets its value on.str-chat. For example,--str-chat__chat-text-incomingpoints at--str-chat__text-primary. Changing--str-chat__text-primaryon an element inside.str-chatdoesn't change--str-chat__chat-text-incomingthere.
Reading the tables
- Light is the default value. When it points at another variable, the value that resolves to is in brackets.
- Dark is the value inside
.str-chat__theme-dark. It shows only the resolved value when the variable points at the same variable as in the light theme, and "same" when nothing changes. - The tables list the variables a built-in rule reads. The stylesheet declares a few more, such as
--str-chat__accent-warningand--str-chat__border-utility-success, that no rule reads, so changing them has no effect.
Theme
Accent colors
| Variable | Light | Dark | Used for |
|---|---|---|---|
--str-chat__accent-primary |
--str-chat__brand-500 (#005fff) |
--str-chat__brand-400 (#4586ff) |
The brand color of controls and indicators: primary buttons, selected checkboxes and switches, mentions, the read status, the played part of a waveform, poll results, upload progress |
--str-chat__accent-success |
--str-chat__green-400 (#00a46e) |
--str-chat__green-300 (#00c384) |
Success messages under form fields; the online indicator builds on it |
--str-chat__accent-error |
--str-chat__red-500 (#d90d10) |
--str-chat__red-400 (#fc526a) |
Errors and destructive actions: failed uploads, field errors, delete buttons |
--str-chat__accent-neutral |
--str-chat__chrome-500 (#687385) |
#ababab |
De-emphasized indicators: the offline indicator, neutral badges, progress bars, a switch that is off |
Text colors
| Variable | Light | Dark | Used for |
|---|---|---|---|
--str-chat__text-primary |
--str-chat__chrome-900 (#1a1b25) |
#f8f8f8 |
Main text: message text, titles and most labels |
--str-chat__text-secondary |
--str-chat__chrome-700 (#414552) |
#d8d8d8 |
Supporting text: subtitles, timestamps in lists, secondary labels |
--str-chat__text-tertiary |
--str-chat__chrome-500 (#687385) |
#ababab |
Lowest-emphasis text: hints, placeholders, empty states |
--str-chat__text-disabled |
--str-chat__chrome-300 (#a3acba) |
#6a6a6a |
Text and icons of disabled controls |
--str-chat__text-link |
--str-chat__brand-500 (#005fff) |
--str-chat__brand-600 (#a5c5ff) |
Links and inline actions, such as the replies button and "also sent in channel" |
--str-chat__text-on-accent |
--str-chat__chrome-0 (#ffffff) |
--str-chat__chrome-1000 (#ffffff) |
Text on an accent-colored surface; tooltips |
--str-chat__text-on-inverse |
--str-chat__chrome-0 (#ffffff) |
#000000 |
Text on an inverse surface: notifications, the command chip |
Background colors
| Variable | Light | Dark | Used for |
|---|---|---|---|
--str-chat__background-core-app |
--str-chat__chrome-0 (#ffffff) |
#000000 |
The outermost background, behind the message list |
--str-chat__background-core-elevation-0 |
--str-chat__chrome-0 (#ffffff) |
#000000 |
The base layer: text inputs and upload progress overlays |
--str-chat__background-core-elevation-1 |
--str-chat__chrome-0 (#ffffff) |
--str-chat__chrome-50 (#1c1c1c) |
Panels: the channel list, headers, the composer, modals |
--str-chat__background-core-elevation-2 |
--str-chat__chrome-0 (#ffffff) |
--str-chat__chrome-100 (#323232) |
Floating surfaces: menus, popovers, the reaction picker |
--str-chat__background-core-elevation-3 |
--str-chat__chrome-0 (#ffffff) |
--str-chat__chrome-200 (#565656) |
Badges and reactions that sit above other UI |
--str-chat__background-core-surface-default |
--str-chat__chrome-100 (#ebeef1) |
#323232 |
Grouped areas; incoming bubbles and secondary buttons build on it |
--str-chat__background-core-surface-subtle |
--str-chat__chrome-50 (#f6f8fa) |
#1c1c1c |
Receded areas: date separators, system messages, the suggestion list |
--str-chat__background-core-surface-card |
--str-chat__chrome-50 (#f6f8fa) |
--str-chat__chrome-100 (#323232) |
Cards: polls and switch fields |
--str-chat__background-core-surface-strong |
--str-chat__chrome-150 (#d5dbe1) |
#464646 |
Areas that stand out, such as attachment cards in incoming bubbles |
--str-chat__background-core-inverse |
--str-chat__chrome-1000 (#000000) |
#ffffff |
High-contrast surfaces: notifications, tooltips, the command chip |
--str-chat__background-core-on-accent |
--str-chat__chrome-0 (#ffffff) |
--str-chat__chrome-1000 (#ffffff) |
Elements on an accent color, such as the playback thumb |
--str-chat__background-core-highlight |
--str-chat__yellow-50 (#fef9da) |
--str-chat__yellow-800 (#5f1a05) |
Highlighted and pinned messages |
--str-chat__background-core-overlay-light |
rgba(255, 255, 255, 0.75) |
rgba(0, 0, 0, 0.75) |
A light translucent layer: the drop zone, image placeholders |
--str-chat__background-core-scrim |
rgba(26, 27, 37, 0.5) |
rgba(0, 0, 0, 0.75) |
The layer behind modals |
--str-chat__background-utility-hover |
rgba(26, 27, 37, 0.1) |
rgba(255, 255, 255, 0.15) |
Hover state of interactive elements |
--str-chat__background-utility-pressed |
rgba(26, 27, 37, 0.15) |
rgba(255, 255, 255, 0.2) |
Pressed state of interactive elements |
--str-chat__background-utility-selected |
rgba(26, 27, 37, 0.2) |
rgba(255, 255, 255, 0.25) |
Selected state: the active channel or thread, a selected reaction |
--str-chat__background-utility-disabled |
--str-chat__chrome-100 (#ebeef1) |
#323232 |
Disabled buttons and composer |
--str-chat__skeleton-loading-base |
--str-chat__base-transparent-0 (rgba(255, 255, 255, 0)) |
same | Base tone of loading placeholders |
--str-chat__skeleton-loading-highlight |
--str-chat__background-core-overlay-light (rgba(255, 255, 255, 0.75)) |
rgba(0, 0, 0, 0.75) |
The moving shimmer of loading placeholders |
Border colors
| Variable | Light | Dark | Used for |
|---|---|---|---|
--str-chat__border-core-default |
--str-chat__chrome-150 (#d5dbe1) |
--str-chat__chrome-200 (#565656) |
Standard borders: panels, inputs, cards, dividers |
--str-chat__border-core-subtle |
--str-chat__chrome-100 (#ebeef1) |
#323232 |
Lighter separators, such as between list items |
--str-chat__border-core-strong |
--str-chat__chrome-300 (#a3acba) |
#6a6a6a |
Emphatic borders; borders inside incoming bubbles build on it |
--str-chat__border-core-inverse |
--str-chat__chrome-0 (#ffffff) |
#000000 |
The ring around the online indicator |
--str-chat__border-core-on-inverse |
--str-chat__chrome-0 (#ffffff) |
#000000 |
Borders on inverse surfaces, such as badges |
--str-chat__border-core-opacity-strong |
rgba(26, 27, 37, 0.25) |
rgba(255, 255, 255, 0.25) |
Translucent borders: waveform bars, the playback thumb |
--str-chat__border-utility-focused |
--str-chat__brand-150 (#c3d9ff) |
#19418d |
The keyboard focus ring |
--str-chat__border-utility-error |
--str-chat__accent-error (#d90d10) |
#fc526a |
Inputs with an error |
--str-chat__border-utility-disabled |
--str-chat__chrome-100 (#ebeef1) |
#323232 |
Disabled buttons and inputs |
Focus outline
Every focusable element inside .str-chat draws this outline when the browser shows its focus, as with keyboard navigation.
| Variable | Light | Dark | Used for |
|---|---|---|---|
--str-chat__focus-outline-color |
--str-chat__border-utility-focused (#c3d9ff) |
#19418d |
Color of the focus ring |
--str-chat__focus-outline |
2px solid var(--str-chat__focus-outline-color) |
same | The outline drawn on a focused element |
--str-chat__focus-outline-offset |
2px |
same | Gap between the element and its focus ring |
Typography
Font family
The SDK doesn't load the Geist font. If your app doesn't load it either, text falls back to the system fonts in the stack.
| Variable | Default | Used for |
|---|---|---|
--str-chat__font-family |
var(--str-chat__typography-font-family-sans), -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, Adwaita Sans, Cantarell, Ubuntu, roboto, noto, helvetica, arial, sans-serif |
The font stack of every text style |
--str-chat__typography-font-family-sans |
var(--str-chat__font-family-geist) |
The first font in the stack |
--str-chat__font-family-geist |
'Geist' |
The design system font |
Text styles
Each text style is a font shorthand, used as font: var(--str-chat__font-body-default). It combines a weight, a size and a line height from the tables below, with --str-chat__font-family.
| Variable | Weight | Size | Line height |
|---|---|---|---|
--str-chat__font-heading-xs |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-sm (14px) |
--str-chat__typography-line-height-normal (20px) |
--str-chat__font-heading-sm |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-md (16px) |
--str-chat__typography-line-height-normal (20px) |
--str-chat__font-heading-md |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-lg (18px) |
--str-chat__typography-line-height-relaxed (24px) |
--str-chat__font-heading-lg |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-xl (20px) |
--str-chat__typography-line-height-relaxed (24px) |
--str-chat__font-body-default |
--str-chat__typography-font-weight-regular (400) |
--str-chat__typography-font-size-md (16px) |
--str-chat__typography-line-height-normal (20px) |
--str-chat__font-body-emphasis |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-md (16px) |
--str-chat__typography-line-height-normal (20px) |
--str-chat__font-caption-default |
--str-chat__typography-font-weight-regular (400) |
--str-chat__typography-font-size-sm (14px) |
--str-chat__typography-line-height-normal (20px) |
--str-chat__font-caption-emphasis |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-sm (14px) |
--str-chat__typography-line-height-normal (20px) |
--str-chat__font-metadata-default |
--str-chat__typography-font-weight-regular (400) |
--str-chat__typography-font-size-xs (12px) |
--str-chat__typography-line-height-tight (16px) |
--str-chat__font-metadata-emphasis |
--str-chat__typography-font-weight-semi-bold (600) |
--str-chat__typography-font-size-xs (12px) |
--str-chat__typography-line-height-tight (16px) |
--str-chat__font-numeric-md |
--str-chat__typography-font-weight-bold (700) |
--str-chat__typography-font-size-xxs (10px) |
100% |
--str-chat__font-numeric-xl |
--str-chat__typography-font-weight-bold (700) |
--str-chat__typography-font-size-sm (14px) |
100% |
Font sizes
| Variable | Default | Used for |
|---|---|---|
--str-chat__typography-font-size-xxs |
--str-chat__font-size-size-10 (10px) |
Smallest counts, such as badge numbers |
--str-chat__typography-font-size-xs |
--str-chat__font-size-size-12 (12px) |
Metadata: timestamps, captions under attachments |
--str-chat__typography-font-size-sm |
--str-chat__font-size-size-14 (14px) |
Captions and small labels |
--str-chat__typography-font-size-md |
--str-chat__font-size-size-16 (16px) |
Body text |
--str-chat__typography-font-size-lg |
--str-chat__font-size-size-18 (18px) |
Medium headings |
--str-chat__typography-font-size-xl |
--str-chat__font-size-size-20 (20px) |
Large headings |
--str-chat__typography-font-size-2xl |
--str-chat__font-size-size-24 (24px) |
Large emoji in the reaction picker and gallery |
Line heights
| Variable | Default | Used for |
|---|---|---|
--str-chat__typography-line-height-tight |
--str-chat__line-height-line-height-16 (16px) |
Metadata and compact labels |
--str-chat__typography-line-height-normal |
--str-chat__line-height-line-height-20 (20px) |
Body text and small headings |
--str-chat__typography-line-height-relaxed |
--str-chat__line-height-line-height-24 (24px) |
Larger headings |
Font weights
| Variable | Default | Used for |
|---|---|---|
--str-chat__typography-font-weight-regular |
400 |
Regular text |
--str-chat__typography-font-weight-medium |
500 |
Medium-weight labels, such as in the gallery and number inputs |
--str-chat__typography-font-weight-semi-bold |
600 |
Headings and emphasized text |
--str-chat__typography-font-weight-bold |
700 |
Counts and numbers in badges |
Corner radius
| Variable | Default | Used for |
|---|---|---|
--str-chat__radius-none |
--str-chat__radius-0 (0px) |
No rounding |
--str-chat__radius-xs |
--str-chat__radius-4 (4px) |
Very small rounding |
--str-chat__radius-sm |
--str-chat__radius-6 (6px) |
Small rounding |
--str-chat__radius-md |
--str-chat__radius-8 (8px) |
Menus, list items, inline attachments |
--str-chat__radius-lg |
--str-chat__radius-12 (12px) |
Cards and attachments |
--str-chat__radius-xl |
--str-chat__radius-16 (16px) |
Modals and system messages |
--str-chat__radius-2xl |
--str-chat__radius-20 (20px) |
Message bubbles |
--str-chat__radius-3xl |
--str-chat__radius-24 (24px) |
The composer, notifications, the audio recorder |
--str-chat__radius-4xl |
--str-chat__radius-32 (32px) |
The reaction picker |
--str-chat__radius-max |
--str-chat__radius-full (9999px) |
Pill shapes and circles: avatars, badges, round buttons |
Shadows
The shadows are layered box-shadow values. Each theme points them at its own elevation values.
| Variable | Light | Dark | Used for |
|---|---|---|---|
--str-chat__box-shadow-2 |
--str-chat__light-elevation-2 |
--str-chat__dark-elevation-2 |
Raised elements: buttons, badges, channel list items |
--str-chat__box-shadow-3 |
--str-chat__light-elevation-3 |
--str-chat__dark-elevation-3 |
Floating elements: menus, tooltips, the emoji and reaction pickers |
| Variable | Value |
|---|---|
--str-chat__light-elevation-2 |
0px 0px 0px 1px rgba(0, 0, 0, 0.05), 0px 2px 4px 0px rgba(0, 0, 0, 0.12), 0px 6px 16px 0px rgba(0, 0, 0, 0.06) |
--str-chat__light-elevation-3 |
0px 0px 0px 1px rgba(0, 0, 0, 0.05), 0px 4px 8px 0px rgba(0, 0, 0, 0.14), 0px 12px 24px 0px rgba(0, 0, 0, 0.1) |
--str-chat__dark-elevation-2 |
0px 0px 0px 1px rgba(255, 255, 255, 0.15), 0px 2px 4px 0px rgba(0, 0, 0, 0.22), 0px 6px 16px 0px rgba(0, 0, 0, 0.12) |
--str-chat__dark-elevation-3 |
0px 0px 0px 1px rgba(255, 255, 255, 0.15), 0px 4px 8px 0px rgba(0, 0, 0, 0.24), 0px 12px 24px 0px rgba(0, 0, 0, 0.14) |
Layout
Spacing
| Variable | Default | Used for |
|---|---|---|
--str-chat__spacing-none |
--str-chat__space-0 (0px) |
No space |
--str-chat__spacing-xxxs |
--str-chat__space-2 (2px) |
Hairline gaps |
--str-chat__spacing-xxs |
--str-chat__space-4 (4px) |
Minimal padding and tight gaps |
--str-chat__spacing-xs |
--str-chat__space-8 (8px) |
Small padding and default gaps |
--str-chat__spacing-sm |
--str-chat__space-12 (12px) |
Internal spacing in inputs and buttons |
--str-chat__spacing-md |
--str-chat__space-16 (16px) |
Padding of sections, headers and the composer |
--str-chat__spacing-lg |
--str-chat__space-20 (20px) |
Spacing between groups |
--str-chat__spacing-xl |
--str-chat__space-24 (24px) |
Comfortable spacing in panels |
--str-chat__spacing-2xl |
--str-chat__space-32 (32px) |
Large spacing in panels and modals |
--str-chat__spacing-3xl |
--str-chat__space-40 (40px) |
Wide layout spacing, such as empty states |
Sizes
| Variable | Default | Used for |
|---|---|---|
--str-chat__icon-size-xs |
--str-chat__size-12 (12px) |
Extra small icons |
--str-chat__icon-size-sm |
--str-chat__size-16 (16px) |
Small icons |
--str-chat__icon-size-md |
--str-chat__size-20 (20px) |
Medium icons |
--str-chat__icon-size-lg |
--str-chat__size-32 (32px) |
Large icons, such as empty-state illustrations |
--str-chat__icon-stroke-default |
--str-chat__w150 (1.5px) |
Default icon stroke width |
--str-chat__icon-stroke-emphasis |
--str-chat__w200 (2px) |
Bold icon stroke width |
Scale values
The spacing, radius, size and typography variables above point at these steps. The default theme uses only some of them; the rest are there for your own overrides.
| Variables | Values | Steps for |
|---|---|---|
--str-chat__size-<n> |
<n>px, for n = 2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 128, 144, 208, 240, 320, 480, 560, 640, 760 |
Sizes |
--str-chat__space-<n> |
<n>px, for n = 0, 2, 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80 |
Spacing steps |
--str-chat__radius-<n> |
<n>px, for n = 0, 2, 4, 6, 8, 12, 16, 20, 24, 32; --str-chat__radius-full is 9999px |
Corner radius steps |
--str-chat__font-size-size-<n> |
<n>px, for n = 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, 28, 32, 40, 48, 64 |
Font sizes |
--str-chat__line-height-line-height-<n> |
<n>px, for n = 8, 10, 12, 13, 14, 15, 16, 17, 18, 20, 24, 28, 32, 40, 48 |
Line heights |
--str-chat__w<n> |
--str-chat__w100: 1px, --str-chat__w120: 1.2px, --str-chat__w150: 1.5px, --str-chat__w200: 2px, --str-chat__w300: 3px, --str-chat__w400: 4px |
Stroke widths |
Best Practices
- Use global variables for changes that should apply everywhere, such as the brand color or the font.
- Keep one source of truth for colors and typography, so your tokens don't drift apart.
- Change layout variables sparingly. Components are aligned to the spacing scale.
- Check contrast and spacing in both the light and dark themes after a change.
- Move to component variables only when a global change reaches too far.
The colors come from the generated light.scss and dark.scss. The text styles are in variables/fonts.scss, the shadows in variables/shadows.scss and the focus outline in base.scss.