Skip to the page
TACTILE UI

Kit part · Forms · Sign in & sign up

Sign-in

Sign in and sign up on one plate: Google and GitHub keys with their marks stamped in monochrome, an email route underneath, a two-way selector that re-prints the plate, and errors that say how to fix them.

Element
<form>
Needs
kit.css + kit.mjs
Rules
sign-in.rules.md · 5
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

Providers only · the Tactile UI account
EmailAdd the part after the @, e.g. [email protected].
Error on the email
Busy · waiting on the server
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartRestPressedFocusError · busy
Provider—
Email
—
Go keySign inSign inSign inSigning in

Graphite

PartRestPressedFocusError · busy
Provider—
Email
—
Go keySign inSign inSign inSigning in
03

API

NameKindValuesWhat it does
form.tui-sign-inclasswith .tui-panelThe plate. novalidate: the part checks the fields and writes the hints.
input[name="mode"]radio"in" · "up"In a .tui-sign-in__mode selector. Switching disables the other mode’s fields, so they neither post nor block.
[data-in] · [data-up]attributeon any elementShown only when signing in, or only when making an account: titles, fields, the go key’s words, the foot.
.tui-sign-in__providerclasson a keyA provider key, full width, its mark stamped into the cap in the ink colour.
.tui-sign-in__orclasson <p>“or with email”, printed between two hairlines.
.tui-sign-in__secretclassaround the passwordOne well for the password and its Show key.
.tui-sign-in__revealclassaria-pressedShows and hides the password; the words change to Hide.
data-missing · data-invalidattributeon an inputYour own words for an empty or a wrong field.
data-min-upattributee.g. "10"A minimum password length, applied only when making an account.
data-doneattribute"… {email}"Printed in the status line when nothing else handles the form: for static pages and demos.
.tui-sign-in__statusclassrole="status"One line with a lamp: green when done, red when it failed.
tui-sign-ineventcancelable · detail { mode, data, done, fail }A valid form was sent. preventDefault() to handle it; the go key blinks amber until you call done(text) or fail(text).
aria-busyattributeset by the partOn the form while it waits; the go key shows its amber lamp.
04

Markup

<form class="tui-panel tui-sign-in" action="/auth/email" method="post" novalidate aria-labelledby="si-t">
  <div class="tui-sign-in__head">
    <h2 class="tui-sign-in__title" id="si-t"><span data-in>Sign in to Lode</span><span data-up>Make a Lode account</span></h2>
    <div class="tui-seg tui-seg--plain tui-sign-in__mode" role="radiogroup" aria-label="Account">
      <label><input type="radio" name="mode" value="in" checked><span>Sign in</span></label>
      <label><input type="radio" name="mode" value="up"><span>New account</span></label>
    </div>
  </div>
  <div class="tui-sign-in__providers">
    <a class="tui-key tui-key--lg tui-sign-in__provider" href="/auth/sign-in?provider=google"><svg>…</svg>Continue with Google</a>
    <a class="tui-key tui-key--lg tui-sign-in__provider" href="/auth/sign-in?provider=github"><svg>…</svg>Continue with GitHub</a>
  </div>
  <p class="tui-sign-in__or">or with email</p>
  <div class="tui-sign-in__fields">
    <label class="tui-field" data-up><span class="tui-label">Name</span>
      <input class="tui-input" name="name" autocomplete="name" required><span class="tui-hint"></span></label>
    <label class="tui-field"><span class="tui-label">Email</span>
      <input class="tui-input" type="email" name="email" autocomplete="email" required><span class="tui-hint"></span></label>
    <div class="tui-field"><label class="tui-label" for="si-pw">Password</label>
      <div class="tui-sign-in__secret">
        <input class="tui-input" id="si-pw" type="password" name="password" required data-min-up="10">
        <button type="button" class="tui-key tui-key--flat tui-key--sm tui-sign-in__reveal" aria-pressed="false" aria-controls="si-pw">Show</button>
      </div>
      <span class="tui-hint"><span data-up>10 characters or more.</span></span></div>
  </div>
  <button class="tui-key tui-key--primary tui-key--lg tui-sign-in__go">
    <span class="tui-lamp" data-lamp="amber" data-on data-blink></span><span data-in>Sign in</span><span data-up>Make the account</span>
  </button>
  <p class="tui-sign-in__status" role="status"></p>
</form>
import '/kit/kit.mjs';   // wires every form.tui-sign-in, now and later

document.addEventListener('tui-sign-in', async (e) => {
  e.preventDefault();                      // we'll handle it; the go key blinks amber
  const { mode, data, done, fail } = e.detail;
  const r = await fetch(`/auth/${mode}`, { method: 'POST', body: data });
  r.ok ? location.assign('/account') : fail('That email and password do not match. Try again, or reset it.');
});

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • A real <form> with labelled fields and the right autocomplete tokens (email, current-password, and new-password when making an account) so password managers help.
  • The mode is a real radio group; switching it disables the hidden fields, so neither the keyboard nor the form ever reaches them.
  • Errors set aria-invalid, rewrite the hint the field points to with aria-describedby, and put the focus on the first field to fix. Nothing typed is cleared.
  • The Show key is a toggle (aria-pressed) that names what it will do.
  • The status line is a polite live region with words beside its lamp.
06

How it’s built

Kit parts and one delegated module. The plate is a panel; the mode selector is the plain selector, and the whole switch is CSS (.tui-sign-in:has(input[value="up"]:checked) shows [data-up] and hides [data-in]), while the module disables the hidden inputs and swaps the password’s autocomplete. The marks are monochrome SVGs in currentColor with two drop shadows, dark above and lit below, so they read as stamped into the cap. Checking and hints run from listeners on the document, so a form added after load needs no call.

07

Rules

sign-in.rules.mdIn the brain

Providers first and in monochrome; ask for more only when an account is being made.

5 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08

Field

Text entry in a recess, the label printed above it, and a hint that says how to fix an error.

Selector

One mode of two to five: latching keys in a tray, one down at a time, each with a lamp that lights when chosen.

Key

Every button, and every link that acts like one: a cap on a skirt that drops below the plate when pressed.

Docs
Finish
Volume