diff --git a/packages/shared/src/lib/log.ts b/packages/shared/src/lib/log.ts index a8d5fe8a94..8d418c39ce 100644 --- a/packages/shared/src/lib/log.ts +++ b/packages/shared/src/lib/log.ts @@ -84,7 +84,6 @@ export enum Origin { Leaderboard = 'leaderboard', // Onboarding v2 OnboardingModal = 'onboarding modal', - OnboardingFeedEnd = 'onboarding feed end', // Auth BetterAuthEmailLogin = 'betterauth email login', BetterAuthEmailLoginBoot = 'betterauth email login boot', @@ -474,7 +473,6 @@ export enum TargetType { HighlightsCard = 'highlights card', AdvertiseHereCta = 'advertise here cta', // Onboarding v2 - HeroCta = 'hero cta', SignupChooser = 'signup chooser', SignupPrompt = 'signup prompt', ProfileImport = 'profile import', diff --git a/packages/webapp/components/onboarding/OnboardingV2.tsx b/packages/webapp/components/onboarding/OnboardingV2.tsx index 45bfdb5cb3..b25351e869 100644 --- a/packages/webapp/components/onboarding/OnboardingV2.tsx +++ b/packages/webapp/components/onboarding/OnboardingV2.tsx @@ -52,11 +52,6 @@ import { import { ChromeIcon } from '@dailydotdev/shared/src/components/icons/Browser/Chrome'; import { MagicIcon } from '@dailydotdev/shared/src/components/icons/Magic'; -import { TerminalIcon } from '@dailydotdev/shared/src/components/icons/Terminal'; -import { HomeIcon } from '@dailydotdev/shared/src/components/icons/Home'; -import { HotIcon } from '@dailydotdev/shared/src/components/icons/Hot'; -import { EyeIcon } from '@dailydotdev/shared/src/components/icons/Eye'; -import { SquadIcon } from '@dailydotdev/shared/src/components/icons/Squad'; import { VIcon } from '@dailydotdev/shared/src/components/icons/V'; import { StarIcon } from '@dailydotdev/shared/src/components/icons/Star'; import { IconSize } from '@dailydotdev/shared/src/components/Icon'; @@ -87,74 +82,6 @@ import { OnboardingV2Styles } from './OnboardingV2Styles'; import { useOnboardingAnimations } from './useOnboardingAnimations'; import { OnboardingChooserGrid } from './OnboardingChooserGrid'; -type RisingTag = { - label: string; - left: string; - delay: string; - duration: string; - driftX: number; -}; - -const RISING_TAGS_DESKTOP: RisingTag[] = [ - { label: 'React', left: '8%', delay: '0s', duration: '14s', driftX: 12 }, - { label: 'AI & ML', left: '28%', delay: '1.2s', duration: '15s', driftX: -8 }, - { - label: 'System Design', - left: '52%', - delay: '0.6s', - duration: '14.5s', - driftX: 10, - }, - { label: 'Docker', left: '78%', delay: '2s', duration: '13.8s', driftX: -14 }, - { - label: 'TypeScript', - left: '18%', - delay: '3.4s', - duration: '15.2s', - driftX: 8, - }, - { - label: 'Next.js', - left: '88%', - delay: '2.8s', - duration: '14.4s', - driftX: -10, - }, - { - label: 'Python', - left: '42%', - delay: '4.2s', - duration: '14.8s', - driftX: -6, - }, - { - label: 'Kubernetes', - left: '66%', - delay: '5s', - duration: '14.2s', - driftX: 12, - }, -]; - -const RISING_TAGS_MOBILE: RisingTag[] = [ - { label: 'React', left: '10%', delay: '0s', duration: '13.5s', driftX: 8 }, - { - label: 'AI & ML', - left: '55%', - delay: '1.5s', - duration: '14s', - driftX: -10, - }, - { label: 'Docker', left: '30%', delay: '3s', duration: '13s', driftX: 6 }, - { - label: 'TypeScript', - left: '75%', - delay: '4.5s', - duration: '14.5s', - driftX: -8, - }, -]; - export type OnboardingStep = | 'hero' | 'prompt' @@ -253,21 +180,16 @@ export const OnboardingV2 = (): ReactElement => { const [showMobileAppPopup, setShowMobileAppPopup] = useState(false); const { mounted, - tagsReady, - feedVisible, heroRef, confettiParticles, isEdgeBrowser, extensionImages, } = useOnboardingAnimations(step); - const [aiPrompt, setAiPrompt] = usePersistentContext( - ONBOARDING_AI_PROMPT_KEY, - '', - ); - const [extensionSeen, setExtensionSeen] = usePersistentContext( - ONBOARDING_EXTENSION_SEEN_KEY, - false, - ); + const [aiPrompt, setAiPrompt, isAiPromptFetched] = + usePersistentContext(ONBOARDING_AI_PROMPT_KEY, '', undefined, ''); + const [extensionSeen, setExtensionSeen, isExtensionSeenFetched] = + usePersistentContext(ONBOARDING_EXTENSION_SEEN_KEY, false); + const shouldShowExtension = showExtensionCta && !extensionSeen; const [authDisplay, setAuthDisplay] = useState(AuthDisplay.OnboardingSignup); const [isLoginFlow, setIsLoginFlow] = useState(false); const [acceptedMarketing, setAcceptedMarketing] = useState(true); @@ -280,9 +202,12 @@ export const OnboardingV2 = (): ReactElement => { >(null); const [importBodyHeight, setImportBodyHeight] = useState(null); const [importExiting, setImportExiting] = useState(false); - const [signupContext, setSignupContext] = usePersistentContext< - 'github' | 'ai' | null - >(ONBOARDING_SIGNUP_CONTEXT_KEY, null, ['github', 'ai']); + const [signupContext, setSignupContext, isSignupContextFetched] = + usePersistentContext<'github' | 'ai' | null>( + ONBOARDING_SIGNUP_CONTEXT_KEY, + null, + ['github', 'ai'], + ); const pageRef = useRef(null); const importTimersRef = useRef[]>([]); const importBodyContentRef = useRef(null); @@ -299,14 +224,6 @@ export const OnboardingV2 = (): ReactElement => { [activeFeedName], ); - const openSignup = useCallback( - (context: 'github' | 'ai') => { - setSignupContext(context); - setStep('prompt'); - }, - [setSignupContext], - ); - const clearImportTimers = useCallback(() => { importTimersRef.current.forEach(clearTimeout); importTimersRef.current = []; @@ -487,7 +404,7 @@ export const OnboardingV2 = (): ReactElement => { setImportExiting(true); trackTimer(() => { setImportExiting(false); - setStep(showExtensionCta ? 'extension' : 'complete'); + setStep(shouldShowExtension ? 'extension' : 'complete'); }, 350); }, 600); }, FINISHING_ANIMATION_MS); @@ -501,7 +418,7 @@ export const OnboardingV2 = (): ReactElement => { completeAction, router, trackTimer, - showExtensionCta, + shouldShowExtension, ], ); @@ -516,6 +433,8 @@ export const OnboardingV2 = (): ReactElement => { if ( !isAuthReady || isAuthenticating || + !isAiPromptFetched || + !isSignupContextFetched || ( [ 'auth', @@ -556,12 +475,12 @@ export const OnboardingV2 = (): ReactElement => { return; } - if (extensionSeen || !showExtensionCta) { - setStep('complete'); - } else { - setStep('extension'); + if (!isExtensionSeenFetched) { + return; } + setStep(shouldShowExtension ? 'extension' : 'complete'); + return; } @@ -610,7 +529,10 @@ export const OnboardingV2 = (): ReactElement => { isLoggedIn, isOnboardingActionsReady, isOnboardingComplete, - extensionSeen, + isExtensionSeenFetched, + isAiPromptFetched, + isSignupContextFetched, + shouldShowExtension, aiPrompt, signupContext, step, @@ -618,7 +540,6 @@ export const OnboardingV2 = (): ReactElement => { setSignupContext, startImportFlowGithub, startAiProcessing, - showExtensionCta, ]); useEffect(() => { @@ -722,7 +643,10 @@ export const OnboardingV2 = (): ReactElement => { return (
@@ -756,46 +680,17 @@ export const OnboardingV2 = (): ReactElement => { )} - {/* ── Dummy Sidebar (laptop only) ── */} - - {/* ── Hero ── */}
-
+
{
- {tagsReady && - RISING_TAGS_DESKTOP.map((tag) => ( - - {tag.label} - - ))}
{/* Single radial hero glow */}
- {/* Centered text content */} -
+ {/* Title + chooser */} +
- {/* Mobile-only rising tags */} -
- {tagsReady && - RISING_TAGS_MOBILE.map((tag) => ( - - {tag.label} - - ))} -
- {/* Headline */} + {/* Headline — slightly smaller than typo-mega1 on tablet */}
{ )} style={{ transitionDelay: '200ms' }} > -

+

- Staying updated shouldn't be hard + Staying sharp shouldn't be hard
- Get your personalized dev feed + A dev feed built around your stack

- {/* Subtext */} -
-

- Millions of developers rely on daily.dev for tech news, tools, and - discussions that actually matter. Tailored to your stack from day - one. -

-
- - {/* Hero CTA group */} -
-
-
- - + />
-
- - {/* Mobile-only bottom rising tags */} -
- {tagsReady && - RISING_TAGS_MOBILE.map((tag) => ( - - {tag.label} - - ))} -
+ )}
@@ -1306,77 +1077,23 @@ export const OnboardingV2 = (): ReactElement => {
)} - {/* ── Feed ── */} -
- - - - - - - - {/* ── Inline chooser panel at feed end ── */} - {step === 'hero' && ( -
-
-
-
-

- You just explored the global feed. -

-

- Now build a feed that is truly yours -

-
- - { - if (isLoggedIn) { - startImportFlowGithub(); - } else { - initiateGithubAuth(); - } - }} - onAiSubmit={() => { - if (isLoggedIn) { - startAiProcessing(); - } else { - setSignupContext('ai'); - openSignupAuth(); - } - }} - /> -
-
- )} -
+ {/* ── Feed (only on complete step — chooser lives in hero) ── */} + {step === 'complete' && ( +
+ + + + + + + +
+ )} {step !== 'complete' && (
@@ -1574,7 +1291,7 @@ export const OnboardingV2 = (): ReactElement => { pathname: `${webappUrl}onboarding`, query: { ...router.query, step: 'complete' }, }); - setStep(showExtensionCta ? 'extension' : 'complete'); + setStep(shouldShowExtension ? 'extension' : 'complete'); }} className="mx-auto mt-6 rounded-14 bg-white px-8 py-3 font-bold text-black transition-all duration-200 typo-callout hover:-translate-y-0.5 hover:shadow-[0_8px_28px_rgba(255,255,255,0.12)] disabled:cursor-not-allowed disabled:opacity-40" > diff --git a/packages/webapp/components/onboarding/OnboardingV2Styles.tsx b/packages/webapp/components/onboarding/OnboardingV2Styles.tsx index c47d49c61d..335d1a7492 100644 --- a/packages/webapp/components/onboarding/OnboardingV2Styles.tsx +++ b/packages/webapp/components/onboarding/OnboardingV2Styles.tsx @@ -11,12 +11,6 @@ export const OnboardingV2Styles = (): ReactElement => ( display: none !important; } - .onb-feed-stage:not(.onb-feed-unlocked) article:nth-of-type(n + 19) { - display: none !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) article:nth-of-type(18) ~ div { - display: none !important; - } .onb-hero .onb-float-1, .onb-hero .onb-float-2, .onb-hero .onb-float-3 { @@ -26,8 +20,6 @@ export const OnboardingV2Styles = (): ReactElement => ( padding-top: 0 !important; } - /* fade-out handled by .onb-revealed nth-of-type rules below */ - /* ─── HERO PARALLAX ─── */ .onb-hero { --scroll-y: 0; @@ -64,41 +56,6 @@ export const OnboardingV2Styles = (): ReactElement => ( opacity: calc(1 - var(--scroll-y) * 0.001); } - /* ─── RISING TAG CLOUD ─── */ - @keyframes onb-tag-rise { - 0% { - opacity: 0; - transform: translate3d(0, 0, 0) scale(0.88); - } - 6% { - opacity: 0.6; - transform: translate3d(calc(var(--tag-drift-x, 0px) * 0.1), -6vh, 0) - scale(0.95); - } - 40% { - opacity: 0.45; - transform: translate3d(calc(var(--tag-drift-x, 0px) * 0.55), -28vh, 0) - scale(1); - } - 75% { - opacity: 0.15; - transform: translate3d(calc(var(--tag-drift-x, 0px) * 0.85), -52vh, 0) - scale(1.01); - } - 100% { - opacity: 0; - transform: translate3d(var(--tag-drift-x, 0px), -70vh, 0) scale(1.02); - } - } - .onb-rising-tag { - opacity: 0; - animation: onb-tag-rise var(--tag-duration, 14s) linear infinite; - animation-delay: var(--tag-delay, 0s); - contain: layout style; - will-change: transform, opacity; - transform: translateZ(0); - } - /* ─── SHIMMER ─── */ .onb-shimmer { position: relative; @@ -166,17 +123,6 @@ export const OnboardingV2Styles = (): ReactElement => ( opacity: 1; } } - .onb-feed-stage:not(.onb-feed-unlocked) article.onb-revealed { - opacity: 1 !important; - transform: translateY(0) scale(1) !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) article.onb-revealed:hover { - transform: translateY(0) scale(1) !important; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25), - 0 0 0 1px rgba(255, 255, 255, 0.03) !important; - border-color: rgba(255, 255, 255, 0.06) !important; - } - /* ─── CHIP POP ─── */ @keyframes onb-chip-pop { 0% { @@ -531,12 +477,6 @@ export const OnboardingV2Styles = (): ReactElement => ( animation: onb-celebration-sparkle 1.2s ease-out forwards; } - .onb-feed-unlocked article { - opacity: 1 !important; - transform: none !important; - pointer-events: auto !important; - } - /* ─── AI PROCESSING ORB ─── */ @keyframes onb-ai-orb-breathe { 0%, @@ -899,72 +839,6 @@ export const OnboardingV2Styles = (): ReactElement => ( box-shadow: 0 28px 90px rgba(0, 0, 0, 0.62); } - /* ─── FEED ARTICLE SCROLL-REVEAL ─── */ - .onb-feed-stage:not(.onb-feed-unlocked) article { - opacity: 0; - transform: translateY(0.75rem) scale(0.995); - transition: opacity 0.38s cubic-bezier(0.16, 1, 0.3, 1), - transform 0.38s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.25s ease, - border-color 0.25s ease !important; - transition-delay: var(--reveal-delay, 0ms); - } - - /* ─── FEED FADE-OUT GRADIENT (bottom of visible articles) ─── */ - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(13).onb-revealed { - opacity: 0.88 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(14).onb-revealed { - opacity: 0.72 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(15).onb-revealed { - opacity: 0.52 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(16).onb-revealed { - opacity: 0.32 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(17).onb-revealed { - opacity: 0.15 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(18).onb-revealed { - opacity: 0.05 !important; - } - - @media (max-width: 63.9375rem) { - .onb-feed-stage:not(.onb-feed-unlocked) article:nth-of-type(n + 11) { - display: none !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) article:nth-of-type(10) ~ div { - display: none !important; - } - - .onb-feed-stage:not(.onb-feed-unlocked) article.onb-revealed:hover { - box-shadow: none !important; - } - - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(7).onb-revealed { - opacity: 0.88 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(8).onb-revealed { - opacity: 0.65 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(9).onb-revealed { - opacity: 0.38 !important; - } - .onb-feed-stage:not(.onb-feed-unlocked) - article:nth-of-type(10).onb-revealed { - opacity: 0.12 !important; - } - } - /* ─── TOPIC PILLS (no interaction) ─── */ .onb-marquee span { pointer-events: none; @@ -983,8 +857,6 @@ export const OnboardingV2Styles = (): ReactElement => ( } } - /* mobile rising tags already handled by .onb-rising-tag */ - /* ─── MOBILE MODAL SLIDE-UP ─── */ @keyframes onb-modal-slide-up { from { @@ -1019,7 +891,6 @@ export const OnboardingV2Styles = (): ReactElement => ( } /* ─── OFF-SCREEN ANIMATION PAUSING ─── */ - .onb-hero-offscreen .onb-rising-tag, .onb-hero-offscreen .onb-hero-radial, .onb-hero-offscreen .onb-float-1, .onb-hero-offscreen .onb-float-2, @@ -1028,8 +899,7 @@ export const OnboardingV2Styles = (): ReactElement => ( .onb-hero-offscreen .onb-btn-glow { animation-play-state: paused !important; } - .onb-hero-offscreen .onb-hero-radial, - .onb-hero-offscreen .onb-rising-tag { + .onb-hero-offscreen .onb-hero-radial { will-change: auto !important; } @@ -1091,8 +961,7 @@ export const OnboardingV2Styles = (): ReactElement => ( .onb-sparkle, .onb-confetti-star, .onb-eng-pulse, - .onb-eng-floater, - .onb-rising-tag { + .onb-eng-floater { animation: none !important; opacity: 1 !important; } diff --git a/packages/webapp/components/onboarding/useOnboardingAnimations.ts b/packages/webapp/components/onboarding/useOnboardingAnimations.ts index ba38d56ad7..018b3baefc 100644 --- a/packages/webapp/components/onboarding/useOnboardingAnimations.ts +++ b/packages/webapp/components/onboarding/useOnboardingAnimations.ts @@ -49,10 +49,8 @@ function buildConfettiParticles(): ConfettiParticle[] { } export function useOnboardingAnimations(step: OnboardingStep) { - // ── Mount + tag reveal + feed visible state ── + // ── Mount state ── const [mounted, setMounted] = useState(false); - const [tagsReady, setTagsReady] = useState(false); - const [feedVisible, setFeedVisible] = useState(false); // ── Refs ── const heroRef = useRef(null); @@ -65,34 +63,6 @@ export function useOnboardingAnimations(step: OnboardingStep) { return () => cancelAnimationFrame(raf); }, []); - // ── Tag reveal timing ── - useEffect(() => { - if (!mounted) { - return undefined; - } - let idleTimer: number | null = null; - let revealTimer: ReturnType | null = null; - - const revealTags = () => { - revealTimer = setTimeout(() => setTagsReady(true), 180); - }; - - if ('requestIdleCallback' in window) { - idleTimer = window.requestIdleCallback(revealTags, { timeout: 1400 }); - } else { - revealTimer = setTimeout(() => setTagsReady(true), 1200); - } - - return () => { - if (idleTimer !== null && 'cancelIdleCallback' in window) { - window.cancelIdleCallback(idleTimer); - } - if (revealTimer !== null) { - window.clearTimeout(revealTimer); - } - }; - }, [mounted]); - // ── Body overflow management ── useEffect(() => { const isModalOpen = step !== 'hero' && step !== 'complete'; @@ -110,77 +80,6 @@ export function useOnboardingAnimations(step: OnboardingStep) { }; }, [step]); - // ── Feed article reveal + intersection observer ── - useEffect(() => { - if (!mounted) { - return undefined; - } - - const timer = window.setTimeout(() => { - setFeedVisible(true); - }, 400); - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - (entry.target as HTMLElement).classList.add('onb-revealed'); - observer.unobserve(entry.target); - } - }); - }, - { rootMargin: '0px 0px -40px 0px', threshold: 0.05 }, - ); - - const observeFeedArticles = () => { - document - .querySelectorAll('.onb-feed-stage article') - .forEach((article, i) => { - if (!article.dataset.onbRevealDelay) { - article.style.setProperty( - '--reveal-delay', - `${Math.min(i * 60, 400)}ms`, - ); - // eslint-disable-next-line no-param-reassign - article.dataset.onbRevealDelay = 'true'; - } - - if (article.classList.contains('onb-revealed')) { - return; - } - - observer.observe(article); - }); - }; - - observeFeedArticles(); - - const mutationObserver = new MutationObserver((mutations) => { - const hasNewArticles = mutations.some((mutation) => - Array.from(mutation.addedNodes).some( - (node) => - node instanceof HTMLElement && - (node.tagName === 'ARTICLE' || node.querySelector('article')), - ), - ); - if (hasNewArticles) { - observeFeedArticles(); - } - }); - const feedContainer = - document.querySelector('.onb-feed-stage') ?? document.body; - mutationObserver.observe(feedContainer, { - childList: true, - subtree: true, - }); - - return () => { - window.clearTimeout(timer); - mutationObserver.disconnect(); - observer.disconnect(); - }; - }, [mounted]); - // ── Parallax scroll ── useEffect(() => { const prefersReduced = window.matchMedia( @@ -246,8 +145,6 @@ export function useOnboardingAnimations(step: OnboardingStep) { return { mounted, - tagsReady, - feedVisible, heroRef, confettiParticles, isEdgeBrowser,