@askimut/widget #
Embeddable Askimut widgets: show your answered Q&A and accept native Questions on any site. Built as Elena Progressive Web Components — plain light-DOM elements, no Shadow DOM, no framework required.
<askimut-list>— renders the Recipient's answered Q&A as stacked groups (same presentation as the Askimut profile), each card linking to its Askimut permalink.<askimut-ask>— the ask slot: sign-in button (OAuth popup on Askimut), ask form, or nothing when the Recipient's Questions are closed. Submits native (askimutSource) Questions.fetchQuestions/fetchQuestion— a headless API client that fetches the same data without rendering any UI.
Both are composed inside a plain <askimut> wrapper element (not a registered custom element) that scopes the Recipient and theme to its children.
CDN quick start #
Drop this into any static page — no bundler. Replace the handle and the example origin with your own; the URL is stable on the Askimut static origin:
<link rel="stylesheet" href="https://askimut.example.com/widget/widget.css" />
<script type="module" src="https://askimut.example.com/widget/widget.js"></script>
<askimut recipient="you.example.com">
<askimut-list></askimut-list>
<askimut-ask></askimut-ask>
</askimut>
The script registers askimut-list and askimut-ask on import. With a CDN embed you don't need the origin attribute: the widget defaults to the origin the bundle itself was loaded from.
npm quick start #
For bundler-based sites (React, Astro, Svelte, …):
pnpm add @askimut/widget
// Importing the module registers both custom elements (it calls define() for you).
import "@askimut/widget";
// Named imports skip registration — call define() yourself if you use them:
import { AskimutList, AskimutAsk } from "@askimut/widget";
// AskimutList.define();
// AskimutAsk.define();
<askimut recipient="you.example.com" origin="https://askimut.example.com">
<askimut-list></askimut-list>
<askimut-ask></askimut-ask>
</askimut>
When the bundle ships with the host page (npm embed), set origin explicitly — the default (the bundle's own origin) would incorrectly point at your site.
Headless API client #
The package also ships a data-only client for the public Widget API — the same reads the widgets perform, with no rendering, no components, and no DOM required. Useful for SSR, static-site generation, or custom UIs.
// npm — imports nothing visual, registers no elements:
import { fetchQuestions, fetchQuestion } from "@askimut/widget/api";
// CDN — the same module standalone:
// import { fetchQuestions, fetchQuestion } from "https://askimut.example.com/widget/api.js";
// A Recipient's answered Q&A, grouped exactly as <askimut-list> renders it:
const { handle, groups } = await fetchQuestions("you.example.com", {
origin: "https://askimut.example.com",
});
// groups: [{ primary, similar, hiddenCount }] — primary.answer.answerContent.content
// holds the Answer; `handle` (from the API) builds /{handle}/{questionId} permalinks.
// One Question by id — the same public record the /{handle}/{questionId}
// permalink page shows: answered or pending (answer: null), never dismissed
// or deleted, Unavailable Asker redaction applied:
const question = await fetchQuestion("you.example.com", "q-123", {
origin: "https://askimut.example.com",
});
- The
originoption defaults to the origin the module itself was loaded from — convenient on the CDN, wrong for bundler imports, where you should set it explicitly (same rule as the wrapper'soriginattribute). - Any failure throws an
AskimutApiErrorwith the HTTPstatus(0for network failures), the requesturl, and the server's error message; a missingrecipient/questionIdthrows aTypeError. - Pending Questions from Blocked or Suspended askers are hidden, matching the widget list's moderation behavior.
The client is also available as named exports of the main bundle — import { fetchQuestions } from "@askimut/widget" or from the CDN widget.js — at the cost of registering the custom elements on import.
Wrapper attributes #
| Attribute | Required | Meaning |
|---|---|---|
recipient |
yes | Askimut handle or DID. Children share this context. |
origin |
no | Askimut origin for API calls, OAuth popup, and permalinks. Defaults to the origin the widget bundle was loaded from. |
unstyled |
no | Boolean. Removes Askimut branding — see below. |
Children are explicit and order is yours: interleave the widget parts with your own content, or use only one of them.
Styling: styled vs unstyled #
Styled (default). The wrapper defines Askimut design tokens as CSS custom properties, which the descendants consume:
askimut {
--askimut-surface: #ffffff; /* card / input background */
--askimut-ink: #1c1917; /* primary text */
--askimut-muted: #78716c; /* secondary text */
--askimut-border: #e7e5e4; /* card / input border */
--askimut-accent: #2c5ea8; /* link + button accent */
--askimut-radius: 8px; /* corner radius */
--askimut-stack-offset: 8px; /* stacked-card peek offset */
--askimut-font: "IBM Plex Sans", sans-serif;
--askimut-mono: "IBM Plex Mono", monospace;
}
Override any of them on the wrapper (inline or in your stylesheet) to re-theme.
Unstyled. Add the boolean unstyled attribute and Askimut branding goes away:
<askimut recipient="you.example.com" unstyled>
<askimut-list></askimut-list>
<askimut-ask></askimut-ask>
</askimut>
Layout stays (block flow, stacked cards), the --askimut-* hooks remain, and their defaults turn neutral: fonts and text colors inherit from your page, backgrounds and borders go transparent, and buttons fall back to the browser's native look. Style the light-DOM internals directly — e.g. askimut-list .card { … } — or set tokens on the wrapper for finer control.
Components and API #
The Custom Elements Manifest (custom-elements.json, published in the package and served alongside the bundle) documents props, events, and CSS custom properties for both elements.