Custom CSS — fine-tune the widget's look
Paste a CSS string in admin → Widget → Custom CSS. It's injected inside the widget iframe's <head> last, so it wins specificity ties against our defaults.
When to reach for it
Use custom CSS when the Appearance panel can't get you what you need — typographic tweaks, specific spacing, hiding parts of the UI, matching a non-trivial brand system. Don't reach for it first: the Appearance panel handles the common 90% and keeps your widget benefiting from our default styling improvements over time. Custom CSS is a blunt instrument — once you override a selector, our updates to that selector stop affecting you.
Targetable classes
The widget exposes stable, prefixed class names. The main ones:
| Class | What it styles |
|---|---|
.wilow-bubble |
The chat launcher bubble (bottom-right by default). |
.wilow-header |
The top bar with the bot name and close button. |
.wilow-bot-name |
The bot's name text in the header. |
.wilow-message |
Any chat bubble (both visitor and bot — use .wilow-message--user / .wilow-message--bot to differentiate). |
.wilow-input |
The text input at the bottom. |
.wilow-feedback-btn |
The 👍 / 👎 buttons on bot turns. |
.wilow-handoff-cta |
The "Contact a human" button. |
.wilow-lead-chip |
The "Got your details — we'll be in touch" confirmation chip. |
Inspect the running widget with devtools to see the current full set. We add classes as features land; the ones above are under a compatibility promise — we won't rename them without warning.
Worked example — darker chat header
.wilow-header {
background: #0f172a;
color: #f8fafc;
}
.wilow-header .wilow-bot-name {
font-weight: 600;
letter-spacing: 0.02em;
}Limits
- 20 KB max. Enforced at save time; larger inputs are rejected with a clear error.
- No
@import— would fetch external CSS on every widget load. url(...)only acceptshttps://…,data:image/…, or relative paths.javascript:anddata:text/…are rejected to block script injection.- No
expression(),behavior:,-moz-binding,<script>,</style>— historical JS-in-CSS vectors.
Validation runs server-side. A rejected input surfaces an inline error in the editor; fix it and save again.
Testing your CSS
The live preview on the Custom CSS page renders the same widget bundle visitors see, with whatever you've typed into the CSS editor — no need to save first, no devtools required. Type a rule, look at the preview, iterate. Hit Save when you're happy and it goes live on the next widget load on your site.
If the widget on your live site starts looking broken on a specific page and you can't explain it, temporarily clear the custom CSS field, save, and reload that page. If the problem goes away, your CSS is the cause; narrow down with binary search by reintroducing half the rules at a time.
Pitfalls
- Turning the feature off preserves your CSS — if we disable custom CSS on your workspace, the stored CSS stays but the widget stops serving it on the next config fetch. Re-enabling turns it on again automatically. You won't have to re-enter anything.
- Don't style the host page. The widget runs in an iframe, so your CSS can't leak onto the visitor's site (or be overridden by it). That's intentional.
- Avoid
!importantunless necessary. We already inject your CSS last, so specificity is normally on your side.!importantmakes future overrides painful — prefer a more specific selector. - Dark-mode media queries work.
@media (prefers-color-scheme: dark)runs against the visitor's OS preference — use it to match sites that already flip for dark mode.