Style engagement widgets with CSS
The supported CSS surface on the chat bubble, the question bar, the inline button and Starters: eight custom properties, nine shadow parts, and what stays private.
The chat bubble, question bar, inline button and Starters render inside a shadow root, so your storefront's CSS can't reach them by accident, and can't restyle them by accident either. Two mechanisms are open on purpose:
- CSS custom properties: recolour a widget. Use these first.
- Shadow parts (
::part): reach an element directly when a custom property doesn't cover what you need.
Both are written in your own stylesheet. Neither needs a new Content Security Policy directive: the rules are yours, served from your origin, under the style-src you already have. Nothing here replaces the dashboard's appearance fields. Reach for CSS when the structured fields don't express what you want.
The dashboard says Chat bubble, the CSS says launcher
Every identifier on this page (data-iadvize-launcher-host, the
--iadvize-launcher-* properties, the launcher parts) keeps the name it
shipped with. The dashboard now labels that widget format Chat bubble;
nothing in the contract below changed with it, so no stylesheet needs editing.
Only what's listed here is a contract
The names on this page are the whole supported surface. Every other element, attribute, and internal detail of a widget is private and may change without notice, including in a release that carries no changelog entry. Renaming or removing anything listed here is a breaking change and is announced in the changelog.
Host selectors
Every widget the tag renders on a page has a host element you write your rules against. The two floating formats append theirs to <body>; an inline widget's host (the inline button's or Starters' alike) is at its mount point, and which selector reaches it depends on the mount mode:
| Selector | Element |
|---|---|
[data-iadvize-launcher-host] | One per launcher widget rendered here |
[data-iadvize-composer-host] | One per question bar rendered here |
iadvize-widget | An inline button or a Starters widget mounted as our custom element (no attribute involved) |
[data-iadvize-widget-host] | An inline button or a Starters widget we mounted ourselves (the marker element and CSS selector modes) |
All inline forms share the same host selectors, whichever inline format renders inside them. This is part of the contract, so renaming or removing any of it is a breaking change.
The host carries no widget id, so a rule written against these selectors applies to every widget of that type on the page. If you run two launchers on one site, you can't target one of them from CSS. Configure them separately in the dashboard instead. An inline widget is the one exception in practice: in the custom-element mode you can narrow to a single widget through the slot you chose, iadvize-widget[data-iadvize-slot='product-cta'].
CSS custom properties
Recommended, because they don't depend on the widget's internal structure: if we restructure the DOM later, your rules keep working.
Only colours are exposed. Size, corner radius, and shadow are not themeable this way: set them from the widget's Design card, or reach for a part.
Set a property on the host, or on any ancestor it inherits from (:root, body, a wrapper):
[data-iadvize-launcher-host] {
--iadvize-launcher-bg: #111111;
--iadvize-launcher-fg: #ffffff;
}Chat bubble and inline button
| Property | Paints |
|---|---|
--iadvize-launcher-bg | The button's background |
--iadvize-launcher-fg | The button's text colour (i.e. the optional visible label) |
The inline button reuses these, on purpose
There is no --iadvize-button-* set and no button part. An inline button
is the chat bubble's control in a different place (a control, an icon and
a label), so it carries the same two properties and the same three part names
below, and every rule you already wrote against a launcher applies to it
unchanged. Set them on iadvize-widget or [data-iadvize-widget-host], or on
any ancestor they inherit from.
Question bar
| Property | Paints |
|---|---|
--iadvize-composer-bg | The bar's background |
--iadvize-composer-fg | The text the shopper types |
--iadvize-composer-accent | The send button's circle |
--iadvize-composer-accent-fg | The arrow glyph inside the send button |
Starters
Starters reuses both sets: no `--iadvize-starters-*` property
Starters is a composite widget: an icon+label header, up to three
starter-question chips, and a free-text
field. It carries no properties of its own: the header takes
--iadvize-launcher-bg/-fg (the same tinted-icon-and-label pair a chat
bubble uses), the field takes
--iadvize-composer-bg/-fg/-accent/-accent-fg (the same
neutral-bar-plus-accented-send-button pair the question bar uses), and the
chips take --iadvize-starter-bg/-fg below, same as every other format's
chips. Set them on iadvize-widget or [data-iadvize-widget-host], or on any
ancestor they inherit from.
Starter questions
The starter-question chips a widget renders above itself. Both formats use the same two properties, set on whichever host the chips belong to.
| Property | Paints |
|---|---|
--iadvize-starter-bg | A chip's background |
--iadvize-starter-fg | A chip's text |
Unset, a chip inverts its widget's own resolved colours: the widget's foreground becomes the chip's background, and vice versa.
Leave a property unset and the widget renders the value resolved from its dashboard configuration and your site's branding. An unset property changes nothing.
The loader stores these as var() references, so they resolve live. A media query, a dark-mode block, or a runtime setProperty() call all take effect immediately:
@media (prefers-color-scheme: dark) {
[data-iadvize-launcher-host] {
--iadvize-launcher-bg: #f5f5f5;
--iadvize-launcher-fg: #111111;
}
}The glyph and the label share one colour
The launcher's icon is an inline <svg> whose fill is currentColor, so it
follows the same colour as the text label. Setting --iadvize-launcher-fg
recolours both, and so does a color declaration on ::part(launcher).
There is no separate property for the glyph on its own.
Shadow parts
Nine elements carry a part name. Nothing else does.
| Part | Element |
|---|---|
launcher | The <button>: a chat bubble's, or an inline button's |
launcher-icon | The <svg> glyph: inside the launcher button, or inside Starters' non-interactive header |
launcher-label | The <span> holding the label (optional on a bubble, required on an inline button and on Starters) |
composer | The question bar itself, or Starters' free-text field's own wrapper |
composer-input | The text field a shopper types in |
composer-send | The send button |
starters | Starters' own host wrapper, selecting the whole widget |
starter-questions | The column wrapping a widget's starter-question chips |
starter-question | One chip's <button> |
On Starters, `launcher-icon`/`launcher-label` are not a button
Reusing these two part names does not mean Starters' icon+label header is
clickable. Unlike the chat bubble and the inline button, it is plain,
non-interactive markup: no click handler, no keyboard focus stop, no
:focus-visible ring. Only the chips and the free-text field open the
conversation. The names are reused because Shadow Parts select by attribute,
not by the underlying element's interactivity, and the visual role (an icon
beside a line of text) is the same one.
[data-iadvize-launcher-host]::part(launcher) {
box-shadow: none;
border: 2px solid #111111;
}
/* The same three parts reach an inline button. */
iadvize-widget[data-iadvize-slot='product-cta']::part(launcher) {
border-radius: 4px;
}
[data-iadvize-launcher-host]::part(launcher-label) {
letter-spacing: 0.04em;
text-transform: uppercase;
}
[data-iadvize-composer-host]::part(composer-send) {
border-radius: 8px;
}A ::part() rule from your stylesheet beats the inline styles the loader applies, so you don't need !important to override a property we already set. The loader sets box-shadow, border, and border-radius itself, and all three examples above still take effect as written.
Two limits are worth knowing before you build on parts:
- A part freezes internal structure. These eight names are a public API we're committed to, but the elements around them aren't. Prefer a custom property when one covers your case.
- You can't select inside a part.
::part()accepts pseudo-classes (::part(launcher):hoverworks) but not descendant selectors: there's no way to reach an element nested inside a named part.
The label inherits your page's font, on both the chat bubble and the inline button, because a shadow root inherits inheritable properties from its host. That's the default with no CSS from you; setting font-family on the host or on ::part(launcher-label) changes it. There is no font field in the dashboard and the tag requests no font file, so this stylesheet is the only place a face is chosen.
One thing you can only do here, and it matters for an inline button: reserve its space, so nothing on the page moves when it appears. See Reserve the space.
What this surface doesn't cover
- The conversation panel and everything inside it. The conversation runs in a cross-origin iframe, which your CSS cannot style at all. Its colours, font, and logo come from Site → Settings → Branding. See Branding.
- Geometry, via custom properties. There is no custom property for size, corner radius, or shadow. Use the widget's Design card, or a
::part(launcher)/::part(composer)rule. - The page-side override.
iadvize('set', { launcher: … })acceptsenabled,offsetBottom, andoffsetSideonly. There is no page-side way to change a widget's appearance from JavaScript: that's the dashboard's job, or this stylesheet's. - The AI-transparency disclosure. Every control's accessible name and
titlealways carry it (the chat bubble's and the inline button's alike), and so does every starter-question chip's accessible name. No CSS you write, and no field you set, removes it.
Embed the assistant on your storefront
Paste one script tag to load the assistant. Open it from a floating chat bubble, your own button, or both. No login, works from the site key.
Web tag performance
What the web tag costs a storefront page: the size budget we commit to, the current measurement, what we enforce on every change, and how to measure it live on your own storefront.