Web SDK theming
Restyle the Runbear chat widget with --runbear-* CSS variables, turn on dark mode, and target its stable class and data hooks from your own CSS.
The widget ships its own stylesheet and keeps it to itself: every rule it emits is scoped to
[data-runbear], the attribute on the widget's own element. It declares nothing on your :root
and doesn't restyle your application. You restyle the widget with CSS variables, and target its
parts through stable class and data hooks.
This page describes 0.3.0 and later. If you are upgrading from 0.2.x, see Migrating from 0.2.x.
CSS variables#
Set any of these on any ancestor of the widget, including :root, and the widget picks them
up. The names below are the public contract; variables not listed here are internal and may change
in any release.
| Variable | Light default | Dark default | Used for |
|---|---|---|---|
--runbear-background | 0 0% 100% | 240 10% 3.9% | Widget surface |
--runbear-foreground | 240 10% 3.9% | 0 0% 98% | Body text |
--runbear-border | 240 5.9% 90% | 240 3.7% 15.9% | Dividers and outlines |
--runbear-primary | 240 5.9% 10% | 0 0% 98% | Send button, user message, tooltips |
--runbear-primary-foreground | 0 0% 98% | 240 5.9% 10% | Text on primary |
--runbear-secondary | 240 4.8% 95.9% | 240 3.7% 15.9% | Secondary buttons |
--runbear-secondary-foreground | 240 5.9% 10% | 0 0% 98% | Text on secondary |
--runbear-muted | 240 4.8% 95.9% | 240 3.7% 15.9% | Muted surfaces, hover states |
--runbear-muted-foreground | 240 3.8% 46.1% | 240 5% 64.9% | Placeholder and helper text |
--runbear-accent | 240 4.8% 95.9% | 240 3.7% 15.9% | Hovered controls |
--runbear-accent-foreground | 240 5.9% 10% | 0 0% 98% | Text on accent |
--runbear-destructive | 0 84.2% 60.2% | 0 62.8% 30.6% | Destructive actions |
--runbear-destructive-foreground | 0 0% 98% | 0 0% 98% | Text on destructive |
--runbear-input | 240 5.9% 90% | 240 3.7% 15.9% | Input borders |
--runbear-ring | 240 10% 3.9% | 240 4.9% 83.9% | Focus rings |
--runbear-radius | 0.5rem | 0.5rem | Corner radius. A length, not a color |
:root {
--runbear-primary: 221 83% 53%;
--runbear-primary-foreground: 0 0% 100%;
--runbear-radius: 0.75rem;
}Dark mode#
Dark mode is off unless you ask for it. There are three ways, in the order most sites want them:
- Follow your app's theme switch. Put
class="dark"on<html>,<body>, or any ancestor of the widget. - Switch one widget only. Add the class to the widget element itself:
document.getElementById("runbear-chat-container").classList.add("dark"). - Follow the operating system. Add
data-runbear-theme="auto"to the widget or any ancestor, and the widget followsprefers-color-scheme. This is opt-in, so a site with its own light and dark control never has the widget disagree with it.
A --runbear-* value you set applies to both palettes. To differ per theme, scope the override
the same way you turned dark mode on:
:root { --runbear-primary: 221 83% 53%; }
.dark { --runbear-primary: 213 94% 68%; }or, with data-runbear-theme="auto":
:root { --runbear-primary: 221 83% 53%; }
@media (prefers-color-scheme: dark) {
[data-runbear-theme="auto"] { --runbear-primary: 213 94% 68%; }
}Class and data hooks#
These names are stable and safe to target from your own CSS.
The widget
| Hook | Element |
|---|---|
[data-runbear], #runbear-chat-container, .runbear-chat-window | The widget's outer element |
.runbear-chat-surface | The layout element inside it, where the UI is mounted |
.runbear-suggestions | The suggested-questions block |
#runbear-chat-form | The message form |
#runbear-chat-input-textarea | The message box |
#runbear-chat-input-button, #runbear-chat-stop-button, #runbear-chat-attachments-button | The send, stop and attachment buttons |
Response components (0.3.0 and later; the card hooks from 0.4.0)
| Hook | Element |
|---|---|
[data-runbear-components] | The wrapper around all of one message's components |
.runbear-component, [data-runbear-component="<name>"] | One component, whatever its name. The attribute carries the component's name, so a component added later is targetable without a new class |
[data-runbear-state="live"], [data-runbear-state="inert"] | On the same element: whether the component still accepts a click |
.runbear-confirm, .runbear-select | A confirm or select component |
.runbear-component-options, [data-runbear-component-options] | The group of option buttons |
.runbear-component-option, [data-runbear-component-option] | One option button |
[data-runbear-component-disclosure] | The "Label → value" lines under the buttons |
.runbear-component-fallback, [data-runbear-component-fallback] | The fallbackText shown when a component can't be rendered |
.runbear-card | A card |
.runbear-card-title, .runbear-card-subtitle | The card's title and subtitle |
.runbear-card-fields, .runbear-card-field | The card's field list and one field |
.runbear-card-field-label, .runbear-card-field-value | A field's label and value |
An inert option button carries aria-disabled="true", not the disabled attribute, so it stays
focusable and readable. Style it with [aria-disabled="true"], not :disabled.
Overriding a rule#
Utility classes the SDK generates apply to the descendants of the widget's outer element.
Nothing the SDK emits uses !important, and its rules have the specificity of one attribute plus
one class.
The SDK's stylesheet is appended to <head> when the module is imported, so it is the last sheet in
the document and wins every tie. To override a rule, be more specific:
#runbear-chat-container .runbear-suggestions button { … } overrides; a bare
.runbear-suggestions button { … } doesn't. The --runbear-* variables need none of this: the
widget reads them from your side of the cascade, so they always apply.
The only rules that reach outside the widget are two that set Tailwind's private --tw-* custom
properties on every element. They affect nothing but Tailwind's own internals.
Migrating from 0.2.x#
0.3.0 isolated the widget's styles, which is a breaking change for any site that themed it:
- The theme variables moved from
:rootto the widget element and were renamed from--background,--primaryand the rest to the--runbear-*names above. Rename your overrides:--primarybecomes--runbear-primary, and so on. --popover*,--card*and--chart-*were removed.- The widget no longer restyles the rest of your page, so anything on your page that relied on the SDK's variables needs its own.
Related#
- Install: the CSP requirement
- Response components: what the component hooks style
- Configuration: assistant name and avatar