@squeletteapp/widget
v3.1.3
Published
Embeddable Squelette widget
Maintainers
Readme
Squelette Widget
Embeddable feedback & roadmap widget built with Preact and delivered as a Web Component. It mounts as <squelette-widget> with an isolated Shadow DOM, lazy-loads the interactive app on demand, and streams feedback from https://api.squelette.app using your project credentials.
CDN usage
<script src="https://cdn.squelette.app/widget.js" async></script>
<script>
const widget = document.createElement('squelette-widget');
widget.setAttribute('project', 'squelette');
widget.setAttribute('board', 'feature-requests');
document.body.appendChild(widget);
</script>Programmatic helper:
<script src="https://cdn.squelette.app/widget.js" async></script>
<script>
window.SqueletteWidget?.create({
project: 'squelette',
board: 'feature-requests',
theme: 'auto',
locale: 'en',
label: 'Share feedback'
});
</script>Attributes & options
| Name | Type | Default | Description |
| ----------- | ------------- | ------- | ----------- |
| project | string | – | Required. Identifies the project in the Squelette API. |
| board | string | null | Optional board/roadmap identifier. |
| theme | light\|dark\|auto | auto | Controls light/dark appearance. auto tracks system preference. |
| locale | string | en | Locale forwarded to the iframe (?locale=). |
| token | string | – | Optional bearer token used when requesting the embedded experience. |
| signature | string | – | Optional request signature for API verification. |
| label | string | Feedback | Launcher button label. |
| open | boolean attr | false | When present, forces the widget open. |
Public methods
Every widget instance exposes an imperative API:
open()/close()/toggle()destroy()– unmounts the application and removes the element.on(event, handler)– subscribe toopen,close, orsubmitevents. Returns an unsubscribe function.
Submit events are forwarded when the embedded iframe posts window.parent.postMessage({ type: 'squelette:submit', payload }, '*').
Theming
Hosts can override CSS custom properties directly on the element:
squelette-widget {
--sq-accent: #ff6b00;
--sq-bg: #11131f;
--sq-fg: #f8fafc;
--sq-border: rgba(15, 23, 42, 0.24);
--sq-radius: 18px;
}These cascade into the Shadow DOM so host styles never leak inside.
Development
bun install
cd packages/widget
bun run dev # Vite dev server with auto registration
bun run build # tsc declarations + Vite library build
bun run test # Vitest (happy-dom)The Vite build emits dist/widget.js (IIFE, ready for <script> tags) and dist/widget.es.js (ESM) plus type declarations under dist/types.
Architecture
src/widget/element.tsx– custom element class, orchestrates Shadow DOM lifecycle.src/widget/components/widget-shell.tsx– Preact UI for the launcher/panel/loader.src/widget/styles.ts– shared base styles injected via constructable stylesheets.src/widget/utils.ts– helpers for attribute reflection, iframe URL shaping, theming.src/app/app.tsx– lazy-loaded iframe renderer that animates on first paint.
Event bridge & identity
Dispatch an identity update from the host page to enrich the embedded experience:
window.dispatchEvent(
new CustomEvent('squelette:identity', {
detail: {
userId: '123',
email: '[email protected]',
name: 'Ada Lovelace'
}
})
);Inside the iframe, send postMessage({ type: 'squelette:submit', payload }) to bubble successful submissions back to the host application.
Security notes
- All network calls are constrained to
https://api.squelette.appvia the iframesrcURL. No arbitrary HTML injection is allowed. - The iframe runs in a restrictive sandbox (
allow-scripts,allow-same-origin,allow-forms, and related capabilities expressly required for the widget). Host tokens can be passed via element attributes.
