Skip to content

Commit bb71f72

Browse files
authored
fix(onboarding): use brand mark in recovery cards (#115)
## Summary - replace the generic Sparkles icon in both onboarding recovery states with the Cheatcode brand mark - generalize the shared recovery-card visual slot so branded visuals and Lucide icons use one accessible API - preserve the existing card dimensions, icon sizing, action behavior, and Lucide stroke weight ## Context Direct user-reported onboarding UI defect; no Linear issue or plan document was created for this focused fix. ## Decisions Made | Decision | Choice | Alternatives considered | Reasoning | |---|---|---|---| | Recovery visual API | Accept a component slot with `className` and `aria-hidden` | Duplicate a card, add a brand-specific boolean, or wrap the mark as a fake Lucide icon | Keeps one recovery surface and lets the caller choose the correct visual without vendor coupling | | Lucide stroke styling | Apply stroke width through the shared class | Keep a Lucide-only `strokeWidth` prop | Preserves existing icon appearance while allowing non-SVG brand components | ## How to Review 1. Review `recovery-card.tsx` for the generalized visual contract. 2. Review `onboarding-flow.tsx` for both branded recovery-state call sites. ## Verification - [x] `pnpm lint` - [x] `pnpm typecheck` - [x] `pnpm turbo build --force` - [x] `pnpm deadcode` - [x] `pnpm architecture:check` - [x] `pnpm turbo skills:build` - [x] local stack started through the documented Compose flow - [x] direct authenticated browser flow reached `Setup needs one more step` - [x] DOM confirmed `CheatcodeMark` present, `lucide-sparkles` absent, and `Finish setup` enabled - [x] visual screenshot confirmed the 18px mark remains centered in the existing 44px recovery visual
1 parent 806cda4 commit bb71f72

2 files changed

Lines changed: 10 additions & 7 deletions

File tree

apps/web/src/components/onboarding/onboarding-flow.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

3-
import { Sparkles as SparklesIcon } from "@/components/ui";
43
import { CheatcodeLoader } from "@/components/ui/cheatcode-loader";
4+
import { CheatcodeMark } from "@/components/ui/cheatcode-mark";
55
import { RecoveryCard } from "@/components/ui/recovery-card";
66
import { createOnboardingStepProps, renderOnboardingStep } from "./onboarding-step-router";
77
import { STEP_ORDER, useOnboardingFlow } from "./use-onboarding-flow";
@@ -52,7 +52,7 @@ function RetryCard({ isPending, onRetry }: { isPending: boolean; onRetry: () =>
5252
pendingLabel: "Finishing setup…",
5353
}}
5454
description="Your progress is saved. Try again to finish setting up your session."
55-
icon={SparklesIcon}
55+
icon={CheatcodeMark}
5656
title="Setup needs one more step"
5757
/>
5858
);
@@ -69,7 +69,7 @@ function LoadErrorCard({ isPending, onRetry }: { isPending: boolean; onRetry: ()
6969
}}
7070
announce="assertive"
7171
description="Cheatcode couldn't reach your profile. Check your connection and try again."
72-
icon={SparklesIcon}
72+
icon={CheatcodeMark}
7373
title="Setup couldn't load"
7474
/>
7575
);

apps/web/src/components/ui/recovery-card.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import Link from "next/link";
2+
import type { ReactNode } from "react";
23
import type { LucideIcon } from "@/components/ui";
34
import { Loader2, RefreshCw } from "@/components/ui";
45
import { cn } from "@/lib/ui/cn";
56

7+
type RecoveryVisual = (props: { "aria-hidden": true; className: string }) => ReactNode;
8+
69
type RecoveryActionBase = {
710
icon?: LucideIcon | undefined;
811
label: string;
@@ -31,7 +34,7 @@ type RecoveryCardProps = {
3134
description: string;
3235
detail?: string | undefined;
3336
headingLevel?: 1 | 2 | 3 | undefined;
34-
icon: LucideIcon;
37+
icon: RecoveryVisual;
3538
size?: "compact" | "default" | undefined;
3639
title: string;
3740
variant?: "inline" | "stacked" | undefined;
@@ -42,7 +45,7 @@ type RecoveryContentProps = Pick<
4245
"action" | "description" | "detail" | "size" | "title"
4346
> & {
4447
headingTag: "h1" | "h2" | "h3";
45-
icon: LucideIcon;
48+
icon: RecoveryVisual;
4649
};
4750

4851
/** Recovery surface for blocking failures and compact section-level failures. */
@@ -107,7 +110,7 @@ function InlineRecoveryContent({
107110
<div className="flex flex-col gap-3 px-4 py-3.5 min-[540px]:flex-row min-[540px]:items-center">
108111
<div className="flex min-w-0 flex-1 items-center gap-3.5">
109112
<span className="flex size-10 shrink-0 items-center justify-center rounded-[12px] bg-secondary text-fg-secondary ring-1 ring-border/50">
110-
<Icon aria-hidden="true" className="size-[17px]" strokeWidth={1.7} />
113+
<Icon aria-hidden className="size-[17px] [stroke-width:1.7]" />
111114
</span>
112115
<div className="min-w-0">
113116
<Heading className="font-semibold text-[13px] text-foreground leading-5">{title}</Heading>
@@ -141,7 +144,7 @@ function StackedRecoveryContent({
141144
)}
142145
>
143146
<span className="flex size-11 items-center justify-center rounded-[14px] bg-bg-secondary text-fg-secondary ring-1 ring-border/70">
144-
<Icon aria-hidden="true" className="size-[18px]" strokeWidth={1.7} />
147+
<Icon aria-hidden className="size-[18px] [stroke-width:1.7]" />
145148
</span>
146149
<Heading className="mt-4 font-semibold text-[14px] text-foreground leading-5">
147150
{title}

0 commit comments

Comments
 (0)