Styling

Class reference and CSS patterns for customising the look of a Landbot v3 web widget.

The Landbot v3 web widget renders into an iframe whose internal markup follows a BEM-ish convention (.Block__Element, .Block--modifier). This page is the class catalogue you'll need when writing custom CSS — what each class targets, with an example.

Warning All classes documented here are v3 only. v2 widgets use a completely different DOM and class scheme.

Where to put CSS

Three places, in increasing order of dynamism:

  1. Builder → Design → Custom CSS. Static, applied to every conversation. The right place for theme-level work (colours, typography, sizing).
  2. Parent-page injection. Insert a <style> element from the page that hosts the widget. The right place for launcher/bubble rules and for site-wide overrides outside the builder's reach. See Embedding in your app.
  3. From inside a Code block. Apply a rule mid-flow with landbotScope.document.styleSheets[0].insertRule(...). The right place for state-dependent styles ("when the user picks dark mode, restyle everything").

Conventions

  • Class names are case-sensitive. .MessageBubble and .messagebubble are different selectors.
  • Most overrides need !important to win against Landbot's defaults. The class catalogues below mark this where it matters.
  • Generic utility classes you'll see throughout the DOM — .is-pulled-right, .columns, .is-multiple, .is-pulled-left — are part of the widget's underlying CSS framework and can be targeted alongside the Landbot-specific classes documented below.
  • A few designs expose CSS custom properties you can override on * or .Background instead of writing full rules. See CSS variables.

Targeting specific blocks

Every flow block renders with a data-block attribute on its container, formatted as <block_id>_<index> (index starts at 0, and for repeated blocks the index increments). Use an attribute selector to target one block in isolation:

[data-block="welcome_0"] .MessageMediaBubble {
  position: relative;
  left: 60px;
}

[data-block="NEJdOSa6m_0"] .MessageBubble { background-color: #636BFC !important; }
[data-block="NEJdOSa6m_1"] .MessageBubble { background-color: #4FCCC2 !important; }
[data-block="NEJdOSa6m_2"] .MessageBubble { background-color: #F58B0B !important; }

:nth-child(n), :nth-child(odd), and :nth-child(even) are supported inside the selector — useful for striping a list of options.

For brick blocks (nested groups), use only the final child id, not the parent path.

Rich HTML in a message

A message body accepts a raw HTML fragment, not just text. For best rendering inside the builder, wrap the HTML in a code block in the message editor.

Use this when CSS alone can't get you the layout you want — typically rich profile cards, structured product listings, or media + caption combinations.

<div class="message-card">
  <img src="https://i.pravatar.cc/300" alt="" width="48" height="48"
       style="border-radius: 50%; float: left; margin-right: 12px;" />
  <div>
    <p style="font-weight: 700; margin: 0;">John Smith</p>
    <p style="opacity: 0.7; margin: 0;">@johnsmith</p>
  </div>
  <p style="clear: both; padding-top: 8px;">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Phasellus nec iaculis mauris.
  </p>
  <p style="opacity: 0.5;">
    <time datetime="2026-01-01">11:09 PM · 1 Jan 2026</time>
  </p>
</div>

Pair the HTML with a targeted CSS rule from the Custom CSS panel (e.g. .message-card { ... }) to keep styling out of inline style attributes when the same layout appears in multiple messages.

Class reference

Background

Class Targets
.Background The full-bleed container behind every conversation.
.Background--image Image background variant.
.Background--color Solid colour background variant.
.Background--video Video background variant.
.Background          { opacity: 0; }
.Background--image   { border: 5px solid black; }
Class Targets
.Header The top bar. Background colour usually needs !important.
.Header__Logo The avatar/logo image.
.Header__Title The bot name.
.Header__Subtitle The status line under the title.
.Header--isSticky Modifier applied while the header is stuck to the top during scroll.
.Header__BotInfo, .Header__NavigationButton Inner layout slots, useful with .Header--isSticky.
.Header { background-color: lightgrey !important; height: 70px; }
.Header { box-shadow: none !important; }
.Header__Title { text-align: right; }

Message bubbles

Class Targets
.MessageBubble Every chat bubble (bot and user).
.MessageBubble__content The text inside the bubble.
.MessageBubbleText Text-only message bubbles.
.is-author-user Modifier on user-authored bubbles. Applies to all user bubbles — you can't target individual ones with this alone. Combine with [data-block=...] if you need granularity.
.Message__message-content The wrapper around bubble + author info.
.MessageBubble           { width: 600px; }
.MessageBubble__content  { font-weight: 900; }
.is-author-user          { background-color: grey !important; }

Media

Class Targets
.MessageMediaBubble Container for inline image/document/video/audio messages.
.Media Generic media wrapper.
.modal, .modal-content, .modal-close The click-to-expand image lightbox.
.MessageMediaBubble img  { width: 600px !important; transform: scale(0.9); }
.MessageMediaBubble      { box-shadow: 10px 10px 10px 10px; }
.modal                   { display: none !important; }   /* disable the lightbox */

Buttons

Class Targets
.input-button An individual choice button.
.input-button-label The label inside an option button.
.input-small-header The "Select one" / "Select up to N" helper text above a button group.
.buttons, .input-buttons The flex container holding the button row.
.is-multiple Modifier on multi-select pickers.
.InputCards__SlideInfoContent, .is-pulled-right Inner layout of picture-card pickers.
.input-small-header                       { color: red; }
.buttons                                  { flex-wrap: nowrap !important; }
.input-buttons .is-multiple               { background-color: lightgrey !important; }
.InputCards__SlideInfoContent .is-pulled-right { display: none; }

Form block

The multi-question Form block lives inside [data-type="multi_question"]. Index individual questions with :nth-child(n) (1-based).

Selector Targets
[data-type="multi_question"] .MessageBubble__content h4 Form title
[data-type="multi_question"] .MessageBubbleText p Form subtitle
[data-type="multi_question"] .Message__message-content Form block width / wrapper
[data-type="multi_question"] form div.columns:nth-child(1) .input-label-text Nth question header (1-based)
[data-type="multi_question"] form div.columns:nth-child(1) .input-help-text Nth question help text
[data-type="multi_question"] form div.columns:nth-child(1) input Nth question input
[data-type="multi_question"] form button Submit button

Dynamic data

Class Targets
.dropdown-item, .DropdownItem__Content Autocomplete dropdown entries.
.InputCards__Title Card carousel title.
.InputCards__Description Card description.
.InputCards__Details Card details line.
.InputCards__HighLighted Highlighted text in a card.
.InputCards__CTA Card call-to-action button.
.InputCards__ArrowPrev, .InputCards__ArrowNext Carousel navigation arrows.
.InputCards__Title        { color: green; }
.InputCards__Description  { color: red; }
.InputCards__CTA          { color: brown !important; border-color: blue !important; }
.InputCards__ArrowNext    { background-color: green; }

Miscellaneous

Class Targets
.is-emoji-button Emoji picker button.
.InputRating Star/number rating widget.
.lb-loader Typing indicator.
.InputTextLong__TextArea Multi-line text input.
.MessageDate Date separators between message groups.
.is-horizontally-centered Centered helper text rows (e.g. "Conversation ended").
.OffsetMenu__Content, .OffsetMenu__CloseButton The slide-out persistent menu.
.button--PersistentMenu Each item in the persistent menu.
.BackButton__Button The "back" button in flows that allow it.
.GdprLink, .GdprLink__link The "Powered by" / GDPR consent line.
.Body The main scrolling region of the conversation.
.Footer The footer (input area) below the conversation.
.panel-container Offline / no-connection banner container.
.lb-loader               { color: blue; }
.BackButton__Button      { display: none; }
.MessageDate             { display: none; }
.button--PersistentMenu  { background-color: transparent; border-radius: 3px !important; }

Scrollbar (WebKit only)

::-webkit-scrollbar       { -webkit-appearance: none; width: 8px; }
::-webkit-scrollbar-thumb { border-radius: 5px; background-color: rgba(0,0,0,0.5); }
.Body                     { overflow: overlay !important; }

Firefox doesn't honour ::-webkit-scrollbar. Use scrollbar-width / scrollbar-color there.

Widget launcher and bubble

Used by the Popup and Livechat widgets. Some classes live on the parent page (the wrapper that contains the iframe), others live inside the iframe.

Scope Class Targets
Parent .LandbotLivechat Livechat outer wrapper on the host page.
Parent .LandbotPopup Popup outer wrapper on the host page.
Parent .LandbotLivechat.is-open, .LandbotPopup.is-open Modifier added when the chat panel is open.
Iframe .LivechatLauncher The launcher bubble.
Iframe .LivechatLauncher .image The image inside the launcher.
Iframe .launcher__bubble, .launcher__avatar, .is-launcher__avatar Avatar slots inside the bubble.
Iframe .LivechatProactive Proactive teaser message floating above the launcher.
Iframe .Proactive__card-content, .Proactive__close__button Inner layout / close button of a proactive card.
Iframe .Proactive__messages, .Proactive__messages article The list of proactive bubbles.

Because the launcher sits outside the iframe and the proactive sits inside, real launcher customisations almost always touch both stylesheets. See Embedding → Injecting CSS at runtime for the two-scope pattern.

A common recipe — bounce the launcher on hover:

@keyframes bounce {
  0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(-30px); }
  60% { transform: translateY(-15px); }
}

.LivechatLauncher {
  animation-duration: 1s;
  animation-fill-mode: both;
  animation-timing-function: ease-in-out;
}

.LivechatLauncher:hover {
  cursor: pointer;
  animation-name: bounce;
  animation-iteration-count: infinite;
}

CSS variables

Some Landbot internals are driven by CSS custom properties — override them on * (or a more specific selector) instead of writing full rules.

Variable Effect
--form_inputs_border_color Border colour of form inputs.
--background_color_gradient_from Start colour of a gradient background.
--background_color_gradient_to End colour of a gradient background.
* {
  --form_inputs_border_color: blue !important;
}

See also