Widget details for careful sites.
The widget is one script tag. This page is for sites with a Content Security Policy, their own dialogs, or a taste for a good entrance.
Escape closes one layer at a time.
Escape closes the chat, one layer at a time: when your own modal dialog or popover is open,
Escape closes that first. A modal dialog or popover inside a closed shadow root is hidden from
the page, so there one Escape closes both. Use an open shadow root, or call preventDefault() on that Escape, to keep the chat open.
Running a Content Security Policy?
Add the cStar host to three directives in your existing policy. The widget loads its scripts, calls its API and opens the chat frame from there.
A blocked chat sends csp_blocked to your CStarChat.on('error') handler (older versions sent config_failed). See the widget load errors.
script-src 'self' https://www.cstar.help;
frame-src https://www.cstar.help;
connect-src 'self' https://www.cstar.help; Built your own chat layout with data-cstar-* attributes?
It signs customers in and streams replies from cStar's database host, so connect-src needs that host as well.
connect-src 'self' https://www.cstar.help https://kmorvgcubqthkfrasbny.supabase.co wss://kmorvgcubqthkfrasbny.supabase.co; No 'unsafe-inline' needed for styles. When style-src blocks the widget's inline styles, it applies them with a constructed stylesheet, which CSP allows.
Your console still shows a few reports from cStar scripts about blocked inline styles, and you can
ignore them. Older browsers without constructed stylesheets need 'unsafe-inline' in style-src. Without it the widget stays hidden and logs
the fix in the console.
Using nonces? Put yours on the cstar.js tag. The widget passes it to every script
and style it adds, so a nonce-only policy works without 'strict-dynamic'.
Give your chat button an entrance.
A puff of smoke before the button shows up, a confetti cannon when the chat opens. Pick one for each moment in Settings > Chat Widget > Entrance Effects and watch the preview. Visitors who ask their device for less motion see no effect, and the button always shows.
Or set them through the API. custom plays your own effect:
keyframes of opacity, scale, translate and rotate, plus an optional particle burst. Numbers only,
in steps of 42 ms, never longer than 840 ms.
curl -X PATCH "https://www.cstar.help/api/v1/teams/YOUR_TEAM_ID/settings/widget" \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{"entrance":{"launcher":"custom","open":"confetti","custom":{
"keyframes":[{"opacity":0,"translateY":42,"rotate":-12},{"scale":1.1},{}],
"durationMs":630,"easing":"bounce",
"particles":{"count":13,"shape":"star","colors":["#8b5cf6","#ffd700"]}}}}' Want full control? Add onEntrance before the cStar tag.
It runs on your own page for each moment, and preventDefault() skips the effect you saved, so you can
play your own. Entrances play in the widget this tag adds, not in a layout you build with data-cstar-* attributes or the npm packages.
<script>
window.cStarConfig = {
onEntrance(entrance) {
// entrance.moment: 'launcher' or 'open'
// entrance.element: the chat button, or the chat panel
if (entrance.reducedMotion) return;
entrance.preventDefault();
// filter and rotate, not opacity and transform: the widget's own
// transitions on those would win over your animation.
entrance.element.animate(
[
{ filter: 'opacity(0)', rotate: '-180deg' },
{ filter: 'opacity(1)', rotate: '0deg' }
],
{ duration: 420, easing: 'ease-out' }
);
}
};
</script>