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:
- Builder → Design → Custom CSS. Static, applied to every conversation. The right place for theme-level work (colours, typography, sizing).
- 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. - 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.
.MessageBubbleand.messagebubbleare different selectors. - Most overrides need
!importantto 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.Backgroundinstead 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; }
Header
| 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
- Embedding in your app — the parent-page CSS injection patterns these classes are usually used with.
Landbot.Popup,Landbot.Livechat— the widgets that own the launcher and proactive UI.- Themed examples in the help center — Back to School, Minimalist, Translucid, RTL, Carrd, CV Template, Video Bubble, Typewriter, and more end-to-end designs you can copy and adapt.