diff --git a/package-lock.json b/package-lock.json index 889d019..381103d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@monaco-editor/react": "^4.7.0", "@uiw/codemirror-theme-vscode": "^4.21.9", "@uiw/react-codemirror": "^4.21.9", + "framer-motion": "^12.9.7", "monaco-editor": "^0.52.2", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -2676,6 +2677,33 @@ "dev": true, "license": "ISC" }, + "node_modules/framer-motion": { + "version": "12.9.7", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.9.7.tgz", + "integrity": "sha512-Eo5TYU6sEPPy82GDx32PJm++G+AkBCrzxtEQOWLnpQX896Q3LFrsYhMZ5YO5ct4wL7wyHU6hqlrpYXeexKAevg==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.9.6", + "motion-utils": "^12.9.4", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2977,6 +3005,21 @@ "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", "license": "MIT" }, + "node_modules/motion-dom": { + "version": "12.9.6", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.9.6.tgz", + "integrity": "sha512-IK9pm5zU8BIp3FCoUGF3T7AHVLVOlXxlwco/bIbcnpBtyYb2gDQhdOzUh2KSDJVjYl1MZ9vdq8tnFTTahX2lfg==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.9.4" + } + }, + "node_modules/motion-utils": { + "version": "12.9.4", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.9.4.tgz", + "integrity": "sha512-BW3I65zeM76CMsfh3kHid9ansEJk9Qvl+K5cu4DVHKGsI52n76OJ4z2CUJUV+Mn3uEP9k1JJA3tClG0ggSrRcg==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3427,6 +3470,12 @@ "node": ">=8.0" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index a8ec0eb..964af04 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@monaco-editor/react": "^4.7.0", "@uiw/codemirror-theme-vscode": "^4.21.9", "@uiw/react-codemirror": "^4.21.9", + "framer-motion": "^12.9.7", "monaco-editor": "^0.52.2", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/public/images/battlesnake.png b/public/images/battlesnake.png new file mode 100644 index 0000000..1f3206a Binary files /dev/null and b/public/images/battlesnake.png differ diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index f507995..48226b0 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -87,7 +87,7 @@ const Navbar = () => {
- BATTLESNAKE + ByteCamp
diff --git a/src/components/Services.jsx b/src/components/Services.jsx index c4e59ce..bc25437 100644 --- a/src/components/Services.jsx +++ b/src/components/Services.jsx @@ -1,17 +1,29 @@ -import React from 'react'; -import '../scss/components/Services.scss'; -import ServiceCard from '../components/ServicesCard.jsx'; +import React from "react"; +import "../scss/components/_services.scss"; +import ServiceCard from "../components/ServicesCard.jsx"; +import battlesnakeLogo from "../../public/images/battlesnake.png"; function Services() { return (
-

Develop your own algorithm to find food, stay alive, and eliminate others. Battlesnakes are controlled by a web server you deploy, running the code you write.

+

+ Develop your own algorithm to find food, stay alive, and eliminate + others. Battlesnakes are controlled by a web server you deploy, running + the code you write. +

+ } title="What is Battlesnake?" desc="Battlesnake is a competitive game where your code is the controller. All you need is a web server that responds to the Battlesnake API." - cta="What how it works" + cta="How it works?" + link="https://docs.battlesnake.com/" /> { + if (isInView) { + controls.start("visible"); + } + }, [isInView, controls]); + + // Different animation variants based on the card index for a staggered effect + const getVariants = () => { + // Alternate between slide-in directions based on index + const direction = index % 2 === 0 ? 1 : -1; + + return { + hidden: { + opacity: 0, + x: 60 * direction, + y: 20, + }, + visible: { + opacity: 1, + x: 0, + y: 0, + transition: { + type: "spring", + duration: 0.8, + delay: index * 0.2, // Stagger effect based on index + damping: 15, + stiffness: 100, + }, + }, + }; + }; -function ServiceCard({ icon, title, desc, cta }) { return ( -
-
{icon}
-

{title}

+ +
+
+ + + {icon} + + + + {title} + +

{desc}

- {cta} → -
+ + + {cta} → + + ); } diff --git a/src/scss/components/Services.scss b/src/scss/components/Services.scss deleted file mode 100644 index 563a6aa..0000000 --- a/src/scss/components/Services.scss +++ /dev/null @@ -1,26 +0,0 @@ -.services { - padding: 5rem 3rem; - // background-image: url("../../../public/images/grid-background-3.png"); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - background-attachment: fixed; - - h2 { - font-size: 2.5rem; - font-weight: 900; - text-align: center; - margin-bottom: 3rem; - color: #fff; - text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7); - background-color: rgba(0, 0, 0, 0.5); - padding: 1.5rem; - border-radius: 10px; - } - - .cards { - display: flex; - justify-content: space-between; - gap: 2rem; - } -} diff --git a/src/scss/components/ServicesCard.scss b/src/scss/components/ServicesCard.scss deleted file mode 100644 index f6a5848..0000000 --- a/src/scss/components/ServicesCard.scss +++ /dev/null @@ -1,32 +0,0 @@ -.service-card { - background-color: #fff; - border: 2px solid #000; - border-radius: 1rem; - padding: 2rem; - box-shadow: 6px 6px 0 #000; - flex: 1; - - .icon { - font-size: 2.5rem; - margin-bottom: 1rem; - } - - h3 { - font-size: 1.5rem; - font-weight: 700; - margin-bottom: 1rem; - } - - p { - font-size: 1rem; - line-height: 1.6; - margin-bottom: 1.5rem; - } - - .link { - font-weight: bold; - text-decoration: none; - color: #202020; - } - } - \ No newline at end of file diff --git a/src/scss/components/_navbar.scss b/src/scss/components/_navbar.scss index 117bb35..6c687a6 100644 --- a/src/scss/components/_navbar.scss +++ b/src/scss/components/_navbar.scss @@ -202,7 +202,7 @@ $mobile-breakpoint: 1160px; background-color: rgba($dark-bg, 0.7); &-text { - font-size: 2rem; + font-size: 1.2rem; color: white; letter-spacing: 2px; animation: neon-flicker 5s infinite alternate; diff --git a/src/scss/components/_services.scss b/src/scss/components/_services.scss new file mode 100644 index 0000000..84e78f8 --- /dev/null +++ b/src/scss/components/_services.scss @@ -0,0 +1,77 @@ +// Services.scss +$neon-pink: #ff2a6d; +$neon-blue: #05d9e8; +$neon-purple: #d300c5; +$neon-yellow: #f7f500; +$dark-bg: #0d0221; +$cyber-black: #1a1a1a; + +@keyframes gridMovement { + 0% { + background-position: 0 0; + } + 100% { + background-position: 0 25px; + } +} + +.services { + padding: 5rem 3rem; + background-color: $dark-bg; + position: relative; + overflow: hidden; + + // Grid background effect + .grid-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: linear-gradient( + rgba($neon-blue, 0.1) 1px, + transparent 1px + ), + linear-gradient(90deg, rgba($neon-blue, 0.1) 1px, transparent 1px); + background-size: 25px 25px; + z-index: 0; + opacity: 0.3; + pointer-events: none; + animation: gridMovement 15s linear infinite; + } + + .services-heading { + font-size: 2rem; + font-weight: 700; + text-align: center; + margin-bottom: 4rem; + color: white; + text-shadow: 0 0 10px rgba($neon-pink, 0.5); + position: relative; + z-index: 1; + max-width: 900px; + margin-left: auto; + margin-right: auto; + + // Responsive font size + @media (max-width: 768px) { + font-size: 1.5rem; + margin-bottom: 2.5rem; + } + } + + .cards-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + position: relative; + z-index: 1; + max-width: 1200px; + margin: 0 auto; + + // Stack cards on small screens + @media (max-width: 768px) { + grid-template-columns: 1fr; + } + } +} diff --git a/src/scss/components/_servicesCard.scss b/src/scss/components/_servicesCard.scss new file mode 100644 index 0000000..8bb5ed5 --- /dev/null +++ b/src/scss/components/_servicesCard.scss @@ -0,0 +1,115 @@ +// ServicesCard.scss +$neon-pink: #ff2a6d; +$neon-blue: #05d9e8; +$neon-purple: #d300c5; +$neon-yellow: #f7f500; +$dark-bg: #0d0221; +$cyber-black: #1a1a1a; + +@keyframes scanline { + 0% { + transform: translateY(-100%); + } + 100% { + transform: translateY(100%); + } +} + +@keyframes borderGlow { + 0% { + box-shadow: 0 0 5px $neon-pink; + } + 50% { + box-shadow: 0 0 15px $neon-pink, 0 0 25px $neon-pink; + } + 100% { + box-shadow: 0 0 5px $neon-pink; + } +} + +.service-card { + position: relative; + background-color: $cyber-black; + border-radius: 4px; + padding: 2rem; + color: white; + margin-bottom: 20px; + overflow: hidden; + border: 1px solid rgba($neon-pink, 0.5); + box-shadow: 0 0 10px rgba($neon-pink, 0.3); + width: 100%; + + // The card border effect + .card-border { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + opacity: 0; + transition: opacity 0.5s ease; + } + + // Scanline effect + .card-glow { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient(90deg, transparent, $neon-pink, transparent); + animation: scanline 2s linear infinite; + opacity: 0; + transition: opacity 0.5s ease; + } + + &:hover { + animation: borderGlow 1.5s infinite; + border-color: $neon-pink; + + .card-border { + opacity: 1; + } + + .card-glow { + opacity: 0.7; + } + } + + .icon { + font-size: 2.5rem; + margin-bottom: 1rem; + transition: color 0.3s ease, text-shadow 0.3s ease; + } + + h3 { + font-size: 1.5rem; + font-weight: 700; + margin-bottom: 1rem; + letter-spacing: 1px; + color: white; + transition: color 0.3s ease, text-shadow 0.3s ease; + } + + p { + font-size: 1rem; + line-height: 1.6; + margin-bottom: 1.5rem; + color: #aaa; + } + + .link { + font-weight: bold; + text-decoration: none; + color: white; + transition: color 0.3s ease; + display: inline-block; + letter-spacing: 0.5px; + position: relative; + + &:hover { + color: $neon-pink; + } + } +}