@cobuntu/widgets
v0.15.0
Published
Drop-in widgets (cart, auth, messages, notifications, search, nav) for Cobuntu communities. One source, two distribution paths: vanilla IIFE bundles served from /widgets/v1/*.js for any landing that can hold a <script> tag, and React wrapper components fo
Maintainers
Readme
@cobuntu/widgets
Drop-in widgets for Cobuntu communities — cart, auth, messages, notifications, search, nav. One source, two distribution paths.
Two ways to consume
1. Vanilla <script> tag
For any landing that can hold a script tag — Squarespace, Webflow, Lovable, plain HTML, WordPress, Framer.
<script src="https://app.cobuntu.com/widgets/v1/cart.js" async></script>
<script src="https://app.cobuntu.com/widgets/v1/auth.js" async></script>
<div data-cobuntu-cart></div>
<div data-cobuntu-auth data-variant="button"></div>Or use a relative URL when the customer's apex is reverse-proxied
through Cobuntu (e.g. belaescala.com/widgets/v1/cart.js).
Per-widget data-attribute + CSS-vars contract is in this repo's
docs/features/cobuntu-*-widget.md files.
2. npm package (React)
For consumers running React with a bundler (Next.js, Vite, etc.):
npm install @cobuntu/widgetsimport { CobuntuCart, CobuntuAuth, CobuntuMessages } from "@cobuntu/widgets";
export function Header() {
return (
<header>
<CobuntuSearch />
<CobuntuMessages requireAuth="hide" />
<CobuntuNotifications requireAuth="hide" />
<CobuntuCart />
<CobuntuAuth variant="button" />
</header>
);
}The React wrappers internally call ensureWidgetScript(...) to lazy-
load the same IIFE bundle from the configured origin (defaults to
https://app.cobuntu.com). One source of truth, no drift.
react is an optional peer dependency — only required if you
import the React wrappers. Vanilla <script> users don't need React.
Layout
packages/widgets/
src/components/ — one .ts per widget (vanilla DOM)
src/components/*.test.ts — vitest suites
src/react/ — React wrapper components
scripts/build.mjs — esbuild → ../../public/widgets/v1/*.js (IIFE)
scripts/build-npm.mjs — esbuild + tsc → dist/{index.mjs, index.cjs, index.d.ts}
dist/ — npm publish artifacts (gitignored)Build
| Output | Command |
|---|---|
| Vanilla IIFE bundles (served at /widgets/v1/) | npm run build |
| npm-publishable bundles (ESM + CJS + d.ts in dist/) | npm run build:npm |
The monorepo's root package.json runs the vanilla build as part of
prebuild. The npm build only runs when publishing
(via prepublishOnly).
Tests
# From the repo root
npx vitest run packages/widgets/srcCoverage is on the pure helpers (cookie reading, JWT decoding, gate predicates, formatters). DOM render paths are exercised in live smoke tests on deployed customer landings — vanilla TS in jsdom drifts from production browser behavior fast.
Versioning
- IIFE URLs are frozen. Once a customer pastes a
<script src="/widgets/v1/cart.js">, the contract (data-attributes + CSS vars +window.CobuntuCartAPI) cannot break. Refactors that change the contract ship under/widgets/v2/; both versions coexist until customers migrate. - npm package follows semver. Pre-1.0 the React API may shift on minor bumps; the underlying IIFE contract stays frozen.
- See CHANGELOG.md for what's in each release.
Publish (Cobuntu staff only)
# From this package directory:
npm run build:npm # produces dist/
npm publish --access public # uploads to npmprepublishOnly runs build:npm automatically, so the second
command is the only one strictly required.
Styling — --cobuntu-* CSS variables
Widget chrome (border, background, radius, padding, color) is driven by
CSS custom properties read off :root:
| Variable | Role |
| -------------------- | ----------------------------------- |
| --cobuntu-bg | Button background |
| --cobuntu-border | Button border |
| --cobuntu-radius | Border radius |
| --cobuntu-padding | Button padding |
| --cobuntu-color | Icon/text color |
| --cobuntu-badge-bg | Cart/notifications badge tint |
| --cobuntu-drawer-bg/--cobuntu-drawer-text | Drawer colors |
Where the vars come from depends on how you serve the page:
Cobuntu-proxied landings (custom-domain customers whose apex is reverse-proxied through the community-app — e.g.
belaescala.com). The proxy splices a<style data-cobuntu-widget-theme>block into the landing's<head>automatically, populated from yourstorefrontConfig.header(admin → Settings → Header). Update the config, refresh, the widget chrome updates. No widget version bump, no landing CSS change.Standalone sites (loading the IIFE bundle directly without the proxy in front). Declare the vars in your own CSS:
:root { --cobuntu-border: 0; --cobuntu-bg: transparent; --cobuntu-radius: 0; }
If neither path runs, the widget falls back to its built-in defaults
(1px solid currentColor, transparent bg, square corners) — usable
but rarely what a polished site wants.
Authed endpoints — same-origin by default
From v0.5.0 onwards, every authed call the widget bundles make
(/api/users/me, /api/users/me/notifications/*,
/api/users/me/friend-requests/*, /api/users/me/friends/*,
/api/users/me/checkout, /api/messaging/conversations/unread/counts)
goes to a same-origin relative path by default. The fetch lands on
the customer apex (e.g. belaescala.com), which the community-app
reverse proxy resolves into a thin per-endpoint route handler in
app/api/... that forwards the visitor's
cobuntu_auth cookie to api.cobuntu.com server-side.
Why same-origin matters: the cobuntu_auth cookie is host-scoped
to the customer's apex by document.cookie (set client-side with no
domain= attribute — see lib/api.ts). A
cross-origin fetch from the widget on foodieslx.com to
api.cobuntu.com carries no cookie even with credentials: "include",
so the BE sees an anonymous request. Same-origin via the apex proxy
makes the cookie travel server-side, which works on every apex
without per-customer setup.
Cookie-flow prerequisite
For widgets to detect login, the user must sign in on the apex
itself (e.g. foodieslx.com/login, which proxies through to
community-app's branded login page and sets the cookie on the
foodieslx.com host). Signing in on app.cobuntu.com or a
<community>.cobuntu.com subdomain lands the cookie on that host,
which the widget on the customer apex can't read.
Build-time override (standalone non-proxied consumers)
A consumer hosting a static site off any Cobuntu-proxied apex
(e.g. an internal dashboard on a domain not reverse-proxied through
community-app) can opt out via the WIDGET_API_URL env var read by
packages/widgets/scripts/build.mjs:
WIDGET_API_URL=https://api.cobuntu.com npm run build:widgetsThat bakes __WIDGET_API_URL__ into the IIFE so every authed call
goes back to an absolute URL. Those consumers ship their own
auth-cookie forwarding too — cross-origin cookies aren't sent
automatically and the BE needs the JWT one way or another.
Architecture
See docs/features/widget-architecture-unification.md for the full
two-distribution design and the rationale for the React wrapper
deferring to the IIFE bundle via ensureWidgetScript.
