Updates
8
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
node_modules
|
||||
.next
|
||||
node_shell
|
||||
.env
|
||||
node_modules
|
||||
.next
|
||||
node_shell
|
||||
.env
|
||||
/dump
|
38
Dockerfile
@ -1,20 +1,20 @@
|
||||
# Set Node.js version
|
||||
FROM node:16
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json and package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Expose port 3000
|
||||
EXPOSE 3000
|
||||
|
||||
# Run the app
|
||||
# Set Node.js version
|
||||
FROM node:16
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json and package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Expose port 3000
|
||||
EXPOSE 3000
|
||||
|
||||
# Run the app
|
||||
CMD ["npm", "run", "build", "&&", "npm", "start"]
|
@ -1,2 +1,2 @@
|
||||
# personal_site
|
||||
My personal site
|
||||
# personal_site
|
||||
My personal site
|
||||
|
@ -1,61 +1,61 @@
|
||||
/** # MODULE TRACE
|
||||
======================================================================
|
||||
* Detected 1 files that call this module. The files are listed below:
|
||||
======================================================================
|
||||
* `import` Statement Found in [HomepageComponent.tsx] => file:///d:\GitHub\personal_site\app\(components)\HomepageComponent.tsx
|
||||
==== MODULE TRACE END ==== */
|
||||
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function AboutSection() {
|
||||
return (
|
||||
<div
|
||||
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll page-section"
|
||||
id="about-section"
|
||||
>
|
||||
<div className="h-44"></div>
|
||||
|
||||
<div className="flex flex-col xl:flex-row w-full gap-8">
|
||||
<div className="w-full xl:w-[40%]"></div>
|
||||
|
||||
<div className="w-full xl:w-[60%] px-4 md:px-6 py-2">
|
||||
<section className="flex flex-col items-start gap-4">
|
||||
<h2 className="mb-0">About Me</h2>
|
||||
|
||||
<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>
|
||||
|
||||
<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"
|
||||
>
|
||||
99designs
|
||||
</a>{" "}
|
||||
profile.
|
||||
</span>
|
||||
|
||||
<hr className="w-full my-8" />
|
||||
|
||||
<a
|
||||
href="/about"
|
||||
className="button outlined"
|
||||
>
|
||||
Learn More About Me
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
/** # MODULE TRACE
|
||||
======================================================================
|
||||
* Detected 1 files that call this module. The files are listed below:
|
||||
======================================================================
|
||||
* `import` Statement Found in [HomepageComponent.tsx] => file:///d:\GitHub\personal_site\app\(components)\HomepageComponent.tsx
|
||||
==== MODULE TRACE END ==== */
|
||||
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function AboutSection() {
|
||||
return (
|
||||
<div
|
||||
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll page-section"
|
||||
id="about-section"
|
||||
>
|
||||
<div className="h-44"></div>
|
||||
|
||||
<div className="flex flex-col xl:flex-row w-full gap-8">
|
||||
<div className="w-full xl:w-[40%]"></div>
|
||||
|
||||
<div className="w-full xl:w-[60%] px-4 md:px-6 py-2">
|
||||
<section className="flex flex-col items-start gap-4">
|
||||
<h2 className="mb-0">About Me</h2>
|
||||
|
||||
<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>
|
||||
|
||||
<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"
|
||||
>
|
||||
99designs
|
||||
</a>{" "}
|
||||
profile.
|
||||
</span>
|
||||
|
||||
<hr className="w-full my-8" />
|
||||
|
||||
<a
|
||||
href="/about"
|
||||
className="button outlined"
|
||||
>
|
||||
Learn More About Me
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,65 +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>
|
||||
);
|
||||
}
|
||||
"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>
|
||||
);
|
||||
}
|
||||
|
@ -1,24 +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>
|
||||
);
|
||||
}
|
||||
"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>
|
||||
);
|
||||
}
|
||||
|
@ -1,124 +1,124 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import { gsap } from "gsap";
|
||||
import { TextPlugin } from "gsap/all";
|
||||
gsap.registerPlugin(TextPlugin);
|
||||
|
||||
export default function 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 (
|
||||
<div
|
||||
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"
|
||||
id="hero-content-wrapper"
|
||||
>
|
||||
<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"
|
||||
id="main-image"
|
||||
style={{
|
||||
transform: "scale(1.2)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/images/my-photo-stroked.png"
|
||||
width={500}
|
||||
height={562}
|
||||
alt="Benjamin Toby Image"
|
||||
className="contrast-[140%] grayscale mt-20 flex items-center justify-center object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="max-w-2xl relative z-10 mt-10 w-full"
|
||||
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"
|
||||
>
|
||||
Howdy Tech Enthusiasts
|
||||
</span>
|
||||
<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"
|
||||
>
|
||||
Linkedin
|
||||
</a>
|
||||
<a
|
||||
href="/contact"
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
color: "white",
|
||||
border: "2px solid white",
|
||||
}}
|
||||
className="button grow hero-button-link"
|
||||
>
|
||||
Contact Me
|
||||
</a>
|
||||
<a
|
||||
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
|
||||
download={true}
|
||||
className="button grow hero-button-link"
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
color: "white",
|
||||
border: "2px solid white",
|
||||
}}
|
||||
>
|
||||
See my resume
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import { gsap } from "gsap";
|
||||
import { TextPlugin } from "gsap/all";
|
||||
gsap.registerPlugin(TextPlugin);
|
||||
|
||||
export default function 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 (
|
||||
<div
|
||||
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"
|
||||
id="hero-content-wrapper"
|
||||
>
|
||||
<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"
|
||||
id="main-image"
|
||||
style={{
|
||||
transform: "scale(1.2)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/images/my-photo-stroked.png"
|
||||
width={500}
|
||||
height={562}
|
||||
alt="Benjamin Toby Image"
|
||||
className="contrast-[140%] grayscale mt-20 flex items-center justify-center object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="max-w-2xl relative z-10 mt-10 w-full"
|
||||
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"
|
||||
>
|
||||
Howdy Tech Enthusiasts
|
||||
</span>
|
||||
<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"
|
||||
>
|
||||
Linkedin
|
||||
</a>
|
||||
<a
|
||||
href="/contact"
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
color: "white",
|
||||
border: "2px solid white",
|
||||
}}
|
||||
className="button grow hero-button-link"
|
||||
>
|
||||
Contact Me
|
||||
</a>
|
||||
<a
|
||||
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
|
||||
download={true}
|
||||
className="button grow hero-button-link"
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
color: "white",
|
||||
border: "2px solid white",
|
||||
}}
|
||||
>
|
||||
See my resume
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,56 +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>
|
||||
);
|
||||
}
|
||||
"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>
|
||||
);
|
||||
}
|
||||
|
@ -1,91 +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>
|
||||
);
|
||||
}
|
||||
"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>
|
||||
);
|
||||
}
|
||||
|
@ -1,36 +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>
|
||||
);
|
||||
}
|
||||
"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>
|
||||
);
|
||||
}
|
||||
|
@ -1,43 +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>
|
||||
);
|
||||
}
|
||||
"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>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
@ -1,149 +1,149 @@
|
||||
import { gsap } from "gsap";
|
||||
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||
import TextPlugin from "gsap/TextPlugin";
|
||||
|
||||
/**
|
||||
* Register plugins
|
||||
*/
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
gsap.registerPlugin(TextPlugin);
|
||||
|
||||
/**
|
||||
* Animates the hero section
|
||||
*/
|
||||
export function hero() {
|
||||
/**
|
||||
* Animate hero section
|
||||
*/
|
||||
gsap.fromTo(
|
||||
"#main-image",
|
||||
{
|
||||
z: -100,
|
||||
scale: 1.2,
|
||||
},
|
||||
{
|
||||
z: 0,
|
||||
duration: 1.5,
|
||||
scale: 1,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
"#hero-text-section",
|
||||
{
|
||||
z: -100,
|
||||
scale: 1.2,
|
||||
},
|
||||
{
|
||||
z: 0,
|
||||
duration: 2.5,
|
||||
scale: 1,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
"#main-image",
|
||||
{
|
||||
y: 0,
|
||||
// scale: 1,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 1,
|
||||
},
|
||||
y: 150,
|
||||
// scale: 0.8,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
"#hero-text-section",
|
||||
{
|
||||
y: 0,
|
||||
// scale: 1,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 2,
|
||||
},
|
||||
y: 100,
|
||||
// scale: 0.8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the about section
|
||||
*/
|
||||
export function about() {
|
||||
gsap.fromTo(
|
||||
"#about-section",
|
||||
{
|
||||
y: 40,
|
||||
scale: 0.8,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 4,
|
||||
},
|
||||
y: 0,
|
||||
scale: 1.1,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.to("#about-me-label", {
|
||||
scrollTrigger: {
|
||||
scrub: 2,
|
||||
},
|
||||
y: 300,
|
||||
scale: 0.8,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the about section
|
||||
*/
|
||||
export function genericScroll() {
|
||||
gsap.fromTo(
|
||||
".generic-scroll",
|
||||
{
|
||||
y: 0,
|
||||
scale: 0.9,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 3,
|
||||
},
|
||||
y: 100,
|
||||
scale: 1,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the about section
|
||||
*/
|
||||
export function appear() {
|
||||
gsap.fromTo(
|
||||
".appear",
|
||||
{
|
||||
y: 40,
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
y: 0,
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 2,
|
||||
delay: (i, target) => {
|
||||
const datasetDelay = target?.dataset?.delay;
|
||||
|
||||
if (datasetDelay) {
|
||||
return datasetDelay;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
import { gsap } from "gsap";
|
||||
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||
import TextPlugin from "gsap/TextPlugin";
|
||||
|
||||
/**
|
||||
* Register plugins
|
||||
*/
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
gsap.registerPlugin(TextPlugin);
|
||||
|
||||
/**
|
||||
* Animates the hero section
|
||||
*/
|
||||
export function hero() {
|
||||
/**
|
||||
* Animate hero section
|
||||
*/
|
||||
gsap.fromTo(
|
||||
"#main-image",
|
||||
{
|
||||
z: -100,
|
||||
scale: 1.2,
|
||||
},
|
||||
{
|
||||
z: 0,
|
||||
duration: 1.5,
|
||||
scale: 1,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
"#hero-text-section",
|
||||
{
|
||||
z: -100,
|
||||
scale: 1.2,
|
||||
},
|
||||
{
|
||||
z: 0,
|
||||
duration: 2.5,
|
||||
scale: 1,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
"#main-image",
|
||||
{
|
||||
y: 0,
|
||||
// scale: 1,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 1,
|
||||
},
|
||||
y: 150,
|
||||
// scale: 0.8,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
"#hero-text-section",
|
||||
{
|
||||
y: 0,
|
||||
// scale: 1,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 2,
|
||||
},
|
||||
y: 100,
|
||||
// scale: 0.8,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the about section
|
||||
*/
|
||||
export function about() {
|
||||
gsap.fromTo(
|
||||
"#about-section",
|
||||
{
|
||||
y: 40,
|
||||
scale: 0.8,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 4,
|
||||
},
|
||||
y: 0,
|
||||
scale: 1.1,
|
||||
}
|
||||
);
|
||||
|
||||
gsap.to("#about-me-label", {
|
||||
scrollTrigger: {
|
||||
scrub: 2,
|
||||
},
|
||||
y: 300,
|
||||
scale: 0.8,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the about section
|
||||
*/
|
||||
export function genericScroll() {
|
||||
gsap.fromTo(
|
||||
".generic-scroll",
|
||||
{
|
||||
y: 0,
|
||||
scale: 0.9,
|
||||
},
|
||||
{
|
||||
scrollTrigger: {
|
||||
scrub: 3,
|
||||
},
|
||||
y: 100,
|
||||
scale: 1,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the about section
|
||||
*/
|
||||
export function appear() {
|
||||
gsap.fromTo(
|
||||
".appear",
|
||||
{
|
||||
y: 40,
|
||||
scale: 0.8,
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
y: 0,
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
duration: 2,
|
||||
delay: (i, target) => {
|
||||
const datasetDelay = target?.dataset?.delay;
|
||||
|
||||
if (datasetDelay) {
|
||||
return datasetDelay;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,208 +1,208 @@
|
||||
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 (!isMobile) {
|
||||
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,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (!isMobile) {
|
||||
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,30 +1,30 @@
|
||||
[
|
||||
{
|
||||
"title": "Adobe Photoshop",
|
||||
"description": "Web design, image manipulation, image compositing, and more."
|
||||
},
|
||||
{
|
||||
"title": "Adobe Illustrator",
|
||||
"description": "Vector graphic of all types"
|
||||
},
|
||||
{
|
||||
"title": "Figma",
|
||||
"description": "Web, UI, UX design."
|
||||
},
|
||||
{
|
||||
"title": "Affinity Designer",
|
||||
"description": "Vector graphics."
|
||||
},
|
||||
{
|
||||
"title": "After Effects",
|
||||
"description": "Motion graphics and animation"
|
||||
},
|
||||
{
|
||||
"title": "Adobe XD",
|
||||
"description": "UI/UX design"
|
||||
},
|
||||
{
|
||||
"title": "Webflow",
|
||||
"description": "Visual Web coding"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"title": "Adobe Photoshop",
|
||||
"description": "Web design, image manipulation, image compositing, and more."
|
||||
},
|
||||
{
|
||||
"title": "Adobe Illustrator",
|
||||
"description": "Vector graphic of all types"
|
||||
},
|
||||
{
|
||||
"title": "Figma",
|
||||
"description": "Web, UI, UX design."
|
||||
},
|
||||
{
|
||||
"title": "Affinity Designer",
|
||||
"description": "Vector graphics."
|
||||
},
|
||||
{
|
||||
"title": "After Effects",
|
||||
"description": "Motion graphics and animation"
|
||||
},
|
||||
{
|
||||
"title": "Adobe XD",
|
||||
"description": "UI/UX design"
|
||||
},
|
||||
{
|
||||
"title": "Webflow",
|
||||
"description": "Visual Web coding"
|
||||
}
|
||||
]
|
||||
|
@ -1,38 +1,38 @@
|
||||
[
|
||||
{
|
||||
"title": "HTML, CSS, Javascript",
|
||||
"description": "The basics, the bedrock of all websites."
|
||||
},
|
||||
{
|
||||
"title": "React JS",
|
||||
"description": "JavaScript library for high-performance web applications"
|
||||
},
|
||||
{
|
||||
"title": "Next JS",
|
||||
"description": "High performance React and Node js web framework for building blazing flast and performant web applications"
|
||||
},
|
||||
{
|
||||
"title": "Tailwind CSS",
|
||||
"description": "Lighting fast mobile first styling"
|
||||
},
|
||||
{
|
||||
"title": "Node JS",
|
||||
"description": "JavaScript runtime for the server. For creating backend architectures and APIs"
|
||||
},
|
||||
{
|
||||
"title": "Ubuntu Linux",
|
||||
"description": "Secure server management with ubuntu and Linux"
|
||||
},
|
||||
{
|
||||
"title": "Nginx",
|
||||
"description": "Super secure web server, reverse proxy and load balancer"
|
||||
},
|
||||
{
|
||||
"title": "MySQL",
|
||||
"description": "Tried and tested data storage, querying, and management."
|
||||
},
|
||||
{
|
||||
"title": "Git and Github",
|
||||
"description": "Version control"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"title": "HTML, CSS, Javascript",
|
||||
"description": "The basics, the bedrock of all websites."
|
||||
},
|
||||
{
|
||||
"title": "React JS",
|
||||
"description": "JavaScript library for high-performance web applications"
|
||||
},
|
||||
{
|
||||
"title": "Next JS",
|
||||
"description": "High performance React and Node js web framework for building blazing flast and performant web applications"
|
||||
},
|
||||
{
|
||||
"title": "Tailwind CSS",
|
||||
"description": "Lighting fast mobile first styling"
|
||||
},
|
||||
{
|
||||
"title": "Node JS",
|
||||
"description": "JavaScript runtime for the server. For creating backend architectures and APIs"
|
||||
},
|
||||
{
|
||||
"title": "Ubuntu Linux",
|
||||
"description": "Secure server management with ubuntu and Linux"
|
||||
},
|
||||
{
|
||||
"title": "Nginx",
|
||||
"description": "Super secure web server, reverse proxy and load balancer"
|
||||
},
|
||||
{
|
||||
"title": "MySQL",
|
||||
"description": "Tried and tested data storage, querying, and management."
|
||||
},
|
||||
{
|
||||
"title": "Git and Github",
|
||||
"description": "Version control"
|
||||
}
|
||||
]
|
||||
|
@ -1,23 +1,27 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<div className="-mt-10 appear">
|
||||
<div
|
||||
className="flex flex-col items-start gap-0 max-w-6xl w-full relative generic-scroll"
|
||||
// id="hero-text-section"
|
||||
>
|
||||
<span className="text-primary-light text-lg">About Me</span>
|
||||
<h1
|
||||
className="text-5xl leading-snug"
|
||||
id="hero-text"
|
||||
>
|
||||
Ben of All Trades, Master of All
|
||||
</h1>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<div className="-mt-10 appear">
|
||||
<div
|
||||
className="flex flex-col items-start gap-0 max-w-6xl w-full relative generic-scroll"
|
||||
// id="hero-text-section"
|
||||
>
|
||||
<span className="text-primary-light text-lg">About Me</span>
|
||||
<h1 className="text-5xl leading-snug" id="hero-text">
|
||||
I live on the bleeding edge of technology
|
||||
</h1>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,98 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function MoreAboutMe() {
|
||||
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-center pb-40 generic-scroll">
|
||||
<div className="h-20"></div>
|
||||
|
||||
<div
|
||||
className="flex flex-col w-full gap-8 appear"
|
||||
data-delay={2}
|
||||
>
|
||||
<div className="w-full">
|
||||
<h2>
|
||||
More About Me
|
||||
{/* <TextShuffler textInput="More About Me" /> */}
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col items-start gap-4">
|
||||
<div className="flex w-full gap-4">
|
||||
<div
|
||||
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/dev/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
|
||||
onClick={() => {
|
||||
setTargetStack("dev");
|
||||
}}
|
||||
>
|
||||
<div>Web Dev Stack</div>
|
||||
</div>
|
||||
<div
|
||||
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/design/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
|
||||
onClick={() => {
|
||||
setTargetStack("design");
|
||||
}}
|
||||
>
|
||||
<div>UI/UX Stack</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function MoreAboutMe() {
|
||||
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-center pb-40 generic-scroll">
|
||||
<div className="h-20"></div>
|
||||
|
||||
<div
|
||||
className="flex flex-col w-full gap-8 appear"
|
||||
data-delay={2}
|
||||
>
|
||||
<div className="w-full">
|
||||
<h2>
|
||||
More About Me
|
||||
{/* <TextShuffler textInput="More About Me" /> */}
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col items-start gap-4">
|
||||
<div className="flex w-full gap-4">
|
||||
<div
|
||||
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/dev/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
|
||||
onClick={() => {
|
||||
setTargetStack("dev");
|
||||
}}
|
||||
>
|
||||
<div>Web Dev Stack</div>
|
||||
</div>
|
||||
<div
|
||||
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/design/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
|
||||
onClick={() => {
|
||||
setTargetStack("design");
|
||||
}}
|
||||
>
|
||||
<div>UI/UX Stack</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import MoreAboutMe from "./(components)/MoreAboutMe";
|
||||
import Hero from "./(components)/Hero";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Hero />
|
||||
<MoreAboutMe />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
import React from "react";
|
||||
import MoreAboutMe from "./(components)/MoreAboutMe";
|
||||
import Hero from "./(components)/Hero";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Hero />
|
||||
<MoreAboutMe />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// export async function GET(request: Request, context?: {}) {
|
||||
// console.log(request.headers);
|
||||
|
||||
// return NextResponse.json({ name: "Benjamin Toby" });
|
||||
// }
|
||||
export async function GET(request: Request) {
|
||||
return new Response(JSON.stringify({ name: "Benjamin Toby" }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
},
|
||||
});
|
||||
}
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// export async function GET(request: Request, context?: {}) {
|
||||
// console.log(request.headers);
|
||||
|
||||
// return NextResponse.json({ name: "Benjamin Toby" });
|
||||
// }
|
||||
export async function GET(request: Request) {
|
||||
return new Response(JSON.stringify({ name: "Benjamin Toby" }), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -1,178 +1,178 @@
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
|
||||
import TextShuffler from "../../components/actions/TextShuffler";
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>{blogPost.title} | Tben.me Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={blogPost.excerpt}
|
||||
/>
|
||||
</Head>
|
||||
<GeneralLayout>
|
||||
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
|
||||
<button
|
||||
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
|
||||
onClick={(e) => {
|
||||
window.history.back();
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<h1 className="m-0">
|
||||
<TextShuffler textInput={blogPost.title} />
|
||||
</h1>
|
||||
<span className="text-lg">
|
||||
<TextShuffler textInput={blogPost.excerpt} />
|
||||
</span>
|
||||
<span className="text-base opacity-50">
|
||||
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
|
||||
dangerouslySetInnerHTML={{ __html: blogPost.body }}
|
||||
></span>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
}
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
// ## Environment processes
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## User Authentication
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Page/Site Data Data Fetching
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select * from blog_posts WHERE slug='${params?.single}'`,
|
||||
});
|
||||
|
||||
const post = postsResponse.payload[0];
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Server Props Return
|
||||
return {
|
||||
props: {
|
||||
blogPost: post,
|
||||
},
|
||||
revalidate: 3600,
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
/**
|
||||
* Data fetching
|
||||
*
|
||||
* @abstract fetch date from the server or externnal source
|
||||
*/
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select slug from blog_posts`,
|
||||
});
|
||||
|
||||
const posts: { slug: string }[] | null = postsResponse.payload;
|
||||
|
||||
const paths = posts?.map((entry) => {
|
||||
return {
|
||||
params: { single: entry.slug },
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
paths: paths ? paths : [],
|
||||
fallback: "blocking",
|
||||
};
|
||||
};
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
|
||||
import TextShuffler from "../../components/actions/TextShuffler";
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>{blogPost.title} | Tben.me Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={blogPost.excerpt}
|
||||
/>
|
||||
</Head>
|
||||
<GeneralLayout>
|
||||
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
|
||||
<button
|
||||
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
|
||||
onClick={(e) => {
|
||||
window.history.back();
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<h1 className="m-0">
|
||||
<TextShuffler textInput={blogPost.title} />
|
||||
</h1>
|
||||
<span className="text-lg">
|
||||
<TextShuffler textInput={blogPost.excerpt} />
|
||||
</span>
|
||||
<span className="text-base opacity-50">
|
||||
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
|
||||
dangerouslySetInnerHTML={{ __html: blogPost.body }}
|
||||
></span>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
}
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
// ## Environment processes
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## User Authentication
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Page/Site Data Data Fetching
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select * from blog_posts WHERE slug='${params?.single}'`,
|
||||
});
|
||||
|
||||
const post = postsResponse.payload[0];
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Server Props Return
|
||||
return {
|
||||
props: {
|
||||
blogPost: post,
|
||||
},
|
||||
revalidate: 3600,
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
/**
|
||||
* Data fetching
|
||||
*
|
||||
* @abstract fetch date from the server or externnal source
|
||||
*/
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select slug from blog_posts`,
|
||||
});
|
||||
|
||||
const posts: { slug: string }[] | null = postsResponse.payload;
|
||||
|
||||
const paths = posts?.map((entry) => {
|
||||
return {
|
||||
params: { single: entry.slug },
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
paths: paths ? paths : [],
|
||||
fallback: "blocking",
|
||||
};
|
||||
};
|
||||
|
@ -1,72 +1,72 @@
|
||||
/////////////////////////////////////////////
|
||||
//* IMPORTS
|
||||
/////////////////////////////////////////////
|
||||
import React from "react";
|
||||
import { Metadata } from "next";
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
import { headers, cookies } from "next/headers";
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//* Metadata
|
||||
/////////////////////////////////////////////
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Blog posts | Tben.me",
|
||||
description: "Tech talks and tutorials by Tben",
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//* Main Function
|
||||
/////////////////////////////////////////////
|
||||
/**
|
||||
* Blog page index
|
||||
* ==============================================================================
|
||||
*/
|
||||
export default async function BlogIndex() {
|
||||
//* Data fetching
|
||||
/////////////////////////////////////////////
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: process.env.DB_NAME,
|
||||
query: "select title,slug,excerpt,date_created from blog_posts limit 10",
|
||||
});
|
||||
|
||||
const posts = postsResponse?.success ? postsResponse.payload : [];
|
||||
try {
|
||||
const test = await fetch("http://localhost:5000/api/test");
|
||||
const result = await test.json();
|
||||
console.log(result);
|
||||
} catch (error: any) {
|
||||
console.log(error.message);
|
||||
}
|
||||
|
||||
console.log("Referer:", headers().get("referer"));
|
||||
console.log("Host:", headers().get("host"));
|
||||
// console.log(nextHeaders.cookies());
|
||||
|
||||
//* Main Function Return
|
||||
/////////////////////////////////////////////
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="flex flex-col items-start max-w-6xl w-full">
|
||||
<h1 className="mb-8">My Blog</h1>
|
||||
<div className="flex flex-col items-start w-full gap-4">
|
||||
{posts.map((post: { slug: string; title: string; excerpt: string; date_created: string }, index: number) => (
|
||||
<a
|
||||
key={index}
|
||||
href={`/blog/${post.slug}`}
|
||||
className="flex flex-col items-start gap-2 w-full hover:bg-blue-600 border border-solid border-white/20 p-8 transition-all bg-primary/10"
|
||||
>
|
||||
<h2 className="m-0">{post.title}</h2>
|
||||
<span className="opacity-80">{post.excerpt}</span>
|
||||
<span className="text-sm opacity-50">{post.date_created.substring(0, 24)}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
/** ********************************************** */
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
//* IMPORTS
|
||||
/////////////////////////////////////////////
|
||||
import React from "react";
|
||||
import { Metadata } from "next";
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
import { headers, cookies } from "next/headers";
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//* Metadata
|
||||
/////////////////////////////////////////////
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Blog posts | Tben.me",
|
||||
description: "Tech talks and tutorials by Tben",
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//* Main Function
|
||||
/////////////////////////////////////////////
|
||||
/**
|
||||
* Blog page index
|
||||
* ==============================================================================
|
||||
*/
|
||||
export default async function BlogIndex() {
|
||||
//* Data fetching
|
||||
/////////////////////////////////////////////
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: process.env.DB_NAME,
|
||||
query: "select title,slug,excerpt,date_created from blog_posts limit 10",
|
||||
});
|
||||
|
||||
const posts = postsResponse?.success ? postsResponse.payload : [];
|
||||
try {
|
||||
const test = await fetch("http://localhost:5000/api/test");
|
||||
const result = await test.json();
|
||||
console.log(result);
|
||||
} catch (error: any) {
|
||||
console.log(error.message);
|
||||
}
|
||||
|
||||
console.log("Referer:", headers().get("referer"));
|
||||
console.log("Host:", headers().get("host"));
|
||||
// console.log(nextHeaders.cookies());
|
||||
|
||||
//* Main Function Return
|
||||
/////////////////////////////////////////////
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="flex flex-col items-start max-w-6xl w-full">
|
||||
<h1 className="mb-8">My Blog</h1>
|
||||
<div className="flex flex-col items-start w-full gap-4">
|
||||
{posts.map((post: { slug: string; title: string; excerpt: string; date_created: string }, index: number) => (
|
||||
<a
|
||||
key={index}
|
||||
href={`/blog/${post.slug}`}
|
||||
className="flex flex-col items-start gap-2 w-full hover:bg-blue-600 border border-solid border-white/20 p-8 transition-all bg-primary/10"
|
||||
>
|
||||
<h2 className="m-0">{post.title}</h2>
|
||||
<span className="opacity-80">{post.excerpt}</span>
|
||||
<span className="text-sm opacity-50">{post.date_created.substring(0, 24)}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
/** ********************************************** */
|
||||
}
|
||||
|
102
app/layout.tsx
@ -1,51 +1,51 @@
|
||||
/** # MODULE TRACE
|
||||
======================================================================
|
||||
* No imports found for this Module
|
||||
==== MODULE TRACE END ==== */
|
||||
|
||||
// General imports
|
||||
import { Metadata } from "next";
|
||||
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
|
||||
|
||||
// Styles imports
|
||||
import "../styles/main.css";
|
||||
import "../styles/tw_main.css";
|
||||
|
||||
// Metadata
|
||||
export const metadata: Metadata = {
|
||||
title: "Homepage",
|
||||
description: "Software engineer, UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, Next JS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
|
||||
keywords: "UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, NextJS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
|
||||
};
|
||||
|
||||
// Main Layout Component
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<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
|
||||
src="/scripts/main.js"
|
||||
defer
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<GeneralLayout>{children}</GeneralLayout>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
/** # MODULE TRACE
|
||||
======================================================================
|
||||
* No imports found for this Module
|
||||
==== MODULE TRACE END ==== */
|
||||
|
||||
// General imports
|
||||
import { Metadata } from "next";
|
||||
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
|
||||
|
||||
// Styles imports
|
||||
import "../styles/main.css";
|
||||
import "../styles/tw_main.css";
|
||||
|
||||
// Metadata
|
||||
export const metadata: Metadata = {
|
||||
title: "Homepage",
|
||||
description: "Software engineer, UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, Next JS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
|
||||
keywords: "UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, NextJS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
|
||||
};
|
||||
|
||||
// Main Layout Component
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<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
|
||||
src="/scripts/main.js"
|
||||
defer
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<GeneralLayout>{children}</GeneralLayout>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
42
app/page.tsx
@ -1,21 +1,21 @@
|
||||
import React from "react";
|
||||
import HomepageComponent from "./(components)/HomepageComponent";
|
||||
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
export const revalidate = 3600;
|
||||
|
||||
type DsqlResponse = {
|
||||
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} />;
|
||||
}
|
||||
import React from "react";
|
||||
import HomepageComponent from "./(components)/HomepageComponent";
|
||||
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
export const revalidate = 3600;
|
||||
|
||||
type DsqlResponse = {
|
||||
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,54 +1,54 @@
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Imports
|
||||
* ------------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
/** ********************* React/Next Imports */
|
||||
import React from "react";
|
||||
import TextShuffler from "./actions/TextShuffler";
|
||||
/** ~ End React/Next Imports *************** */
|
||||
|
||||
/** ********************* Functions and Other Page Imports */
|
||||
/** ~ End Functions and Other Page Imports *************** */
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Main Component { Functional }
|
||||
* ------------------------------------------------------------------------------
|
||||
* @param {Object} props - React component props including { children }
|
||||
*
|
||||
*/
|
||||
/** ********************* Page Main Component */
|
||||
export default function PortfolioEntry({ title, description, url, image }) {
|
||||
// ## Get Contexts
|
||||
// -----------------------
|
||||
|
||||
// ## Javascript Variables
|
||||
// -----------------------
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
// -----------------------
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<div className="portfolio-entry-block">
|
||||
<h2 style={ { marginTop: "0" } }><TextShuffler textInput={ title } /></h2>
|
||||
<div className="portfolio-image-wrapper">
|
||||
<img src={ image } alt="" className="portfolio-image" />
|
||||
</div>
|
||||
<span>
|
||||
<TextShuffler textInput={ description } />
|
||||
</span>
|
||||
<div className="hero-ctas-section">
|
||||
<a href={ url } target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// -----------------------
|
||||
};
|
||||
/** ~ End Page Main Component *************** */
|
||||
|
||||
// export default Header;
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Imports
|
||||
* ------------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
/** ********************* React/Next Imports */
|
||||
import React from "react";
|
||||
import TextShuffler from "./actions/TextShuffler";
|
||||
/** ~ End React/Next Imports *************** */
|
||||
|
||||
/** ********************* Functions and Other Page Imports */
|
||||
/** ~ End Functions and Other Page Imports *************** */
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Main Component { Functional }
|
||||
* ------------------------------------------------------------------------------
|
||||
* @param {Object} props - React component props including { children }
|
||||
*
|
||||
*/
|
||||
/** ********************* Page Main Component */
|
||||
export default function PortfolioEntry({ title, description, url, image }) {
|
||||
// ## Get Contexts
|
||||
// -----------------------
|
||||
|
||||
// ## Javascript Variables
|
||||
// -----------------------
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
// -----------------------
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<div className="portfolio-entry-block">
|
||||
<h2 style={ { marginTop: "0" } }><TextShuffler textInput={ title } /></h2>
|
||||
<div className="portfolio-image-wrapper">
|
||||
<img src={ image } alt="" className="portfolio-image" />
|
||||
</div>
|
||||
<span>
|
||||
<TextShuffler textInput={ description } />
|
||||
</span>
|
||||
<div className="hero-ctas-section">
|
||||
<a href={ url } target="_blank">Visit Site</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// -----------------------
|
||||
};
|
||||
/** ~ End Page Main Component *************** */
|
||||
|
||||
// export default Header;
|
||||
|
@ -1,98 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import React, { FC, useEffect, useState, ReactElement } from "react";
|
||||
import { gsap } from "gsap";
|
||||
|
||||
type ChildProps = {
|
||||
textInput: string;
|
||||
delay?: number;
|
||||
};
|
||||
|
||||
const TextShuffler: FC<ChildProps> = ({ textInput, delay }): ReactElement => {
|
||||
let [readyState, setReadyState] = useState(false);
|
||||
|
||||
const spanRef = React.useRef<HTMLElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const spanObserver = new IntersectionObserver(
|
||||
(entries, observer) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
if (delay) {
|
||||
setTimeout(() => {
|
||||
setReadyState(true);
|
||||
}, delay);
|
||||
} else {
|
||||
setReadyState(true);
|
||||
}
|
||||
if (spanRef.current) observer.unobserve(spanRef.current);
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: "0px 0px 0px 0px",
|
||||
}
|
||||
);
|
||||
|
||||
if (spanRef.current) spanObserver.observe(spanRef.current);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!readyState) return;
|
||||
|
||||
let chars = textInput.split("");
|
||||
|
||||
let charsSpans = chars.map((char) => `<span style="opacity:0">${char}</span>`);
|
||||
|
||||
if (spanRef?.current?.innerHTML) spanRef.current.innerHTML = charsSpans.join("");
|
||||
|
||||
if (spanRef.current) {
|
||||
gsap.to(spanRef.current, {
|
||||
opacity: 1,
|
||||
duration: 1,
|
||||
});
|
||||
}
|
||||
|
||||
let textSpans: NodeList | null = spanRef.current ? spanRef.current.querySelectorAll("span") : null;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (textSpans) {
|
||||
textSpans.forEach((span) => {
|
||||
gsap.to(span, {
|
||||
opacity: 1,
|
||||
duration: Math.random() * 1.5,
|
||||
});
|
||||
});
|
||||
|
||||
textSpans.forEach((span) => {
|
||||
gsap.killTweensOf(span, "opacity");
|
||||
gsap.to(span, {
|
||||
opacity: 0,
|
||||
duration: Math.random() * 1.5,
|
||||
delay: Math.random() * 0.5,
|
||||
});
|
||||
});
|
||||
|
||||
textSpans.forEach((span) => {
|
||||
gsap.killTweensOf(span, "opacity");
|
||||
|
||||
gsap.to(span, {
|
||||
opacity: 1,
|
||||
duration: Math.random() * 1.5,
|
||||
delay: Math.random(),
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [readyState]);
|
||||
|
||||
return (
|
||||
<span
|
||||
className="shuffled-text-span"
|
||||
ref={spanRef}
|
||||
style={{ opacity: 0 }}
|
||||
>
|
||||
{textInput}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextShuffler;
|
||||
"use client";
|
||||
|
||||
import React, { FC, useEffect, useState, ReactElement } from "react";
|
||||
import { gsap } from "gsap";
|
||||
|
||||
type ChildProps = {
|
||||
textInput: string;
|
||||
delay?: number;
|
||||
};
|
||||
|
||||
const TextShuffler: FC<ChildProps> = ({ textInput, delay }): ReactElement => {
|
||||
let [readyState, setReadyState] = useState(false);
|
||||
|
||||
const spanRef = React.useRef<HTMLElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const spanObserver = new IntersectionObserver(
|
||||
(entries, observer) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
if (delay) {
|
||||
setTimeout(() => {
|
||||
setReadyState(true);
|
||||
}, delay);
|
||||
} else {
|
||||
setReadyState(true);
|
||||
}
|
||||
if (spanRef.current) observer.unobserve(spanRef.current);
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: "0px 0px 0px 0px",
|
||||
}
|
||||
);
|
||||
|
||||
if (spanRef.current) spanObserver.observe(spanRef.current);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!readyState) return;
|
||||
|
||||
let chars = textInput.split("");
|
||||
|
||||
let charsSpans = chars.map((char) => `<span style="opacity:0">${char}</span>`);
|
||||
|
||||
if (spanRef?.current?.innerHTML) spanRef.current.innerHTML = charsSpans.join("");
|
||||
|
||||
if (spanRef.current) {
|
||||
gsap.to(spanRef.current, {
|
||||
opacity: 1,
|
||||
duration: 1,
|
||||
});
|
||||
}
|
||||
|
||||
let textSpans: NodeList | null = spanRef.current ? spanRef.current.querySelectorAll("span") : null;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (textSpans) {
|
||||
textSpans.forEach((span) => {
|
||||
gsap.to(span, {
|
||||
opacity: 1,
|
||||
duration: Math.random() * 1.5,
|
||||
});
|
||||
});
|
||||
|
||||
textSpans.forEach((span) => {
|
||||
gsap.killTweensOf(span, "opacity");
|
||||
gsap.to(span, {
|
||||
opacity: 0,
|
||||
duration: Math.random() * 1.5,
|
||||
delay: Math.random() * 0.5,
|
||||
});
|
||||
});
|
||||
|
||||
textSpans.forEach((span) => {
|
||||
gsap.killTweensOf(span, "opacity");
|
||||
|
||||
gsap.to(span, {
|
||||
opacity: 1,
|
||||
duration: Math.random() * 1.5,
|
||||
delay: Math.random(),
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [readyState]);
|
||||
|
||||
return (
|
||||
<span
|
||||
className="shuffled-text-span"
|
||||
ref={spanRef}
|
||||
style={{ opacity: 0 }}
|
||||
>
|
||||
{textInput}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextShuffler;
|
||||
|
@ -1,129 +1,129 @@
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function Homepage(props) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
/** ********************* Head Items */
|
||||
let head = (
|
||||
<React.Fragment>
|
||||
<title>Showmerebates | Home</title>
|
||||
<meta name="description" content="Find great property rebates" />
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GeneralLayout head={ head } user={ props.user }>
|
||||
<main>
|
||||
<Hero />
|
||||
<HowItWorks />
|
||||
<FeaturedProperties data={ props.data } user={ props.user } />
|
||||
<AboutUsSection />
|
||||
{ !props.user && <LoginPromptPopup user={ props.user } /> }
|
||||
</main>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
* @param {Object} req - http incoming request object
|
||||
* @param {Object} res - http response object
|
||||
* @param {Object} query - queries attached to the url
|
||||
*/
|
||||
export async function getServerSideProps({ req, res, query }) {
|
||||
// ## Environment processes
|
||||
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## User Authentication
|
||||
const user = await userAuth(req, res);
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Page/Site Data Data Fetching
|
||||
let properties = await dbHandler(`
|
||||
SELECT
|
||||
ListingKeyNumeric,City,RoomsTotal,BathroomsFull,BathroomsTotalInteger,BedroomsTotal,UnparsedAddress,BuildingAreaTotal,ListPrice,PostalCode
|
||||
FROM
|
||||
utahapidata
|
||||
WHERE
|
||||
PhotosCount > 0 AND ListPrice > 0 AND BedroomsTotal > 0 LIMIT 3
|
||||
`);
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Server Props Return
|
||||
return {
|
||||
props: {
|
||||
user: user,
|
||||
data: properties,
|
||||
},
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
}
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function Homepage(props) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
/** ********************* Head Items */
|
||||
let head = (
|
||||
<React.Fragment>
|
||||
<title>Showmerebates | Home</title>
|
||||
<meta name="description" content="Find great property rebates" />
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GeneralLayout head={ head } user={ props.user }>
|
||||
<main>
|
||||
<Hero />
|
||||
<HowItWorks />
|
||||
<FeaturedProperties data={ props.data } user={ props.user } />
|
||||
<AboutUsSection />
|
||||
{ !props.user && <LoginPromptPopup user={ props.user } /> }
|
||||
</main>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
* @param {Object} req - http incoming request object
|
||||
* @param {Object} res - http response object
|
||||
* @param {Object} query - queries attached to the url
|
||||
*/
|
||||
export async function getServerSideProps({ req, res, query }) {
|
||||
// ## Environment processes
|
||||
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## User Authentication
|
||||
const user = await userAuth(req, res);
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Page/Site Data Data Fetching
|
||||
let properties = await dbHandler(`
|
||||
SELECT
|
||||
ListingKeyNumeric,City,RoomsTotal,BathroomsFull,BathroomsTotalInteger,BedroomsTotal,UnparsedAddress,BuildingAreaTotal,ListPrice,PostalCode
|
||||
FROM
|
||||
utahapidata
|
||||
WHERE
|
||||
PhotosCount > 0 AND ListPrice > 0 AND BedroomsTotal > 0 LIMIT 3
|
||||
`);
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Server Props Return
|
||||
return {
|
||||
props: {
|
||||
user: user,
|
||||
data: properties,
|
||||
},
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
}
|
||||
|
@ -1,73 +1,73 @@
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function SingleBlogPostPreset(props) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GeneralLayout head={ head } user={ props.user }>
|
||||
<main>
|
||||
<Hero />
|
||||
<HowItWorks />
|
||||
<FeaturedProperties data={ props.data } user={ props.user } />
|
||||
<AboutUsSection />
|
||||
{ !props.user && <LoginPromptPopup user={ props.user } /> }
|
||||
</main>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function SingleBlogPostPreset(props) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GeneralLayout head={ head } user={ props.user }>
|
||||
<main>
|
||||
<Hero />
|
||||
<HowItWorks />
|
||||
<FeaturedProperties data={ props.data } user={ props.user } />
|
||||
<AboutUsSection />
|
||||
{ !props.user && <LoginPromptPopup user={ props.user } /> }
|
||||
</main>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"title": "",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"title": "",
|
||||
"id": 1
|
||||
}
|
||||
]
|
||||
|
@ -1,26 +1,26 @@
|
||||
[
|
||||
{
|
||||
"title": "Showme Rebates",
|
||||
"description": "Showmerebates curates agents' rebates/cashback on properties in Utah, Texas, Florida, and Kansas states",
|
||||
"url": "https://showmerebates.com/",
|
||||
"image": "/images/showmerebates.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Summit Lending",
|
||||
"description": "Summit Lending is a mortgage broker offering competitive rates for home loans of all sorts",
|
||||
"url": "https://summitlending.com",
|
||||
"image": "/images/summit-lending.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Datasquirel",
|
||||
"description": "Datasquirel is a fast and efficient cloud-based SQL data management system",
|
||||
"url": "https://datasquirel.com",
|
||||
"image": "/images/datasquirel-img.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Homeruntoken",
|
||||
"description": "Real Estate Investing Made Easy",
|
||||
"url": "https://homeruntoken.vercel.app/",
|
||||
"image": "/images/Homeruntoken-graphic.jpg"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"title": "Showme Rebates",
|
||||
"description": "Showmerebates curates agents' rebates/cashback on properties in Utah, Texas, Florida, and Kansas states",
|
||||
"url": "https://showmerebates.com/",
|
||||
"image": "/images/showmerebates.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Summit Lending",
|
||||
"description": "Summit Lending is a mortgage broker offering competitive rates for home loans of all sorts",
|
||||
"url": "https://summitlending.com",
|
||||
"image": "/images/summit-lending.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Datasquirel",
|
||||
"description": "Datasquirel is a fast and efficient cloud-based SQL data management system",
|
||||
"url": "https://datasquirel.com",
|
||||
"image": "/images/datasquirel-img.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Homeruntoken",
|
||||
"description": "Real Estate Investing Made Easy",
|
||||
"url": "https://homeruntoken.vercel.app/",
|
||||
"image": "/images/Homeruntoken-graphic.jpg"
|
||||
}
|
||||
]
|
||||
|
@ -1,32 +1,32 @@
|
||||
[
|
||||
{
|
||||
"title": "Showme Rebates",
|
||||
"description": "Property rebates in Utah",
|
||||
"url": "https://dev.showmerebates.com/",
|
||||
"image": "/images/showmerebates.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Guaranteed software",
|
||||
"description": "software development agency",
|
||||
"url": "https://guaranteed.software/",
|
||||
"image": "/images/guaranteed.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Renition",
|
||||
"description": "Curated top quality Apparel",
|
||||
"url": "https://renition.com",
|
||||
"image": "/images/renition-graphic.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Castcord",
|
||||
"description": "Social Media web app",
|
||||
"url": "https://cast-cord.com",
|
||||
"image": "/images/castcord-graphic.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Next 7 Web Engine",
|
||||
"description": "Next 7 is an all-in-one web engine featuring a web page builder and a content management system",
|
||||
"url": "https://next7.io",
|
||||
"image": "/images/next-7-graphic-min.jpg"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"title": "Showme Rebates",
|
||||
"description": "Property rebates in Utah",
|
||||
"url": "https://dev.showmerebates.com/",
|
||||
"image": "/images/showmerebates.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Guaranteed software",
|
||||
"description": "software development agency",
|
||||
"url": "https://guaranteed.software/",
|
||||
"image": "/images/guaranteed.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Renition",
|
||||
"description": "Curated top quality Apparel",
|
||||
"url": "https://renition.com",
|
||||
"image": "/images/renition-graphic.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Castcord",
|
||||
"description": "Social Media web app",
|
||||
"url": "https://cast-cord.com",
|
||||
"image": "/images/castcord-graphic.jpg"
|
||||
},
|
||||
{
|
||||
"title": "Next 7 Web Engine",
|
||||
"description": "Next 7 is an all-in-one web engine featuring a web page builder and a content management system",
|
||||
"url": "https://next7.io",
|
||||
"image": "/images/next-7-graphic-min.jpg"
|
||||
}
|
||||
]
|
||||
|
@ -1,135 +1,135 @@
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Function
|
||||
* ==============================================================================
|
||||
* @param {Object} mailObject - foundUser if any
|
||||
*/
|
||||
/** ********************* Main API Handler */
|
||||
module.exports = async function httpFetch({ protocol, options, paradigm }) {
|
||||
if (!protocol) protocol = "http";
|
||||
|
||||
/**
|
||||
* Http(s) Response function
|
||||
*
|
||||
* @description handle http and https protocols differently
|
||||
* @param {object} response => response object
|
||||
* @param {Function} resolve => promise resolve method
|
||||
* @param {Function} reject => promise reject method
|
||||
*/
|
||||
function httpResponse(response, resolve, reject) {
|
||||
var str = "";
|
||||
|
||||
/** * Append data chunks to "str" variable
|
||||
*/
|
||||
response.on("data", function (chunk) {
|
||||
str += chunk;
|
||||
});
|
||||
|
||||
/** * the whole response has been received, so we just print it out here
|
||||
*/
|
||||
response.on("end", function () {
|
||||
resolve(JSON.parse(str));
|
||||
});
|
||||
|
||||
/** * Handle errors
|
||||
*/
|
||||
response.on("error", (err) => {
|
||||
console.log(err);
|
||||
reject("Fetch Failed");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch protocols
|
||||
*
|
||||
* @description handle http and https protocols differently
|
||||
*/
|
||||
switch (protocol) {
|
||||
case "http":
|
||||
/**
|
||||
* Handle http protocol
|
||||
*
|
||||
* @description handles fetch requests with http protocol
|
||||
* @see => handles strapi api calls only for now
|
||||
*/
|
||||
return await new Promise((resolve, reject) => {
|
||||
http.request(
|
||||
/** * Make Request
|
||||
* @abstract Make Request
|
||||
*/
|
||||
{
|
||||
...options,
|
||||
host: "localhost",
|
||||
port: "1337",
|
||||
// path: "/api/blog-posts",
|
||||
// href: "http://localhost:1337/api/blog-posts",
|
||||
headers: {
|
||||
Authorization: `bearer ${process.env.STRAPI_API_KEY_DEV}`,
|
||||
},
|
||||
},
|
||||
/** * Handle Response
|
||||
* @abstract Handle response
|
||||
*/
|
||||
(response) => {
|
||||
httpResponse(response, resolve, reject);
|
||||
}
|
||||
).end();
|
||||
});
|
||||
break;
|
||||
|
||||
case "https":
|
||||
/**
|
||||
* Handle http protocol
|
||||
*
|
||||
* @description handles fetch requests with http protocol
|
||||
* @see => handles strapi api calls only for now
|
||||
*/
|
||||
return await new Promise((resolve, reject) => {
|
||||
https.request(
|
||||
/** * Make Request
|
||||
* @abstract Make Request
|
||||
*/
|
||||
{
|
||||
...options,
|
||||
host: "cms.showmerebates.com",
|
||||
headers: {
|
||||
Authorization: `bearer ${process.env.STRAPI_API_KEY}`,
|
||||
},
|
||||
},
|
||||
/** * Handle Response
|
||||
* @abstract Handle response
|
||||
*/
|
||||
(response) => {
|
||||
httpResponse(response, resolve, reject);
|
||||
}
|
||||
).end();
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Function
|
||||
* ==============================================================================
|
||||
* @param {Object} mailObject - foundUser if any
|
||||
*/
|
||||
/** ********************* Main API Handler */
|
||||
module.exports = async function httpFetch({ protocol, options, paradigm }) {
|
||||
if (!protocol) protocol = "http";
|
||||
|
||||
/**
|
||||
* Http(s) Response function
|
||||
*
|
||||
* @description handle http and https protocols differently
|
||||
* @param {object} response => response object
|
||||
* @param {Function} resolve => promise resolve method
|
||||
* @param {Function} reject => promise reject method
|
||||
*/
|
||||
function httpResponse(response, resolve, reject) {
|
||||
var str = "";
|
||||
|
||||
/** * Append data chunks to "str" variable
|
||||
*/
|
||||
response.on("data", function (chunk) {
|
||||
str += chunk;
|
||||
});
|
||||
|
||||
/** * the whole response has been received, so we just print it out here
|
||||
*/
|
||||
response.on("end", function () {
|
||||
resolve(JSON.parse(str));
|
||||
});
|
||||
|
||||
/** * Handle errors
|
||||
*/
|
||||
response.on("error", (err) => {
|
||||
console.log(err);
|
||||
reject("Fetch Failed");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch protocols
|
||||
*
|
||||
* @description handle http and https protocols differently
|
||||
*/
|
||||
switch (protocol) {
|
||||
case "http":
|
||||
/**
|
||||
* Handle http protocol
|
||||
*
|
||||
* @description handles fetch requests with http protocol
|
||||
* @see => handles strapi api calls only for now
|
||||
*/
|
||||
return await new Promise((resolve, reject) => {
|
||||
http.request(
|
||||
/** * Make Request
|
||||
* @abstract Make Request
|
||||
*/
|
||||
{
|
||||
...options,
|
||||
host: "localhost",
|
||||
port: "1337",
|
||||
// path: "/api/blog-posts",
|
||||
// href: "http://localhost:1337/api/blog-posts",
|
||||
headers: {
|
||||
Authorization: `bearer ${process.env.STRAPI_API_KEY_DEV}`,
|
||||
},
|
||||
},
|
||||
/** * Handle Response
|
||||
* @abstract Handle response
|
||||
*/
|
||||
(response) => {
|
||||
httpResponse(response, resolve, reject);
|
||||
}
|
||||
).end();
|
||||
});
|
||||
break;
|
||||
|
||||
case "https":
|
||||
/**
|
||||
* Handle http protocol
|
||||
*
|
||||
* @description handles fetch requests with http protocol
|
||||
* @see => handles strapi api calls only for now
|
||||
*/
|
||||
return await new Promise((resolve, reject) => {
|
||||
https.request(
|
||||
/** * Make Request
|
||||
* @abstract Make Request
|
||||
*/
|
||||
{
|
||||
...options,
|
||||
host: "cms.showmerebates.com",
|
||||
headers: {
|
||||
Authorization: `bearer ${process.env.STRAPI_API_KEY}`,
|
||||
},
|
||||
},
|
||||
/** * Handle Response
|
||||
* @abstract Handle response
|
||||
*/
|
||||
(response) => {
|
||||
httpResponse(response, resolve, reject);
|
||||
}
|
||||
).end();
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
@ -1,10 +1,10 @@
|
||||
const sanitizeHtmlOptions: object = {
|
||||
allowedTags: ["b", "i", "em", "strong", "a", "p", "span", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "img", "div", "button", "pre", "code", "br"],
|
||||
allowedAttributes: {
|
||||
a: ["href"],
|
||||
img: ["src", "alt", "width", "height", "class", "style"],
|
||||
"*": ["style", "class"],
|
||||
},
|
||||
};
|
||||
|
||||
export default sanitizeHtmlOptions;
|
||||
const sanitizeHtmlOptions: object = {
|
||||
allowedTags: ["b", "i", "em", "strong", "a", "p", "span", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "img", "div", "button", "pre", "code", "br"],
|
||||
allowedAttributes: {
|
||||
a: ["href"],
|
||||
img: ["src", "alt", "width", "height", "class", "style"],
|
||||
"*": ["style", "class"],
|
||||
},
|
||||
};
|
||||
|
||||
export default sanitizeHtmlOptions;
|
||||
|
@ -1,40 +1,40 @@
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
export default async function submitContactForm(e: any, setSuccess: Dispatch<SetStateAction<string | null>>, setLoading: Dispatch<SetStateAction<boolean>>) {
|
||||
e.preventDefault();
|
||||
|
||||
setLoading(true);
|
||||
|
||||
let name = e.target[0].value;
|
||||
let email = e.target[1].value;
|
||||
let message = e.target[2].value;
|
||||
|
||||
let body = {
|
||||
name: name,
|
||||
email: email,
|
||||
message: message,
|
||||
};
|
||||
|
||||
let res = await fetch("/api/contactForm", {
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
let data = await res.json();
|
||||
|
||||
console.log(data);
|
||||
|
||||
if (data.msg === "Success") {
|
||||
setSuccess("Success");
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
setSuccess("Failed");
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
export default async function submitContactForm(e: any, setSuccess: Dispatch<SetStateAction<string | null>>, setLoading: Dispatch<SetStateAction<boolean>>) {
|
||||
e.preventDefault();
|
||||
|
||||
setLoading(true);
|
||||
|
||||
let name = e.target[0].value;
|
||||
let email = e.target[1].value;
|
||||
let message = e.target[2].value;
|
||||
|
||||
let body = {
|
||||
name: name,
|
||||
email: email,
|
||||
message: message,
|
||||
};
|
||||
|
||||
let res = await fetch("/api/contactForm", {
|
||||
method: "post",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
let data = await res.json();
|
||||
|
||||
console.log(data);
|
||||
|
||||
if (data.msg === "Success") {
|
||||
setSuccess("Success");
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
setSuccess("Failed");
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
export const textSHuffle = (text, dispatch) => {
|
||||
|
||||
let shuffleTimer = 0;
|
||||
let startText = text;
|
||||
let shuffledtext
|
||||
|
||||
setInterval(()=>{
|
||||
shuffleTimer++
|
||||
shuffledtext = startText + "whatever";
|
||||
}, 200);
|
||||
|
||||
return shuffledtext
|
||||
export const textSHuffle = (text, dispatch) => {
|
||||
|
||||
let shuffleTimer = 0;
|
||||
let startText = text;
|
||||
let shuffledtext
|
||||
|
||||
setInterval(()=>{
|
||||
shuffleTimer++
|
||||
shuffledtext = startText + "whatever";
|
||||
}, 200);
|
||||
|
||||
return shuffledtext
|
||||
}
|
@ -1,132 +1,132 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
export default function threeJsAnimations() {
|
||||
const animationWrapper = document.getElementById("homepage-animation-wrapper");
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(75, animationWrapper.clientWidth / animationWrapper.clientHeight, 0.1, 1000);
|
||||
|
||||
const light = new THREE.PointLight(0xff0000, 1, 200);
|
||||
light.position.set(-1, -1, 5);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight("#adb2d3");
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
|
||||
pointLight.position.set(10, 10, 10);
|
||||
const sphereSize = 1;
|
||||
const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(animationWrapper.clientWidth, animationWrapper.clientHeight);
|
||||
animationWrapper.appendChild(renderer.domElement);
|
||||
|
||||
const geometry = new THREE.BoxGeometry();
|
||||
|
||||
const material = new THREE.MeshPhongMaterial({
|
||||
// light
|
||||
// specular: "rgb(30, 33, 68)",
|
||||
// intermediate
|
||||
color: "rgb(30, 33, 68)",
|
||||
// dark
|
||||
// emissive: "rgb(30, 33, 68)",
|
||||
shininess: 50,
|
||||
wireframe: true,
|
||||
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
|
||||
});
|
||||
const cube = new THREE.Mesh(geometry, material);
|
||||
|
||||
const material2 = new THREE.MeshPhongMaterial({
|
||||
// light
|
||||
// specular: "rgb(30, 33, 68)",
|
||||
// intermediate
|
||||
color: "#1668e4",
|
||||
// dark
|
||||
// emissive: "rgb(30, 33, 68)",
|
||||
shininess: 50,
|
||||
wireframe: false,
|
||||
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
|
||||
});
|
||||
const cube2 = new THREE.Mesh(geometry, material2);
|
||||
cube2.position.x = window.innerWidth > 500 ? 1.5 : -0.5;
|
||||
cube2.position.y = window.innerWidth > 500 ? 1.5 : 1.8;
|
||||
cube2.position.z = -1;
|
||||
cube2.scale.x = 0.3;
|
||||
cube2.scale.y = 0.3;
|
||||
cube2.scale.z = 0.3;
|
||||
|
||||
const material3 = new THREE.MeshPhongMaterial({
|
||||
// light
|
||||
// specular: "rgb(30, 33, 68)",
|
||||
// intermediate
|
||||
color: "rgb(53, 65, 194)",
|
||||
// dark
|
||||
// emissive: "rgb(30, 33, 68)",
|
||||
shininess: 50,
|
||||
wireframe: false,
|
||||
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
|
||||
});
|
||||
const cube3 = new THREE.Mesh(geometry, material3);
|
||||
|
||||
scene.add(cube);
|
||||
scene.add(cube2);
|
||||
scene.add(cube3);
|
||||
scene.add(ambientLight);
|
||||
scene.add(light);
|
||||
scene.add(pointLight);
|
||||
scene.add(pointLightHelper);
|
||||
|
||||
cube3.position.x = -1.5;
|
||||
cube3.position.y = -1.5;
|
||||
cube3.position.z = -1;
|
||||
cube3.scale.x = 0.2;
|
||||
cube3.scale.y = 0.2;
|
||||
cube3.scale.z = 0.2;
|
||||
|
||||
camera.position.z = 2;
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
renderer.render(scene, camera);
|
||||
cube.rotation.x += 0.002;
|
||||
cube.rotation.y += 0.002;
|
||||
|
||||
cube2.rotation.x += 0.005;
|
||||
cube2.rotation.y += 0.005;
|
||||
|
||||
cube3.rotation.x += 0.01;
|
||||
cube3.rotation.y += 0.01;
|
||||
|
||||
scene.rotation.z += 0.0003;
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
window.addEventListener("mousemove", (e) => {
|
||||
let relMouseX = e.x - window.innerWidth / 2;
|
||||
let relMouseY = e.y - window.innerHeight / 2;
|
||||
// console.log("X pos: ", relMouseX);
|
||||
// console.log("Y pos: ", relMouseY);
|
||||
// console.log(e.y);
|
||||
relMouseX < 0 ? (cube.rotation.x += 0.003) : (cube.rotation.x += 0.003);
|
||||
relMouseY > 0 ? (cube.rotation.y += 0.003) : (cube.rotation.y += 0.003);
|
||||
|
||||
relMouseX < 0 ? (cube2.position.x += 0.001) : (cube2.position.x -= 0.001);
|
||||
relMouseY > 0 ? (cube2.position.y += 0.001) : (cube2.position.y -= 0.001);
|
||||
|
||||
relMouseX < 0 ? (cube3.position.x += 0.0007) : (cube3.position.x -= 0.0007);
|
||||
relMouseY > 0 ? (cube3.position.y += 0.0007) : (cube3.position.y -= 0.0007);
|
||||
});
|
||||
|
||||
window.addEventListener("resize", (e) => {
|
||||
renderer.setSize(animationWrapper.clientWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", (e) => {
|
||||
if (window.scrollY > 100) {
|
||||
scene.remove(cube2);
|
||||
scene.remove(cube3);
|
||||
} else {
|
||||
scene.add(cube2);
|
||||
scene.add(cube3);
|
||||
}
|
||||
});
|
||||
}
|
||||
import * as THREE from "three";
|
||||
|
||||
export default function threeJsAnimations() {
|
||||
const animationWrapper = document.getElementById("homepage-animation-wrapper");
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(75, animationWrapper.clientWidth / animationWrapper.clientHeight, 0.1, 1000);
|
||||
|
||||
const light = new THREE.PointLight(0xff0000, 1, 200);
|
||||
light.position.set(-1, -1, 5);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight("#adb2d3");
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
|
||||
pointLight.position.set(10, 10, 10);
|
||||
const sphereSize = 1;
|
||||
const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(animationWrapper.clientWidth, animationWrapper.clientHeight);
|
||||
animationWrapper.appendChild(renderer.domElement);
|
||||
|
||||
const geometry = new THREE.BoxGeometry();
|
||||
|
||||
const material = new THREE.MeshPhongMaterial({
|
||||
// light
|
||||
// specular: "rgb(30, 33, 68)",
|
||||
// intermediate
|
||||
color: "rgb(30, 33, 68)",
|
||||
// dark
|
||||
// emissive: "rgb(30, 33, 68)",
|
||||
shininess: 50,
|
||||
wireframe: true,
|
||||
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
|
||||
});
|
||||
const cube = new THREE.Mesh(geometry, material);
|
||||
|
||||
const material2 = new THREE.MeshPhongMaterial({
|
||||
// light
|
||||
// specular: "rgb(30, 33, 68)",
|
||||
// intermediate
|
||||
color: "#1668e4",
|
||||
// dark
|
||||
// emissive: "rgb(30, 33, 68)",
|
||||
shininess: 50,
|
||||
wireframe: false,
|
||||
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
|
||||
});
|
||||
const cube2 = new THREE.Mesh(geometry, material2);
|
||||
cube2.position.x = window.innerWidth > 500 ? 1.5 : -0.5;
|
||||
cube2.position.y = window.innerWidth > 500 ? 1.5 : 1.8;
|
||||
cube2.position.z = -1;
|
||||
cube2.scale.x = 0.3;
|
||||
cube2.scale.y = 0.3;
|
||||
cube2.scale.z = 0.3;
|
||||
|
||||
const material3 = new THREE.MeshPhongMaterial({
|
||||
// light
|
||||
// specular: "rgb(30, 33, 68)",
|
||||
// intermediate
|
||||
color: "rgb(53, 65, 194)",
|
||||
// dark
|
||||
// emissive: "rgb(30, 33, 68)",
|
||||
shininess: 50,
|
||||
wireframe: false,
|
||||
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
|
||||
});
|
||||
const cube3 = new THREE.Mesh(geometry, material3);
|
||||
|
||||
scene.add(cube);
|
||||
scene.add(cube2);
|
||||
scene.add(cube3);
|
||||
scene.add(ambientLight);
|
||||
scene.add(light);
|
||||
scene.add(pointLight);
|
||||
scene.add(pointLightHelper);
|
||||
|
||||
cube3.position.x = -1.5;
|
||||
cube3.position.y = -1.5;
|
||||
cube3.position.z = -1;
|
||||
cube3.scale.x = 0.2;
|
||||
cube3.scale.y = 0.2;
|
||||
cube3.scale.z = 0.2;
|
||||
|
||||
camera.position.z = 2;
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
renderer.render(scene, camera);
|
||||
cube.rotation.x += 0.002;
|
||||
cube.rotation.y += 0.002;
|
||||
|
||||
cube2.rotation.x += 0.005;
|
||||
cube2.rotation.y += 0.005;
|
||||
|
||||
cube3.rotation.x += 0.01;
|
||||
cube3.rotation.y += 0.01;
|
||||
|
||||
scene.rotation.z += 0.0003;
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
window.addEventListener("mousemove", (e) => {
|
||||
let relMouseX = e.x - window.innerWidth / 2;
|
||||
let relMouseY = e.y - window.innerHeight / 2;
|
||||
// console.log("X pos: ", relMouseX);
|
||||
// console.log("Y pos: ", relMouseY);
|
||||
// console.log(e.y);
|
||||
relMouseX < 0 ? (cube.rotation.x += 0.003) : (cube.rotation.x += 0.003);
|
||||
relMouseY > 0 ? (cube.rotation.y += 0.003) : (cube.rotation.y += 0.003);
|
||||
|
||||
relMouseX < 0 ? (cube2.position.x += 0.001) : (cube2.position.x -= 0.001);
|
||||
relMouseY > 0 ? (cube2.position.y += 0.001) : (cube2.position.y -= 0.001);
|
||||
|
||||
relMouseX < 0 ? (cube3.position.x += 0.0007) : (cube3.position.x -= 0.0007);
|
||||
relMouseY > 0 ? (cube3.position.y += 0.0007) : (cube3.position.y -= 0.0007);
|
||||
});
|
||||
|
||||
window.addEventListener("resize", (e) => {
|
||||
renderer.setSize(animationWrapper.clientWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", (e) => {
|
||||
if (window.scrollY > 100) {
|
||||
scene.remove(cube2);
|
||||
scene.remove(cube3);
|
||||
} else {
|
||||
scene.add(cube2);
|
||||
scene.add(cube3);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1766.7 2003.6" style="enable-background:new 0 0 1766.7 2003.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.1;fill:#1730C6;enable-background:new ;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2_1_">
|
||||
<g id="Layer_1-2">
|
||||
<path class="st0" d="M1468,1688.2l137.5-1540.8H883.3v1702.9L1468,1688.2z"/>
|
||||
<path class="st1" d="M0,0l160.8,1803.3l721.5,200.3l0,0l723.5-200.6l161-1803L0,0z M1396.7,816.3L1396.7,816.3l-5.4,59.4
|
||||
l-52,582.3l-3.3,37.4l-452.6,125.4l0,0l-1,0.3l-453.1-125.7l-31-347.3h222l15.8,176.4l246.4,66.5h0.2l0,0l246.7-66.6l25.7-286.9
|
||||
H388.4l-19.8-221.2h805.5l20.1-226.5H348.9l-20.1-221.2h1107.9L1396.7,816.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1766.7 2003.6" style="enable-background:new 0 0 1766.7 2003.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.1;fill:#1730C6;enable-background:new ;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2_1_">
|
||||
<g id="Layer_1-2">
|
||||
<path class="st0" d="M1468,1688.2l137.5-1540.8H883.3v1702.9L1468,1688.2z"/>
|
||||
<path class="st1" d="M0,0l160.8,1803.3l721.5,200.3l0,0l723.5-200.6l161-1803L0,0z M1396.7,816.3L1396.7,816.3l-5.4,59.4
|
||||
l-52,582.3l-3.3,37.4l-452.6,125.4l0,0l-1,0.3l-453.1-125.7l-31-347.3h222l15.8,176.4l246.4,66.5h0.2l0,0l246.7-66.6l25.7-286.9
|
||||
H388.4l-19.8-221.2h805.5l20.1-226.5H348.9l-20.1-221.2h1107.9L1396.7,816.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 938 B After Width: | Height: | Size: 955 B |
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1C3766;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
|
||||
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#1C3766;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
|
||||
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 626 B After Width: | Height: | Size: 638 B |
@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 433.6 491.7" style="enable-background:new 0 0 433.6 491.7;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.1;fill:#2236C6;enable-background:new ;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2_1_">
|
||||
<g id="Layer_1-2">
|
||||
<polygon class="st0" points="216.8,454.1 360.3,414.3 394,36.2 216.8,36.2 "/>
|
||||
<path class="st1" d="M0,0l39.5,442.6l177.1,49.2l177.6-49.2L433.6,0L0,0z M351.3,105l-2.5,27.5l-1.1,12.2H140l5,55.6h197.8
|
||||
l-1.3,14.6l-12.8,142.9l-0.8,9.2l-111.1,30.8l0,0l-0.2,0.1L105.3,367l-7.6-85.2h54.5l3.9,43.3l60.4,16.3l0,0l0,0l60.5-16.3
|
||||
l6.3-70.4H95.3L82,105l-1.3-14.6h271.9L351.3,105z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 433.6 491.7" style="enable-background:new 0 0 433.6 491.7;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.1;fill:#2236C6;enable-background:new ;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_2_1_">
|
||||
<g id="Layer_1-2">
|
||||
<polygon class="st0" points="216.8,454.1 360.3,414.3 394,36.2 216.8,36.2 "/>
|
||||
<path class="st1" d="M0,0l39.5,442.6l177.1,49.2l177.6-49.2L433.6,0L0,0z M351.3,105l-2.5,27.5l-1.1,12.2H140l5,55.6h197.8
|
||||
l-1.3,14.6l-12.8,142.9l-0.8,9.2l-111.1,30.8l0,0l-0.2,0.1L105.3,367l-7.6-85.2h54.5l3.9,43.3l60.4,16.3l0,0l0,0l60.5-16.3
|
||||
l6.3-70.4H95.3L82,105l-1.3-14.6h271.9L351.3,105z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 908 B After Width: | Height: | Size: 925 B |
@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 310 310" style="enable-background:new 0 0 310 310;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="XMLID_801_">
|
||||
<path id="XMLID_802_" class="st0" d="M72.2,99.7H9.9c-2.8,0-5,2.2-5,5v199.9c0,2.8,2.2,5,5,5h62.2c2.8,0,5-2.2,5-5V104.7
|
||||
C77.2,102,74.9,99.7,72.2,99.7z"/>
|
||||
<path id="XMLID_803_" class="st0" d="M41.1,0.3C18.4,0.3,0,18.7,0,41.4c0,22.6,18.4,41,41.1,41c22.6,0,41-18.4,41-41
|
||||
C82.1,18.7,63.7,0.3,41.1,0.3z"/>
|
||||
<path id="XMLID_804_" class="st0" d="M230.5,94.8c-25,0-43.5,10.7-54.7,23v-13c0-2.8-2.2-5-5-5h-59.6c-2.8,0-5,2.2-5,5v199.9
|
||||
c0,2.8,2.2,5,5,5h62.1c2.8,0,5-2.2,5-5v-98.9c0-33.3,9.1-46.3,32.3-46.3c25.3,0,27.3,20.8,27.3,48v97.2c0,2.8,2.2,5,5,5H305
|
||||
c2.8,0,5-2.2,5-5V195C310,145.4,300.5,94.8,230.5,94.8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 310 310" style="enable-background:new 0 0 310 310;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="XMLID_801_">
|
||||
<path id="XMLID_802_" class="st0" d="M72.2,99.7H9.9c-2.8,0-5,2.2-5,5v199.9c0,2.8,2.2,5,5,5h62.2c2.8,0,5-2.2,5-5V104.7
|
||||
C77.2,102,74.9,99.7,72.2,99.7z"/>
|
||||
<path id="XMLID_803_" class="st0" d="M41.1,0.3C18.4,0.3,0,18.7,0,41.4c0,22.6,18.4,41,41.1,41c22.6,0,41-18.4,41-41
|
||||
C82.1,18.7,63.7,0.3,41.1,0.3z"/>
|
||||
<path id="XMLID_804_" class="st0" d="M230.5,94.8c-25,0-43.5,10.7-54.7,23v-13c0-2.8-2.2-5-5-5h-59.6c-2.8,0-5,2.2-5,5v199.9
|
||||
c0,2.8,2.2,5,5,5h62.1c2.8,0,5-2.2,5-5v-98.9c0-33.3,9.1-46.3,32.3-46.3c25.3,0,27.3,20.8,27.3,48v97.2c0,2.8,2.2,5,5,5H305
|
||||
c2.8,0,5-2.2,5-5V195C310,145.4,300.5,94.8,230.5,94.8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
|
||||
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
|
||||
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 626 B After Width: | Height: | Size: 638 B |
786
index.html
@ -1,394 +1,394 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
|
||||
<head>
|
||||
<!-- ...................................................................................................................... Head -->
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Tben Web Designer</title>
|
||||
<meta name="description" content="Web/Graphic/Motion Designer, UI UX Designer, Frontend Web Developer">
|
||||
<link rel="shortcut icon" href="https://benjamintoby.github.io/personal_site/images/favicon4.ico"
|
||||
type="image/x-icon">
|
||||
<link href="https://fonts.googleapis.com/css?family=Titillium+Web:400,500,600,700,800|Material+Icons"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="main.css">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<!-- #################################################################################-- Preloader -->
|
||||
|
||||
<div class="preloader-init" id="preloader"></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Header -->
|
||||
|
||||
<header>
|
||||
<a href="#">
|
||||
<img src="images/logo-v3.svg" alt="">
|
||||
<div>Tben.<br>Design</div>
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<nav>
|
||||
<a href="#my-work" id="my-work-link" class="nav-link">My Work</a>
|
||||
<a href="#about-me" id="about-me-link" class="nav-link">About Me</a>
|
||||
<a href="#my-specialties" id="resume-link" class="nav-link footer-special">Resume</a>
|
||||
<a href="#contact-me" id="contact-me-link" class="nav-link footer-special">Contact Me</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div style="display: flex;align-items: center;">
|
||||
<a href="mailto:benoti.san@gmail.com" class="social-media-links material-icons" id="mail">mail</a>
|
||||
<a href="https://linkedin.com/in/benjamin-toby" class="social-media-links material-icons" id="linkedin"
|
||||
target="_blank"><img src="images/linkedin.svg" alt=""></a>
|
||||
</div>
|
||||
|
||||
<button class="hamburger-button">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</button>
|
||||
|
||||
</header>
|
||||
|
||||
<div id="header-controller"></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Preloader -->
|
||||
|
||||
<div id="preloader-container">
|
||||
<img src="images/clouds.jpg" alt="">
|
||||
<img src="images/logo-v3.svg" alt="">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Hero -->
|
||||
|
||||
<section class="hero-section">
|
||||
<div></div>
|
||||
|
||||
<div>
|
||||
<div class="main-text-block">
|
||||
Hi. I’m a
|
||||
<h1 class="main-hero-text">
|
||||
<a href="#" class="uiux-designer-link">UI UX Designer</a>,
|
||||
<a href="#" class="web-designer-link">Web Designer</a>,
|
||||
<a href="#" class="frontend-designer-link">Frontend Web Developer</a>,
|
||||
<a href="#" class="graphic-motion-designer-link">Graphic and motion Designer</a>.
|
||||
</h1> <br>
|
||||
<button class="main-cta-button"><a href="#contact-me">Let's Talk</a></button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <div class="hero-scroll-down-block">
|
||||
<a href="#my-work">
|
||||
<div>‹</div>
|
||||
</a>
|
||||
|
||||
<img src="images/rotating-text.svg" alt="" class="rotating-text-image">
|
||||
</div> -->
|
||||
|
||||
<div class="hero-text-section-graphic">
|
||||
<a href="#my-work">
|
||||
<img src="images/down-arrow.svg" alt="">
|
||||
</a>
|
||||
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="images/clouds.jpg" alt="" id="clouds">
|
||||
<img src="images/mountains.png" alt="" id="mountains">
|
||||
|
||||
<div class="benjamin-image-block-wrapper">
|
||||
<div class="benjamin-image-block">
|
||||
<div class="benjamin-image-block-overlay"></div>
|
||||
</div>
|
||||
<div class="service-display-block">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="benjamin-big-text-block">Benjamin<br>Toby</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- My Work -->
|
||||
|
||||
<section class="my-work-section" id="my-work">
|
||||
<div>
|
||||
<h2 class="scroll-into-view-content" data-delay="200">Some of My Work</h2>
|
||||
<p class="scroll-into-view-content" data-delay="400">Here are a few of my works: you can find more of my
|
||||
work on my <a href="https://99designs.com/profiles/2220588" target="_blank">99designs</a> profile.</p>
|
||||
<div class="portfolio-buttons-container">
|
||||
<button class="portfolio-left-button"><img src="images/white-arrow.svg" alt=""></button>
|
||||
<button class="portfolio-right-button"><img src="images/white-arrow.svg" alt=""></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-into-view">
|
||||
<div class="portfolio-items-container scroll-into-view-content" data-delay="800">
|
||||
|
||||
<!-- Porfolio entry -->
|
||||
<div class="portfolio-entry" id="transcend-barriers">
|
||||
<!-- <img src="images/my-image-large.jpg" alt=""> -->
|
||||
<div>
|
||||
<h3>Transcend Barriers Animation</h3>
|
||||
<p>This animation was made for web view. I used aftereffects and bodymoving to accomplish this:
|
||||
the result? High definition svg anmimation that looks crisp on any resolution and maintains
|
||||
a relatively light weight</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>javascript</div><img src="images/javascript.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Porfolio entry -->
|
||||
<div class="portfolio-entry" id="github-to-asana">
|
||||
<!-- <img src="images/mountains.png" alt=""> -->
|
||||
<div>
|
||||
<h3>Github to Asana</h3>
|
||||
<p>This project is an ongoing webapp that connects github to asana. In this project I'm focusing
|
||||
on the UI/UX, together with frontend functionality.</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>HTML5</div><img src="images/html5.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>CSS3</div><img src="images/css3.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>javascript</div><img src="images/javascript.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Git version control</div><img src="images/git-version-control.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="images/Project_GitToAsana_LandingPage_002.jpg" alt="">
|
||||
<div id="github-to-asana-animation"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- About Me -->
|
||||
|
||||
<section class="about-me-section" id="about-me">
|
||||
<div class="scroll-into-view">
|
||||
<img src="images/my-image-large.jpg" alt="">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="scroll-into-view-content">About Me</h2>
|
||||
<p class="scroll-into-view-content">I've traversed multiple fields over the past decade: from engineering
|
||||
studies, to solar power constructions, to web design, graphic design, 2d animations, and code. Over the
|
||||
years I've learnt the best skill: which is learnability and adaptability: hence I aim to cut across many
|
||||
different fields: the world changes fast, and I think this is the only skill that matters.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- My Specialties -->
|
||||
|
||||
<section class="my-specialties-section" id="my-specialties">
|
||||
<h2 class="scroll-into-view-content">My Tools For Creating Awesomeness</h2>
|
||||
<p class="scroll-into-view-content">Ever expanding and ever improving, I have proffesional grade understanding
|
||||
of the tools below. But I don't plan to stop here.</p>
|
||||
|
||||
<div>
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="0">
|
||||
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="100">
|
||||
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="200">
|
||||
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="300">
|
||||
<div>HTML 5</div><img src="images/html5.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="400">
|
||||
<div>CSS3</div><img src="images/css3.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="500">
|
||||
<div>javascript</div><img src="images/javascript.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="600">
|
||||
<div>Git Version Control</div><img src="images/git-version-control.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="700">
|
||||
<div>Figma</div><img src="images/figma.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="800">
|
||||
<div>Wordpress</div><img src="images/wordpress.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="900">
|
||||
<div>Webflow</div><img src="images/webflow.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1000">
|
||||
<div>Adobe XD</div><img src="images/adobe-xd.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1100">
|
||||
<div>Visual Studio Code</div><img src="images/vs-code.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1200">
|
||||
<div>Dreamweaver</div><img src="images/dreamweaver.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1300">
|
||||
<div>Adobe Animate</div><img src="images/adobe animate.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1400">
|
||||
<div>Affinity Photo and Affinity Designer</div><img src="images/affinity.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="scroll-into-view-content-2" data-delay="500">Download My Resume</button>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Contact Me -->
|
||||
|
||||
<section class="contact-section" id="contact-me">
|
||||
<div>
|
||||
<h2 class="scroll-into-view-content">So. Let's talk</h2>
|
||||
|
||||
<p class="scroll-into-view-content">Your business is my business as soon as you hit that contact button. I'm
|
||||
ready
|
||||
to attend to your needs as soon as possible. Just drop a line.</p>
|
||||
<a href="mailto:benoti.san@gmail.com" class="scroll-into-view-content-2" data-delay="500">
|
||||
Email Address <span class="material-icons">mail</span>
|
||||
</a>
|
||||
|
||||
<div class="scroll-into-view-content-2" data-delay="700">
|
||||
<div>Copyright 2021 Tben.Design</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="scroll-into-view" id="footer-right-section">
|
||||
|
||||
<div class="scroll-into-view-content" data-delay="800">
|
||||
<form id="contact-form" class="main-cta-form" method="post" action="contact-form-process.php"
|
||||
autocomplete="on">
|
||||
<input type="text" required placeholder="Enter Full Name">
|
||||
<input type="email" required placeholder="Enter Email Address">
|
||||
<textarea name="Message" rows="3" maxlength="3000" required placeholder="Enter Message"></textarea>
|
||||
<button type="submit">Send Message</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="contact-section-controller"></div>
|
||||
|
||||
<div class="footer-bar">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Scripts -->
|
||||
|
||||
<script src="scripts/anime.min.js"></script>
|
||||
<script src="scripts/lottie.min.js"></script>
|
||||
<script src="scripts/main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
|
||||
<head>
|
||||
<!-- ...................................................................................................................... Head -->
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Tben Web Designer</title>
|
||||
<meta name="description" content="Web/Graphic/Motion Designer, UI UX Designer, Frontend Web Developer">
|
||||
<link rel="shortcut icon" href="https://benjamintoby.github.io/personal_site/images/favicon4.ico"
|
||||
type="image/x-icon">
|
||||
<link href="https://fonts.googleapis.com/css?family=Titillium+Web:400,500,600,700,800|Material+Icons"
|
||||
rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="main.css">
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<!-- #################################################################################-- Preloader -->
|
||||
|
||||
<div class="preloader-init" id="preloader"></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Header -->
|
||||
|
||||
<header>
|
||||
<a href="#">
|
||||
<img src="images/logo-v3.svg" alt="">
|
||||
<div>Tben.<br>Design</div>
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<nav>
|
||||
<a href="#my-work" id="my-work-link" class="nav-link">My Work</a>
|
||||
<a href="#about-me" id="about-me-link" class="nav-link">About Me</a>
|
||||
<a href="#my-specialties" id="resume-link" class="nav-link footer-special">Resume</a>
|
||||
<a href="#contact-me" id="contact-me-link" class="nav-link footer-special">Contact Me</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div style="display: flex;align-items: center;">
|
||||
<a href="mailto:benoti.san@gmail.com" class="social-media-links material-icons" id="mail">mail</a>
|
||||
<a href="https://linkedin.com/in/benjamin-toby" class="social-media-links material-icons" id="linkedin"
|
||||
target="_blank"><img src="images/linkedin.svg" alt=""></a>
|
||||
</div>
|
||||
|
||||
<button class="hamburger-button">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</button>
|
||||
|
||||
</header>
|
||||
|
||||
<div id="header-controller"></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Preloader -->
|
||||
|
||||
<div id="preloader-container">
|
||||
<img src="images/clouds.jpg" alt="">
|
||||
<img src="images/logo-v3.svg" alt="">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Hero -->
|
||||
|
||||
<section class="hero-section">
|
||||
<div></div>
|
||||
|
||||
<div>
|
||||
<div class="main-text-block">
|
||||
Hi. I’m a
|
||||
<h1 class="main-hero-text">
|
||||
<a href="#" class="uiux-designer-link">UI UX Designer</a>,
|
||||
<a href="#" class="web-designer-link">Web Designer</a>,
|
||||
<a href="#" class="frontend-designer-link">Frontend Web Developer</a>,
|
||||
<a href="#" class="graphic-motion-designer-link">Graphic and motion Designer</a>.
|
||||
</h1> <br>
|
||||
<button class="main-cta-button"><a href="#contact-me">Let's Talk</a></button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <div class="hero-scroll-down-block">
|
||||
<a href="#my-work">
|
||||
<div>‹</div>
|
||||
</a>
|
||||
|
||||
<img src="images/rotating-text.svg" alt="" class="rotating-text-image">
|
||||
</div> -->
|
||||
|
||||
<div class="hero-text-section-graphic">
|
||||
<a href="#my-work">
|
||||
<img src="images/down-arrow.svg" alt="">
|
||||
</a>
|
||||
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="images/clouds.jpg" alt="" id="clouds">
|
||||
<img src="images/mountains.png" alt="" id="mountains">
|
||||
|
||||
<div class="benjamin-image-block-wrapper">
|
||||
<div class="benjamin-image-block">
|
||||
<div class="benjamin-image-block-overlay"></div>
|
||||
</div>
|
||||
<div class="service-display-block">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="benjamin-big-text-block">Benjamin<br>Toby</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- My Work -->
|
||||
|
||||
<section class="my-work-section" id="my-work">
|
||||
<div>
|
||||
<h2 class="scroll-into-view-content" data-delay="200">Some of My Work</h2>
|
||||
<p class="scroll-into-view-content" data-delay="400">Here are a few of my works: you can find more of my
|
||||
work on my <a href="https://99designs.com/profiles/2220588" target="_blank">99designs</a> profile.</p>
|
||||
<div class="portfolio-buttons-container">
|
||||
<button class="portfolio-left-button"><img src="images/white-arrow.svg" alt=""></button>
|
||||
<button class="portfolio-right-button"><img src="images/white-arrow.svg" alt=""></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scroll-into-view">
|
||||
<div class="portfolio-items-container scroll-into-view-content" data-delay="800">
|
||||
|
||||
<!-- Porfolio entry -->
|
||||
<div class="portfolio-entry" id="transcend-barriers">
|
||||
<!-- <img src="images/my-image-large.jpg" alt=""> -->
|
||||
<div>
|
||||
<h3>Transcend Barriers Animation</h3>
|
||||
<p>This animation was made for web view. I used aftereffects and bodymoving to accomplish this:
|
||||
the result? High definition svg anmimation that looks crisp on any resolution and maintains
|
||||
a relatively light weight</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>javascript</div><img src="images/javascript.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Porfolio entry -->
|
||||
<div class="portfolio-entry" id="github-to-asana">
|
||||
<!-- <img src="images/mountains.png" alt=""> -->
|
||||
<div>
|
||||
<h3>Github to Asana</h3>
|
||||
<p>This project is an ongoing webapp that connects github to asana. In this project I'm focusing
|
||||
on the UI/UX, together with frontend functionality.</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>HTML5</div><img src="images/html5.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>CSS3</div><img src="images/css3.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>javascript</div><img src="images/javascript.svg" alt="">
|
||||
</div>
|
||||
<div class="specialty-skill">
|
||||
<div>Git version control</div><img src="images/git-version-control.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="images/Project_GitToAsana_LandingPage_002.jpg" alt="">
|
||||
<div id="github-to-asana-animation"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- About Me -->
|
||||
|
||||
<section class="about-me-section" id="about-me">
|
||||
<div class="scroll-into-view">
|
||||
<img src="images/my-image-large.jpg" alt="">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="scroll-into-view-content">About Me</h2>
|
||||
<p class="scroll-into-view-content">I've traversed multiple fields over the past decade: from engineering
|
||||
studies, to solar power constructions, to web design, graphic design, 2d animations, and code. Over the
|
||||
years I've learnt the best skill: which is learnability and adaptability: hence I aim to cut across many
|
||||
different fields: the world changes fast, and I think this is the only skill that matters.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- My Specialties -->
|
||||
|
||||
<section class="my-specialties-section" id="my-specialties">
|
||||
<h2 class="scroll-into-view-content">My Tools For Creating Awesomeness</h2>
|
||||
<p class="scroll-into-view-content">Ever expanding and ever improving, I have proffesional grade understanding
|
||||
of the tools below. But I don't plan to stop here.</p>
|
||||
|
||||
<div>
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="0">
|
||||
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="100">
|
||||
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="200">
|
||||
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="300">
|
||||
<div>HTML 5</div><img src="images/html5.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="400">
|
||||
<div>CSS3</div><img src="images/css3.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="500">
|
||||
<div>javascript</div><img src="images/javascript.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="600">
|
||||
<div>Git Version Control</div><img src="images/git-version-control.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="700">
|
||||
<div>Figma</div><img src="images/figma.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content" data-delay="800">
|
||||
<div>Wordpress</div><img src="images/wordpress.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="900">
|
||||
<div>Webflow</div><img src="images/webflow.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1000">
|
||||
<div>Adobe XD</div><img src="images/adobe-xd.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1100">
|
||||
<div>Visual Studio Code</div><img src="images/vs-code.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1200">
|
||||
<div>Dreamweaver</div><img src="images/dreamweaver.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1300">
|
||||
<div>Adobe Animate</div><img src="images/adobe animate.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="specialty-skill scroll-into-view-content-2" data-delay="1400">
|
||||
<div>Affinity Photo and Affinity Designer</div><img src="images/affinity.svg" alt="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="scroll-into-view-content-2" data-delay="500">Download My Resume</button>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Contact Me -->
|
||||
|
||||
<section class="contact-section" id="contact-me">
|
||||
<div>
|
||||
<h2 class="scroll-into-view-content">So. Let's talk</h2>
|
||||
|
||||
<p class="scroll-into-view-content">Your business is my business as soon as you hit that contact button. I'm
|
||||
ready
|
||||
to attend to your needs as soon as possible. Just drop a line.</p>
|
||||
<a href="mailto:benoti.san@gmail.com" class="scroll-into-view-content-2" data-delay="500">
|
||||
Email Address <span class="material-icons">mail</span>
|
||||
</a>
|
||||
|
||||
<div class="scroll-into-view-content-2" data-delay="700">
|
||||
<div>Copyright 2021 Tben.Design</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="scroll-into-view" id="footer-right-section">
|
||||
|
||||
<div class="scroll-into-view-content" data-delay="800">
|
||||
<form id="contact-form" class="main-cta-form" method="post" action="contact-form-process.php"
|
||||
autocomplete="on">
|
||||
<input type="text" required placeholder="Enter Full Name">
|
||||
<input type="email" required placeholder="Enter Email Address">
|
||||
<textarea name="Message" rows="3" maxlength="3000" required placeholder="Enter Message"></textarea>
|
||||
<button type="submit">Send Message</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="contact-section-controller"></div>
|
||||
|
||||
<div class="footer-bar">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- #################################################################################-- Scripts -->
|
||||
|
||||
<script src="scripts/anime.min.js"></script>
|
||||
<script src="scripts/lottie.min.js"></script>
|
||||
<script src="scripts/main.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
@ -1,79 +1,79 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Choosing your stack",
|
||||
"slug": "choosing-your-tech-stack",
|
||||
"description": "So many technologies, so little time to vet all",
|
||||
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
|
||||
"body": [
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "The tech pool is an ever growing trojan horse. JavaScript libraries alone are getting out of hand: we seem to be getting a new one every 6 months. Hell even I have a javascript library of my own. Trying to pick from this pool can easily turn into mission impossible: understandably so: there's just too many of them"
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "The thing is, each of these libraries and frameworks end up doing essentially the same thing in thier respective categories: react creates reuseable javascript components: same with vue, same with angular, svelte. And for the most part, you won't be using every single feature provided by these libraries: just the ones that suit your project."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "In truth, the tech stack you choose doesn't mean much: you can get 10 different options which achieve the same goal. Really, all that matters is the developer you pick: because a masterful developer can create great products using any stack of his/her choosing."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"children": [
|
||||
{
|
||||
"tag": "span",
|
||||
"content": "If you're just starting off, perhaps the best step is to evaluate the stacks and get a recommendation from an expert in the field: you may be surprised to find out you don't need as many technologies as you think: you may even be surprised that using a traditional framework like ruby on rails is actually a lot easier than wordpress. "
|
||||
},
|
||||
{
|
||||
"tag": "a",
|
||||
"href": "/contact",
|
||||
"class": "text-blue-300",
|
||||
"content": "Reach out"
|
||||
},
|
||||
{
|
||||
"tag": "span",
|
||||
"content": " to find out more."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Find your perfect framework",
|
||||
"slug": "find-your-perfect-framework",
|
||||
"description": "How much can a web framework affect your project?",
|
||||
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
|
||||
"body": [
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Web frameworks are a great way to get the best out of multiple technologies while keeping your development time short and reliability high. But just like it is with every aspect of web development, there's a dilemma of \"Which framework should I go with\"."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Different frameworks come with different structures and different selections of languages and packages. In truth, you don't necessarily need a framework: nearly all major programming languages can handle applications on thier own: but as you build more projects, you keep encountering more repetitions, and you end up abstracting those repetitions into reuseable components: you now have a framework of your own. Now this isn't bad at all: infact, this is the end goal of a truly performant app: frameworks often come with a lot of packages you may not need: which end up bugging down your application: if you can develop a framework of your own, you can eliminate this downside: but, you have to be prepared to spend a lot of time on your project: if you have the time, then go for it: else, go for a framework."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Haven said this, there are lots of frameworks you can choose from: depending on your time and budget: here are some great picks you should consider:"
|
||||
},
|
||||
{
|
||||
"tag": "h2",
|
||||
"content": "1. Next JS"
|
||||
},
|
||||
{
|
||||
"tag": "img",
|
||||
"src": "https://miro.medium.com/max/1400/1*htbUdWgFQ3a94PMEvBr_hQ.png",
|
||||
"class": "w-full",
|
||||
"style": {
|
||||
"border": "1px solid #ffffff40"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Next JS is slowly becoming the household name for web development frameworks: it features an immensely powerful and efficient structure, based on React JS: which enables server side rendering of pages: as opposed to the traditional SPA(single page application) model React was created for. Next JS handles the heavilifting of routing, apis, frontend and backend components, module bundling, and linting, leaving you with a relatively easy platform to integrate your project. Next JS is growing very quick and in no time, it will become the most used framework for enterprise applications"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Choosing your stack",
|
||||
"slug": "choosing-your-tech-stack",
|
||||
"description": "So many technologies, so little time to vet all",
|
||||
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
|
||||
"body": [
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "The tech pool is an ever growing trojan horse. JavaScript libraries alone are getting out of hand: we seem to be getting a new one every 6 months. Hell even I have a javascript library of my own. Trying to pick from this pool can easily turn into mission impossible: understandably so: there's just too many of them"
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "The thing is, each of these libraries and frameworks end up doing essentially the same thing in thier respective categories: react creates reuseable javascript components: same with vue, same with angular, svelte. And for the most part, you won't be using every single feature provided by these libraries: just the ones that suit your project."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "In truth, the tech stack you choose doesn't mean much: you can get 10 different options which achieve the same goal. Really, all that matters is the developer you pick: because a masterful developer can create great products using any stack of his/her choosing."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"children": [
|
||||
{
|
||||
"tag": "span",
|
||||
"content": "If you're just starting off, perhaps the best step is to evaluate the stacks and get a recommendation from an expert in the field: you may be surprised to find out you don't need as many technologies as you think: you may even be surprised that using a traditional framework like ruby on rails is actually a lot easier than wordpress. "
|
||||
},
|
||||
{
|
||||
"tag": "a",
|
||||
"href": "/contact",
|
||||
"class": "text-blue-300",
|
||||
"content": "Reach out"
|
||||
},
|
||||
{
|
||||
"tag": "span",
|
||||
"content": " to find out more."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Find your perfect framework",
|
||||
"slug": "find-your-perfect-framework",
|
||||
"description": "How much can a web framework affect your project?",
|
||||
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
|
||||
"body": [
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Web frameworks are a great way to get the best out of multiple technologies while keeping your development time short and reliability high. But just like it is with every aspect of web development, there's a dilemma of \"Which framework should I go with\"."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Different frameworks come with different structures and different selections of languages and packages. In truth, you don't necessarily need a framework: nearly all major programming languages can handle applications on thier own: but as you build more projects, you keep encountering more repetitions, and you end up abstracting those repetitions into reuseable components: you now have a framework of your own. Now this isn't bad at all: infact, this is the end goal of a truly performant app: frameworks often come with a lot of packages you may not need: which end up bugging down your application: if you can develop a framework of your own, you can eliminate this downside: but, you have to be prepared to spend a lot of time on your project: if you have the time, then go for it: else, go for a framework."
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Haven said this, there are lots of frameworks you can choose from: depending on your time and budget: here are some great picks you should consider:"
|
||||
},
|
||||
{
|
||||
"tag": "h2",
|
||||
"content": "1. Next JS"
|
||||
},
|
||||
{
|
||||
"tag": "img",
|
||||
"src": "https://miro.medium.com/max/1400/1*htbUdWgFQ3a94PMEvBr_hQ.png",
|
||||
"class": "w-full",
|
||||
"style": {
|
||||
"border": "1px solid #ffffff40"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "p",
|
||||
"content": "Next JS is slowly becoming the household name for web development frameworks: it features an immensely powerful and efficient structure, based on React JS: which enables server side rendering of pages: as opposed to the traditional SPA(single page application) model React was created for. Next JS handles the heavilifting of routing, apis, frontend and backend components, module bundling, and linting, leaving you with a relatively easy platform to integrate your project. Next JS is growing very quick and in no time, it will become the most used framework for enterprise applications"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -1,46 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { gsap } from "gsap";
|
||||
// import scrollTrigger from "gsap/ScrollTrigger";
|
||||
|
||||
const BG = () => {
|
||||
React.useEffect(() => {
|
||||
// gsap.registerPlugin(scrollTrigger);
|
||||
|
||||
gsap.to("#bg-blurred-image", {
|
||||
scrollTrigger: {
|
||||
scrub: 5,
|
||||
},
|
||||
y: 50,
|
||||
scale: 0.8,
|
||||
});
|
||||
|
||||
gsap.to("#bg-image", {
|
||||
scrollTrigger: {
|
||||
scrub: 2,
|
||||
},
|
||||
y: 50,
|
||||
opacity: 0,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<img
|
||||
src="/images/rm378-07c-min.png"
|
||||
alt="bg-image"
|
||||
className="fixed top-0 left-0 w-full h-full object-cover z-[-2] p-[100px] opacity-20 blur-sm"
|
||||
id="bg-blurred-image"
|
||||
/>
|
||||
<img
|
||||
src="/images/rm378-07c-min.png"
|
||||
alt="bg-image"
|
||||
className="fixed top-0 left-0 w-full h-full object-cover z-[-1] p-[150px] opacity-5"
|
||||
id="bg-image"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default BG;
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { gsap } from "gsap";
|
||||
// import scrollTrigger from "gsap/ScrollTrigger";
|
||||
|
||||
const BG = () => {
|
||||
React.useEffect(() => {
|
||||
// gsap.registerPlugin(scrollTrigger);
|
||||
|
||||
gsap.to("#bg-blurred-image", {
|
||||
scrollTrigger: {
|
||||
scrub: 5,
|
||||
},
|
||||
y: 50,
|
||||
scale: 0.8,
|
||||
});
|
||||
|
||||
gsap.to("#bg-image", {
|
||||
scrollTrigger: {
|
||||
scrub: 2,
|
||||
},
|
||||
y: 50,
|
||||
opacity: 0,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<img
|
||||
src="/images/rm378-07c-min.png"
|
||||
alt="bg-image"
|
||||
className="fixed top-0 left-0 w-full h-full object-cover z-[-2] p-[100px] opacity-20 blur-sm"
|
||||
id="bg-blurred-image"
|
||||
/>
|
||||
<img
|
||||
src="/images/rm378-07c-min.png"
|
||||
alt="bg-image"
|
||||
className="fixed top-0 left-0 w-full h-full object-cover z-[-1] p-[150px] opacity-5"
|
||||
id="bg-image"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default BG;
|
||||
|
@ -1,53 +1,53 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
const GeneralFooter = () => {
|
||||
const date = new Date();
|
||||
return (
|
||||
<footer
|
||||
className="mt-10 w-full flex flex-col items-center py-20"
|
||||
style={{
|
||||
borderTop: "1px solid rgba(255,255,255,0.2)",
|
||||
}}
|
||||
>
|
||||
<div className="max-w-6xl w-full flex flex-wrap items-stretch gap-10">
|
||||
<a
|
||||
href="/"
|
||||
data-href="/"
|
||||
className="logo-link-block -mb-4"
|
||||
>
|
||||
<Image
|
||||
src="/images/logo-white.svg"
|
||||
width={50}
|
||||
height={100}
|
||||
alt="Logo"
|
||||
/>
|
||||
</a>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex-col flex items-start gap-1">
|
||||
<span className="opacity-50">Contact Me</span>
|
||||
<div className="flex gap-4">
|
||||
<a
|
||||
href="https://www.linkedin.com/in/benjamin-toby/"
|
||||
target="_blank"
|
||||
>
|
||||
LinkedIn
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/BenjaminToby"
|
||||
target="_blank"
|
||||
>
|
||||
Github
|
||||
</a>
|
||||
<a href="mailto:benoti.san@gmail.com">Mail</a>
|
||||
<a href="tel:+2348123682346">Phone</a>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-sm opacity-40">Copyright © {date.getFullYear()} Tben.me. All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralFooter;
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
const GeneralFooter = () => {
|
||||
const date = new Date();
|
||||
return (
|
||||
<footer
|
||||
className="mt-10 w-full flex flex-col items-center py-20"
|
||||
style={{
|
||||
borderTop: "1px solid rgba(255,255,255,0.2)",
|
||||
}}
|
||||
>
|
||||
<div className="max-w-6xl w-full flex flex-wrap items-stretch gap-10">
|
||||
<a
|
||||
href="/"
|
||||
data-href="/"
|
||||
className="logo-link-block -mb-4"
|
||||
>
|
||||
<Image
|
||||
src="/images/logo-white.svg"
|
||||
width={50}
|
||||
height={100}
|
||||
alt="Logo"
|
||||
/>
|
||||
</a>
|
||||
<div className="flex flex-col items-start justify-between">
|
||||
<div className="flex-col flex items-start gap-1">
|
||||
<span className="opacity-50">Contact Me</span>
|
||||
<div className="flex gap-4">
|
||||
<a
|
||||
href="https://www.linkedin.com/in/benjamin-toby/"
|
||||
target="_blank"
|
||||
>
|
||||
LinkedIn
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/BenjaminToby"
|
||||
target="_blank"
|
||||
>
|
||||
Github
|
||||
</a>
|
||||
<a href="mailto:benoti.san@gmail.com">Mail</a>
|
||||
<a href="tel:+2348123682346">Phone</a>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-sm opacity-40">Copyright © {date.getFullYear()} Tben.me. All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralFooter;
|
||||
|
@ -1,101 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
import { gsap } from "gsap";
|
||||
import HeaderNav from "./HeaderNav";
|
||||
|
||||
/**
|
||||
* General Header for all pages
|
||||
*/
|
||||
const GeneralHeader = (): React.ReactElement => {
|
||||
const links: { title: string; url: string }[] = require("./links.json");
|
||||
|
||||
const [mobileNavOpen, setMobileNavOpen] = React.useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Animate the header on mount
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
gsap.fromTo(
|
||||
"#main-header",
|
||||
{
|
||||
y: -20,
|
||||
filter: "blur(100px)",
|
||||
webkitFilter: "blur(100px)",
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 1,
|
||||
filter: "none",
|
||||
webkitFilter: "none",
|
||||
delay: 0.5,
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
|
||||
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 (
|
||||
<header
|
||||
id="main-header"
|
||||
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 z-10 fixed xl:sticky top-0"
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
data-href="/"
|
||||
className="logo-link-block scale-90 sm:scale-100"
|
||||
>
|
||||
<Image
|
||||
src="/images/logo-white.svg"
|
||||
width={50}
|
||||
height={100}
|
||||
alt="Logo"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<HeaderNav />
|
||||
|
||||
<button
|
||||
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 top-[35px] right-10 z-10 -rotate-45"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setMobileNavOpen(!mobileNavOpen);
|
||||
}}
|
||||
>
|
||||
<div className="w-8 h-1 rounded-full bg-[black]"></div>
|
||||
<div className="w-8 h-1 rounded-full bg-[black]"></div>
|
||||
</button>
|
||||
|
||||
<div
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralHeader;
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
import { gsap } from "gsap";
|
||||
import HeaderNav from "./HeaderNav";
|
||||
|
||||
/**
|
||||
* General Header for all pages
|
||||
*/
|
||||
const GeneralHeader = (): React.ReactElement => {
|
||||
const links: { title: string; url: string }[] = require("./links.json");
|
||||
|
||||
const [mobileNavOpen, setMobileNavOpen] = React.useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Animate the header on mount
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
gsap.fromTo(
|
||||
"#main-header",
|
||||
{
|
||||
y: -20,
|
||||
filter: "blur(100px)",
|
||||
webkitFilter: "blur(100px)",
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
duration: 1,
|
||||
filter: "none",
|
||||
webkitFilter: "none",
|
||||
delay: 0.5,
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
|
||||
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 (
|
||||
<header
|
||||
id="main-header"
|
||||
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 z-10 fixed xl:sticky top-0"
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
data-href="/"
|
||||
className="logo-link-block scale-90 sm:scale-100"
|
||||
>
|
||||
<Image
|
||||
src="/images/logo-white.svg"
|
||||
width={50}
|
||||
height={100}
|
||||
alt="Logo"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<HeaderNav />
|
||||
|
||||
<button
|
||||
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 top-[35px] right-10 z-10 -rotate-45"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setMobileNavOpen(!mobileNavOpen);
|
||||
}}
|
||||
>
|
||||
<div className="w-8 h-1 rounded-full bg-[black]"></div>
|
||||
<div className="w-8 h-1 rounded-full bg-[black]"></div>
|
||||
</button>
|
||||
|
||||
<div
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralHeader;
|
||||
|
@ -1,62 +1,62 @@
|
||||
"use client";
|
||||
|
||||
import React, { ReactElement } from "react";
|
||||
import GeneralHeader from "./GeneralHeader";
|
||||
import GeneralFooter from "./GeneralFooter";
|
||||
import { gsap } from "gsap";
|
||||
import BG from "./BG";
|
||||
|
||||
export const SiteContext: any = React.createContext({});
|
||||
|
||||
type GeneralLayoutProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const GeneralLayout = ({ children }: GeneralLayoutProps): ReactElement => {
|
||||
const [readyState, setReadyState] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const links: NodeListOf<HTMLAnchorElement> | null = document.querySelectorAll("nav a");
|
||||
|
||||
links?.forEach((link) => {
|
||||
if (link.dataset.href === window.location.pathname) {
|
||||
link.classList.add("active-page");
|
||||
}
|
||||
|
||||
if (window.location.pathname.match(new RegExp(`${link.dataset.href}\\/.*`))) {
|
||||
link.classList.add("active-page");
|
||||
}
|
||||
});
|
||||
|
||||
gsap.to("#main-content-wrapper", {
|
||||
opacity: 1,
|
||||
duration: 2,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SiteContext.Provider value={{ readyState, setReadyState }}>
|
||||
<div
|
||||
id="main-content-wrapper"
|
||||
style={{
|
||||
opacity: 0,
|
||||
}}
|
||||
className="px-4 md:px-10"
|
||||
>
|
||||
<GeneralHeader />
|
||||
<main
|
||||
className="flex items-center flex-col w-full"
|
||||
// style={{
|
||||
// perspective: "800px",
|
||||
// }}
|
||||
>
|
||||
{children}
|
||||
</main>
|
||||
<GeneralFooter />
|
||||
</div>
|
||||
<BG />
|
||||
</SiteContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralLayout;
|
||||
"use client";
|
||||
|
||||
import React, { ReactElement } from "react";
|
||||
import GeneralHeader from "./GeneralHeader";
|
||||
import GeneralFooter from "./GeneralFooter";
|
||||
import { gsap } from "gsap";
|
||||
import BG from "./BG";
|
||||
|
||||
export const SiteContext: any = React.createContext({});
|
||||
|
||||
type GeneralLayoutProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const GeneralLayout = ({ children }: GeneralLayoutProps): ReactElement => {
|
||||
const [readyState, setReadyState] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const links: NodeListOf<HTMLAnchorElement> | null = document.querySelectorAll("nav a");
|
||||
|
||||
links?.forEach((link) => {
|
||||
if (link.dataset.href === window.location.pathname) {
|
||||
link.classList.add("active-page");
|
||||
}
|
||||
|
||||
if (window.location.pathname.match(new RegExp(`${link.dataset.href}\\/.*`))) {
|
||||
link.classList.add("active-page");
|
||||
}
|
||||
});
|
||||
|
||||
gsap.to("#main-content-wrapper", {
|
||||
opacity: 1,
|
||||
duration: 2,
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SiteContext.Provider value={{ readyState, setReadyState }}>
|
||||
<div
|
||||
id="main-content-wrapper"
|
||||
style={{
|
||||
opacity: 0,
|
||||
}}
|
||||
className="px-4 md:px-10"
|
||||
>
|
||||
<GeneralHeader />
|
||||
<main
|
||||
className="flex items-center flex-col w-full"
|
||||
// style={{
|
||||
// perspective: "800px",
|
||||
// }}
|
||||
>
|
||||
{children}
|
||||
</main>
|
||||
<GeneralFooter />
|
||||
</div>
|
||||
<BG />
|
||||
</SiteContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneralLayout;
|
||||
|
@ -1,60 +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;
|
||||
"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;
|
||||
|
@ -1,27 +1,27 @@
|
||||
[
|
||||
{
|
||||
"title": "Home",
|
||||
"url": "/"
|
||||
},
|
||||
{
|
||||
"title": "About Me",
|
||||
"url": "/about"
|
||||
},
|
||||
{
|
||||
"title": "My Work",
|
||||
"url": "/work"
|
||||
},
|
||||
{
|
||||
"title": "My Resume",
|
||||
"url": "/documents/Resume-Benjamin-Toby-Linkedin.pdf",
|
||||
"download": true
|
||||
},
|
||||
{
|
||||
"title": "Blog",
|
||||
"url": "/blog"
|
||||
},
|
||||
{
|
||||
"title": " Contact Me",
|
||||
"url": "/contact"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"title": "Home",
|
||||
"url": "/"
|
||||
},
|
||||
{
|
||||
"title": "About Me",
|
||||
"url": "/about"
|
||||
},
|
||||
{
|
||||
"title": "My Work",
|
||||
"url": "/work"
|
||||
},
|
||||
{
|
||||
"title": "My Resume",
|
||||
"url": "/documents/Resume-Benjamin-Toby-Linkedin.pdf",
|
||||
"download": true
|
||||
},
|
||||
{
|
||||
"title": "Blog",
|
||||
"url": "/blog"
|
||||
},
|
||||
{
|
||||
"title": " Contact Me",
|
||||
"url": "/contact"
|
||||
}
|
||||
]
|
||||
|
12
next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
@ -1,15 +1,15 @@
|
||||
import "../styles/main.css";
|
||||
import "../styles/tw_main.css";
|
||||
import { Fragment } from "react";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<Fragment>
|
||||
<Component {...pageProps} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
||||
import "../styles/main.css";
|
||||
import "../styles/tw_main.css";
|
||||
import { Fragment } from "react";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<Fragment>
|
||||
<Component {...pageProps} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
||||
|
@ -1,19 +1,19 @@
|
||||
import React from "react";
|
||||
import { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<script
|
||||
src="/scripts/main.js"
|
||||
defer
|
||||
></script>
|
||||
</Head>
|
||||
<body className="w-full">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
import React from "react";
|
||||
import { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<script
|
||||
src="/scripts/main.js"
|
||||
defer
|
||||
></script>
|
||||
</Head>
|
||||
<body className="w-full">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
|
@ -1,49 +1,49 @@
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const sanitizeHtml = require("sanitize-html");
|
||||
const nodemailer = require("nodemailer");
|
||||
|
||||
import sanitizeHtmlOptions from "../../functions/backend/sanitizeHtmlOptions";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: process.env.TBENMAIL_HOST,
|
||||
port: 587,
|
||||
auth: {
|
||||
user: process.env.TBENMAIL_EMAIL,
|
||||
pass: process.env.TBENMAIL_EMAIL_PASSWORD,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* API handler
|
||||
*
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "POST") {
|
||||
let name = req.body.name;
|
||||
let email = req.body.email;
|
||||
let message = req.body.message;
|
||||
|
||||
const html = `<h1>Message from ${name} | ${email}</h1><h4>Name:</h4><p>${name}</p><h4>Email:</h4><p>${email}</p><h4>Message:</h4><p>${message}</p>`;
|
||||
const sanitizedHtml = sanitizeHtml(html, sanitizeHtmlOptions);
|
||||
|
||||
try {
|
||||
let info = await transporter.sendMail({
|
||||
from: `"Tben.me" <${process.env.TBENMAIL_EMAIL}>`,
|
||||
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com",
|
||||
subject: "Tben.me | Client Message",
|
||||
text: "Hello from tben",
|
||||
html: sanitizedHtml,
|
||||
});
|
||||
|
||||
console.log("Message sent: %s", info.messageId);
|
||||
res.json({ msg: "Success", info: info });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.json({ msg: "Failed" });
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const sanitizeHtml = require("sanitize-html");
|
||||
const nodemailer = require("nodemailer");
|
||||
|
||||
import sanitizeHtmlOptions from "../../functions/backend/sanitizeHtmlOptions";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: process.env.TBENMAIL_HOST,
|
||||
port: 587,
|
||||
auth: {
|
||||
user: process.env.TBENMAIL_EMAIL,
|
||||
pass: process.env.TBENMAIL_EMAIL_PASSWORD,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* API handler
|
||||
*
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "POST") {
|
||||
let name = req.body.name;
|
||||
let email = req.body.email;
|
||||
let message = req.body.message;
|
||||
|
||||
const html = `<h1>Message from ${name} | ${email}</h1><h4>Name:</h4><p>${name}</p><h4>Email:</h4><p>${email}</p><h4>Message:</h4><p>${message}</p>`;
|
||||
const sanitizedHtml = sanitizeHtml(html, sanitizeHtmlOptions);
|
||||
|
||||
try {
|
||||
let info = await transporter.sendMail({
|
||||
from: `"Tben.me" <${process.env.TBENMAIL_EMAIL}>`,
|
||||
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com",
|
||||
subject: "Tben.me | Client Message",
|
||||
text: "Hello from tben",
|
||||
html: sanitizedHtml,
|
||||
});
|
||||
|
||||
console.log("Message sent: %s", info.messageId);
|
||||
res.json({ msg: "Success", info: info });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.json({ msg: "Failed" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,45 +1,45 @@
|
||||
import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
|
||||
import Cors from "cors";
|
||||
|
||||
const cors = Cors({
|
||||
methods: ["GET"],
|
||||
origin: "*",
|
||||
});
|
||||
|
||||
// Helper method to wait for a middleware to execute before continuing
|
||||
// And to throw an error when an error happens in a middleware
|
||||
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fn(req, res, (result: any) => {
|
||||
if (result instanceof Error) {
|
||||
return reject(result);
|
||||
}
|
||||
|
||||
return resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {NextApiHandler}
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
// res.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
// res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// res.setHeader("Access-Control-Allow-Methods", "*");
|
||||
// res.setHeader("Access-Control-Allow-Headers", "*");
|
||||
|
||||
res.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// another common pattern
|
||||
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS,PATCH,DELETE,POST,PUT");
|
||||
res.setHeader("Access-Control-Allow-Headers", "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version");
|
||||
|
||||
// await runMiddleware(req, res, cors);
|
||||
|
||||
res.json({
|
||||
title: "Hello There",
|
||||
message: "General Kenobi",
|
||||
});
|
||||
}
|
||||
import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
|
||||
import Cors from "cors";
|
||||
|
||||
const cors = Cors({
|
||||
methods: ["GET"],
|
||||
origin: "*",
|
||||
});
|
||||
|
||||
// Helper method to wait for a middleware to execute before continuing
|
||||
// And to throw an error when an error happens in a middleware
|
||||
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fn(req, res, (result: any) => {
|
||||
if (result instanceof Error) {
|
||||
return reject(result);
|
||||
}
|
||||
|
||||
return resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {NextApiHandler}
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
// res.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
// res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// res.setHeader("Access-Control-Allow-Methods", "*");
|
||||
// res.setHeader("Access-Control-Allow-Headers", "*");
|
||||
|
||||
res.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
// another common pattern
|
||||
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS,PATCH,DELETE,POST,PUT");
|
||||
res.setHeader("Access-Control-Allow-Headers", "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version");
|
||||
|
||||
// await runMiddleware(req, res, cors);
|
||||
|
||||
res.json({
|
||||
title: "Hello There",
|
||||
message: "General Kenobi",
|
||||
});
|
||||
}
|
||||
|
@ -1,178 +1,178 @@
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
|
||||
import TextShuffler from "../../components/actions/TextShuffler";
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>{blogPost.title} | Tben.me Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={blogPost.excerpt}
|
||||
/>
|
||||
</Head>
|
||||
<GeneralLayout>
|
||||
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
|
||||
<button
|
||||
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
|
||||
onClick={(e) => {
|
||||
window.history.back();
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<h1 className="m-0">
|
||||
<TextShuffler textInput={blogPost.title} />
|
||||
</h1>
|
||||
<span className="text-lg">
|
||||
<TextShuffler textInput={blogPost.excerpt} />
|
||||
</span>
|
||||
<span className="text-base opacity-50">
|
||||
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
|
||||
dangerouslySetInnerHTML={{ __html: blogPost.body }}
|
||||
></span>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
}
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
// ## Environment processes
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## User Authentication
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Page/Site Data Data Fetching
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select * from blog_posts WHERE slug='${params?.single}'`,
|
||||
});
|
||||
|
||||
const post = postsResponse.payload[0];
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Server Props Return
|
||||
return {
|
||||
props: {
|
||||
blogPost: post,
|
||||
},
|
||||
revalidate: 3600,
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
/**
|
||||
* Data fetching
|
||||
*
|
||||
* @abstract fetch date from the server or externnal source
|
||||
*/
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select slug from blog_posts`,
|
||||
});
|
||||
|
||||
const posts: { slug: string }[] | null = postsResponse.payload;
|
||||
|
||||
const paths = posts?.map((entry) => {
|
||||
return {
|
||||
params: { single: entry.slug },
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
paths: paths ? paths : [],
|
||||
fallback: "blocking",
|
||||
};
|
||||
};
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Imports
|
||||
* ==============================================================================
|
||||
*/
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
|
||||
const datasquirel = require("datasquirel");
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
|
||||
import TextShuffler from "../../components/actions/TextShuffler";
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
* Main Component { Functional }
|
||||
* ==============================================================================
|
||||
* @param {Object} props - Server props
|
||||
*/
|
||||
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
// ## Get Contexts
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Javascript Variables
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## React Hooks { useState, useEffect, useRef, etc ... }
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Function Return
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Head>
|
||||
<title>{blogPost.title} | Tben.me Blog</title>
|
||||
<meta
|
||||
name="description"
|
||||
content={blogPost.excerpt}
|
||||
/>
|
||||
</Head>
|
||||
<GeneralLayout>
|
||||
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
|
||||
<button
|
||||
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
|
||||
onClick={(e) => {
|
||||
window.history.back();
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<h1 className="m-0">
|
||||
<TextShuffler textInput={blogPost.title} />
|
||||
</h1>
|
||||
<span className="text-lg">
|
||||
<TextShuffler textInput={blogPost.excerpt} />
|
||||
</span>
|
||||
<span className="text-base opacity-50">
|
||||
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
|
||||
dangerouslySetInnerHTML={{ __html: blogPost.body }}
|
||||
></span>
|
||||
</GeneralLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
}
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
// ## Environment processes
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## User Authentication
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Page/Site Data Data Fetching
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select * from blog_posts WHERE slug='${params?.single}'`,
|
||||
});
|
||||
|
||||
const post = postsResponse.payload[0];
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
|
||||
// ## Server Props Return
|
||||
return {
|
||||
props: {
|
||||
blogPost: post,
|
||||
},
|
||||
revalidate: 3600,
|
||||
};
|
||||
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
/** ********************************************** */
|
||||
};
|
||||
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
/** ****************************************************************************** */
|
||||
|
||||
/**
|
||||
* Server Side Props or Static Props
|
||||
* ==============================================================================
|
||||
*/
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
/**
|
||||
* Data fetching
|
||||
*
|
||||
* @abstract fetch date from the server or externnal source
|
||||
*/
|
||||
const postsResponse = await datasquirel.get({
|
||||
key: process.env.DATASQUIREL_API_KEY,
|
||||
db: "tbenme",
|
||||
query: `select slug from blog_posts`,
|
||||
});
|
||||
|
||||
const posts: { slug: string }[] | null = postsResponse.payload;
|
||||
|
||||
const paths = posts?.map((entry) => {
|
||||
return {
|
||||
params: { single: entry.slug },
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
paths: paths ? paths : [],
|
||||
fallback: "blocking",
|
||||
};
|
||||
};
|
||||
|
@ -1,39 +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;
|
||||
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;
|
||||
|
108
pages/work.jsx
@ -1,54 +1,54 @@
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import TextShuffler from "../components/actions/TextShuffler";
|
||||
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
|
||||
import PortfolioEntry from "../components/PortfolioEntry";
|
||||
|
||||
const myWork = () => {
|
||||
const portfolioEntries = require("../components/portfolioEntries.json");
|
||||
|
||||
return (
|
||||
<GeneralLayout>
|
||||
<Head>
|
||||
<title>My Work | Tben</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Some of my Work"
|
||||
/>
|
||||
</Head>
|
||||
|
||||
<div className="flex flex-col items-start w-full max-w-6xl">
|
||||
<h1>
|
||||
<TextShuffler textInput="My Work" />
|
||||
</h1>
|
||||
<span className="hero-sub-text mb-2">
|
||||
<TextShuffler
|
||||
textInput="Some of the projects I've worked on"
|
||||
delay={500}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div className="portfolio-entries-block mt-4">
|
||||
{portfolioEntries.map((entry) => (
|
||||
<PortfolioEntry
|
||||
key={entry.title}
|
||||
title={entry.title}
|
||||
description={entry.description}
|
||||
url={entry.url}
|
||||
image={entry.image}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</GeneralLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default myWork;
|
||||
|
||||
// {
|
||||
// "title": "Stirrmedia Social webapp",
|
||||
// "description": "A new social media experience without censorship",
|
||||
// "url": "https://stirrmedia.com",
|
||||
// "image": "/images/stirrmediascreenshot.png"
|
||||
// },
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import TextShuffler from "../components/actions/TextShuffler";
|
||||
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
|
||||
import PortfolioEntry from "../components/PortfolioEntry";
|
||||
|
||||
const myWork = () => {
|
||||
const portfolioEntries = require("../components/portfolioEntries.json");
|
||||
|
||||
return (
|
||||
<GeneralLayout>
|
||||
<Head>
|
||||
<title>My Work | Tben</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Some of my Work"
|
||||
/>
|
||||
</Head>
|
||||
|
||||
<div className="flex flex-col items-start w-full max-w-6xl">
|
||||
<h1>
|
||||
<TextShuffler textInput="My Work" />
|
||||
</h1>
|
||||
<span className="hero-sub-text mb-2">
|
||||
<TextShuffler
|
||||
textInput="Some of the projects I've worked on"
|
||||
delay={500}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div className="portfolio-entries-block mt-4">
|
||||
{portfolioEntries.map((entry) => (
|
||||
<PortfolioEntry
|
||||
key={entry.title}
|
||||
title={entry.title}
|
||||
description={entry.description}
|
||||
url={entry.url}
|
||||
image={entry.image}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</GeneralLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default myWork;
|
||||
|
||||
// {
|
||||
// "title": "Stirrmedia Social webapp",
|
||||
// "description": "A new social media experience without censorship",
|
||||
// "url": "https://stirrmedia.com",
|
||||
// "image": "/images/stirrmediascreenshot.png"
|
||||
// },
|
||||
|
@ -1,3 +1,3 @@
|
||||
User-agent: *
|
||||
|
||||
User-agent: *
|
||||
|
||||
Sitemap: https://tben.me/sitemap.xml
|
@ -1,169 +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,
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
/**
|
||||
* 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,22 +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",
|
||||
// });
|
||||
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,42 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://tben.me/</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/about</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/work</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/blog</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/blog/choosing-your-tech-stack</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/blog/find-your-perfect-framework</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/contact</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/documents/Benjamin-Toby-CV-7-27-2022.pdf</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://tben.me/</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/about</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/work</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/blog</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/blog/choosing-your-tech-stack</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/blog/find-your-perfect-framework</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/contact</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://tben.me/documents/Benjamin-Toby-CV-7-27-2022.pdf</loc>
|
||||
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
|
||||
</url>
|
||||
</urlset>
|
@ -1,441 +1,441 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap");
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
/* overflow-x: hidden; */
|
||||
scroll-behavior: smooth;
|
||||
font-family: "Source Code Pro", Helvetica;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #222;
|
||||
letter-spacing: -0.8px;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-color: #1668e4;
|
||||
--main-color-lighter: #5698fc;
|
||||
--dark-color: #201e1e;
|
||||
--sec-color-3: #688e26;
|
||||
--sec-color-4: #adb2d3;
|
||||
--sec-color-5: #c2a878;
|
||||
--light-color-1: rgb(64, 37, 216);
|
||||
--test-color: rgb(113, 116, 255);
|
||||
--transparent-white: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--sec-color-2);
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
hr {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--dark-color);
|
||||
/* background-color: #c52532; */
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
form * {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
header {
|
||||
z-index: 1000000;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 52px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
p a,
|
||||
span a {
|
||||
color: var(--main-color-lighter);
|
||||
/* border-bottom: 1px solid var(--main-color-lighter); */
|
||||
}
|
||||
|
||||
p a:hover,
|
||||
span a:hover {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
/* ################################################# -- Sliders */
|
||||
aside,
|
||||
.side-nav-block {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* width */
|
||||
aside::-webkit-scrollbar,
|
||||
.side-nav-block::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
aside::-webkit-scrollbar-track,
|
||||
.side-nav-block::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
aside::-webkit-scrollbar-thumb,
|
||||
.side-nav-block::-webkit-scrollbar-thumb {
|
||||
background: #dbe1eb;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
aside::-webkit-scrollbar-thumb:hover,
|
||||
.side-nav-block::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/*############################################# -- Common Actions */
|
||||
|
||||
.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.no-pointer-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pointer-events {
|
||||
pointer-events: visible;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
/*############################################# -- Header */
|
||||
|
||||
header {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo-link-block h1 {
|
||||
font-size: 28px;
|
||||
margin: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.active-page {
|
||||
opacity: 1;
|
||||
border-bottom: 2px solid white;
|
||||
}
|
||||
|
||||
/*############################################# -- Shuffled Text */
|
||||
|
||||
#__next {
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/* .shuffled-text-span span {
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.5s;
|
||||
} */
|
||||
|
||||
@keyframes shuffle {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*############################################# -- Hero Section */
|
||||
.hero-sub-text {
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
color: var(--dark-color);
|
||||
background-color: white;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a:hover {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
/*############################################# -- 404 page */
|
||||
.not-found-page-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*############################################# -- Portfolio page */
|
||||
.portfolio-entries-block {
|
||||
display: grid;
|
||||
gap: 40px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portfolio-entry-block {
|
||||
max-width: 700px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 20px;
|
||||
}
|
||||
/*############################################# -- Contact Forms */
|
||||
form {
|
||||
margin-top: 40px;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
padding: 15px 20px;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.message-response {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--dark-color);
|
||||
border: 2px solid #688e26;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.message-response.failed {
|
||||
border: 2px solid #d42222;
|
||||
}
|
||||
|
||||
#homepage-animation-wrapper {
|
||||
width: 150%;
|
||||
height: 100%;
|
||||
background-color: var(--dark-color);
|
||||
}
|
||||
|
||||
.tech-stack-header {
|
||||
color: rgb(113, 116, 255);
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.portfolio-image-wrapper {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
object-position: left top;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
span img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
/* .transition-fade {
|
||||
transition: 0.4s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
html.is-animating .transition-fade {
|
||||
opacity: 0;
|
||||
} */
|
||||
|
||||
/* ###############################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################### -- Mobile Styles -- ########################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
############################################################################################### */
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
}
|
||||
|
||||
@media (max-width: 990px) {
|
||||
.portfolio-entries-block {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 20px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.portfolio-image-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
object-fit: contain;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
}
|
||||
@import url("https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap");
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
/* overflow-x: hidden; */
|
||||
scroll-behavior: smooth;
|
||||
font-family: "Source Code Pro", Helvetica;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #222;
|
||||
letter-spacing: -0.8px;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-color: #1668e4;
|
||||
--main-color-lighter: #5698fc;
|
||||
--dark-color: #201e1e;
|
||||
--sec-color-3: #688e26;
|
||||
--sec-color-4: #adb2d3;
|
||||
--sec-color-5: #c2a878;
|
||||
--light-color-1: rgb(64, 37, 216);
|
||||
--test-color: rgb(113, 116, 255);
|
||||
--transparent-white: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--sec-color-2);
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
hr {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--dark-color);
|
||||
/* background-color: #c52532; */
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
form * {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
header {
|
||||
z-index: 1000000;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 52px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
p a,
|
||||
span a {
|
||||
color: var(--main-color-lighter);
|
||||
/* border-bottom: 1px solid var(--main-color-lighter); */
|
||||
}
|
||||
|
||||
p a:hover,
|
||||
span a:hover {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
/* ################################################# -- Sliders */
|
||||
aside,
|
||||
.side-nav-block {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* width */
|
||||
aside::-webkit-scrollbar,
|
||||
.side-nav-block::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
aside::-webkit-scrollbar-track,
|
||||
.side-nav-block::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
aside::-webkit-scrollbar-thumb,
|
||||
.side-nav-block::-webkit-scrollbar-thumb {
|
||||
background: #dbe1eb;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
aside::-webkit-scrollbar-thumb:hover,
|
||||
.side-nav-block::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/*############################################# -- Common Actions */
|
||||
|
||||
.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.no-pointer-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pointer-events {
|
||||
pointer-events: visible;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
/*############################################# -- Header */
|
||||
|
||||
header {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo-link-block h1 {
|
||||
font-size: 28px;
|
||||
margin: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.active-page {
|
||||
opacity: 1;
|
||||
border-bottom: 2px solid white;
|
||||
}
|
||||
|
||||
/*############################################# -- Shuffled Text */
|
||||
|
||||
#__next {
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/* .shuffled-text-span span {
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.5s;
|
||||
} */
|
||||
|
||||
@keyframes shuffle {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*############################################# -- Hero Section */
|
||||
.hero-sub-text {
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
color: var(--dark-color);
|
||||
background-color: white;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a:hover {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
/*############################################# -- 404 page */
|
||||
.not-found-page-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*############################################# -- Portfolio page */
|
||||
.portfolio-entries-block {
|
||||
display: grid;
|
||||
gap: 40px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portfolio-entry-block {
|
||||
max-width: 700px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 20px;
|
||||
}
|
||||
/*############################################# -- Contact Forms */
|
||||
form {
|
||||
margin-top: 40px;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
padding: 15px 20px;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.message-response {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--dark-color);
|
||||
border: 2px solid #688e26;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.message-response.failed {
|
||||
border: 2px solid #d42222;
|
||||
}
|
||||
|
||||
#homepage-animation-wrapper {
|
||||
width: 150%;
|
||||
height: 100%;
|
||||
background-color: var(--dark-color);
|
||||
}
|
||||
|
||||
.tech-stack-header {
|
||||
color: rgb(113, 116, 255);
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.portfolio-image-wrapper {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
object-position: left top;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
span img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
/* .transition-fade {
|
||||
transition: 0.4s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
html.is-animating .transition-fade {
|
||||
opacity: 0;
|
||||
} */
|
||||
|
||||
/* ###############################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################### -- Mobile Styles -- ########################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
############################################################################################### */
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
}
|
||||
|
||||
@media (max-width: 990px) {
|
||||
.portfolio-entries-block {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 20px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.portfolio-image-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
object-fit: contain;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
}
|
||||
|
28
scripts/lottie.min.js
vendored
958
styles/main.css
@ -1,479 +1,479 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Cutive+Mono&display=swap");
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
/* scroll-behavior: smooth; */
|
||||
font-family: "Cutive Mono", Helvetica;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #222;
|
||||
letter-spacing: -0.8px;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-color: #7174ff;
|
||||
--main-color-lighter: #9d9eff;
|
||||
--main-color-darker: #7072c9;
|
||||
--main-color-dark: #292a6b;
|
||||
--dark-color: #181515;
|
||||
--sec-color-3: #688e26;
|
||||
--sec-color-4: #adb2d3;
|
||||
--sec-color-5: #c2a878;
|
||||
--light-color-1: rgb(64, 37, 216);
|
||||
--test-color: #7174ff;
|
||||
--transparent-white: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
main {
|
||||
padding-top: 200px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
main {
|
||||
padding-top: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--sec-color-2);
|
||||
}
|
||||
|
||||
button,
|
||||
.button {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
border: 1px solid transparent;
|
||||
color: var(--dark-color);
|
||||
background-color: white;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
button.outlined,
|
||||
.button.outlined {
|
||||
border: 1px solid white;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
hr {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
.button:hover {
|
||||
background-color: var(--dark-color);
|
||||
/* background-color: #c52532; */
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
form * {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
header {
|
||||
z-index: 1000000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 52px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 44px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
p a,
|
||||
span a {
|
||||
color: var(--main-color-lighter);
|
||||
/* border-bottom: 1px solid var(--main-color-lighter); */
|
||||
}
|
||||
|
||||
p a:hover,
|
||||
span a:hover {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
/* ################################################# -- Sliders */
|
||||
aside,
|
||||
.side-nav-block {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* width */
|
||||
aside::-webkit-scrollbar,
|
||||
.side-nav-block::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
aside::-webkit-scrollbar-track,
|
||||
.side-nav-block::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
aside::-webkit-scrollbar-thumb,
|
||||
.side-nav-block::-webkit-scrollbar-thumb {
|
||||
background: #dbe1eb;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
aside::-webkit-scrollbar-thumb:hover,
|
||||
.side-nav-block::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/*############################################# -- Common Actions */
|
||||
|
||||
.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.no-pointer-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pointer-events {
|
||||
pointer-events: visible;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
/*############################################# -- Header */
|
||||
|
||||
header {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo-link-block h1 {
|
||||
font-size: 28px;
|
||||
margin: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.active-page {
|
||||
opacity: 1;
|
||||
border-bottom: 2px solid white;
|
||||
}
|
||||
|
||||
/*############################################# -- Shuffled Text */
|
||||
|
||||
#__next {
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/* .shuffled-text-span span {
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.5s;
|
||||
} */
|
||||
|
||||
@keyframes shuffle {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*############################################# -- Hero Section */
|
||||
.hero-sub-text {
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
color: var(--dark-color);
|
||||
background-color: white;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a:hover {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
/*############################################# -- 404 page */
|
||||
.not-found-page-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*############################################# -- Portfolio page */
|
||||
.portfolio-entries-block {
|
||||
display: grid;
|
||||
gap: 40px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portfolio-entry-block {
|
||||
max-width: 700px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 20px;
|
||||
}
|
||||
/*############################################# -- Contact Forms */
|
||||
form {
|
||||
margin-top: 40px;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
padding: 15px 20px;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
width: 100%;
|
||||
resize: none;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.message-response {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--dark-color);
|
||||
border: 2px solid #688e26;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.message-response.failed {
|
||||
border: 2px solid #d42222;
|
||||
}
|
||||
|
||||
#homepage-animation-wrapper {
|
||||
width: 150%;
|
||||
height: 100%;
|
||||
background-color: var(--dark-color);
|
||||
}
|
||||
|
||||
.tech-stack-header {
|
||||
color: #7174ff;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.portfolio-image-wrapper {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
object-position: left top;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
span img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
/* .transition-fade {
|
||||
transition: 0.4s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
html.is-animating .transition-fade {
|
||||
opacity: 0;
|
||||
} */
|
||||
|
||||
/* ###############################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################### -- Mobile Styles -- ########################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
############################################################################################### */
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
#main-image {
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 990px) {
|
||||
.portfolio-entries-block {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.portfolio-image-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
object-fit: contain;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
}
|
||||
@import url("https://fonts.googleapis.com/css?family=Cutive+Mono&display=swap");
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
/* scroll-behavior: smooth; */
|
||||
font-family: "Cutive Mono", Helvetica;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: #222;
|
||||
letter-spacing: -0.8px;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-color: #7174ff;
|
||||
--main-color-lighter: #9d9eff;
|
||||
--main-color-darker: #7072c9;
|
||||
--main-color-dark: #292a6b;
|
||||
--dark-color: #181515;
|
||||
--sec-color-3: #688e26;
|
||||
--sec-color-4: #adb2d3;
|
||||
--sec-color-5: #c2a878;
|
||||
--light-color-1: rgb(64, 37, 216);
|
||||
--test-color: #7174ff;
|
||||
--transparent-white: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
main {
|
||||
padding-top: 200px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
main {
|
||||
padding-top: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--sec-color-2);
|
||||
}
|
||||
|
||||
button,
|
||||
.button {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
border: 1px solid transparent;
|
||||
color: var(--dark-color);
|
||||
background-color: white;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
button.outlined,
|
||||
.button.outlined {
|
||||
border: 1px solid white;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
hr {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
.button:hover {
|
||||
background-color: var(--dark-color);
|
||||
/* background-color: #c52532; */
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
form * {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
header {
|
||||
z-index: 1000000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 52px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 44px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
p a,
|
||||
span a {
|
||||
color: var(--main-color-lighter);
|
||||
/* border-bottom: 1px solid var(--main-color-lighter); */
|
||||
}
|
||||
|
||||
p a:hover,
|
||||
span a:hover {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
/* ################################################# -- Sliders */
|
||||
aside,
|
||||
.side-nav-block {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* width */
|
||||
aside::-webkit-scrollbar,
|
||||
.side-nav-block::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
aside::-webkit-scrollbar-track,
|
||||
.side-nav-block::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
aside::-webkit-scrollbar-thumb,
|
||||
.side-nav-block::-webkit-scrollbar-thumb {
|
||||
background: #dbe1eb;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
aside::-webkit-scrollbar-thumb:hover,
|
||||
.side-nav-block::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
/*############################################# -- Common Actions */
|
||||
|
||||
.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.no-pointer-events {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pointer-events {
|
||||
pointer-events: visible;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
/*############################################# -- Header */
|
||||
|
||||
header {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo-link-block h1 {
|
||||
font-size: 28px;
|
||||
margin: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.active-page {
|
||||
opacity: 1;
|
||||
border-bottom: 2px solid white;
|
||||
}
|
||||
|
||||
/*############################################# -- Shuffled Text */
|
||||
|
||||
#__next {
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-duration: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
/* .shuffled-text-span span {
|
||||
animation-name: shuffle;
|
||||
animation-timing-function: ease-out;
|
||||
animation-delay: 0.5s;
|
||||
} */
|
||||
|
||||
@keyframes shuffle {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*############################################# -- Hero Section */
|
||||
.hero-sub-text {
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 40px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a {
|
||||
padding: 10px 25px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
color: var(--dark-color);
|
||||
background-color: white;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.hero-ctas-section a:hover {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
border-color: var(--transparent-white);
|
||||
}
|
||||
|
||||
/*############################################# -- 404 page */
|
||||
.not-found-page-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*############################################# -- Portfolio page */
|
||||
.portfolio-entries-block {
|
||||
display: grid;
|
||||
gap: 40px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portfolio-entry-block {
|
||||
max-width: 700px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 20px;
|
||||
}
|
||||
/*############################################# -- Contact Forms */
|
||||
form {
|
||||
margin-top: 40px;
|
||||
max-width: 1000px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
padding: 15px 20px;
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
width: 100%;
|
||||
resize: none;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.message-response {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--dark-color);
|
||||
border: 2px solid #688e26;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.message-response.failed {
|
||||
border: 2px solid #d42222;
|
||||
}
|
||||
|
||||
#homepage-animation-wrapper {
|
||||
width: 150%;
|
||||
height: 100%;
|
||||
background-color: var(--dark-color);
|
||||
}
|
||||
|
||||
.tech-stack-header {
|
||||
color: #7174ff;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.portfolio-image-wrapper {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
object-position: left top;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
span img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
/* * */
|
||||
|
||||
/* .transition-fade {
|
||||
transition: 0.4s;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
html.is-animating .transition-fade {
|
||||
opacity: 0;
|
||||
} */
|
||||
|
||||
/* ###############################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################### -- Mobile Styles -- ########################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
##################################################################################################
|
||||
############################################################################################### */
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
#main-image {
|
||||
position: fixed;
|
||||
top: 40px;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 990px) {
|
||||
.portfolio-entries-block {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.hero-ctas-section {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.portfolio-image-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.portfolio-image {
|
||||
object-fit: contain;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
}
|
||||
|
@ -762,4 +762,4 @@
|
||||
.xl\:pt-0 {
|
||||
padding-top: 0px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,35 @@
|
||||
module.exports = {
|
||||
content: ["./components/**/*.{html,js,jsx}", "./pages/**/*.{jsx,tsx}", "./app/**/*.{html,js,jsx,tsx}", "./layouts/**/*.{jsx,tsx}"],
|
||||
theme: {
|
||||
screens: {
|
||||
xs: "350px",
|
||||
sm: "450px",
|
||||
md: "600px",
|
||||
sl: "800px",
|
||||
lg: "976px",
|
||||
xl: "1200px",
|
||||
},
|
||||
colors: {
|
||||
primary: "#7174ff",
|
||||
primary_light: "#9d9eff",
|
||||
primary_dark: "#7072c9",
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["Graphik", "sans-serif"],
|
||||
serif: ["Merriweather", "serif"],
|
||||
},
|
||||
// extend: {
|
||||
// spacing: {
|
||||
// 128: "32rem",
|
||||
// 144: "36rem",
|
||||
// },
|
||||
// borderRadius: {
|
||||
// "4xl": "2rem",
|
||||
// },
|
||||
// },
|
||||
},
|
||||
plugins: [],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
};
|
||||
module.exports = {
|
||||
content: ["./components/**/*.{html,js,jsx}", "./pages/**/*.{jsx,tsx}", "./app/**/*.{html,js,jsx,tsx}", "./layouts/**/*.{jsx,tsx}"],
|
||||
theme: {
|
||||
screens: {
|
||||
xs: "350px",
|
||||
sm: "450px",
|
||||
md: "600px",
|
||||
sl: "800px",
|
||||
lg: "976px",
|
||||
xl: "1200px",
|
||||
},
|
||||
colors: {
|
||||
primary: "#7174ff",
|
||||
primary_light: "#9d9eff",
|
||||
primary_dark: "#7072c9",
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["Graphik", "sans-serif"],
|
||||
serif: ["Merriweather", "serif"],
|
||||
},
|
||||
// extend: {
|
||||
// spacing: {
|
||||
// 128: "32rem",
|
||||
// 144: "36rem",
|
||||
// },
|
||||
// borderRadius: {
|
||||
// "4xl": "2rem",
|
||||
// },
|
||||
// },
|
||||
},
|
||||
plugins: [],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
};
|
||||
|
@ -1,3 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
28
vercel.json
@ -1,14 +1,14 @@
|
||||
{
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/api/:path*",
|
||||
"destination": "/api/:path*",
|
||||
"headers": [
|
||||
{ "key": "Access-Control-Allow-Credentials", "value": "true" },
|
||||
{ "key": "Access-Control-Allow-Origin", "value": "*" },
|
||||
{ "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
|
||||
{ "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/api/:path*",
|
||||
"destination": "/api/:path*",
|
||||
"headers": [
|
||||
{ "key": "Access-Control-Allow-Credentials", "value": "true" },
|
||||
{ "key": "Access-Control-Allow-Origin", "value": "*" },
|
||||
{ "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
|
||||
{ "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|