Layout Components
Welcome back
One Amoleo account for Pets, Connections, and everything else in the family.
Powered by Amoleo·Accounts
No account yet? Signing in creates one the first time — there's nothing separate to fill in.
No local password field anywhere — the button redirects to Accounts' hosted sign-in, which handles both signing in and registration. Coloured with Pets' own --accent (#D9603F); Connections' AuthScreen.jsx is the other live implementation, same shape.
Family auth box — the login/register component
The sign-in-or-create-an-account card every logged-in Amoleo product shows
before a visitor is authenticated. Amoleo-Connections' AuthScreen.jsx /
.auth-card is the reference implementation — this doc writes down what
about it is shared family design versus what any product wearing it is
free to change.
docs/family-header.md already named Connections as this pattern's origin
("Two states, not three... modelled on Connections' actual implementation")
and recorded that Connections' component "becomes the canonical one — not
just for Connections." This doc is that promise kept: the component's own
mechanics, moved out of the header spec (which only needed to say where it
sits) and into a spec of its own, the same split family-wordmark.md made
for the header and footer sizes that reference it.
Where it sits inside a page is the header spec's job, not this one's —
desktop hero + in-page form vs. mobile sticky pop-down, breakpoints, how it
relates to the header bar. See family-header.md, "Two states, not three."
This doc covers the card itself: its structure, its states, and — the part
that actually varies product to product — where it takes its colour from.
node tools/check-auth-box.mjs
1. One component, five products' worth of accent
The whole point: the box looks identical in shape across every product and
never identical in colour. Tabs, fields, buttons, spacing, radii — all one
shared design. The highlight colour — the active tab, the focus ring, the
submit button, the link hover — is always the current product's own
--accent, never a fixed hex baked into the component.
This is the same relationship the footer lockup has to --brand-* (shared
structure, per-product colour — family-footer.md §2) and the header's
link/button rule has to each site's theme tokens (family-header.md §4).
The auth box is the third surface that shape applies to, and the one where
getting it wrong is easiest to miss: a hardcoded #0b6e8f (Connections'
own accent) copied into Pets' version of this component would render, would
look intentional, and would be wrong the day anyone reused the component
without also swapping the colour by hand.
Two tokens carry the whole rule:
| Token | Used for |
|---|---|
--accent |
Active tab background, input focus outline, submit button, link/hover colour |
--on-accent |
Text/icon colour sitting on top of --accent fills (the active tab's label, the submit button's label) |
Everything else the card uses is neutral — family-theme.md's shared base
(panel, bg, text, muted, border) plus the semantic --danger /
--danger-tint pair for the error state, under whatever names each repo's
own theme gives them (family-theme.md §2 already documents that Website,
Connections and Pets don't agree on names for the same five concepts — the
auth box inherits that aliasing, it doesn't invent a second scheme).
Checked, not just written down: check-auth-box.mjs reads the CSS rule
that styles the active tab and the submit button in every adopted repo and
fails if either sets its highlight colour to a literal hex/rgb() instead of
a var(--...) reference — the same shape of check checkSuffixColour
already runs for the wordmark (family-theme.md §3.5). It cannot confirm
the variable resolves to that product's own accent rather than someone
else's (that needs the alias map a human keeps current in auth-box.json,
same as baseTheme.repos.<name>.aliases) — only that it isn't a number.
2. Structure — the part that's a literal shared component
Unlike the header (rules, not one markup block — different repos need
genuinely different content) the auth box is one component, the way the
footer is: the same tabs, the same fields, the same states, copied into each
repo rather than each repo inventing its own shape. tools/auth-box.json's
classes section is the literal class-name contract check-auth-box.mjs
looks for.
.auth-card— the outer card.min(420px, 100%)wide, padded, sitting on--panelwith the theme's standard border and shadow..auth-heading/.auth-tagline— the card's own title ("Welcome back" / "Create an account") and one line of supporting copy underneath, both swapping with the mode..auth-tabscontaining two.auth-tab, one.active— sign in and create account are tabs sharing one track, not a link buried at the bottom of the form. Which of the two things you're doing is visible before you fill anything in, not discovered afterwards on submit.role="tablist"/role="tab"/aria-selectedthroughout — this is real tab semantics, not styled buttons pretending to be tabs.- Fields, each a
<label>+<input>pair, plus one of:.field-hint— quiet, permanent help text (password length, what a username becomes part of) shown while the field is still valid or untouched..field-problem— the same slot, replaced with a validation message once the field is touched and invalid. One slot, two states, never both at once.
.password-fieldwrapping the password<input>plus a.password-toggle— the show/hide control sits inside the field (absolutely positioned,padding-righton the input to clear it) rather than beside it, so the row never changes height when the control appears..auth-error— one slot for a server-rejected submit (wrong password, duplicate email), left-bordered in--danger,role="alert"..auth-submit— full-width primary button, fixedmin-height: 44pxso a busy-state spinner appearing beside the label never resizes the row..auth-switch— one line under the card ("No account yet? Create one") that also flips the tab — a second way into the same toggle, not a second interaction.
Reference markup: Amoleo-Connections/client/src/components/AuthScreen.jsx.
The CSS itself was extracted from Connections' /* Sign in and create account */ block into
tools/family-css/components/auth-box.css,
compiled into family-css/base.css — see
docs/family-css-system.md. Connections wired the
shared file in and completed the --panel-2/--border rename
family-css-system.md §4 describes on 6 Aug 2026 — its own duplicated auth
box CSS is gone from styles.css now, replaced by the shared file plus two
small per-context overrides (.landing-auth .auth-card's padding delta,
noted inline). What's canonical lives in the one file now, not two
converging copies.
3. Validation is touched-based, not eager
A field only shows .field-problem once it has been left (onBlur) or the
form has been submitted — never while the visitor is still typing the first
character. Telling someone their email is invalid before they've finished
typing it is nagging, not help. This is a behavioural rule, not something a
text-search checker can verify from CSS — it's listed here so a reimplementation
doesn't quietly drop it, and is one of the items in §6 a human still confirms
by eye.
What each product validates is its own business rule and stays that way. Password length (Connections: 10 characters), username shape and reserved words, whatever server-side rules a product enforces — none of that is shared design, and this spec does not try to make it so. What's shared is the pattern — touched-based reveal, a hint that becomes a problem, one slot for both — not the specific rules run through it.
4. States — two tabs, not three, and not a modal
Corrected the same day family-header.md was: the login/register form is
not a separate screen from the marketing/landing content, and sign-in and
register are not two separate screens either. There is exactly one pre-auth
state, and inside it exactly one card whose two tabs swap which half of the
form is visible. No route change, no modal, no page navigation between
"login" and "register" — switchMode() just flips local state.
5. Per-repo status
| Repo | Applicable? | Adopted? | Notes |
|---|---|---|---|
| Amoleo-Connections | Yes | Yes — reference implementation | AuthScreen.jsx / .auth-card in client/src/. Everything in §1–4 above was extracted from this code, not designed fresh. |
| Amoleo-Pets | Yes | No | Separate LoginForm.jsx / RegisterForm.jsx on the generic .card class — no tabs (a "Create New Account" button instead), no password show/hide, no field-hint/field-problem split, eager-ish validation. Recorded in family-header.md §2 as owed work: Pets moves onto the Connections pattern wholesale, header and auth box together, not two separate migrations. |
| Amoleo-Website | No | — | No login system. Nothing renders in its place. |
| Amoleo-Rissbrook | No | — | No login system, and none planned — a static genealogy archive. |
| Amoleo-Accounts | Yes | Yes — adopted via #6 | client/src/components/AuthCard.jsx, wired to the server-side interaction plumbing (server/src/routes/interactionApi.js) and placed per Decision 9 (family-header.md). This row said "no code yet" for some time after Accounts shipped — corrected 3 Sept 2026; tools/auth-box.json had the current state right the whole time, only this table had drifted. |
| Amoleo-Todo | Unknown | No — no code yet | Design-only; whether Todo gets its own login or rides Accounts' SSO isn't decided. Not registered in auth-box.json until it is. |
6. What to check afterwards
Automated (check-auth-box.mjs):
- Every adopted repo's CSS defines the full class set in §2 —
.auth-card,.auth-tabs/.auth-tab/.active,.password-field/.password-toggle,.field-hint/.field-problem,.auth-error,.auth-submit,.auth-switch. - The active-tab rule and the submit-button rule set their highlight colour
through a
var(--...)reference, never a literal hex orrgb().
By eye, on top of that:
- The two tabs really are keyboard- and screen-reader-operable as tabs
(
role="tablist"/role="tab"/aria-selected), not just styled to look like them. - A field's validation message appears only after it's been left or the form submitted — not while the visitor is still typing.
- The password toggle sits inside the field without shifting its height, in both states.
- There is one pre-auth state, not a separate route for register.
- The
var(--accent)the active tab and submit button resolve to is genuinely that product's own accent — the family's shared base theme (family-theme.md) plus each product's own colour (tools/tokens/base.tokens.json), not another product's or a leftover generic value. This is the same class of bug §3.5 offamily-theme.mdfound in Connections' own wordmark — a fallback that resolves to something, just not the right something.
Generated from PetWeightTracker/client/src/components/AuthCard.jsx + K-Drama Connections/client/src/components/AuthScreen.jsx (live implementations — docs/family-auth-box.md and tools/auth-box.json are stale, see note above). Edit there and re-run npm run build in docs-site-src/ — never hand-edit a page.