# @bitgate/widget Drop-in custom elements. Framework-agnostic, no bundler required by consumers, and built strictly on the published API. ## Install ```bash npm install && npm run build:widget ``` Copy `packages/widget/dist/bitgate.js` (and its `.map`) onto your own origin. The bundle is deliberately not committed: build it from reviewed source rather than trusting a prebuilt minified blob for a moderation widget. ## The one-element version ```html ``` `` builds the runtime, loads administrative state, and shares both with every descendant. Nothing else to wire. ### Provider attributes | Attribute | Meaning | | --- | --- | | `relays` | Comma-separated relay URLs (required) | | `root` | Root administrator pubkey, from your deployment config | | `policy` | `social`, `commerce`, `admin-only` (default), or `allowlist` | | `viewer` | Viewer pubkey, if known at render time | | `application` / `namespace` | Storage and event namespacing | `allowlist` is the symmetric twin of a denylist: only publishers with a `user-allow` contribution are shown, unlisted ones are hidden across all four dimensions, and protected actors (root, moderators) are never gated. ### Target attributes Any target-bearing element reads its target from markup: ```html … … … ``` Assigning `.target` in JavaScript still works and takes precedence. ### With BitLogin ```js const provider = document.querySelector("bitgate-provider"); await provider.ready; await provider.useSigner(window.nostr); ``` Await `ready` rather than listening for `bitgate:ready` — the event fires during element upgrade, which is before a script that runs after parsing could attach a listener. ## The rule these elements follow **They render decisions and issue commands. They compute no policy.** If an element here ever needed to compare a count to a threshold, that would mean the policy definition is missing a profile — not that the widget needs a number. Keeping that line is the whole reason the engine was extracted. ## Elements ### Viewer-facing | Element | Purpose | | --- | --- | | `` | Wraps content and applies the decision: blur on `restrict`, disclosure on `hide`, warning on `warn` | | `` | Report dialog. Requires no capability — anyone may report | | `` | Readout of effect, reasons, and evidence | ### Moderator-facing | Element | Purpose | | --- | --- | | `` | What the signed-in account may actually do | | `` | A capability-gated button that explains refusals | | `` | Effective state, contributor attribution, and controls | ## Usage ```html ``` Elements receive a runtime; they never construct one. A widget that owned relay connections would make it impossible to share one governance runtime across a page. ## Events All events are `composed` so they cross shadow boundaries. | Event | Fired when | | --- | --- | | `bitgate:revealed` | The viewer chose to show hidden content | | `bitgate:reported` / `bitgate:report-failed` | A report was published or refused | | `bitgate:action-completed` / `:action-refused` / `:action-failed` | An action resolved | | `bitgate:account-restricted` | The admin panel restricted an account | ## Wording The engine emits stable reason identifiers, never prose, so applications phrase them for their own audience: ```js veil.reasonText = { "admin-user-deny": "This seller is suspended", "trusted-report-threshold": "Flagged by several buyers you follow", }; ``` ## Design decisions worth knowing **Hidden content is removed from the accessibility tree**, not merely covered. A screen reader must not read out something the viewer was not meant to see. **A hidden decision renders a disclosure, not nothing.** Content that silently vanishes is indistinguishable from a bug and leaves no route to appeal. Where the profile permits an override, a "Show anyway" control appears; where it does not, the reason still does. **Refused actions stay visible and explain themselves.** A moderator who cannot see that they lack a capability will assume the tool is broken, and a hidden button teaches nothing about the authority model. The command layer enforces the same check independently — the UI is an explanation, never the gate. **Styling is yours.** Elements expose `part` attributes and inherit `color` and `font` from the host. Override the CSS custom properties or target parts: | Token | Purpose | | --- | --- | | `--bitgate-fg` | Foreground / text (`currentColor`) | | `--bitgate-muted` | Secondary text | | `--bitgate-border` | Control borders | | `--bitgate-surface` | Button / panel background | | `--bitgate-warn` | Warning copy | | `--bitgate-danger` | Danger / destructive accent | | `--bitgate-radius` | Control corner radius | Each token prefers `--bitgate-*`, then the deprecated `--gov-*` name (`--gov-fg`, `--gov-muted`, `--gov-border`, `--gov-surface`, `--gov-warn`, `--gov-danger`, `--gov-radius`), then the built-in default. Existing `--gov-*` overrides keep working; new pages should set `--bitgate-*`. `::part()` names are unchanged. ```css bitgate-veil { --bitgate-warn: #c2410c; } bitgate-veil::part(veil) { border: 1px solid red; } ``` ## Version The library version is the `package.json` version, injected into browser bundles at build time: ```js import { VERSION } from "@bitgate/widget"; console.log(VERSION); // e.g. "0.1.0" console.log(window.bitgate.version); // same string, for script-tag hosts ``` `window.bitgate` is created if missing; if a host already put an object there, only `.version` is written. Spec / wire versions (`bitgate/1`, policy `schemaVersion`) are independent of this package version. ## Environment `defineBitGateElements()` is safe to call repeatedly and safe to import in Node — registration is a no-op without a DOM, so a server-rendering host can import the helpers without guarding.