For developers · Phase 2

The embeddable widget UI kit

Every visual building block of the Botino chat + voice widget - anatomy, states, theming tokens, data shapes and the JS API. Built so an engineer (or coding agent) can wire a real backend & API to UI that already matches the brand. The same .cw* classes power the live preview in the app's Appearance tab.

Drop-in, framework-agnostic CSS custom-property theming Voice + chat WCAG AA contrast
01 - Anatomy

Launcher and conversation window

The widget is two top-level pieces: a floating launcher bubble and the conversation window it opens. Both inherit your theme tokens and sit in a fixed corner of the host page.

TV Store Assistant
Online · replies instantly
Hi! How can I help with your order today?
Where's my parcel?
Track orderTalk to a human
Powered by Botino
PartClassRole
Header.cw__headerAvatar, bot name, live status, minimize/close.
Message list.cw__bodyScrollable stream of bot/user bubbles & quick replies.
Bot bubble.cw__msg--botAnswer from the knowledge base, with avatar.
User bubble.cw__msg--userVisitor's message, accent colour, right-aligned.
Quick replies.cw__chipsTappable suggested actions / prompts.
Composer.cw__footText input, mic toggle and send button.
Attribution.cw__poweredOptional "Powered by Botino" (hidden on Pro).
Launcher.cw-launcherFloating bubble with optional unread badge.

The widget renders inside a Shadow DOM in production so host-page styles never leak in. Tokens are still exposed as inheritable CSS custom properties - see Theming.

02 - Component states

Every state you'll wire up

Launcher, messages, voice, input, empty, loading and error states - the full surface a backend drives via the event API.

Launcher · idle .cw-launcher
Closed state. Click to open the window.
3
Launcher · unread .cw-launcher__badge
Badge driven by botino:unread count.
Launcher · voicebot
Waveform glyph signals a voice-enabled bot.
Your order #4821 ships tomorrow before 12:00.
Bot message .cw__msg--bot
role: "assistant" · grounded answer.
Can I change the delivery date?
User message .cw__msg--user
role: "user" · accent bubble, right aligned.
Track orderReturnsTalk to a human
Quick replies .cw__chips
Suggested actions from message.quickReplies[].
Typing indicator .cw__typing
Shown between botino:thinking and first token.
“Your order ships tomorrow.”
Listening…
Voice mode .cw__voice
STT/TTS panel · live transcription & waveform.
Mic · idle / recording .is-rec
Toggle with Botino.startVoice().
How can I help?
Ask about orders, products or delivery.
Empty · welcome
First open · greeting from config.welcome.
Loading .skeleton
History fetch in flight · before stream hydrates.
Connection lost
Error · offline
On botino:error · offers retry.
This chat may be processed by AI to assist you. Privacy.
Consent · GDPR / RODO
Optional gate before the first message.
03 - Theming

Theme with CSS custom properties

Every colour the merchant picks in the dashboard's Appearance tab maps to one CSS variable on the .cw root. Set them inline, in your stylesheet, or let the dashboard push them at runtime.

TokenDefaultDrives
--cw-accent#3B5BFCHeader, launcher, send button
--cw-accent-2#6D4AF2Gradient partner
--cw-user#3B5BFCUser message bubble
--cw-bot#EEF2F7Bot message bubble
--cw-text#0B1220Bot bubble text colour
.botino-widget {
  --cw-accent: #3B5BFC;
  --cw-accent-2: #6D4AF2;
  --cw-user: #3B5BFC;
  --cw-bot: #EEF2F7;
  --cw-text: #0B1220;
}
Brand presets
Botino Teal Violet Warm
Botino
Online
Hello!
Hi there
Botino brand
Clinic
Online
Hello!
Hi there
Teal
Store
Online
Hello!
Hi there
Warm
04 - Data model

Shapes the backend exchanges

Wire these JSON contracts to your API. The widget is presentation-only - it renders whatever the backend streams and emits events back.

Widget config (init)
{
  "chatId": "tv-store-9f2a-4c1e",
  "name": "TV Store Assistant",
  "status": "Online · replies instantly",
  "welcome": "Hi! How can I help?",
  "position": "bottom-right",
  "locale": "auto",
  "voice": { "enabled": true, "voiceId": "zofia-pl" },
  "theme": {
    "accent": "#3B5BFC",
    "accent2": "#6D4AF2",
    "userBubble": "#3B5BFC",
    "botBubble": "#EEF2F7"
  },
  "quickReplies": ["Track order", "Returns"]
}
Message object
{
  "id": "msg_01HZ…",
  "role": "assistant",   // user | assistant | system
  "type": "text",        // text | voice | card
  "content": "Your order #4821 ships tomorrow.",
  "quickReplies": ["Track it", "Change date"],
  "sources": [{ "title": "Shipping policy", "url": "/shipping" }],
  "createdAt": "2026-06-04T08:12:30Z",
  "tokens": 142
}

Suggested REST surface: POST /v1/chats/:chatId/messages to send, GET /v1/chats/:chatId/history to hydrate, and a WebSocket / SSE channel for streamed assistant tokens. The widget never holds API keys - it authenticates with a short-lived session token scoped to chatId.

05 - JavaScript API

Methods & events

After the script loads, a global Botino object is available. Drive the widget imperatively and subscribe to lifecycle events to sync your own analytics or CRM.

Methods

Botino.init(config)
Botino.open() · Botino.close() · Botino.toggle()
Botino.sendMessage(text)
Botino.startVoice() · Botino.stopVoice()
Botino.setTheme({ accent, accent2, userBubble, botBubble })
Botino.setUnread(count)
Botino.identify({ userId, email, name })
Botino.reset()

Events

Subscribe with Botino.on(event, cb).

botino:ready botino:open botino:close botino:message botino:thinking botino:reply botino:voicestart botino:voiceend botino:unread botino:handoff botino:error

Script-tag attributes

chat-idBot identifier (required)
data-positionbottom-right | bottom-left
data-localeauto | en | pl
data-openauto | manual
06 - Embed

Drop in anywhere

One script tag for static sites & CMSs; an npm wrapper for SPAs. Both read the same config from the dashboard.

<!-- before </body> -->
<script src="https://cdn.botino.eu/botino-voicebot.js"
        chat-id="tv-store-9f2a-4c1e"
        data-position="bottom-right"
        data-locale="auto" defer></script>
// React / Next.js
import { Botino } from "@botino/widget";

useEffect(() => {
  Botino.init({ chatId: "tv-store-9f2a-4c1e", locale: "auto" });
  Botino.on("botino:handoff", openLiveChat);
  return () => Botino.reset();
}, []);
Do
  • Theme via the CSS custom properties - they cascade into the Shadow DOM.
  • Render streamed tokens into the open bot bubble for a live feel.
  • Use botino:handoff to bridge into your human live-chat.
  • Keep the mic hit-target ≥ 44px (already met by .cw__mic).
Don't
  • Hard-code colours in markup - drive everything from setTheme().
  • Ship API keys to the browser; use a session token scoped to the chatId.
  • Block first paint - the script is defer and self-mounts.
  • Override .cw* internals; use the documented tokens & API.