Website components
The account button and the data-toolaby-* attributes that sign buyers in, show content by what they own, and sell your tools.
toolaby-web.js defines one element, <toolaby-user>, and reads a set of data-toolaby-* attributes on your page. Together they cover sign-in, content by sign-in state or by what the buyer owns, and selling, with no code of your own.
This page lists every attribute, part, custom property and event, then complete recipes. It assumes the script is on the page; see Your website.
<toolaby-user>
<toolaby-user></toolaby-user>| State | It shows |
|---|---|
| Before the first answer | Nothing, in the space it will take, so the header does not move and a signed-in buyer never sees Sign in first. |
| Signed out | A Sign in button. It opens the sign-in page, and the buyer returns to the page they were on, including its #section. |
| Signed in | The buyer's picture, or their initial, with a menu: their name and email, Manage account (the account page) and Sign out. |
The page cannot see the buyer (link) | An Account link to the account page. |
The menu opens on a click or ↓. ↑ ↓ Home End move between its items, Esc closes it and returns focus to the button, and a click outside closes it.
Attributes
| Attribute | |
|---|---|
sign-in-url | A sign-in page of your own, used instead of the hosted sign-in page. It receives ?next= with the page to return to. See What works in each way. |
account-url | An account page of your own, used for Manage account instead of the hosted account page. |
after-sign-out-url | A page on your site to go to after Sign out. Without it, the page stays and the button shows Sign in. |
sign-in-label | The signed-out button's text. Default: Sign in. |
theme | light or dark. Without it, the button matches the background of the element it sits in. |
align | end (the default) opens the menu aligned to the button's right edge; start, to its left edge. |
Styling
The button draws in its own shadow root: your CSS does not reach inside it, and its CSS does not reach your page. It uses your page's font. Style it with these custom properties, set on toolaby-user or any ancestor:
| Property | |
|---|---|
--toolaby-accent | The Sign in button's background. Default: near-black, or near-white on a dark page. |
--toolaby-accent-fg | The Sign in button's text colour. |
--toolaby-radius | The corner radius of the button and the menu. Default: 10px. |
For anything else, each part is exposed to ::part():
| Part | |
|---|---|
button | The Sign in button. |
avatar | The signed-in button. |
menu | The menu. |
item | Each item in the menu. |
toolaby-user { --toolaby-accent: #635bff; --toolaby-radius: 999px; }
toolaby-user::part(avatar) { width: 36px; height: 36px; }Events
| Event | On | |
|---|---|---|
toolaby:ready | window | The first answer arrived. event.detail is Toolaby.state. |
toolaby:change | window | A later answer differs from the one before: the buyer signed in or out, or bought something. |
toolaby:signed-out | the element | The buyer signed out from its menu. |
toolaby:error | the element | An action failed. event.detail.message says why. |
Content by state
<div data-toolaby-signed-out hidden>
<button data-toolaby-signin>Sign in</button>
</div>
<div data-toolaby-signed-in hidden>
Welcome back, <span data-toolaby-name></span> (<span data-toolaby-email></span>)
</div>
<div data-toolaby-owns="word-count" hidden>Thanks for buying Word Count.</div>Elements with these attributes are hidden until the first answer arrives, then shown or hidden by it. Give them the hidden attribute in your HTML as well, so nothing shows before the script runs.
data-toolaby-owns hides an element from a buyer who does not hold the tool. It does not keep the element's content from them: anyone can read your page's HTML. Keep content that must stay private on your server.
Buttons
<button data-toolaby-signin>Sign in</button>
<button data-toolaby-signout>Sign out</button>
<button data-toolaby-checkout="word-count">Buy</button>
<button data-toolaby-checkout="word-count" data-toolaby-plan="yearly">Subscribe yearly</button>
<button data-toolaby-trial="word-count">Try it free</button>
<button data-toolaby-portal>Manage billing</button>data-toolaby-checkoutopens Stripe Checkout for the tool, on your Stripe account.data-toolaby-trialstarts the tool's free trial for the signed-in buyer, then opens the after-purchase page, which signs the extension in to the buyer's account.data-toolaby-portalopens the Stripe customer portal for a signed-in buyer.
The trial needs no card. It lasts as long as the longest trial your plans offer, and unlocks what that plan does, with the Free plan's features. When several plans offer it, it is the one that unlocks the fewest features. One buyer has one trial of a tool.
A subscription and a trial need a signed-in buyer; a lifetime licence does not. Clicked signed out, they open the sign-in page. When the buyer returns, the checkout or the trial continues without another click. It continues once: if the page still cannot see the session, it fires toolaby:error instead of sending the buyer round again.
Attribute reference
| Attribute | On | |
|---|---|---|
data-toolaby-signin | a button | Opens the sign-in page and returns to this page. In a form with an email field, on your domain, sends a sign-in link to that address instead and fires toolaby:sent; through a proxy or in development it opens the sign-in page. |
data-toolaby-next | the same button | Where the buyer lands after signing in, instead of this page. |
data-toolaby-signout | a button | Signs the buyer out. |
data-toolaby-checkout | a button | Opens Stripe Checkout for the tool with this id. A subscription signed out signs in first and continues. |
data-toolaby-plan | the same button | The plan's id. Without it, the tool's default plan. |
data-toolaby-email | the same button | The buyer's address for checkout or a sign-in link, instead of reading it from the form. |
data-toolaby-ext | the same button | The id of the extension that sent the buyer, so the purchase unlocks it at once. |
data-toolaby-trial | a button | Starts the free trial of the tool with this id, then opens the after-purchase page. Signed out, signs in first and continues. |
data-toolaby-portal | a button | Opens the Stripe customer portal. Its value, when present, is a subscription's id. |
data-toolaby-signed-in | any element | Shown when a buyer is signed in. |
data-toolaby-signed-out | any element | Shown when no buyer is signed in. |
data-toolaby-owns | any element | Shown when the signed-in buyer holds the tool with this id. |
data-toolaby-email | any other element | Filled with the signed-in buyer's email; hidden when there is none. |
data-toolaby-name | any element | Filled with the account's name; hidden when there is none. |
data-toolaby-ignore | any element | None of these attributes acts inside it. Put it around anything a visitor wrote. |
A button that fails fires toolaby:error on itself, with event.detail.message.
Content your visitors write
If your page shows HTML that visitors write, such as comments, posts or profiles, wrap it in an element with data-toolaby-ignore. Otherwise a visitor can write <button data-toolaby-trial="word-count">, and one click by a buyer starts that buyer's only trial. Inside the wrapper, the attributes on this page do nothing.
<section data-toolaby-ignore>
<!-- comments rendered from your database -->
</section>Recipes
A header with the account button
<header>
<a href="/">Word Count</a>
<nav><a href="/pricing">Pricing</a></nav>
<toolaby-user after-sign-out-url="/"></toolaby-user>
</header>
<script src="https://accounts.example.com/client/toolaby-web.js" defer></script>A pricing section
<section id="pricing">
<h2>Word Count Pro</h2>
<p>€3 a month, or €29 once.</p>
<button data-toolaby-checkout="word-count" data-toolaby-plan="monthly">Subscribe</button>
<button data-toolaby-checkout="word-count">Buy once</button>
<button data-toolaby-trial="word-count">Try it free for 7 days</button>
<p data-toolaby-owns="word-count" hidden>You have Pro. <button data-toolaby-portal>Manage billing</button></p>
</section>Plan ids are on the tool's Pricing page. The default plan needs no data-toolaby-plan.
A page the extension sends buyers to
An extension's Buy can open your page with ?ext=<extension id>. Pass it on, and the purchase unlocks that extension while the buyer is still looking:
<button id="buy" data-toolaby-checkout="word-count">Buy</button>
<script>
const ext = new URLSearchParams(location.search).get('ext');
if (ext) document.getElementById('buy').setAttribute('data-toolaby-ext', ext);
</script>A sign-in form of your own
On your domain, a form with an email field sends the link from your page:
<form>
<input type="email" name="email" required>
<button data-toolaby-signin>Email me a sign-in link</button>
</form>
<p id="sent" hidden>Check your inbox.</p>
<script>
window.addEventListener('toolaby:sent', () => { document.getElementById('sent').hidden = false; });
</script>Through a proxy or in development the same button opens the hosted sign-in page.