Skip to content

Commit 66346be

Browse files
capJavertclaude
andcommitted
feat(onboarding): add inline chooser panel at feed end, extract OnboardingChooserGrid
- Add chooser cards (GitHub + AI) inline below the feed preview - Extract OnboardingChooserGrid component to share between modal and inline panel - Fix bug: AI path now sets signupContext before auth so import triggers after signup - Persistent backdrop across prompt/chooser/auth modal transitions - Remove min-h-0 override on AuthOptions to fix layout shift Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b25d774 commit 66346be

File tree

2 files changed

+380
-326
lines changed

2 files changed

+380
-326
lines changed
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
import type { ReactElement } from 'react';
2+
import React from 'react';
3+
import classNames from 'classnames';
4+
import { GitHubIcon } from '@dailydotdev/shared/src/components/icons/GitHub';
5+
import { MagicIcon } from '@dailydotdev/shared/src/components/icons/Magic';
6+
import { TerminalIcon } from '@dailydotdev/shared/src/components/icons/Terminal';
7+
import { NewTabIcon } from '@dailydotdev/shared/src/components/icons/NewTab';
8+
import { IconSize } from '@dailydotdev/shared/src/components/Icon';
9+
10+
const PARTICLES = [
11+
{
12+
px: '-6rem',
13+
py: '-3.5rem',
14+
dur: '3.0s',
15+
delay: '0s',
16+
color: 'bg-accent-cheese-default',
17+
},
18+
{
19+
px: '5.5rem',
20+
py: '-4rem',
21+
dur: '3.4s',
22+
delay: '0.5s',
23+
color: 'bg-accent-water-default',
24+
},
25+
{
26+
px: '-5rem',
27+
py: '3.5rem',
28+
dur: '3.2s',
29+
delay: '1.0s',
30+
color: 'bg-accent-cabbage-default',
31+
},
32+
{
33+
px: '6rem',
34+
py: '3rem',
35+
dur: '3.6s',
36+
delay: '1.5s',
37+
color: 'bg-accent-onion-default',
38+
},
39+
{
40+
px: '0.5rem',
41+
py: '-5rem',
42+
dur: '2.8s',
43+
delay: '0.7s',
44+
color: 'bg-accent-cheese-default',
45+
},
46+
{
47+
px: '-6.5rem',
48+
py: '0.5rem',
49+
dur: '3.1s',
50+
delay: '1.2s',
51+
color: 'bg-accent-water-default',
52+
},
53+
];
54+
55+
const STEPS = [
56+
{ text: 'We spot your stack from GitHub', icon: 'stack' },
57+
{ text: 'AI matches your skills to topics', icon: 'ai' },
58+
{ text: 'Your feed is ready in seconds', icon: 'feed' },
59+
] as const;
60+
61+
type Props = {
62+
aiPrompt: string;
63+
onAiPromptChange: (value: string) => void;
64+
canStartAiFlow: boolean;
65+
onGithubClick: () => void;
66+
onAiSubmit: () => void;
67+
};
68+
69+
export function OnboardingChooserGrid({
70+
aiPrompt,
71+
onAiPromptChange,
72+
canStartAiFlow,
73+
onGithubClick,
74+
onAiSubmit,
75+
}: Props): ReactElement {
76+
return (
77+
<div className="relative z-1 grid gap-4 tablet:grid-cols-2 tablet:items-stretch tablet:gap-5">
78+
{/* ── Path A: GitHub ── */}
79+
<div className="onb-glass flex h-full min-h-[24rem] flex-1 flex-col items-center overflow-hidden rounded-16 border border-white/[0.06] p-6 transition-all duration-700 ease-out tablet:min-h-[26rem] tablet:self-stretch">
80+
{/* Animated orb */}
81+
<div
82+
className="relative -mx-6 -mt-6 mb-0 flex h-32 items-center justify-center"
83+
style={{ width: 'calc(100% + 3rem)' }}
84+
>
85+
<div
86+
className="pointer-events-none absolute inset-0"
87+
style={{
88+
background:
89+
'radial-gradient(ellipse at 50% 0%, var(--theme-accent-cabbage-default) 0%, transparent 65%)',
90+
opacity: 0.22,
91+
}}
92+
/>
93+
<div className="ghub-orb-glow bg-accent-cabbage-default/15 pointer-events-none absolute h-32 w-52 rounded-full blur-3xl" />
94+
<svg
95+
className="ghub-ring pointer-events-none absolute"
96+
style={{ width: '11rem', height: '11rem' }}
97+
viewBox="0 0 176 176"
98+
>
99+
<circle
100+
cx="88"
101+
cy="88"
102+
r="84"
103+
fill="none"
104+
stroke="var(--theme-accent-cabbage-default)"
105+
strokeWidth="1.5"
106+
strokeDasharray="6 10"
107+
opacity="0.18"
108+
/>
109+
</svg>
110+
<svg
111+
className="ghub-ring-reverse pointer-events-none absolute h-24 w-24"
112+
viewBox="0 0 96 96"
113+
>
114+
<circle
115+
cx="48"
116+
cy="48"
117+
r="44"
118+
fill="none"
119+
stroke="var(--theme-accent-cabbage-default)"
120+
strokeWidth="1.5"
121+
strokeDasharray="4 6"
122+
opacity="0.35"
123+
/>
124+
</svg>
125+
<svg
126+
className="onb-ring-slow pointer-events-none absolute h-16 w-16"
127+
viewBox="0 0 64 64"
128+
>
129+
<circle
130+
cx="32"
131+
cy="32"
132+
r="28"
133+
fill="none"
134+
stroke="var(--theme-accent-cabbage-default)"
135+
strokeWidth="1"
136+
strokeDasharray="3 5"
137+
opacity="0.3"
138+
/>
139+
</svg>
140+
{PARTICLES.map((p) => (
141+
<span
142+
key={`ghub-${p.delay}`}
143+
className={classNames(
144+
'ghub-particle pointer-events-none absolute h-2 w-2 rounded-full',
145+
p.color,
146+
)}
147+
style={
148+
{
149+
'--px': p.px,
150+
'--py': p.py,
151+
'--dur': p.dur,
152+
'--delay': p.delay,
153+
animationDelay: p.delay,
154+
} as React.CSSProperties
155+
}
156+
/>
157+
))}
158+
<div className="relative flex h-14 w-14 items-center justify-center rounded-full bg-surface-float">
159+
<GitHubIcon
160+
secondary
161+
className="h-[26px] w-[26px] text-text-primary"
162+
/>
163+
</div>
164+
</div>
165+
166+
<h4 className="mb-1.5 break-words text-center font-bold text-text-primary typo-body">
167+
One-click setup
168+
</h4>
169+
<p className="mb-5 text-center text-text-tertiary typo-footnote">
170+
Connect GitHub and let our AI do the rest.
171+
</p>
172+
173+
<div className="mb-5 flex w-full flex-col items-center gap-3">
174+
{STEPS.map(({ text, icon }) => (
175+
<div
176+
key={text}
177+
className="flex w-full max-w-[15.5rem] items-center gap-2"
178+
>
179+
<span
180+
className={classNames(
181+
'flex h-6 w-6 shrink-0 items-center justify-center rounded-full',
182+
icon === 'stack' &&
183+
'bg-accent-avocado-default/20 text-accent-avocado-default',
184+
icon === 'ai' &&
185+
'bg-accent-cheese-default/20 text-accent-cheese-default',
186+
icon === 'feed' &&
187+
'bg-accent-water-default/20 text-accent-water-default',
188+
)}
189+
>
190+
{icon === 'stack' && (
191+
<TerminalIcon size={IconSize.Size16} secondary />
192+
)}
193+
{icon === 'ai' && (
194+
<MagicIcon size={IconSize.Size16} secondary />
195+
)}
196+
{icon === 'feed' && (
197+
<NewTabIcon size={IconSize.Size16} secondary />
198+
)}
199+
</span>
200+
<span className="text-left text-text-primary typo-footnote">
201+
{text}
202+
</span>
203+
</div>
204+
))}
205+
</div>
206+
207+
<div className="bg-border-subtlest-tertiary/30 mb-5 h-px w-full" />
208+
209+
<div className="relative mt-auto w-full pt-4">
210+
<div className="onb-btn-glow pointer-events-none absolute -inset-2 rounded-16 bg-white/[0.04] blur-lg" />
211+
<button
212+
type="button"
213+
onClick={onGithubClick}
214+
className="onb-btn-shine focus-visible:ring-white/20 group relative flex w-full items-center justify-center gap-2.5 overflow-hidden rounded-14 bg-white px-5 py-3.5 font-bold text-black transition-all duration-300 typo-callout hover:-translate-y-1 hover:shadow-[0_8px_30px_rgba(255,255,255,0.12)] focus-visible:outline-none focus-visible:ring-2"
215+
>
216+
<GitHubIcon secondary size={IconSize.XSmall} />
217+
Continue with GitHub
218+
<svg
219+
width="14"
220+
height="14"
221+
viewBox="0 0 24 24"
222+
fill="none"
223+
className="text-black/30 transition-transform duration-300 group-hover:translate-x-0.5"
224+
>
225+
<path
226+
d="M5 12h14M12 5l7 7-7 7"
227+
stroke="currentColor"
228+
strokeWidth="2"
229+
strokeLinecap="round"
230+
strokeLinejoin="round"
231+
/>
232+
</svg>
233+
</button>
234+
</div>
235+
<p className="mt-2.5 text-text-quaternary typo-caption2">
236+
Read-only access &middot; No special permissions
237+
</p>
238+
</div>
239+
240+
{/* ── Path B: AI ── */}
241+
<div className="onb-glass flex h-full min-h-[24rem] flex-1 flex-col items-center overflow-hidden rounded-16 border border-white/[0.06] p-6 transition-all duration-700 ease-out tablet:min-h-[26rem] tablet:self-stretch">
242+
<div
243+
className="relative -mx-6 -mt-6 mb-0 flex h-32 items-center justify-center"
244+
style={{ width: 'calc(100% + 3rem)' }}
245+
>
246+
<div
247+
className="pointer-events-none absolute inset-0"
248+
style={{
249+
background:
250+
'radial-gradient(ellipse at 50% 0%, var(--theme-accent-onion-default) 0%, transparent 65%)',
251+
opacity: 0.22,
252+
}}
253+
/>
254+
<div className="relative flex h-14 w-14 items-center justify-center rounded-full bg-surface-float">
255+
<MagicIcon secondary size={IconSize.Small} className="text-white" />
256+
</div>
257+
</div>
258+
259+
<h4 className="mb-1.5 break-words text-center font-bold text-text-primary typo-body">
260+
Tell our AI about yourself
261+
</h4>
262+
<p className="mb-5 text-center text-text-tertiary typo-footnote">
263+
Describe your stack and let AI build your feed.
264+
</p>
265+
266+
<div className="onb-textarea-glow mb-3 w-full rounded-12 border border-white/[0.06] bg-white/[0.02] transition-all duration-300 focus-within:border-white/[0.18] focus-within:bg-white/[0.08] focus-within:shadow-[0_0_0_1px_rgba(255,255,255,0.14),0_12px_28px_rgba(0,0,0,0.3)] hover:border-white/[0.06] hover:bg-white/[0.02] hover:shadow-none">
267+
<textarea
268+
value={aiPrompt}
269+
onChange={(e) => onAiPromptChange(e.target.value)}
270+
onKeyDown={(e) => {
271+
if (e.key !== 'Enter' || e.shiftKey) {
272+
return;
273+
}
274+
e.preventDefault();
275+
if (aiPrompt.trim()) {
276+
onAiSubmit();
277+
}
278+
}}
279+
rows={4}
280+
placeholder="I'm a frontend engineer working with React and TypeScript. Interested in system design, AI tooling..."
281+
className="min-h-[6.25rem] w-full resize-none bg-transparent px-3.5 pb-2 pt-3 text-text-primary transition-colors duration-200 typo-callout placeholder:text-text-quaternary focus:outline-none focus:placeholder:text-text-disabled"
282+
/>
283+
</div>
284+
285+
<div className="relative mt-auto w-full pt-4">
286+
<div className="onb-btn-glow pointer-events-none absolute -inset-2 rounded-16 bg-white/[0.04] blur-lg" />
287+
<button
288+
type="button"
289+
disabled={!canStartAiFlow}
290+
onClick={onAiSubmit}
291+
className={classNames(
292+
'focus-visible:ring-white/20 group relative flex w-full items-center justify-center gap-2.5 overflow-hidden rounded-14 px-5 py-3.5 font-bold transition-all duration-300 typo-callout focus-visible:outline-none focus-visible:ring-2',
293+
canStartAiFlow
294+
? 'bg-white text-black hover:-translate-y-1 hover:shadow-[0_8px_30px_rgba(255,255,255,0.12)]'
295+
: 'cursor-not-allowed bg-white/[0.08] text-text-disabled',
296+
)}
297+
>
298+
<MagicIcon
299+
secondary
300+
size={IconSize.Size16}
301+
className="transition-transform duration-300 group-hover:translate-x-0.5"
302+
/>
303+
Generate my feed with AI
304+
</button>
305+
</div>
306+
<p className="mt-2.5 text-text-quaternary typo-caption2">
307+
AI-powered &middot; instant personalization
308+
</p>
309+
</div>
310+
</div>
311+
);
312+
}

0 commit comments

Comments
 (0)