diff --git a/src/package.json b/src/package.json index 84c8e10..98e31a3 100644 --- a/src/package.json +++ b/src/package.json @@ -16,6 +16,8 @@ }, "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-responsive": "^10.0.0", + "typewriter-effect": "^2.21.0" } } diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml index 6446eb6..54880a8 100644 --- a/src/pnpm-lock.yaml +++ b/src/pnpm-lock.yaml @@ -14,6 +14,12 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-responsive: + specifier: ^10.0.0 + version: 10.0.0(react@18.3.1) + typewriter-effect: + specifier: ^2.21.0 + version: 2.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@astrojs/react': specifier: ^2.3.2 @@ -762,6 +768,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + css-mediaquery@0.1.2: + resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -982,6 +991,9 @@ packages: http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} @@ -1118,6 +1130,9 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + matchmediaquery@0.4.2: + resolution: {integrity: sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==} + mdast-util-definitions@6.0.0: resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} @@ -1286,6 +1301,10 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -1338,6 +1357,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1426,6 +1448,9 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@6.5.0: resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} @@ -1436,11 +1461,23 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-responsive@10.0.0: + resolution: {integrity: sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8.0' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -1531,6 +1568,9 @@ packages: engines: {node: '>=10'} hasBin: true + shallow-equal@3.1.0: + resolution: {integrity: sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==} + sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1635,6 +1675,12 @@ packages: engines: {node: '>=14.17'} hasBin: true + typewriter-effect@2.21.0: + resolution: {integrity: sha512-Y3VL1fuJpUBj0gS4OTXBLzy1gnYTYaBuVuuO99tGNyTkkub5CXi+b/hsV7Og9fp6HlhogOwWJwgq7iXI5sQlEg==} + peerDependencies: + react: ^17.x || ^18.x + react-dom: ^17.x || ^18.x + ultrahtml@1.5.3: resolution: {integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==} @@ -2532,6 +2578,8 @@ snapshots: cookie@0.7.2: {} + css-mediaquery@0.1.2: {} + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -2790,6 +2838,8 @@ snapshots: http-cache-semantics@4.1.1: {} + hyphenate-style-name@1.1.0: {} + import-meta-resolve@4.1.0: {} is-arrayish@0.3.2: @@ -2896,6 +2946,10 @@ snapshots: markdown-table@3.0.4: {} + matchmediaquery@0.4.2: + dependencies: + css-mediaquery: 0.1.2 + mdast-util-definitions@6.0.0: dependencies: '@types/mdast': 4.0.4 @@ -3236,6 +3290,8 @@ snapshots: normalize-range@0.1.2: {} + object-assign@4.1.1: {} + object-hash@3.0.0: {} onetime@7.0.0: @@ -3296,6 +3352,8 @@ snapshots: path-parse@1.0.7: {} + performance-now@2.1.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3367,18 +3425,38 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + property-information@6.5.0: {} queue-microtask@1.2.3: {} quick-lru@5.1.1: {} + raf@3.4.1: + dependencies: + performance-now: 2.1.0 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 + react-is@16.13.1: {} + + react-responsive@10.0.0(react@18.3.1): + dependencies: + hyphenate-style-name: 1.1.0 + matchmediaquery: 0.4.2 + prop-types: 15.8.1 + react: 18.3.1 + shallow-equal: 3.1.0 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -3538,6 +3616,8 @@ snapshots: semver@7.6.3: {} + shallow-equal@3.1.0: {} + sharp@0.33.5: dependencies: color: 4.2.3 @@ -3671,6 +3751,13 @@ snapshots: typescript@5.6.3: {} + typewriter-effect@2.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + prop-types: 15.8.1 + raf: 3.4.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + ultrahtml@1.5.3: {} unified@11.0.5: diff --git a/src/src/components/footer/index.tsx b/src/src/components/footer/index.tsx index 2ca5ce4..ba17e10 100644 --- a/src/src/components/footer/index.tsx +++ b/src/src/components/footer/index.tsx @@ -1,7 +1,9 @@ import React from "react"; -export default function Footer() { +export default function Footer({ fixed = false }) { return ( -

Footer

+ ); } diff --git a/src/src/components/header/index.tsx b/src/src/components/header/index.tsx index e1127fe..9da30fd 100644 --- a/src/src/components/header/index.tsx +++ b/src/src/components/header/index.tsx @@ -1,7 +1,41 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; + +import { Links } from "@/components/header/links"; export default function Header() { + const [visible, setVisible] = useState(true); + const [lastScrollY, setLastScrollY] = useState(0); + + useEffect(() => { + const handleScroll = () => { + const currentScrollY = window.scrollY; + setVisible(currentScrollY < lastScrollY || currentScrollY < 10); + setLastScrollY(currentScrollY); + }; + + window.addEventListener("scroll", handleScroll); + return () => window.removeEventListener("scroll", handleScroll); + }, [lastScrollY]); + + const headerLinks = Links.map((link) => ( +
+ {link.label} +
+ )); + return ( -

Header

+
+
+ {headerLinks} +
+
+ {headerLinks} +
+
); -} +}; diff --git a/src/src/components/header/links.ts b/src/src/components/header/links.ts index 64e7291..d2e9958 100644 --- a/src/src/components/header/links.ts +++ b/src/src/components/header/links.ts @@ -6,9 +6,9 @@ interface HeaderLink { } export const Links: HeaderLink[] = [ - { id: 0, href: "/", label: "Home", color: "green" }, - { id: 1, href: "projects", label: "Projects", color: "yellow" }, - { id: 2, href: "resume", label: "Resume", color: "blue" }, - { id: 3, href: "blog", label: "Blog", color: "purple" }, - { id: 4, href: "shop", label: "Shop", color: "aqua" } + { id: 0, href: "/", label: "Home", color: "text-green" }, + { id: 1, href: "projects", label: "Projects", color: "text-yellow" }, + { id: 2, href: "resume", label: "Resume", color: "text-blue" }, + { id: 3, href: "blog", label: "Blog", color: "text-purple" }, + { id: 4, href: "shop", label: "Shop", color: "text-aqua" } ]; diff --git a/src/src/components/hero/index.tsx b/src/src/components/hero/index.tsx new file mode 100644 index 0000000..f5bb8e7 --- /dev/null +++ b/src/src/components/hero/index.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import Typewriter from "typewriter-effect"; + +interface TypewriterOptions { + autoStart: boolean; + loop: boolean; + delay: number; + deleteSpeed: number; + cursor: string; +} + +interface TypewriterInstance { + typeString: (str: string) => TypewriterInstance; + pauseFor: (ms: number) => TypewriterInstance; + deleteAll: () => TypewriterInstance; + start: () => TypewriterInstance; +} + +export default function Hero() { + const handleInit = (typewriter: TypewriterInstance): void => { + typewriter + .typeString("
Hello, I'm
Timothy Pidashev
") + .pauseFor(2500) + .deleteAll() + .start(); + + typewriter + .typeString("
I've been turning
coffee into code
since 2018!
") + .pauseFor(2500) + .deleteAll() + .start(); + + typewriter + .typeString("
Check out my blog and shop

or contact me below!
") + .pauseFor(2500) + .deleteAll() + .start(); + }; + + const typewriterOptions: TypewriterOptions = { + autoStart: true, + loop: true, + delay: 50, + deleteSpeed: 800, + cursor: '' + }; + + return ( + <> +
+
+
+ +
+
+
+ + ); +}; diff --git a/src/src/layouts/main.astro b/src/src/layouts/index.astro similarity index 93% rename from src/src/layouts/main.astro rename to src/src/layouts/index.astro index 07b9c46..bbdc466 100644 --- a/src/src/layouts/main.astro +++ b/src/src/layouts/index.astro @@ -19,6 +19,6 @@ import Footer from "@/components/footer";
-