Embed widget
Any website — blog, documentation, static HTML, whatever the stack — can embed your community's comments. The widget loads as an iframe from your community address: no CORS, no tracking, no requests to third parties.
Below, <your-community> stands for your community's address, for example
yourname.pukalani.app.
Adding it
<div id="pukalani-comments"></div>
<script async src="https://<your-community>/embed.js"
data-target-id="my-blogpost-42"
data-target-type="blog"></script>
That's all. If the <div> is missing, the loader creates one itself in front
of the script tag.
Alternative: web component
More convenient in CMSes and frameworks that handle custom elements more
cleanly than injected <script> tags:
<script async src="https://<your-community>/pukalani-comments.js"></script>
<pukalani-comments target-id="my-blogpost-42" target-type="blog"
theme="auto" locale="en" primary="sky"></pukalani-comments>
Attributes react live: theme switches without reloading, the other attributes
reload the iframe. What is rendered is the same sealed-off iframe in the shadow
DOM — security is identical to the script loader.
Attributes
| Attribute | Required | Default | Meaning |
|---|---|---|---|
data-target-id | yes | — | Stable key of the thread (≤ 255 characters) |
data-target-type | no | page | Namespace, e.g. blog, docs (≤ 64 characters) |
data-theme | no | auto | light, dark or auto (follows prefers-color-scheme) |
data-locale | no | en | de or en — language of the widget interface |
data-primary | no | The community's setting | Accent colour from the whitelist (red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose) |
data-container | no | pukalani-comments | ID of the target element; several widgets on one page each need their own container |
target-id, not the URL. A URL-based ID orphans the thread
as soon as the page moves. A slug or the ID from your CMS survives any rebuild.The widget's background is transparent only with data-theme="auto" — it then
takes on the surface of your page.
Switching the theme at runtime
If your page has a light/dark switch of its own:
document.querySelector('#pukalani-comments iframe').contentWindow
.postMessage({ type: 'pukalani:set-theme', theme: 'dark' }, 'https://<your-community>')
Comment counter
embed.js fills in every element carrying data-pukalani-count — handy for
"N comments" links in an article list:
<a href="/blog/post-42#comments">
<span data-pukalani-count data-target-id="post-42" data-target-type="blog">…</span>
</a>
<script async src="https://<your-community>/embed.js"></script>
data-target-type is optional (default page). The counter also works without
a widget on the same page — the script alone is enough.
What you have to set up in your community
- Enable embedding. While the feature is off,
/embedreturns a 404; all other pages forbid framing anyway (clickjacking protection for login and dashboard). - Register domains. Under Dashboard → Embed sites you enter which
domains may embed; optionally you can limit per domain which
target-typevalues are allowed. Changes take effect immediately. Unregistered embedders see a friendly message instead of an empty frame. - Guest comments (optional): commenting without an account — only a freely chosen display name is asked for, no email address. The default is off.
Signing in inside the widget
The sign-in button in the widget opens a popup on your community address — the normal login runs there, including sign-in by code. Afterwards the iframe takes over the session through a short-lived handover token and a partitioned cookie (CHIPS).
The consequence: the sign-in applies per embedding domain (as with other comment services). Browsers that discard partitioned cookies stay in reading mode and show a note with a link to the community.
Data protection with guest comments
If you allow guest comments:
- Only the freely chosen display name is stored from a guest — it appears on the comment line and nowhere else.
- No contact details are collected: no email address, no IP hash. Until 2 August 2026 the form additionally asked for an email address and filed it with an IP hash for moderation. That collection has been dropped without replacement, because the data was never retrievable; old data is deleted after 90 days. So there is no channel for asking a guest a question — anyone who needs that requires an account.
- Guests can neither edit nor vote.
- A tight rate limit applies (5 comments per minute and IP), and the comments count against your community's quota.
Limits
- SEO: the comments live in the iframe under your community address.
Crawlers of the embedding page don't see them — and
/embedis set tonoindexon top of that. - Reading without cookies: following along works without any cookie or storage access, even with strict tracking protection.
- No inline variant: comments directly in the host page's DOM (without an iframe) deliberately don't exist — that would need an HTML sanitiser of its own and an open CORS allowlist.