How to customize the widget appearance with CSS
Colors, position corner, titles, and messages are set in the eChat backend - see Widget settings. Everything else about how the widget sits on the page - the distance from the screen edge, the size of the chat window, its stacking order against your own fixed elements - is done with CSS in your Magento theme.
What your theme CSS can and cannot reach
The Magento extension injects only a small loader script. The widget itself is rendered by eChat as two <iframe> elements in your store page, and its stylesheet is injected as a <style data-echat-styles> tag into your page's <head>:
| Element | What it is |
|---|---|
#echat-button | The launcher button. |
#echat-content | The chat window. |
Both are ordinary elements in your storefront DOM, so your theme's CSS can position, resize, and re-layer them like any other element.
What your CSS cannot reach is the content inside those iframes - the message list, the header, the input field. An iframe is a separate document, and no store stylesheet applies across that boundary. Those parts are styled through the backend settings (Primary Color, Title, Subtitle, and so on).
Use !important, or a more specific selector, on every override. The widget's <style data-echat-styles> tag is appended to <head> when the script loads - that is, after your theme's stylesheets - so a rule with the same specificity as the widget's own would lose the tie on source order.
Selectors
These selectors are stable and safe to target:
| Selector | Matches |
|---|---|
#echat-button | The launcher button iframe. |
#echat-content | The chat window iframe. |
.echat-iframe | Both of them. |
.echat-iframe--button | The launcher button iframe. |
.echat-iframe--content | The chat window iframe. |
The widget also reflects its current state in attributes on those elements, so you can style a specific state instead of all of them:
| Attribute | On | Meaning |
|---|---|---|
[data-position="BOTTOM_RIGHT"] / [data-position="BOTTOM_LEFT"] | both | The corner chosen in the backend. The button and the window carry their own Position setting, so they can differ. |
[data-mode="mobile"] / [data-mode="desktop"] | both | Which layout the widget is using. |
[data-has-title] | button | The launcher has a Title, so it is a wide pill rather than a circle. |
[data-launcher] | button | The launcher is the visible entry point. On mobile the button is hidden without this attribute. |
[data-open] | window | The chat window is open. |
[data-visible] | window | The chat window occupies space. Without it the window is collapsed to 1px, so size overrides must target [data-visible]. |
[data-expanded] | window | The visitor expanded the window to its larger size. |
[data-popover] | window | The window is showing a popover, for example the invitation message. |
Defaults you are overriding
Knowing the built-in values makes the overrides predictable:
| Launcher button | Chat window | |
|---|---|---|
| Distance from the bottom | 0 | 70px - it sits directly above the button |
| Distance from the anchored side | 0 | 0 |
| Size | 70x70, or 200px wide with a Title | 1px when collapsed, 420x700 when visible, 520px / 95vh when expanded |
z-index | 2147483641 | 2147483640 |
On a mobile viewport the open window covers the whole screen and ignores these values.
Where to put the CSS
Add the rules to your theme's stylesheet - for a Luma-based theme, app/design/frontend/<Vendor>/<theme>/web/css/source/_extend.less; for Hyvä, your Tailwind CSS entry file - then deploy static content and flush the cache.
Example: move the widget away from the corner
The most common case: a sticky footer, a cookie bar, or a back-to-top button already occupies the corner. Lift the widget 50px off the bottom and pull it 40px in from the right:
#echat-button {
bottom: 50px !important;
right: 40px !important;
}
/* The window sits 70px above the button, so its offset is 50 + 70. */
#echat-content:not([data-mode="mobile"]) {
bottom: 120px !important;
right: 40px !important;
}
Two things to note:
- The
70pxgap is what keeps the window and the button aligned as one stack. Whatever you set on the button, add70pxto it for the window. - The window rule is scoped with
:not([data-mode="mobile"])on purpose. Without it, an!importantoffset would also apply on mobile and break the full-screen chat window there. The launcher button keeps its offset on mobile either way.
With Position set to Bottom Left, use left instead of right:
#echat-button {
bottom: 50px !important;
left: 40px !important;
}
#echat-content:not([data-mode="mobile"]) {
bottom: 120px !important;
left: 40px !important;
}
Example: make the chat window wider
Size lives on the visible state, so target [data-visible]:
#echat-content[data-visible] {
width: 480px !important;
max-height: 760px !important;
}
Leave [data-expanded] alone unless you also want to change the expanded size:
#echat-content[data-visible][data-expanded] {
width: 600px !important;
}
Example: keep the widget below your own fixed element
The widget uses a very high z-index. If one of your own fixed elements - a cookie bar, a slide-out cart - must stay above it, lower the widget instead of raising yours:
.echat-iframe {
z-index: 2000 !important;
}
Keep the value above the rest of your storefront, or the chat window will end up behind page content.
Example: hide the widget on mobile
#echat-button[data-mode="mobile"],
#echat-content[data-mode="mobile"] {
display: none !important;
}
The widget script is served from the eChat CDN and cached, and your storefront pages are cached by Magento as well. After a change, flush the Magento cache and reload with a hard refresh; a short delay before the new styles appear is normal.
The selectors and state attributes above are stable. The default sizes and offsets in the table are not a contract - they can change in a widget update. Keep your overrides to the properties you actually need, so an update changes as little as possible for you.