✨ feat: add typewriter headline animation to Hero section#843
Conversation
Replaces static GradientText headline with a new HeroTypewriterHeadline component that cycles through rotating words (Bank, Financial App, Neobank, Wallet, Institution) with a blinking cursor effect. Also adds a DeFindex logo with dynamic glow animation tied to typing state. Improves Partners Slider with scale hover effect. Closes #818
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Adds an animated, rotating typewriter headline to the landing page Hero section (including cursor + logo glow state), and enhances partner logo hover interactions for a more dynamic homepage.
Changes:
- Introduces
HeroTypewriterHeadlineclient component to replace the static Hero headline with a typewriter rotation. - Updates
Heroto render the new typewriter headline and tweaks small-screen hero image positioning. - Adds a hover scale effect to partner logos in
PartnersSlider.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| apps/landing/src/components/globals/PartnersSlider.tsx | Adds hover scale styling to partner logos. |
| apps/landing/src/components/globals/HeroTypewriterHeadline.tsx | New typewriter headline component with cursor blink + logo glow tied to typing state. |
| apps/landing/src/components/globals/Hero.tsx | Swaps static headline for the new typewriter component and adjusts hero layout classes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| width={140} | ||
| height={48} | ||
| className="h-10 md:h-12 w-auto mx-auto opacity-60 hover:opacity-100 transition-opacity duration-150" | ||
| className="h-10 md:h-12 w-auto mx-auto opacity-60 hover:opacity-100 hover:transform hover:scale-110 transition-opacity duration-200" |
There was a problem hiding this comment.
The hover scale effect won’t animate because only transition-opacity is set. Also hover:transform means the transform property only exists on hover, which can make the scale-in/out janky. Prefer adding transform unconditionally and use transition-transform (or transition-all) alongside the opacity transition.
| <div className="text-center lg:text-left z-10 col-span-2 lg:col-span-2 lg:col-start-2 px-2 sm:px-4 lg:px-6 lg:mr-6 sm:h-200 w-full"> | ||
| <HeroTypewriterHeadline /> |
There was a problem hiding this comment.
sm:h-200 relies on a 200 entry in the Tailwind spacing scale, but apps/landing/tailwind.config.ts extends spacing from SPACING (xs/sm/md/...) and doesn’t define 200. This class will be ignored, so the intended layout sizing won’t apply. Use an arbitrary value (e.g., sm:h-[200px]) or add the token to the Tailwind spacing scale.
| <GradientText | ||
| as="h2" | ||
| variant="secondary" | ||
| textStroke={COLORS.dark} |
There was a problem hiding this comment.
This change removes the previous <h1> from the Hero (the brand name) and the new headline is rendered as h2, leaving the homepage without an H1 in the Hero section. For accessibility/SEO semantics, consider making the main hero headline an h1 (or add an h1 that’s visually hidden, e.g., sr-only, for the brand).
| > | ||
| {`Yield Infrastructure for every ${displayedWord}`} | ||
| <span style={{ opacity: cursorVisible ? 1 : 0 }}>|</span> | ||
| </GradientText> |
There was a problem hiding this comment.
The blinking cursor character (|) is part of the accessible text and may be announced by screen readers. Mark the cursor span as aria-hidden="true" (and optionally separate it from the main string) so assistive tech doesn’t read it as content.
| useEffect(() => { | ||
| const id = setInterval( | ||
| () => setCursorVisible((v) => !v), | ||
| CURSOR_BLINK_INTERVAL_MS, | ||
| ); | ||
| return () => clearInterval(id); | ||
| }, []); | ||
|
|
||
| useEffect(() => { | ||
| const currentWord = ROTATING_WORDS[wordIndex]; | ||
|
|
||
| if (!isDeleting && displayedWord === currentWord) { | ||
| const id = setTimeout(() => setIsDeleting(true), PAUSE_AFTER_TYPED_MS); | ||
| return () => clearTimeout(id); | ||
| } | ||
|
|
||
| if (isDeleting && displayedWord === '') { | ||
| setIsDeleting(false); | ||
| setWordIndex((i) => (i + 1) % ROTATING_WORDS.length); | ||
| return; | ||
| } | ||
|
|
||
| const delay = isDeleting ? DELETING_SPEED_MS : TYPING_SPEED_MS; | ||
| const id = setTimeout(() => { | ||
| setDisplayedWord( | ||
| isDeleting | ||
| ? currentWord.slice(0, displayedWord.length - 1) | ||
| : currentWord.slice(0, displayedWord.length + 1), | ||
| ); | ||
| }, delay); | ||
| return () => clearTimeout(id); | ||
| }, [displayedWord, isDeleting, wordIndex]); |
There was a problem hiding this comment.
The typewriter animation (interval + per-character timeouts) doesn’t respect prefers-reduced-motion. Consider disabling the typing/deleting loop (and cursor blink) when the user requests reduced motion, and render a static headline instead.
Summary
GradientTextheadline in the Hero with a newHeroTypewriterHeadlinecomponentPartnersSliderwith a scale hover effect on partner logosCloses #818
Test plan