updates
@ -1,120 +1,51 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
// import TextShuffler from "../../components/actions/TextShuffler";
|
|
||||||
import { about, genericScroll } from "../(utils)/animate";
|
|
||||||
|
|
||||||
export default function AboutSection() {
|
export default function AboutSection() {
|
||||||
React.useEffect(about, []);
|
|
||||||
React.useEffect(genericScroll, []);
|
|
||||||
|
|
||||||
const webDevStack = require("../(utils)/web-dev-stack.json");
|
|
||||||
const uiStack = require("../(utils)/ui-ux-stack.json");
|
|
||||||
|
|
||||||
const [targetStack, setTargetStack] = React.useState("dev");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll"
|
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll page-section"
|
||||||
// id="about-section"
|
id="about-section"
|
||||||
>
|
>
|
||||||
{/* <span
|
|
||||||
className="text-[300px] uppercase absolute whitespace-nowrap opacity-5 -mt-64 z-[-1]"
|
|
||||||
id="about-me-label"
|
|
||||||
>
|
|
||||||
About Me
|
|
||||||
</span> */}
|
|
||||||
<div className="h-44"></div>
|
<div className="h-44"></div>
|
||||||
|
|
||||||
<div className="flex flex-col xl:flex-row w-full gap-8">
|
<div className="flex flex-col xl:flex-row w-full gap-8">
|
||||||
<div className="w-full xl:w-[40%]">
|
<div className="w-full xl:w-[40%]"></div>
|
||||||
<h2>
|
|
||||||
About Me
|
|
||||||
{/* <TextShuffler textInput="About Me" /> */}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
|
<div className="w-full xl:w-[60%] px-4 md:px-6 py-2">
|
||||||
<div className="flex flex-col w-full gap-2">
|
<section className="flex flex-col items-start gap-4">
|
||||||
<div
|
<h2 className="mb-0">About Me</h2>
|
||||||
className={"p-4 cursor-pointer hover:opacity-60" + (targetStack.match(/dev/i) ? " bg-[#343680] w-full xl:w-[120%]" : " w-full border border-solid border-white/10")}
|
|
||||||
onClick={() => {
|
<span className="text-[24px]">Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me.</span>
|
||||||
setTargetStack("dev");
|
|
||||||
}}
|
<hr className="w-full my-8" />
|
||||||
|
|
||||||
|
<h3 className="m-0">_ Code Ben</h3>
|
||||||
|
<span className="text-[24px]">In the last two years I've developed from a complete designer to pro software engineer. After countless days of writing code, debugging, testing, building projects, server administration, deployment, CI/CD, etc, I've developed the most vital skill of all: adaptability. The ability to asimilate new knowledge at record pace: the tech industry moves really fast: you either keepup, or fall behind.</span>
|
||||||
|
|
||||||
|
<hr className="w-full my-8" />
|
||||||
|
|
||||||
|
<h3 className="m-0">Graphic Design</h3>
|
||||||
|
<span className="text-[24px]">
|
||||||
|
After spending about 5 years in the design industry, I've picked up a few vital concepts about UI/UX design. My design path still sips into my developer life: and I must say, it's the perfect harmony of modern tech. Some of my designs can be found on my{" "}
|
||||||
|
<a
|
||||||
|
href="https://99designs.com/profiles/tben"
|
||||||
|
target="_blank"
|
||||||
>
|
>
|
||||||
<div>Web Dev Stack</div>
|
99designs
|
||||||
</div>
|
</a>{" "}
|
||||||
<div
|
profile.
|
||||||
className={"p-4 cursor-pointer hover:opacity-60" + (targetStack.match(/design/i) ? " bg-[#343680] w-full xl:w-[120%]" : " w-full border border-solid border-white/10")}
|
|
||||||
onClick={() => {
|
|
||||||
setTargetStack("design");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div>UI/UX Stack</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span className="hero-sub-text">
|
|
||||||
Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done.
|
|
||||||
{/* <TextShuffler
|
|
||||||
textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done."
|
|
||||||
delay={500}
|
|
||||||
/> */}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<hr className="w-full my-8" />
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/about"
|
href="/about"
|
||||||
className="button"
|
className="button outlined"
|
||||||
>
|
>
|
||||||
Learn More About Me
|
Learn More About Me
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full xl:w-[60%] bg-[#343680] px-4 md:px-6 py-2">
|
|
||||||
<section>
|
|
||||||
<h3>
|
|
||||||
{targetStack?.match(/dev/i) ? "Web Dev Tech Stack" : "UI/UX tech stack"}
|
|
||||||
{/* <TextShuffler textInput={targetStack?.match(/dev/i) ? "Web Dev Tech Stack" : "UI/UX tech stack"} /> */}
|
|
||||||
</h3>
|
|
||||||
<hr />
|
|
||||||
<ul style={{ maxWidth: "800px" }}>
|
|
||||||
{targetStack?.match(/dev/i) ? (
|
|
||||||
<React.Fragment>
|
|
||||||
{webDevStack.map((item: { title: string; description: string }, index: number) => (
|
|
||||||
<li
|
|
||||||
key={index}
|
|
||||||
className="mb-4"
|
|
||||||
>
|
|
||||||
<h4 className="m-0">
|
|
||||||
{item.title}
|
|
||||||
{/* <TextShuffler textInput={item.title} /> */}
|
|
||||||
</h4>
|
|
||||||
<span className="opacity-80">
|
|
||||||
{item.description}
|
|
||||||
{/* <TextShuffler textInput={item.description} /> */}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<React.Fragment>
|
|
||||||
{uiStack.map((item: { title: string; description: string }, index: number) => (
|
|
||||||
<li
|
|
||||||
key={index}
|
|
||||||
className="mb-4"
|
|
||||||
>
|
|
||||||
<h4 className="m-0">
|
|
||||||
{item.title}
|
|
||||||
{/* <TextShuffler textInput={item.title} /> */}
|
|
||||||
</h4>
|
|
||||||
<span className="opacity-80">
|
|
||||||
{item.description}
|
|
||||||
{/* <TextShuffler textInput={item.description} /> */}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
65
app/(components)/ContactForm.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import submitContactForm from "../../functions/frontend/submitContactForm";
|
||||||
|
|
||||||
|
export default function ContactForm() {
|
||||||
|
let [success, setSuccess] = React.useState<string | null>(null);
|
||||||
|
let [loading, setLoading] = React.useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
autoComplete="on"
|
||||||
|
onSubmit={(e: any) => {
|
||||||
|
submitContactForm(e, setSuccess, setLoading);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading && <div className="flex items-center justify-center w-full h-full absolute top-0 left-0 bg-[black]/90">Sending Mail...</div>}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Your Name"
|
||||||
|
autoComplete="name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder="Your Email Address"
|
||||||
|
autoComplete="email"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<textarea
|
||||||
|
name="message"
|
||||||
|
id="contact-form-message"
|
||||||
|
cols={30}
|
||||||
|
rows={10}
|
||||||
|
placeholder="Message"
|
||||||
|
></textarea>
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
{success === "Success" && (
|
||||||
|
<div className="message-response">
|
||||||
|
Success!!!{" "}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reload
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{success === "Failed" && (
|
||||||
|
<div className="message-response failed">
|
||||||
|
Failed{" "}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reload
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
24
app/(components)/ContactMeSection.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import ContactForm from "./ContactForm";
|
||||||
|
|
||||||
|
export default function ContactMeSection() {
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl w-full flex items-start page-section">
|
||||||
|
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]">
|
||||||
|
<div className="h-[150px]"></div>
|
||||||
|
|
||||||
|
<h2 className="m-0">Why So Serious?</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
|
||||||
|
<span className="text-[24px]">Let's talk.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-start w-full">
|
||||||
|
<ContactForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -2,73 +2,121 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
// import TextShuffler from "../../components/actions/TextShuffler";
|
import { gsap } from "gsap";
|
||||||
import { hero } from "../(utils)/animate";
|
import { TextPlugin } from "gsap/all";
|
||||||
|
gsap.registerPlugin(TextPlugin);
|
||||||
|
|
||||||
export default function Hero() {
|
export default function Hero() {
|
||||||
React.useEffect(hero, []);
|
/**
|
||||||
|
* Handle animations on page load
|
||||||
|
*/
|
||||||
|
React.useEffect(() => {
|
||||||
|
const tl = gsap.timeline();
|
||||||
|
|
||||||
|
gsap.fromTo(
|
||||||
|
"#hero-sub-text",
|
||||||
|
{
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opacity: 1,
|
||||||
|
delay: 0.5,
|
||||||
|
duration: 1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
gsap.to("#hero-text", {
|
||||||
|
text: "I'm Benjamin Toby, a Software Engineer and UI/UX expert.",
|
||||||
|
delay: 1,
|
||||||
|
duration: 2,
|
||||||
|
ease: "none",
|
||||||
|
});
|
||||||
|
gsap.fromTo(
|
||||||
|
".hero-button-link",
|
||||||
|
{
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
opacity: 1,
|
||||||
|
delay: 3,
|
||||||
|
duration: 1,
|
||||||
|
stagger: 0.5,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render component
|
||||||
|
*/
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
<div className="flex flex-col xl:flex-row items-center gap-0 justify-center -mt-[80px] xl:-mt-[160px] w-full relative pt-[500px] md:pt-0">
|
className="flex flex-col xl:flex-row items-center gap-0 justify-center -mt-[80px] xl:-mt-[160px] w-full relative pt-[500px] xl:pt-0 page-section"
|
||||||
<div
|
id="hero-content-wrapper"
|
||||||
className="rounded-full max-w-[450px] absolute md:relative top-0 flex items-center md:items-start justify-center mr-0 xl:-mr-14 bg-[#3e3f9c] overflow-hidden"
|
>
|
||||||
id="main-image"
|
<div
|
||||||
>
|
className="rounded-full max-w-[450px] absolute md:relative flex items-center md:items-start justify-center mr-0 xl:-mr-14 bg-[#3e3f9c] overflow-hidden"
|
||||||
<Image
|
id="main-image"
|
||||||
src="/images/my-photo.png"
|
style={{
|
||||||
// fill
|
transform: "scale(1.2)",
|
||||||
width={500}
|
}}
|
||||||
height={562}
|
>
|
||||||
objectFit="contain"
|
<img
|
||||||
alt="Benjamin Toby Image"
|
src="/images/my-photo-stroked.png"
|
||||||
className="contrast-[140%] grayscale mt-20 flex items-center justify-center"
|
width={500}
|
||||||
/>
|
height={562}
|
||||||
</div>
|
alt="Benjamin Toby Image"
|
||||||
|
className="contrast-[140%] grayscale mt-20 flex items-center justify-center object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="max-w-2xl relative z-10 mt-10 w-full"
|
className="max-w-2xl relative z-10 mt-10 w-full"
|
||||||
id="hero-text-section"
|
id="hero-text-section"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
id="hero-sub-text"
|
||||||
|
className="uppercase bg-primary_light text-[black]/80 font-bold px-3 py-1 tracking-wide"
|
||||||
>
|
>
|
||||||
<h1
|
Howdy Tech Enthusiasts
|
||||||
className="text-4xl md:text-5xl leading-snug"
|
</span>
|
||||||
id="hero-text"
|
<div className="h-4"></div>
|
||||||
|
<h1
|
||||||
|
className="text-5xl md:text-[58px] leading-[1.1] gsap-text h-auto xl:h-[220px]"
|
||||||
|
id="hero-text"
|
||||||
|
></h1>
|
||||||
|
|
||||||
|
<div className="h-10 flex xl:hidden"></div>
|
||||||
|
|
||||||
|
<div className="gap-4 flex items-center flex-wrap">
|
||||||
|
<a
|
||||||
|
href="https://www.linkedin.com/in/benjamin-toby/"
|
||||||
|
target="_blank"
|
||||||
|
className="button grow hero-button-link"
|
||||||
>
|
>
|
||||||
I'm Benjamin Toby, a Software Engineer and UI/UX expert
|
Linkedin
|
||||||
{/* Fullstack developer, UI/UX designer, Software Engineer, welcome */}
|
</a>
|
||||||
{/* <TextShuffler
|
<a
|
||||||
textInput="I'm Benjamin Toby, a Software Engineer and UI/UX expert"
|
href="/contact"
|
||||||
delay={500}
|
style={{
|
||||||
/> */}
|
backgroundColor: "transparent",
|
||||||
</h1>
|
color: "white",
|
||||||
|
border: "2px solid white",
|
||||||
<div className="gap-4 flex items-center flex-wrap">
|
}}
|
||||||
<a
|
className="button grow hero-button-link"
|
||||||
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
|
>
|
||||||
download={true}
|
Contact Me
|
||||||
className="button grow"
|
</a>
|
||||||
>
|
<a
|
||||||
See my resume
|
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
|
||||||
</a>
|
download={true}
|
||||||
<a
|
className="button grow hero-button-link"
|
||||||
href="https://www.linkedin.com/in/benjamin-toby/"
|
style={{
|
||||||
target="_blank"
|
backgroundColor: "transparent",
|
||||||
className="button grow"
|
color: "white",
|
||||||
>
|
border: "2px solid white",
|
||||||
Linkedin
|
}}
|
||||||
</a>
|
>
|
||||||
<a
|
See my resume
|
||||||
href="/contact"
|
</a>
|
||||||
style={{
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
color: "white",
|
|
||||||
border: "2px solid white",
|
|
||||||
}}
|
|
||||||
className="button grow"
|
|
||||||
>
|
|
||||||
Contact Me
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
56
app/(components)/HomepageComponent.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import Hero from "./Hero";
|
||||||
|
import AboutSection from "./AboutSection";
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
import MyWorkSection from "./MyWorkSection";
|
||||||
|
import MySkillsSection from "./MySkillsSection";
|
||||||
|
import homepageTimeline from "../(utils)/homepage-timeline";
|
||||||
|
import { SiteContext } from "../../layouts/general_layout/GeneralLayout";
|
||||||
|
import ContactMeSection from "./ContactMeSection";
|
||||||
|
|
||||||
|
export type Project = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image?: string | null;
|
||||||
|
url?: string | null;
|
||||||
|
full_description?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChildProps = {
|
||||||
|
projects: Project[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function HomepageComponent({ projects }: ChildProps) {
|
||||||
|
const layoutContext: any = React.useContext(SiteContext);
|
||||||
|
|
||||||
|
if (layoutContext) layoutContext.projects = projects;
|
||||||
|
|
||||||
|
const comp = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
let ctx = gsap.context(() => {
|
||||||
|
homepageTimeline({ componentRef: comp });
|
||||||
|
}, comp);
|
||||||
|
|
||||||
|
return () => ctx.revert();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div
|
||||||
|
id="homepage-content-wrapper"
|
||||||
|
ref={comp}
|
||||||
|
className="z-0 xl:z-50 mb-32"
|
||||||
|
>
|
||||||
|
<Hero />
|
||||||
|
<AboutSection />
|
||||||
|
<MySkillsSection />
|
||||||
|
<MyWorkSection />
|
||||||
|
<ContactMeSection />
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
91
app/(components)/MySkillsSection.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function MySkillsSection() {
|
||||||
|
const webDevStack = require("../(utils)/web-dev-stack.json");
|
||||||
|
const uiStack = require("../(utils)/ui-ux-stack.json");
|
||||||
|
|
||||||
|
const [targetStack, setTargetStack] = React.useState("dev");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl w-full flex flex-col items-start pb-40 page-section">
|
||||||
|
<div className="h-[200px]"></div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]">
|
||||||
|
<h2 className="mb-0">My Skills</h2>
|
||||||
|
|
||||||
|
<span className="text-[24px]">I am well-versed in the full spectrum of design and development. While I started out as a designer, I have focused more on development in the last few years.</span>
|
||||||
|
|
||||||
|
<hr className="w-full" />
|
||||||
|
|
||||||
|
<h3 className="m-0">{targetStack?.match(/dev/i) ? "Web Dev Tech Stack" : "UI/UX tech stack"}</h3>
|
||||||
|
|
||||||
|
<div className="flex flex-col xl:flex-col items-start gap-4 w-full">
|
||||||
|
{/* <h3>My Tech Stack</h3> */}
|
||||||
|
<div className="flex flex-wrap w-full gap-2">
|
||||||
|
<div
|
||||||
|
className={"p-4 text-lg cursor-pointer grow" + (targetStack.match(/dev/i) ? " bg-[white] text-[black] font-bold" : " border border-solid border-white/10 hover:opacity-60 ")}
|
||||||
|
onClick={() => {
|
||||||
|
setTargetStack("dev");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>Web Dev Stack</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={"p-4 text-lg cursor-pointer grow" + (targetStack.match(/design/i) ? " bg-[white] text-[black] font-bold" : " border border-solid border-white/10 hover:opacity-60 ")}
|
||||||
|
onClick={() => {
|
||||||
|
setTargetStack("design");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>UI/UX Stack</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
style={{ maxWidth: "800px" }}
|
||||||
|
className="pl-6"
|
||||||
|
>
|
||||||
|
{targetStack?.match(/dev/i) ? (
|
||||||
|
<React.Fragment>
|
||||||
|
{webDevStack.map((item: { title: string; description: string }, index: number) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="mb-4"
|
||||||
|
>
|
||||||
|
<h4 className="m-0 text-[#BBE572]">
|
||||||
|
{item.title}
|
||||||
|
{/* <TextShuffler textInput={item.title} /> */}
|
||||||
|
</h4>
|
||||||
|
<span className="opacity-80">
|
||||||
|
{item.description}
|
||||||
|
{/* <TextShuffler textInput={item.description} /> */}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
{uiStack.map((item: { title: string; description: string }, index: number) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="mb-4"
|
||||||
|
>
|
||||||
|
<h4 className="m-0 text-[#BBE572]">
|
||||||
|
{item.title}
|
||||||
|
{/* <TextShuffler textInput={item.title} /> */}
|
||||||
|
</h4>
|
||||||
|
<span className="opacity-80">
|
||||||
|
{item.description}
|
||||||
|
{/* <TextShuffler textInput={item.description} /> */}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
36
app/(components)/MyWorkCard.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Project } from "./HomepageComponent";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
export default function MyWorkCard({ project }: { project: Project }) {
|
||||||
|
return (
|
||||||
|
<div className="bg-primary/10 w-full shadow-2xl shadow-[black] relative">
|
||||||
|
<Image
|
||||||
|
src={project.image || ""}
|
||||||
|
alt={project.title}
|
||||||
|
width={600}
|
||||||
|
height={300}
|
||||||
|
className="object-cover object-top w-full"
|
||||||
|
/>
|
||||||
|
<div className="p-10">
|
||||||
|
<h3 className="m-0">{project.title}</h3>
|
||||||
|
<p className="m-0">{project.description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={project.url || "#"}
|
||||||
|
className="absolute z-10 top-[340px] right-6 w-10 h-10 p-2 flex items-center justify-center rounded-full bg-[white]"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/images/external-link-dark.png"
|
||||||
|
alt="External Link Icon"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
43
app/(components)/MyWorkSection.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { SiteContext } from "../../layouts/general_layout/GeneralLayout";
|
||||||
|
import { Project } from "./HomepageComponent";
|
||||||
|
import MyWorkCard from "./MyWorkCard";
|
||||||
|
|
||||||
|
export default function MyWorkSection() {
|
||||||
|
const layoutContext: { projects: Project[] } = React.useContext(SiteContext);
|
||||||
|
const projects = layoutContext.projects;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl w-full flex flex-col xl:flex-row items-start pb-40 page-section">
|
||||||
|
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]"></div>
|
||||||
|
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]">
|
||||||
|
<div className="h-[150px]"></div>
|
||||||
|
|
||||||
|
<h2 className="m-0">Some of my work</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
|
||||||
|
<span className="text-[24px]">I've been creating and building awesome designs and applications since 2016. Here are a few picks:</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-start gap-44 mt-10">
|
||||||
|
{projects &&
|
||||||
|
projects.slice(0, 4).map((project, index) => (
|
||||||
|
<MyWorkCard
|
||||||
|
key={index}
|
||||||
|
project={project}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="/work"
|
||||||
|
className="button outlined mt-10"
|
||||||
|
>
|
||||||
|
See More
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
206
app/(utils)/homepage-timeline.ts
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
import { gsap } from "gsap";
|
||||||
|
import { ScrollTrigger, Observer } from "gsap/all";
|
||||||
|
gsap.registerPlugin(ScrollTrigger, Observer);
|
||||||
|
|
||||||
|
export default function homepageTimeline({ componentRef }: { componentRef: { current: HTMLDivElement | null } }) {
|
||||||
|
const mm = gsap.matchMedia();
|
||||||
|
|
||||||
|
const compHeight = componentRef.current?.clientHeight;
|
||||||
|
|
||||||
|
const isMobile = window.innerWidth <= 1200;
|
||||||
|
|
||||||
|
// mm.add("(min-width: 1200px)", () => {
|
||||||
|
ScrollTrigger.create({
|
||||||
|
trigger: "#homepage-content-wrapper",
|
||||||
|
start: "200px 200px",
|
||||||
|
end: "bottom 90%",
|
||||||
|
pin: isMobile ? null : "#main-image",
|
||||||
|
scrub: 1,
|
||||||
|
onUpdate: (self) => {
|
||||||
|
const scrollTop = compHeight ? self.progress * compHeight : 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Origin
|
||||||
|
*/
|
||||||
|
if (scrollTop < 300) {
|
||||||
|
gsap.to("nav", {
|
||||||
|
opacity: 1,
|
||||||
|
pointerEvents: "visible",
|
||||||
|
});
|
||||||
|
gsap.to("header", {
|
||||||
|
zIndex: 2000,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
gsap.to("nav", {
|
||||||
|
opacity: 0,
|
||||||
|
pointerEvents: "none",
|
||||||
|
});
|
||||||
|
gsap.to("header", {
|
||||||
|
zIndex: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Mobile
|
||||||
|
*/
|
||||||
|
if (isMobile && scrollTop > 100) {
|
||||||
|
gsap.to("#main-image", {
|
||||||
|
opacity: 0.1,
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
gsap.to("#main-image", {
|
||||||
|
opacity: 1,
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEnter: (self) => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Position color map
|
||||||
|
*/
|
||||||
|
const positionColorMap: {
|
||||||
|
x: number;
|
||||||
|
imgBg: string;
|
||||||
|
bodyBg: string;
|
||||||
|
duration: number;
|
||||||
|
imgSrc?: string;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
x: 0,
|
||||||
|
imgBg: "#3e3f9c",
|
||||||
|
bodyBg: "#181515",
|
||||||
|
duration: 1,
|
||||||
|
imgSrc: "/images/my-photo-stroked.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: -50,
|
||||||
|
imgBg: "#FE6847",
|
||||||
|
bodyBg: "#292a6b",
|
||||||
|
duration: 1,
|
||||||
|
imgSrc: "/images/my-photo-3.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 600,
|
||||||
|
imgBg: "#688e26",
|
||||||
|
bodyBg: "#181515",
|
||||||
|
duration: 1.2,
|
||||||
|
imgSrc: "/images/programming-laptop.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: -50,
|
||||||
|
imgBg: "#1B2CC1",
|
||||||
|
bodyBg: "#091540",
|
||||||
|
duration: 1.2,
|
||||||
|
imgSrc: "/images/projects-section-image.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: 600,
|
||||||
|
imgBg: "#ffd87d",
|
||||||
|
bodyBg: "#3e3f9c",
|
||||||
|
duration: 1.2,
|
||||||
|
imgSrc: "/images/why-so-serious.png",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate section based on scroll position
|
||||||
|
*/
|
||||||
|
const sections = document.querySelectorAll(".page-section");
|
||||||
|
|
||||||
|
function activateSection(index: number) {
|
||||||
|
const targetPositionMap = positionColorMap[index];
|
||||||
|
|
||||||
|
if (!targetPositionMap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsap.to("#main-image", {
|
||||||
|
backgroundColor: targetPositionMap.imgBg,
|
||||||
|
x: isMobile ? 0 : targetPositionMap.x,
|
||||||
|
duration: targetPositionMap.duration,
|
||||||
|
});
|
||||||
|
gsap.to("body", {
|
||||||
|
backgroundColor: targetPositionMap.bodyBg,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (targetPositionMap?.imgSrc) {
|
||||||
|
changeMainImage(targetPositionMap.imgSrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 2) {
|
||||||
|
gsap.to("#main-image img", {
|
||||||
|
filter: "none",
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
gsap.to("#main-image img", {
|
||||||
|
filter: "grayscale(100%) contrast(140%)",
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.forEach((section, index) => {
|
||||||
|
ScrollTrigger.create({
|
||||||
|
trigger: section,
|
||||||
|
start: "top 50%",
|
||||||
|
end: "bottom 50%",
|
||||||
|
onEnter: (self) => {
|
||||||
|
activateSection(index);
|
||||||
|
},
|
||||||
|
onEnterBack: (self) => {
|
||||||
|
activateSection(index);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry animation timeline
|
||||||
|
*/
|
||||||
|
const entryTimeline = gsap.timeline();
|
||||||
|
|
||||||
|
entryTimeline
|
||||||
|
.from("#main-image", {
|
||||||
|
clipPath: "circle(0)",
|
||||||
|
})
|
||||||
|
.to("#main-image", {
|
||||||
|
clipPath: "circle(100%)",
|
||||||
|
scale: 1,
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
gsap.to("#main-image", {
|
||||||
|
y: 20,
|
||||||
|
repeat: -1,
|
||||||
|
duration: 2,
|
||||||
|
yoyo: true,
|
||||||
|
ease: "sine.inOut",
|
||||||
|
});
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeMainImage(src: string) {
|
||||||
|
const mainImage: HTMLImageElement | null = document.querySelector("#main-image img");
|
||||||
|
|
||||||
|
if (mainImage) {
|
||||||
|
await gsap.to(mainImage, {
|
||||||
|
opacity: 0,
|
||||||
|
duration: 0.5,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mainImage) {
|
||||||
|
mainImage.setAttribute("src", src);
|
||||||
|
|
||||||
|
mainImage.onload = () => {
|
||||||
|
gsap.to(mainImage, {
|
||||||
|
opacity: 1,
|
||||||
|
duration: 0.5,
|
||||||
|
delay: 0.5,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"title": "HTML, CSS, Javascript",
|
|
||||||
"description": "The basics, the bedrock of all websites."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "HTML, CSS, Javascript",
|
"title": "HTML, CSS, Javascript",
|
||||||
"description": "The basics, the bedrock of all websites."
|
"description": "The basics, the bedrock of all websites."
|
||||||
|
@ -1,37 +1,22 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
// import TextShuffler from "../../../components/actions/TextShuffler";
|
|
||||||
import { appear, genericScroll } from "../../(utils)/animate";
|
|
||||||
|
|
||||||
export default function Hero() {
|
export default function Hero() {
|
||||||
React.useEffect(genericScroll, []);
|
|
||||||
React.useEffect(appear, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="-mt-10 appear">
|
<div className="-mt-10 appear">
|
||||||
<div
|
<div
|
||||||
className="flex flex-col items-start gap-0 max-w-6xl w-full relative generic-scroll"
|
className="flex flex-col items-start gap-0 max-w-6xl w-full relative generic-scroll"
|
||||||
// id="hero-text-section"
|
// id="hero-text-section"
|
||||||
>
|
>
|
||||||
<span className="text-primary-light text-lg">
|
<span className="text-primary-light text-lg">About Me</span>
|
||||||
About Me
|
|
||||||
{/* <TextShuffler textInput="About Me" /> */}
|
|
||||||
</span>
|
|
||||||
<h1
|
<h1
|
||||||
className="text-5xl leading-snug"
|
className="text-5xl leading-snug"
|
||||||
id="hero-text"
|
id="hero-text"
|
||||||
>
|
>
|
||||||
Ben of All Trades, Master of All
|
Ben of All Trades, Master of All
|
||||||
{/* <TextShuffler textInput="Ben of All Trade, Master of All" /> */}
|
|
||||||
</h1>
|
</h1>
|
||||||
<span className="hero-sub-text">
|
<span className="hero-sub-text">Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done.</span>
|
||||||
Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done.
|
|
||||||
{/* <TextShuffler
|
|
||||||
textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done."
|
|
||||||
delay={1000}
|
|
||||||
/> */}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
// import TextShuffler from "../../../components/actions/TextShuffler";
|
|
||||||
import { appear, genericScroll } from "../../(utils)/animate";
|
|
||||||
|
|
||||||
export default function MoreAboutMe() {
|
export default function MoreAboutMe() {
|
||||||
React.useEffect(genericScroll, []);
|
|
||||||
React.useEffect(appear, []);
|
|
||||||
|
|
||||||
const webDevStack = require("../../(utils)/web-dev-stack.json");
|
const webDevStack = require("../../(utils)/web-dev-stack.json");
|
||||||
const uiStack = require("../../(utils)/ui-ux-stack.json");
|
const uiStack = require("../../(utils)/ui-ux-stack.json");
|
||||||
|
|
||||||
|
@ -18,6 +18,21 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
{/* <script
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/TextPlugin.min.js"
|
||||||
|
></script> */}
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/studio-freight/lenis@1/bundled/lenis.min.js"></script>
|
||||||
|
|
||||||
<script
|
<script
|
||||||
src="/scripts/main.js"
|
src="/scripts/main.js"
|
||||||
defer
|
defer
|
||||||
|
27
app/page.tsx
@ -1,12 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Hero from "./(components)/Hero";
|
import HomepageComponent from "./(components)/HomepageComponent";
|
||||||
import AboutSection from "./(components)/AboutSection";
|
|
||||||
|
|
||||||
export default function Homepage() {
|
const datasquirel = require("datasquirel");
|
||||||
return (
|
|
||||||
<React.Fragment>
|
export const revalidate = 3600;
|
||||||
<Hero />
|
|
||||||
<AboutSection />
|
type DsqlResponse = {
|
||||||
</React.Fragment>
|
success: boolean;
|
||||||
);
|
payload: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function Homepage() {
|
||||||
|
const projects: DsqlResponse = await datasquirel.get({
|
||||||
|
db: process.env.DB_NAME,
|
||||||
|
key: process.env.DATASQUIREL_API_KEY,
|
||||||
|
query: "SELECT * FROM portfolio ORDER BY project_order ASC",
|
||||||
|
});
|
||||||
|
|
||||||
|
return <HomepageComponent projects={projects.payload} />;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
export default async function submitContactForm(e, setSuccess) {
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
|
||||||
|
export default async function submitContactForm(e: any, setSuccess: Dispatch<SetStateAction<string | null>>, setLoading: Dispatch<SetStateAction<boolean>>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
let name = e.target[0].value;
|
let name = e.target[0].value;
|
||||||
let email = e.target[1].value;
|
let email = e.target[1].value;
|
||||||
let message = e.target[2].value;
|
let message = e.target[2].value;
|
||||||
@ -31,4 +35,6 @@ export default async function submitContactForm(e, setSuccess) {
|
|||||||
} else {
|
} else {
|
||||||
setSuccess("Failed");
|
setSuccess("Failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
@ -33,6 +33,12 @@ const GeneralFooter = () => {
|
|||||||
>
|
>
|
||||||
LinkedIn
|
LinkedIn
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/BenjaminToby"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Github
|
||||||
|
</a>
|
||||||
<a href="mailto:benoti.san@gmail.com">Mail</a>
|
<a href="mailto:benoti.san@gmail.com">Mail</a>
|
||||||
<a href="tel:+2348123682346">Phone</a>
|
<a href="tel:+2348123682346">Phone</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
import { gsap } from "gsap";
|
import { gsap } from "gsap";
|
||||||
|
import HeaderNav from "./HeaderNav";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General Header for all pages
|
* General Header for all pages
|
||||||
@ -10,6 +12,8 @@ import { gsap } from "gsap";
|
|||||||
const GeneralHeader = (): React.ReactElement => {
|
const GeneralHeader = (): React.ReactElement => {
|
||||||
const links: { title: string; url: string }[] = require("./links.json");
|
const links: { title: string; url: string }[] = require("./links.json");
|
||||||
|
|
||||||
|
const [mobileNavOpen, setMobileNavOpen] = React.useState<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animate the header on mount
|
* Animate the header on mount
|
||||||
*/
|
*/
|
||||||
@ -33,10 +37,26 @@ const GeneralHeader = (): React.ReactElement => {
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (mobileNavOpen) {
|
||||||
|
gsap.to("#mobile-nave-drawer", {
|
||||||
|
opacity: 1,
|
||||||
|
pointerEvents: "all",
|
||||||
|
duration: 0.5,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
gsap.to("#mobile-nave-drawer", {
|
||||||
|
opacity: 0,
|
||||||
|
pointerEvents: "none",
|
||||||
|
duration: 0.5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [mobileNavOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
id="main-header"
|
id="main-header"
|
||||||
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 relative z-10"
|
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 z-10 fixed xl:sticky top-0"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
@ -51,21 +71,29 @@ const GeneralHeader = (): React.ReactElement => {
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav>
|
<HeaderNav />
|
||||||
{links.map((link: { title: string; url: string; download?: boolean }, index) => {
|
|
||||||
return (
|
<button
|
||||||
<a
|
className="p-2 w-14 h-14 rounded-full flex flex-col items-center justify-center gap-2 hover:bg-[white]/90 xl:hidden fixed right-10 z-10 -rotate-45"
|
||||||
key={index}
|
onClick={(e) => {
|
||||||
href={link.url}
|
e.preventDefault();
|
||||||
data-href={link.url}
|
setMobileNavOpen(!mobileNavOpen);
|
||||||
className="text-lg"
|
}}
|
||||||
download={link.download}
|
>
|
||||||
>
|
<div className="w-8 h-1 rounded-full bg-[black]"></div>
|
||||||
{link.title}
|
<div className="w-8 h-1 rounded-full bg-[black]"></div>
|
||||||
</a>
|
</button>
|
||||||
);
|
|
||||||
})}
|
<div
|
||||||
</nav>
|
id="mobile-nave-drawer"
|
||||||
|
className="flex xl:hidden flex-col fixed top-0 right-0 w-screen max-w-[400px] h-screen overflow-auto bg-[black] p-8"
|
||||||
|
style={{
|
||||||
|
opacity: 0,
|
||||||
|
pointerEvents: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HeaderNav mobile={true} />
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ import GeneralFooter from "./GeneralFooter";
|
|||||||
import { gsap } from "gsap";
|
import { gsap } from "gsap";
|
||||||
import BG from "./BG";
|
import BG from "./BG";
|
||||||
|
|
||||||
export const SiteContext = React.createContext({});
|
export const SiteContext: any = React.createContext({});
|
||||||
|
|
||||||
type GeneralLayoutProps = {
|
type GeneralLayoutProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -46,9 +46,9 @@ const GeneralLayout = ({ children }: GeneralLayoutProps): ReactElement => {
|
|||||||
<GeneralHeader />
|
<GeneralHeader />
|
||||||
<main
|
<main
|
||||||
className="flex items-center flex-col w-full"
|
className="flex items-center flex-col w-full"
|
||||||
style={{
|
// style={{
|
||||||
perspective: "800px",
|
// perspective: "800px",
|
||||||
}}
|
// }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
|
60
layouts/general_layout/HeaderNav.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { gsap } from "gsap";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General Header for all pages
|
||||||
|
*/
|
||||||
|
const HeaderNav = ({ mobile }: { mobile?: boolean }): React.ReactElement => {
|
||||||
|
const links: { title: string; url: string }[] = require("./links.json");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className={"items-start xl:items-center gap-x-6 gap-y-2 xl:flex-row" + (mobile ? " flex-col" : " hidden xl:flex")}>
|
||||||
|
{links.map((link: { title: string; url: string; download?: boolean }, index) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={index}
|
||||||
|
href={link.url}
|
||||||
|
data-href={link.url}
|
||||||
|
className="text-lg"
|
||||||
|
download={link.download}
|
||||||
|
>
|
||||||
|
{link.title}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<span className="opacity-20">|</span>
|
||||||
|
|
||||||
|
<div className="flex items-start xl:items-center gap-4 flex-col xl:flex-row">
|
||||||
|
<a
|
||||||
|
href="https://github.com/BenjaminToby"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/images/github-white.png"
|
||||||
|
alt="Benjamin Toby Github"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className="flex items-center"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://www.linkedin.com/in/benjamin-toby/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/images/linkedin-white.png"
|
||||||
|
alt="Benjamin Toby Github"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
className="flex items-center"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeaderNav;
|
205
package-lock.json
generated
@ -23,7 +23,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.4.2",
|
"@types/node": "^20.4.2",
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.2.15",
|
||||||
"tailwindcss": "^3.0.23",
|
"autoprefixer": "^10.4.14",
|
||||||
|
"postcss": "^8.4.26",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -352,6 +354,39 @@
|
|||||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/autoprefixer": {
|
||||||
|
"version": "10.4.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
||||||
|
"integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"browserslist": "^4.21.5",
|
||||||
|
"caniuse-lite": "^1.0.30001464",
|
||||||
|
"fraction.js": "^4.2.0",
|
||||||
|
"normalize-range": "^0.1.2",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"postcss-value-parser": "^4.2.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"autoprefixer": "bin/autoprefixer"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "0.27.2",
|
"version": "0.27.2",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||||
@ -398,6 +433,38 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/browserslist": {
|
||||||
|
"version": "4.21.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
|
||||||
|
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"caniuse-lite": "^1.0.30001503",
|
||||||
|
"electron-to-chromium": "^1.4.431",
|
||||||
|
"node-releases": "^2.0.12",
|
||||||
|
"update-browserslist-db": "^1.0.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"browserslist": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/busboy": {
|
"node_modules/busboy": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
@ -664,6 +731,12 @@
|
|||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/electron-to-chromium": {
|
||||||
|
"version": "1.4.466",
|
||||||
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.466.tgz",
|
||||||
|
"integrity": "sha512-TSkRvbXRXD8BwhcGlZXDsbI2lRoP8dvqR7LQnqQNk9KxXBc4tG8O+rTuXgTyIpEdiqSGKEBSqrxdqEntnjNncA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@ -675,6 +748,15 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/escalade": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
@ -772,6 +854,19 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fraction.js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/infusion"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@ -1178,6 +1273,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next/node_modules/postcss": {
|
||||||
|
"version": "8.4.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||||
|
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.4",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-releases": {
|
||||||
|
"version": "2.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||||
|
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "6.9.3",
|
"version": "6.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.3.tgz",
|
||||||
@ -1195,6 +1319,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/normalize-range": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@ -1302,9 +1435,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.14",
|
"version": "8.4.26",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz",
|
||||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
"integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1313,10 +1446,14 @@
|
|||||||
{
|
{
|
||||||
"type": "tidelift",
|
"type": "tidelift",
|
||||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.6",
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
},
|
},
|
||||||
@ -1650,34 +1787,6 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss/node_modules/postcss": {
|
|
||||||
"version": "8.4.26",
|
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz",
|
|
||||||
"integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==",
|
|
||||||
"dev": true,
|
|
||||||
"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.6",
|
|
||||||
"picocolors": "^1.0.0",
|
|
||||||
"source-map-js": "^1.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || >=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tailwindcss/node_modules/postcss-js": {
|
"node_modules/tailwindcss/node_modules/postcss-js": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||||
@ -1788,6 +1897,36 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/update-browserslist-db": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"picocolors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"update-browserslist-db": "cli.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"browserslist": ">= 4.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
@ -39,7 +39,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.4.2",
|
"@types/node": "^20.4.2",
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.2.15",
|
||||||
"tailwindcss": "^3.0.23",
|
"autoprefixer": "^10.4.14",
|
||||||
|
"postcss": "^8.4.26",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let info = await transporter.sendMail({
|
let info = await transporter.sendMail({
|
||||||
from: email,
|
from: process.env.OUTLOOK_EMAIL,
|
||||||
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com",
|
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com",
|
||||||
subject: "Tben.me | Client Message",
|
subject: "Tben.me | Client Message",
|
||||||
text: "Hello from tben",
|
text: "Hello from tben",
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Head from "next/head";
|
|
||||||
import TextShuffler from "../components/actions/TextShuffler";
|
|
||||||
import submitContactForm from "../functions/frontend/submitContactForm";
|
|
||||||
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
|
|
||||||
import threeJsAnimations from "../functions/frontend/threeJsAnimations";
|
|
||||||
|
|
||||||
const contact = () => {
|
|
||||||
let [success, setSuccess] = React.useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<GeneralLayout>
|
|
||||||
<Head>
|
|
||||||
<title>Contact me</title>
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Get in touch"
|
|
||||||
/>
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
<div className="flex flex-col items-start max-w-6xl w-full">
|
|
||||||
<h1>
|
|
||||||
<TextShuffler textInput="Get in touch" />
|
|
||||||
</h1>
|
|
||||||
<span className="hero-sub-text">
|
|
||||||
<TextShuffler
|
|
||||||
textInput="Have a question? Want to work together? Send me a message!"
|
|
||||||
delay={500}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<form
|
|
||||||
autoComplete="on"
|
|
||||||
onSubmit={(e) => {
|
|
||||||
submitContactForm(e, setSuccess);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Your Name"
|
|
||||||
autoComplete="name"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
placeholder="Your Email Address"
|
|
||||||
autoComplete="email"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<textarea
|
|
||||||
name="message"
|
|
||||||
id="contact-form-message"
|
|
||||||
cols="30"
|
|
||||||
rows="10"
|
|
||||||
placeholder="Message"
|
|
||||||
></textarea>
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
{success === "Success" && (
|
|
||||||
<div className="message-response">
|
|
||||||
Success!!!{" "}
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{success === "Failed" && (
|
|
||||||
<div className="message-response failed">
|
|
||||||
Failed{" "}
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
window.location.reload();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</GeneralLayout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default contact;
|
|
39
pages/contact.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import TextShuffler from "../components/actions/TextShuffler";
|
||||||
|
import submitContactForm from "../functions/frontend/submitContactForm";
|
||||||
|
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
|
||||||
|
import threeJsAnimations from "../functions/frontend/threeJsAnimations";
|
||||||
|
import ContactForm from "../app/(components)/ContactForm";
|
||||||
|
|
||||||
|
const contact = () => {
|
||||||
|
let [success, setSuccess] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GeneralLayout>
|
||||||
|
<Head>
|
||||||
|
<title>Contact me</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Get in touch"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-start max-w-6xl w-full">
|
||||||
|
<h1>
|
||||||
|
<TextShuffler textInput="Get in touch" />
|
||||||
|
</h1>
|
||||||
|
<span className="hero-sub-text">
|
||||||
|
<TextShuffler
|
||||||
|
textInput="Have a question? Want to work together? Send me a message!"
|
||||||
|
delay={500}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<ContactForm />
|
||||||
|
</div>
|
||||||
|
</GeneralLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default contact;
|
BIN
public/images/external-link-dark.png
Normal file
After Width: | Height: | Size: 983 B |
BIN
public/images/external-link.png
Normal file
After Width: | Height: | Size: 785 B |
BIN
public/images/github-white.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
public/images/linkedin-white.png
Normal file
After Width: | Height: | Size: 919 B |
BIN
public/images/my-photo-2.png
Normal file
After Width: | Height: | Size: 230 KiB |
BIN
public/images/my-photo-3.png
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
public/images/my-photo-stroked.png
Normal file
After Width: | Height: | Size: 237 KiB |
BIN
public/images/programming-laptop.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
public/images/projects-section-image.png
Normal file
After Width: | Height: | Size: 168 KiB |
BIN
public/images/why-so-serious.png
Normal file
After Width: | Height: | Size: 129 KiB |
169
public/scripts/homepage-gsap.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* Register plugins
|
||||||
|
*/
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const mm = gsap.matchMedia();
|
||||||
|
|
||||||
|
mm.add("(min-width: 1200px)", () => {
|
||||||
|
ScrollTrigger.create({
|
||||||
|
trigger: "#homepage-content-wrapper",
|
||||||
|
// toggleActions: "play none none none",
|
||||||
|
start: "top 200px",
|
||||||
|
end: "bottom 90%",
|
||||||
|
pin: "#main-image",
|
||||||
|
// markers: true,
|
||||||
|
scrub: 1,
|
||||||
|
onUpdate: (self) => {
|
||||||
|
/**
|
||||||
|
* Origin
|
||||||
|
*/
|
||||||
|
if (self.progress < 0.4) {
|
||||||
|
gsap.to("#main-image", {
|
||||||
|
backgroundColor: "#3e3f9c",
|
||||||
|
x: 0,
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
gsap.to("body", {
|
||||||
|
backgroundColor: "#181515",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 2
|
||||||
|
*/
|
||||||
|
if (self.progress > 0.4) {
|
||||||
|
gsap.to("#main-image", {
|
||||||
|
backgroundColor: "#FE6847",
|
||||||
|
x: -50,
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
gsap.to("body", {
|
||||||
|
backgroundColor: "#292a6b",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// if (self.progress > 0 && self.progress < 1) {
|
||||||
|
// gsap.to("#main-image", {
|
||||||
|
// position: "fixed",
|
||||||
|
// top: 20,
|
||||||
|
// left: 20,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
onLeave: (self) => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// gsap.to("#main-image", {
|
||||||
|
// scale: 1,
|
||||||
|
// duration: 1,
|
||||||
|
// });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry animation timeline
|
||||||
|
*/
|
||||||
|
const entryTimeline = gsap.timeline();
|
||||||
|
|
||||||
|
entryTimeline.to("#main-image", {
|
||||||
|
scale: 1,
|
||||||
|
duration: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll animation timeline
|
||||||
|
*/
|
||||||
|
// const scrollTimeline = gsap.timeline({
|
||||||
|
// scrollTrigger: {
|
||||||
|
// trigger: "#homepage-content-wrapper",
|
||||||
|
// start: "top 200px",
|
||||||
|
// end: "bottom 90%",
|
||||||
|
// scrub: 1,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// scrollTimeline
|
||||||
|
// // .to("#main-image", {
|
||||||
|
// // scale: 1,
|
||||||
|
// // duration: 1,
|
||||||
|
// // })
|
||||||
|
// .to("#main-image", {
|
||||||
|
// rotate: -10,
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates the hero section
|
||||||
|
*/
|
||||||
|
// function homepageTimeline() {
|
||||||
|
// /**
|
||||||
|
// * Animate hero section
|
||||||
|
// */
|
||||||
|
// tl.to("#main-image", {
|
||||||
|
// y: 100,
|
||||||
|
// }).to("#main-image", {
|
||||||
|
// y: -100,
|
||||||
|
// });
|
||||||
|
// tl.to("#main-image", {
|
||||||
|
// x: 300,
|
||||||
|
// scrollTrigger: {
|
||||||
|
// trigger: "#main-image",
|
||||||
|
// // toggleActions: "play none none none",
|
||||||
|
// // start: "top center",
|
||||||
|
// // end: "bottom 500px",
|
||||||
|
// // pin: "#main-image",
|
||||||
|
// markers: true,
|
||||||
|
// scrub: 1,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .to({
|
||||||
|
// scrollTrigger: {
|
||||||
|
// trigger: "#homepage-content-wrapper",
|
||||||
|
// // toggleActions: "play none none none",
|
||||||
|
// start: "top top",
|
||||||
|
// end: "bottom bottom",
|
||||||
|
// pin: "#main-image",
|
||||||
|
// markers: true,
|
||||||
|
// scrub: 1,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// .to("#main-image", {
|
||||||
|
// y: 300,
|
||||||
|
// duration: 1,
|
||||||
|
// scrollTrigger: {
|
||||||
|
// // trigger: "#homepage-content-wrapper",
|
||||||
|
// // toggleActions: "play none none none",
|
||||||
|
// start: "top 100%",
|
||||||
|
// end: "bottom 0%",
|
||||||
|
// pin: "#main-image",
|
||||||
|
// markers: true,
|
||||||
|
// scrub: 1,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// requestAnimationFrame(async () => {
|
||||||
|
// await tl.fromTo(
|
||||||
|
// "#main-image",
|
||||||
|
// {
|
||||||
|
// scale: 1.2,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// scale: 1,
|
||||||
|
// duration: 1,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// await tl.to("#main-image", {
|
||||||
|
// y: 300,
|
||||||
|
// duration: 1,
|
||||||
|
// scrollTrigger: {
|
||||||
|
// // trigger: "#main-image",
|
||||||
|
// // toggleActions: "play none none none",
|
||||||
|
// // start: "top center",
|
||||||
|
// // end: "bottom 500px",
|
||||||
|
// pin: true,
|
||||||
|
// markers: true,
|
||||||
|
// scrub: 1,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
@ -1 +1,22 @@
|
|||||||
|
console.log("main js");
|
||||||
|
|
||||||
|
const lenis = new Lenis();
|
||||||
|
|
||||||
|
lenis.on("scroll", (e) => {
|
||||||
|
// console.log(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
function raf(time) {
|
||||||
|
lenis.raf(time);
|
||||||
|
requestAnimationFrame(raf);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(raf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GSAP globals
|
||||||
|
*/
|
||||||
|
|
||||||
|
// gsap.to(".gsap-text", {
|
||||||
|
// text: "Welcome",
|
||||||
|
// });
|
||||||
|
@ -1 +0,0 @@
|
|||||||
console.log("main js");
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
html {
|
html {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
scroll-behavior: smooth;
|
/* scroll-behavior: smooth; */
|
||||||
font-family: "Cutive Mono", Helvetica;
|
font-family: "Cutive Mono", Helvetica;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@ -19,12 +19,14 @@ html {
|
|||||||
:root {
|
:root {
|
||||||
--main-color: #7174ff;
|
--main-color: #7174ff;
|
||||||
--main-color-lighter: #9d9eff;
|
--main-color-lighter: #9d9eff;
|
||||||
|
--main-color-darker: #7072c9;
|
||||||
|
--main-color-dark: #292a6b;
|
||||||
--dark-color: #181515;
|
--dark-color: #181515;
|
||||||
--sec-color-3: #688e26;
|
--sec-color-3: #688e26;
|
||||||
--sec-color-4: #adb2d3;
|
--sec-color-4: #adb2d3;
|
||||||
--sec-color-5: #c2a878;
|
--sec-color-5: #c2a878;
|
||||||
--light-color-1: rgb(64, 37, 216);
|
--light-color-1: rgb(64, 37, 216);
|
||||||
--test-color: rgb(113, 116, 255);
|
--test-color: #7174ff;
|
||||||
--transparent-white: rgba(255, 255, 255, 0.2);
|
--transparent-white: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +62,13 @@ button,
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.outlined,
|
||||||
|
.button.outlined {
|
||||||
|
border: 1px solid white;
|
||||||
|
background-color: transparent;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
@ -88,11 +97,11 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 38px;
|
font-size: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 30px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
@ -310,9 +319,15 @@ textarea {
|
|||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-response {
|
.message-response {
|
||||||
@ -407,6 +422,11 @@ html.is-animating .transition-fade {
|
|||||||
############################################################################################### */
|
############################################################################################### */
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
|
#main-image {
|
||||||
|
position: fixed;
|
||||||
|
top: 40px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 990px) {
|
@media (max-width: 990px) {
|
||||||
|
@ -110,14 +110,50 @@
|
|||||||
position: relative
|
position: relative
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom-\[20px\] {
|
||||||
|
bottom: 20px
|
||||||
|
}
|
||||||
|
|
||||||
.left-0 {
|
.left-0 {
|
||||||
left: 0px
|
left: 0px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-0 {
|
||||||
|
right: 0px
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-10 {
|
||||||
|
right: 2.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-6 {
|
||||||
|
right: 1.5rem
|
||||||
|
}
|
||||||
|
|
||||||
.top-0 {
|
.top-0 {
|
||||||
top: 0px
|
top: 0px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom-\[40px\] {
|
||||||
|
bottom: 40px
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-\[320px\] {
|
||||||
|
top: 320px
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-\[340px\] {
|
||||||
|
top: 340px
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-\[330px\] {
|
||||||
|
top: 330px
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-0 {
|
||||||
|
z-index: 0
|
||||||
|
}
|
||||||
|
|
||||||
.z-10 {
|
.z-10 {
|
||||||
z-index: 10
|
z-index: 10
|
||||||
}
|
}
|
||||||
@ -134,6 +170,11 @@
|
|||||||
margin: 0px
|
margin: 0px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.my-8 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 2rem
|
||||||
|
}
|
||||||
|
|
||||||
.-mb-4 {
|
.-mb-4 {
|
||||||
margin-bottom: -1rem
|
margin-bottom: -1rem
|
||||||
}
|
}
|
||||||
@ -142,18 +183,22 @@
|
|||||||
margin-top: -2.5rem
|
margin-top: -2.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.-mt-64 {
|
.-mt-\[80px\] {
|
||||||
margin-top: -16rem
|
margin-top: -80px
|
||||||
}
|
}
|
||||||
|
|
||||||
.-mt-\[120px\] {
|
.mb-0 {
|
||||||
margin-top: -120px
|
margin-bottom: 0px
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-2 {
|
.mb-2 {
|
||||||
margin-bottom: 0.5rem
|
margin-bottom: 0.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mb-32 {
|
||||||
|
margin-bottom: 8rem
|
||||||
|
}
|
||||||
|
|
||||||
.mb-4 {
|
.mb-4 {
|
||||||
margin-bottom: 1rem
|
margin-bottom: 1rem
|
||||||
}
|
}
|
||||||
@ -178,42 +223,90 @@
|
|||||||
margin-top: 1rem
|
margin-top: 1rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.-mt-\[80px\] {
|
|
||||||
margin-top: -80px
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex {
|
.flex {
|
||||||
display: flex
|
display: flex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-1 {
|
||||||
|
height: 0.25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-10 {
|
||||||
|
height: 2.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-14 {
|
||||||
|
height: 3.5rem
|
||||||
|
}
|
||||||
|
|
||||||
.h-20 {
|
.h-20 {
|
||||||
height: 5rem
|
height: 5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-4 {
|
||||||
|
height: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
.h-44 {
|
.h-44 {
|
||||||
height: 11rem
|
height: 11rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-\[150px\] {
|
||||||
|
height: 150px
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-\[200px\] {
|
||||||
|
height: 200px
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-auto {
|
||||||
|
height: auto
|
||||||
|
}
|
||||||
|
|
||||||
.h-full {
|
.h-full {
|
||||||
height: 100%
|
height: 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-screen {
|
||||||
|
height: 100vh
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-10 {
|
||||||
|
width: 2.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-14 {
|
||||||
|
width: 3.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-8 {
|
||||||
|
width: 2rem
|
||||||
|
}
|
||||||
|
|
||||||
.w-full {
|
.w-full {
|
||||||
width: 100%
|
width: 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-screen {
|
||||||
|
width: 100vw
|
||||||
|
}
|
||||||
|
|
||||||
.max-w-2xl {
|
.max-w-2xl {
|
||||||
max-width: 42rem
|
max-width: 42rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.max-w-3xl {
|
|
||||||
max-width: 48rem
|
|
||||||
}
|
|
||||||
|
|
||||||
.max-w-6xl {
|
.max-w-6xl {
|
||||||
max-width: 72rem
|
max-width: 72rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.max-w-\[400px\] {
|
||||||
|
max-width: 400px
|
||||||
|
}
|
||||||
|
|
||||||
.max-w-\[450px\] {
|
.max-w-\[450px\] {
|
||||||
max-width: 450px
|
max-width: 450px
|
||||||
}
|
}
|
||||||
@ -222,6 +315,15 @@
|
|||||||
flex-grow: 1
|
flex-grow: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.-rotate-45 {
|
||||||
|
--tw-rotate: -45deg;
|
||||||
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
|
||||||
|
}
|
||||||
|
|
||||||
|
.transform {
|
||||||
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))
|
||||||
|
}
|
||||||
|
|
||||||
.cursor-pointer {
|
.cursor-pointer {
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
}
|
}
|
||||||
@ -278,16 +380,33 @@
|
|||||||
gap: 1rem
|
gap: 1rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap-44 {
|
||||||
|
gap: 11rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-6 {
|
||||||
|
gap: 1.5rem
|
||||||
|
}
|
||||||
|
|
||||||
.gap-8 {
|
.gap-8 {
|
||||||
gap: 2rem
|
gap: 2rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.overflow-hidden {
|
.gap-x-6 {
|
||||||
overflow: hidden
|
-moz-column-gap: 1.5rem;
|
||||||
|
column-gap: 1.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.whitespace-nowrap {
|
.gap-y-2 {
|
||||||
white-space: nowrap
|
row-gap: 0.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.overflow-auto {
|
||||||
|
overflow: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.overflow-hidden {
|
||||||
|
overflow: hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
.rounded-full {
|
.rounded-full {
|
||||||
@ -316,15 +435,52 @@
|
|||||||
background-color: rgb(62 63 156 / var(--tw-bg-opacity))
|
background-color: rgb(62 63 156 / var(--tw-bg-opacity))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-\[black\] {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(0 0 0 / var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-\[black\]\/90 {
|
||||||
|
background-color: rgb(0 0 0 / 0.9)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-\[white\] {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
.bg-primary\/10 {
|
.bg-primary\/10 {
|
||||||
background-color: rgb(113 116 255 / 0.1)
|
background-color: rgb(113 116 255 / 0.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-primary_light {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(157 158 255 / var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-contain {
|
||||||
|
-o-object-fit: contain;
|
||||||
|
object-fit: contain
|
||||||
|
}
|
||||||
|
|
||||||
.object-cover {
|
.object-cover {
|
||||||
-o-object-fit: cover;
|
-o-object-fit: cover;
|
||||||
object-fit: cover
|
object-fit: cover
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.object-top {
|
||||||
|
-o-object-position: top;
|
||||||
|
object-position: top
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-10 {
|
||||||
|
padding: 2.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-2 {
|
||||||
|
padding: 0.5rem
|
||||||
|
}
|
||||||
|
|
||||||
.p-4 {
|
.p-4 {
|
||||||
padding: 1rem
|
padding: 1rem
|
||||||
}
|
}
|
||||||
@ -341,11 +497,21 @@
|
|||||||
padding: 150px
|
padding: 150px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.px-3 {
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
padding-right: 0.75rem
|
||||||
|
}
|
||||||
|
|
||||||
.px-4 {
|
.px-4 {
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
padding-right: 1rem
|
padding-right: 1rem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.py-1 {
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
padding-bottom: 0.25rem
|
||||||
|
}
|
||||||
|
|
||||||
.py-2 {
|
.py-2 {
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
padding-bottom: 0.5rem
|
padding-bottom: 0.5rem
|
||||||
@ -365,12 +531,8 @@
|
|||||||
padding-bottom: 10rem
|
padding-bottom: 10rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.pt-\[450px\] {
|
.pl-6 {
|
||||||
padding-top: 450px
|
padding-left: 1.5rem
|
||||||
}
|
|
||||||
|
|
||||||
.pt-\[480px\] {
|
|
||||||
padding-top: 480px
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pt-\[500px\] {
|
.pt-\[500px\] {
|
||||||
@ -382,8 +544,8 @@
|
|||||||
line-height: 1
|
line-height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-\[300px\] {
|
.text-\[24px\] {
|
||||||
font-size: 300px
|
font-size: 24px
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-base {
|
.text-base {
|
||||||
@ -401,31 +563,43 @@
|
|||||||
line-height: 1.25rem
|
line-height: 1.25rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-\[20px\] {
|
|
||||||
font-size: 20px
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-xl {
|
.text-xl {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
line-height: 1.75rem
|
line-height: 1.75rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-4xl {
|
.font-bold {
|
||||||
font-size: 2.25rem;
|
font-weight: 700
|
||||||
line-height: 2.5rem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.uppercase {
|
.uppercase {
|
||||||
text-transform: uppercase
|
text-transform: uppercase
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.leading-\[1\.1\] {
|
||||||
|
line-height: 1.1
|
||||||
|
}
|
||||||
|
|
||||||
.leading-snug {
|
.leading-snug {
|
||||||
line-height: 1.375
|
line-height: 1.375
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-primary-light {
|
.tracking-wide {
|
||||||
|
letter-spacing: 0.025em
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-\[\#BBE572\] {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(157 158 255 / var(--tw-text-opacity))
|
color: rgb(187 229 114 / var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-\[black\] {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(0 0 0 / var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-\[black\]\/80 {
|
||||||
|
color: rgb(0 0 0 / 0.8)
|
||||||
}
|
}
|
||||||
|
|
||||||
.opacity-20 {
|
.opacity-20 {
|
||||||
@ -448,6 +622,17 @@
|
|||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shadow-2xl {
|
||||||
|
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
||||||
|
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-\[black\] {
|
||||||
|
--tw-shadow-color: black;
|
||||||
|
--tw-shadow: var(--tw-shadow-colored)
|
||||||
|
}
|
||||||
|
|
||||||
.blur {
|
.blur {
|
||||||
--tw-blur: blur(8px);
|
--tw-blur: blur(8px);
|
||||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
|
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
|
||||||
@ -478,6 +663,10 @@
|
|||||||
transition-duration: 150ms
|
transition-duration: 150ms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hover\:bg-\[white\]\/90:hover {
|
||||||
|
background-color: rgb(255 255 255 / 0.9)
|
||||||
|
}
|
||||||
|
|
||||||
.hover\:opacity-60:hover {
|
.hover\:opacity-60:hover {
|
||||||
opacity: 0.6
|
opacity: 0.6
|
||||||
}
|
}
|
||||||
@ -506,17 +695,20 @@
|
|||||||
padding-bottom: 2.5rem
|
padding-bottom: 2.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.md\:pt-0 {
|
.md\:text-\[58px\] {
|
||||||
padding-top: 0px
|
font-size: 58px
|
||||||
}
|
|
||||||
|
|
||||||
.md\:text-5xl {
|
|
||||||
font-size: 3rem;
|
|
||||||
line-height: 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
|
.xl\:sticky {
|
||||||
|
position: sticky
|
||||||
|
}
|
||||||
|
|
||||||
|
.xl\:z-50 {
|
||||||
|
z-index: 50
|
||||||
|
}
|
||||||
|
|
||||||
.xl\:-mr-14 {
|
.xl\:-mr-14 {
|
||||||
margin-right: -3.5rem
|
margin-right: -3.5rem
|
||||||
}
|
}
|
||||||
@ -525,14 +717,26 @@
|
|||||||
margin-top: -160px
|
margin-top: -160px
|
||||||
}
|
}
|
||||||
|
|
||||||
.xl\:w-\[120\%\] {
|
.xl\:flex {
|
||||||
width: 120%
|
display: flex
|
||||||
|
}
|
||||||
|
|
||||||
|
.xl\:hidden {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.xl\:h-\[220px\] {
|
||||||
|
height: 220px
|
||||||
}
|
}
|
||||||
|
|
||||||
.xl\:w-\[40\%\] {
|
.xl\:w-\[40\%\] {
|
||||||
width: 40%
|
width: 40%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.xl\:w-\[50\%\] {
|
||||||
|
width: 50%
|
||||||
|
}
|
||||||
|
|
||||||
.xl\:w-\[60\%\] {
|
.xl\:w-\[60\%\] {
|
||||||
width: 60%
|
width: 60%
|
||||||
}
|
}
|
||||||
@ -544,4 +748,12 @@
|
|||||||
.xl\:flex-col {
|
.xl\:flex-col {
|
||||||
flex-direction: column
|
flex-direction: column
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.xl\:items-center {
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.xl\:pt-0 {
|
||||||
|
padding-top: 0px
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
primary: "#7174ff",
|
primary: "#7174ff",
|
||||||
"primary-light": "#9d9eff",
|
primary_light: "#9d9eff",
|
||||||
|
primary_dark: "#7072c9",
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["Graphik", "sans-serif"],
|
sans: ["Graphik", "sans-serif"],
|
||||||
|