diff --git a/package-lock.json b/package-lock.json index ab823a6..4fbe9be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "react-dom": "^18", "react-hook-form": "^7.52.1", "react-spring": "^9.7.3", + "styled-components": "^6.1.12", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", "zod": "^3.23.8" @@ -2304,6 +2305,24 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -4467,6 +4486,11 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "peer": true }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, "node_modules/@types/webxr": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.19.tgz", @@ -5414,6 +5438,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001640", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz", @@ -5872,6 +5904,24 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -11578,6 +11628,11 @@ "node": ">=8" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -12051,6 +12106,65 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "peer": true }, + "node_modules/styled-components": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz", + "integrity": "sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -12073,6 +12187,11 @@ } } }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/package.json b/package.json index bc5b852..908cd58 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react-dom": "^18", "react-hook-form": "^7.52.1", "react-spring": "^9.7.3", + "styled-components": "^6.1.12", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", "zod": "^3.23.8" diff --git a/public/profile-1.png b/public/profile-1.png new file mode 100644 index 0000000..ebf74cd Binary files /dev/null and b/public/profile-1.png differ diff --git a/public/profile-2.png b/public/profile-2.png new file mode 100644 index 0000000..e3245af Binary files /dev/null and b/public/profile-2.png differ diff --git a/public/profile-3.png b/public/profile-3.png new file mode 100644 index 0000000..a9ecc0a Binary files /dev/null and b/public/profile-3.png differ diff --git a/public/profiles.png b/public/profiles.png deleted file mode 100644 index 97894e9..0000000 Binary files a/public/profiles.png and /dev/null differ diff --git a/src/components/feature.tsx b/src/components/feature.tsx index 4095a63..e08864a 100644 --- a/src/components/feature.tsx +++ b/src/components/feature.tsx @@ -19,7 +19,7 @@ export default function Feature({

{title}

{description}

-
+
{children}
diff --git a/src/components/features.tsx b/src/components/features.tsx index eb60840..fe9ac47 100644 --- a/src/components/features.tsx +++ b/src/components/features.tsx @@ -1,8 +1,74 @@ +"use client"; + +import { useEffect, useState } from "react"; import Feature, { FeatureCard } from "./feature"; import { Button } from "./ui/button"; import TextReveal from "./ui/text-reveal"; +import styled, { css, keyframes } from "styled-components"; + +const profileColors = [ + "#F5ED97", + "#C2E3B7", + "#EEDBF9", +]; + +const enterAnimation = keyframes` + from { + opacity: 0; + transform: translate(-50%, -50%) scale(0.5); + left: 100%; + } + + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + left: 50%; + } +`; + +const exitAnimation = keyframes` + from { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + left: 50%; + } + + to { + opacity: 0; + transform: translate(-50%, -50%) scale(0.5); + left: 0; + } +`; + +const ProfileImage = styled.img<{ enter: boolean }>` + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + width: 75%; + animation: ${({ enter }: any) => enter ? css`${enterAnimation} 0.5s` : css`${exitAnimation} 0.5s`} forwards; +`; export default function Features() { + const [currentProfileColor, setCurrentProfileColor] = useState(profileColors[0]); + const [profile1Enter, setProfile1Enter] = useState(false); + const [profile2Enter, setProfile2Enter] = useState(false); + const [profile3Enter, setProfile3Enter] = useState(false); + + useEffect(() => { + let currentProfile = 0; + setProfile1Enter(true); + setCurrentProfileColor(profileColors[currentProfile]); + + const profiles = document.querySelectorAll("#profile-1, #profile-2, #profile-3"); + setInterval(() => { + currentProfile = (currentProfile + 1) % profiles.length; + setProfile1Enter(currentProfile === 0); + setProfile2Enter(currentProfile === 1); + setProfile3Enter(currentProfile === 2); + setCurrentProfileColor(profileColors[currentProfile]); + }, 3500); + }, []); return (
@@ -25,10 +91,10 @@ export default function Features() { - Profiles + color={currentProfileColor}> + + +

Want more?