Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .hintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": [
"development"
],
"hints": {
"axe/name-role-value": [
"default",
{
"button-name": "off"
}
]
}
}
77 changes: 77 additions & 0 deletions app/about/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use client";

import { motion } from "framer-motion";
import { FaGithub } from "react-icons/fa";

import Image from "next/image";

export default function AboutPage() {
return (
<motion.main className="space-y-16 px-6 py-12" initial="hidden" animate="visible">
{/* 🌟 About Me Section */}
<motion.section className="space-y-4">
<h2 className="text-2xl font-semibold text-zinc-900 dark:text-zinc-100">
About Me
</h2>
<p className="text-zinc-600 dark:text-zinc-400 max-w-lg">
Hey, I'm Khalil! I love building intuitive and performant web experiences.
My focus is on bridging design and development, ensuring that every digital
product I create is smooth, functional, and visually appealing.
</p>
</motion.section>

{/* 🔥 Now Section */}
<motion.section className="space-y-4">
<h2 className="mb-3 text-lg font-medium">
Now
</h2>
<p className="text-zinc-600 dark:text-zinc-400">
I'm building tools that leverage DevOps to optimize software engineering workflows, exploring cloud computing to enhance scalability, and diving into distributed ledgers to push the boundaries of secure and decentralized systems. Through tackling complex logic challenges, I continuously refine my problem-solving skills, bridging the gap between infrastructure and innovation.
</p>

</motion.section>

{/* 🎉 Fun Facts Section */}
<motion.section className="space-y-4">
<h2 className="text-2xl font-semibold text-zinc-900 dark:text-zinc-100">
Fun Facts
</h2>
<div className="grid gap-4 sm:grid-cols-2">
<div className="rounded-lg border p-4 dark:border-zinc-700">
<h3 className="text-lg font-medium">🔥 Night Owl Coder</h3>
<p className="text-sm text-zinc-500">
I do my best work late at night with a cup of coffee.
</p>
</div>
<div className="rounded-lg border p-4 dark:border-zinc-700">
<h3 className="text-lg font-medium">🎮 Gamer at Heart</h3>
<p className="text-sm text-zinc-500">
I enjoy playing strategy and open-world games in my free time.
</p>
</div>
</div>
</motion.section>
<motion.section

>
<h3 className="mb-3 text-lg font-medium">More</h3>
<p className="text-zinc-600 dark:text-zinc-400">
Once, I heard <i>"software is just code."</i> But then I realized—it really is just code.
The magic isn’t in the lines themselves, but in how you put them together.
</p>
<p className="mt-2 text-zinc-600 dark:text-zinc-400">
Check out my <a
href="https://github.com/0xquirkai"
target="_blank"
rel="noopener noreferrer"
className="mt-4 inline-block font-medium text-dark-600 hover:underline dark:text-dark-400"
>
<FaGithub/> </a> I keep it updated with projects, experiments, and things I break on purpose.
You might find something useful or at least interesting.
</p>

</motion.section>

</motion.main>
);
}
16 changes: 11 additions & 5 deletions app/blog/layout.tsx → app/blog/(post)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@
import { TextMorph } from '@/components/ui/text-morph'
import { ScrollProgress } from '@/components/ui/scroll-progress'
import { useEffect, useState } from 'react'
import { FaRegCopy } from "react-icons/fa6";

function CopyButton() {
const [text, setText] = useState('Copy')
const currentUrl = typeof window !== 'undefined' ? window.location.href : ''
const [currentUrl, setCurrentUrl] = useState('')

useEffect(() => {
setTimeout(() => {
setText('Copy')
}, 2000)
}, [text])

useEffect(() => {
setCurrentUrl(window.location.href)
}, [])

return (
<button
onClick={() => {
Expand All @@ -23,7 +28,6 @@ function CopyButton() {
type="button"
>
<TextMorph>{text}</TextMorph>
<span>URL</span>
</button>
)
}
Expand All @@ -43,9 +47,11 @@ export default function LayoutBlogPost({
}}
/>

<div className="absolute right-4 top-24">
<CopyButton />
</div>
<div className="absolute right-4 top-28 border border-zinc-500 px-3 py-1 rounded-lg bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-300 shadow-md hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-all">
<CopyButton />
</div>


<main className="prose prose-gray mt-24 pb-20 prose-h4:prose-base dark:prose-invert prose-h1:text-xl prose-h1:font-medium prose-h2:mt-12 prose-h2:scroll-m-20 prose-h2:text-lg prose-h2:font-medium prose-h3:text-base prose-h3:font-medium prose-h4:font-medium prose-h5:text-base prose-h5:font-medium prose-h6:text-base prose-h6:font-medium prose-strong:font-medium">
{children}
</main>
Expand Down
56 changes: 56 additions & 0 deletions app/blog/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import Link from "next/link";
import { AnimatedBackground } from '@/components/ui/animated-background'
import {

BLOG_POSTS,

} from '../data'
const VARIANTS_SECTION = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
};

const TRANSITION_SECTION = { duration: 0.3, ease: "easeInOut" };

export default function BlogPage() {
const [posts, setPosts] = useState([]);


return (
<motion.section
variants={VARIANTS_SECTION}
initial="hidden"
animate="visible"
transition={TRANSITION_SECTION}
>
<h3 className="mb-3 text-lg font-medium">Blog</h3>
<div className="flex flex-col space-y-0">
<AnimatedBackground
enableHover
className="h-full w-full rounded-lg bg-zinc-100 dark:bg-zinc-900/80 -z-10"
transition={{ type: "spring", bounce: 0, duration: 0.2 }}
>
{BLOG_POSTS.map((post) => (
<Link
key={post.uid}
className="-mx-3 rounded-xl px-3 py-3 block"
href={post.link}
data-id={post.uid}
>
<div className="flex flex-col space-y-1">
<h4 className="font-normal dark:text-zinc-100">{post.title}</h4>
<p className="text-black-500 dark:text-zinc-400">
{post.description}
</p>
</div>
</Link>
))}
</AnimatedBackground>
</div>
</motion.section>
);
}
18 changes: 15 additions & 3 deletions app/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import Link from 'next/link'

export function Header() {
return (
<header className="mb-8 flex items-center justify-between">
<div>
<header className="mb-8 flex items-center justify-between ">
<div className='flex items-center gap-2'>
<Link href="/" className="font-medium text-black dark:text-white">
Julien Nim
</Link>
</Link> -
<TextEffect
as="p"
preset="fade"
Expand All @@ -19,6 +19,18 @@ export function Header() {
Design Engineer
</TextEffect>
</div>
<div className='flex items-center gap-4 justify-between'>
<Link href="/solutions" className="text-black dark:text-white">
Solutions
</Link>
<Link href="/about" className="text-black dark:text-white">
About
</Link>
<Link href="/blog" className="text-black dark:text-white">
Blog
</Link>

</div>
</header>
)
}
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${geist.variable} ${geistMono.variable} bg-white tracking-tight antialiased dark:bg-zinc-950`}
className={`${geist.variable} ${geistMono.variable} bg-white tracking-tight antialiased dark:bg-zinc-900`}
>
<ThemeProvider
enableSystem={true}
Expand Down
98 changes: 98 additions & 0 deletions app/solutions/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"use client";
import { motion } from 'motion/react'

import { useEffect, useState } from "react";
import { FaGithub } from "react-icons/fa";
import { CgWebsite } from "react-icons/cg";

export default function ProjectsPage() {
const TRANSITION_SECTION = {
duration: 0.3,
}

const VARIANTS_SECTION = {
hidden: { opacity: 0, y: 20, filter: 'blur(8px)' },
visible: { opacity: 1, y: 0, filter: 'blur(0px)' },
}

const [projects, setProjects] = useState([]);

useEffect(() => {
fetch("/solutions/projects.json")
.then((res) => res.json())
.then((data) => setProjects(data))
.catch((error) => console.error("Error loading projects:", error));
}, []);

return (
<motion.section
variants={VARIANTS_SECTION}
transition={TRANSITION_SECTION}
>
<h3 className="mb-5 text-lg font-medium">Solutions</h3>
<div className="grid grid-cols-1 gap-6">
{projects.map((project) => (
<ProjectItem key={project.name} project={project} />
))}
</div>
</motion.section>

);
}

function ProjectItem({ project }) {
const [hovered, setHovered] = useState(false);
const [position, setPosition] = useState({ x: 0, y: 0 });

const handleMouseMove = (event) => {
setPosition({ x: event.clientX + 15, y: event.clientY + 15 });
};

return (
<div
className="flex justify-between items-center gap-4 p-4 rounded-lg border-zinc-200 dark:border-zinc-700 transition hover:bg-zinc-100 dark:hover:bg-zinc-800 relative"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onMouseMove={handleMouseMove}
>
{/* Project Info */}
<div className="flex gap-2">
<span className="font-medium text-zinc-900 dark:text-zinc-50">
{project.name}
</span>
<span className="text-zinc-600 dark:text-zinc-400">&nbsp;&#8226;&nbsp; {project.description}</span>
</div>


{/* Icons */}
<div className="flex gap-2">
<a href={project.link} target="_blank" rel="noopener noreferrer">
<CgWebsite
size={18}
className="text-zinc-600 dark:text-zinc-400 hover:text-blue-500 dark:hover:text-blue-400 transition justify-self-end
"
/>
</a>

<a href={project.github} target="_blank" rel="noopener noreferrer">
<FaGithub
size={20}
className="text-zinc-600 dark:text-zinc-400 hover:text-gray-900 dark:hover:text-gray-100 transition justify-self-end"
/>
</a>

</div>


{/* Floating Image Next to Cursor */}
{hovered && (
<img
src={project.image}
alt={project.name}
className="fixed w-40 h-40 object-cover rounded-lg shadow-lg border border-zinc-300 dark:border-zinc-700 pointer-events-none z-10"
style={{ top: `${position.y}px`, left: `${position.x}px` }}
/>
)}
</div>
);
}
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"tailwind-merge": "^2.5.5"
},
"devDependencies": {
Expand Down
15 changes: 15 additions & 0 deletions public/solutions/projects.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[{
"name": "Project 1",
"description": "This is a cool project.",
"link": "https://example.com",
"github": "https://github.com/example",
"image": "https://images.unsplash.com/photo-1742943679521-f4736500a471?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
},
{
"name": "Project 2",
"description": "Another awesome project.",
"link": "https://example.com",
"github": "https://github.com/example",
"image": "/images/project2.jpg"
}
]