feat: Initialize m5rcel portfolio project
This commit sets up the foundational structure for the m5rcel portfolio website. It includes: - Basic Vite project configuration with React and TypeScript. - Essential dependencies like React, Framer Motion, and Lucide React. - Initial HTML structure with meta tags and Tailwind CSS setup. - Placeholder data structures for projects and timeline. - A basic loading screen component. - Updated README with local run instructions.
This commit is contained in:
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
29
App.tsx
Normal file
29
App.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React, { useState } from 'react';
|
||||
import Navbar from './components/Navbar';
|
||||
import Hero from './components/Hero';
|
||||
import Projects from './components/Projects';
|
||||
import About from './components/About';
|
||||
import Footer from './components/Footer';
|
||||
import LoadingScreen from './components/LoadingScreen';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && <LoadingScreen onComplete={() => setIsLoading(false)} />}
|
||||
|
||||
<div className={`min-h-screen bg-[#f2f2f2] ${isLoading ? 'hidden' : 'block'}`}>
|
||||
<Navbar toggleTheme={() => {}} isDark={false} />
|
||||
<main>
|
||||
<Hero />
|
||||
<Projects />
|
||||
<About />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
25
README.md
25
README.md
@@ -1,11 +1,20 @@
|
||||
<div align="center">
|
||||
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
|
||||
<h1>Built with AI Studio</h2>
|
||||
|
||||
<p>The fastest path from prompt to production with Gemini.</p>
|
||||
|
||||
<a href="https://aistudio.google.com/apps">Start building</a>
|
||||
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/drive/1TiMFoAG3MlNJvKqVz68y71I_K1ca--la
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
|
||||
84
components/About.tsx
Normal file
84
components/About.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
import React from 'react';
|
||||
import { FileCode2, Terminal, Database, Server, GitBranch, Layout } from 'lucide-react';
|
||||
|
||||
const TECH_STACKS = [
|
||||
{
|
||||
name: 'TypeScript',
|
||||
desc: 'Strict syntactical superset of JavaScript',
|
||||
icon: FileCode2
|
||||
},
|
||||
{
|
||||
name: 'Python',
|
||||
desc: 'Data structures, scripting & automation',
|
||||
icon: Terminal
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
desc: 'Relational database management',
|
||||
icon: Database
|
||||
},
|
||||
{
|
||||
name: 'Node.js',
|
||||
desc: 'Server-side JavaScript runtime',
|
||||
icon: Server
|
||||
},
|
||||
{
|
||||
name: 'Git',
|
||||
desc: 'Version control & collaboration',
|
||||
icon: GitBranch
|
||||
},
|
||||
{
|
||||
name: 'HTML5',
|
||||
desc: 'Semantic web markup & accessibility',
|
||||
icon: Layout
|
||||
}
|
||||
];
|
||||
|
||||
const About: React.FC = () => {
|
||||
return (
|
||||
<section id="about" className="bg-[#f2f2f2] py-16">
|
||||
<div className="max-w-[980px] mx-auto px-4 grid grid-cols-1 md:grid-cols-3 gap-12 items-center">
|
||||
|
||||
{/* Left Column: Bio - Vertically Centered */}
|
||||
<div className="md:col-span-2 space-y-6">
|
||||
<h3 className="text-2xl font-display font-medium text-[#333] border-b border-[#ccc] pb-2">
|
||||
Why m5rcel?
|
||||
</h3>
|
||||
<div className="flex gap-6 items-center">
|
||||
<img src="https://github.com/m4rcel-lol.png" alt="m4rcel-lol Avatar" className="w-[120px] h-[120px] rounded shadow-md border border-white" />
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-[#444] font-sans leading-relaxed">
|
||||
It’s not just about writing code. It’s about creating an experience that feels magical. From the moment the page loads, everything should feel responsive, fluid, and intuitive.
|
||||
</p>
|
||||
<p className="text-sm text-[#444] font-sans leading-relaxed">
|
||||
I specialize in creating pixel-perfect interfaces that pay homage to the golden era of design while utilizing the raw power of modern web technologies.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Skills (Sidebar style) */}
|
||||
<div className="md:col-span-1 bg-white border border-[#d6d6d6] rounded-md shadow-sm p-5 h-fit">
|
||||
<h3 className="text-lg font-bold text-[#333] mb-4 font-sans text-center border-b border-[#eee] pb-2">Tech Stacks</h3>
|
||||
|
||||
<ul className="space-y-4">
|
||||
{TECH_STACKS.map((tech, i) => (
|
||||
<li key={i} className="flex items-start gap-3">
|
||||
<div className="mt-0.5 text-[#555]">
|
||||
<tech.icon size={18} strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-bold text-[#333]">{tech.name}</div>
|
||||
<div className="text-[11px] text-[#666] leading-tight mt-0.5">{tech.desc}</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
17
components/Footer.tsx
Normal file
17
components/Footer.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="bg-[#f2f2f2] border-t border-[#d6d6d6] pt-8 pb-12 text-[11px] font-sans text-[#666]">
|
||||
<div className="max-w-[980px] mx-auto px-4">
|
||||
|
||||
<div className="flex justify-center text-center">
|
||||
<p>Copyright © 2025 m5rcel. All rights reserved.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
77
components/Hero.tsx
Normal file
77
components/Hero.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const Hero: React.FC = () => {
|
||||
return (
|
||||
<section className="bg-[#f2f2f2] border-b border-[#d6d6d6] overflow-hidden">
|
||||
{/* 2009 Layout: Fixed width centered */}
|
||||
<div className="max-w-[980px] mx-auto pt-12 pb-16 px-4 text-center relative">
|
||||
|
||||
{/* Main Headline - Myriad Pro style */}
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-5xl md:text-6xl font-display font-semibold text-[#333] tracking-tight mb-2"
|
||||
>
|
||||
m5rcel.
|
||||
</motion.h1>
|
||||
|
||||
{/* Subheadline */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.3, duration: 0.8 }}
|
||||
className="text-2xl md:text-3xl font-light text-[#666] mb-8 font-sans"
|
||||
>
|
||||
The developer that likes to do stuff out of boredom.
|
||||
</motion.p>
|
||||
|
||||
{/* Product Hero Image with Reflection */}
|
||||
<motion.div
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.5, duration: 0.8 }}
|
||||
className="relative z-10 mb-8"
|
||||
>
|
||||
<div className="reflection">
|
||||
<img
|
||||
src="https://picsum.photos/id/48/900/400"
|
||||
alt="Hero Product"
|
||||
className="mx-auto rounded-md shadow-2xl border border-white"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* The Classic "Aqua" Buttons */}
|
||||
<div className="flex justify-center gap-6 mt-12 relative z-20">
|
||||
<a href="mailto:m5r@kitties.email" className="group relative inline-flex items-center justify-center">
|
||||
{/* Glossy Button Container */}
|
||||
<div className="px-8 py-2 rounded-full bg-glossy-blue shadow-md border border-[#0450a3] active:brightness-90">
|
||||
<span className="text-white text-sm font-bold font-sans drop-shadow-md">
|
||||
Contact Me
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/m4rcel-lol" className="group relative inline-flex items-center justify-center">
|
||||
{/* Silver Button Container */}
|
||||
<div className="px-8 py-2 rounded-full bg-glossy-silver shadow-md border border-[#a6a6a6] hover:bg-white active:brightness-95">
|
||||
<span className="text-[#333] text-sm font-bold font-sans text-emboss">
|
||||
See GitHub
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Small legal text typical of ads */}
|
||||
<p className="mt-8 text-[10px] text-gray-400 font-sans">
|
||||
Requires an internet connection. Battery life varies by use and configuration.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hero;
|
||||
32
components/LoadingScreen.tsx
Normal file
32
components/LoadingScreen.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Apple } from 'lucide-react';
|
||||
|
||||
interface LoadingScreenProps {
|
||||
onComplete: () => void;
|
||||
}
|
||||
|
||||
const LoadingScreen: React.FC<LoadingScreenProps> = ({ onComplete }) => {
|
||||
useEffect(() => {
|
||||
// Simulate classic boot time
|
||||
const timer = setTimeout(() => {
|
||||
onComplete();
|
||||
}, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [onComplete]);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex flex-col items-center justify-center bg-[#bfbfbf]">
|
||||
{/* Mac OS X Boot Logo */}
|
||||
<div className="mb-12">
|
||||
<Apple size={96} className="text-[#555555] fill-[#555555]" />
|
||||
</div>
|
||||
|
||||
{/* Classic Mac Spinner */}
|
||||
<div className="relative w-8 h-8">
|
||||
<div className="absolute inset-0 border-4 border-[#888888] border-t-[#444444] rounded-full animate-spin"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingScreen;
|
||||
27
components/Navbar.tsx
Normal file
27
components/Navbar.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { Apple } from 'lucide-react';
|
||||
|
||||
interface NavbarProps {
|
||||
toggleTheme: () => void;
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
const Navbar: React.FC<NavbarProps> = () => {
|
||||
return (
|
||||
<nav className="w-full bg-glossy-nav border-b border-[#333] shadow-md h-[44px] relative z-50 font-sans antialiased">
|
||||
<div className="max-w-[980px] mx-auto h-full flex items-center justify-center md:justify-start px-2">
|
||||
|
||||
{/* Nav Items */}
|
||||
<ul className="flex items-center h-full">
|
||||
{/* Apple Logo */}
|
||||
<li className="h-full flex items-center px-4">
|
||||
<Apple className="text-[#e6e6e6] drop-shadow-md filter" size={18} fill="#e6e6e6" />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
||||
84
components/Projects.tsx
Normal file
84
components/Projects.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
import React from 'react';
|
||||
import { PROJECTS } from '../constants';
|
||||
|
||||
const Projects: React.FC = () => {
|
||||
return (
|
||||
<section id="projects" className="bg-[#fff] border-b border-[#d6d6d6] py-12">
|
||||
<div className="max-w-[980px] mx-auto px-4">
|
||||
|
||||
{/* Section Header */}
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl font-display font-medium text-[#333] mb-2">
|
||||
Built for performance.
|
||||
</h2>
|
||||
<p className="text-[#666] text-lg font-sans">
|
||||
Thousands of lines of code. Zero compromise.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 2009 Grid Layout */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{PROJECTS.map((project) => (
|
||||
<div key={project.id} className="flex flex-col group">
|
||||
{/* Image Box */}
|
||||
<div className="h-[240px] w-full overflow-hidden rounded-md border border-gray-300 shadow-md mb-4 bg-gray-100 relative">
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
{/* Gloss Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent pointer-events-none" />
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3 className="text-xl font-bold text-[#333] font-sans mb-1 group-hover:text-[#0083e6] transition-colors cursor-pointer flex items-center gap-2">
|
||||
{project.title}
|
||||
|
||||
{/* Fedora SVG */}
|
||||
{project.id === 'fedora-bytebeat' && (
|
||||
<svg viewBox="0 0 14 14" className="w-5 h-5 mb-0.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 6.8848754,1.0000711 c -3.2587005,0.0618 -5.8826129,2.72355 -5.8848705,5.99671 l 0,4.6430899 c 0.00181,0.75194 0.6125562,1.3602 1.3651321,1.3602 2.682e-4,0 0.00137,0 0.00164,0 8.23e-4,-10e-6 0.00249,0 0.00329,0 l 0.00164,0 4.6315803,0 c 3.3124987,-10e-4 5.9967077,-2.68707 5.9967077,-5.9999999 0,-3.31392 -2.686309,-6 -5.9999997,-6 -0.02588,0 -0.0515,-3.1999e-4 -0.0773,0 -0.0128,1.6e-4 -0.02505,-2.3999e-4 -0.03783,0 z m 1.730263,1.82073 c 0.02126,-5.4e-4 0.04275,0 0.06414,0 0.209167,0 0.341292,0.0205 0.539474,0.0724 0.226849,0.0595 0.394658,0.24499 0.394735,0.41447 1.01e-4,0.24819 -0.136939,0.39309 -0.422696,0.39309 -0.154071,0 -0.22822,-0.0345 -0.511513,-0.0345 -0.895596,0 -1.626782,0.73112 -1.628287,1.62664 l 0,1.22369 c 0,0.22408 0.18709,0.41283 0.411183,0.41283 l 0.93092,0 c 0.23447,0 0.41758,0.17967 0.417764,0.41447 0,0.23473 -0.183215,0.41447 -0.417764,0.41447 l -1.129933,0 -0.21217,0 0,0.21054 0,1.42762 c 0,1.3691599 -1.104569,2.4753299 -2.473686,2.4753299 -0.209215,0 -0.3413107,-0.0205 -0.5394734,-0.0724 -0.2267786,-0.0594 -0.3946594,-0.24681 -0.394737,-0.41611 0,-0.24819 0.1367469,-0.39145 0.4226979,-0.39145 0.1538263,0 0.2283257,0.0345 0.5115125,0.0345 0.895603,0 1.626888,-0.7313 1.628291,-1.6266399 -3e-6,0 0,-1.2316 0,-1.23191 -3e-6,-0.22438 -0.187095,-0.40954 -0.411187,-0.40954 -6.36e-4,0 -0.0016,0 -0.0016,0 l -0.930918,0 c -0.234955,0 -0.416121,-0.17954 -0.416121,-0.41447 -6.1e-5,-0.23627 0.184085,-0.41447 0.424343,-0.41447 l 1.125,0 0.210527,0 0,-0.21217 0,-1.42106 c -3e-6,-1.34791 1.069895,-2.44152 2.409538,-2.47532 z m 1.30921,0.0954 c 0.8562886,0.44851 1.4407896,1.34457 1.4407896,2.37829 0,1.3868 -1.052185,2.51856 -2.4013146,2.65954 0.164957,-0.15256 0.268091,-0.36887 0.268091,-0.6102 -2.04e-4,-0.26094 -0.121941,-0.49472 -0.310853,-0.64802 0.668547,-0.1158 1.1825636,-0.70111 1.1825636,-1.40132 1e-6,-0.50763 -0.2695986,-0.9535 -0.6726976,-1.20395 0.353577,-0.0982 0.6037636,-0.41032 0.6036196,-0.78289 -6.9e-5,-0.14405 -0.04107,-0.27657 -0.1101986,-0.39145 z m -5.6282893,3.81415 c -0.1658665,0.15217 -0.2697985,0.37124 -0.2697371,0.61348 0,0.26114 0.120642,0.49314 0.309212,0.64638 -0.6683184,0.11707 -1.1809229,0.70752 -1.1809229,1.4079 0,0.50754 0.2684383,0.9523099 0.671053,1.2022999 -0.3531752,0.0987 -0.6036184,0.411 -0.6036184,0.7829 6.89e-5,0.14404 0.039434,0.2763 0.1085531,0.39144 -0.8552602,-0.44881 -1.4391449,-1.34359 -1.4391449,-2.3766399 0,-1.38801 1.0536597,-2.52765 2.4046052,-2.66776 z" fill="#000000"/>
|
||||
</svg>
|
||||
)}
|
||||
|
||||
{/* Python SVG */}
|
||||
{['m5rcode', 'bytebeat-win', 'fedora-bytebeat'].includes(project.id) && (
|
||||
<svg viewBox="0 0 512 512" className="w-4 h-4 mb-0.5" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<path d="M194.734,246.879h121.768c33.9,0,60.956-27.908,60.956-61.95V68.846c0-33.035-27.87-57.855-60.956-63.371 c-20.943-3.484-42.673-5.069-63.51-4.971c-20.845,0.097-40.74,1.874-58.258,4.971c-51.586,9.117-60.952,28.191-60.952,63.371 v46.463H255.69v15.486H133.782h-45.75c-35.434,0-66.459,21.295-76.158,61.808c-11.192,46.435-11.694,75.409,0,123.898 c8.666,36.088,29.359,61.807,64.79,61.807h41.917v-55.699C118.581,282.37,153.39,246.879,194.734,246.879z M187.063,84.333 c-12.636,0-22.877-10.355-22.877-23.161c0-12.849,10.241-23.3,22.877-23.3c12.594,0,22.873,10.451,22.873,23.3 C209.936,73.979,199.658,84.333,187.063,84.333z M499.37,192.603c-8.761-35.27-25.484-61.808-60.96-61.808h-45.75v54.134 c0,41.972-35.582,77.292-76.158,77.292H194.734c-33.349,0-60.952,28.547-60.952,61.954v116.079 c0,33.037,28.726,52.476,60.952,61.943c38.589,11.353,75.59,13.409,121.768,0c30.688-8.876,60.956-26.764,60.956-61.943v-46.461 H255.69v-15.486h121.768h60.952c35.431,0,48.638-24.715,60.96-61.807C512.092,278.314,511.549,241.589,499.37,192.603z M324.178,424.766c12.64,0,22.873,10.356,22.873,23.156c0,12.85-10.233,23.305-22.873,23.305 c-12.595,0-22.877-10.455-22.877-23.305C301.301,435.122,311.583,424.766,324.178,424.766z" fill="#000000" />
|
||||
</g>
|
||||
</svg>
|
||||
)}
|
||||
|
||||
{/* Vercel SVG */}
|
||||
{project.id === 'geminicord' && (
|
||||
<svg viewBox="0 0 15 15" className="w-4 h-4 mb-0.5" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M7.49998 1L6.92321 2.00307L1.17498 12L0.599976 13H1.7535H13.2464H14.4L13.825 12L8.07674 2.00307L7.49998 1ZM7.49998 3.00613L2.3285 12H12.6714L7.49998 3.00613Z"
|
||||
fill="#333333"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</h3>
|
||||
<p className="text-sm text-[#666] leading-relaxed font-sans mb-3">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
<a
|
||||
href={project.githubUrl}
|
||||
className="text-[12px] font-bold text-[#0083e6] hover:underline flex items-center gap-1 font-sans"
|
||||
>
|
||||
Learn more <span className="text-[10px]">▶</span>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
84
constants.ts
Normal file
84
constants.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Project, TimelineItem } from './types';
|
||||
|
||||
// Apple's "Emphasized" easing
|
||||
export const EASE_APPLE = [0.2, 0, 0, 1];
|
||||
|
||||
export const PROJECTS: Project[] = [
|
||||
{
|
||||
id: 'm5rcode',
|
||||
title: 'm5rcode',
|
||||
category: 'Language Design',
|
||||
description: 'Experimental polyglot programming language written with a blend of Python, JavaScript, PHP, C#, and C++.',
|
||||
image: 'https://picsum.photos/seed/m5rcode/800/600',
|
||||
tags: ['C++', 'Python', 'Compiler'],
|
||||
githubUrl: 'https://github.com/m4rcel-lol/m5rcode',
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
id: 'geminicord',
|
||||
title: 'Geminicord',
|
||||
category: 'AI Web Application',
|
||||
description: "Pixel-perfect recreation of the modern Discord UI, powered by Google's Gemini AI.",
|
||||
image: 'https://picsum.photos/seed/geminicord/800/600',
|
||||
tags: ['React', 'Gemini API', 'UI Clone'],
|
||||
githubUrl: 'https://github.com/m4rcel-lol/custom-discord-ai-chatbot-site',
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
id: 'bytebeat-win',
|
||||
title: 'Python Bytebeat Player',
|
||||
category: 'Audio Software',
|
||||
description: 'Play Bytebeat sequences on PC using Python.',
|
||||
image: 'https://picsum.photos/seed/bytebeat1/800/600',
|
||||
tags: ['Python', 'Audio Synthesis'],
|
||||
githubUrl: 'https://github.com/m4rcel-lol/python-bytebeat-player',
|
||||
},
|
||||
{
|
||||
id: 'fedora-bytebeat',
|
||||
title: 'Python Bytebeat Player',
|
||||
category: 'Linux Audio',
|
||||
description: 'Play Bytebeat sequences on PC using Python, but on linux.',
|
||||
image: 'https://picsum.photos/seed/fedora/800/600',
|
||||
tags: ['Python', 'Linux', 'Fedora'],
|
||||
githubUrl: 'https://github.com/m4rcel-lol/fedora-bytebeat-player',
|
||||
},
|
||||
];
|
||||
|
||||
export const TIMELINE: TimelineItem[] = [
|
||||
{
|
||||
year: '2024',
|
||||
title: 'Senior Frontend Engineer',
|
||||
description: 'Leading UI architecture for next-gen consumer products.',
|
||||
},
|
||||
{
|
||||
year: '2022',
|
||||
title: 'Creative Developer',
|
||||
description: 'Bridging the gap between design and engineering with motion-heavy interfaces.',
|
||||
},
|
||||
{
|
||||
year: '2020',
|
||||
title: 'Open Source Contributor',
|
||||
description: 'Started journey contributing to major UI libraries.',
|
||||
},
|
||||
];
|
||||
|
||||
// Animation Variants
|
||||
export const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 40 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.8, ease: EASE_APPLE }
|
||||
}
|
||||
};
|
||||
|
||||
export const staggerContainer = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.1,
|
||||
delayChildren: 0.2
|
||||
}
|
||||
}
|
||||
};
|
||||
97
index.html
Normal file
97
index.html
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<title>m5rcel - Developer & Designer</title>
|
||||
<meta name="description" content="Portfolio of m5rcel. Designed in California." />
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: [
|
||||
'"Lucida Grande"',
|
||||
'"Lucida Sans Unicode"',
|
||||
'"Helvetica Neue"',
|
||||
'Helvetica',
|
||||
'Arial',
|
||||
'sans-serif',
|
||||
],
|
||||
display: [
|
||||
'"Myriad Pro"',
|
||||
'"Helvetica Neue"',
|
||||
'Arial',
|
||||
'sans-serif',
|
||||
]
|
||||
},
|
||||
colors: {
|
||||
retro: {
|
||||
navTop: '#4e4e4e',
|
||||
navBot: '#383838',
|
||||
blueTop: '#71b1f8',
|
||||
blueBot: '#0083e6',
|
||||
bg: '#f2f2f2',
|
||||
text: '#333333',
|
||||
textLight: '#666666',
|
||||
border: '#d6d6d6',
|
||||
},
|
||||
},
|
||||
boxShadow: {
|
||||
'retro': '0 1px 4px rgba(0,0,0,0.3)',
|
||||
'retro-inset': 'inset 0 1px 3px rgba(0,0,0,0.2)',
|
||||
'gloss': 'inset 0 1px 0 rgba(255,255,255,0.4)',
|
||||
},
|
||||
backgroundImage: {
|
||||
'glossy-nav': 'linear-gradient(to bottom, #5e5e5e 0%, #404040 50%, #353535 51%, #383838 100%)',
|
||||
'glossy-blue': 'linear-gradient(to bottom, #7cbdfe 0%, #2f8bfd 50%, #0672e7 51%, #167efb 100%)',
|
||||
'glossy-silver': 'linear-gradient(to bottom, #f2f2f2 0%, #e6e6e6 50%, #d9d9d9 51%, #e6e6e6 100%)',
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background-color: #f2f2f2;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* The iconic wet floor reflection */
|
||||
.reflection {
|
||||
-webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255,255,255,0.4)));
|
||||
}
|
||||
|
||||
/* 2009 Search Bar Inset Shadow */
|
||||
.search-inset {
|
||||
box-shadow: inset 0 1px 3px rgba(0,0,0,0.5), 0 1px 0 rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
/* Text Emboss */
|
||||
.text-emboss {
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.text-shadow-nav {
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.6);
|
||||
}
|
||||
</style>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"react": "https://aistudiocdn.com/react@^19.2.1",
|
||||
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.1/",
|
||||
"react/": "https://aistudiocdn.com/react@^19.2.1/",
|
||||
"framer-motion": "https://aistudiocdn.com/framer-motion@^12.23.25",
|
||||
"lucide-react": "https://aistudiocdn.com/lucide-react@^0.556.0"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
15
index.tsx
Normal file
15
index.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
throw new Error("Could not find root element to mount to");
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
5
metadata.json
Normal file
5
metadata.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "m5rcel Portfolio",
|
||||
"description": "A premium, Apple-styled personal portfolio website for m5rcel, featuring cinematic animations and clean typography.",
|
||||
"requestFramePermissions": []
|
||||
}
|
||||
23
package.json
Normal file
23
package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "m5rcel-portfolio",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"framer-motion": "^12.23.25",
|
||||
"lucide-react": "^0.556.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.0",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vite": "^6.2.0"
|
||||
}
|
||||
}
|
||||
29
tsconfig.json
Normal file
29
tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"experimentalDecorators": true,
|
||||
"useDefineForClassFields": false,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"moduleResolution": "bundler",
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"allowJs": true,
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
22
types.ts
Normal file
22
types.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export interface Project {
|
||||
id: string;
|
||||
title: string;
|
||||
category: string;
|
||||
description: string;
|
||||
image: string;
|
||||
tags: string[];
|
||||
githubUrl: string;
|
||||
demoUrl?: string;
|
||||
featured?: boolean;
|
||||
}
|
||||
|
||||
export interface Skill {
|
||||
name: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface TimelineItem {
|
||||
year: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
23
vite.config.ts
Normal file
23
vite.config.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import path from 'path';
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, '.', '');
|
||||
return {
|
||||
server: {
|
||||
port: 3000,
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
plugins: [react()],
|
||||
define: {
|
||||
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '.'),
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user