Cette page est publiée en anglais et en ukrainien ; la version anglaise figure ci-dessous.
Widget theming: CSS tokens and parts
For developers, in code: the --sa-* theme tokens, the three layers that set them, ::part() for everything else, custom CSS, and the orb variables page CSS may touch.
The widget’s appearance is driven by CSS custom properties on its host element. Three layers feed
the same variables, and the precedence is simple: your page’s CSS on #sa-widget > the page’s
ShukaiConfig.theme > the owner’s theme in the panel > the built-in defaults. A page’s
ShukaiConfig.theme, if set, replaces the owner’s theme as a whole — it does not merge token by
token (Configuration). The panel is a friendly front end to what you
could also set in CSS.
In the panel: the colours are on Widget → Appearance → Brand & colours (Widget settings). This page is the same in CSS and code.
#sa-widget {
--sa-primary: #e11d48;
--sa-launcher-radius: 16px;
--sa-font: "Inter", sans-serif;
}
Set the variables on #sa-widget, not on :root — the host’s all: initial does not reset
custom properties, but the orb’s slot variables default to initial and read only from the host.
The theme tokens
| Group | Token | Default | What it styles |
|---|---|---|---|
| palette | primary |
#4f46e5 |
the brand colour |
on-primary |
#ffffff |
ink on the primary; if you set primary and not this, the widget picks a readable ink by luminance |
|
accent |
var(--sa-primary) |
the secondary role (chips, highlights) | |
on-accent |
var(--sa-on-primary) |
ink on the accent; auto-contrast like on-primary |
|
surface |
#ffffff |
bubbles, the card, the composer | |
text |
#1a1a1a |
body text | |
| typography | font |
-apple-system, Segoe UI, Roboto, sans-serif |
base family — load your @font-face on the host page and it is visible inside the shadow root |
font-size |
16px |
base size | |
font-button |
var(--sa-font) |
buttons | |
button-font-size |
13px |
buttons | |
font-teaser |
var(--sa-font) |
the teaser bubble | |
teaser-font-size |
14px |
the teaser bubble | |
| launcher | launcher-bg |
initial (white) |
an image launcher’s fill |
launcher-color |
initial (--sa-text) |
the launcher label’s colour | |
launcher-size |
56px |
the button | |
launcher-radius |
50% |
the button’s corners — one length or percentage; two values or a bare number make a labelled pill draw square | |
launcher-icon-size |
24px |
the icon | |
launcher-image-pad |
8px |
padding around an image | |
launcher-shadow |
0 4px 12px rgba(0,0,0,.2) |
the elevated container’s shadow | |
| shapes | bubble-radius |
14px |
message bubbles |
chip-radius |
16px |
FAQ and suggestion chips | |
| teaser | teaser-bg |
#ffffff |
the greeting bubble |
teaser-color |
#1a1a1a |
its text | |
teaser-radius |
14px |
its corners |
In ShukaiConfig.theme and in the panel the token is written without the prefix
({ "launcher-radius": "16px" }); unknown tokens are ignored, and a value is rejected if it
contains ;, {, }, <, >, a backslash, url(, expression, javascript: or @import,
or runs past 200 characters. Page CSS has no such limit — it is your page.
The orb’s variables
The orb’s body colour is voiceOrbColor (or the primary), drawn through a derived palette per
state. Page CSS may set, beyond the theme tokens:
--sa-orb-<state>and--sa-orb-<state>-inkfor the twelve states (idle,listening,thinking,speaking,dormant,acting,consent,happy,confused,muted,offline,raised);- the skin’s slots,
--sa-orb-<skin>-<slot>(Launcher and skins); Robot 2.0 keeps its older names--sa-orb-face,--sa-orb-eye,--sa-orb-mouth; --sa-orb-mark— the ink of the?/!badge and the raised hand; page CSS only, not a theme token.
Never set the *-auto variables: auto-contrast is computed once at mount from the served colours.
If you override the visor or the eyes from page CSS, set both — an auto-picked partner is not
re-derived for a colour JavaScript cannot see.
::part() for everything the tokens do not reach
Every piece of the widget’s UI carries a part name, so a page can restyle it the ordinary way.
A page’s ::part() rule beats any normal declaration inside the shadow root.
#sa-widget::part(companion-bubble) {
box-shadow: none;
}
#sa-widget::part(nav-suggestion) {
border-radius: 4px;
}
| Component | Parts |
|---|---|
| launcher and teaser | launcher, teaser-group, teaser, teaser-faqs, teaser-faq |
| the companion (dock and column) | companion, companion-dock, companion-aura, companion-mic, companion-stop, companion-dictation, companion-conversation, companion-contact, companion-close, companion-status, companion-ai-label, companion-hint, companion-consent, companion-more, companion-faqs, companion-faq, companion-language, companion-language-popover |
| bubbles | companion-bubbles, companion-bubble, companion-bubble-body, companion-bubble-stub, companion-bubble-dismiss |
| the answer card | companion-card, companion-card-header, companion-card-log, companion-card-list, companion-card-list-header, companion-card-list-close, companion-card-new, companion-card-history, companion-card-close, companion-card-back |
| stream, history, handoff | companion-stream, companion-history, companion-history-row, companion-history-remove, companion-handoff |
| composer | companion-composer, companion-composer-input, companion-send |
| answer parts | nav-suggestions, nav-suggestion, confirm-chips, confirm-accept, confirm-decline, handoff-button, handoff-form, handoff-dismiss, handoff-contact, handoff-comment, handoff-send, handoff-receipt |
| language picker | language-chip, language-popover |
One exception: on touch devices the widget forces its inputs to 16 px !important (to stop iOS
zooming on focus), so ::part(companion-composer-input) { font-size: 14px } loses there.
Custom CSS from the panel
Widget.custom_css is set through the management API only — the panel has no field for it — and
is appended last inside the shadow root, so it wins ties with the widget’s own rules. It can
never touch the host page. Two things about it: a page’s ::part() rule still beats it, and the
right-to-left padding rules are more specific than a plain selector, so .sa-bubble { padding: … }
is ignored for Arabic and Hebrew visitors unless written as .root[data-dir="rtl"] .sa-bubble
(Languages and RTL).