├── .eslintignore ├── .eslintrc.json ├── public ├── img │ ├── demo.png │ ├── favicon.ico │ ├── favicon.jpg │ ├── og-image.png │ ├── demo-rounded.png │ ├── germany-flag.png │ └── favicon-rounded.jpg └── svg │ ├── vercel.svg │ └── next.svg ├── .prettierignore ├── postcss.config.js ├── .env.example ├── functions └── isNewYearsPeriod.js ├── jsconfig.json ├── hooks └── useSWR.js ├── .gitignore ├── loaders ├── font.jsx └── theme.jsx ├── prettier.config.mjs ├── app ├── api │ ├── repositories │ │ └── route.js │ └── contact │ │ └── route.js ├── layout.jsx ├── error.jsx ├── not-found.jsx ├── contact │ └── page.jsx ├── about │ └── page.jsx ├── page.jsx └── projects │ └── page.jsx ├── next.config.mjs ├── package.json ├── providers ├── mobile.jsx ├── meta.jsx └── fireworks.jsx ├── styles ├── font.css ├── globals.css └── theme.css ├── components ├── client │ ├── Loading.jsx │ └── NewYearNotice.jsx ├── Footer.jsx └── Header.jsx ├── README.md ├── tailwind.config.js ├── LICENSE └── main.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | coverage 3 | node_modules 4 | pnpm-lock-yaml -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /public/img/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/demo.png -------------------------------------------------------------------------------- /public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/favicon.ico -------------------------------------------------------------------------------- /public/img/favicon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/favicon.jpg -------------------------------------------------------------------------------- /public/img/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/og-image.png -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .next 4 | build 5 | .contentlayer 6 | package.json 7 | pnpm-lock.yaml -------------------------------------------------------------------------------- /public/img/demo-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/demo-rounded.png -------------------------------------------------------------------------------- /public/img/germany-flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/germany-flag.png -------------------------------------------------------------------------------- /public/img/favicon-rounded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binary-blazer/portfolio/HEAD/public/img/favicon-rounded.jpg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DISCORD_WEBHOOK="" # Discord Webhook URL (https://discord.com/) 2 | GITHUB_TOKEN="" # Github Token (https://github.com/) -------------------------------------------------------------------------------- /functions/isNewYearsPeriod.js: -------------------------------------------------------------------------------- 1 | export default function isNewYearsPeriod() { 2 | const today = new Date(); 3 | const month = today.getMonth(); // 0-11 4 | const day = today.getDate(); 5 | return (month === 11 && day === 31) || (month === 0 && day === 1); 6 | } 7 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"], 5 | "styles/*": ["./styles/*"], 6 | "components/*": ["./components/*"], 7 | "hooks/*": ["./hooks/*"], 8 | "public/*": ["./public/*"], 9 | "providers/*": ["./providers/*"], 10 | "loaders/*": ["./loaders/*"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hooks/useSWR.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import swr from "swr"; 3 | 4 | export default function useSWR(url) { 5 | const { data, error } = swr(url, async (url) => { 6 | const response = await axios.get(url); 7 | return response.data; 8 | }); 9 | 10 | return { 11 | data, 12 | error, 13 | isLoading: !data && !error, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /public/svg/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # contentlayer 4 | .contentlayer 5 | 6 | # env 7 | .env 8 | 9 | # dependencies 10 | /node_modules 11 | /.pnp 12 | .pnp.js 13 | .yarn/install-state.gz 14 | 15 | # testing 16 | /coverage 17 | 18 | # next.js 19 | /.next/ 20 | /out/ 21 | 22 | # production 23 | /build 24 | 25 | # misc 26 | .DS_Store 27 | *.pem 28 | 29 | # debug 30 | npm-debug.log* 31 | yarn-debug.log* 32 | yarn-error.log* 33 | 34 | # local env files 35 | .env*.local 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | -------------------------------------------------------------------------------- /loaders/font.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | export default function FontLoader({ children }) { 6 | React.useEffect(() => { 7 | setInterval(() => { 8 | if (typeof window !== "undefined") { 9 | const font = window.localStorage.getItem("font"); 10 | 11 | if (font) { 12 | window.document.documentElement.classList.add( 13 | `font-${font.toLowerCase()}`, 14 | ); 15 | } else { 16 | window.document.documentElement.classList.add("font-inter"); 17 | } 18 | } 19 | }, 100); 20 | }, []); 21 | 22 | return children; 23 | } 24 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | 3 | const config = { 4 | importOrder: [ 5 | // prettier 6 | "^(react/(.*)$)|^(react$)", 7 | "^(next/(.*)$)|^(next$)", 8 | "^@/env(.*)$", 9 | "^types$", 10 | "^@/types/(.*)$", 11 | "^@/components/ui/(.*)$", 12 | "^@/components/(.*)$", 13 | "^@/app/(.*)$", 14 | "^@/config/(.*)$", 15 | "^@/lib/(.*)$", 16 | "^@/hooks/(.*)$", 17 | "^@/styles/(.*)$", 18 | "^[./]", 19 | "", 20 | ], 21 | importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], 22 | plugins: [ 23 | "@ianvs/prettier-plugin-sort-imports", 24 | "prettier-plugin-tailwindcss", 25 | ], 26 | }; 27 | 28 | export default config; 29 | -------------------------------------------------------------------------------- /app/api/repositories/route.js: -------------------------------------------------------------------------------- 1 | import { config } from "@/main.config"; 2 | 3 | export async function GET(request) { 4 | try { 5 | const username = config.github.username; 6 | const token = process.env.GITHUB_TOKEN; 7 | const url = `https://api.github.com/users/${username}/repos?sort=updated&per_page=8`; 8 | 9 | if (!token) throw new Error("No GITHUB_TOKEN in .env file found"); 10 | 11 | const response = await fetch(url, { 12 | headers: { 13 | Authorization: `token ${token}`, 14 | }, 15 | }); 16 | 17 | const repositories = await response.json(); 18 | return new Response(JSON.stringify(repositories), { 19 | headers: { 20 | "content-type": "application/json", 21 | }, 22 | }); 23 | } catch (error) { 24 | console.log(error); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | protocol: "https", 7 | hostname: "avatars.githubusercontent.com", 8 | port: "", 9 | pathname: "/u/**", 10 | }, 11 | { 12 | protocol: "https", 13 | hostname: "opengraph.githubassets.com", 14 | port: "", 15 | pathname: "/**", 16 | }, 17 | { 18 | protocol: "https", 19 | hostname: "www.contenthook.dev", 20 | port: "", 21 | pathname: "/**", 22 | }, 23 | ], 24 | }, 25 | redirects: async () => { 26 | return [ 27 | { 28 | source: "/socials/github", 29 | destination: "https://github.com/binary-blazer/portfolio", 30 | permanent: true, 31 | }, 32 | ]; 33 | }, 34 | }; 35 | 36 | export default nextConfig; 37 | -------------------------------------------------------------------------------- /loaders/theme.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | export default function ThemeLoader({ children }) { 6 | const availableThemes = ["blue", "green", "red", "yellow", "purple", "pink"]; 7 | 8 | React.useEffect(() => { 9 | setInterval(() => { 10 | if (typeof window !== "undefined") { 11 | const theme = window.localStorage.getItem("theme"); 12 | if (theme) { 13 | if (!availableThemes.includes(theme)) { 14 | const set = () => { 15 | window.localStorage.setItem("theme", "blue"); 16 | window.document.documentElement.classList.add("theme-blue"); 17 | }; 18 | 19 | return set(); 20 | } 21 | 22 | window.document.documentElement.classList.add(`theme-${theme}`); 23 | } else { 24 | window.localStorage.setItem("theme", "blue"); 25 | window.document.documentElement.classList.add("theme-blue"); 26 | } 27 | } 28 | }, 100); 29 | }, []); 30 | 31 | return children; 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio", 3 | "version": "3.8.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "lint:fix": "next lint --fix", 11 | "format": "prettier --write .", 12 | "format:check": "prettier --check ." 13 | }, 14 | "dependencies": { 15 | "@fireworks-js/react": "^2.10.8", 16 | "@headlessui/react": "^2.2.0", 17 | "@sdevs/screen-sizr": "^2.0.0", 18 | "cobe": "^0.6.3", 19 | "dotenv": "^16.4.5", 20 | "framer-motion": "^11.3.7", 21 | "next": "14.2.32", 22 | "react": "^18.3.1", 23 | "react-dom": "^18.3.1" 24 | }, 25 | "devDependencies": { 26 | "@ianvs/prettier-plugin-sort-imports": "^4.3.1", 27 | "@tailwindcss/typography": "^0.5.13", 28 | "autoprefixer": "^10.4.19", 29 | "eslint": "^9.7.0", 30 | "eslint-config-next": "14.2.5", 31 | "postcss": "^8.4.39", 32 | "prettier": "3.3.3", 33 | "prettier-plugin-tailwindcss": "^0.6.5", 34 | "tailwindcss": "^3.4.6" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /providers/mobile.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect, useState } from "react"; 4 | import { config } from "@/main.config"; 5 | 6 | export default function MobileProvider({ children }) { 7 | const [isMobile, setIsMobile] = useState(false); 8 | 9 | useEffect(() => { 10 | if (!config.mobileAllowed) return setIsMobile(false); 11 | const userAgent = 12 | typeof window.navigator === "undefined" ? "" : navigator.userAgent; 13 | const mobile = Boolean( 14 | /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( 15 | userAgent, 16 | ), 17 | ); 18 | setIsMobile(mobile); 19 | }, []); 20 | 21 | return ( 22 | <> 23 | {!isMobile ? ( 24 | children 25 | ) : ( 26 |
27 |

28 | 404 29 |

30 |

31 | This page is not available on mobile at the moment. 32 |

33 |
34 | )} 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /styles/font.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"); 2 | @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"); 3 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"); 4 | @import url("https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap"); 5 | @import url("https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap"); 6 | @import url("https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap"); 7 | 8 | .font-inter, 9 | .font-inter * { 10 | font-family: "Inter", sans-serif; 11 | } 12 | 13 | .font-roboto, 14 | .font-roboto * { 15 | font-family: "Roboto", sans-serif; 16 | } 17 | 18 | .font-poppins, 19 | .font-poppins * { 20 | font-family: "Poppins", sans-serif; 21 | } 22 | 23 | .font-lato, 24 | .font-lato * { 25 | font-family: "Lato", sans-serif; 26 | } 27 | 28 | .font-nunito, 29 | .font-nunito * { 30 | font-family: "Nunito", sans-serif; 31 | } 32 | 33 | .font-raleway, 34 | .font-raleway * { 35 | font-family: "Raleway", sans-serif; 36 | } 37 | -------------------------------------------------------------------------------- /components/client/Loading.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { AnimatePresence, motion } from "framer-motion"; 5 | 6 | export default function Loading() { 7 | const [loading, setLoading] = React.useState(true); 8 | 9 | React.useEffect(() => { 10 | setTimeout(() => { 11 | setLoading(false); 12 | }, 2000); 13 | }, []); 14 | 15 | return ( 16 | 17 | {loading && ( 18 | 22 | 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )} 35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /components/Footer.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useEffect } from "react"; 4 | import Image from "next/image"; 5 | import germanyFlag from "../public/img/germany-flag.png"; 6 | 7 | export default function Footer() { 8 | useEffect(() => { 9 | console.log( 10 | "This site was created by BinaryBlazer. Please keep footer and the header credits intact.", 11 | ); 12 | }, []); 13 | 14 | return ( 15 | <> 16 | {/* Please don't touch the footer credits. I worked hard to make my developer portfolio. */} 17 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /public/svg/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/layout.jsx: -------------------------------------------------------------------------------- 1 | import { Inter } from "next/font/google"; 2 | import NewYearNotice from "@/components/client/NewYearNotice"; 3 | import FireworksProvider from "@/providers/fireworks"; 4 | import Loading from "components/client/Loading"; 5 | import Footer from "components/Footer"; 6 | import Header from "components/Header"; 7 | import FontLoader from "loaders/font"; 8 | import ThemeLoader from "loaders/theme"; 9 | import MetaProvider from "providers/meta"; 10 | import MobileProvider from "providers/mobile"; 11 | import "styles/globals.css"; 12 | import "styles/theme.css"; 13 | import "styles/font.css"; 14 | 15 | const inter = Inter({ subsets: ["latin"] }); 16 | 17 | export const metadata = { 18 | title: "Home - BinaryBlazer", 19 | description: "BinaryBlazer's portfolio and blog", 20 | }; 21 | 22 | export default function RootLayout({ children }) { 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | {children} 35 |