Toolaby Wall

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>
StateIt shows
Before the first answerNothing, in the space it will take, so the header does not move and a signed-in buyer never sees Sign in first.
Signed outA Sign in button. It opens the sign-in page, and the buyer returns to the page they were on, including its #section.
Signed inThe 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-urlA 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-urlAn account page of your own, used for Manage account instead of the hosted account page.
after-sign-out-urlA page on your site to go to after Sign out. Without it, the page stays and the button shows Sign in.
sign-in-labelThe signed-out button's text. Default: Sign in.
themelight or dark. Without it, the button matches the background of the element it sits in.
alignend (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-accentThe Sign in button's background. Default: near-black, or near-white on a dark page.
--toolaby-accent-fgThe Sign in button's text colour.
--toolaby-radiusThe corner radius of the button and the menu. Default: 10px.

For anything else, each part is exposed to ::part():

Part
buttonThe Sign in button.
avatarThe signed-in button.
menuThe menu.
itemEach item in the menu.
toolaby-user { --toolaby-accent: #635bff; --toolaby-radius: 999px; }
toolaby-user::part(avatar) { width: 36px; height: 36px; }

Events

EventOn
toolaby:readywindowThe first answer arrived. event.detail is Toolaby.state.
toolaby:changewindowA later answer differs from the one before: the buyer signed in or out, or bought something.
toolaby:signed-outthe elementThe buyer signed out from its menu.
toolaby:errorthe elementAn 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-checkout opens Stripe Checkout for the tool, on your Stripe account.
  • data-toolaby-trial starts 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-portal opens 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

AttributeOn
data-toolaby-signina buttonOpens 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-nextthe same buttonWhere the buyer lands after signing in, instead of this page.
data-toolaby-signouta buttonSigns the buyer out.
data-toolaby-checkouta buttonOpens Stripe Checkout for the tool with this id. A subscription signed out signs in first and continues.
data-toolaby-planthe same buttonThe plan's id. Without it, the tool's default plan.
data-toolaby-emailthe same buttonThe buyer's address for checkout or a sign-in link, instead of reading it from the form.
data-toolaby-extthe same buttonThe id of the extension that sent the buyer, so the purchase unlocks it at once.
data-toolaby-triala buttonStarts the free trial of the tool with this id, then opens the after-purchase page. Signed out, signs in first and continues.
data-toolaby-portala buttonOpens the Stripe customer portal. Its value, when present, is a subscription's id.
data-toolaby-signed-inany elementShown when a buyer is signed in.
data-toolaby-signed-outany elementShown when no buyer is signed in.
data-toolaby-ownsany elementShown when the signed-in buyer holds the tool with this id.
data-toolaby-emailany other elementFilled with the signed-in buyer's email; hidden when there is none.
data-toolaby-nameany elementFilled with the account's name; hidden when there is none.
data-toolaby-ignoreany elementNone 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.

See also

On this page