Files
bitlogin/packages/demo/public/docs.html
T
thePR0M3TH3AN f725e2cd83 Drop the "B" monogram, use one true logo lockup everywhere
The monogram read as a Bitcoin symbol, which BitLogin has nothing to
do with -- revert the icon/mark back to the brass ring + checkmark
(now brass/gold to match the ring, not white) with no letterform.

Also stop composing the icon and wordmark as two independently-sized
elements in each place they appear (a --mark height here, a
--wordmark height there) -- that let their relative size and spacing
drift apart between the site nav and the widget's header. Bake both
into a single SVG (assets/lockup.svg) with the layout fixed once, and
use that same file's markup everywhere a caller only ever sets one
overall height. Site nav and the widget's welcome screen now render
from the exact same source.

Also fills in the hero credential card's placeholder passphrase
("a few good words") with a realistic 6-word example matching what
the generator actually produces, and centers the last two cards of
the 6-card "Why this is different" grid under the middle two columns
instead of left-aligning them under columns 1-2.
2026-07-21 18:35:38 -04:00

252 lines
24 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Integrate BitLogin — docs</title>
<meta name="description" content="How to add the BitLogin login widget to any static site in a few minutes." />
<link rel="icon" href="./assets/icon.svg" type="image/svg+xml" />
<link rel="manifest" href="./manifest.webmanifest" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,500;0,9..144,600;1,9..144,500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="./assets/site.css" />
</head>
<body>
<nav class="nav">
<a class="brand" href="./index.html">
<svg class="lockup" xmlns="http://www.w3.org/2000/svg" viewBox="0 -74.8 511.6 103.3" role="img" aria-label="BitLogin"> <defs> <linearGradient id="bl-lockup-seal" x1="0" y1="0" x2="1" y2="1"> <stop offset="0" stop-color="#8368ff" /> <stop offset="1" stop-color="#6a4de8" /> </linearGradient> <linearGradient id="bl-lockup-brass" x1="0" y1="0" x2="1" y2="1"> <stop offset="0" stop-color="#e6c481" /> <stop offset="1" stop-color="#b3924a" /> </linearGradient> </defs> <g transform="translate(0 -61.88) scale(1.3496)"> <circle cx="32" cy="32" r="21" fill="none" stroke="url(#bl-lockup-brass)" stroke-width="2.5" /> <circle cx="32" cy="32" r="14.5" fill="url(#bl-lockup-seal)" /> <path d="M27 32.5l3.6 3.6L38 28.2" fill="none" stroke="url(#bl-lockup-brass)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" /> </g> <g transform="translate(92.50 0)"> <path d="M68.55 -16.85Q68.55 -9.4 62.13 -4.7Q55.7 0 42.85 0L8.2 0Q6.65 0 5.93 -0.62Q5.2 -1.25 5.2 -2.3Q5.2 -4.2 7.3 -4.85L10.15 -5.5Q11.4 -5.85 12.03 -6.55Q12.65 -7.25 12.65 -8.35L12.65 -61.65Q12.65 -62.75 12.03 -63.45Q11.4 -64.15 10.15 -64.5L7.3 -65.15Q5.2 -65.8 5.2 -67.7Q5.2 -68.8 5.93 -69.4Q6.65 -70 8.2 -70L35.2 -70Q44.5 -70 51.08 -67.45Q57.65 -64.9 61.1 -60.37Q64.55 -55.85 64.55 -49.8Q64.55 -44.75 61.73 -40.8Q58.9 -36.85 53.35 -34.58Q47.8 -32.3 39.65 -32.3L42.45 -33.85Q50.4 -33.85 56.25 -31.67Q62.1 -29.5 65.33 -25.67Q68.55 -21.85 68.55 -16.85ZM37.15 -30.45L23.8 -30.45L23.8 -34.5L35.9 -34.5Q40.35 -34.5 43.45 -36.18Q46.55 -37.85 48.15 -41.1Q49.75 -44.35 49.75 -49Q49.75 -53.95 47.78 -57.62Q45.8 -61.3 42 -63.35Q38.2 -65.4 32.7 -65.4L28.15 -65.4L28.15 -9.45Q28.15 -7 29.68 -5.8Q31.2 -4.6 34.15 -4.6L39.45 -4.6Q43.9 -4.6 46.93 -6.15Q49.95 -7.7 51.5 -10.57Q53.05 -13.45 53.05 -17.25Q53.05 -23.25 48.98 -26.85Q44.9 -30.45 37.15 -30.45ZM94.5 -45.1L94.5 -8Q94.5 -6.45 94.93 -5.72Q95.35 -5 96.25 -4.7L98.15 -4.2Q99.1 -3.9 99.55 -3.37Q100 -2.85 100 -2.05Q100 -1.1 99.35 -0.55Q98.7 0 97.25 0L77.55 0Q76.15 0 75.5 -0.55Q74.85 -1.1 74.85 -2.05Q74.85 -2.8 75.3 -3.32Q75.75 -3.85 76.7 -4.2L78.65 -4.7Q79.55 -5.05 79.98 -5.75Q80.4 -6.45 80.4 -8L80.4 -36.65Q80.4 -37.95 80.03 -38.5Q79.65 -39.05 78.8 -39.2L76.15 -39.4Q75.25 -39.6 74.85 -40.05Q74.45 -40.5 74.45 -41.2Q74.45 -42.05 74.93 -42.55Q75.4 -43.05 76.75 -43.55L86.05 -46.95Q88 -47.65 89.18 -47.95Q90.35 -48.25 91.15 -48.25Q92.85 -48.25 93.68 -47.37Q94.5 -46.5 94.5 -45.1ZM86.35 -55.9Q82.35 -55.9 79.93 -57.97Q77.5 -60.05 77.5 -63.4Q77.5 -66.75 79.93 -68.78Q82.35 -70.8 86.35 -70.8Q90.4 -70.8 92.83 -68.78Q95.25 -66.75 95.25 -63.4Q95.25 -60.05 92.83 -57.97Q90.4 -55.9 86.35 -55.9ZM108.9 -42.45L106.6 -43.15Q105.4 -43.55 104.9 -44.07Q104.4 -44.6 104.4 -45.3Q104.4 -46.3 105.08 -46.82Q105.75 -47.35 106.9 -47.35L109.95 -47.35Q111.15 -47.35 111.95 -47.8Q112.75 -48.25 113.6 -49.4L119.05 -57.05Q120.1 -58.35 121.05 -58.95Q122 -59.55 122.95 -59.55Q124 -59.55 124.6 -58.9Q125.2 -58.25 125.2 -57L125.2 -14.25Q125.2 -10.75 126.58 -8.9Q127.95 -7.05 130.4 -7.05Q132.25 -7.05 133.28 -7.77Q134.3 -8.5 134.9 -9.45Q135.5 -10.4 136.08 -11.12Q136.65 -11.85 137.55 -11.9Q138.3 -11.9 138.73 -11.4Q139.15 -10.9 139.15 -9.75Q139.1 -6.9 137.28 -4.45Q135.45 -2 132.2 -0.47Q128.95 1.05 124.8 1.05Q118.4 1.05 114.78 -2.18Q111.15 -5.4 111.15 -11.9L111.15 -39.5Q111.15 -40.8 110.63 -41.42Q110.1 -42.05 108.9 -42.45ZM119.65 -42.4L119.65 -47.35L136.2 -47.35Q137.35 -47.35 138.03 -46.87Q138.7 -46.4 138.7 -45.5Q138.7 -44.2 137.4 -43.3Q136.1 -42.4 133.2 -42.4ZM174.95 -65.15L172.1 -64.5Q170.9 -64.15 170.25 -63.45Q169.6 -62.75 169.6 -61.65L169.6 -8.55Q169.6 -6.65 170.7 -5.82Q171.8 -5 174.05 -5L182.35 -5Q185.2 -5 187.2 -5.77Q189.2 -6.55 190.8 -8.5Q192.4 -10.45 194.05 -14.05L197.05 -20.95Q197.65 -22.2 198.53 -22.62Q199.4 -23.05 200.55 -22.75Q201.75 -22.45 202.25 -21.62Q202.75 -20.8 202.35 -19.4L197.85 -1.8Q197.3 0.05 196.48 0.95Q195.65 1.85 194.05 1.85Q193 1.85 191.98 1.4Q190.95 0.95 189.73 0.48Q188.5 0 186.65 0L149.7 0Q148.15 0 147.43 -0.62Q146.7 -1.25 146.7 -2.3Q146.7 -4.2 148.8 -4.85L151.65 -5.5Q152.85 -5.85 153.5 -6.55Q154.15 -7.25 154.15 -8.35L154.15 -61.65Q154.15 -62.75 153.5 -63.45Q152.85 -64.15 151.65 -64.5L148.8 -65.15Q146.7 -65.8 146.7 -67.7Q146.7 -68.8 147.43 -69.4Q148.15 -70 149.7 -70L174.05 -70Q175.6 -70 176.33 -69.4Q177.05 -68.8 177.05 -67.7Q177.05 -65.8 174.95 -65.15ZM234.65 -48.5Q242.4 -48.5 248.25 -45.35Q254.1 -42.2 257.38 -36.58Q260.65 -30.95 260.65 -23.5Q260.65 -16.45 257.27 -10.85Q253.9 -5.25 247.95 -2Q242 1.25 234.15 1.25Q226.45 1.25 220.63 -1.95Q214.8 -5.15 211.5 -10.77Q208.2 -16.4 208.2 -23.75Q208.2 -30.9 211.58 -36.47Q214.95 -42.05 220.9 -45.28Q226.85 -48.5 234.65 -48.5ZM238.2 -4Q241.6 -4.5 243.68 -7.12Q245.75 -9.75 246.33 -14.35Q246.9 -18.95 245.7 -25.4Q244.55 -31.85 242.35 -36.02Q240.15 -40.2 237.15 -42.05Q234.15 -43.9 230.65 -43.3Q227.2 -42.75 225.13 -40.12Q223.05 -37.5 222.53 -32.93Q222 -28.35 223.15 -21.85Q224.3 -15.45 226.5 -11.27Q228.7 -7.1 231.7 -5.25Q234.7 -3.4 238.2 -4ZM299.9 -4.6Q292.95 -5.6 289.83 -6.37Q286.7 -7.15 285.9 -7.85Q285.1 -8.55 285.1 -9.3Q285.1 -10.1 285.75 -10.8Q286.4 -11.5 287.9 -12.15L286.85 -12.95Q281.9 -12.45 279.08 -11.12Q276.25 -9.8 275.08 -7.95Q273.9 -6.1 273.9 -4Q273.9 -1.6 275.33 0.1Q276.75 1.8 280.65 3.13Q284.55 4.45 292.05 5.55Q298.15 6.45 301.55 7.48Q304.95 8.5 306.3 9.8Q307.65 11.1 307.65 12.85Q307.65 15.05 306.25 16.63Q304.85 18.2 301.8 19.03Q298.75 19.85 293.7 19.85Q284.9 19.85 281.35 17.23Q277.8 14.6 277.8 10.3Q277.8 7.95 279.77 6.08Q281.75 4.2 285.5 3.6L284.7 2.1Q274.75 3.3 270.9 6.35Q267.05 9.4 267.05 13.35Q267.05 16.55 269.35 19.08Q271.65 21.6 276.97 23.05Q282.3 24.5 291.35 24.5Q305.3 24.5 312.68 20.13Q320.05 15.75 320.05 8.9Q320.05 5.15 318.13 2.5Q316.2 -0.15 311.77 -1.87Q307.35 -3.6 299.9 -4.6ZM301.05 -43.3L304.3 -42.85Q306.15 -46.5 307.35 -47.75Q308.55 -49 310.15 -49Q311.6 -49 312.43 -48.28Q313.25 -47.55 313.9 -46.6Q314.55 -45.65 315.45 -44.92Q316.35 -44.2 317.95 -44.2Q320.25 -44.2 321.6 -45.87Q322.95 -47.55 322.95 -50.3Q322.95 -53.4 320.95 -55.17Q318.95 -56.95 315.8 -56.95Q311.95 -56.95 308.52 -54.42Q305.1 -51.9 302.75 -46.8ZM315.9 -30.45Q315.9 -35.5 313.13 -39.58Q310.35 -43.65 305.23 -46Q300.1 -48.35 293 -48.35Q285.7 -48.35 280.22 -45.9Q274.75 -43.45 271.72 -39.08Q268.7 -34.7 268.7 -28.9Q268.7 -23.85 271.47 -19.78Q274.25 -15.7 279.4 -13.35Q284.55 -11 291.65 -11Q298.95 -11 304.43 -13.45Q309.9 -15.9 312.9 -20.28Q315.9 -24.65 315.9 -30.45ZM290.5 -44.1Q294.8 -44.4 297.55 -40.7Q300.3 -37 301.1 -30Q301.9 -23.15 300.08 -19.45Q298.25 -15.75 294.1 -15.45Q291.25 -15.3 289.05 -16.87Q286.85 -18.45 285.45 -21.67Q284.05 -24.9 283.5 -29.55Q283 -34.15 283.63 -37.3Q284.25 -40.45 286 -42.17Q287.75 -43.9 290.5 -44.1ZM345.55 -45.1L345.55 -8Q345.55 -6.45 345.98 -5.72Q346.4 -5 347.3 -4.7L349.2 -4.2Q350.15 -3.9 350.6 -3.37Q351.05 -2.85 351.05 -2.05Q351.05 -1.1 350.4 -0.55Q349.75 0 348.3 0L328.6 0Q327.2 0 326.55 -0.55Q325.9 -1.1 325.9 -2.05Q325.9 -2.8 326.35 -3.32Q326.8 -3.85 327.75 -4.2L329.7 -4.7Q330.6 -5.05 331.03 -5.75Q331.45 -6.45 331.45 -8L331.45 -36.65Q331.45 -37.95 331.08 -38.5Q330.7 -39.05 329.85 -39.2L327.2 -39.4Q326.3 -39.6 325.9 -40.05Q325.5 -40.5 325.5 -41.2Q325.5 -42.05 325.97 -42.55Q326.45 -43.05 327.8 -43.55L337.1 -46.95Q339.05 -47.65 340.23 -47.95Q341.4 -48.25 342.2 -48.25Q343.9 -48.25 344.73 -47.37Q345.55 -46.5 345.55 -45.1ZM337.4 -55.9Q333.4 -55.9 330.98 -57.97Q328.55 -60.05 328.55 -63.4Q328.55 -66.75 330.98 -68.78Q333.4 -70.8 337.4 -70.8Q341.45 -70.8 343.88 -68.78Q346.3 -66.75 346.3 -63.4Q346.3 -60.05 343.88 -57.97Q341.45 -55.9 337.4 -55.9ZM377.2 -45.1L377.2 -8Q377.2 -6.45 377.65 -5.75Q378.1 -5.05 379 -4.7L380.8 -4.2Q382.4 -3.55 382.4 -2.2Q382.4 0 379.6 0L360.3 0Q358.9 0 358.25 -0.55Q357.6 -1.1 357.6 -2.05Q357.6 -2.8 358.03 -3.32Q358.45 -3.85 359.4 -4.2L361.4 -4.7Q362.3 -5.05 362.73 -5.75Q363.15 -6.45 363.15 -8L363.15 -36.65Q363.15 -37.95 362.78 -38.5Q362.4 -39.05 361.55 -39.2L358.9 -39.4Q358 -39.6 357.6 -40.05Q357.2 -40.5 357.2 -41.2Q357.2 -42.05 357.67 -42.55Q358.15 -43.05 359.5 -43.55L368.8 -46.95Q370.7 -47.65 371.85 -47.95Q373 -48.25 374.05 -48.25Q375.6 -48.25 376.4 -47.37Q377.2 -46.5 377.2 -45.1ZM375.7 -33.95L373.4 -36.3L375.35 -38.05Q381.7 -43.85 386.42 -46.17Q391.15 -48.5 395.3 -48.5Q401.75 -48.5 405.67 -44.4Q409.6 -40.3 409.6 -33.4L409.6 -8.15Q409.6 -6.5 410.08 -5.75Q410.55 -5 411.5 -4.7L413.25 -4.2Q414.25 -3.85 414.67 -3.32Q415.1 -2.8 415.1 -2.05Q415.1 -1.1 414.45 -0.55Q413.8 0 412.4 0L393.05 0Q390.25 0 390.25 -2.2Q390.25 -3.55 391.8 -4.2L393.7 -4.7Q394.7 -5.05 395.13 -5.8Q395.55 -6.55 395.55 -8.15L395.55 -31.35Q395.55 -35.75 393.4 -37.93Q391.25 -40.1 387.65 -40.1Q385.4 -40.1 382.88 -39.05Q380.35 -38 377.7 -35.7Z" fill="currentColor" /> </g> </svg>
</a>
<div class="links">
<a href="./index.html">Overview</a>
<a class="active" href="./docs.html">Integrate</a>
<a href="./account.html">Try it</a>
</div>
</nav>
<header class="hero" style="padding-top:48px; padding-bottom:8px">
<span class="eyebrow">Integration guide</span>
<h1 style="font-size:clamp(32px, 4.5vw, 44px)">Add BitLogin to your site</h1>
<p class="lead" style="max-width:520px">No npm install required. Two files, one custom element — and every user who already has a BitLogin account elsewhere can sign in with the login they already know.</p>
</header>
<section class="container narrow">
<ol class="step-list">
<li>
<h3>Copy the widget files next to your site</h3>
<p>Grab <code>bitlogin.js</code> and <code>cryptoWorker.js</code> (plus the shared chunk they both import) from a BitLogin release (or build <code>@bitlogin/widget</code> yourself) and drop them in a folder your site serves, e.g. <code>/vendor/bitlogin/</code>. All of them must live in the same directory — the main script loads the worker by a relative URL.</p>
</li>
<li>
<h3>Include the script and the element</h3>
<pre><code>&lt;script type="module" src="/vendor/bitlogin/bitlogin.js"&gt;&lt;/script&gt;
&lt;bitlogin-auth
vault-relays="wss://relay-one.example,wss://relay-two.example,wss://relay-three.example"
discovery-relays="wss://discovery-one.example,wss://discovery-two.example"&gt;
&lt;/bitlogin-auth&gt;</code></pre>
<p class="section-sub" style="margin:10px 0 0">That's it — the element renders its own sign-in/create/recover UI inside a shadow root, so it won't collide with your site's CSS.</p>
<div class="callout">
<strong>Mount it somewhere your own re-renders won't touch.</strong> Removing
<code>&lt;bitlogin-auth&gt;</code> from the DOM (including indirectly, by resetting an
ancestor's <code>innerHTML</code> — a common pattern in hand-rolled UI without a
framework's reconciler) tears down its crypto Web Worker. If that happens mid-session,
every signing call made afterward just hangs forever with no error, because the worker
is gone and nothing ever replies — a call already in flight when it happens can even
look like it succeeded. If your app re-renders the container this element lives in,
give it a container that's never replaced wholesale and toggle the <code>hidden</code>
attribute instead of removing the element. See the account manager demo's source for a
single-page example, or BitRoad's <code>src/nostr/bitloginAdapter.mjs</code> /
<code>#bitloginMount</code> (a sibling Nostr commerce app that embeds BitLogin) for a
worked example of keeping the element alive across a host app's own re-renders.
</div>
</li>
<li>
<h3>React to sign-in and sign-out</h3>
<pre><code>const bl = document.querySelector('bitlogin-auth');
bl.addEventListener('bitlogin-login', (e) => {
console.log('signed in as', e.detail.publicKey);
});
bl.addEventListener('bitlogin-logout', () => {
console.log('signed out');
});</code></pre>
</li>
<li>
<h3>Sign events from anywhere on the page</h3>
<p>Once a user is signed in, BitLogin installs a <code>window.nostr</code> provider shaped like the de facto NIP-07 interface used by browser extensions (Alby, nos2x, …). Any existing Nostr web app code that already calls <code>window.nostr</code> works unmodified.</p>
<pre><code>const pubkey = await window.nostr.getPublicKey();
const signed = await window.nostr.signEvent({
kind: 1,
content: 'hello from my app',
tags: [],
created_at: Math.floor(Date.now() / 1000),
});
// NIP-44 encrypted DMs
const cipher = await window.nostr.nip44.encrypt(peerPubkeyHex, 'hi there');
const plain = await window.nostr.nip44.decrypt(peerPubkeyHex, cipher);</code></pre>
<div class="callout">
<strong>Private keys never leave a dedicated Web Worker.</strong> The main thread — your page's JS — only ever
sees public keys, signed events, and ciphertext. <code>window.nostr</code> calls are proxied into the worker
and back.
</div>
</li>
<li>
<h3>Or talk to the element directly — no <code>window.nostr</code> contention</h3>
<p><code>&lt;bitlogin-auth&gt;</code> mirrors the same four calls as instance methods, scoped to that specific
element rather than the single shared <code>window.nostr</code> slot:</p>
<pre><code>const bl = document.querySelector('bitlogin-auth');
const pubkey = await bl.getPublicKey();
const signed = await bl.signEvent({ kind: 1, content: 'hello', tags: [] });
const cipher = await bl.nip44Encrypt(peerPubkeyHex, 'hi there');
const plain = await bl.nip44Decrypt(peerPubkeyHex, cipher);</code></pre>
<p>Prefer this if your app already holds a reference to the element — e.g. you built the sign-in UI yourself
instead of relying on <code>window.nostr</code>-reading app code. It keeps working even if something else
currently owns <code>window.nostr</code> (another extension, a different widget instance), and it's what a host
app should use if it wants a specific, known signer rather than "whichever provider is active this moment."
BitRoad's <code>src/nostr/bitloginAdapter.mjs</code> is a real example: its signer adapter calls these four
methods directly and never touches <code>window.nostr</code> at all.</p>
</li>
<li>
<h3>Supporting multiple signing methods (extensions, NIP-46, BitLogin)</h3>
<p><code>window.nostr</code> is a single global slot — only one provider can occupy it at a time. If a user also
has a NIP-07 extension (Alby, nos2x, …) installed, BitLogin will not overwrite it: it only installs
<code>window.nostr</code> when nothing is already there. The reverse isn't in BitLogin's control — an extension
that loads after BitLogin can still overwrite it, same as any two NIP-07 providers would collide.</p>
<p>To know specifically whether BitLogin is the active signer (as opposed to an extension), check
<code>window.bitlogin?.isActiveSigner()</code> rather than just <code>typeof window.nostr !== 'undefined'</code> —
the latter only tells you <em>a</em> signer is present, not which one. <code>window.bitlogin</code> is installed
as soon as the script loads, independent of whether <code>&lt;bitlogin-auth&gt;</code> has signed anyone in yet,
so it's safe to feature-detect immediately.</p>
<pre><code>if (window.bitlogin) {
console.log('BitLogin is available, version', window.bitlogin.version);
}
if (window.bitlogin?.isActiveSigner()) {
// window.nostr is currently backed by BitLogin specifically
}</code></pre>
<p>NIP-46 (remote "bunker" signers) is a different transport — it doesn't typically hook
<code>window.nostr</code> directly, so it isn't something BitLogin needs to avoid colliding with. A site can
offer "Sign in with BitLogin," "Connect an extension," and "Connect a bunker" as separate options; whichever
the user picks becomes the active signer, and your app code should call whatever's currently active rather
than assume a specific one.</p>
<div class="callout">
<strong>Don't give your <code>&lt;bitlogin-auth&gt;</code> element <code>id="bitlogin"</code>.</strong>
Browsers automatically expose any element with a matching <code>id</code> or <code>name</code> attribute as
a same-named global — an element with <code>id="bitlogin"</code> becomes <code>window.bitlogin</code>,
shadowing the API object described above. Use something like <code>id="bitlogin-widget"</code> instead
(as this demo does).
</div>
<h4 style="margin-top:20px">Letting a user switch away from BitLogin without reloading</h4>
<p>By default, once <code>&lt;bitlogin-auth&gt;</code> mounts it claims <code>window.nostr</code> and keeps it —
even after the user logs out of BitLogin, since they might just be about to log back in with the same widget.
If your site offers a signer picker and the user switches to a different method, call
<code>window.bitlogin.releaseSigner()</code> to hand the slot back (it's a no-op, safe to call speculatively,
if BitLogin doesn't currently hold it):</p>
<pre><code>// User picked a different signing method in your own UI
if (window.bitlogin?.releaseSigner()) {
// window.nostr is now undefined; install the other method's provider
}</code></pre>
<p>If they switch back to BitLogin later, call <code>claimSigner()</code> on the element instance to reclaim
the slot (this also happens automatically every time a user completes sign-in, sign-up, or recovery through
the widget itself, so you only need this for a picker UI that lives outside the widget):</p>
<pre><code>document.querySelector('bitlogin-auth').claimSigner();</code></pre>
<p>Both <code>claimSigner()</code>/<code>releaseSigner()</code> on the element and
<code>window.bitlogin.releaseSigner()</code> also fire <code>bitlogin-signer-claimed</code> /
<code>bitlogin-signer-released</code> events (on the element and on <code>window</code>, respectively) if you'd
rather react to the change than check a return value.</p>
<div class="callout">
<strong><code>claimSigner()</code> can fail without breaking anything.</strong> Some NIP-07 extensions
install <code>window.nostr</code> as a non-configurable, non-writable property specifically to stop another
script from overwriting it. When that happens, <code>claimSigner()</code> returns <code>false</code> instead
of throwing (and <code>bitlogin-signer-claimed</code>'s <code>event.detail</code> carries
<code>{ windowNostrClaimed: false, error }</code>), and every BitLogin sign-in flow completes normally
regardless — the failure only means <code>window.nostr</code> still points at that other extension. If your
app talks to the element directly (previous step) this never matters at all; only code that reads
<code>window.nostr</code> itself is affected, and only for as long as the other extension holds the slot.
</div>
</li>
<li>
<h3>Choose your relays</h3>
<p>BitLogin ships with a small built-in bootstrap relay list, but you should point <code>vault-relays</code> and
<code>discovery-relays</code> at relays you trust for your users. Use at least three vault relays for
redundancy — capsule publication and reads use a quorum, so no single relay is a single point of failure.</p>
</li>
<li>
<h3>Make it look like your site</h3>
<p>The widget renders in a shadow root, but every color, radius, font, and its max-width are all read from CSS
custom properties on <code>:host</code> — and custom properties inherit through the shadow boundary, so your
page's own CSS can override every one of them without touching the widget's internals:</p>
<pre><code>bitlogin-auth {
--bl-accent: #3d9bff; /* primary button / link / focus ring color */
--bl-accent-hover: #2f86e0; /* primary button hover */
--bl-accent-fg: #04101f; /* text color on top of --bl-accent */
--bl-bg: #12151f; /* card background */
--bl-fg: #eaeef6; /* body text */
--bl-muted: #98a2b6; /* secondary text */
--bl-border: rgba(255, 255, 255, 0.10);
--bl-input-bg: #1a1e2b; /* input / credential-box background */
--bl-danger: #ff6b6b;
--bl-danger-bg: rgba(255, 107, 107, 0.16);
--bl-radius: 14px;
--bl-font-family: Inter, ui-sans-serif, system-ui, sans-serif;
--bl-max-width: none; /* default 380px keeps it a fixed-width card;
override to fill a wider container */
}</code></pre>
<p class="section-sub" style="margin:10px 0 0">Overriding none of these is a valid choice — the widget falls
back to its own light theme, a <code>prefers-color-scheme: dark</code> media query, and a
<code>data-theme="dark"|"light"</code> attribute override for pages with their own theme toggle. See BitRoad's
<code>bitlogin-auth { ... }</code> rule in its <code>src/styles.css</code> for a real integration that maps
every one of these onto its own design tokens.</p>
</li>
<li>
<h3>Showing it as one option among several</h3>
<p>If your sign-in screen lists BitLogin alongside other methods (an extension, a bunker, email) rather than
embedding the full widget up front, use the brand mark instead — the same seal shown on this site and inside
the widget's own header, as a plain SVG with a transparent background:</p>
<pre><code>&lt;button&gt;
&lt;img src="/assets/mark.svg" alt="" width="20" height="20" /&gt;
Sign in with BitLogin
&lt;/button&gt;</code></pre>
<p class="section-sub" style="margin:10px 0 0">Grab <code>mark.svg</code> (icon only), <code>wordmark.svg</code>
(the "BitLogin" logotype as vector paths — no font to load), or <code>lockup.svg</code> (both combined) from
this site's own <code>assets/</code> folder. All three are plain SVG with no external dependencies, and
<code>wordmark.svg</code>'s text is filled with <code>currentColor</code> so it follows your page's own text
color.</p>
</li>
<li>
<h3>That's the whole integration</h3>
<p>No account database, no password-reset email, no server-side session. The <a href="./account.html">account manager demo</a>
shows the full loop: create an account, sign a test event, rotate the password, and export a recovery file.</p>
</li>
</ol>
<div class="callout">
<strong>Password-manager save prompts.</strong> Because the widget's login form lives inside
a shadow root and submits via JavaScript (never a real form POST), browsers' passive
"offer to save this password" heuristics don't reliably fire — and the generated-password
screen never puts the password into a real input at all, since it's only shown as text.
The widget works around this by explicitly calling the Credential Management API
(<code>navigator.credentials.store()</code>) after every successful registration, login,
password rotation, and recovery. This is Chromium-only (Chrome, Edge, Brave, Opera) —
Firefox and Safari never implemented <code>PasswordCredential</code>, so users on those
browsers will need to save their generated password manually.
</div>
</section>
<footer>
BitLogin is an experimental alpha protocol. This demo is a reference implementation, not a production account service.
<div class="footer-links"><a href="https://github.com/PR0M3TH3AN/bitlogin">Source on GitHub</a> · MIT licensed — <a href="https://github.com/PR0M3TH3AN/bitlogin/blob/main/LICENSE">LICENSE</a></div>
</footer>
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("./sw.js").catch(() => {});
}
</script>
</body>
</html>