This commit is contained in:
Tben 2023-07-19 15:49:15 +01:00
parent b18cbba53d
commit 550d54fdd7
67 changed files with 3098 additions and 2397 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ node_modules
.next .next
node_shell node_shell
.env .env
/dump

View File

@ -0,0 +1,115 @@
"use client";
import React from "react";
import TextShuffler from "../../components/actions/TextShuffler";
import { about } from "../(utils)/animate";
export default function AboutSection() {
React.useEffect(about, []);
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"
id="about-section"
>
{/* <span
className="text-[300px] uppercase absolute whitespace-nowrap opacity-5 -mt-64 z-[-1]"
id="about-me-label"
>
About Me
</span> */}
<div className="h-44"></div>
<div className="flex flex-col xl:flex-row w-full gap-8">
<div className="w-full xl:w-[40%]">
<h2>
<TextShuffler textInput="About Me" />
</h2>
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
<div className="flex flex-col w-full gap-2">
<div
className={"p-4 cursor-pointer hover:opacity-60" + (targetStack.match(/dev/i) ? " bg-[#343680] w-full xl:w-[120%]" : " w-full border border-solid border-white/10")}
onClick={() => {
setTargetStack("dev");
}}
>
<div>Web Dev Stack</div>
</div>
<div
className={"p-4 cursor-pointer hover:opacity-60" + (targetStack.match(/design/i) ? " bg-[#343680] w-full xl:w-[120%]" : " w-full border border-solid border-white/10")}
onClick={() => {
setTargetStack("design");
}}
>
<div>UI/UX Stack</div>
</div>
</div>
<span className="hero-sub-text">
<TextShuffler
textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done."
delay={500}
/>
</span>
<a
href="/about"
className="button"
>
Learn More About Me
</a>
</div>
</div>
<div className="w-full xl:w-[60%] bg-[#343680] px-4 md:px-6 py-2">
<section>
<h3>
<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">
<TextShuffler textInput={item.title} />
</h4>
<span className="opacity-80">
<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">
<TextShuffler textInput={item.title} />
</h4>
<span className="opacity-80">
<TextShuffler textInput={item.description} />
</span>
</li>
))}
</React.Fragment>
)}
</ul>
</section>
</div>
</div>
</div>
);
}

76
app/(components)/Hero.tsx Normal file
View File

@ -0,0 +1,76 @@
"use client";
import React from "react";
import Image from "next/image";
import TextShuffler from "../../components/actions/TextShuffler";
import { hero } from "../(utils)/animate";
export default function Hero() {
React.useEffect(hero, []);
return (
<div>
<div className="flex flex-col xl:flex-row items-center gap-0 justify-center -mt-[120px] xl:-mt-[160px] w-full relative pt-[450px] md:pt-0">
<div
className="rounded-full max-w-[450px] absolute md:relative top-0 flex items-center md:items-start justify-center mr-0 xl:-mr-14 bg-[#3e3f9c] overflow-hidden"
id="main-image"
>
<Image
src="/images/my-photo.png"
// fill
width={500}
height={562}
objectFit="contain"
alt="Benjamin Toby Image"
className="contrast-[140%] grayscale mt-20 flex items-center justify-center"
/>
</div>
<div
className="max-w-2xl relative z-10 mt-10 w-full"
id="hero-text-section"
>
<h1
className="text-5xl leading-snug"
id="hero-text"
>
{/* Hi */}
{/* Fullstack developer, UI/UX designer, Software Engineer, welcome */}
<TextShuffler
textInput="I'm Benjamin Toby, a Software Engineer and UI/UX expert"
delay={500}
/>
</h1>
<div className="gap-4 flex items-center">
<a
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
download={true}
className="button"
>
See my resume
</a>
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
className="button"
>
Linkedin
</a>
<a
href="/contact"
style={{
backgroundColor: "transparent",
color: "white",
border: "2px solid white",
}}
className="button"
>
Contact Me
</a>
</div>
</div>
</div>
</div>
);
}

6
app/(styles)/header.css Normal file
View File

@ -0,0 +1,6 @@
header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}

149
app/(utils)/animate.ts Normal file
View File

@ -0,0 +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;
},
}
);
}

View File

@ -0,0 +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"
}
]

View File

@ -0,0 +1,42 @@
[
{
"title": "HTML, CSS, Javascript",
"description": "The basics, the bedrock of all websites."
},
{
"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"
}
]

View File

@ -0,0 +1,35 @@
"use client";
import React from "react";
import TextShuffler from "../../../components/actions/TextShuffler";
import { appear, genericScroll } from "../../(utils)/animate";
export default function Hero() {
React.useEffect(genericScroll, []);
React.useEffect(appear, []);
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">
<TextShuffler textInput="About Me" />
</span>
<h1
className="text-5xl leading-snug"
id="hero-text"
>
<TextShuffler textInput="Ben of All Trade, Master of All" />
</h1>
<span className="hero-sub-text">
<TextShuffler
textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done."
delay={1000}
/>
</span>
</div>
</div>
);
}

View File

@ -0,0 +1,97 @@
"use client";
import React from "react";
import TextShuffler from "../../../components/actions/TextShuffler";
import { appear, genericScroll } from "../../(utils)/animate";
export default function MoreAboutMe() {
React.useEffect(genericScroll, []);
React.useEffect(appear, []);
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>
<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>
<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">
<TextShuffler textInput={item.title} />
</h4>
<span className="opacity-80">
<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">
<TextShuffler textInput={item.title} />
</h4>
<span className="opacity-80">
<TextShuffler textInput={item.description} />
</span>
</li>
))}
</React.Fragment>
)}
</ul>
</section>
</div>
</div>
</div>
);
}

12
app/about/page.tsx Normal file
View File

@ -0,0 +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>
);
}

31
app/layout.tsx Normal file
View File

@ -0,0 +1,31 @@
// 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
src="/scripts/main.js"
defer
></script>
</head>
<body>
<GeneralLayout>{children}</GeneralLayout>
</body>
</html>
);
}

12
app/page.tsx Normal file
View File

@ -0,0 +1,12 @@
import React from "react";
import Hero from "./(components)/Hero";
import AboutSection from "./(components)/AboutSection";
export default function Homepage() {
return (
<React.Fragment>
<Hero />
<AboutSection />
</React.Fragment>
);
}

View File

@ -1,167 +0,0 @@
import React, { Fragment, useEffect, useState } from 'react';
import { gsap } from "gsap";
let timer = 0;
let keyNum = 0;
let textTimeout;
let textInterval;
// let chars = ("abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+=-;.,/?><][}{:`~").split("");
let interval = 200;
const TextShuffler = ({ textInput, delay }) => {
// let [text, setText] = useState(textInput);
// let [refresh, setRefresh] = useState(0);
let [readyState, setReadyState] = useState(false);
const spanRef = React.useRef();
useEffect(() => {
const spanObserver = new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
if (delay) {
setTimeout(() => {
setReadyState(true);
}, delay);
} else {
setReadyState(true);
}
observer.unobserve(spanRef.current)
}
}, {
rootMargin: "0px 0px 0px 0px"
});
spanObserver.observe(spanRef.current)
}, [])
// useEffect(() => {
// if (!readyState) return;
// requestAnimationFrame(() => {
// textTimeout = setTimeout(() => {
// if (timer === 7) {
// setText(textInput);
// return window.clearTimeout(textTimeout);
// };
// setRefresh(prev => prev + 1);
// timer++
// }, 300);
// })
// }, [refresh, readyState])
useEffect(() => {
if (!readyState) return;
let chars = textInput.split("");
let charsSpans = chars.map(char => `<span style="opacity:0">${char}</span>`)
spanRef.current.innerHTML = charsSpans.join("");
gsap.to(spanRef.current, {
opacity: 1,
duration: 1
});
let textSpans = spanRef.current.querySelectorAll("span");
// textInterval = setInterval(() => {
// if (timer >= 600) {
// window.clearInterval(textInterval);
// requestAnimationFrame(() => {
// textSpans.forEach(span => {
// // gsap.killTweensOf(span, "opacity");
// gsap.to(span, {
// opacity: 1,
// duration: Math.random(),
// delay: Math.random()
// })
// })
// })
// return;
// }
// requestAnimationFrame(() => {
// textSpans.forEach(span => {
// gsap.to(span, {
// opacity: 1,
// duration: Math.random() * 2,
// delay: Math.random() * 1.5
// })
// })
// // textSpans.forEach(span => {
// // gsap.to(span, {
// // opacity: 0,
// // duration: Math.random() * 2,
// // delay: Math.random() * 1.5
// // })
// // })
// // gsap.to(spanRef.current, {
// // opacity: 0,
// // duration: 2
// // });
// timer += interval;
// })
// }, interval)
requestAnimationFrame(() => {
// let charsSpans = chars.map(char => `<span>${char}</span>`)
// spanRef.current.innerHTML = charsSpans.join("");
// let textSpans = spanRef.current.querySelectorAll("span");
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 } }>
{/* { text.split("").map(char => <span key={ keyNum++ } style={ { animationDelay: Math.random() * 1.5 + "s", animationDuration: (Math.random() * 2) + "s" } }>{ char }</span>) } */ }
{ textInput }
</span>
)
}
export default TextShuffler

View File

@ -0,0 +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;

View File

@ -0,0 +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;

View File

@ -2,7 +2,6 @@ import * as THREE from "three";
export default function threeJsAnimations() { export default function threeJsAnimations() {
const animationWrapper = document.getElementById("homepage-animation-wrapper"); const animationWrapper = document.getElementById("homepage-animation-wrapper");
console.log(THREE);
const scene = new THREE.Scene(); const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, animationWrapper.clientWidth / animationWrapper.clientHeight, 0.1, 1000); const camera = new THREE.PerspectiveCamera(75, animationWrapper.clientWidth / animationWrapper.clientHeight, 0.1, 1000);
@ -16,13 +15,6 @@ export default function threeJsAnimations() {
const sphereSize = 1; const sphereSize = 1;
const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize); const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
// const spotLight = new THREE.SpotLight(0xffffff);
// spotLight.position.set(10, 10, 10);
// scene.add(spotLight);
// const spotLightHelper = new THREE.SpotLightHelper(spotLight);
// scene.add(spotLightHelper);
const renderer = new THREE.WebGLRenderer(); const renderer = new THREE.WebGLRenderer();
renderer.setSize(animationWrapper.clientWidth, animationWrapper.clientHeight); renderer.setSize(animationWrapper.clientWidth, animationWrapper.clientHeight);
animationWrapper.appendChild(renderer.domElement); animationWrapper.appendChild(renderer.domElement);
@ -122,21 +114,10 @@ export default function threeJsAnimations() {
relMouseX < 0 ? (cube3.position.x += 0.0007) : (cube3.position.x -= 0.0007); relMouseX < 0 ? (cube3.position.x += 0.0007) : (cube3.position.x -= 0.0007);
relMouseY > 0 ? (cube3.position.y += 0.0007) : (cube3.position.y -= 0.0007); relMouseY > 0 ? (cube3.position.y += 0.0007) : (cube3.position.y -= 0.0007);
// relMouseX < 0 ? scene.rotateX(5) : scene.rotateX(-5);
// relMouseY > 0 ? scene.rotateX(5) : scene.rotateX(-5);
// relMouseX < 0 ? (scene.rotation.z += 0.005) : (scene.rotation.x += 0.005);
// relMouseY > 0 ? (scene.rotation.y += 0.005) : (scene.rotation.y += 0.005);
// cube.rotation.x += relMouseX / e.x;
// cube.rotation.y += relMouseY / e.y;
// scene.rotation.x;
}); });
window.addEventListener("resize", (e) => { window.addEventListener("resize", (e) => {
renderer.setSize(animationWrapper.clientWidth, window.innerHeight); renderer.setSize(animationWrapper.clientWidth, window.innerHeight);
// animationWrapper.querySelector("canvas").width = animationWrapper.clientWidth;
// animationWrapper.querySelector("canvas").height = animationWrapper.clientHeight;
}); });
window.addEventListener("scroll", (e) => { window.addEventListener("scroll", (e) => {

View File

@ -0,0 +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;

View File

@ -1,15 +0,0 @@
import React from 'react'
const GeneralFooter = () => {
const date = new Date();
return (
<footer className='mt-10'>
{/* <div className='flex-col flex'>
<a href="/" className='text-xl font-bold mb-1'>Tben.me</a>
</div> */}
<span className='text-sm opacity-40'>Copyright © { date.getFullYear() } Tben.me. All Rights Reserved.</span>
</footer>
)
}
export default GeneralFooter

View File

@ -0,0 +1,42 @@
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="/">LinkedIn</a>
<a href="/">Mail</a>
<a href="/">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;

View File

@ -1,45 +0,0 @@
import React from 'react'
import { useRouter } from 'next/router';
const GeneralHeader = () => {
const router = useRouter();
function pushRouter(e) {
e.preventDefault();
let url = e.target.dataset.href;
router.push(url);
}
return (
<header>
<a className="logo-link-block" onClick={ () => {
pushRouter("/")
} }><h1>Tben.me</h1></a>
<nav>
<a href='/' data-href="/" onClick={ (e) => {
pushRouter(e)
} }>Home</a>
<a href='/about' data-href="/about" onClick={ (e) => {
pushRouter(e)
} }>About Me</a>
<a href='/work' data-href="/work" onClick={ (e) => {
pushRouter(e)
} }>My Work</a>
<a href='/blog' data-href="/blog" onClick={ (e) => {
pushRouter(e)
} }>Blog</a>
<a data-href="#" href='/documents/Benjamin-Toby-CV-7-27-2022.pdf' target="_blank">My Resume</a>
<a href='/contact' data-href="/contact" onClick={ (e) => {
pushRouter(e)
} }>Contact Me</a>
</nav>
</header>
)
}
export default GeneralHeader

View File

@ -0,0 +1,73 @@
import React from "react";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { gsap } from "gsap";
/**
* General Header for all pages
*/
const GeneralHeader = (): React.ReactElement => {
const links: { title: string; url: string }[] = require("./links.json");
/**
* 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,
}
);
}, []);
return (
<header
id="main-header"
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 relative z-10"
>
<a
href="/"
data-href="/"
className="logo-link-block"
>
<Image
src="/images/logo-white.svg"
width={50}
height={100}
alt="Logo"
/>
</a>
<nav>
{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>
);
})}
</nav>
</header>
);
};
export default GeneralHeader;

View File

@ -1,94 +0,0 @@
import React, { Fragment } from 'react';
import GeneralHeader from './GeneralHeader';
import Head from "next/head"
import GeneralFooter from './GeneralFooter';
import { gsap } from "gsap";
import threeJsAnimations from '../../functions/frontend/threeJsAnimations';
export const SiteContext = React.createContext();
const GeneralLayout = ({ children, pageName }) => {
const [readyState, setReadyState] = React.useState(false);
// React.useEffect(() => {
// setReadyState(true);
// }, [readyState]);
React.useEffect(() => {
// barba.init({
// transitions: [{
// name: 'default-transition',
// leave() {
// // create your stunning leave animation here
// return gsap.to("main", {
// opacity: 1,
// duration: 1,
// delay: 2
// })
// },
// enter() {
// // create your amazing enter animation here
// return gsap.from("main", {
// opacity: 0,
// duration: 1,
// delay: 2
// })
// }
// }]
// });
// window.addEventListener("load", () => {
// })
// setReadyState(true);
document.querySelectorAll("nav a").forEach((link) => {
let locationRegex = new RegExp(`${window.location.pathname}.*?`);
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,
})
// gsap.to("#page-loader", {
// opacity: 0,
// duration: 0.5,
// delay: 1,
// pointerEvents: "none"
// })
// document.getElementById("page-loader").style.opacity
threeJsAnimations();
}, [])
return (
<SiteContext.Provider value={ { readyState, setReadyState } }>
<Head>
<meta name="keywords" content="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" />
</Head>
<div id='main-content-wrapper' style={ { opacity: 0 } }>
<GeneralHeader />
<main>
{ children }
</main>
<GeneralFooter />
<div className='fixed top-0 left-0 -z-10' id='homepage-animation-wrapper'></div>
</div>
</SiteContext.Provider>
)
}
export default GeneralLayout

View File

@ -0,0 +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 = 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;

View File

@ -0,0 +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"
}
]

6
next-env.d.ts vendored Normal file
View File

@ -0,0 +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.

View File

@ -3,4 +3,14 @@ module.exports = {
eslint: { eslint: {
ignoreDuringBuilds: true, ignoreDuringBuilds: true,
}, },
images: {
remotePatterns: [
{
protocol: "https",
hostname: "static.datasquirel.com",
port: "",
pathname: "/images/**",
},
],
},
}; };

2038
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,14 +27,19 @@
"dependencies": { "dependencies": {
"@barba/core": "^2.9.7", "@barba/core": "^2.9.7",
"contentful": "^9.1.32", "contentful": "^9.1.32",
"datasquirel": "^1.1.82",
"gsap": "^3.10.4", "gsap": "^3.10.4",
"next": "^12.0.4", "next": "^13.4.10",
"nodemailer": "^6.7.2", "nodemailer": "^6.7.2",
"react": "^17.0.2", "react": "^18.2.0",
"react-dom": "^17.0.2", "react-dom": "^18.2.0",
"sanitize-html": "^2.11.0",
"three": "^0.138.3" "three": "^0.138.3"
}, },
"devDependencies": { "devDependencies": {
"tailwindcss": "^3.0.23" "@types/node": "^20.4.2",
"@types/react": "^18.2.15",
"tailwindcss": "^3.0.23",
"typescript": "^5.1.6"
} }
} }

View File

@ -1,20 +0,0 @@
import React from 'react'
import TextShuffler from '../components/actions/TextShuffler'
import { textSHuffle } from '../functions/frontend/textShuffle'
import GeneralLayout from '../layouts/general_layout/GeneralLayout'
const index = () => {
return (
<div className='not-found-page-wrapper'>
<h1><TextShuffler textInput="404" /></h1>
<span className='hero-sub-text'>
<TextShuffler textInput="Oops ... page not found" />
</span>
<div className="hero-ctas-section">
<a href='/'>Go Back Home</a>
</div>
</div>
)
}
export default index

View File

@ -2,7 +2,9 @@ import "../styles/main.css";
import "../styles/tw_main.css"; import "../styles/tw_main.css";
import { Fragment } from "react"; import { Fragment } from "react";
function MyApp({ Component, pageProps }) { import type { AppProps } from "next/app";
function MyApp({ Component, pageProps }: AppProps) {
return ( return (
<Fragment> <Fragment>
<Component {...pageProps} /> <Component {...pageProps} />

View File

@ -5,11 +5,12 @@ export default function Document() {
return ( return (
<Html> <Html>
<Head> <Head>
{/* <script src="https://unpkg.com/@barba/core"></script> */} <script
{/* <script src="/scripts/swup.js"></script> */} src="/scripts/main.js"
<script src="/scripts/main.js" defer></script> defer
></script>
</Head> </Head>
<body className="bg-black"> <body className="w-full">
<Main /> <Main />
<NextScript /> <NextScript />
</body> </body>

View File

@ -1,139 +0,0 @@
import React from 'react'
import Head from 'next/head'
import TextShuffler from '../components/actions/TextShuffler'
import GeneralLayout from '../layouts/general_layout/GeneralLayout'
import threeJsAnimations from '../functions/frontend/threeJsAnimations'
const about = () => {
return (
<GeneralLayout pageName="about">
<Head>
<title>About Me</title>
<meta name="description" content="Ben of all trade, master of all! Learn more about me" />
</Head>
<h1><TextShuffler textInput="Ben of all trade, master of all!" /></h1>
<span className='hero-sub-text'>
<TextShuffler textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done." delay={ 500 } />
</span>
<div className='w-full h-6'></div>
<section>
<h2><TextShuffler textInput="Web Dev Tech Stack" /></h2>
<ul style={ { maxWidth: "800px" } }>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="HTML, CSS, Javascript" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="The basics, the bedrock of all websites." />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="React JS" /></h3>
<span className='opacity-50'>
<TextShuffler textInput=" JavaScript library for high-performance web applications" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Next JS" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="High performance React and Node js web framework for building blazing flast and performant web applications" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Tailwind CSS" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Lighting fast mobile first styling" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Node JS" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="JavaScript runtime for the server. For creating backend architectures and APIs" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Ubuntu Linux" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Secure server management with ubuntu and Linux" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Nginx" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Super secure web server, reverse proxy and load balancer" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="MySQL" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Tried and tested data storage, querying, and management." />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Git and Github" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Version control" />
</span>
</li>
</ul>
</section>
<section>
<h2><TextShuffler textInput="UI/UX tech Stack" /></h2>
<ul style={ { maxWidth: "800px" } }>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Adobe Photoshop" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Web design, image manipulation, image compositing, and more" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Adobe Illustrator" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Vector graphic of all types" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Figma" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Web, UI, UX design." />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Affinity Designer" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Vector graphics." />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="After Effects" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Motion graphics and animation" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Adobe XD" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="UI/UX design" />
</span>
</li>
<li>
<h3 className="tech-stack-header"><TextShuffler textInput="Webflow" /></h3>
<span className='opacity-50'>
<TextShuffler textInput="Visual Web coding" />
</span>
</li>
</ul>
</section>
<div className="hero-ctas-section">
<a href='/documents/Benjamin_Toby_CV-updated.pdf' download={ true }>See my resume</a>
<a href='https://www.linkedin.com/in/benjamin-toby/' target="_blank">Linkedin</a>
</div>
</GeneralLayout>
)
}
export default about

View File

@ -1,57 +0,0 @@
/**
*
*
* Imports
* ------------------------------------------------------------------------------
*
*/
const fs = require("fs");
const nodemailer = require("nodemailer");
/** ********************* Functions and Other API Imports */
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
user: "benoti.san@gmail.com",
pass: "dkxxbvomzyqfyfuq",
},
});
/**
*
*
* API handler
* ------------------------------------------------------------------------------
* @param {Object} req - http incoming request
* @param {Object} res - http response
*
*/
export default async function handler(req, res) {
/** ********************* Get Page Data */
if (req.method === "POST") {
let name = req.body.name;
let email = req.body.email;
let message = req.body.message;
console.log("Message Sending ...");
try {
// send mail with defined transport object
let info = await transporter.sendMail({
from: email, // sender address
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com", // list of receivers
subject: "Tben.me | Client Message", // Subject line
text: "Hello from tben",
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>`, // html body
});
console.log("Message sent: %s", info.messageId);
res.json({ msg: "Success", info: info });
} catch (error) {
console.log(error);
res.json({ msg: "Failed" });
}
}
}

49
pages/api/contactForm.ts Normal file
View File

@ -0,0 +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.OUTLOOK_HOST,
port: 587,
auth: {
user: process.env.OUTLOOK_EMAIL,
pass: process.env.OUTLOOK_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: 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" });
}
}
}

View File

@ -1,302 +0,0 @@
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
import Head from "next/head";
const https = require("https");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
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 }) {
// ## 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-3xl">
<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-3xl w-full gap-4 text-lg" dangerouslySetInnerHTML={ { __html: blogPost.body } }></span>
</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 getStaticProps({ params }) {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
const postsResponse = await new Promise((resolve, reject) => {
https
.get(
/** ********************* Get Options object */
{
host: "datasquirel.com",
path: `/api/query/get?db=tbenme&query=select+*+from+blog_posts+where+slug='${params.single}'`,
headers: {
Authorization: process.env.DATASQUIREL_API_KEY,
},
},
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************* Callback function */
(response) => {
var str = "";
// ## another chunk of data has been received, so append it to `str`
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))
});
response.on("error", (err) => {
console.log(err);
});
}
)
.end();
});
// if (!postsResponse.success || !postsResponse.payload[0]) {
// return {
// redirect: {
// destination: "/blog",
// permanent: false
// }
// }
// }
const post = postsResponse.payload[0];
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
blogPost: post,
},
revalidate: 3600
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* 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 getStaticPaths() {
/**
* Data fetching
*
* @abstract fetch date from the server or externnal source
*/
const postsResponse = await new Promise((resolve, reject) => {
https
.get(
/** ********************* Get Options object */
{
host: "datasquirel.com",
path: `/api/query/get?db=tbenme&query=select+slug+from+blog_posts`,
headers: {
Authorization: process.env.DATASQUIREL_API_KEY,
},
},
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************* Callback function */
(response) => {
var str = "";
// ## another chunk of data has been received, so append it to `str`
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))
});
response.on("error", (err) => {
console.log(err);
});
}
)
.end();
});
// if (!postsResponse.success) {
// return {
// redirect: {
// destination: "/blog",
// permanent: false
// }
// }
// }
const posts = postsResponse.payload;
const paths = posts.map((entry) => {
return {
params: { single: entry.slug }
}
})
return {
paths: paths,
fallback: "blocking",
}
}
// Legacy
// { blogPost.body.map((element) => {
// reactKey++;
// if (element.tag.match(/img/i)) {
// return <img
// key={ reactKey }
// src={ element.src }
// width={ element.width }
// height={ element.height }
// className={ element.class }
// alt={ element.alt }
// style={ element.style }
// />
// }
// function construtElement(elementEntry) {
// if (elementEntry.children) {
// return (
// <elementEntry.tag
// key={ reactKey }
// className={ elementEntry.class ? elementEntry.class : null }
// href={ elementEntry.href }
// style={ element.style }
// >
// { elementEntry.children.map(child => construtElement(child)) }
// </elementEntry.tag>
// )
// }
// return (
// <elementEntry.tag
// key={ reactKey }
// className={ elementEntry.class ? elementEntry.class : null }
// href={ elementEntry.href }
// >
// { elementEntry.content }
// </elementEntry.tag>
// )
// }
// return construtElement(element);
// }
// ) }

178
pages/blog/[single].tsx Normal file
View File

@ -0,0 +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",
};
};

View File

@ -6,9 +6,8 @@
import React from "react"; import React from "react";
import Head from "next/head"; import Head from "next/head";
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPropsContext } from "next";
// const contentful = require('contentful'); const datasquirel = require("datasquirel");
const https = require("https")
/** ********************************************** */ /** ********************************************** */
/** ********************************************** */ /** ********************************************** */
@ -31,7 +30,7 @@ import TextShuffler from "../../components/actions/TextShuffler";
* ============================================================================== * ==============================================================================
* @param {Object} props - Server props * @param {Object} props - Server props
*/ */
export default function BlogIndex(props) { export default function BlogIndex(props: InferGetStaticPropsType<typeof getStaticProps>) {
/** /**
* Get Contexts * Get Contexts
* *
@ -65,29 +64,42 @@ export default function BlogIndex(props) {
<React.Fragment> <React.Fragment>
<Head> <Head>
<title>Blog | Tben.me</title> <title>Blog | Tben.me</title>
<meta name="description" content="Tech talks" /> <meta
name="description"
content="Tech talks"
/>
</Head> </Head>
<GeneralLayout> <GeneralLayout>
<h1 className="mb-8"><TextShuffler textInput="My Blog" /></h1> <div className="flex flex-col items-start max-w-6xl w-full">
<h1 className="mb-8">
<TextShuffler textInput="My Blog" />
</h1>
<div className="flex flex-col items-start w-full gap-4"> <div className="flex flex-col items-start w-full gap-4">
{ props.blogPosts.map(post => {props.blogPosts.map((post: { slug: string; title: string; excerpt: string; date_created: string }, index: number) => (
<a <a
key={index}
href={`/blog/${post.slug}`} 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" 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"
key={ post.slug }
> >
<h2 className="m-0"><TextShuffler textInput={ post.title } /></h2> <h2 className="m-0">
<span className="opacity-80"><TextShuffler textInput={ post.excerpt } /></span> <TextShuffler textInput={post.title} />
<span className="text-sm opacity-50"><TextShuffler textInput={ post.date_created.substring(0, 24) } /></span> </h2>
<span className="opacity-80">
<TextShuffler textInput={post.excerpt} />
</span>
<span className="text-sm opacity-50">
<TextShuffler textInput={post.date_created.substring(0, 24)} />
</span>
</a> </a>
) } ))}
</div>
</div> </div>
</GeneralLayout> </GeneralLayout>
</React.Fragment> </React.Fragment>
); );
/** ********************************************** */ /** ********************************************** */
}; }
/** ****************************************************************************** */ /** ****************************************************************************** */
/** ****************************************************************************** */ /** ****************************************************************************** */
@ -97,27 +109,15 @@ export default function BlogIndex(props) {
/** ****************************************************************************** */ /** ****************************************************************************** */
/** /**
* ==============================================================================
* Server Side Props or Static Props * 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 getStaticProps({ req, res, query }) { export const getStaticProps: GetStaticProps = async ({ params }: GetStaticPropsContext) => {
/** /**
* User Auth * User Auth
* *
* @abstract grab user * @abstract grab user
*/ */
// const contentfulClient = contentful.createClient({
// space: process.env.CONTENTFUL_SPACE_ID,
// accessToken: process.env.CONTENTFUL_API_KEY,
// });
// const posts = await contentfulClient.getEntries()
// console.log(posts);
/** ********************************************** */ /** ********************************************** */
@ -126,59 +126,19 @@ export async function getStaticProps({ req, res, query }) {
* *
* @abstract fetch date from the server or externnal source * @abstract fetch date from the server or externnal source
*/ */
// let blogPosts = await httpFetch({ const postsResponse = await datasquirel.get({
// options: { key: process.env.DATASQUIREL_API_KEY,
// method:"DELETE", db: "tbenme",
// path: "/api/tben-blogs" query: "select title,slug,excerpt,date_created from blog_posts limit 10",
// },
// paradigm:"https"
// })
// console.log(blogPosts.data);
const postsResponse = await new Promise((resolve, reject) => {
https
.get(
/** ********************* Get Options object */
{
host: "datasquirel.com",
path: `/api/query/get?db=tbenme&query=select+title,slug,excerpt,date_created+from+blog_posts+limit+10`,
headers: {
Authorization: process.env.DATASQUIREL_API_KEY,
},
},
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************* Callback function */
(response) => {
var str = "";
// ## another chunk of data has been received, so append it to `str`
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))
});
response.on("error", (err) => {
console.log(err);
});
}
)
.end();
}); });
if (!postsResponse.success) { if (!postsResponse.success) {
return { return {
redirect: { redirect: {
destination: "/", destination: "/",
permanent: false permanent: false,
} },
} };
} }
const posts = postsResponse.payload; const posts = postsResponse.payload;
@ -192,10 +152,10 @@ export async function getStaticProps({ req, res, query }) {
props: { props: {
blogPosts: posts, blogPosts: posts,
}, },
revalidate: 3600 revalidate: 3600,
}; };
/** ********************************************** */ /** ********************************************** */
/** ********************************************** */ /** ********************************************** */
/** ********************************************** */ /** ********************************************** */
} };

View File

@ -1,36 +1,88 @@
import React from 'react' import React from "react";
import Head from 'next/head' import Head from "next/head";
import TextShuffler from '../components/actions/TextShuffler' import TextShuffler from "../components/actions/TextShuffler";
import submitContactForm from '../functions/frontend/submitContactForm' import submitContactForm from "../functions/frontend/submitContactForm";
import GeneralLayout from '../layouts/general_layout/GeneralLayout' import GeneralLayout from "../layouts/general_layout/GeneralLayout";
import threeJsAnimations from '../functions/frontend/threeJsAnimations' import threeJsAnimations from "../functions/frontend/threeJsAnimations";
const contact = () => { const contact = () => {
let [success, setSuccess] = React.useState(false); let [success, setSuccess] = React.useState(false);
return ( return (
<GeneralLayout> <GeneralLayout>
<Head> <Head>
<title>Contact me</title> <title>Contact me</title>
<meta name="description" content="Get in touch" /> <meta
name="description"
content="Get in touch"
/>
</Head> </Head>
<h1><TextShuffler textInput="Great things await ..." /></h1> <div className="flex flex-col items-start max-w-6xl w-full">
<span className='hero-sub-text'> <h1>
<TextShuffler textInput="Let's talk" delay={ 500 } /> <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> </span>
<form autoComplete='on' onSubmit={ (e) => { submitContactForm(e, setSuccess) } }> <form
<input type="text" placeholder='Your Name' autoComplete='name' required /> autoComplete="on"
<input type="email" placeholder='Your Email Address' autoComplete='email' required /> onSubmit={(e) => {
<textarea name="message" id="contact-form-message" cols="30" rows="10" placeholder='Message'></textarea> submitContactForm(e, setSuccess);
}}
>
<input
type="text"
placeholder="Your Name"
autoComplete="name"
required
/>
<input
type="email"
placeholder="Your Email Address"
autoComplete="email"
required
/>
<textarea
name="message"
id="contact-form-message"
cols="30"
rows="10"
placeholder="Message"
></textarea>
<button type="submit">Submit</button> <button type="submit">Submit</button>
{ success === "Success" && <div className='message-response'>Success!!! <button onClick={ () => { window.location.reload() } }>Reload</button></div> } {success === "Success" && (
{ success === "Failed" && <div className='message-response failed'>Failed <button onClick={ () => { window.location.reload() } }>Reload</button></div> } <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> </form>
</div>
</GeneralLayout> </GeneralLayout>
) );
} };
export default contact export default contact;

View File

@ -1,35 +0,0 @@
import Head from 'next/head'
import React from 'react'
import TextShuffler from '../components/actions/TextShuffler'
import threeJsAnimations from '../functions/frontend/threeJsAnimations'
import GeneralLayout from '../layouts/general_layout/GeneralLayout'
const index = () => {
return (
<React.Fragment>
<Head>
<title>Benjamin Toby | Fullstack developer, UI UX designer</title>
<meta name="description" content="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" />
</Head>
<GeneralLayout pageName="home">
<h1><TextShuffler textInput="UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer" /></h1>
<span className='hero-sub-text'>
<TextShuffler textInput="Hi, I'm Benjamin Toby, a fullstack web developer and UI/UX expert." delay={ 500 } />
</span>
<div className="hero-ctas-section">
<a href='/documents/Benjamin_Toby_CV-updated.pdf' download={ true }>See my resume</a>
<a href='https://www.linkedin.com/in/benjamin-toby/' target="_blank">Linkedin</a>
<a href='/contact' style={ {
backgroundColor: "transparent",
color: "white",
border: "2px solid white"
} }>Contact Me</a>
</div>
</GeneralLayout>
</React.Fragment>
)
}
export default index

View File

@ -1,8 +1,8 @@
import React from 'react' import React from "react";
import Head from 'next/head' import Head from "next/head";
import TextShuffler from '../components/actions/TextShuffler' import TextShuffler from "../components/actions/TextShuffler";
import GeneralLayout from '../layouts/general_layout/GeneralLayout' import GeneralLayout from "../layouts/general_layout/GeneralLayout";
import PortfolioEntry from '../components/PortfolioEntry' import PortfolioEntry from "../components/PortfolioEntry";
const myWork = () => { const myWork = () => {
const portfolioEntries = require("../components/portfolioEntries.json"); const portfolioEntries = require("../components/portfolioEntries.json");
@ -11,21 +11,40 @@ const myWork = () => {
<GeneralLayout> <GeneralLayout>
<Head> <Head>
<title>My Work | Tben</title> <title>My Work | Tben</title>
<meta name="description" content="Some of my Work" /> <meta
name="description"
content="Some of my Work"
/>
</Head> </Head>
<h1><TextShuffler textInput="My Work" /></h1>
<span className='hero-sub-text'> <div className="flex flex-col items-start w-full max-w-6xl">
<TextShuffler textInput="Some of my work ..." delay={ 500 } /> <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> </span>
<div className='portfolio-entries-block mt-4'> <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 } />) } {portfolioEntries.map((entry) => (
<PortfolioEntry
key={entry.title}
title={entry.title}
description={entry.description}
url={entry.url}
image={entry.image}
/>
))}
</div>
</div> </div>
</GeneralLayout> </GeneralLayout>
) );
} };
export default myWork export default myWork;
// { // {
// "title": "Stirrmedia Social webapp", // "title": "Stirrmedia Social webapp",

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 320 B

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 611 611" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<rect id="Artboard1" x="0" y="0" width="610.63" height="610.63" style="fill:none;"/>
<clipPath id="_clip1">
<rect id="Artboard11" serif:id="Artboard1" x="0" y="0" width="610.63" height="610.63"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g id="Layer_2">
<g id="Layer_1-2">
<path d="M610.63,515.37L610.63,619.37L590.72,607.63L461.72,531.55L306.83,617L151.94,702.39L138.33,709.89L0,786.14L0,0L362,0C434.173,0 490.98,15.707 532.42,47.12C534.38,48.613 536.297,50.137 538.17,51.69C575.177,82.423 593.98,125.773 594.58,181.74L594.58,184.49C594.58,225.95 583.717,260.877 561.99,289.27C540.263,317.663 511.353,336.547 475.26,345.92C518.027,355.92 551.283,376.477 575.03,407.59C598.777,438.703 610.643,474.63 610.63,515.37ZM368.49,519.89C378.163,512.21 383,499.673 383,482.28C383,448.193 363.28,431.15 323.84,431.15L222.6,431.15L222.6,531.42L323.87,531.42C343.923,531.42 358.797,527.577 368.49,519.89ZM368,227.61C368,210.23 363.153,197.36 353.46,189C343.767,180.64 328.893,176.463 308.84,176.47L222.6,176.47L222.6,276.74L308.83,276.74C328.877,276.74 343.75,272.74 353.45,264.74C363.15,256.74 368,244.363 368,227.61Z" style="fill:rgb(99,105,176);fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 610.63 1042.32"><defs><style>.cls-1{fill:#565e9a;}.cls-2{fill:#6369b0;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><polygon class="cls-1" points="610.63 784.67 610.63 952.95 461.71 1042.32 461.71 874.03 464.52 872.35 610.63 784.67"/><polygon class="cls-1" points="610.63 619.33 610.63 784.67 461.71 702.53 468.56 698.7 563.9 645.44 610.63 619.33"/><path class="cls-2" d="M610.63,515.37v104l-19.91-11.74-129-76.08L306.83,617,151.94,702.39l-13.61,7.5L0,786.14V0H362Q470.26,0,532.42,47.12q2.94,2.24,5.75,4.57,55.51,46.1,56.41,130.05c0,.92,0,1.83,0,2.75q0,62.19-32.59,104.78t-86.73,56.65q64.15,15,99.77,61.67T610.63,515.37Zm-242.14,4.52Q383,508.37,383,482.28q0-51.13-59.16-51.13H222.6V531.42H323.87Q353.95,531.42,368.49,519.89ZM368,227.61q0-26.07-14.54-38.61t-44.62-12.53H222.6V276.74h86.23q30.07,0,44.62-12T368,227.61Z"/><polygon class="cls-2" points="461.71 874.03 461.71 1042.32 306.83 957.3 306.83 789.02 461.71 874.03"/><polygon class="cls-1" points="306.83 789.02 306.83 957.3 151.94 1042.32 151.94 875.51 156.15 873.19 306.83 789.02"/><polygon class="cls-2" points="151.94 875.51 151.94 1042.32 0 958.98 0 786.14 151.94 875.51"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 611 1043" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Layer_2">
<g id="Layer_1-2">
<path d="M610.63,784.67L610.63,952.95L461.71,1042.32L461.71,874.03L464.52,872.35L610.63,784.67Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M610.63,619.33L610.63,784.67L461.71,702.53L468.56,698.7L563.9,645.44L610.63,619.33Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M610.63,515.37L610.63,619.37L590.72,607.63L461.72,531.55L306.83,617L151.94,702.39L138.33,709.89L0,786.14L0,0L362,0C434.173,0 490.98,15.707 532.42,47.12C534.38,48.613 536.297,50.137 538.17,51.69C575.177,82.423 593.98,125.773 594.58,181.74L594.58,184.49C594.58,225.95 583.717,260.877 561.99,289.27C540.263,317.663 511.353,336.547 475.26,345.92C518.027,355.92 551.283,376.477 575.03,407.59C598.777,438.703 610.643,474.63 610.63,515.37ZM368.49,519.89C378.163,512.21 383,499.673 383,482.28C383,448.193 363.28,431.15 323.84,431.15L222.6,431.15L222.6,531.42L323.87,531.42C343.923,531.42 358.797,527.577 368.49,519.89ZM368,227.61C368,210.23 363.153,197.36 353.46,189C343.767,180.64 328.893,176.463 308.84,176.47L222.6,176.47L222.6,276.74L308.83,276.74C328.877,276.74 343.75,272.74 353.45,264.74C363.15,256.74 368,244.363 368,227.61Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M461.71,874.03L461.71,1042.32L306.83,957.3L306.83,789.02L461.71,874.03Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M306.83,789.02L306.83,957.3L151.94,1042.32L151.94,875.51L156.15,873.19L306.83,789.02Z" style="fill:white;fill-rule:nonzero;"/>
<path d="M151.94,875.51L151.94,1042.32L0,958.98L0,786.14L151.94,875.51Z" style="fill:white;fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
public/images/my-photo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

441
public/styles/main.css Normal file
View File

@ -0,0 +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) {
}

View File

@ -1,10 +1,9 @@
@import url("https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap"); @import url("https://fonts.googleapis.com/css?family=Cutive+Mono&display=swap");
html { html {
width: 100%; width: 100%;
/* overflow-x: hidden; */
scroll-behavior: smooth; scroll-behavior: smooth;
font-family: "Source Code Pro", Helvetica; font-family: "Cutive Mono", Helvetica;
font-size: 16px; font-size: 16px;
line-height: 1.5; line-height: 1.5;
color: #222; color: #222;
@ -18,9 +17,9 @@ html {
} }
:root { :root {
--main-color: #1668e4; --main-color: #7174ff;
--main-color-lighter: #5698fc; --main-color-lighter: #9d9eff;
--dark-color: #201e1e; --dark-color: #181515;
--sec-color-3: #688e26; --sec-color-3: #688e26;
--sec-color-4: #adb2d3; --sec-color-4: #adb2d3;
--sec-color-5: #c2a878; --sec-color-5: #c2a878;
@ -32,7 +31,6 @@ html {
body { body {
width: 100%; width: 100%;
margin: 0px; margin: 0px;
padding: 40px;
top: 0; top: 0;
justify-content: center; justify-content: center;
background-color: var(--dark-color); background-color: var(--dark-color);
@ -49,19 +47,25 @@ a:hover {
color: var(--sec-color-2); color: var(--sec-color-2);
} }
button { button,
.button {
padding: 10px 25px; padding: 10px 25px;
border: none; border: none;
cursor: pointer; cursor: pointer;
font-size: inherit; font-size: inherit;
border: 1px solid transparent; border: 1px solid transparent;
color: var(--dark-color);
background-color: white;
font-size: 18px;
font-weight: 600;
} }
hr { hr {
opacity: 0.3; opacity: 0.3;
} }
button:hover { button:hover,
.button:hover {
background-color: var(--dark-color); background-color: var(--dark-color);
/* background-color: #c52532; */ /* background-color: #c52532; */
color: white; color: white;
@ -75,7 +79,6 @@ form * {
header { header {
z-index: 1000000; z-index: 1000000;
margin-bottom: 40px;
} }
h1 { h1 {
@ -84,6 +87,18 @@ h1 {
margin-bottom: 10px; margin-bottom: 10px;
} }
h2 {
font-size: 38px;
}
h3 {
font-size: 30px;
}
h4 {
font-size: 22px;
}
/* * */ /* * */
/* * */ /* * */
/* * */ /* * */
@ -204,9 +219,7 @@ nav a:hover {
/*############################################# -- Shuffled Text */ /*############################################# -- Shuffled Text */
#__next { #__next {
max-width: 1200px;
width: 100%; width: 100%;
height: 100%;
opacity: 0; opacity: 0;
animation-name: shuffle; animation-name: shuffle;
animation-timing-function: ease-out; animation-timing-function: ease-out;
@ -329,7 +342,7 @@ textarea {
} }
.tech-stack-header { .tech-stack-header {
color: rgb(113, 116, 255); color: #7174ff;
margin-bottom: 3px; margin-bottom: 3px;
} }
@ -412,11 +425,6 @@ html.is-animating .transition-fade {
gap: 15px; gap: 15px;
} }
body {
padding: 20px;
padding-bottom: 80px;
}
.hero-ctas-section { .hero-ctas-section {
flex-wrap: wrap; flex-wrap: wrap;
} }

View File

@ -1,4 +1,6 @@
*, ::before, ::after { *, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0; --tw-translate-x: 0;
--tw-translate-y: 0; --tw-translate-y: 0;
--tw-rotate: 0; --tw-rotate: 0;
@ -10,6 +12,59 @@
--tw-pan-y: ; --tw-pan-y: ;
--tw-pinch-zoom: ; --tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity; --tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia:
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ; --tw-ordinal: ;
--tw-slashed-zero: ; --tw-slashed-zero: ;
--tw-numeric-figure: ; --tw-numeric-figure: ;
@ -47,32 +102,56 @@
position: fixed position: fixed
} }
.top-0 { .absolute {
top: 0px position: absolute
}
.relative {
position: relative
} }
.left-0 { .left-0 {
left: 0px left: 0px
} }
.-z-10 { .top-0 {
z-index: -10 top: 0px
}
.z-10 {
z-index: 10
}
.z-\[-1\] {
z-index: -1
}
.z-\[-2\] {
z-index: -2
} }
.m-0 { .m-0 {
margin: 0px margin: 0px
} }
.mt-4 { .-mb-4 {
margin-top: 1rem margin-bottom: -1rem
} }
.mt-10 { .-mt-10 {
margin-top: 2.5rem margin-top: -2.5rem
} }
.mb-1 { .-mt-64 {
margin-bottom: 0.25rem margin-top: -16rem
}
.-mt-\[120px\] {
margin-top: -120px
}
.mb-2 {
margin-bottom: 0.5rem
} }
.mb-4 { .mb-4 {
@ -83,34 +162,42 @@
margin-bottom: 2rem margin-bottom: 2rem
} }
.mb-0 { .mr-0 {
margin-bottom: 0px margin-right: 0px
} }
.mb-3 { .mt-10 {
margin-bottom: 0.75rem margin-top: 2.5rem
} }
.mb-2 { .mt-20 {
margin-bottom: 0.5rem margin-top: 5rem
}
.mt-4 {
margin-top: 1rem
} }
.flex { .flex {
display: flex display: flex
} }
.h-6 { .h-20 {
height: 1.5rem height: 5rem
}
.h-44 {
height: 11rem
}
.h-full {
height: 100%
} }
.w-full { .w-full {
width: 100% width: 100%
} }
.max-w-xl {
max-width: 36rem
}
.max-w-2xl { .max-w-2xl {
max-width: 42rem max-width: 42rem
} }
@ -119,18 +206,66 @@
max-width: 48rem max-width: 48rem
} }
.max-w-4xl { .max-w-6xl {
max-width: 56rem max-width: 72rem
}
.max-w-\[450px\] {
max-width: 450px
}
.grow {
flex-grow: 1
}
.cursor-pointer {
cursor: pointer
} }
.flex-col { .flex-col {
flex-direction: column flex-direction: column
} }
.flex-col-reverse {
flex-direction: column-reverse
}
.flex-wrap {
flex-wrap: wrap
}
.items-start { .items-start {
align-items: flex-start align-items: flex-start
} }
.items-center {
align-items: center
}
.items-stretch {
align-items: stretch
}
.justify-center {
justify-content: center
}
.justify-between {
justify-content: space-between
}
.gap-0 {
gap: 0px
}
.gap-1 {
gap: 0.25rem
}
.gap-10 {
gap: 2.5rem
}
.gap-2 { .gap-2 {
gap: 0.5rem gap: 0.5rem
} }
@ -139,6 +274,22 @@
gap: 1rem gap: 1rem
} }
.gap-8 {
gap: 2rem
}
.overflow-hidden {
overflow: hidden
}
.whitespace-nowrap {
white-space: nowrap
}
.rounded-full {
border-radius: 9999px
}
.border { .border {
border-width: 1px border-width: 1px
} }
@ -151,30 +302,23 @@
border-style: solid border-style: solid
} }
.border-blue-500 { .bg-\[\#343680\] {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity))
}
.border-white\/40 {
border-color: rgb(255 255 255 / 0.4)
}
.border-white\/20 {
border-color: rgb(255 255 255 / 0.2)
}
.border-white\/50 {
border-color: rgb(255 255 255 / 0.5)
}
.bg-blue-600 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(37 99 235 / var(--tw-bg-opacity)) background-color: rgb(52 54 128 / var(--tw-bg-opacity))
} }
.bg-transparent { .bg-\[\#3e3f9c\] {
background-color: transparent --tw-bg-opacity: 1;
background-color: rgb(62 63 156 / var(--tw-bg-opacity))
}
.bg-primary\/10 {
background-color: rgb(113 116 255 / 0.1)
}
.object-cover {
-o-object-fit: cover;
object-fit: cover
} }
.p-4 { .p-4 {
@ -185,8 +329,58 @@
padding: 2rem padding: 2rem
} }
.text-xl { .p-\[100px\] {
font-size: 1.25rem; padding: 100px
}
.p-\[150px\] {
padding: 150px
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem
}
.py-20 {
padding-top: 5rem;
padding-bottom: 5rem
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem
}
.pb-40 {
padding-bottom: 10rem
}
.pt-\[450px\] {
padding-top: 450px
}
.text-5xl {
font-size: 3rem;
line-height: 1
}
.text-\[300px\] {
font-size: 300px
}
.text-base {
font-size: 1rem;
line-height: 1.5rem
}
.text-lg {
font-size: 1.125rem;
line-height: 1.75rem line-height: 1.75rem
} }
@ -195,63 +389,66 @@
line-height: 1.25rem line-height: 1.25rem
} }
.text-lg { .text-\[20px\] {
font-size: 1.125rem; font-size: 20px
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem line-height: 1.75rem
} }
.text-base { .uppercase {
font-size: 1rem; text-transform: uppercase
line-height: 1.5rem
} }
.font-bold { .leading-snug {
font-weight: 700 line-height: 1.375
} }
.text-white { .text-primary-light {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)) color: rgb(157 158 255 / var(--tw-text-opacity))
}
.text-white\/50 {
color: rgb(255 255 255 / 0.5)
}
.opacity-50 {
opacity: 0.5
}
.opacity-40 {
opacity: 0.4
}
.opacity-90 {
opacity: 0.9
}
.opacity-60 {
opacity: 0.6
} }
.opacity-20 { .opacity-20 {
opacity: 0.2 opacity: 0.2
} }
.opacity-30 { .opacity-40 {
opacity: 0.3 opacity: 0.4
} }
.opacity-70 { .opacity-5 {
opacity: 0.7 opacity: 0.05
}
.opacity-50 {
opacity: 0.5
} }
.opacity-80 { .opacity-80 {
opacity: 0.8 opacity: 0.8
} }
.outline { .blur {
outline-style: solid --tw-blur: blur(8px);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
}
.blur-sm {
--tw-blur: blur(4px);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
}
.contrast-\[140\%\] {
--tw-contrast: contrast(140%);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
}
.grayscale {
--tw-grayscale: grayscale(100%);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)
} }
.filter { .filter {
@ -264,7 +461,65 @@
transition-duration: 150ms transition-duration: 150ms
} }
.hover\:bg-blue-600:hover { .hover\:opacity-60:hover {
--tw-bg-opacity: 1; opacity: 0.6
background-color: rgb(37 99 235 / var(--tw-bg-opacity)) }
@media (min-width: 600px) {
.md\:relative {
position: relative
}
.md\:items-start {
align-items: flex-start
}
.md\:px-10 {
padding-left: 2.5rem;
padding-right: 2.5rem
}
.md\:px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem
}
.md\:py-10 {
padding-top: 2.5rem;
padding-bottom: 2.5rem
}
.md\:pt-0 {
padding-top: 0px
}
}
@media (min-width: 1200px) {
.xl\:-mr-14 {
margin-right: -3.5rem
}
.xl\:-mt-\[160px\] {
margin-top: -160px
}
.xl\:w-\[120\%\] {
width: 120%
}
.xl\:w-\[40\%\] {
width: 40%
}
.xl\:w-\[60\%\] {
width: 60%
}
.xl\:flex-row {
flex-direction: row
}
.xl\:flex-col {
flex-direction: column
}
} }

View File

@ -1,5 +1,5 @@
module.exports = { module.exports = {
content: ["./components/**/*.{html,js,jsx}", "./pages/**/*.jsx", , "./layouts/**/*.jsx"], content: ["./components/**/*.{html,js,jsx}", "./pages/**/*.{jsx,tsx}", "./app/**/*.{html,js,jsx,tsx}", "./layouts/**/*.{jsx,tsx}"],
theme: { theme: {
screens: { screens: {
xs: "350px", xs: "350px",
@ -9,7 +9,10 @@ module.exports = {
lg: "976px", lg: "976px",
xl: "1200px", xl: "1200px",
}, },
// colors: {}, colors: {
primary: "#7174ff",
"primary-light": "#9d9eff",
},
fontFamily: { fontFamily: {
sans: ["Graphik", "sans-serif"], sans: ["Graphik", "sans-serif"],
serif: ["Merriweather", "serif"], serif: ["Merriweather", "serif"],

111
tsconfig.json Normal file
View File

@ -0,0 +1,111 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */ /* Type Checking */,
"strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"noEmit": true,
"incremental": true,
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"moduleResolution": "node",
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "pages/_app.js", ".next/types/**/*.ts", "dump/index.jsx", "pages/_document.js"],
"exclude": ["node_modules"]
}