Skip to content

Commit 5e08b00

Browse files
davidercruzclaude
andcommitted
feat(persona-quiz): cosmic restyle for intro/question/reveal
Recreate the "genie" cosmic aesthetic from the design mock using daily.dev design-system tokens only (no raw colours): drifting accent glow-orbs, a gradient-text headline, glassy surface cards, a floating genie motif, a gradient progress bar, and pill CTAs. Adds an intro "Begin" screen (gated on the step headline, so intro-less embeds are unchanged). On the reveal, the persona name is now the hero with its tagline as a smaller subtitle (was inverted). Also wire AuthContext + Settings providers into the /dev/persona-quiz preview, which previously 500'd because /dev/* routes get a minimal provider tree and useFeedSettings reads AuthContext without a null guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3da5a55 commit 5e08b00

5 files changed

Lines changed: 238 additions & 95 deletions

File tree

packages/shared/src/features/onboarding/steps/FunnelPersonaQuiz/FunnelPersonaQuiz.spec.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,26 @@ describe('FunnelPersonaQuiz', () => {
229229
});
230230
});
231231

232+
it('shows an intro screen and starts the quiz on Begin when a headline is set', async () => {
233+
renderStep({
234+
parameters: {
235+
...parameters,
236+
headline: 'Let me guess who you are',
237+
explainer: 'A few quick questions.',
238+
},
239+
});
240+
expect(
241+
await screen.findByText('Let me guess who you are'),
242+
).toBeInTheDocument();
243+
expect(
244+
screen.queryByText('Where do you spend most time?'),
245+
).not.toBeInTheDocument();
246+
fireEvent.click(screen.getByText('Begin'));
247+
expect(
248+
await screen.findByText('Where do you spend most time?'),
249+
).toBeInTheDocument();
250+
});
251+
232252
it('walks Q→A→reveal via static `next` pointers and emits Complete with payload', async () => {
233253
const { onTransition } = renderStep();
234254
fireEvent.click(await screen.findByText('Frontend'));

packages/shared/src/features/onboarding/steps/FunnelPersonaQuiz/PersonaQuizQuestion.tsx

Lines changed: 84 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -28,96 +28,107 @@ export const PersonaQuizQuestionView = ({
2828
}: PersonaQuizQuestionProps): ReactElement => (
2929
<div
3030
className={classNames(
31-
'flex w-full flex-1 flex-col gap-6 px-4 py-6 tablet:mx-auto laptop:gap-10',
31+
'flex w-full flex-1 flex-col gap-6 px-4 py-6 tablet:mx-auto laptop:gap-8',
3232
question.cols === 3 ? 'tablet:max-w-lg' : 'tablet:max-w-md',
3333
)}
3434
>
35-
<header className="flex items-center gap-3">
35+
<header className="flex flex-col gap-2">
3636
<Typography
3737
type={TypographyType.Footnote}
3838
color={TypographyColor.Tertiary}
3939
>
4040
{`Question ${step} of ${totalSteps}`}
4141
</Typography>
42-
<div className="flex flex-1 gap-1">
43-
{Array.from({ length: totalSteps }, (_, index) => (
44-
<span
45-
key={`step-${index}`}
46-
className={classNames(
47-
'h-1.5 flex-1 rounded-4 transition-colors',
48-
index < step ? 'bg-brand-default' : 'bg-border-subtlest-tertiary',
49-
)}
50-
/>
51-
))}
42+
<div className="h-1.5 w-full overflow-hidden rounded-full bg-border-subtlest-tertiary">
43+
<div
44+
className="h-full rounded-full bg-gradient-to-r from-accent-bacon-default to-accent-cabbage-default transition-all duration-500"
45+
style={{ width: `${Math.min(100, (step / totalSteps) * 100)}%` }}
46+
/>
5247
</div>
5348
</header>
5449

55-
<Typography
56-
tag={TypographyTag.H2}
57-
type={TypographyType.Title2}
58-
color={TypographyColor.Primary}
59-
bold
60-
className="text-center"
61-
>
62-
{question.prompt}
63-
</Typography>
64-
65-
{question.imageUrl && (
66-
<div className="grid place-items-center">
67-
<Image
68-
alt="Question additional context"
69-
aria-hidden
70-
className="mx-auto w-full max-w-md object-contain object-center"
71-
role="presentation"
72-
src={question.imageUrl}
73-
/>
50+
<div className="relative overflow-hidden rounded-24 border border-border-subtlest-tertiary bg-surface-float p-6 backdrop-blur tablet:p-8">
51+
<div className="mb-4 flex items-center justify-center gap-2">
52+
<span aria-hidden className="text-2xl">
53+
🧞
54+
</span>
55+
<Typography
56+
type={TypographyType.Footnote}
57+
className="uppercase tracking-wider text-accent-cabbage-default"
58+
>
59+
Picturing you…
60+
</Typography>
7461
</div>
75-
)}
7662

77-
<div
78-
className={classNames('grid gap-3', {
79-
'grid-cols-1': !question.cols || question.cols === 1,
80-
'grid-cols-2': question.cols === 2,
81-
'grid-cols-3': question.cols === 3,
82-
})}
83-
>
84-
{question.options.map((option) => (
85-
<button
86-
key={option.id}
87-
type="button"
88-
onClick={() => onSelect(option)}
89-
className={classNames(
90-
'flex min-w-0 gap-2 rounded-12 border border-border-subtlest-tertiary bg-background-default text-text-secondary transition-colors hover:border-text-tertiary hover:text-text-primary',
91-
question.cols === 3
92-
? 'flex-col items-center px-2 py-2.5 text-center'
93-
: 'items-center gap-3 px-4 py-3 text-left',
94-
)}
95-
>
96-
{option.emoji && (
97-
<span
98-
aria-hidden
99-
className={classNames(
100-
'shrink-0 leading-none',
101-
question.cols === 3 ? 'text-xl' : 'text-2xl',
102-
)}
103-
>
104-
{option.emoji}
105-
</span>
106-
)}
107-
<Typography
108-
type={
109-
question.cols === 3 ? TypographyType.Callout : TypographyType.Body
110-
}
111-
color={TypographyColor.Primary}
63+
<Typography
64+
tag={TypographyTag.H2}
65+
type={TypographyType.Title1}
66+
color={TypographyColor.Primary}
67+
bold
68+
className="text-center"
69+
>
70+
{question.prompt}
71+
</Typography>
72+
73+
{question.imageUrl && (
74+
<div className="mt-6 grid place-items-center">
75+
<Image
76+
alt="Question additional context"
77+
aria-hidden
78+
className="mx-auto w-full max-w-md object-contain object-center"
79+
role="presentation"
80+
src={question.imageUrl}
81+
/>
82+
</div>
83+
)}
84+
85+
<div
86+
className={classNames('mt-7 grid gap-3', {
87+
'grid-cols-1': !question.cols || question.cols === 1,
88+
'grid-cols-2': question.cols === 2,
89+
'grid-cols-3': question.cols === 3,
90+
})}
91+
>
92+
{question.options.map((option) => (
93+
<button
94+
key={option.id}
95+
type="button"
96+
onClick={() => onSelect(option)}
11297
className={classNames(
113-
'min-w-0 break-words',
114-
question.cols !== 3 && 'flex-1',
98+
'hover:bg-accent-cabbage-default/10 flex min-w-0 gap-2 rounded-12 border border-border-subtlest-tertiary bg-background-default text-text-secondary transition-all hover:-translate-y-0.5 hover:border-accent-cabbage-default hover:text-text-primary',
99+
question.cols === 3
100+
? 'flex-col items-center px-2 py-2.5 text-center'
101+
: 'items-center gap-3 px-4 py-3 text-left',
115102
)}
116103
>
117-
{option.label}
118-
</Typography>
119-
</button>
120-
))}
104+
{option.emoji && (
105+
<span
106+
aria-hidden
107+
className={classNames(
108+
'shrink-0 leading-none',
109+
question.cols === 3 ? 'text-xl' : 'text-2xl',
110+
)}
111+
>
112+
{option.emoji}
113+
</span>
114+
)}
115+
<Typography
116+
type={
117+
question.cols === 3
118+
? TypographyType.Callout
119+
: TypographyType.Body
120+
}
121+
color={TypographyColor.Primary}
122+
className={classNames(
123+
'min-w-0 break-words',
124+
question.cols !== 3 && 'flex-1',
125+
)}
126+
>
127+
{option.label}
128+
</Typography>
129+
</button>
130+
))}
131+
</div>
121132
</div>
122133
</div>
123134
);

packages/shared/src/features/onboarding/steps/FunnelPersonaQuiz/PersonaQuizReveal.tsx

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
import {
1010
Typography,
1111
TypographyColor,
12-
TypographyTag,
1312
TypographyType,
1413
} from '../../../../components/typography/Typography';
1514
import { Origin } from '../../../../lib/log';
@@ -24,6 +23,7 @@ import { PREVIEW_FEED_QUERY } from '../../../../graphql/feed';
2423
import { SearchField } from '../../../../components/fields/SearchField';
2524
import useDebounceFn from '../../../../hooks/useDebounceFn';
2625
import { useTagSearch } from '../../../../hooks/useTagSearch';
26+
import { onboardingGradientClasses } from '../../../../components/onboarding/common';
2727

2828
interface PersonaQuizRevealProps {
2929
archetype: PersonaArchetype | null;
@@ -89,33 +89,49 @@ export const PersonaQuizReveal = ({
8989
});
9090
const searchTags = searchResult?.searchTags?.tags ?? [];
9191

92-
const headline = archetype?.headline ?? headlineFromTags(tags);
93-
const eyebrow = archetype?.name ?? reveal.eyebrow;
92+
// The persona name is the hero; its headline is the smaller tagline beneath.
93+
// With no resolved archetype, fall back to a tag-derived hero line.
94+
const title = archetype?.name ?? headlineFromTags(tags);
95+
const subtitle = archetype?.headline;
96+
const { eyebrow } = reveal;
9497
const description = archetype?.description;
9598

9699
return (
97100
<div className="flex w-full flex-1 flex-col gap-6 px-4 py-6 tablet:mx-auto tablet:max-w-4xl">
98-
<header className="flex flex-col items-center gap-2 text-center">
101+
<header className="flex flex-col items-center gap-3 text-center">
102+
<div className="relative grid place-items-center">
103+
<div
104+
aria-hidden
105+
className="bg-accent-cabbage-default/20 absolute h-24 w-24 rounded-full blur-3xl"
106+
/>
107+
<span aria-hidden className="animate-float-slow relative text-7xl">
108+
🧞
109+
</span>
110+
</div>
99111
{eyebrow && (
100112
<Typography
101-
type={TypographyType.Footnote}
102-
color={TypographyColor.Tertiary}
113+
type={TypographyType.Caption1}
114+
className="uppercase tracking-[0.15em] text-accent-cabbage-default"
103115
>
104116
{eyebrow}
105117
</Typography>
106118
)}
107-
<Typography
108-
tag={TypographyTag.H2}
109-
type={TypographyType.Title1}
110-
color={TypographyColor.Primary}
111-
bold
112-
>
113-
{headline}
114-
</Typography>
119+
<h2 className={classNames('typo-mega2', onboardingGradientClasses)}>
120+
{title}
121+
</h2>
122+
{subtitle && (
123+
<Typography
124+
type={TypographyType.Title3}
125+
color={TypographyColor.Secondary}
126+
>
127+
{subtitle}
128+
</Typography>
129+
)}
115130
{description && (
116131
<Typography
117132
type={TypographyType.Body}
118133
color={TypographyColor.Tertiary}
134+
className="max-w-md"
119135
>
120136
{description}
121137
</Typography>

0 commit comments

Comments
 (0)