feat: initial commit

This commit is contained in:
2025-11-19 20:44:34 +01:00
commit ed7aa29ac6
11 changed files with 9573 additions and 0 deletions

4
.babelrc Normal file
View File

@ -0,0 +1,4 @@
{
"sourceType": "unambiguous",
"presets": [["@babel/preset-env", { "modules": false }]]
}

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
dist/

7990
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "portfolio-website",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.28.5",
"@babel/preset-env": "^7.28.5",
"autoprefixer": "^10.4.22",
"babel-loader": "^10.0.0",
"css-loader": "^7.1.2",
"html-loader": "^5.1.0",
"html-webpack-plugin": "^5.6.5",
"mini-css-extract-plugin": "^2.9.4",
"postcss": "^8.5.6",
"postcss-loader": "^8.2.0",
"tailwindcss": "^3.4.18",
"webpack": "^5.103.0",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.2"
},
"dependencies": {
"@tailwindcss/postcss": "^4.1.17"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

BIN
src/images/profile.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

1244
src/index.html Normal file

File diff suppressed because it is too large Load Diff

154
src/index.js Normal file
View File

@ -0,0 +1,154 @@
import "./styles.css";
// Set year in footer
const yearEl = document.getElementById("year");
if (yearEl) {
yearEl.textContent = new Date().getFullYear().toString();
}
// Hero entrance animation
window.addEventListener("DOMContentLoaded", () => {
const heroEls = document.querySelectorAll("[data-hero]");
heroEls.forEach((el, idx) => {
setTimeout(() => {
el.classList.add("animate-fade-in-up");
el.classList.remove("opacity-0", "translate-y-6");
}, 150 * idx);
});
});
// Scroll animations for sections
const animatedSections = document.querySelectorAll("[data-animate]");
animatedSections.forEach((section) => {
section.classList.add(
"opacity-0",
"translate-y-8",
"transition-all",
"duration-700"
);
});
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("animate-fade-in-up");
entry.target.classList.remove("opacity-0", "translate-y-8");
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.12 }
);
animatedSections.forEach((section) => observer.observe(section));
// tailwind.config = {
// theme: {
// extend: {
// fontFamily: {
// sans: [
// "Inter",
// "ui-sans-serif",
// "system-ui",
// "-apple-system",
// "BlinkMacSystemFont",
// '"Segoe UI"',
// "sans-serif",
// ],
// heading: [
// "Montserrat",
// "ui-sans-serif",
// "system-ui",
// "-apple-system",
// "BlinkMacSystemFont",
// '"Segoe UI"',
// "sans-serif",
// ],
// },
// colors: {
// primary: "#1e90ff", // dodgerblue
// "bg-primary": "#1e90ff",
// "bg-dark": "#020617", // slate-950-ish
// "card-dark": "#020617",
// "card-elevated": "#020617",
// },
// boxShadow: {
// glow: "0 0 40px rgba(30,144,255,0.45)",
// },
// keyframes: {
// "fade-in-up": {
// "0%": { opacity: "0", transform: "translateY(20px)" },
// "100%": { opacity: "1", transform: "translateY(0)" },
// },
// "fade-in": {
// "0%": { opacity: "0" },
// "100%": { opacity: "1" },
// },
// "scale-in": {
// "0%": { opacity: "0", transform: "scale(0.96)" },
// "100%": { opacity: "1", transform: "scale(1)" },
// },
// "glow-pulse": {
// "0%,100%": { boxShadow: "0 0 0 rgba(30,144,255,0.0)" },
// "50%": { boxShadow: "0 0 40px rgba(30,144,255,0.5)" },
// },
// "border-spin": {
// "0%": { transform: "rotate(0deg)" },
// "100%": { transform: "rotate(360deg)" },
// },
// },
// animation: {
// "fade-in-up": "fade-in-up 0.8s ease-out forwards",
// "fade-in": "fade-in 1s ease-out forwards",
// "scale-in": "scale-in 0.7s ease-out forwards",
// "glow-pulse": "glow-pulse 2s ease-in-out infinite",
// "border-spin": "border-spin 8s linear infinite",
// },
// },
// },
// };
// --- SCRIPTING
// Set year
// document.getElementById("year").textContent = new Date()
// .getFullYear()
// .toString();
// // Hero entrance animation
// window.addEventListener("DOMContentLoaded", () => {
// const heroEls = document.querySelectorAll("[data-hero]");
// heroEls.forEach((el, idx) => {
// setTimeout(() => {
// el.classList.add("animate-fade-in-up");
// el.classList.remove("opacity-0", "translate-y-6");
// }, 150 * idx);
// });
// });
// // Scroll animations for sections
// const animatedSections = document.querySelectorAll("[data-animate]");
// animatedSections.forEach((section) => {
// section.classList.add(
// "opacity-0",
// "translate-y-8",
// "transition-all",
// "duration-700"
// );
// });
// const observer = new IntersectionObserver(
// (entries) => {
// entries.forEach((entry) => {
// if (entry.isIntersecting) {
// entry.target.classList.add("animate-fade-in-up");
// entry.target.classList.remove("opacity-0", "translate-y-8");
// observer.unobserve(entry.target);
// }
// });
// },
// { threshold: 0.12 }
// );
// animatedSections.forEach((section) => observer.observe(section));

16
src/styles.css Normal file
View File

@ -0,0 +1,16 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
@apply bg-bg-dark text-slate-100 antialiased;
}
/* Hide scrollbars on cards but keep scroll behavior */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}

67
tailwind.config.js Normal file
View File

@ -0,0 +1,67 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
theme: {
extend: {
fontFamily: {
sans: [
"Inter",
"ui-sans-serif",
"system-ui",
"-apple-system",
"BlinkMacSystemFont",
'"Segoe UI"',
"sans-serif",
],
heading: [
"Montserrat",
"ui-sans-serif",
"system-ui",
"-apple-system",
"BlinkMacSystemFont",
'"Segoe UI"',
"sans-serif",
],
},
colors: {
primary: "#1e90ff", // dodgerblue
"bg-dark": "#020617",
"card-dark": "#020617",
"card-elevated": "#020617",
},
boxShadow: {
glow: "0 0 40px rgba(30,144,255,0.45)",
},
keyframes: {
"fade-in-up": {
"0%": { opacity: "0", transform: "translateY(20px)" },
"100%": { opacity: "1", transform: "translateY(0)" },
},
"fade-in": {
"0%": { opacity: "0" },
"100%": { opacity: "1" },
},
"scale-in": {
"0%": { opacity: "0", transform: "scale(0.96)" },
"100%": { opacity: "1", transform: "scale(1)" },
},
"glow-pulse": {
"0%,100%": { boxShadow: "0 0 0 rgba(30,144,255,0.0)" },
"50%": { boxShadow: "0 0 40px rgba(30,144,255,0.5)" },
},
"border-spin": {
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" },
},
},
animation: {
"fade-in-up": "fade-in-up 0.8s ease-out forwards",
"fade-in": "fade-in 1s ease-out forwards",
"scale-in": "scale-in 0.7s ease-out forwards",
"glow-pulse": "glow-pulse 2s ease-in-out infinite",
"border-spin": "border-spin 8s linear infinite",
},
},
},
plugins: [],
};

58
webpack.config.js Normal file
View File

@ -0,0 +1,58 @@
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = (env, argv) => {
const isProd = argv.mode === "production";
return {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: isProd ? "js/[name].[contenthash].js" : "js/[name].js",
clean: true,
},
module: {
rules: [
{
test: /\.html$/i,
loader: "html-loader",
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: "babel-loader",
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: "asset/resource",
generator: {
filename: "images/[name][hash][ext][query]",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new MiniCssExtractPlugin({
filename: isProd ? "css/[name].[contenthash].css" : "css/[name].css",
}),
],
devServer: {
static: "./dist",
hot: true,
open: true,
port: 5173,
},
resolve: {
extensions: [".js"],
},
devtool: isProd ? "source-map" : "eval-source-map",
};
};