The four conflicts that cause almost every ticket
- 1Global input and button styling. Themes set font-family, padding, and borders on every input, frequently with !important, so a widget's own styles lose.
- 2Box sizing. A theme using content-box makes padded inputs overflow their container, which reads as "the form is broken on mobile".
- 3Container width. Fixed-width content wrappers clip anything wider, so the right-hand column of a two-column form disappears.
- 4jQuery and plugin scripts. Older themes ship their own jQuery and plugins that bind to every form submit on the page, intercepting the widget's submission.
Why more CSS is not the fix
The usual vendor response is to ship higher-specificity selectors or its own !important rules. That works until the next theme, then it fails differently, and now the widget's styles are also leaking into the host page and breaking the operator's design. Specificity escalation is an arms race with no end state.
// Style isolation, not specificity escalation
const host = document.querySelector("#chauffeur-quote");
const root = host.attachShadow({ mode: "open" });
root.innerHTML = "<style>" + widgetCss + "</style>" + widgetHtml;Everything inside that shadow root is unreachable from the theme's stylesheet. The widget renders the same on a 2014 theme as it does on a 2026 block theme, which is what makes the product supportable.
What Shadow DOM does not solve
- Caching plugins serving a stale copy of the page. Purge after every change.
- A host container that is itself too narrow. The widget adapts, but it cannot exceed its parent.
- Mail delivery. Style isolation has no opinion on whether your server can send email.
- Content Security Policy headers blocking inline styles or the script's origin.
Run the widget's self-test in the browser console before sending traffic. It confirms the script loaded, the config parsed, and the endpoint answers — which is the difference between a five-minute install and a week of email.