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>
This commit is contained in:
2026-06-10 21:53:43 -07:00
parent f4d6961792
commit afe36fd347
37 changed files with 5043 additions and 1 deletions
@@ -0,0 +1,267 @@
// hf-kit.jsx — Wiggleverse ecomm HI-FI primitives (Direction A · Quiet centered).
// Depth = layered surfaces + hairlines (the brand avoids shadows). Hover = 13px lift.
// Merchant persona: Ben's Bets · ben@bensbets.com
const HFDS = window.WiggleverseDesignSystem_94cd80;
const { Button: HFButton, Soul: HFSoul, Eyebrow: HFEyebrow, Tag: HFTag } = HFDS;
// Resolve a brand asset: blob URL when bundled offline (window.__resources), else relative path.
const HF_ASSET = (id, path) => (window.__resources && window.__resources[id]) || path;
window.HF_ASSET = HF_ASSET;
// ---- token shorthands ----------------------------------------------------
const H = {
sky: 'var(--wv-midnight)', night: 'var(--wv-night)',
raised: 'var(--wv-indigo)', raisedHi: 'var(--wv-indigo-2)',
star: 'var(--wv-starlight)', soft: 'var(--wv-starlight-78)',
mute: 'var(--wv-starlight-60)', faint: 'var(--wv-starlight-55)',
lilac: 'var(--wv-lilac)', violet: 'var(--wv-violet)',
gold: 'var(--wv-gold)', goldHi: 'var(--wv-gold-hi)', goldInk: 'var(--wv-gold-ink)',
hair: 'var(--border-soft)', hairCard: 'var(--border-card)', hairStrong: 'var(--border-strong)',
disp: 'var(--wv-font-display)', body: 'var(--wv-font-body)', soul: 'var(--wv-font-human)',
shadow: 'var(--shadow-soft)',
};
const MENU_EMAIL = 'ben@bensbets.com';
const STORE = "Ben's Bets";
// barely-there starfield (Direction A: restrained) — a few asymmetric nodes
const HF_STARS =
'radial-gradient(1.3px 1.3px at 16% 22%, rgba(237,234,255,.34), transparent),' +
'radial-gradient(1.2px 1.2px at 78% 14%, rgba(237,234,255,.22), transparent),' +
'radial-gradient(1.1px 1.1px at 88% 46%, rgba(155,140,255,.30), transparent),' +
'radial-gradient(1.3px 1.3px at 30% 72%, rgba(237,234,255,.24), transparent),' +
'radial-gradient(1.1px 1.1px at 62% 86%, rgba(237,234,255,.18), transparent),' +
'radial-gradient(1.2px 1.2px at 7% 56%, rgba(155,140,255,.26), transparent)';
// gold horizon — the brand's warmth, a low glow at the bottom of the sky
const HF_HORIZON = 'radial-gradient(120% 70% at 50% 142%, rgba(244,199,107,.16) 0%, rgba(244,199,107,.05) 38%, transparent 64%)';
// One-time injected stylesheet: hover + focus affordances (subtle motion only).
function HFStyles() {
return (
<style dangerouslySetInnerHTML={{ __html: `
.hf { --r: var(--radius-card); }
.hf a.lk { color: var(--wv-lilac); text-decoration: none; transition: color .15s ease; }
.hf a.lk:hover { color: var(--wv-gold); }
.hf .row-lift { transition: transform .15s ease, border-color .15s ease, background .15s ease; }
.hf .row-lift:hover { transform: translateY(-1px); border-color: var(--border-strong); background: var(--wv-indigo-2); }
.hf .field { transition: border-color .15s ease, box-shadow .15s ease, background .15s ease; }
.hf .field:hover { border-color: var(--border-strong); }
.hf .field.is-focus { border-color: var(--wv-gold); box-shadow: 0 0 0 3px var(--wv-gold-28); background: rgba(237,234,255,.06); }
.hf .field.is-attn { border-color: var(--wv-gold); }
.hf .caret { display:inline-block; width:1.5px; height:1.1em; background: var(--wv-gold); margin-left:1px; vertical-align:-2px; animation: hfblink 1.1s steps(1) infinite; }
@keyframes hfblink { 50% { opacity: 0; } }
.hf .signout { font-family: var(--wv-font-display); font-weight:500; font-size:14px; color: var(--wv-starlight); background:transparent; border:none; cursor:pointer; padding:0; transition: color .15s ease; }
.hf .signout:hover { color: var(--wv-gold); }
.hf .navitem { transition: background .15s ease, color .15s ease; }
` }} />
);
}
// ── Screen ground ──────────────────────────────────────────────────────────
function HFScreen({ children, horizon = false, stars = true, bg = H.sky, style = {} }) {
const layers = [];
if (stars) layers.push(HF_STARS);
if (horizon) layers.unshift(HF_HORIZON);
const background = layers.length ? `${layers.join(',')}, ${bg}` : bg;
return (
<div className="hf" style={{
width: '100%', height: '100%', boxSizing: 'border-box', background,
color: H.star, fontFamily: H.body, display: 'flex', flexDirection: 'column',
position: 'relative', overflow: 'hidden', ...style,
}}>{children}</div>
);
}
// ── Wordmark — mark tile + "ecomm" in Space Grotesk ───────────────────────
function HFWordmark({ size = 26, sub = false }) {
return (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: size * 0.42 }}>
<img src={HF_ASSET('markTile', 'assets/mark-tile.svg')} width={size} height={size}
style={{ borderRadius: size * 0.22, display: 'block' }} alt="" />
<span style={{ display: 'inline-flex', alignItems: 'baseline', gap: size * 0.3 }}>
<span style={{ fontFamily: H.disp, fontWeight: 700, fontSize: size * 0.86, letterSpacing: '-0.015em', color: H.star }}>ecomm</span>
{sub && <span style={{ fontFamily: H.body, fontSize: size * 0.42, color: H.mute, letterSpacing: '0.02em' }}>by Wiggleverse</span>}
</span>
</span>
);
}
// ── TopBar — glass chrome ──────────────────────────────────────────────────
function HFTopBar({ left, right, pad = '0 36px', height = 68 }) {
return (
<header style={{
flex: '0 0 auto', height, padding: pad, display: 'flex', alignItems: 'center',
justifyContent: 'space-between', borderBottom: `1px solid ${H.hair}`,
background: 'var(--glass-sky)', backdropFilter: 'blur(var(--glass-blur))',
position: 'relative', zIndex: 5,
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>{left}</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>{right}</div>
</header>
);
}
function HFAccountChip({ email = MENU_EMAIL, menu = false }) {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 14, position: 'relative' }}>
<span style={{ width: 30, height: 30, borderRadius: 15, background: 'var(--wv-lilac-16)', border: `1px solid ${H.hairCard}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: H.disp, fontWeight: 700, fontSize: 13, color: H.star }}>B</span>
<span style={{ fontFamily: H.body, fontSize: 14, color: H.soft }}>{email}</span>
<span style={{ width: 1, height: 18, background: H.hair }} />
<button className="signout">Sign out</button>
{menu && (
<div style={{ position: 'absolute', top: 40, right: 0, minWidth: 216, zIndex: 9, background: H.raised, border: `1px solid ${H.hairCard}`, borderRadius: 12, padding: 7, boxShadow: H.shadow }}>
<div style={{ padding: '9px 12px' }}>
<div style={{ fontFamily: H.disp, fontWeight: 500, fontSize: 13.5, color: H.star }}>Ben</div>
<div style={{ fontFamily: H.body, fontSize: 12.5, color: H.mute }}>{email}</div>
</div>
<div style={{ borderTop: `1px solid ${H.hair}`, margin: '6px 4px' }} />
<div className="navitem" style={{ padding: '9px 12px', fontFamily: H.body, fontSize: 13.5, color: H.soft, borderRadius: 8, cursor: 'pointer' }}>Account settings</div>
<div className="navitem" style={{ padding: '9px 12px', fontFamily: H.body, fontSize: 13.5, color: H.gold, fontWeight: 600, borderRadius: 8, cursor: 'pointer' }}>Sign out</div>
</div>
)}
</div>
);
}
// ── Field ──────────────────────────────────────────────────────────────────
function HFField({ label, placeholder, value, helper, error, focus = false, optional = false, hint }) {
const cls = `field${focus ? ' is-focus' : ''}${error ? ' is-attn' : ''}`;
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<label style={{ fontFamily: H.body, fontWeight: 500, fontSize: 13.5, color: H.soft, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
<span>{label}</span>
{optional && <span style={{ fontSize: 12.5, color: H.mute, fontWeight: 400 }}>optional</span>}
</label>
<div className={cls} style={{
height: 52, borderRadius: 10, padding: '0 16px', display: 'flex', alignItems: 'center',
background: 'rgba(237,234,255,.035)', border: `1.5px solid ${H.hair}`,
fontFamily: H.body, fontSize: 16, color: value ? H.star : H.mute,
}}>
{value || placeholder}
{focus && !value && <span className="caret" />}
{hint && <span style={{ marginLeft: 'auto', fontFamily: H.body, fontSize: 12.5, color: H.mute }}>{hint}</span>}
</div>
{error && <HFNote tone="attn">{error}</HFNote>}
{helper && !error && <HFNote>{helper}</HFNote>}
</div>
);
}
function HFNote({ children, tone }) {
return <span style={{ fontFamily: H.body, fontSize: 13, lineHeight: 1.45, color: tone === 'attn' ? H.gold : H.mute, display: 'flex', gap: 7, alignItems: 'baseline' }}>
{tone === 'attn' && <span aria-hidden="true" style={{ color: H.gold, fontSize: 11 }}></span>}
<span>{children}</span>
</span>;
}
// ── CodeField — 6 cells ─────────────────────────────────────────────────────
function HFCodeField({ filled = 0, error = false, vals = ['4', '1', '9', '2', '0', '4'] }) {
return (
<div style={{ display: 'flex', gap: 11 }}>
{[0, 1, 2, 3, 4, 5].map((i) => {
const active = i === filled && !error;
const has = i < filled;
return (
<div key={i} className={`field${active ? ' is-focus' : ''}${error ? ' is-attn' : ''}`} style={{
width: 52, height: 64, borderRadius: 11, display: 'flex', alignItems: 'center', justifyContent: 'center',
background: 'rgba(237,234,255,.035)', border: `1.5px solid ${H.hair}`,
fontFamily: H.disp, fontWeight: 500, fontSize: 26, color: H.star,
}}>
{has ? vals[i] : (active ? <span className="caret" style={{ height: 28 }} /> : '')}
</div>
);
})}
</div>
);
}
// ── Banner — gold attention / lilac info (no red in palette) ───────────────
function HFBanner({ children, tone = 'attn', title }) {
const attn = tone === 'attn';
return (
<div style={{
display: 'flex', gap: 12, padding: '14px 16px', borderRadius: 12,
background: attn ? 'rgba(244,199,107,.10)' : 'var(--wv-lilac-08)',
border: `1px solid ${attn ? 'var(--wv-gold-40)' : 'var(--wv-lilac-18)'}`,
alignItems: 'flex-start',
}}>
<span aria-hidden="true" style={{ color: attn ? H.gold : H.lilac, fontSize: 13, lineHeight: '22px' }}>{attn ? '◆' : '◇'}</span>
<div style={{ fontFamily: H.body, fontSize: 14, lineHeight: 1.5, color: H.soft }}>
{title && <div style={{ color: H.star, fontWeight: 600, marginBottom: 2 }}>{title}</div>}
{children}
</div>
</div>
);
}
// ── AuthCard — the one surface that earns a real shadow ────────────────────
function HFAuthCard({ children, width = 440 }) {
return (
<div style={{
width, maxWidth: '100%', boxSizing: 'border-box', background: H.raised,
border: `1px solid ${H.hairCard}`, borderRadius: 'var(--radius-card)',
padding: '40px 40px 34px', display: 'flex', flexDirection: 'column', gap: 22,
boxShadow: H.shadow, position: 'relative', zIndex: 2,
}}>{children}</div>
);
}
// ── Primary action wrapper (DS Button, full-width option) ──────────────────
function HFPrimary({ children, full = true, disabled = false }) {
return (
<HFButton variant="primary" href={disabled ? undefined : '#'} disabled={disabled}
style={{ justifyContent: 'center', width: full ? '100%' : 'auto', padding: '.85rem 1.4rem', fontSize: 15.5, boxSizing: 'border-box' }}>
{children}
</HFButton>
);
}
// ── Footer ──────────────────────────────────────────────────────────────────
function HFFooter({ pad = '20px 36px' }) {
return (
<footer style={{ flex: '0 0 auto', padding: pad, borderTop: `1px solid ${H.hair}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 16, fontFamily: H.body, fontSize: 12.5, color: H.mute, background: 'rgba(9,12,34,.4)' }}>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 9 }}>
<img src={HF_ASSET('markGold', 'assets/mark-mono-gold.svg')} width={16} height={16} alt="" style={{ opacity: 0.85 }} />
ecomm · a Wiggleverse line
</span>
<span style={{ display: 'inline-flex', gap: 18 }}>
<a className="lk" href="#" style={{ color: H.mute }}>Privacy</a>
<a className="lk" href="#" style={{ color: H.mute }}>Open Core</a>
<a className="lk" href="#" style={{ color: H.mute }}>Terms</a>
</span>
</footer>
);
}
// ── Heading helper ───────────────────────────────────────────────────────────
function HFHeading({ children, sub, size = 27 }) {
return (
<div>
<h1 style={{ fontFamily: H.disp, fontWeight: 700, letterSpacing: '-0.015em', fontSize: size, color: H.star, margin: '0 0 9px', lineHeight: 1.08 }}>{children}</h1>
{sub && <p style={{ fontFamily: H.body, fontSize: 14.5, lineHeight: 1.55, color: H.soft, margin: 0 }}>{sub}</p>}
</div>
);
}
const hfHonest = { fontFamily: H.body, fontSize: 13, color: H.mute, lineHeight: 1.5, margin: 0 };
// ── StateCard — frames a single state in the states stacks ─────────────────
function HFStateCard({ label, note, children }) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 11 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 9, flexWrap: 'wrap' }}>
<HFTag variant="soon">{label}</HFTag>
{note && <span style={{ fontFamily: H.body, fontSize: 12.5, color: H.mute }}>{note}</span>}
</div>
<div style={{ background: H.raised, border: `1px solid ${H.hairCard}`, borderRadius: 12, padding: 22 }}>{children}</div>
</div>
);
}
function HFStates({ children }) {
return <div className="hf" style={{ height: '100%', background: `${HF_STARS}, ${H.sky}`, padding: 26, boxSizing: 'border-box', overflow: 'hidden', fontFamily: H.body, display: 'flex', flexDirection: 'column', gap: 20 }}>{children}</div>;
}
Object.assign(window, {
HF_H: H, HF_STARS, HF_HORIZON, HF_STORE: STORE, HF_EMAIL: MENU_EMAIL, HF_DSC: HFDS,
HFStyles, HFScreen, HFWordmark, HFTopBar, HFAccountChip, HFField, HFNote, HFCodeField,
HFBanner, HFAuthCard, HFPrimary, HFFooter, HFHeading, HFStateCard, HFStates,
hfHonest,
});