From a6a0bbef0b6fad91fd1476f1b0bf7cef21e98fff Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Fri, 24 Oct 2025 05:06:16 +0200 Subject: [PATCH] feat: add typewriter animation effect to hero text - Added typewriter-effect package for animated text rendering - Implemented sequential typing animation for welcome message and signature - Added client-side rendering check to prevent hydration issues - Adjusted text opacity and timing delays for better readability - Created invisible placeholder text to maintain layout during animation loading --- package-lock.json | 17 ++++++++- package.json | 3 +- src/components/Hero.jsx | 76 ++++++++++++++++++++++++++++++++++++----- yarn.lock | 12 +++++-- 4 files changed, 96 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98f7a85..30c173b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,8 @@ "react-use-measure": "^2.1.7", "slick-carousel": "^1.8.1", "swiper": "^11.1.7", - "tailwindcss": "^3.4.6" + "tailwindcss": "^3.4.6", + "typewriter-effect": "^2.22.0" }, "devDependencies": { "eslint": "^8.56.0", @@ -27468,6 +27469,20 @@ "node": ">=4.2.0" } }, + "node_modules/typewriter-effect": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/typewriter-effect/-/typewriter-effect-2.22.0.tgz", + "integrity": "sha512-01HCRYY462wT8Fxps/epwGCioZd/GMXY0aLKhFKrfJ5Xhgf54/SiDx7Oq7PoES5kGqOEAdW8FS8HYVM2WSvfhQ==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1", + "raf": "^3.4.1" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index a30ae87..67a6f06 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "react-use-measure": "^2.1.7", "slick-carousel": "^1.8.1", "swiper": "^11.1.7", - "tailwindcss": "^3.4.6" + "tailwindcss": "^3.4.6", + "typewriter-effect": "^2.22.0" }, "devDependencies": { "eslint": "^8.56.0", diff --git a/src/components/Hero.jsx b/src/components/Hero.jsx index 9858142..e438ddd 100644 --- a/src/components/Hero.jsx +++ b/src/components/Hero.jsx @@ -1,9 +1,19 @@ +'use client' + import Image from 'next/image' +import { useState, useEffect } from 'react' +import Typewriter from 'typewriter-effect' import { Button } from '@/components/Button' import { Container } from '@/components/Container' import BgNoise from '@/components/BgNoise' export function Hero() { + const [isClient, setIsClient] = useState(false) + + useEffect(() => { + setIsClient(true) + }, []) + return ( <>
@@ -37,18 +47,68 @@ export function Hero() {
-

Mon cher,

-

- If you find yourself here, you’ve survived the noise.
- Sit, let the glass sweat a little.
- The band will start soon, and perhaps someone will meet your eyes before the chorus ends.
- If not, the night still owes you a dance. +

+ Mon cher, + {isClient && ( + + { + typewriter.typeString('Mon cher,').start() + }} + /> + + )} +

+

+ Sit, let the glass sweat a little.
The band will start soon, and perhaps someone will meet your eyes before the chorus ends.
If not, the night still owes you a dance.' }} /> + {isClient && ( + + { + typewriter + .pauseFor(2000) + .typeString( + 'If you find yourself here, you’ve survived the noise.
', + ) + .pauseFor(500) + .typeString('Sit, let the glass sweat a little.
') + .pauseFor(500) + .typeString( + 'The band will start soon, and perhaps someone will meet your eyes before the chorus ends.
', + ) + .pauseFor(500) + .typeString('If not, the night still owes you a dance.') + .start() + }} + /> +
+ )} +

+

+ — M.N. + {isClient && ( + + { + typewriter + .pauseFor(17000) + .typeString('— M.N.') + .start() + }} + /> + + )}

-

— M.N.

diff --git a/yarn.lock b/yarn.lock index bd6daca..f473429 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11064,7 +11064,7 @@ react-dev-utils@^11.0.3: strip-ansi "6.0.0" text-table "0.2.0" -react-dom@*, "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^18, "react-dom@^18.0.0 || ^19.0.0", react-dom@^18.2.0, react-dom@>=16, react-dom@>=16.13, react-dom@>=16.6.0, react-dom@>=16.8.0: +react-dom@*, "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0 || ^19.0.0", react-dom@^18, "react-dom@^18.0.0 || ^19.0.0", react-dom@^18.2.0, react-dom@>=16, react-dom@>=16.13, react-dom@>=16.6.0, react-dom@>=16.8.0, react-dom@>=17.0.0: version "18.3.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -11208,7 +11208,7 @@ react-use-measure@^2.1.7: resolved "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz" integrity sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg== -react@*, "react@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", "react@^17.0.0 || ^18.0.0 || ^19.0.0", react@^18, "react@^18.0.0 || ^19.0.0", react@^18.2.0, react@^18.3.1, "react@>= 16", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16, react@>=16.13, react@>=16.6.0, react@>=16.8.0: +react@*, "react@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", "react@^17.0.0 || ^18.0.0 || ^19.0.0", react@^18, "react@^18.0.0 || ^19.0.0", react@^18.2.0, react@^18.3.1, "react@>= 16", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16, react@>=16.13, react@>=16.6.0, react@>=16.8.0, react@>=17.0.0: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -13182,6 +13182,14 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typewriter-effect@^2.22.0: + version "2.22.0" + resolved "https://registry.npmjs.org/typewriter-effect/-/typewriter-effect-2.22.0.tgz" + integrity sha512-01HCRYY462wT8Fxps/epwGCioZd/GMXY0aLKhFKrfJ5Xhgf54/SiDx7Oq7PoES5kGqOEAdW8FS8HYVM2WSvfhQ== + dependencies: + prop-types "^15.8.1" + raf "^3.4.1" + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"