# @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.