Files
wiggleverse-ecomm-content/ui/designs/ecomm-login-and-create-storefront-designs/project/hf-signin.jsx
T
ben.stull afe36fd347 add ui design ./ui/designs/ecomm-login-and-create-storefront-designs/
Claude Design export for SD-0001 (login + create-storefront surfaces): wf-*/hf-*
screens, design-system bundle, offline preview. First artifact in the ui/designs/
collection (ecomm#8). Also ignore .DS_Store.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 21:53:43 -07:00

117 lines
6.5 KiB
React

// hf-signin.jsx — Sign in (PUC-2 / PUC-3) hi-fi. Email → one-time code + states.
const { HFScreen: SScreen, HFWordmark: SWordmark, HFTopBar: STopBar, HFFooter: SFooter,
HFAuthCard: SAuthCard, HFField: SField, HFCodeField: SCodeField, HFBanner: SBanner,
HFPrimary: SPrimary, HFHeading: SHeading, HFStateCard: SStateCard, HFStates: SStates,
HF_H: S, hfHonest: sHonest } = window;
function SBack({ label = 'Back' }) {
return <a className="lk" href="#" style={{ fontFamily: S.body, fontSize: 14, color: S.mute }}> {label}</a>;
}
// ── Step 1 · email ──────────────────────────────────────────────────────────
function EmailStep({ framing = 'signup', s = {} }) {
const login = framing === 'login';
return (
<>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 2 }}><SWordmark size={24} /></div>
<SHeading sub={login ? 'Enter your email and we\u2019ll send a one-time code to sign in.' : 'Enter your email to begin. There\u2019s no password to create.'}>
{login ? 'Log in' : 'Create your storefront'}
</SHeading>
{s.deliveryFail && <SBanner title="We couldn't send the code">The email didn't go out. Try again in a moment — nothing was lost.</SBanner>}
<SField label="Email" placeholder="you@example.com" value={s.email}
focus={!s.emailError && !s.email} error={s.emailError} />
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<SPrimary disabled={!!s.cooldown}>{s.cooldown ? `Resend in ${s.cooldown}s` : 'Send code'}</SPrimary>
<p style={sHonest}>We'll email you a one-time code. That's all we need — no password, ever.</p>
</div>
</>
);
}
// ── Step 2 · code ────────────────────────────────────────────────────────────
function CodeStep({ s = {} }) {
const email = s.email || 'ben@bensbets.com';
let err = null;
if (s.codeWrong) err = `That code didn't match ${s.attempts ?? 2} attempts left.`;
if (s.codeExpired) err = 'That code expired. Send a fresh one below.';
return (
<>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: 2 }}><SWordmark size={24} /></div>
<SHeading sub={<>We sent a 6-digit code to <span style={{ color: S.star }}>{email}</span>. <a className="lk" href="#">Wrong address?</a></>}>
Check your email
</SHeading>
{s.welcome && <SBanner tone="info" title={s.welcome === 'new' ? 'Welcome to ecomm' : 'Welcome back'}>
{s.welcome === 'new' ? 'A new account was created for this email.' : 'Signed in to your existing account.'}
</SBanner>}
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, alignItems: 'center' }}>
<SCodeField filled={s.filled ?? 3} error={!!(s.codeWrong || s.codeExpired)} />
{err && <span style={{ fontFamily: S.body, fontSize: 13, color: S.gold, display: 'flex', gap: 6, alignSelf: 'flex-start' }}><span aria-hidden="true" style={{ fontSize: 11 }}>◆</span>{err}</span>}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<SPrimary>Continue</SPrimary>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<p style={sHonest}>Good for 10 minutes.</p>
<a className="lk" href="#" style={{ fontFamily: S.disp, fontWeight: 500, fontSize: 13.5, color: s.cooldown ? S.mute : S.lilac }}>
{s.cooldown ? `Resend in ${s.cooldown}s` : 'Resend code'}
</a>
</div>
</div>
</>
);
}
function HFSignIn({ step = 'email', framing = 'signup', device = 'desktop', s = {} }) {
const desktop = device === 'desktop';
return (
<SScreen horizon>
<STopBar pad={desktop ? '0 36px' : '0 22px'} left={<SWordmark size={desktop ? 22 : 20} />} right={<SBack />} />
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: desktop ? 32 : 18 }}>
<SAuthCard width={desktop ? 432 : '100%'}>
{step === 'email' ? <EmailStep framing={framing} s={s} /> : <CodeStep s={s} />}
</SAuthCard>
</div>
{desktop && <SFooter />}
</SScreen>
);
}
// ── States stack ─────────────────────────────────────────────────────────────
function codeNote(text) {
return <span style={{ fontFamily: S.body, fontSize: 13, color: S.gold, display: 'flex', gap: 6, marginTop: 10 }}><span aria-hidden="true" style={{ fontSize: 11 }}>◆</span>{text}</span>;
}
function HFSignInStates() {
return (
<SStates>
<SStateCard label="Invalid email" note="inline field error">
<SField label="Email" value="ben@bensbets" error="That doesn't look like an email address." />
</SStateCard>
<SStateCard label="Resend cooldown" note="PUC-2c · 60s per email">
<div style={{ display: 'flex', flexDirection: 'column', gap: 11 }}>
<SPrimary disabled>Resend in 47s</SPrimary>
<span style={sHonest}>One code per minute keeps inboxes — and abuse — in check.</span>
</div>
</SStateCard>
<SStateCard label="Delivery failed" note="502 · honest failure (INV-9)">
<SBanner title="We couldn't send the code">The email didn't go out — try again. We'd never fake a success.</SBanner>
</SStateCard>
<SStateCard label="Wrong code" note="PUC-2a">
<SCodeField filled={6} error />{codeNote("That code didn't match — 2 attempts left.")}
</SStateCard>
<SStateCard label="Expired code" note="PUC-2b · 10-min window">
<SCodeField filled={6} error />{codeNote('That code expired. Send a fresh one below.')}
</SStateCard>
<SStateCard label="Attempts exhausted" note="5 tries · code invalidated (INV-3)">
<SBanner title="Too many attempts">That code is no longer valid. Request a fresh one to keep going.</SBanner>
</SStateCard>
<SStateCard label="Honest welcome" note="PUC-3 · same flow, true label">
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<SBanner tone="info" title="Welcome to ecomm">A new account was created for this email.</SBanner>
<SBanner tone="info" title="Welcome back">Signed in to your existing account.</SBanner>
</div>
</SStateCard>
</SStates>
);
}
Object.assign(window, { HFSignIn, HFSignInStates });