Main pages
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
// app/projects/code/page.tsx
|
||||
|
||||
import MatrixRain from '@/app/components/MatrixRain';
|
||||
import { FaCodeBranch, FaGithub, FaGitlab, FaGlobe, FaStar } from 'react-icons/fa';
|
||||
|
||||
interface GithubProfile {
|
||||
html_url: string;
|
||||
avatar_url: string;
|
||||
name: string;
|
||||
login: string;
|
||||
bio: string;
|
||||
followers: number;
|
||||
public_repos: number;
|
||||
}
|
||||
|
||||
interface GithubRepo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
html_url: string;
|
||||
language: string;
|
||||
stargazers_count: number;
|
||||
forks: number;
|
||||
}
|
||||
|
||||
interface GitlabRepo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
web_url: string;
|
||||
star_count: number;
|
||||
forks_count: number;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
async function getProfile(): Promise<GithubProfile | null> {
|
||||
try {
|
||||
const res = await fetch('https://api.github.com/users/WilliamSoderberg', { next: { revalidate: 60 } });
|
||||
if (!res.ok) return null;
|
||||
return res.json();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function getGithubRepos(): Promise<GithubRepo[]> {
|
||||
try {
|
||||
const res = await fetch('https://api.github.com/users/WilliamSoderberg/repos?sort=updated', { next: { revalidate: 60 } });
|
||||
if (!res.ok) return [];
|
||||
return res.json();
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getGitlabRepos(): Promise<GitlabRepo[]> {
|
||||
try {
|
||||
const res = await fetch('https://gitlab.soderberg.tech/api/v4/groups/soderberg-tech/projects', {
|
||||
next: { revalidate: 60 }
|
||||
});
|
||||
|
||||
if (!res.ok) return [];
|
||||
const projects: GitlabRepo[] = await res.json();
|
||||
const projectsWithLanguages = await Promise.all(
|
||||
projects.map(async (repo) => {
|
||||
try {
|
||||
const langRes = await fetch(`https://gitlab.soderberg.tech/api/v4/projects/${repo.id}/languages`, {
|
||||
next: { revalidate: 60 }
|
||||
});
|
||||
|
||||
if (langRes.ok) {
|
||||
const languages = await langRes.json();
|
||||
const topLanguage = Object.keys(languages).sort((a, b) => languages[b] - languages[a])[0];
|
||||
return { ...repo, language: topLanguage };
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to fetch language for GitLab repo: ${repo.id}`);
|
||||
}
|
||||
return repo;
|
||||
})
|
||||
);
|
||||
|
||||
return projectsWithLanguages;
|
||||
} catch (e) {
|
||||
console.error("GitLab fetch failed, returning empty array");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const langColors: Record<string, string> = {
|
||||
JavaScript: 'bg-[#f1e05a]',
|
||||
TypeScript: 'bg-[#2b7489]',
|
||||
Python: 'bg-[#3572A5]',
|
||||
PHP: 'bg-[#4F5D95]',
|
||||
CSS: 'bg-[#563d7c]',
|
||||
HTML: 'bg-[#e44b23]',
|
||||
Vue: 'bg-[#41b883]'
|
||||
};
|
||||
|
||||
export default async function CodePage() {
|
||||
const [profile, githubRepos, gitlabRepos] = await Promise.all([
|
||||
getProfile(),
|
||||
getGithubRepos(),
|
||||
getGitlabRepos()
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className="flex flex-col flex-1 relative min-h-dvh">
|
||||
<MatrixRain />
|
||||
|
||||
<div className="h-25 shrink-0"></div>
|
||||
|
||||
<div className="relative z-10 w-full max-w-350 mx-auto px-4 pb-20 flex flex-col gap-16">
|
||||
|
||||
{/* 2. Profile Card Section */}
|
||||
{profile && (
|
||||
<section className="flex justify-center mt-10">
|
||||
<div className="relative flex flex-col justify-center items-center bg-linear-to-b from-[#333333]/90 to-[#2a2a2a]/90 backdrop-blur-lg rounded-3xl text-center p-8 shadow-[0_8px_32px_rgba(0,0,0,0.5)] w-full max-w-md border border-white/10">
|
||||
<a href={profile.html_url} target="_blank" rel="noopener noreferrer">
|
||||
<FaGithub className="absolute text-white text-[28px] top-5 right-5 hover:text-[#84c0a0] transition-colors" />
|
||||
</a>
|
||||
<div className="w-full">
|
||||
<div className="relative h-37.5 w-37.5 mx-auto mb-5">
|
||||
<img src={profile.avatar_url} alt="Avatar" className="h-full w-full rounded-full object-cover border-4 border-[#84c0a0] shadow-[0_0_15px_rgba(132,192,160,0.5)]" />
|
||||
</div>
|
||||
<h1 className="mb-1">
|
||||
<a className="text-white text-[26px] font-bold tracking-wide no-underline hover:text-[#84c0a0] transition-colors" href={profile.html_url} target="_blank" rel="noopener noreferrer">
|
||||
{profile.name}
|
||||
</a>
|
||||
</h1>
|
||||
<div className="mb-5">
|
||||
<a className="text-[#a0aab6] font-medium no-underline hover:text-white transition-colors" href={profile.html_url} target="_blank" rel="noopener noreferrer">
|
||||
@{profile.login}
|
||||
</a>
|
||||
</div>
|
||||
<p className="font-medium mx-auto text-[#84c0a0] mt-2.5 text-[15px] leading-relaxed">
|
||||
{profile.bio}
|
||||
</p>
|
||||
<div className="mt-8 relative border-t border-white/10 pt-5 flex justify-around">
|
||||
<div className="relative">
|
||||
<div className="text-white text-[22px] font-bold">{profile.followers}</div>
|
||||
<div className="text-[11px] font-bold text-[#a0aab6] tracking-widest uppercase mt-1">Followers</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="text-white text-[22px] font-bold">{profile.public_repos}</div>
|
||||
<div className="text-[11px] font-bold text-[#a0aab6] tracking-widest uppercase mt-1">Repositories</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 3. GitLab Repositories Section */}
|
||||
<section className="bg-[#fc6d26]/10 backdrop-blur-lg rounded-3xl border border-[#fc6d26]/30 p-8 shadow-2xl">
|
||||
<div className="flex justify-center items-center flex-col mb-10">
|
||||
<a href="https://gitlab.soderberg.tech/WilliamSoderberg" target="_blank" rel="noopener noreferrer">
|
||||
<FaGitlab className="text-[#fc6d26] text-[55px] drop-shadow-[0_0_15px_rgba(252,109,38,0.4)] hover:scale-110 transition-transform cursor-pointer" />
|
||||
</a>
|
||||
<h2 className="text-white mt-3.75 text-[32px] md:text-[38px] uppercase font-bold text-center tracking-wider">
|
||||
GitLab Projects
|
||||
</h2>
|
||||
<p className="text-gray-300 text-center max-w-2xl mt-3">Self-hosted infrastructure and private development.</p>
|
||||
</div>
|
||||
|
||||
{gitlabRepos.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 justify-center">
|
||||
{gitlabRepos.map((repo) => (
|
||||
<a key={repo.id} href={repo.web_url} target="_blank" rel="noopener noreferrer" className="group flex flex-col justify-between bg-linear-to-br from-[#2a2a2a]/90 to-[#222222]/90 border border-white/10 p-6 rounded-2xl hover:-translate-y-1 hover:border-[#fc6d26]/60 hover:shadow-[0_8px_24px_rgba(252,109,38,0.2)] transition-all duration-300 no-underline">
|
||||
<div>
|
||||
<h3 className="text-[#fc6d26] text-[19px] font-bold group-hover:text-white transition-colors truncate">
|
||||
{repo.name}
|
||||
</h3>
|
||||
<p className="text-[14px] mb-4 mt-2.5 text-gray-400 text-left line-clamp-3 leading-relaxed">
|
||||
{repo.description || 'No description provided.'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center text-[13px] text-[#8b949e] mt-4 font-medium">
|
||||
{/* NEW: GitLab Language Badge */}
|
||||
{repo.language && (
|
||||
<div className="flex items-center mr-5">
|
||||
<span className={`w-2.5 h-2.5 rounded-full mr-2 ${langColors[repo.language] || 'bg-gray-500'}`}></span>
|
||||
<span className="text-gray-300">{repo.language}</span>
|
||||
</div>
|
||||
)}
|
||||
{repo.star_count > 0 && (
|
||||
<span className="flex items-center mr-4">
|
||||
<FaStar className="mr-1.5" /> {repo.star_count}
|
||||
</span>
|
||||
)}
|
||||
{repo.forks_count > 0 && (
|
||||
<span className="flex items-center">
|
||||
<FaCodeBranch className="mr-1.5" /> {repo.forks_count}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center text-gray-400 italic py-8 bg-black/20 rounded-xl">No public GitLab repositories found or API is restricted.</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* 4. GitHub Repositories Section */}
|
||||
<section className="bg-[#84c0a0]/10 backdrop-blur-lg rounded-3xl border border-[#84c0a0]/30 p-8 shadow-2xl">
|
||||
<div className="flex justify-center items-center flex-col mb-10">
|
||||
<FaGithub className="text-white text-[55px] drop-shadow-[0_0_15px_rgba(255,255,255,0.2)]" />
|
||||
<h2 className="text-white mt-3.75 text-[32px] md:text-[38px] uppercase font-bold text-center tracking-wider">
|
||||
Public Repositories
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6 justify-center">
|
||||
{githubRepos.map((repo) => (
|
||||
<a key={repo.id} href={repo.html_url} target="_blank" rel="noopener noreferrer" className="group flex flex-col justify-between bg-linear-to-br from-[#2a2a2a]/90 to-[#222222]/90 border border-white/10 p-6 rounded-2xl hover:-translate-y-1 hover:border-[#84c0a0]/60 hover:shadow-[0_8px_24px_rgba(132,192,160,0.2)] transition-all duration-300 no-underline">
|
||||
<div>
|
||||
<h3 className="text-[#4e9fff] text-[19px] font-bold group-hover:text-white transition-colors truncate">
|
||||
{repo.name}
|
||||
</h3>
|
||||
<p className="text-[14px] mb-4 mt-2.5 text-gray-400 text-left line-clamp-3 leading-relaxed">
|
||||
{repo.description || 'No description provided.'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center text-[13px] text-[#8b949e] mt-4 font-medium">
|
||||
{repo.language && (
|
||||
<div className="flex items-center mr-5">
|
||||
<span className={`w-2.5 h-2.5 rounded-full mr-2 ${langColors[repo.language] || 'bg-gray-500'}`}></span>
|
||||
<span className="text-gray-300">{repo.language}</span>
|
||||
</div>
|
||||
)}
|
||||
{repo.stargazers_count > 0 && (
|
||||
<span className="flex items-center mr-4">
|
||||
<FaStar className="mr-1.5" /> {repo.stargazers_count}
|
||||
</span>
|
||||
)}
|
||||
{repo.forks > 0 && (
|
||||
<span className="flex items-center">
|
||||
<FaCodeBranch className="mr-1.5" /> {repo.forks}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 5. Hosted Websites Section */}
|
||||
<section className="bg-white/5 backdrop-blur-lg rounded-3xl border border-white/10 p-10 shadow-2xl">
|
||||
<div className="flex justify-center items-center flex-col mb-10">
|
||||
<FaGlobe className="text-white text-[55px] drop-shadow-[0_0_15px_rgba(255,255,255,0.2)]" />
|
||||
<h2 className="text-white mt-3.75 text-[32px] md:text-[38px] uppercase font-bold text-center tracking-wider">
|
||||
Hosted Websites
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* Changed from Flex to Grid for perfect centering! */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12 max-w-5xl mx-auto justify-items-center items-center py-6">
|
||||
<a className="no-underline text-white text-[22px] flex flex-col items-center hover:scale-105 transition-transform group" href="https://soderberg.tech">
|
||||
<img src="/assets/logos/logo.svg" alt="Soderberg Tech" className="w-22.5 h-16.25 object-contain mb-5 drop-shadow-md group-hover:drop-shadow-[0_0_10px_rgba(255,255,255,0.5)] transition-all" />
|
||||
<span className="font-bold tracking-wide text-center">Soderberg Tech</span>
|
||||
</a>
|
||||
<a className="no-underline text-white text-[22px] flex flex-col items-center hover:scale-105 transition-transform group" href="https://lirkab.se">
|
||||
<img src="/assets/logos/lirkab.svg" alt="Lirkab" className="w-22.5 h-16.25 object-contain mb-5 drop-shadow-md group-hover:drop-shadow-[0_0_10px_rgba(255,255,255,0.5)] transition-all" />
|
||||
<span className="font-bold tracking-wide text-center">Lirkab</span>
|
||||
</a>
|
||||
<a className="no-underline text-white text-[22px] flex flex-col items-center hover:scale-105 transition-transform group" href="https://lerbergetsvolleyboll.se">
|
||||
<img src="/assets/logos/lvs.svg" alt="Lerbergets Volleyboll" className="w-22.5 h-16.25 object-contain mb-5 drop-shadow-md group-hover:drop-shadow-[0_0_10px_rgba(255,255,255,0.5)] transition-all" />
|
||||
<span className="font-bold tracking-wide text-center">Lerbergets Volleyboll</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// app/projects/design/page.tsx
|
||||
|
||||
'use client';
|
||||
|
||||
import { Center, Environment, Loader, OrbitControls, useGLTF } from '@react-three/drei';
|
||||
import { Canvas, useThree } from '@react-three/fiber';
|
||||
import { Suspense, useEffect, useState, useTransition } from 'react';
|
||||
import { TbCube3dSphere } from 'react-icons/tb';
|
||||
import * as THREE from 'three';
|
||||
|
||||
const models = [
|
||||
{ name: 'Wallet', src: 'Wallet' },
|
||||
{ name: 'Car', src: 'Car' },
|
||||
{ name: 'Magnet holder', src: 'Magnet holder' },
|
||||
{ name: 'PC', src: 'PC' },
|
||||
];
|
||||
|
||||
function Model({ url, onReady }: { url: string; onReady: () => void }) {
|
||||
const { scene } = useGLTF(url);
|
||||
|
||||
useEffect(() => {
|
||||
if (scene) {
|
||||
const timer = setTimeout(onReady, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [scene, onReady]);
|
||||
|
||||
return <primitive object={scene} />;
|
||||
}
|
||||
|
||||
function DynamicCamera({ radius }: { radius: number }) {
|
||||
const { camera } = useThree();
|
||||
useEffect(() => {
|
||||
camera.position.set(0, radius * 0.5, radius * 2.5);
|
||||
camera.lookAt(0, 0, 0);
|
||||
camera.updateProjectionMatrix();
|
||||
}, [radius, camera]);
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function DesignPage() {
|
||||
const [activeModel, setActiveModel] = useState(models[0].src);
|
||||
const [modelRadius, setModelRadius] = useState(1);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [isSceneReady, setIsSceneReady] = useState(false);
|
||||
const [isCentering, setIsCentering] = useState(true);
|
||||
|
||||
const handleModelChange = (newModel: string) => {
|
||||
if (newModel === activeModel) return;
|
||||
|
||||
setIsCentering(true);
|
||||
setIsSceneReady(false);
|
||||
|
||||
startTransition(() => {
|
||||
setActiveModel(newModel);
|
||||
});
|
||||
};
|
||||
|
||||
const showOverlay = isPending || !isSceneReady;
|
||||
|
||||
return (
|
||||
<main className="flex flex-col flex-1 relative bg-[#0d1117] overflow-hidden">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-200 h-200 bg-[#84c0a0]/10 blur-[150px] rounded-full pointer-events-none z-0"></div>
|
||||
<div className="h-25 shrink-0"></div>
|
||||
|
||||
<div className="relative z-10 w-full max-w-350 mx-auto px-4 pb-20 flex flex-col gap-10">
|
||||
<div className="pt-5 flex justify-center items-center flex-col text-center">
|
||||
<TbCube3dSphere className="text-white text-[80px] drop-shadow-[0_0_15px_rgba(255,255,255,0.2)]" />
|
||||
<h2 className="text-white text-[32px] md:text-[45px] uppercase mt-3.75 font-bold tracking-wider">CAD Library</h2>
|
||||
</div>
|
||||
|
||||
<section className="w-full flex justify-center">
|
||||
<div className="w-full relative bg-white/5 backdrop-blur-xl rounded-4xl border border-white/10 shadow-[0_8px_32px_rgba(0,0,0,0.5)] p-4 md:p-8 flex flex-col">
|
||||
<div className="w-full h-[50vh] md:h-[65vh] rounded-[20px] overflow-hidden relative bg-linear-to-br from-[#1a1a1a] to-[#0f0f0f] shadow-inner border border-white/5 cursor-grab active:cursor-grabbing">
|
||||
|
||||
{showOverlay && (
|
||||
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center bg-[#0d1117]/80 backdrop-blur-md transition-all duration-500">
|
||||
<div className="w-12 h-12 border-4 border-[#84c0a0]/20 border-t-[#84c0a0] rounded-full animate-spin mb-4"></div>
|
||||
<p className="text-[#84c0a0] font-mono text-xs uppercase tracking-[0.2em] font-bold">
|
||||
{isPending ? "Fetching Data..." : "Compiling Materials..."}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Canvas shadows={{ type: THREE.PCFShadowMap }} dpr={[1, 2]} camera={{ near: 0.001, far: 1000, fov: 50 }}>
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[10, 10, 10]} intensity={1} castShadow />
|
||||
<Environment preset="city" />
|
||||
<DynamicCamera radius={modelRadius} />
|
||||
|
||||
<Suspense fallback={null}>
|
||||
<Center
|
||||
key={activeModel}
|
||||
onCentered={({ boundingSphere }) => {
|
||||
setModelRadius(boundingSphere.radius);
|
||||
requestAnimationFrame(() => setIsCentering(false));
|
||||
}}
|
||||
>
|
||||
<group visible={!isCentering && isSceneReady}>
|
||||
<Model
|
||||
url={`/models/${activeModel}.glb`}
|
||||
onReady={() => setIsSceneReady(true)}
|
||||
/>
|
||||
</group>
|
||||
</Center>
|
||||
</Suspense>
|
||||
|
||||
<OrbitControls
|
||||
makeDefault
|
||||
autoRotate
|
||||
autoRotateSpeed={0.5}
|
||||
enablePan={true}
|
||||
enableZoom={true}
|
||||
minDistance={modelRadius * 0.5}
|
||||
maxDistance={modelRadius * 6}
|
||||
/>
|
||||
</Canvas>
|
||||
|
||||
<Loader
|
||||
containerStyles={{ background: 'transparent' }}
|
||||
innerStyles={{ display: 'none' }}
|
||||
/>
|
||||
|
||||
<a href={`/models/${activeModel}.usdz`} rel="ar" className="absolute top-4 right-4 bg-black/50 backdrop-blur-md text-white text-sm px-4 py-2 rounded-full border border-white/10 hover:bg-white/20 transition-colors md:hidden z-10 flex items-center gap-2">
|
||||
<img src={`/models/${activeModel}.png`} alt="AR Preview" className="w-5 h-5 rounded-full object-cover" />
|
||||
View in AR
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="w-full mt-8 flex justify-center">
|
||||
<div className="flex flex-wrap justify-center gap-4 md:gap-8 w-full px-2">
|
||||
{models.map((model) => (
|
||||
<button
|
||||
key={model.name}
|
||||
className={`shrink-0 w-20 h-20 md:w-25 md:h-25 bg-contain bg-no-repeat bg-center rounded-2xl transition-all duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#84c0a0] relative group ${activeModel === model.src
|
||||
? 'bg-[#2a2a2a] border-2 border-[#84c0a0] scale-110 shadow-[0_0_20px_rgba(132,192,160,0.3)]'
|
||||
: 'bg-[#1a1a1a] border border-white/10 hover:bg-[#252525] hover:-translate-y-1'
|
||||
}`}
|
||||
style={{ backgroundImage: `url('/models/${model.src}.png')` }}
|
||||
onClick={() => handleModelChange(model.src)}
|
||||
onMouseEnter={() => useGLTF.preload(`/models/${model.src}.glb`)}
|
||||
aria-label={`View ${model.name} model`}
|
||||
>
|
||||
<span className="absolute -bottom-8 left-1/2 -translate-x-1/2 text-xs font-semibold text-white opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap bg-black/80 px-2 py-1 rounded">
|
||||
{model.name}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// app/projects/page.tsx
|
||||
|
||||
import Link from 'next/link';
|
||||
import BlurredCode from '@/app/components/BlurredCode';
|
||||
import { TbCube3dSphere } from 'react-icons/tb';
|
||||
import { FaCode } from 'react-icons/fa';
|
||||
|
||||
export default function ProjectsPage() {
|
||||
return (
|
||||
<main className="flex flex-col flex-1 relative bg-[#0d1117] overflow-x-hidden">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-150 md:w-250 h-150 md:h-250 bg-[#84c0a0]/10 blur-[150px] rounded-full pointer-events-none z-0"></div>
|
||||
|
||||
<div className="h-25 shrink-0"></div>
|
||||
|
||||
<div className="relative z-10 flex-1 flex flex-col justify-center items-center py-10 md:py-20 px-6">
|
||||
<div className="mb-12 text-center">
|
||||
<h1 className="text-white text-[32px] md:text-[55px] uppercase font-extrabold tracking-wider drop-shadow-lg">
|
||||
My Portfolios
|
||||
</h1>
|
||||
<p className="text-[#a0aab6] mt-2 text-base md:text-xl max-w-md mx-auto">
|
||||
Choose a discipline to explore my work.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section className="flex flex-col md:flex-row gap-8 md:gap-12 w-full max-w-275 items-stretch">
|
||||
|
||||
<Link
|
||||
href="/projects/code"
|
||||
className="relative flex-1 group min-h-75 md:h-125 rounded-4xl overflow-hidden border border-white/10 shadow-[0_8px_32px_rgba(0,0,0,0.5)] transition-all duration-500 hover:-translate-y-2 hover:border-[#84c0a0]/60 no-underline"
|
||||
>
|
||||
<BlurredCode />
|
||||
<div className="absolute inset-0 z-10 bg-[#0d1117]/60 backdrop-blur-[2px] group-hover:bg-[#0d1117]/20 group-hover:backdrop-blur-[1px] transition-all duration-500 flex flex-col justify-center items-center p-8">
|
||||
<FaCode className="text-white text-[50px] md:text-[80px] mb-4 md:mb-6 opacity-80 group-hover:opacity-100 group-hover:scale-110 group-hover:text-[#84c0a0] transition-all duration-500" />
|
||||
<h2 className="text-white text-[32px] md:text-[50px] font-bold text-center tracking-wide">
|
||||
Coding
|
||||
</h2>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="/projects/design"
|
||||
className="relative flex-1 group min-h-75 md:h-125 rounded-4xl overflow-hidden border border-white/10 shadow-[0_8px_32px_rgba(0,0,0,0.5)] transition-all duration-500 hover:-translate-y-2 hover:border-[#84c0a0]/60 no-underline"
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 z-0 bg-center bg-cover bg-no-repeat opacity-40 group-hover:opacity-100 group-hover:scale-105 transition-all duration-700"
|
||||
style={{ backgroundImage: `url('/assets/images/CAD.png')` }}
|
||||
></div>
|
||||
|
||||
<div className="absolute inset-0 z-10 bg-[#0d1117]/60 backdrop-blur-[2px] group-hover:bg-[#0d1117]/20 group-hover:backdrop-blur-[1px] transition-all duration-500 flex flex-col justify-center items-center p-8">
|
||||
<TbCube3dSphere className="text-white text-[50px] md:text-[80px] mb-4 md:mb-6 opacity-80 group-hover:opacity-100 group-hover:scale-110 group-hover:text-[#84c0a0] transition-all duration-500" />
|
||||
<h2 className="text-white text-[32px] md:text-[50px] font-bold text-center tracking-wide">
|
||||
CAD & Design
|
||||
</h2>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user