npm package: @squeletteapp/widget
Description: Embeddable Squelette widget
Author: Squelette
Latest version: 3.1.4

# 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

```html
<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:

```html
<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 to `open`, `close`, or `submit` events. 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:

```css
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

```bash
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:

```js
window.dispatchEvent(
  new CustomEvent('squelette:identity', {
    detail: {
      userId: '123',
      email: 'person@squelette.app',
      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.app` via the iframe `src` URL. 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.