design: increase entrepreneur onboarding textarea to 200px and add reassuring subheader text, resolve unused label import error

This commit is contained in:
2026-06-08 10:52:13 -07:00
parent 0d8e982f81
commit a6d0688c94

View File

@@ -1,5 +1,20 @@
import React, { useState, useEffect, useRef, useMemo, useCallback } from "react"; import React, {
import { WizardTop, WizardBody, WizardQ, WizardFooter, Label, LANE_LABELS, ChipGroup, PresetGroup, Field } from "./onboarding-primitives"; useState,
useEffect,
useRef,
useMemo,
useCallback,
} from "react";
import {
WizardTop,
WizardBody,
WizardQ,
WizardFooter,
LANE_LABELS,
ChipGroup,
PresetGroup,
Field,
} from "./onboarding-primitives";
// Entrepreneur path — 4 steps. Each step is a focused question. // Entrepreneur path — 4 steps. Each step is a focused question.
const ENTREP_TOTAL = 4; const ENTREP_TOTAL = 4;
@@ -28,7 +43,10 @@ export function EntrepIdea({ value, onChange }) {
else setTimeout(() => setDeleting(true), 1500); else setTimeout(() => setDeleting(true), 1500);
} else { } else {
if (phChars > 0) setPhChars(phChars - 1); if (phChars > 0) setPhChars(phChars - 1);
else { setDeleting(false); setPhIdx((phIdx + 1) % IDEA_PROMPTS.length); } else {
setDeleting(false);
setPhIdx((phIdx + 1) % IDEA_PROMPTS.length);
}
} }
}, speed); }, speed);
return () => clearTimeout(t); return () => clearTimeout(t);
@@ -38,12 +56,12 @@ export function EntrepIdea({ value, onChange }) {
<> <>
<WizardQ <WizardQ
title="What are you building?" title="What are you building?"
sub="One paragraph is enough. Talk like you would to a friend." sub="Don't worry if it's not crisp yet — just dump your thoughts. Talk like you would to a friend."
/> />
<div style={{ position: "relative" }}> <div style={{ position: "relative" }}>
<textarea <textarea
className="wiz-input" className="wiz-input"
style={{ minHeight: 140, fontSize: 15 }} style={{ minHeight: 200, fontSize: 15 }}
value={value} value={value}
onChange={(e) => onChange(e.target.value)} onChange={(e) => onChange(e.target.value)}
autoFocus autoFocus
@@ -52,7 +70,10 @@ export function EntrepIdea({ value, onChange }) {
{value.length === 0 && ( {value.length === 0 && (
<div <div
style={{ style={{
position: "absolute", top: 12, left: 14, right: 14, position: "absolute",
top: 12,
left: 14,
right: 14,
pointerEvents: "none", pointerEvents: "none",
color: "var(--fg-faint)", color: "var(--fg-faint)",
font: "14.5px/1.5 var(--font-sans)", font: "14.5px/1.5 var(--font-sans)",
@@ -62,8 +83,11 @@ export function EntrepIdea({ value, onChange }) {
<span <span
style={{ style={{
display: "inline-block", display: "inline-block",
width: 7, height: 14, verticalAlign: "-2px", width: 7,
background: "var(--accent)", marginLeft: 1, height: 14,
verticalAlign: "-2px",
background: "var(--accent)",
marginLeft: 1,
animation: "blink 1s steps(2) infinite", animation: "blink 1s steps(2) infinite",
boxShadow: "0 0 10px var(--accent-glow)", boxShadow: "0 0 10px var(--accent-glow)",
}} }}
@@ -73,7 +97,12 @@ export function EntrepIdea({ value, onChange }) {
</div> </div>
<div <div
className="mono" className="mono"
style={{ fontSize: 11, color: "var(--fg-faint)", letterSpacing: "0.06em", marginTop: -16 }} style={{
fontSize: 11,
color: "var(--fg-faint)",
letterSpacing: "0.06em",
marginTop: -16,
}}
> >
{value.length} chars · be specific where it matters {value.length} chars · be specific where it matters
</div> </div>
@@ -116,18 +145,42 @@ function EntrepAudience({ value, onChange }) {
} }
const GOALS = [ const GOALS = [
{ id: "first_customer", icon: "🎯", label: "First real customer", {
desc: "Someone I don't know pays me. Even once." }, id: "first_customer",
{ id: "ten_users", icon: "👥", label: "Ten weekly users", icon: "🎯",
desc: "A signal the thing actually does something useful." }, label: "First real customer",
{ id: "mrr_1k", icon: "📈", label: "$1k MRR", desc: "Someone I don't know pays me. Even once.",
desc: "Enough to take it seriously." }, },
{ id: "side_quit", icon: "🚪", label: "Replace my day job", {
desc: "The long road. Make this the main thing." }, id: "ten_users",
{ id: "audience", icon: "📣", label: "Build a tiny audience", icon: "👥",
desc: "200 emails, a community, something I can talk to." }, label: "Ten weekly users",
{ id: "ship_it", icon: "🚀", label: "Just ship it", desc: "A signal the thing actually does something useful.",
desc: "I want the thing to exist." }, },
{
id: "mrr_1k",
icon: "📈",
label: "$1k MRR",
desc: "Enough to take it seriously.",
},
{
id: "side_quit",
icon: "🚪",
label: "Replace my day job",
desc: "The long road. Make this the main thing.",
},
{
id: "audience",
icon: "📣",
label: "Build a tiny audience",
desc: "200 emails, a community, something I can talk to.",
},
{
id: "ship_it",
icon: "🚀",
label: "Just ship it",
desc: "I want the thing to exist.",
},
]; ];
function EntrepGoal({ value, onChange }) { function EntrepGoal({ value, onChange }) {
@@ -139,7 +192,9 @@ function EntrepGoal({ value, onChange }) {
/> />
<PresetGroup <PresetGroup
options={GOALS.map((g) => ({ options={GOALS.map((g) => ({
id: g.id, label: g.label, desc: g.desc, id: g.id,
label: g.label,
desc: g.desc,
icon: <span style={{ fontSize: 14 }}>{g.icon}</span>, icon: <span style={{ fontSize: 14 }}>{g.icon}</span>,
}))} }))}
value={value} value={value}
@@ -151,18 +206,43 @@ function EntrepGoal({ value, onChange }) {
} }
const VIBES = [ const VIBES = [
{ id: "warm", name: "Warm coral", swatch: "linear-gradient(135deg, #E27855, #B33B2A)", {
desc: "Confident, hand-built, warm." }, id: "warm",
{ id: "ink", name: "Ink & paper", swatch: "linear-gradient(135deg, #1d1d1d, #4a4a4a)", name: "Warm coral",
desc: "Editorial, serif, quiet." }, swatch: "linear-gradient(135deg, #E27855, #B33B2A)",
{ id: "sage", name: "Sage matte", swatch: "linear-gradient(135deg, #7BA890, #3F6B57)", desc: "Confident, hand-built, warm.",
desc: "Calm, modern, slightly herbal." }, },
{ id: "neon", name: "Neon arcade", swatch: "linear-gradient(135deg, #5B6CFF, #FF3DDB)", {
desc: "Loud, fun, late-night." }, id: "ink",
{ id: "cream", name: "Cream linen", swatch: "linear-gradient(135deg, #F2E7D5, #C9A977)", name: "Ink & paper",
desc: "Cozy and beige." }, swatch: "linear-gradient(135deg, #1d1d1d, #4a4a4a)",
{ id: "later", name: "Decide later", swatch: "repeating-linear-gradient(45deg, oklch(0.30 0.010 60), oklch(0.30 0.010 60) 6px, oklch(0.22 0.010 60) 6px, oklch(0.22 0.010 60) 12px)", desc: "Editorial, serif, quiet.",
desc: "Vibn picks one that fits." }, },
{
id: "sage",
name: "Sage matte",
swatch: "linear-gradient(135deg, #7BA890, #3F6B57)",
desc: "Calm, modern, slightly herbal.",
},
{
id: "neon",
name: "Neon arcade",
swatch: "linear-gradient(135deg, #5B6CFF, #FF3DDB)",
desc: "Loud, fun, late-night.",
},
{
id: "cream",
name: "Cream linen",
swatch: "linear-gradient(135deg, #F2E7D5, #C9A977)",
desc: "Cozy and beige.",
},
{
id: "later",
name: "Decide later",
swatch:
"repeating-linear-gradient(45deg, oklch(0.30 0.010 60), oklch(0.30 0.010 60) 6px, oklch(0.22 0.010 60) 6px, oklch(0.22 0.010 60) 12px)",
desc: "Vibn picks one that fits.",
},
]; ];
function EntrepVibe({ value, onChange }) { function EntrepVibe({ value, onChange }) {
@@ -190,26 +270,45 @@ function EntrepVibe({ value, onChange }) {
padding: "10px 10px 10px", padding: "10px 10px 10px",
borderRadius: 11, borderRadius: 11,
border: `1px solid ${active ? "var(--accent)" : "var(--hairline)"}`, border: `1px solid ${active ? "var(--accent)" : "var(--hairline)"}`,
background: active ? "oklch(0.20 0.04 35 / 0.4)" : "oklch(0.18 0.009 60 / 0.6)", background: active
boxShadow: active ? "0 0 0 3px oklch(0.74 0.175 35 / 0.1)" : "none", ? "oklch(0.20 0.04 35 / 0.4)"
: "oklch(0.18 0.009 60 / 0.6)",
boxShadow: active
? "0 0 0 3px oklch(0.74 0.175 35 / 0.1)"
: "none",
textAlign: "left", textAlign: "left",
color: "var(--fg)", color: "var(--fg)",
display: "flex", flexDirection: "column", gap: 8, display: "flex",
flexDirection: "column",
gap: 8,
transition: "border-color .15s, background .15s", transition: "border-color .15s, background .15s",
}} }}
> >
<span <span
style={{ style={{
height: 52, borderRadius: 7, height: 52,
borderRadius: 7,
background: v.swatch, background: v.swatch,
border: "1px solid oklch(1 0 0 / 0.08)", border: "1px solid oklch(1 0 0 / 0.08)",
boxShadow: "inset 0 1px 0 oklch(1 0 0 / 0.18)", boxShadow: "inset 0 1px 0 oklch(1 0 0 / 0.18)",
}} }}
/> />
<span style={{ fontSize: 13, fontWeight: 500, letterSpacing: "-0.005em" }}> <span
style={{
fontSize: 13,
fontWeight: 500,
letterSpacing: "-0.005em",
}}
>
{v.name} {v.name}
</span> </span>
<span style={{ fontSize: 11.5, color: "var(--fg-mute)", lineHeight: 1.4 }}> <span
style={{
fontSize: 11.5,
color: "var(--fg-mute)",
lineHeight: 1.4,
}}
>
{v.desc} {v.desc}
</span> </span>
</button> </button>
@@ -221,7 +320,15 @@ function EntrepVibe({ value, onChange }) {
} }
// ── Path wrapper ─────────────────────────────────────────────────────────── // ── Path wrapper ───────────────────────────────────────────────────────────
export function EntrepreneurPath({ data, onUpdate, onBack, onClose, onComplete, onJumpToStep, step }) { export function EntrepreneurPath({
data,
onUpdate,
onBack,
onClose,
onComplete,
onJumpToStep,
step,
}) {
const next = () => { const next = () => {
if (step < ENTREP_TOTAL - 1) onJumpToStep(step + 1); if (step < ENTREP_TOTAL - 1) onJumpToStep(step + 1);
else onComplete(); else onComplete();
@@ -231,20 +338,39 @@ export function EntrepreneurPath({ data, onUpdate, onBack, onClose, onComplete,
else onJumpToStep(step - 1); else onJumpToStep(step - 1);
}; };
let body, canNext, onSkip = null; let body,
canNext,
onSkip = null;
if (step === 0) { if (step === 0) {
body = <EntrepIdea value={data.idea || ""} onChange={(v) => onUpdate({ idea: v })} />; body = (
<EntrepIdea
value={data.idea || ""}
onChange={(v) => onUpdate({ idea: v })}
/>
);
canNext = (data.idea || "").trim().length >= 8; canNext = (data.idea || "").trim().length >= 8;
} else if (step === 1) { } else if (step === 1) {
body = <EntrepAudience value={data.audience || ""} onChange={(v) => onUpdate({ audience: v })} />; body = (
<EntrepAudience
value={data.audience || ""}
onChange={(v) => onUpdate({ audience: v })}
/>
);
canNext = (data.audience || "").trim().length >= 3; canNext = (data.audience || "").trim().length >= 3;
} else if (step === 2) { } else if (step === 2) {
body = <EntrepGoal value={data.goal} onChange={(v) => onUpdate({ goal: v })} />; body = (
<EntrepGoal value={data.goal} onChange={(v) => onUpdate({ goal: v })} />
);
canNext = !!data.goal; canNext = !!data.goal;
} else { } else {
body = <EntrepVibe value={data.vibe} onChange={(v) => onUpdate({ vibe: v })} />; body = (
<EntrepVibe value={data.vibe} onChange={(v) => onUpdate({ vibe: v })} />
);
canNext = !!data.vibe; canNext = !!data.vibe;
onSkip = () => { onUpdate({ vibe: "later" }); next(); }; onSkip = () => {
onUpdate({ vibe: "later" });
next();
};
} }
// 5 total: fork(1) + 4 path steps // 5 total: fork(1) + 4 path steps
@@ -272,5 +398,3 @@ export function EntrepreneurPath({ data, onUpdate, onBack, onClose, onComplete,
</> </>
); );
} }