6 min read

Shipping an embedded widget nobody complains about

Four constraints that decide whether a script you put on other people's websites is a product or a support queue.

Code that runs on your own site can assume things. Code that runs on ten thousand sites you have never seen can assume almost nothing — not the CSS reset, not the framework, not the Content Security Policy, not even that the DOM is stable.

Four constraints do most of the work of making that survivable.

Encapsulate the DOM and the styles

Attach a shadow root and put everything inside it. Styles defined in there do not escape, and the host page's styles do not reach in. A customer's global `button { text-transform: uppercase }` or an aggressive reset cannot reshape your interface.

Without this you will spend the life of the product fielding "the widget looks broken on our site" reports that reproduce nowhere else.

Fail silently, always

If the config request fails, do nothing. If the key is missing, log one warning and stop. If the API is down, do not render.

A customer's visitors must never see an error caused by your script. The worst acceptable outcome is that the button is not there.

Treat the bundle size as a gate, not a goal

"We try to keep it small" survives exactly one urgent feature. Put a byte budget in the build and fail the build when it is exceeded.

Then the conversation about adding a dependency happens at the moment of the decision, with a number attached, instead of six months later when someone runs a Lighthouse report.

Do not make them weaken their CSP

A widget that needs unsafe-inline or unsafe-eval is asking a customer to reduce protection across their entire site for one feature. Many security teams will simply say no, and they are right to.

Load from one origin, call one API origin, inject no inline script. The change you are asking for is then two entries, and it does not weaken anything.

The accessibility ones are not optional either

A labelled dialog, focus trapped while open, Escape closing and returning focus to the launcher, visible focus rings, a live region for status messages, and motion that respects prefers-reduced-motion.

These take an afternoon. Retrofitting them after a customer's accessibility audit flags your widget takes considerably longer, and you do it under a deadline someone else set.