├── .env.example ├── .eslintrc.cjs ├── .gitignore ├── LICENSE.md ├── README.md ├── components.json ├── next.config.js ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.cjs ├── prettier.config.js ├── public ├── assets │ ├── infinitevps.webm │ ├── logo.webp │ ├── portfolio.webm │ ├── scene.splinecode │ ├── translate_bot.webm │ ├── unqueue.webm │ └── wrona.jpeg ├── favicon.ico ├── fonts │ └── ClashGrotesk-Variable.woff2 ├── icon-192x192.png ├── icon-256x256.png ├── icon-384x384.png ├── icon-512x512.png ├── manifest.json └── robots.txt ├── src ├── components │ ├── Container.tsx │ ├── Footer.tsx │ ├── Preloader.tsx │ └── ui │ │ ├── button.tsx │ │ ├── card.tsx │ │ └── carousel.tsx ├── env.js ├── lib │ └── utils.ts ├── pages │ ├── _app.tsx │ └── index.tsx └── styles │ ├── Container.module.css │ ├── Home.module.css │ ├── clash-grotesk.css │ ├── globals.css │ └── locomotive-scroll.css ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | # Since the ".env" file is gitignored, you can use the ".env.example" file to 2 | # build a new ".env" file when you clone the repo. Keep this file up-to-date 3 | # when you add new variables to `.env`. 4 | 5 | # This file will be committed to version control, so make sure not to have any 6 | # secrets in it. If you are cloning this repo, create a copy of this file named 7 | # ".env" and populate it with your secrets. 8 | 9 | # When adding additional environment variables, the schema in "/src/env.js" 10 | # should be updated accordingly. 11 | 12 | # Example: 13 | # SERVERVAR="foo" 14 | # NEXT_PUBLIC_CLIENTVAR="bar" 15 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | const config = { 3 | parser: "@typescript-eslint/parser", 4 | parserOptions: { 5 | project: true, 6 | }, 7 | plugins: ["@typescript-eslint"], 8 | extends: [ 9 | "next/core-web-vitals", 10 | "plugin:@typescript-eslint/recommended-type-checked", 11 | "plugin:@typescript-eslint/stylistic-type-checked", 12 | ], 13 | rules: { 14 | // These opinionated rules are enabled in stylistic-type-checked above. 15 | // Feel free to reconfigure them to your own preference. 16 | "@typescript-eslint/array-type": "off", 17 | "@typescript-eslint/consistent-type-definitions": "off", 18 | 19 | "@typescript-eslint/consistent-type-imports": [ 20 | "warn", 21 | { 22 | prefer: "type-imports", 23 | fixStyle: "inline-type-imports", 24 | }, 25 | ], 26 | "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], 27 | "@typescript-eslint/require-await": "off", 28 | "@typescript-eslint/no-misused-promises": [ 29 | "error", 30 | { 31 | checksVoidReturn: { attributes: false }, 32 | }, 33 | ], 34 | }, 35 | }; 36 | 37 | module.exports = config; 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | next-env.d.ts 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # local env files 34 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 35 | .env 36 | .env*.local 37 | 38 | # vercel 39 | .vercel 40 | 41 | # typescript 42 | *.tsbuildinfo 43 | 44 | # webstorm 45 | .idea 46 | ``` -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 wendoj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🖥️ Personal Portfolio 2 | 3 | A sleek, modern, and responsive portfolio bootstrapped with [create-t3-app](https://create.t3.gg/). 4 | 5 | [Website Preview](https://github.com/wendoj/portfolio/assets/77996774/b73b02d7-0657-41e3-9124-e6d04b17fe93) 6 | 7 | [🔗 Link to portfolio](https://www.wendoj.codes) 8 | 9 | All you need to know about me, my projects and skills can be found here. Personalize the portfolio by modifying `src/pages/index.tsx` and `src/styles/globals.css` to emulate your own portfolio. Made some improvements and want to share? Open a pull request. 10 | For any issues, feel free to report them here. 11 | 12 | ## 🎉 Features 13 | - **Responsive Design**: The portfolio is designed to be fully responsive, providing an optimal viewing experience across a wide range of devices from desktops to mobile phones. 14 | - **Easy Customization**: The portfolio structure is straightforward and well organized, making it easy to customize and showcase your unique set of skills and projects. 15 | - **Stunning UI/UX Design**: The portfolio boasts a sleek and modern design, using smooth animations to capture the attention of potential employers or clients. 16 | - **Interactive UI**: Utilizing modern web development techniques, the portfolio offers an interactive user interface that enhances user experience, such as `locomotive-scroll` and `framer-motion`. 17 | 18 | ## 🚀 Getting Started 19 | 20 | ### Prerequisites 21 | To get started with this portfolio, ensure that you have the following installed on your system: 22 | - Node.js 23 | - npm 24 | - git 25 | 26 | ## 🛠️ Installation 27 | Follow the steps below to clone and run this project on your local system: 28 | 29 | ```bash 30 | # Clone the repository 31 | $ git clone https://github.com/wendoj/developer-portfolio.git 32 | 33 | # Navigate to the project directory 34 | $ cd developer-portfolio 35 | 36 | # Remove current origin repository 37 | $ git remote remove origin 38 | ``` 39 | 40 |
41 | 42 | Then install the required dependencies: 43 | ```bash 44 | # Install dependencies 45 | $ npm install 46 | 47 | # Start the development server: 48 | $ npm run dev 49 | ``` 50 | Now, open your browser and navigate to `http://localhost:3000` to view your portfolio live. 51 | 52 | 53 | ## How do I deploy this? 54 | 55 | Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information. 56 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | await import("./src/env.js"); 6 | import WithPWA from "next-pwa"; 7 | 8 | const withPWA = WithPWA({ 9 | dest: "public", 10 | disable: process.env.NODE_ENV === "development", 11 | register: true, 12 | scope: "/", 13 | sw: "service-worker.js", 14 | }); 15 | 16 | /** 17 | * @type {import('next').NextConfig} 18 | */ 19 | // @ts-ignore 20 | const config = withPWA({ 21 | reactStrictMode: true, 22 | 23 | /** 24 | * If you are using `appDir` then you must comment the below `i18n` config out. 25 | * 26 | * @see https://github.com/vercel/next.js/issues/41980 27 | */ 28 | i18n: { 29 | locales: ["en"], 30 | defaultLocale: "en", 31 | }, 32 | }); 33 | 34 | export default config; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "next build", 8 | "dev": "next dev", 9 | "lint": "next lint", 10 | "start": "next start", 11 | "type-check": "tsc --noEmit", 12 | "format": "prettier --write ./src/**/*.{ts,tsx,js,jsx,json,md}" 13 | }, 14 | "dependencies": { 15 | "@motionone/utils": "^10.17.0", 16 | "@radix-ui/react-hover-card": "^1.0.7", 17 | "@radix-ui/react-icons": "^1.3.0", 18 | "@radix-ui/react-slot": "^1.0.2", 19 | "@splinetool/react-spline": "^2.2.6", 20 | "@splinetool/runtime": "^1.0.38", 21 | "@t3-oss/env-nextjs": "^0.7.1", 22 | "class-variance-authority": "^0.7.0", 23 | "clsx": "^2.1.0", 24 | "embla-carousel-react": "8.0.0-rc21", 25 | "framer-motion": "^10.18.0", 26 | "locomotive-scroll": "5.0.0-beta.11", 27 | "lucide-react": "^0.314.0", 28 | "motion": "^10.17.0", 29 | "next": "^14.0.4", 30 | "next-pwa": "^5.6.0", 31 | "react": "18.2.0", 32 | "react-dom": "18.2.0", 33 | "sharp": "^0.33.2", 34 | "tailwind-merge": "^2.2.1", 35 | "tailwindcss-animate": "^1.0.7", 36 | "vanilla-tilt": "^1.8.1", 37 | "zod": "^3.22.4" 38 | }, 39 | "devDependencies": { 40 | "@types/eslint": "^8.44.7", 41 | "@types/locomotive-scroll": "^4.1.3", 42 | "@types/next-pwa": "^5.6.9", 43 | "@types/node": "^18.17.0", 44 | "@types/react": "^18.2.37", 45 | "@types/react-dom": "^18.2.15", 46 | "@typescript-eslint/eslint-plugin": "^6.11.0", 47 | "@typescript-eslint/parser": "^6.11.0", 48 | "autoprefixer": "^10.4.14", 49 | "eslint": "^8.54.0", 50 | "eslint-config-next": "^14.0.4", 51 | "postcss": "^8.4.31", 52 | "prettier": "^3.1.0", 53 | "prettier-plugin-tailwindcss": "^0.5.7", 54 | "tailwindcss": "^3.3.5", 55 | "typescript": "^5.1.6" 56 | }, 57 | "ct3aMetadata": { 58 | "initVersion": "7.26.0" 59 | }, 60 | "packageManager": "pnpm@8.14.2" 61 | } -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'tailwindcss/nesting': {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | } 8 | }; 9 | 10 | module.exports = config; -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ 2 | const config = { 3 | plugins: ["prettier-plugin-tailwindcss"], 4 | }; 5 | 6 | export default config; 7 | -------------------------------------------------------------------------------- /public/assets/infinitevps.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/infinitevps.webm -------------------------------------------------------------------------------- /public/assets/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/logo.webp -------------------------------------------------------------------------------- /public/assets/portfolio.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/portfolio.webm -------------------------------------------------------------------------------- /public/assets/scene.splinecode: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/scene.splinecode -------------------------------------------------------------------------------- /public/assets/translate_bot.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/translate_bot.webm -------------------------------------------------------------------------------- /public/assets/unqueue.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/unqueue.webm -------------------------------------------------------------------------------- /public/assets/wrona.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/assets/wrona.jpeg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/ClashGrotesk-Variable.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/fonts/ClashGrotesk-Variable.woff2 -------------------------------------------------------------------------------- /public/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/icon-192x192.png -------------------------------------------------------------------------------- /public/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/icon-256x256.png -------------------------------------------------------------------------------- /public/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/icon-384x384.png -------------------------------------------------------------------------------- /public/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrongBlue0703/developer-portfolio/a79db787d6ef24310b11fb52cbbb772937f7718f/public/icon-512x512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme_color": "#7B82FE", 3 | "background_color": "#0A0A13", 4 | "display": "standalone", 5 | "orientation": "portrait", 6 | "scope": "/", 7 | "start_url": "/", 8 | "name": "Strong Blue", 9 | "short_name": "Strong Blue", 10 | "description": "Full-stack website developer and TypeScript enthusiast.", 11 | "icons": [ 12 | { 13 | "src": "/icon-192x192.png", 14 | "sizes": "192x192", 15 | "type": "image/png", 16 | "purpose": "maskable any" 17 | }, 18 | { 19 | "src": "/icon-256x256.png", 20 | "sizes": "256x256", 21 | "type": "image/png", 22 | "purpose": "maskable any" 23 | }, 24 | { 25 | "src": "/icon-384x384.png", 26 | "sizes": "384x384", 27 | "type": "image/png", 28 | "purpose": "maskable any" 29 | }, 30 | { 31 | "src": "/icon-512x512.png", 32 | "sizes": "512x512", 33 | "type": "image/png", 34 | "purpose": "maskable any" 35 | } 36 | ], 37 | "splash_pages": null 38 | } -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / -------------------------------------------------------------------------------- /src/components/Container.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import Link from "next/link"; 3 | import { motion, AnimatePresence } from "framer-motion"; 4 | import { cn, scrollTo } from "@/lib/utils"; 5 | import { useState, useEffect } from "react"; 6 | import Footer from "@/components/Footer"; 7 | import { useRouter } from "next/router"; 8 | import Preloader from "@/components/Preloader"; 9 | import styles from "@/styles/Container.module.css"; 10 | 11 | type IconProps = { 12 | ["data-hide"]: boolean; 13 | }; 14 | 15 | type ContainerProps = { 16 | children: React.ReactNode; 17 | title?: string; 18 | description?: string; 19 | className?: string; 20 | }; 21 | 22 | type NavProps = { 23 | text: string; 24 | href: string; 25 | i: number; 26 | className?: string; 27 | }; 28 | 29 | const variants = { 30 | visible: (i: number) => ({ 31 | opacity: 1, 32 | transition: { 33 | delay: i * 0.12, 34 | }, 35 | }), 36 | hidden: { opacity: 0 }, 37 | }; 38 | 39 | const navLinks = [ 40 | { href: "#home", text: "Home" }, 41 | { href: "#about", text: "About" }, 42 | { href: "#projects", text: "Projects" }, 43 | { href: "#services", text: "Services" }, 44 | ]; 45 | 46 | function handleClick(e: React.MouseEvent) { 47 | const href = e.currentTarget.getAttribute("href"); 48 | 49 | if (href && href.startsWith("#")) { 50 | e.preventDefault(); 51 | const section = document.querySelector(href); 52 | scrollTo(section); 53 | } 54 | } 55 | 56 | function NavItem(props: NavProps) { 57 | return ( 58 | 66 | 71 | {props.text} 72 | 73 | 74 | ); 75 | } 76 | 77 | export default function Container(props: ContainerProps) { 78 | const [isOpen, setIsOpen] = useState(false); 79 | const [isScrolled, setIsScrolled] = useState(false); 80 | const [isLoading, setIsLoading] = useState(true); 81 | 82 | const { children, ...customMeta } = props; 83 | const router = useRouter(); 84 | const meta = { 85 | title: "Strong Blue", 86 | description: `Full-stack website developer and TypeScript enthusiast.`, 87 | image: "/assets/logo.webp", 88 | type: "website", 89 | ...customMeta, 90 | }; 91 | 92 | // handle scroll 93 | useEffect(() => { 94 | const handleScroll = () => { 95 | setIsScrolled(window.scrollY > 0); 96 | }; 97 | 98 | window.addEventListener("scroll", handleScroll); 99 | 100 | return () => { 101 | window.removeEventListener("scroll", handleScroll); 102 | }; 103 | }, []); 104 | 105 | // preloader effect 106 | useEffect(() => { 107 | setTimeout(() => { 108 | setIsLoading(false); 109 | document.body.style.cursor = "default"; 110 | window.scrollTo(0, 0); 111 | }, 2000); 112 | }, []); 113 | 114 | return ( 115 | <> 116 | 117 | {meta.title} 118 | 119 | 120 | 121 | 125 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 241 | 242 | {/* Preloader */} 243 | 244 | {isLoading && } 245 | 246 | 247 | {/* Main content */} 248 |
{children}
249 |