Skip to content
GitHub

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.


On this page

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.

VariableLight defaultDark defaultUsed for
--runbear-background0 0% 100%240 10% 3.9%Widget surface
--runbear-foreground240 10% 3.9%0 0% 98%Body text
--runbear-border240 5.9% 90%240 3.7% 15.9%Dividers and outlines
--runbear-primary240 5.9% 10%0 0% 98%Send button, user message, tooltips
--runbear-primary-foreground0 0% 98%240 5.9% 10%Text on primary
--runbear-secondary240 4.8% 95.9%240 3.7% 15.9%Secondary buttons
--runbear-secondary-foreground240 5.9% 10%0 0% 98%Text on secondary
--runbear-muted240 4.8% 95.9%240 3.7% 15.9%Muted surfaces, hover states
--runbear-muted-foreground240 3.8% 46.1%240 5% 64.9%Placeholder and helper text
--runbear-accent240 4.8% 95.9%240 3.7% 15.9%Hovered controls
--runbear-accent-foreground240 5.9% 10%0 0% 98%Text on accent
--runbear-destructive0 84.2% 60.2%0 62.8% 30.6%Destructive actions
--runbear-destructive-foreground0 0% 98%0 0% 98%Text on destructive
--runbear-input240 5.9% 90%240 3.7% 15.9%Input borders
--runbear-ring240 10% 3.9%240 4.9% 83.9%Focus rings
--runbear-radius0.5rem0.5remCorner radius. A length, not a color
--runbear-chart-1212 100% 38%212 95% 68%First chart series (0.6.0 and later)
--runbear-chart-227 96% 45%30 95% 55%Second chart series (0.6.0 and later)
--runbear-chart-3164 100% 26%160 75% 40%Third chart series (0.6.0 and later)
--runbear-chart-4322 62% 45%322 75% 70%Fourth chart series (0.6.0 and later)

Chart colours come only from these variables, never from the agent: a chart's series take --runbear-chart-1 to --runbear-chart-4 in order.

: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:

  1. Follow your app's theme switch. Put class="dark" on <html>, <body>, or any ancestor of the widget.
  2. Switch one widget only. Add the class to the widget element itself: document.getElementById("runbear-chat-container").classList.add("dark").
  3. Follow the operating system. Add data-runbear-theme="auto" to the widget or any ancestor, and the widget follows prefers-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

HookElement
[data-runbear], #runbear-chat-container, .runbear-chat-windowThe widget's outer element
.runbear-chat-surfaceThe layout element inside it, where the UI is mounted
.runbear-suggestionsThe suggested-questions block
#runbear-chat-formThe message form
#runbear-chat-input-textareaThe message box
#runbear-chat-input-button, #runbear-chat-stop-button, #runbear-chat-attachments-buttonThe send, stop and attachment buttons

Response components (0.3.0 and later; the card hooks from 0.4.0; the image, link, pager, list, table and chart hooks from 0.6.0)

HookElement
[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-selectA confirm or select component
.runbear-list, .runbear-table, .runbear-chartA list, table or chart component (0.6.0 and later), on the same per-component element. It stays when the component shows its fallbackText
.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-cardA card. From 0.6.0 it sits on the per-component element, the one that also carries .runbear-component and data-runbear-component="card"; on 0.4.x and 0.5.x it was on the card's inner box
.runbear-card-title, .runbear-card-subtitleThe card's title and subtitle
.runbear-card-fields, .runbear-card-fieldThe card's field list and one field
.runbear-card-field-label, .runbear-card-field-valueA field's label and value
.runbear-card-imageThe card's image
.runbear-card-links, .runbear-card-linkThe card's links and one link
.runbear-image, .runbear-image-fallbackAn image the widget shows (markdown images included), and the alt text shown when it fails to load
.runbear-linkA link inside a component
.runbear-pager, .runbear-pager-content, .runbear-pager-controlsA paged list or table, its current page, and its controls
.runbear-pager-previous, .runbear-pager-next, .runbear-pager-statusThe Previous and Next buttons and the page status
.runbear-list-title, .runbear-list-items, .runbear-list-itemA list's title, its items, and one item
.runbear-list-item-image, .runbear-list-item-bodyAn item's thumbnail and the text beside it
.runbear-list-item-title, .runbear-list-item-link, .runbear-list-item-subtitleAn item's title, its link, and its subtitle
.runbear-list-item-meta, .runbear-list-item-meta-entryAn item's facts and one fact
.runbear-list-item-meta-label, .runbear-list-item-meta-valueA fact's label and value
.runbear-table-view, .runbear-table-scroll, .runbear-table-gridA table, its scrolling area, and the grid
.runbear-table-caption, .runbear-table-head, .runbear-table-bodyThe table's caption, header row group and body
.runbear-table-header, .runbear-table-sort, .runbear-table-sort-iconA column header, its sort button, and the sort icon
.runbear-table-row, .runbear-table-cellOne row and one cell
.runbear-table-more, .runbear-table-statusThe "…and N more rows" note for rows the table does not show, and the screen-reader sort status
.runbear-chart-title, .runbear-chart-bodyA chart's title and its body
.runbear-chart-figure, .runbear-chart-svg, .runbear-chart-overlayThe chart figure, its SVG, and the layer over the plot
.runbear-chart-axis-x, .runbear-chart-axis-y, .runbear-chart-axis-line, .runbear-chart-axis-labelThe axes, their lines and their labels
.runbear-chart-grid, .runbear-chart-baselineThe grid lines and the zero baseline
.runbear-chart-series, .runbear-chart-series-<n>Every series, and the series in position <n> from 1
.runbear-chart-legend, .runbear-chart-legend-item, .runbear-chart-legend-swatchThe legend, one entry, and the entry's colour swatch
.runbear-chart-hidden-seriesThe note under a chart that carried more series than it draws

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 :root to the widget element and were renamed from --background, --primary and the rest to the --runbear-* names above. Rename your overrides: --primary becomes --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.