Captures the centered-card registration design 1:1 from the claude.ai/design export. Covers all 10 sections: desktop overview, header, above-card copy, form fields, password states, notification card, submit button, success panel, mobile layout, and i18n/a11y/backend implementation notes. Relates to #269 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1372 lines
78 KiB
HTML
1372 lines
78 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Register Page · Final Design Spec · Familienarchiv</title>
|
||
<style>
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
body{font-family:'Helvetica Neue',Arial,sans-serif;background:#ECEAE4;color:#1A1A1A;line-height:1.5}
|
||
.doc{max-width:1400px;margin:0 auto;padding:48px 32px}
|
||
|
||
/* ── Masthead ─── */
|
||
.mast{background:#0D2240;border-radius:10px;padding:32px 40px;margin-bottom:48px}
|
||
.mast-top{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin-bottom:16px}
|
||
.mast h1{font-size:22px;font-weight:900;color:#fff;letter-spacing:-.4px;margin-bottom:6px}
|
||
.mast p{font-size:12px;color:rgba(255,255,255,.5);max-width:640px;line-height:1.7}
|
||
.mast-badge{font-size:9px;font-weight:800;padding:3px 9px;border-radius:20px;text-transform:uppercase;letter-spacing:.8px;white-space:nowrap;flex-shrink:0;margin-top:4px}
|
||
.mb-final{background:#A6DAD8;color:#002850}
|
||
.decisions{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px;border-top:1px solid rgba(255,255,255,.1);padding-top:16px}
|
||
.dec{background:rgba(255,255,255,.06);border-radius:6px;padding:10px 12px}
|
||
.dec-label{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:rgba(255,255,255,.35);margin-bottom:5px}
|
||
.dec-value{font-size:9.5px;font-weight:700;color:#fff;line-height:1.5}
|
||
|
||
/* ── Section headings ─── */
|
||
.sec{margin-bottom:64px}
|
||
.sec+.sec{border-top:2px dashed #C8C4BE;padding-top:56px}
|
||
.sec-h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:1.2px;color:#888;margin-bottom:20px;display:flex;align-items:center;gap:10px}
|
||
.sec-h::after{content:'';flex:1;height:1px;background:#D8D4CE}
|
||
.sec-num{background:#0D2240;color:#fff;font-size:9px;font-weight:900;padding:2px 7px;border-radius:10px}
|
||
|
||
/* ── Grid ─── */
|
||
.sg{display:grid;gap:20px;align-items:start}
|
||
.sg-2{grid-template-columns:1fr 1fr}
|
||
.sg-3{grid-template-columns:1fr 1fr 1fr}
|
||
.sg-4{grid-template-columns:1fr 1fr 1fr 1fr}
|
||
.sb{display:flex;flex-direction:column}
|
||
.sl{font-size:9px;font-weight:800;color:#888;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:6px;display:flex;align-items:center;gap:6px}
|
||
.sc{font-size:10px;color:#888;margin-top:8px;font-style:italic;line-height:1.5}
|
||
|
||
/* ── Browser chrome (desktop) ─── */
|
||
.wf{background:#fff;border:2px solid #B8B4AE;border-radius:10px;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.08)}
|
||
.wf-inner{border-radius:0;overflow:hidden}
|
||
.wf-bar{height:24px;background:#E8E4DF;border-bottom:1px solid #C8C4BE;display:flex;align-items:center;padding:0 9px;gap:4px}
|
||
.dot{width:7px;height:7px;border-radius:50%;background:#C8C4BE}
|
||
.dot.r{background:#F87171}.dot.y{background:#FCD34D}.dot.g{background:#4ADE80}
|
||
.urlbar{flex:1;height:11px;background:#D8D4CE;border-radius:3px;margin-left:6px;display:flex;align-items:center;padding:0 5px}
|
||
.urlbar span{font-size:7.5px;color:#888;font-family:monospace}
|
||
|
||
/* ── Mobile chrome ─── */
|
||
.WF-M{background:#fff;border:2px solid #B8B4AE;border-radius:16px;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.08);width:220px}
|
||
.WF-M-STATUS{height:14px;background:#012851;display:flex;align-items:center;justify-content:space-between;padding:0 10px}
|
||
.WF-M-TIME{font-size:6px;color:#fff;font-weight:700}
|
||
.WF-M-ICONS{display:flex;gap:3px}
|
||
.WF-M-ICON{width:5px;height:5px;background:rgba(255,255,255,.5);border-radius:1px}
|
||
|
||
/* ── State pill labels ─── */
|
||
.state-label{display:inline-block;font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;border-radius:3px;padding:1px 5px;margin-bottom:5px}
|
||
.st-default{background:#E8E4DF;color:#555}
|
||
.st-hover{background:#DBEAFE;color:#1D4ED8}
|
||
.st-focus{background:#EDE9FE;color:#6D28D9}
|
||
.st-active{background:#DCFCE7;color:#166534}
|
||
.st-loading{background:#FEF9C3;color:#854D0E}
|
||
.st-error{background:#FEE2E2;color:#991B1B}
|
||
.st-success{background:#DCFCE7;color:#166534}
|
||
.st-checked{background:#CFFAFE;color:#0e7490}
|
||
|
||
/* ── Annotation callouts ─── */
|
||
.ann-block{background:#FFF7ED;border:1px solid #FDBA74;border-radius:5px;padding:10px 14px;font-size:10.5px;color:#7C2D12;line-height:1.6;margin-top:14px}
|
||
.ann-block strong{font-weight:800}
|
||
.ann-block ul{padding-left:16px;display:flex;flex-direction:column;gap:3px;margin-top:6px}
|
||
.ann-info{background:#EFF6FF;border:1px solid #BFDBFE;border-radius:5px;padding:10px 14px;font-size:10.5px;color:#1E3A5F;line-height:1.6;margin-top:14px}
|
||
.ann-info strong{font-weight:800}
|
||
.ann-info ul{padding-left:16px;display:flex;flex-direction:column;gap:3px;margin-top:6px}
|
||
|
||
/* ── Spec disclaimer ─── */
|
||
.spec-disclaimer{background:#FFF8E1;border:1.5px solid #FFC107;border-radius:6px;padding:11px 16px;font-size:11px;color:#6D4C00;margin-bottom:32px;line-height:1.6}
|
||
.spec-disclaimer strong{font-weight:800}
|
||
|
||
/* ── impl-ref ─── */
|
||
.impl-ref{background:#0d1117;border-radius:8px;margin-top:20px;overflow:hidden;border:1px solid #30363d}
|
||
.impl-ref-hdr{background:#161b22;padding:9px 16px;font-size:9.5px;font-weight:800;color:#f0883e;border-bottom:1px solid #30363d;display:flex;align-items:center;gap:8px;letter-spacing:.4px;text-transform:uppercase}
|
||
.impl-ref-hdr::before{content:'⚙';font-size:12px}
|
||
.impl-ref-hdr span{color:rgba(240,136,62,.55);font-weight:400;margin-left:auto;font-size:9px;text-transform:none;letter-spacing:0}
|
||
.impl-ref table{width:100%;border-collapse:collapse;font-size:10px}
|
||
.impl-ref th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#8b949e;padding:8px 14px;border-bottom:1px solid #21262d}
|
||
.impl-ref td{padding:6px 14px;border-bottom:1px solid #161b22;vertical-align:top;line-height:1.6;color:#c9d1d9}
|
||
.impl-ref tr:last-child td{border-bottom:none}
|
||
.impl-ref td:first-child{color:#79c0ff;font-weight:700;white-space:nowrap;width:210px}
|
||
.impl-ref td code{font-family:'SFMono-Regular',Consolas,monospace;font-size:9.5px;background:#161b22;color:#a5d6ff;padding:1px 5px;border-radius:3px;white-space:nowrap}
|
||
.impl-ref .ir-px{color:#7ee787;font-family:monospace;font-size:9.5px}
|
||
|
||
/* ════════════════════════════════════════
|
||
REGISTER PAGE MOCKUP COMPONENTS
|
||
All sizes scaled to ~55% of real values.
|
||
DO NOT copy sizes from here — use impl-ref tables.
|
||
════════════════════════════════════════ */
|
||
|
||
/* Page shell */
|
||
.RP{background:#f0efe9;display:flex;flex-direction:column;font-family:'Helvetica Neue',Arial,sans-serif}
|
||
|
||
/* ── Header ─── */
|
||
.RP-HEADER{background:#012851;flex-shrink:0}
|
||
.RP-STRIPE{height:2px;background:#a1dcd8}
|
||
.RP-NAV{height:35px;display:flex;align-items:center;padding:0 18px;gap:10px}
|
||
.RP-WORDMARK{font-size:11px;font-weight:900;color:#fff;letter-spacing:.15em;text-transform:uppercase}
|
||
.RP-LANG{margin-left:auto;display:flex;align-items:center;gap:2px}
|
||
.RP-LANG-BTN{font-size:6.5px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.55);padding:3px 6px;border:none;background:transparent;border-bottom:1.5px solid transparent;cursor:pointer}
|
||
.RP-LANG-BTN.on{color:#fff;border-bottom-color:#a1dcd8}
|
||
|
||
/* ── Main area ─── */
|
||
.RP-MAIN{flex:1;display:flex;justify-content:center;align-items:flex-start;padding:35px 18px}
|
||
|
||
/* ── Narrow artboard (card only, for close-ups) ─── */
|
||
.RP-INNER{width:352px}
|
||
|
||
/* ── Above-card ─── */
|
||
.RP-ABOVE{text-align:center;margin-bottom:20px}
|
||
.RP-EYEBROW{display:inline-block;font-size:6px;font-weight:800;letter-spacing:.22em;text-transform:uppercase;color:#4b5563;border-top:1px solid #d4d2c5;border-bottom:1px solid #d4d2c5;padding:3px 8px;margin-bottom:10px}
|
||
.RP-H1{font-size:25px;font-weight:400;color:#012851;line-height:1.12;letter-spacing:-.005em;margin-bottom:9px;font-family:Georgia,serif}
|
||
.RP-SUB{font-size:10px;color:#4b5563;line-height:1.6;font-family:Georgia,serif;max-width:297px;margin:0 auto}
|
||
|
||
/* ── Card ─── */
|
||
.RP-CARD{background:#fff;border:1px solid #e4e2d7;box-shadow:0 1px 2px 0 rgb(0 0 0/.05);border-radius:2px;padding:22px}
|
||
|
||
/* ── Section caption ─── */
|
||
.RP-SCAP{display:flex;align-items:center;gap:7px;margin-bottom:9px}
|
||
.RP-SCAP-TEXT{font-size:6px;font-weight:800;letter-spacing:.18em;text-transform:uppercase;color:#012851;white-space:nowrap}
|
||
.RP-SCAP-RULE{flex:1;height:1px;background:#e4e2d7}
|
||
|
||
/* ── Fields ─── */
|
||
.RP-FIELDS{display:flex;flex-direction:column;gap:15px}
|
||
.RP-FIELD{display:flex;flex-direction:column;gap:0}
|
||
.RP-LABEL{font-size:6.5px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#4b5563;margin-bottom:4px}
|
||
.RP-INPUT{width:100%;background:#fff;border:1px solid #e4e2d7;border-radius:2px;padding:8px 9px;font-size:9.5px;color:#012851;font-family:Georgia,serif;outline:none}
|
||
.RP-INPUT.focused{border-color:#012851;box-shadow:0 0 0 1.5px rgba(1,40,81,.15)}
|
||
.RP-INPUT.invalid{border-color:#c0392b}
|
||
.RP-INPUT::placeholder{color:#9ca3af}
|
||
.RP-HINT{font-size:7px;color:#6b7280;margin-top:3px;display:flex;align-items:center;gap:3px}
|
||
.RP-HINT.ok{color:#0a6b56}
|
||
.RP-HINT.err{color:#c0392b}
|
||
.RP-HINT-DOT{width:4.5px;height:4.5px;border-radius:999px;background:#00c7b1;flex-shrink:0}
|
||
|
||
/* ── 2-col name grid ─── */
|
||
.RP-2COL{display:grid;grid-template-columns:1fr 1fr;gap:9px}
|
||
|
||
/* ── Password input wrapper ─── */
|
||
.RP-PWINPUT{position:relative}
|
||
.RP-PWINPUT .RP-INPUT{padding-right:50px}
|
||
.RP-SHOWBTN{position:absolute;right:0;top:0;bottom:0;padding:0 8px;background:transparent;border:none;border-left:1px solid #e4e2d7;font-size:6px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#4b5563;cursor:pointer;display:flex;align-items:center}
|
||
|
||
/* ── Notification card (CheckOption) ─── */
|
||
.RP-CHECK{display:flex;gap:8px;padding:8px 9px;border:1px solid #e4e2d7;background:#fff;border-radius:2px;align-items:flex-start;cursor:pointer}
|
||
.RP-CHECK.checked{border-color:#012851;background:rgba(161,220,216,.18)}
|
||
.RP-CHECKBOX{flex-shrink:0;width:12px;height:12px;border-radius:2px;border:1.5px solid #9ca3af;background:#fff;display:flex;align-items:center;justify-content:center;margin-top:1px}
|
||
.RP-CHECKBOX.checked{background:#012851;border-color:#012851}
|
||
.RP-CHECKMARK{width:8px;height:8px}
|
||
.RP-CHECK-BODY{display:flex;flex-direction:column;gap:1px}
|
||
.RP-CHECK-TITLE{font-size:8px;font-weight:700;color:#012851}
|
||
.RP-CHECK-DESC{font-size:8.5px;color:#4b5563;font-family:Georgia,serif;line-height:1.5}
|
||
|
||
/* ── Submit button ─── */
|
||
.RP-SUBMIT{width:100%;background:#012851;color:#fff;border:none;border-radius:2px;padding:9px 13px;font-size:7px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;display:flex;align-items:center;justify-content:center;gap:5px;cursor:pointer;margin-top:18px}
|
||
.RP-SUBMIT:hover,.RP-SUBMIT.hover{background:#01386f}
|
||
.RP-SUBMIT.loading{opacity:.85;cursor:wait}
|
||
.RP-SUBMIT svg{width:9px;height:9px}
|
||
|
||
/* ── Footer links ─── */
|
||
.RP-FOOTER{text-align:center;margin-top:13px;display:flex;flex-direction:column;gap:8px}
|
||
.RP-PRIVACY{font-size:7.5px;color:#6b7280;font-family:Georgia,serif;line-height:1.6}
|
||
.RP-PRIVACY a{color:#012851;text-decoration:underline;text-decoration-color:#a1dcd8;text-underline-offset:2px;text-decoration-thickness:1.5px}
|
||
.RP-SIGNIN{font-size:7px;color:#4b5563}
|
||
.RP-SIGNIN a{color:#012851;font-weight:800;text-transform:uppercase;letter-spacing:.1em;font-size:6.5px;text-decoration:underline;text-decoration-color:#a1dcd8;text-underline-offset:2px;text-decoration-thickness:1.5px}
|
||
|
||
/* ── Success panel ─── */
|
||
.RP-SUCCESS{text-align:center;padding:4px 0}
|
||
.RP-SUCCESS-CIRCLE{width:40px;height:40px;border-radius:999px;background:rgba(161,220,216,.35);display:inline-flex;align-items:center;justify-content:center;margin-bottom:13px}
|
||
.RP-SUCCESS-H{font-size:16.5px;font-weight:400;color:#012851;margin-bottom:8px;font-family:Georgia,serif;line-height:1.2}
|
||
.RP-SUCCESS-B{font-size:9.5px;color:#4b5563;font-family:Georgia,serif;line-height:1.55;max-width:231px;margin:0 auto 15px}
|
||
.RP-SUCCESS-ACTIONS{display:flex;flex-direction:column;gap:5px;max-width:176px;margin:0 auto}
|
||
.RP-SUCCESS-RESEND{background:transparent;border:none;font-size:6.5px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#4b5563;padding:5px;cursor:pointer;text-decoration:underline;text-decoration-color:#a1dcd8;text-underline-offset:3px;text-decoration-thickness:1.5px}
|
||
|
||
/* ── Loading spinner ─── */
|
||
.RP-SPINNER{width:8px;height:8px;border:1.5px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:rpspin .7s linear infinite;flex-shrink:0}
|
||
@keyframes rpspin{to{transform:rotate(360deg)}}
|
||
|
||
/* ── Section divider within card ─── */
|
||
.RP-SEC-GAP{margin-bottom:15px}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="doc">
|
||
|
||
<!-- ══════════════════════════════════════
|
||
MASTHEAD
|
||
══════════════════════════════════════ -->
|
||
<div class="mast">
|
||
<div class="mast-top">
|
||
<div>
|
||
<h1>Register Page · Final Design Spec</h1>
|
||
<p>Centered-card variant (Variant A). Friendly, inviting registration form for Familienarchiv — a collaborative archive of family letters from the 19th and 20th centuries. Designed for a dual audience (60+ and 25–42), with large 17px serif inputs, du-Anrede (informal you), German-first with DE/EN/ES toggle, live password-match feedback, mention-notification opt-in, and a post-submit success panel.</p>
|
||
</div>
|
||
<span class="mast-badge mb-final">Final · Ready for implementation</span>
|
||
</div>
|
||
<div class="decisions">
|
||
<div class="dec">
|
||
<div class="dec-label">Variant</div>
|
||
<div class="dec-value">A — Centered card · 640px white card on sand canvas</div>
|
||
</div>
|
||
<div class="dec">
|
||
<div class="dec-label">Fields</div>
|
||
<div class="dec-value">Vorname · Nachname · E-Mail · Passwort · Passwort bestätigen · Benachrichtigungen</div>
|
||
</div>
|
||
<div class="dec">
|
||
<div class="dec-label">Languages</div>
|
||
<div class="dec-value">DE (default) / EN / ES — header toggle; swaps all labels, hints, and validation messages live</div>
|
||
</div>
|
||
<div class="dec">
|
||
<div class="dec-label">Audience</div>
|
||
<div class="dec-value">60+ primary (48px touch targets, 17px serif, calm layout) · 25–42 secondary</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── spec disclaimer ── -->
|
||
<div class="spec-disclaimer">
|
||
<strong>📐 Mockup scale notice —</strong> all font-size, height, and padding values
|
||
in the mockup CSS are scaled to ~55% of actual implementation values.
|
||
<strong>Do not copy sizes from mockup CSS.</strong> Use the ⚙ Implementation
|
||
Reference tables after each section.
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 1 — FULL PAGE OVERVIEW (DESKTOP)
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">1</span>
|
||
Full page — desktop (1280px)
|
||
</div>
|
||
|
||
<div class="sb">
|
||
<span class="state-label st-default">Default — form at rest, German language</span>
|
||
<div class="wf" style="max-width:860px">
|
||
<div class="wf-inner">
|
||
<div class="wf-bar">
|
||
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
|
||
<div class="urlbar"><span>localhost:3000/register</span></div>
|
||
</div>
|
||
<div class="RP">
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV">
|
||
<span class="RP-WORDMARK">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN on">DE</button>
|
||
<button class="RP-LANG-BTN">EN</button>
|
||
<button class="RP-LANG-BTN">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="RP-MAIN">
|
||
<div class="RP-INNER">
|
||
<div class="RP-ABOVE">
|
||
<div class="RP-EYEBROW">Ein Familienprojekt</div>
|
||
<div class="RP-H1">Schön, dass du da bist.</div>
|
||
<div class="RP-SUB">Bereits 1.500 Briefe aus vielen Jahrzehnten sind im Archiv. Mit deinem Konto hilfst du mit, sie zu lesen, zu ordnen und für die nächsten Generationen zu bewahren.</div>
|
||
</div>
|
||
<div class="RP-CARD">
|
||
<div class="RP-FIELDS">
|
||
<!-- Über dich -->
|
||
<div class="RP-SEC-GAP">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div class="RP-2COL">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Frieda" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Lehmann" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Konto -->
|
||
<div class="RP-SEC-GAP">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:9px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">E-Mail-Adresse</div>
|
||
<input class="RP-INPUT" placeholder="name@beispiel.de" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" placeholder="••••••••" readonly>
|
||
<button class="RP-SHOWBTN">Anzeigen</button>
|
||
</div>
|
||
<div class="RP-HINT">Mindestens 8 Zeichen.</div>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort bestätigen</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" placeholder="••••••••" readonly>
|
||
<button class="RP-SHOWBTN">Anzeigen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Benachrichtigungen -->
|
||
<div>
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Benachrichtigungen</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div class="RP-CHECK checked">
|
||
<div class="RP-CHECKBOX checked">
|
||
<svg class="RP-CHECKMARK" viewBox="0 0 16 16" fill="none">
|
||
<path d="M3 8.5l3.2 3.2L13 5" stroke="#fff" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="RP-CHECK-BODY">
|
||
<div class="RP-CHECK-TITLE">Benachrichtige mich,</div>
|
||
<div class="RP-CHECK-DESC">wenn jemand mich in einem Kommentar erwähnt oder mir auf einen Kommentar antwortet.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button class="RP-SUBMIT">
|
||
Konto erstellen
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||
</button>
|
||
<div class="RP-FOOTER">
|
||
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
|
||
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Page structure<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Page wrapper</td><td><code>flex min-h-screen flex-col bg-canvas</code></td><td>—</td><td>Same pattern as login page</td></tr>
|
||
<tr><td>Main centering wrapper</td><td><code>flex flex-1 justify-center items-start px-8 pt-16</code></td><td>64px top padding</td><td>Items start (not center) so tall forms don't overflow</td></tr>
|
||
<tr><td>Content column</td><td><code>w-full max-w-[640px]</code></td><td>640px max</td><td>Fixed max-width card column</td></tr>
|
||
<tr><td>Form card</td><td><code>bg-surface border border-line rounded-sm shadow-sm p-10</code></td><td>40px padding</td><td><code>rounded-sm</code> = 2px; <code>shadow-sm</code> = 0 1px 2px rgb(0 0 0/.05)</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 2 — HEADER ANATOMY
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">2</span>
|
||
Header — anatomy & language toggle states
|
||
</div>
|
||
|
||
<div class="sg sg-3" style="gap:20px;align-items:start">
|
||
|
||
<!-- DE active -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">DE active (default)</span>
|
||
<div class="wf">
|
||
<div class="wf-inner">
|
||
<div class="wf-bar">
|
||
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
|
||
<div class="urlbar"><span>localhost:3000/register</span></div>
|
||
</div>
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV">
|
||
<span class="RP-WORDMARK">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN on">DE</button>
|
||
<button class="RP-LANG-BTN">EN</button>
|
||
<button class="RP-LANG-BTN">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Active language: mint underline + white text. Inactive: 55% opacity.</div>
|
||
</div>
|
||
|
||
<!-- EN active -->
|
||
<div class="sb">
|
||
<span class="state-label st-hover">EN active</span>
|
||
<div class="wf">
|
||
<div class="wf-inner">
|
||
<div class="wf-bar">
|
||
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
|
||
<div class="urlbar"><span>localhost:3000/register?lang=en</span></div>
|
||
</div>
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV">
|
||
<span class="RP-WORDMARK">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN">DE</button>
|
||
<button class="RP-LANG-BTN on">EN</button>
|
||
<button class="RP-LANG-BTN">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Toggle switches all copy live (no page reload). All labels, hints, and validation messages switch language.</div>
|
||
</div>
|
||
|
||
<!-- ES active -->
|
||
<div class="sb">
|
||
<span class="state-label st-active">ES active</span>
|
||
<div class="wf">
|
||
<div class="wf-inner">
|
||
<div class="wf-bar">
|
||
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
|
||
<div class="urlbar"><span>localhost:3000/register?lang=es</span></div>
|
||
</div>
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV">
|
||
<span class="RP-WORDMARK">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN">DE</button>
|
||
<button class="RP-LANG-BTN">EN</button>
|
||
<button class="RP-LANG-BTN on">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Header<span>routes/AuthHeader.svelte (extend) or new RegisterHeader.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Header wrapper</td><td><code>sticky top-0 z-50 bg-[#012851]</code></td><td>—</td><td>Same as authenticated header but no nav links (unauthenticated page)</td></tr>
|
||
<tr><td>Mint accent stripe</td><td><code>h-1 bg-accent</code></td><td>4px</td><td><code>--c-accent: #a1dcd8</code>. Always at the very top of the header.</td></tr>
|
||
<tr><td>Nav bar</td><td><code>h-16 flex items-center px-8</code></td><td>64px tall, 32px side padding</td><td>Max-width container inside: <code>max-w-screen-xl mx-auto w-full</code></td></tr>
|
||
<tr><td>Wordmark</td><td><code>font-sans text-xl font-bold tracking-[.15em] text-white uppercase</code></td><td>20px</td><td>Not a link on the register page (user is not yet logged in)</td></tr>
|
||
<tr><td>Lang toggle container</td><td><code>ml-auto flex items-center gap-1</code></td><td>4px gap</td><td>—</td></tr>
|
||
<tr><td>Lang button — inactive</td><td><code>font-sans text-xs font-bold tracking-[.12em] uppercase text-white/55 px-2.5 py-1.5 border-b-2 border-transparent transition-colors hover:text-white</code></td><td>12px, padding 6px 10px</td><td>Touch target: 12+6+6 = 24px tall, acceptable for a header toggle since it's not a primary action</td></tr>
|
||
<tr><td>Lang button — active</td><td><code>text-white border-b-2 border-accent</code></td><td>—</td><td>Mint underline border</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 3 — ABOVE-CARD SECTION
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">3</span>
|
||
Above-card — eyebrow, headline, subtext
|
||
</div>
|
||
|
||
<div class="sg sg-3" style="gap:20px;align-items:start">
|
||
|
||
<!-- German -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">German (DE)</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#f0efe9;padding:22px 18px">
|
||
<div class="RP-ABOVE" style="margin-bottom:0">
|
||
<div class="RP-EYEBROW">Ein Familienprojekt</div>
|
||
<div class="RP-H1">Schön, dass du da bist.</div>
|
||
<div class="RP-SUB">Bereits 1.500 Briefe aus vielen Jahrzehnten sind im Archiv. Mit deinem Konto hilfst du mit, sie zu lesen, zu ordnen und für die nächsten Generationen zu bewahren.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- English -->
|
||
<div class="sb">
|
||
<span class="state-label st-hover">English (EN)</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#f0efe9;padding:22px 18px">
|
||
<div class="RP-ABOVE" style="margin-bottom:0">
|
||
<div class="RP-EYEBROW">A family project</div>
|
||
<div class="RP-H1">Glad to have you here.</div>
|
||
<div class="RP-SUB">Already 1,500 letters from many decades are in the archive. Your account helps read them, organise them, and keep them safe for the next generations.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Spanish -->
|
||
<div class="sb">
|
||
<span class="state-label st-active">Spanish (ES)</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#f0efe9;padding:22px 18px">
|
||
<div class="RP-ABOVE" style="margin-bottom:0">
|
||
<div class="RP-EYEBROW">Un proyecto familiar</div>
|
||
<div class="RP-H1">Qué bien tenerte aquí.</div>
|
||
<div class="RP-SUB">Ya hay 1.500 cartas de muchas décadas en el archivo. Con tu cuenta ayudas a leerlas, organizarlas y conservarlas para las próximas generaciones.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Above-card<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Above-card wrapper</td><td><code>text-center mb-9</code></td><td>36px bottom margin</td><td>—</td></tr>
|
||
<tr><td>Eyebrow label</td><td><code>inline-block font-sans text-[11px] font-bold tracking-[.22em] uppercase text-ink-2 border-t border-b border-[#d4d2c5] px-3.5 py-1.5 mb-[18px]</code></td><td>11px / padding 6px 14px</td><td>i18n key: <code>m.register_eyebrow()</code></td></tr>
|
||
<tr><td>Headline</td><td><code>font-serif font-normal text-[46px] leading-[1.12] tracking-[-0.005em] text-ink mb-4</code></td><td>46px</td><td>i18n key: <code>m.register_headline()</code>. Tinos (serif), not bold — weight 400.</td></tr>
|
||
<tr><td>Subtext</td><td><code>font-serif text-lg leading-relaxed text-ink-2 max-w-[540px] mx-auto</code></td><td>18px, max-width 540px</td><td>i18n key: <code>m.register_sub()</code>. <code>text-wrap: pretty</code> via inline style for better line breaks.</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 4 — FORM: ÜBER DICH (NAME FIELDS)
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">4</span>
|
||
Form section — Über dich (name fields)
|
||
</div>
|
||
|
||
<div class="sg sg-4" style="gap:16px;align-items:start">
|
||
|
||
<!-- Empty / default -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">Empty</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div class="RP-2COL">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Frieda" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Lehmann" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Focused -->
|
||
<div class="sb">
|
||
<span class="state-label st-focus">Focused — Vorname</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div class="RP-2COL">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT focused" placeholder="z.B. Frieda" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Lehmann" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Focus: navy border + soft navy glow ring.</div>
|
||
</div>
|
||
|
||
<!-- Filled -->
|
||
<div class="sb">
|
||
<span class="state-label st-active">Filled</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div class="RP-2COL">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT" value="Frieda" readonly style="color:#012851">
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT" value="Lehmann" readonly style="color:#012851">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Error (submitted empty) -->
|
||
<div class="sb">
|
||
<span class="state-label st-error">Error — submitted empty</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div class="RP-2COL">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT invalid" placeholder="z.B. Frieda" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT invalid" placeholder="z.B. Lehmann" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Red border only — no text error for empty name fields (border color communicates invalid state on submit).</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Name fields<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Section caption wrapper</td><td><code>flex items-center gap-3 mb-1</code></td><td>—</td><td>Gap 12px between text and rule line</td></tr>
|
||
<tr><td>Section caption text</td><td><code>font-sans text-[11px] font-bold tracking-[.18em] uppercase text-ink whitespace-nowrap</code></td><td>11px</td><td>i18n key: <code>m.register_section_about()</code></td></tr>
|
||
<tr><td>Section caption rule</td><td><code>flex-1 h-px bg-line</code></td><td>1px</td><td>—</td></tr>
|
||
<tr><td>2-column name grid</td><td><code>grid grid-cols-2 gap-4 mt-4</code></td><td>16px gap, 16px top margin</td><td>On mobile (<480px): <code>grid-cols-1</code></td></tr>
|
||
<tr><td>Field wrapper</td><td><code>flex flex-col</code></td><td>—</td><td>—</td></tr>
|
||
<tr><td>Field label</td><td><code>block font-sans text-xs font-bold tracking-[.1em] uppercase text-ink-2 mb-2</code></td><td>12px, 8px bottom margin</td><td>i18n keys: <code>m.register_first_name()</code>, <code>m.register_last_name()</code></td></tr>
|
||
<tr><td>Text input — default</td><td><code>w-full border border-line bg-surface font-serif text-[17px] text-ink placeholder:text-ink-3 px-4 py-[14px] rounded-none outline-none focus-visible:ring-2 focus-visible:ring-focus-ring/15 focus-visible:border-ink transition-colors</code></td><td>17px font / 48px tall (14+14+17+1px borders)</td><td>No border-radius (Tailwind's rounded-none keeps it at 2px via border-radius:2px from global reset — or use <code>rounded-sm</code>)</td></tr>
|
||
<tr><td>Text input — invalid</td><td>+ <code>border-danger focus-visible:ring-danger/20</code></td><td>—</td><td>Applied when <code>submitted && !field.trim()</code></td></tr>
|
||
<tr><td>Placeholders</td><td><code>placeholder:text-ink-3</code></td><td>—</td><td>DE: "z.B. Frieda" / "z.B. Lehmann" · EN: "e.g. Frieda" / "e.g. Lehmann"</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 5 — FORM: KONTO (EMAIL + PASSWORDS)
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">5</span>
|
||
Form section — Konto (e-mail & passwords)
|
||
</div>
|
||
|
||
<div class="sg sg-3" style="gap:16px;align-items:start">
|
||
|
||
<!-- Default -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">Default — empty</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:9px;margin-top:9px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">E-Mail-Adresse</div>
|
||
<input class="RP-INPUT" placeholder="name@beispiel.de" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" placeholder="••••••••" readonly>
|
||
<button class="RP-SHOWBTN">Anzeigen</button>
|
||
</div>
|
||
<div class="RP-HINT">Mindestens 8 Zeichen.</div>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort bestätigen</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" placeholder="••••••••" readonly>
|
||
<button class="RP-SHOWBTN">Anzeigen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Password valid + match -->
|
||
<div class="sb">
|
||
<span class="state-label st-active">Password valid + passwords match</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:9px;margin-top:9px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">E-Mail-Adresse</div>
|
||
<input class="RP-INPUT" value="frieda@beispiel.de" readonly style="color:#012851">
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" value="••••••••" readonly style="color:#012851">
|
||
<button class="RP-SHOWBTN">Verbergen</button>
|
||
</div>
|
||
<div class="RP-HINT ok"><span class="RP-HINT-DOT"></span>Mindestens 8 Zeichen. ✓</div>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort bestätigen</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" value="••••••••" readonly style="color:#012851">
|
||
<button class="RP-SHOWBTN">Verbergen</button>
|
||
</div>
|
||
<div class="RP-HINT ok"><span class="RP-HINT-DOT"></span>Passwörter stimmen überein.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Green dot + green text when passwords match. Show button label changes to "Verbergen".</div>
|
||
</div>
|
||
|
||
<!-- Password mismatch error -->
|
||
<div class="sb">
|
||
<span class="state-label st-error">Password mismatch</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:9px;margin-top:9px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">E-Mail-Adresse</div>
|
||
<input class="RP-INPUT" value="frieda@beispiel.de" readonly style="color:#012851">
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" value="••••••••" readonly style="color:#012851">
|
||
<button class="RP-SHOWBTN">Anzeigen</button>
|
||
</div>
|
||
<div class="RP-HINT ok"><span class="RP-HINT-DOT"></span>Mindestens 8 Zeichen. ✓</div>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort bestätigen</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT invalid" value="•••••••" readonly style="color:#012851">
|
||
<button class="RP-SHOWBTN">Anzeigen</button>
|
||
</div>
|
||
<div class="RP-HINT err">Die beiden Passwörter stimmen noch nicht überein.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Red border on confirm field + red error text. No dot icon for errors.</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Konto fields<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Email input</td><td><code>w-full border border-line bg-surface font-serif text-[17px] text-ink placeholder:text-ink-3 px-4 py-[14px] rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-focus-ring/15 focus-visible:border-ink transition-colors</code></td><td>17px font / 48px tall</td><td>type="email", autocomplete="email"</td></tr>
|
||
<tr><td>Password input wrapper</td><td><code>relative</code></td><td>—</td><td>Contains input + show/hide button</td></tr>
|
||
<tr><td>Password input</td><td>Same as email input + <code>pr-[92px]</code></td><td>92px right padding to clear show/hide btn</td><td>type="password" toggles to type="text" on show</td></tr>
|
||
<tr><td>Show/hide button</td><td><code>absolute right-0 inset-y-0 px-3.5 font-sans text-[11px] font-bold tracking-[.1em] uppercase text-ink-2 border-l border-line hover:text-ink transition-colors</code></td><td>11px font</td><td>type="button" (prevent form submit). Label: <code>m.password_show()</code> / <code>m.password_hide()</code></td></tr>
|
||
<tr><td>Hint text — neutral</td><td><code>font-sans text-[13px] text-ink-3 mt-1.5</code></td><td>13px, 6px top margin</td><td>i18n key: <code>m.register_pw_hint()</code> ("Mindestens 8 Zeichen.")</td></tr>
|
||
<tr><td>Hint text — success (match)</td><td><code>font-sans text-[13px] text-[#0a6b56] mt-1.5 flex items-center gap-1.5</code></td><td>—</td><td>Shown when <code>pw.length > 0 && pw === pw2</code>. Dot: <code>w-2 h-2 rounded-full bg-turquoise inline-block</code></td></tr>
|
||
<tr><td>Hint text — error (mismatch)</td><td><code>font-sans text-[13px] text-danger mt-1.5</code></td><td>—</td><td>Shown when <code>pw2.length > 0 && pw !== pw2</code>. i18n key: <code>m.register_pw_match_no()</code></td></tr>
|
||
<tr><td>Validation logic</td><td>—</td><td>—</td><td>pwValid: length ≥ 8. pwMatch: pw === pw2 && pw.length > 0. pwMismatch: pw2.length > 0 && pw !== pw2. Errors only shown after first submit attempt or onBlur.</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 6 — NOTIFICATION PREFERENCE CARD
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">6</span>
|
||
Notification preference — CheckOption card
|
||
</div>
|
||
|
||
<div class="sg sg-2" style="gap:20px;align-items:start;max-width:720px">
|
||
|
||
<!-- Unchecked -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">Unchecked</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Benachrichtigungen</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="margin-top:9px">
|
||
<div class="RP-CHECK">
|
||
<div class="RP-CHECKBOX"></div>
|
||
<div class="RP-CHECK-BODY">
|
||
<div class="RP-CHECK-TITLE">Benachrichtige mich,</div>
|
||
<div class="RP-CHECK-DESC">wenn jemand mich in einem Kommentar erwähnt oder mir auf einen Kommentar antwortet.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Border: sand (#e4e2d7). Background: white. Checkbox: gray border, white fill.</div>
|
||
</div>
|
||
|
||
<!-- Checked (default) -->
|
||
<div class="sb">
|
||
<span class="state-label st-checked">Checked (default state)</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Benachrichtigungen</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="margin-top:9px">
|
||
<div class="RP-CHECK checked">
|
||
<div class="RP-CHECKBOX checked">
|
||
<svg class="RP-CHECKMARK" viewBox="0 0 16 16" fill="none">
|
||
<path d="M3 8.5l3.2 3.2L13 5" stroke="#fff" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<div class="RP-CHECK-BODY">
|
||
<div class="RP-CHECK-TITLE">Benachrichtige mich,</div>
|
||
<div class="RP-CHECK-DESC">wenn jemand mich in einem Kommentar erwähnt oder mir auf einen Kommentar antwortet.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Default state: checked (opt-in by default). Border: navy. Background: mint tint (rgba(161,220,216,.18)).</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — CheckOption card<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes / values</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Card wrapper (label)</td><td><code><label></code> with <code>flex gap-3.5 p-3.5 border rounded-sm cursor-pointer transition-colors items-start</code></td><td>14px gap, 14px 16px padding</td><td>The entire card is a <code><label></code> for large click target. Wrap real <code><input type="checkbox"></code> visually hidden inside.</td></tr>
|
||
<tr><td>Card — unchecked</td><td><code>border-line bg-surface</code></td><td>—</td><td>—</td></tr>
|
||
<tr><td>Card — checked</td><td><code>border-ink bg-[rgba(161,220,216,0.18)]</code></td><td>—</td><td>Mint tint bg. No Tailwind utility exists — use inline style or arbitrary value.</td></tr>
|
||
<tr><td>Real checkbox input</td><td><code>sr-only</code> (visually hidden, accessible)</td><td>—</td><td>Bind <code>checked</code> state to this. Default: <code>true</code>.</td></tr>
|
||
<tr><td>Custom checkbox box</td><td><code>flex-shrink-0 w-[22px] h-[22px] rounded-sm border-2 flex items-center justify-center mt-px transition-all</code></td><td>22×22px</td><td>Min touch target met by the card itself.</td></tr>
|
||
<tr><td>Checkbox — unchecked</td><td><code>border-gray-400 bg-surface</code></td><td>—</td><td>—</td></tr>
|
||
<tr><td>Checkbox — checked</td><td><code>border-ink bg-ink</code></td><td>—</td><td>White checkmark SVG inside.</td></tr>
|
||
<tr><td>Checkmark SVG</td><td>14×14px, path: M3 8.5l3.2 3.2L13 5, stroke white 2.4, round caps</td><td>14px icon</td><td>Only rendered when checked.</td></tr>
|
||
<tr><td>Option title</td><td><code>font-sans text-sm font-semibold text-ink</code></td><td>14px</td><td>i18n key: <code>m.register_notify_label()</code></td></tr>
|
||
<tr><td>Option description</td><td><code>font-serif text-[15px] leading-normal text-ink-2 mt-0.5</code></td><td>15px</td><td>i18n key: <code>m.register_notify_desc()</code></td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 7 — SUBMIT BUTTON + FOOTER LINKS
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">7</span>
|
||
Submit button & footer links
|
||
</div>
|
||
|
||
<div class="sg sg-3" style="gap:16px;align-items:start">
|
||
|
||
<!-- Default -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">Default</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<button class="RP-SUBMIT">
|
||
Konto erstellen
|
||
<svg viewBox="0 0 24 24" width="9" height="9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||
</button>
|
||
<div class="RP-FOOTER">
|
||
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
|
||
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Hover -->
|
||
<div class="sb">
|
||
<span class="state-label st-hover">Submit — hover</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<button class="RP-SUBMIT hover">
|
||
Konto erstellen
|
||
<svg viewBox="0 0 24 24" width="9" height="9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
|
||
</button>
|
||
<div class="RP-FOOTER">
|
||
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
|
||
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Bg darkens: #012851 → #01386f. No scale transform.</div>
|
||
</div>
|
||
|
||
<!-- Loading -->
|
||
<div class="sb">
|
||
<span class="state-label st-loading">Loading (after submit)</span>
|
||
<div class="wf">
|
||
<div class="wf-inner" style="background:#fff;padding:16px">
|
||
<button class="RP-SUBMIT loading">
|
||
<div class="RP-SPINNER"></div>
|
||
Wird erstellt…
|
||
</button>
|
||
<div class="RP-FOOTER">
|
||
<div class="RP-PRIVACY">Mit dem Erstellen eines Kontos stimmst du der <a href="#">Datenschutzerklärung</a> und den <a href="#">Nutzungsbedingungen</a> zu.</div>
|
||
<div class="RP-SIGNIN">Du hast bereits ein Konto? <a href="#">Anmelden</a></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Arrow icon replaced by spinner. Button opacity 0.85. cursor: wait. 850ms simulated delay then success.</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Submit & footer<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Submit button</td><td><code>w-full bg-primary text-primary-fg font-sans text-[13px] font-bold tracking-[.12em] uppercase py-4 px-6 rounded-sm flex items-center justify-center gap-2.5 transition-colors hover:bg-[#01386f] mt-8</code></td><td>13px font / 16px top+bottom padding / ~53px tall</td><td>Arrow icon: 16×16px SVG. Loading state: add <code>opacity-85 cursor-wait</code>, swap icon for spinner.</td></tr>
|
||
<tr><td>Loading spinner</td><td><code>w-4 h-4 rounded-full border-2 border-white/30 border-t-white animate-spin</code></td><td>16px</td><td>Replaces arrow icon during loading state</td></tr>
|
||
<tr><td>Footer wrapper</td><td><code>mt-6 flex flex-col gap-3.5 text-center</code></td><td>24px top margin, 14px gap</td><td>—</td></tr>
|
||
<tr><td>Privacy text</td><td><code>font-serif text-sm text-ink-3 leading-relaxed</code></td><td>14px</td><td>Links within: <code>text-ink underline decoration-accent underline-offset-[3px] decoration-2</code></td></tr>
|
||
<tr><td>Sign-in prompt</td><td><code>font-sans text-[13px] text-ink-2</code></td><td>13px</td><td>i18n: <code>m.register_have_account()</code> + <code>m.register_sign_in()</code></td></tr>
|
||
<tr><td>Sign-in link</td><td><code>font-sans text-xs font-bold tracking-[.1em] uppercase text-ink underline decoration-accent underline-offset-[4px] decoration-2</code></td><td>12px</td><td>href="/login"</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 8 — SUCCESS PANEL
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">8</span>
|
||
Success panel — post-submit state
|
||
</div>
|
||
|
||
<div class="sg sg-2" style="gap:20px;align-items:start;max-width:720px">
|
||
|
||
<!-- Success — German -->
|
||
<div class="sb">
|
||
<span class="state-label st-success">Success — German</span>
|
||
<div class="wf" style="max-width:380px">
|
||
<div class="wf-inner">
|
||
<div class="wf-bar">
|
||
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
|
||
<div class="urlbar"><span>localhost:3000/register</span></div>
|
||
</div>
|
||
<div class="RP">
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV">
|
||
<span class="RP-WORDMARK">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN on">DE</button>
|
||
<button class="RP-LANG-BTN">EN</button>
|
||
<button class="RP-LANG-BTN">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="RP-MAIN">
|
||
<div class="RP-INNER">
|
||
<div class="RP-CARD">
|
||
<div class="RP-SUCCESS">
|
||
<div class="RP-SUCCESS-CIRCLE">
|
||
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="#012851" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>
|
||
</div>
|
||
<div class="RP-SUCCESS-H">Willkommen im Familienarchiv.</div>
|
||
<div class="RP-SUCCESS-B">Wir haben dir eine E-Mail an frieda@beispiel.de geschickt, um deine Adresse zu bestätigen. Anschließend kannst du dich anmelden.</div>
|
||
<div class="RP-SUCCESS-ACTIONS">
|
||
<button class="RP-SUBMIT" style="margin-top:0">Zur Anmeldung</button>
|
||
<button class="RP-SUCCESS-RESEND">E-Mail erneut senden</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Success — English -->
|
||
<div class="sb">
|
||
<span class="state-label st-success">Success — English</span>
|
||
<div class="wf" style="max-width:380px">
|
||
<div class="wf-inner">
|
||
<div class="wf-bar">
|
||
<div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
|
||
<div class="urlbar"><span>localhost:3000/register?lang=en</span></div>
|
||
</div>
|
||
<div class="RP">
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV">
|
||
<span class="RP-WORDMARK">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN">DE</button>
|
||
<button class="RP-LANG-BTN on">EN</button>
|
||
<button class="RP-LANG-BTN">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="RP-MAIN">
|
||
<div class="RP-INNER">
|
||
<div class="RP-CARD">
|
||
<div class="RP-SUCCESS">
|
||
<div class="RP-SUCCESS-CIRCLE">
|
||
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="#012851" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>
|
||
</div>
|
||
<div class="RP-SUCCESS-H">Welcome to Familienarchiv.</div>
|
||
<div class="RP-SUCCESS-B">We sent an email to frieda@example.com to confirm your address. After that, you can sign in.</div>
|
||
<div class="RP-SUCCESS-ACTIONS">
|
||
<button class="RP-SUBMIT" style="margin-top:0">Go to sign-in</button>
|
||
<button class="RP-SUCCESS-RESEND">Resend email</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Success panel<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Element</th><th>Tailwind classes</th><th>Real px</th><th>Notes</th></tr>
|
||
<tr><td>Success wrapper</td><td><code>text-center py-2</code></td><td>8px top/bottom padding</td><td>Replaces the entire <code><form></code> block. Same card container as the form.</td></tr>
|
||
<tr><td>Checkmark circle</td><td><code>w-[72px] h-[72px] rounded-full bg-[rgba(161,220,216,0.35)] inline-flex items-center justify-center mb-6</code></td><td>72px</td><td>Checkmark SVG: 34×34px, stroke #012851 2.2px.</td></tr>
|
||
<tr><td>Success headline</td><td><code>font-serif font-normal text-[30px] leading-tight text-ink mb-3.5</code></td><td>30px</td><td>i18n key: <code>m.register_success_h()</code></td></tr>
|
||
<tr><td>Success body</td><td><code>font-serif text-[17px] leading-[1.55] text-ink-2 max-w-[420px] mx-auto mb-7</code></td><td>17px, max 420px</td><td>i18n key: <code>m.register_success_b()</code>. Replace <code>{email}</code> token with entered email.</td></tr>
|
||
<tr><td>CTA button</td><td>Same as submit button — full-width navy</td><td>—</td><td>href="/login". i18n: <code>m.register_success_action()</code></td></tr>
|
||
<tr><td>Resend link</td><td><code>font-sans text-xs font-bold tracking-[.1em] uppercase text-ink-2 px-2.5 py-2.5 underline decoration-accent underline-offset-[4px] decoration-2</code></td><td>12px</td><td>i18n: <code>m.register_success_resend()</code>. Calls <code>/api/auth/resend-verification</code>. Container: <code>max-w-[320px] mx-auto flex flex-col gap-2.5</code></td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 9 — MOBILE LAYOUT (320px)
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">9</span>
|
||
Mobile layout — 320px
|
||
</div>
|
||
|
||
<div class="sg sg-3" style="gap:20px;align-items:start">
|
||
|
||
<!-- Mobile default -->
|
||
<div class="sb">
|
||
<span class="state-label st-default">320px — form at rest</span>
|
||
<div class="WF-M">
|
||
<div class="WF-M-STATUS">
|
||
<span class="WF-M-TIME">9:41</span>
|
||
<div class="WF-M-ICONS">
|
||
<div class="WF-M-ICON"></div>
|
||
<div class="WF-M-ICON"></div>
|
||
<div class="WF-M-ICON"></div>
|
||
</div>
|
||
</div>
|
||
<div class="RP" style="min-height:480px">
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV" style="padding:0 10px">
|
||
<span class="RP-WORDMARK" style="font-size:8px">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN on" style="font-size:5px;padding:2px 4px">DE</button>
|
||
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">EN</button>
|
||
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="padding:14px 10px;background:#f0efe9">
|
||
<!-- Above-card collapsed -->
|
||
<div style="text-align:center;margin-bottom:11px">
|
||
<div class="RP-EYEBROW" style="font-size:5px;padding:2px 6px;margin-bottom:6px">Ein Familienprojekt</div>
|
||
<div class="RP-H1" style="font-size:15px;margin-bottom:5px">Schön, dass du<br>da bist.</div>
|
||
<div class="RP-SUB" style="font-size:7.5px;max-width:none">Bereits 1.500 Briefe sind im Archiv. Mit deinem Konto hilfst du mit, sie zu bewahren.</div>
|
||
</div>
|
||
<!-- Form card -->
|
||
<div class="RP-CARD" style="padding:13px">
|
||
<div style="margin-bottom:9px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<!-- Single column on mobile -->
|
||
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Frieda" style="font-size:8px;padding:5px 7px" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT" placeholder="z.B. Lehmann" style="font-size:8px;padding:5px 7px" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-bottom:9px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">E-Mail</div>
|
||
<input class="RP-INPUT" placeholder="name@beispiel.de" style="font-size:8px;padding:5px 7px" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" placeholder="••••••••" style="font-size:8px;padding:5px 7px" readonly>
|
||
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
|
||
</div>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort best.</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" placeholder="••••••••" style="font-size:8px;padding:5px 7px" readonly>
|
||
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button class="RP-SUBMIT" style="font-size:6px;padding:7px;margin-top:10px">Konto erstellen →</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sc">Name grid collapses to single column. Subtext shortened. Card padding reduced. All inputs remain 17px on real device (browser zooms accordingly).</div>
|
||
</div>
|
||
|
||
<!-- Mobile — filled + near submit -->
|
||
<div class="sb">
|
||
<span class="state-label st-active">320px — filled, ready to submit</span>
|
||
<div class="WF-M">
|
||
<div class="WF-M-STATUS">
|
||
<span class="WF-M-TIME">9:41</span>
|
||
<div class="WF-M-ICONS">
|
||
<div class="WF-M-ICON"></div>
|
||
<div class="WF-M-ICON"></div>
|
||
<div class="WF-M-ICON"></div>
|
||
</div>
|
||
</div>
|
||
<div class="RP" style="min-height:480px">
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV" style="padding:0 10px">
|
||
<span class="RP-WORDMARK" style="font-size:8px">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN on" style="font-size:5px;padding:2px 4px">DE</button>
|
||
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">EN</button>
|
||
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="padding:14px 10px;background:#f0efe9">
|
||
<div style="text-align:center;margin-bottom:11px">
|
||
<div class="RP-H1" style="font-size:15px;margin-bottom:5px">Schön, dass du<br>da bist.</div>
|
||
</div>
|
||
<div class="RP-CARD" style="padding:13px">
|
||
<div style="margin-bottom:9px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Über dich</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Vorname</div>
|
||
<input class="RP-INPUT" value="Frieda" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Nachname</div>
|
||
<input class="RP-INPUT" value="Lehmann" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-bottom:9px">
|
||
<div class="RP-SCAP"><span class="RP-SCAP-TEXT">Konto</span><span class="RP-SCAP-RULE"></span></div>
|
||
<div style="display:flex;flex-direction:column;gap:7px;margin-top:7px">
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">E-Mail</div>
|
||
<input class="RP-INPUT" value="frieda@b.de" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Passwort</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" value="••••••••" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
|
||
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
|
||
</div>
|
||
<div class="RP-HINT ok" style="font-size:6px"><span class="RP-HINT-DOT"></span>✓ 8 Zeichen</div>
|
||
</div>
|
||
<div class="RP-FIELD">
|
||
<div class="RP-LABEL">Best.</div>
|
||
<div class="RP-PWINPUT">
|
||
<input class="RP-INPUT" value="••••••••" style="font-size:8px;padding:5px 7px;color:#012851" readonly>
|
||
<button class="RP-SHOWBTN" style="font-size:5px;padding:0 5px">Zeig.</button>
|
||
</div>
|
||
<div class="RP-HINT ok" style="font-size:6px"><span class="RP-HINT-DOT"></span>Stimmen überein.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button class="RP-SUBMIT" style="font-size:6px;padding:7px;margin-top:10px">Konto erstellen →</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Mobile success -->
|
||
<div class="sb">
|
||
<span class="state-label st-success">320px — success panel</span>
|
||
<div class="WF-M">
|
||
<div class="WF-M-STATUS">
|
||
<span class="WF-M-TIME">9:41</span>
|
||
<div class="WF-M-ICONS">
|
||
<div class="WF-M-ICON"></div>
|
||
<div class="WF-M-ICON"></div>
|
||
<div class="WF-M-ICON"></div>
|
||
</div>
|
||
</div>
|
||
<div class="RP" style="min-height:340px">
|
||
<div class="RP-HEADER">
|
||
<div class="RP-STRIPE"></div>
|
||
<div class="RP-NAV" style="padding:0 10px">
|
||
<span class="RP-WORDMARK" style="font-size:8px">Familienarchiv</span>
|
||
<div class="RP-LANG">
|
||
<button class="RP-LANG-BTN on" style="font-size:5px;padding:2px 4px">DE</button>
|
||
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">EN</button>
|
||
<button class="RP-LANG-BTN" style="font-size:5px;padding:2px 4px">ES</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style="padding:14px 10px;background:#f0efe9">
|
||
<div class="RP-CARD" style="padding:16px">
|
||
<div class="RP-SUCCESS" style="padding:6px 0">
|
||
<div class="RP-SUCCESS-CIRCLE" style="width:30px;height:30px;margin-bottom:8px">
|
||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#012851" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.2 4.2L19 7"/></svg>
|
||
</div>
|
||
<div class="RP-SUCCESS-H" style="font-size:12px;margin-bottom:5px">Willkommen im Familienarchiv.</div>
|
||
<div class="RP-SUCCESS-B" style="font-size:7.5px;max-width:none;margin-bottom:10px">Wir haben dir eine E-Mail an frieda@b.de geschickt, um deine Adresse zu bestätigen.</div>
|
||
<div style="max-width:none">
|
||
<button class="RP-SUBMIT" style="font-size:6px;padding:7px;margin-top:0">Zur Anmeldung</button>
|
||
<button class="RP-SUCCESS-RESEND" style="font-size:6px;padding:4px">E-Mail erneut senden</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="impl-ref">
|
||
<div class="impl-ref-hdr">Implementation Reference — Mobile breakpoints<span>routes/register/+page.svelte</span></div>
|
||
<table>
|
||
<tr><th>Breakpoint</th><th>Change</th><th>Tailwind</th><th>Notes</th></tr>
|
||
<tr><td>Default (mobile-first, <480px)</td><td>Single-column name grid</td><td><code>grid grid-cols-1 gap-4</code></td><td>Start mobile-first; add <code>sm:grid-cols-2</code> at 480px+</td></tr>
|
||
<tr><td>sm: 480px+</td><td>2-column name grid</td><td><code>sm:grid-cols-2</code></td><td>Custom breakpoint or use <code>min-[480px]:grid-cols-2</code></td></tr>
|
||
<tr><td>Default (mobile-first)</td><td>Card padding 24px</td><td><code>p-6</code></td><td>Reduced from desktop 40px</td></tr>
|
||
<tr><td>sm: 640px+</td><td>Card padding 40px</td><td><code>sm:p-10</code></td><td>Full desktop padding</td></tr>
|
||
<tr><td>Default (mobile-first)</td><td>Main padding 32px top</td><td><code>pt-8</code></td><td>Reduced from desktop 64px</td></tr>
|
||
<tr><td>sm: 640px+</td><td>Main padding 64px top</td><td><code>sm:pt-16</code></td><td>—</td></tr>
|
||
<tr><td>All breakpoints</td><td>Input font size 17px</td><td><code>text-[17px]</code></td><td>Do NOT reduce on mobile — large text is critical for the 60+ audience. The browser will zoom the viewport to show text clearly.</td></tr>
|
||
<tr><td>All breakpoints</td><td>Touch targets ≥ 44px</td><td>Button: <code>py-4</code> = 16px × 2 + 13px font = 45px ✓</td><td>CheckOption card: entire card is the touch target (~52px+ tall) ✓</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- ══════════════════════════════════════
|
||
SECTION 10 — IMPLEMENTATION NOTES
|
||
══════════════════════════════════════ -->
|
||
<div class="sec">
|
||
<div class="sec-h">
|
||
<span class="sec-num">10</span>
|
||
Implementation notes — i18n, accessibility, backend
|
||
</div>
|
||
|
||
<div class="sg sg-3" style="gap:20px;align-items:start">
|
||
|
||
<!-- i18n keys -->
|
||
<div class="sb">
|
||
<div class="sl">i18n keys (Paraglide)</div>
|
||
<div class="impl-ref" style="margin-top:0">
|
||
<div class="impl-ref-hdr">New keys — messages/de.json + en.json + es.json</div>
|
||
<table>
|
||
<tr><th>Key</th><th>DE</th></tr>
|
||
<tr><td><code>register_eyebrow</code></td><td>Ein Familienprojekt</td></tr>
|
||
<tr><td><code>register_headline</code></td><td>Schön, dass du da bist.</td></tr>
|
||
<tr><td><code>register_sub</code></td><td>Bereits 1.500 Briefe aus vielen Jahrzehnten…</td></tr>
|
||
<tr><td><code>register_section_about</code></td><td>Über dich</td></tr>
|
||
<tr><td><code>register_section_account</code></td><td>Konto</td></tr>
|
||
<tr><td><code>register_section_prefs</code></td><td>Benachrichtigungen</td></tr>
|
||
<tr><td><code>register_first_name</code></td><td>Vorname</td></tr>
|
||
<tr><td><code>register_last_name</code></td><td>Nachname</td></tr>
|
||
<tr><td><code>register_email</code></td><td>E-Mail-Adresse</td></tr>
|
||
<tr><td><code>register_password</code></td><td>Passwort</td></tr>
|
||
<tr><td><code>register_password_confirm</code></td><td>Passwort bestätigen</td></tr>
|
||
<tr><td><code>register_pw_hint</code></td><td>Mindestens 8 Zeichen.</td></tr>
|
||
<tr><td><code>register_pw_match_ok</code></td><td>Passwörter stimmen überein.</td></tr>
|
||
<tr><td><code>register_pw_match_no</code></td><td>Die beiden Passwörter stimmen noch nicht überein.</td></tr>
|
||
<tr><td><code>register_notify_label</code></td><td>Benachrichtige mich,</td></tr>
|
||
<tr><td><code>register_notify_desc</code></td><td>wenn jemand mich in einem Kommentar erwähnt…</td></tr>
|
||
<tr><td><code>register_submit</code></td><td>Konto erstellen</td></tr>
|
||
<tr><td><code>register_submit_loading</code></td><td>Wird erstellt…</td></tr>
|
||
<tr><td><code>register_have_account</code></td><td>Du hast bereits ein Konto?</td></tr>
|
||
<tr><td><code>register_sign_in</code></td><td>Anmelden</td></tr>
|
||
<tr><td><code>register_success_h</code></td><td>Willkommen im Familienarchiv.</td></tr>
|
||
<tr><td><code>register_success_b</code></td><td>Wir haben dir eine E-Mail an {email} geschickt…</td></tr>
|
||
<tr><td><code>register_success_action</code></td><td>Zur Anmeldung</td></tr>
|
||
<tr><td><code>register_success_resend</code></td><td>E-Mail erneut senden</td></tr>
|
||
<tr><td><code>register_privacy</code></td><td>Mit dem Erstellen eines Kontos stimmst du der</td></tr>
|
||
<tr><td><code>register_privacy_link</code></td><td>Datenschutzerklärung</td></tr>
|
||
<tr><td><code>register_terms_link</code></td><td>Nutzungsbedingungen</td></tr>
|
||
<tr><td><code>password_show</code></td><td>Anzeigen</td></tr>
|
||
<tr><td><code>password_hide</code></td><td>Verbergen</td></tr>
|
||
<tr><td><code>page_title_register</code></td><td>Registrieren — Familienarchiv</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Accessibility -->
|
||
<div class="sb">
|
||
<div class="sl">Accessibility requirements</div>
|
||
<div class="impl-ref" style="margin-top:0">
|
||
<div class="impl-ref-hdr">WCAG 2.2 checklist</div>
|
||
<table>
|
||
<tr><th>Criterion</th><th>Implementation</th><th>Level</th></tr>
|
||
<tr><td>1.4.3 Contrast (text)</td><td>Navy #012851 on white: 14.5:1 ✓. Gray #4b5563 on white: 7.6:1 ✓. Gray #6b7280 on white: 5.9:1 ✓ (AA only — use for large text / hints)</td><td>AA</td></tr>
|
||
<tr><td>2.4.2 Page title</td><td><code><svelte:head><title>{m.page_title_register()}</title></code></td><td>A</td></tr>
|
||
<tr><td>1.3.1 Form labels</td><td>Every input has <code><label for="..."></code>. No placeholder-as-label.</td><td>A</td></tr>
|
||
<tr><td>2.4.3 Focus order</td><td>Tab order: DE/EN/ES → Vorname → Nachname → Email → Password → Confirm → Checkbox → Submit</td><td>A</td></tr>
|
||
<tr><td>2.4.7 Focus visible</td><td><code>focus-visible:ring-2 focus-visible:ring-focus-ring/15</code> on all inputs. <code>focus-visible:ring-2 focus-visible:ring-white/80</code> on submit button.</td><td>AA</td></tr>
|
||
<tr><td>2.5.3 Touch target (2.2)</td><td>All buttons ≥ 44px tall. CheckOption card ≥ 44px. Show/hide button: 44px tall (same as input height).</td><td>AA</td></tr>
|
||
<tr><td>4.1.3 Status messages</td><td>Password match/mismatch feedback announced via <code>aria-live="polite"</code> region.</td><td>AA</td></tr>
|
||
<tr><td>1.4.4 Resize text</td><td>All sizes in rem/px that scale with browser zoom. 17px input font = 1.0625rem.</td><td>AA</td></tr>
|
||
<tr><td>Error identification</td><td><code>aria-invalid="true"</code> on invalid inputs. <code>aria-describedby</code> linking to error message element.</td><td>A</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Backend wiring -->
|
||
<div class="sb">
|
||
<div class="sl">Backend wiring</div>
|
||
<div class="impl-ref" style="margin-top:0">
|
||
<div class="impl-ref-hdr">Route + API integration</div>
|
||
<table>
|
||
<tr><th>Concern</th><th>Details</th></tr>
|
||
<tr><td>Route</td><td><code>frontend/src/routes/register/+page.svelte</code> + <code>+page.server.ts</code></td></tr>
|
||
<tr><td>Form action</td><td>POST <code>?/register</code> → <code>+page.server.ts</code> action</td></tr>
|
||
<tr><td>API endpoint</td><td><code>POST /api/auth/register</code> — create new AppUser (first name, last name, email, password, notifyOnMention)</td></tr>
|
||
<tr><td>On success</td><td>Backend sends verification email. Frontend shows success panel with entered email.</td></tr>
|
||
<tr><td>Resend endpoint</td><td><code>POST /api/auth/resend-verification</code> — body: <code>{ email }</code></td></tr>
|
||
<tr><td>Client-side validation</td><td>Run before server submit: required fields, email format, password ≥ 8 chars, password match. Show errors inline on first submit attempt.</td></tr>
|
||
<tr><td>Server-side errors</td><td>Map backend error codes via <code>getErrorMessage(code)</code>: EMAIL_ALREADY_EXISTS → Paraglide key. Never display raw backend messages.</td></tr>
|
||
<tr><td>notifyOnMention field</td><td>CheckOption checked state maps to <code>notifyOnMention: boolean</code> in request body. Default: <code>true</code>.</td></tr>
|
||
<tr><td>Language preference</td><td>The active language is a client-side Paraglide state only (page.svelte <code>$state</code>). Not persisted until the user creates an account; can be added to the registration payload as <code>preferredLanguage: 'de' | 'en' | 'es'</code> if the backend supports it.</td></tr>
|
||
<tr><td>Auth header</td><td>Register page is unauthenticated. Use a minimal header (AuthHeader without nav links) or a dedicated RegisterHeader.</td></tr>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="ann-info" style="margin-top:16px">
|
||
<strong>Implementation sequence:</strong>
|
||
<ul>
|
||
<li>1. Add <code>POST /api/auth/register</code> to Spring Boot backend (new controller + service method)</li>
|
||
<li>2. Add <code>POST /api/auth/resend-verification</code></li>
|
||
<li>3. Regenerate TypeScript API types (<code>npm run generate:api</code>)</li>
|
||
<li>4. Implement <code>frontend/src/routes/register/+page.svelte</code></li>
|
||
<li>5. Add i18n keys to all three message files</li>
|
||
<li>6. Add route to navigation (unauthenticated footer or login page link)</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /.doc -->
|
||
</body>
|
||
</html>
|