back to fresh

This commit is contained in:
2026-10-03 18:56:07 +02:00
parent 277393578a
commit 3ea4946164
35 changed files with 0 additions and 12586 deletions
-16
View File
@@ -1,16 +0,0 @@
# Stage 1: Build the application
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Serve the static files with Nginx
FROM nginx:alpine
# Copy the build files from the builder stage to the Nginx web directory
COPY --from=builder /app/dist /usr/share/nginx/html
# Expose port 80 where the Nginx server runs
EXPOSE 80
# Command to start Nginx server
CMD ["nginx", "-g", "daemon off;"]
-73
View File
@@ -1,73 +0,0 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
-23
View File
@@ -1,23 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
-13
View File
@@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Selimaj.dev | Klesti Selimaj</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
-10111
View File
File diff suppressed because it is too large Load Diff
-84
View File
@@ -1,84 +0,0 @@
{
"name": "selimaj-portfolio",
"version": "1.0.0",
"type": "module",
"license": "MIT",
"scripts": {
"dev": "vite --host",
"build": "vite build",
"preview": "vite preview --host",
"check": "tsc --noEmit",
"format": "prettier --write ."
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-aspect-ratio": "^1.1.7",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-context-menu": "^2.2.16",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-menubar": "^1.1.16",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"axios": "^1.12.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.23.22",
"input-otp": "^1.4.2",
"lucide-react": "^0.453.0",
"nanoid": "^5.1.5",
"next-themes": "^0.4.6",
"react": "^19.2.1",
"react-day-picker": "^9.11.1",
"react-dom": "^19.2.1",
"react-hook-form": "^7.64.0",
"react-resizable-panels": "^3.0.6",
"recharts": "^2.15.2",
"sonner": "^2.0.7",
"streamdown": "^1.4.0",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2",
"wouter": "^3.3.5",
"zod": "^4.1.12"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.15",
"@tailwindcss/vite": "^4.2.1",
"@types/node": "^24.7.0",
"@types/react": "^19.2.1",
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react": "^5.0.4",
"autoprefixer": "^10.4.20",
"pnpm": "^10.15.1",
"postcss": "^8.4.47",
"prettier": "^3.6.2",
"tailwindcss": "^4.2.1",
"tsx": "^4.19.1",
"tw-animate-css": "^1.4.0",
"typescript": "5.6.3",
"vite": "^7.1.7",
"vitest": "^2.1.4"
},
"packageManager": "[email protected]"
}
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.8 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

-11
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.8 KiB

-32
View File
@@ -1,32 +0,0 @@
import { Toaster } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import NotFound from "@/pages/NotFound";
import { Route, Switch } from "wouter";
import ErrorBoundary from "./components/ErrorBoundary";
import { ThemeProvider } from "./contexts/ThemeContext";
import Home from "./pages/Home";
function Router() {
return (
<Switch>
<Route path={"/"} component={Home} />
<Route path={"/404"} component={NotFound} />
<Route component={NotFound} />
</Switch>
);
}
function App() {
return (
<ErrorBoundary>
<ThemeProvider defaultTheme="dark">
<TooltipProvider>
<Toaster />
<Router />
</TooltipProvider>
</ThemeProvider>
</ErrorBoundary>
);
}
export default App;
-198
View File
@@ -1,198 +0,0 @@
/*
* ABOUT SECTION — selimaj.dev
* Design: Glassmorphic Terminal
* Asymmetric layout: text left, abstract visual right
*/
import { EarthIcon, LayersIcon, TargetIcon, ZapIcon } from "lucide-react";
import { useEffect, useRef } from "react";
const ABOUT_VISUAL =
"/logo.png";
function useReveal() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
el.classList.add("visible");
observer.disconnect();
}
},
{ threshold: 0.15 },
);
observer.observe(el);
return () => observer.disconnect();
}, []);
return ref;
}
const highlights = [
{ Icon: ZapIcon, label: "Currently", value: "Grinding on SaaS products" },
{ Icon: LayersIcon, label: "Fav Stack", value: "Rust · TypeScript · Linux" },
{ Icon: TargetIcon, label: "Focus", value: "Low-level systems & UI/UX" },
{ Icon: EarthIcon, label: "Based in", value: "Albania" },
];
export default function AboutSection() {
const ref1 = useReveal();
const ref2 = useReveal();
return (
<section
id="about"
className="py-32 relative"
style={{ background: "#080808" }}
>
{/* Subtle top border */}
<div className="absolute top-0 left-0 right-0 h-px bg-linear-to-r from-transparent via-white/10 to-transparent" />
<div className="max-w-6xl mx-auto px-6">
{/* Section label */}
<div className="terminal-label mb-4">&gt; about.me</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Left: Text */}
<div ref={ref1} className="reveal">
<h2 className="section-heading text-4xl sm:text-5xl text-white mb-8 leading-tight">
Building the future, <span className="text-white/40">one commit at a time.</span>
</h2>
<div
className="space-y-4 text-white/60 leading-relaxed"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
<p>
I'm{" "}
<strong className="text-white font-semibold">
Klesti Selimaj
</strong>{" "}
— a 16-year-old software developer and entrepreneur from Albania
with 6 years of hands-on experience. I started coding at 10 and
never stopped.
</p>
<p>
My passion lies in{" "}
<strong className="text-white/80">
low-level systems programming
</strong>
, building developer tools, and crafting polished user
experiences. I work primarily in{" "}
<strong className="text-white/80">Rust</strong> and{" "}
<strong className="text-white/80">TypeScript</strong>, and I'm
deeply interested in hacking, Linux internals, and TUI
applications.
</p>
<p>
Beyond code, I'm building towards a successful SaaS product —
combining my technical depth with an entrepreneurial mindset to
create tools that developers actually want to use.
</p>
</div>
{/* Highlights grid */}
<div className="mt-10 grid grid-cols-2 gap-3">
{highlights.map((h) => (
<div key={h.label} className="glass rounded-xl p-4 card-hover">
<div className="flex items-center gap-3">
<div className="text-lg">{<h.Icon />}</div>
<div>
<div className="terminal-label text-xs mb-0.5">
{h.label}
</div>
<div
className="text-sm text-white/80 font-medium"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
{h.value}
</div>
</div>
</div>
</div>
))}
</div>
{/* Social links */}
<div className="mt-8 flex flex-wrap gap-3">
{[
{
label: "GitHub",
href: "https://github.com/selimaj-dev",
icon: "⌥",
},
{
label: "YouTube",
href: "https://youtube.com/@SelimajDev",
icon: "▶",
},
{
label: "Instagram",
href: "https://instagram.com/selimaj.dev",
icon: "◈",
},
].map((s) => (
<a
key={s.label}
href={s.href}
target="_blank"
rel="noopener noreferrer"
className="skill-chip hover:no-underline"
>
<span className="text-white/40">{s.icon}</span>
{s.label}
</a>
))}
</div>
</div>
{/* Right: Visual */}
<div ref={ref2} className="reveal flex justify-center lg:justify-end">
<div className="relative w-full max-w-md">
{/* Glow behind image */}
<div
className="absolute inset-0 rounded-2xl blur-3xl opacity-20"
style={{
background:
"radial-gradient(circle, rgba(255,255,255,0.15) 0%, transparent 70%)",
}}
/>
<div className="relative glass rounded-2xl overflow-hidden float-anim">
<img
src={ABOUT_VISUAL}
alt="Abstract tech visualization"
className="w-full h-auto object-cover"
style={{ aspectRatio: "1/1" }}
/>
{/* Overlay with terminal text */}
<div
className="absolute bottom-0 left-0 right-0 p-5"
style={{
background:
"linear-gradient(to top, rgba(8,8,8,0.9) 0%, transparent 100%)",
}}
>
<div className="terminal-label text-xs">
<span className="text-white/30">$</span>{" "}
<span className="text-white/60">whoami</span>
</div>
<div
className="text-white/80 text-sm mt-1"
style={{ fontFamily: "'JetBrains Mono', monospace" }}
>
Klesti Selimaj
</div>
<div className="terminal-label text-xs mt-0.5">
uid=1000(klesti) groups=ORUS,Axiom,Saturn,Nexa
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
-175
View File
@@ -1,175 +0,0 @@
/*
* CONTACT SECTION — selimaj.dev
* Design: Glassmorphic Terminal
* Clean contact links with glass cards
*/
import { useEffect, useRef } from "react";
import { Github, Youtube, Instagram, ExternalLink, Mail, MailIcon } from "lucide-react";
function useReveal(threshold = 0.1) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
el.classList.add("visible");
observer.disconnect();
}
},
{ threshold }
);
observer.observe(el);
return () => observer.disconnect();
}, [threshold]);
return ref;
}
const contactLinks = [
{
icon: Github,
label: "GitHub",
handle: "@selimaj-dev",
href: "https://github.com/selimaj-dev",
description: "97+ repositories · 2,670 contributions/year",
},
{
icon: Youtube,
label: "YouTube",
handle: "@SelimajDev",
href: "https://youtube.com/@SelimajDev",
description: "Dev content, tutorials, and builds",
},
{
icon: Instagram,
label: "Instagram",
handle: "@selimaj.dev",
href: "https://instagram.com/selimaj.dev",
description: "Behind the scenes and updates",
},
{
icon: MailIcon,
label: "Email",
handle: "[email protected]",
href: "mailto:[email protected]",
description: "For business inquiries, collaborations, and contact",
},
];
export default function ContactSection() {
const headerRef = useReveal();
const contentRef = useReveal(0.1);
return (
<section id="contact" className="py-32 relative" style={{ background: "#080808" }}>
<div className="absolute top-0 left-0 right-0 h-px bg-linear-to-r from-transparent via-white/10 to-transparent" />
{/* Subtle radial glow */}
<div
className="absolute inset-0 pointer-events-none"
style={{
background: "radial-gradient(ellipse 60% 40% at 50% 100%, rgba(255,255,255,0.03) 0%, transparent 70%)",
}}
/>
<div className="max-w-6xl mx-auto px-6">
{/* Header */}
<div ref={headerRef} className="reveal mb-16 text-center">
<div className="terminal-label mb-4 text-center">&gt; contact --open</div>
<h2 className="section-heading text-4xl sm:text-5xl text-white leading-tight mb-6">
Let's Build
<br />
<span className="text-white/40">Something Together</span>
</h2>
<p
className="text-white/50 max-w-xl mx-auto text-base leading-relaxed"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
I'm always open to interesting projects, collaborations, or just a good conversation
about tech. Reach out through any of the channels below.
</p>
</div>
{/* Contact cards */}
<div ref={contentRef} className="reveal">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-12">
{contactLinks.map((link) => {
const Icon = link.icon;
return (
<a
key={link.label}
href={link.href}
target="_blank"
rel="noopener noreferrer"
className="glass rounded-2xl p-6 card-hover group no-underline flex flex-col gap-4"
>
<div className="flex items-center justify-between">
<div className="w-10 h-10 glass-strong rounded-xl flex items-center justify-center">
<Icon size={18} className="text-white/70 group-hover:text-white transition-colors" />
</div>
<ExternalLink size={12} className="text-white/20 group-hover:text-white/50 transition-colors" />
</div>
<div>
<div
className="text-white font-semibold text-sm mb-0.5"
style={{ fontFamily: "'Syne', sans-serif" }}
>
{link.label}
</div>
<div className="terminal-label text-xs mb-2">{link.handle}</div>
<div
className="text-white/40 text-xs leading-relaxed"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
{link.description}
</div>
</div>
</a>
);
})}
</div>
{/* Big CTA */}
<div className="glass rounded-2xl p-10 text-center">
<div className="terminal-label mb-4">&gt; open_to_work --status</div>
<h3
className="text-2xl sm:text-3xl font-bold text-white mb-4"
style={{ fontFamily: "'Syne', sans-serif" }}
>
Open to freelance &amp; collaboration
</h3>
<p
className="text-white/50 mb-8 max-w-lg mx-auto text-sm leading-relaxed"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
Looking for interesting projects, SaaS co-founders, or clients who need
a developer who ships fast and cares about quality.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="px-8 py-3.5 bg-white text-black font-semibold rounded-xl hover:bg-white/90 transition-all duration-200 text-sm no-underline"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
View GitHub Profile
</a>
<a
href="https://instagram.com/selimaj.dev"
target="_blank"
rel="noopener noreferrer"
className="px-8 py-3.5 glass border border-white/10 text-white/70 font-medium rounded-xl hover:text-white hover:border-white/20 transition-all duration-200 text-sm no-underline"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
DM on Instagram
</a>
</div>
</div>
</div>
</div>
</section>
);
}
-62
View File
@@ -1,62 +0,0 @@
import { cn } from "@/lib/utils";
import { AlertTriangle, RotateCcw } from "lucide-react";
import { Component, type ReactNode } from "react";
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return (
<div className="flex items-center justify-center min-h-screen p-8 bg-background">
<div className="flex flex-col items-center w-full max-w-2xl p-8">
<AlertTriangle
size={48}
className="text-destructive mb-6 shrink-0"
/>
<h2 className="text-xl mb-4">An unexpected error occurred.</h2>
<div className="p-4 w-full rounded bg-muted overflow-auto mb-6">
<pre className="text-sm text-muted-foreground whitespace-break-spaces">
{this.state.error?.stack}
</pre>
</div>
<button
onClick={() => window.location.reload()}
className={cn(
"flex items-center gap-2 px-4 py-2 rounded-lg",
"bg-primary text-primary-foreground",
"hover:opacity-90 cursor-pointer"
)}
>
<RotateCcw size={16} />
Reload Page
</button>
</div>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
-59
View File
@@ -1,59 +0,0 @@
/*
* FOOTER — selimaj.dev
* Design: Glassmorphic Terminal
* Minimal footer with branding and links
*/
export default function Footer() {
const year = new Date().getFullYear();
return (
<footer
className="py-10 relative"
style={{
background: "#080808",
borderTop: "1px solid rgba(255,255,255,0.06)",
}}
>
<div className="max-w-6xl mx-auto px-6">
<div className="flex flex-col sm:flex-row items-center justify-between gap-4">
{/* Brand */}
<div className="flex items-center gap-3">
<span
className="text-white font-bold text-base tracking-tight"
style={{ fontFamily: "'Syne', sans-serif" }}
>
selimaj<span className="text-white/30">.dev</span>
</span>
<span className="text-white/20 text-xs">·</span>
<span className="terminal-label text-xs">Klesti Selimaj</span>
</div>
{/* Links */}
<div className="flex items-center gap-6">
{[
{ label: "GitHub", href: "https://github.com/selimaj-dev" },
{ label: "YouTube", href: "https://youtube.com/@SelimajDev" },
{ label: "Instagram", href: "https://instagram.com/selimaj.dev" },
].map((link) => (
<a
key={link.label}
href={link.href}
target="_blank"
rel="noopener noreferrer"
className="terminal-label text-xs hover:text-white/70 transition-colors"
>
{link.label}
</a>
))}
</div>
{/* Copyright */}
<div className="terminal-label text-xs">
© {year} selimaj.dev
</div>
</div>
</div>
</footer>
);
}
-167
View File
@@ -1,167 +0,0 @@
/*
* HERO SECTION — selimaj.dev
* Design: Glassmorphic Terminal
* Full-viewport dark hero with animated background image, typewriter effect, and glass CTA
*/
import { useEffect, useState } from "react";
const HERO_BG = "https://d2xsxph8kpxj0f.cloudfront.net/310519663439135967/RkRL4wd7YzNoLyaN7suWbR/hero-bg-G3393XyTzqxu3D4dR63od8.webp";
const typewriterPhrases = [
"Software Developer.",
"Entrepreneur.",
"SaaS Builder.",
"Systems Programmer.",
"Open Source Contributor.",
];
function useTypewriter(phrases: string[], speed = 60, pause = 1800) {
const [display, setDisplay] = useState("");
const [phraseIdx, setPhraseIdx] = useState(0);
const [charIdx, setCharIdx] = useState(0);
const [deleting, setDeleting] = useState(false);
useEffect(() => {
const current = phrases[phraseIdx];
let timeout: ReturnType<typeof setTimeout>;
if (!deleting && charIdx < current.length) {
timeout = setTimeout(() => setCharIdx((c) => c + 1), speed);
} else if (!deleting && charIdx === current.length) {
timeout = setTimeout(() => setDeleting(true), pause);
} else if (deleting && charIdx > 0) {
timeout = setTimeout(() => setCharIdx((c) => c - 1), speed / 2);
} else if (deleting && charIdx === 0) {
setDeleting(false);
setPhraseIdx((i) => (i + 1) % phrases.length);
}
setDisplay(current.slice(0, charIdx));
return () => clearTimeout(timeout);
}, [charIdx, deleting, phraseIdx, phrases, speed, pause]);
return display;
}
export default function HeroSection() {
const typed = useTypewriter(typewriterPhrases);
return (
<section
id="hero"
className="relative min-h-screen flex items-center justify-center overflow-hidden"
style={{ background: "#080808" }}
>
{/* Background image */}
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url(${HERO_BG})`,
opacity: 0.55,
}}
/>
{/* Gradient overlays */}
<div className="absolute inset-0 bg-linear-to-b from-transparent via-transparent to-[#080808]" />
<div className="absolute inset-0 bg-linear-to-r from-[#080808]/60 via-transparent to-[#080808]/60" />
{/* Animated glow orb */}
<div
className="absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/2 w-150 h-150 rounded-full pointer-events-none"
style={{
background: "radial-gradient(circle, rgba(255,255,255,0.04) 0%, transparent 70%)",
animation: "float 6s ease-in-out infinite",
}}
/>
{/* Content */}
<div className="relative z-10 max-w-5xl mx-auto px-6 text-center">
{/* Terminal label */}
<div className="inline-flex items-center gap-2 mb-8 px-4 py-2 glass rounded-full">
<span className="w-2 h-2 rounded-full bg-white/60 inline-block" />
<span className="terminal-label text-xs">
&gt; klesti@selimaj.dev ~ $
</span>
</div>
{/* Main heading */}
<h1
className="text-5xl sm:text-6xl md:text-7xl lg:text-8xl font-bold text-white mb-6 leading-none tracking-tight fade-in-up"
style={{ fontFamily: "'Syne', sans-serif" }}
>
Klesti
<br />
<span className="text-white/30">Selimaj</span>
</h1>
{/* Typewriter subtitle */}
<div
className="text-xl sm:text-2xl md:text-3xl font-medium text-white/70 mb-4 h-10 fade-in-up delay-200"
style={{ fontFamily: "'Syne', sans-serif" }}
>
{typed}
<span className="cursor-blink text-white/50 ml-0.5">|</span>
</div>
{/* Description */}
<p
className="text-base sm:text-lg text-white/45 max-w-2xl mx-auto mb-12 leading-relaxed fade-in-up delay-400"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
16 years old · 6 years of experience · Albania
<br />
Passionate about low-level systems, hacking, UI/UX, and making cool tools.
</p>
{/* CTA Buttons */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 fade-in-up delay-500">
<button
onClick={() => {
document.querySelector("#projects")?.scrollIntoView({ behavior: "smooth" });
}}
className="px-8 py-3.5 bg-white text-black font-semibold rounded-xl hover:bg-white/90 transition-all duration-200 text-sm"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
View Projects
</button>
<button
onClick={() => {
document.querySelector("#contact")?.scrollIntoView({ behavior: "smooth" });
}}
className="px-8 py-3.5 glass text-white/80 font-medium rounded-xl hover:text-white hover:border-white/20 transition-all duration-200 text-sm border border-white/10"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
Get in Touch
</button>
</div>
{/* Stats row */}
<div className="mt-20 flex flex-row flex-wrap items-center justify-center gap-6 sm:gap-12 fade-in-up delay-700">
{[
{ value: "6+", label: "Years Experience" },
{ value: "97+", label: "Repositories" },
{ value: "2,670+", label: "Contributions / Year" },
{ value: "#2", label: "National GitHub user" },
].map((stat) => (
<div key={stat.label} className="text-center">
<div
className="text-3xl font-bold text-white"
style={{ fontFamily: "'Syne', sans-serif" }}
>
{stat.value}
</div>
<div className="terminal-label mt-1">{stat.label}</div>
</div>
))}
</div>
</div>
{/* Scroll indicator */}
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 opacity-0 fade-in-up delay-800" style={{animationFillMode:'forwards'}}>
<div className="w-px h-8 bg-linear-to-b from-white/30 to-transparent" />
<span className="terminal-label text-xs">scroll</span>
</div>
</section>
);
}
-124
View File
@@ -1,124 +0,0 @@
/*
* NAVBAR — selimaj.dev
* Design: Glassmorphic Terminal
* Glass effect: backdrop-blur + rgba background
* Becomes more opaque on scroll
*/
import { useEffect, useState } from "react";
const navLinks = [
{ label: "About", href: "#about" },
{ label: "Projects", href: "#projects" },
{ label: "Skills", href: "#skills" },
{ label: "Contact", href: "#contact" },
];
export default function Navbar() {
const [scrolled, setScrolled] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 40);
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const handleNavClick = (href: string) => {
setMenuOpen(false);
const el = document.querySelector(href);
if (el) {
el.scrollIntoView({ behavior: "smooth" });
}
};
return (
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-500 ${
scrolled ? "navbar-glass-solid" : "navbar-glass"
}`}
>
<div className="max-w-6xl mx-auto px-6 flex items-center justify-between h-16">
{/* Logo */}
<a
href="#"
onClick={(e) => { e.preventDefault(); window.scrollTo({ top: 0, behavior: "smooth" }); }}
className="flex items-center gap-2 group"
>
<span
className="text-white font-bold text-lg tracking-tight"
style={{ fontFamily: "'Syne', sans-serif" }}
>
selimaj<span className="text-white/40">.dev</span>
</span>
</a>
{/* Desktop nav links */}
<div className="hidden md:flex items-center gap-1">
{navLinks.map((link) => (
<button
key={link.href}
onClick={() => handleNavClick(link.href)}
className="px-4 py-2 text-sm text-white/60 hover:text-white transition-colors duration-200 rounded-lg hover:bg-white/5"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
{link.label}
</button>
))}
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="ml-3 px-4 py-2 text-sm font-medium text-black bg-white rounded-lg hover:bg-white/90 transition-all duration-200"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
GitHub
</a>
</div>
{/* Mobile hamburger */}
<button
className="md:hidden flex flex-col gap-1.5 p-2"
onClick={() => setMenuOpen(!menuOpen)}
aria-label="Toggle menu"
>
<span className={`block w-5 h-0.5 bg-white/70 transition-all duration-300 ${menuOpen ? "rotate-45 translate-y-2" : ""}`} />
<span className={`block w-5 h-0.5 bg-white/70 transition-all duration-300 ${menuOpen ? "opacity-0" : ""}`} />
<span className={`block w-5 h-0.5 bg-white/70 transition-all duration-300 ${menuOpen ? "-rotate-45 -translate-y-2" : ""}`} />
</button>
</div>
{/* Mobile menu */}
<div
className={`md:hidden overflow-hidden transition-all duration-300 ${
menuOpen ? "max-h-64 opacity-100" : "max-h-0 opacity-0"
}`}
style={{ background: "rgba(8,8,8,0.95)", borderTop: "1px solid rgba(255,255,255,0.06)" }}
>
<div className="px-6 py-4 flex flex-col gap-1">
{navLinks.map((link) => (
<button
key={link.href}
onClick={() => handleNavClick(link.href)}
className="text-left px-3 py-3 text-sm text-white/60 hover:text-white transition-colors rounded-lg hover:bg-white/5"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
{link.label}
</button>
))}
<a
href="https://github.com/selimaj-dev"
target="_blank"
rel="noopener noreferrer"
className="mt-2 px-3 py-3 text-sm font-medium text-black bg-white rounded-lg text-center hover:bg-white/90 transition-all"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
GitHub
</a>
</div>
</div>
</nav>
);
}
-299
View File
@@ -1,299 +0,0 @@
/*
* PROJECTS SECTION — selimaj.dev
* Design: Glassmorphic Terminal
* Masonry-style grid with glass cards, language badges, and star counts
*/
import { useEffect, useRef } from "react";
import { ExternalLink, Star, GitFork } from "lucide-react";
function useReveal(threshold = 0.1) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
el.classList.add("visible");
observer.disconnect();
}
},
{ threshold },
);
observer.observe(el);
return () => observer.disconnect();
}, [threshold]);
return ref;
}
const LANG_COLORS: Record<string, string> = {
TypeScript: "#3178c6",
Rust: "#ce422b",
Java: "#b07219",
JavaScript: "#f1e05a",
Python: "#3572A5",
Shell: "#89e051",
"C#": "#178600",
};
interface Project {
name: string;
org?: string;
description: string;
language: string;
stars: number;
forks?: number;
href: string;
featured?: boolean;
tag?: string;
}
const projects: Project[] = [
{
name: "osui",
org: "osui-rs",
description:
"A TUI library written in Rust — build terminal UIs with a component-based approach.",
language: "Rust",
stars: 7,
href: "https://github.com/osui-rs/osui",
featured: true,
tag: "Library",
},
{
name: "saturnclient",
org: "saturnclientmc",
description:
"Saturn Client — a Minecraft client full of customizability and free cosmetics.",
language: "Java",
stars: 7,
forks: 2,
href: "https://github.com/saturnclientmc/saturnclient",
featured: true,
tag: "Game Client",
},
{
name: "dev-run",
org: "orus-dev",
description:
"DevRun — a community-driven competitive programming platform inspired by speedrunning.",
language: "TypeScript",
stars: 3,
href: "https://github.com/orus-dev/dev-run",
featured: true,
tag: "Platform",
},
{
name: "nexa-trade",
org: "nexa-sol",
description:
"The Solana trading terminal supercharged by AI — trade smarter.",
language: "TypeScript",
stars: 1,
href: "https://github.com/nexa-sol/nexa-trade",
featured: true,
tag: "FinTech",
},
{
name: "dockship",
org: "orus-dev",
description:
"A powerful self-hosted deployment platform for managing your applications.",
language: "TypeScript",
stars: 1,
href: "https://github.com/orus-dev/dockship",
tag: "DevOps",
},
{
name: "axiom",
org: "AxiomChat",
description: "A modern real-time chat application built with TypeScript.",
language: "TypeScript",
stars: 1,
href: "https://github.com/AxiomChat/axiom",
tag: "App",
},
{
name: "linear-linux",
description:
"Linux support for linear.app — bringing the popular project management tool to Linux.",
language: "JavaScript",
stars: 27,
forks: 5,
href: "https://github.com/selimaj-dev/linear-linux",
tag: "Tool",
},
{
name: "osmium",
description: "A TUI component-based game engine built on top of OSUI.",
language: "Rust",
stars: 1,
href: "https://github.com/selimaj-dev/osmium",
tag: "Engine",
},
{
name: "spark",
description:
"AI documentation generator that takes context from your repository's source code.",
language: "Python",
stars: 2,
href: "https://github.com/selimaj-dev/spark",
tag: "AI Tool",
},
{
name: "flux",
description:
"A tool and library for using multiple languages in one codebase seamlessly.",
language: "Rust",
stars: 1,
href: "https://github.com/selimaj-dev/flux",
tag: "Tool",
},
{
name: "session-rs",
description: "A lightweight async WebSocket protocol library for Rust.",
language: "Rust",
stars: 1,
href: "https://github.com/selimaj-dev/session-rs",
tag: "Library",
},
{
name: "tailwind-motlib",
description: "A motion and stagger animation library for Tailwind CSS.",
language: "TypeScript",
stars: 0,
href: "https://github.com/selimaj-dev/tailwind-motlib",
tag: "Library",
},
];
function ProjectCard({ project, delay }: { project: Project; delay: number }) {
const langColor = LANG_COLORS[project.language] || "#888";
return (
<a
href={project.href}
target="_blank"
rel="noopener noreferrer"
className="block glass rounded-2xl p-6 card-hover group no-underline"
style={{ animationDelay: `${delay}ms` }}
>
{/* Header */}
<div className="flex items-start justify-between mb-3">
<div className="flex-1 min-w-0">
{project.tag && (
<span className="terminal-label text-xs inline-block mb-2 px-2 py-0.5 rounded-full border border-white/10 bg-white/5">
{project.tag}
</span>
)}
<h3
className="text-white font-semibold text-base leading-tight truncate"
style={{ fontFamily: "'Syne', sans-serif" }}
>
{project.org ? (
<span className="text-white/40">{project.org}/</span>
) : null}
{project.name}
</h3>
</div>
<ExternalLink
size={14}
className="text-white/20 group-hover:text-white/60 transition-colors ml-3 mt-1 shrink-0"
/>
</div>
{/* Description */}
<p
className="text-white/50 text-sm leading-relaxed mb-5 line-clamp-2"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
{project.description}
</p>
{/* Footer */}
<div className="flex items-center gap-4">
<span className="lang-badge">
<span className="lang-dot" style={{ backgroundColor: langColor }} />
{project.language}
</span>
<span className="lang-badge">
<Star size={11} className="text-white/40" />
{project.stars}
</span>
{project.forks !== undefined && (
<span className="lang-badge">
<GitFork size={11} className="text-white/40" />
{project.forks}
</span>
)}
</div>
</a>
);
}
export default function ProjectsSection() {
const headerRef = useReveal();
const gridRef = useReveal(0.05);
const featured = projects.filter((p) => p.featured);
const rest = projects.filter((p) => !p.featured);
return (
<section
id="projects"
className="py-32 relative"
style={{ background: "#080808" }}
>
<div className="absolute top-0 left-0 right-0 h-px bg-linear-to-r from-transparent via-white/10 to-transparent" />
<div className="max-w-6xl mx-auto px-6">
{/* Header */}
<div ref={headerRef} className="reveal mb-16">
<div className="terminal-label mb-4">&gt; ls projects/</div>
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4">
<h2 className="section-heading text-4xl sm:text-5xl text-white leading-tight">
Things I've <span className="text-white/40">Built</span>
</h2>
<a
href="https://github.com/selimaj-dev?tab=repositories"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-white/40 hover:text-white transition-colors terminal-label"
>
View all 35+ repos →
</a>
</div>
</div>
{/* Featured projects (larger cards) */}
<div ref={gridRef} className="reveal">
<div className="terminal-label mb-4 text-xs">// featured</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
{featured.map((project, i) => (
<ProjectCard
key={project.name}
project={project}
delay={i * 80}
/>
))}
</div>
{/* Rest of projects (smaller grid) */}
<div className="terminal-label mb-4 text-xs mt-8">
// more projects
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
{rest.map((project, i) => (
<ProjectCard
key={project.name}
project={project}
delay={i * 60}
/>
))}
</div>
</div>
</div>
</section>
);
}
-248
View File
@@ -1,248 +0,0 @@
/*
* SKILLS SECTION — selimaj.dev
* Design: Glassmorphic Terminal
* Flowing chip layout with category grouping
*/
import {
CpuIcon,
PaletteIcon,
RocketIcon,
ShieldCheckIcon,
TerminalIcon,
ZapIcon,
} from "lucide-react";
import { useEffect, useRef } from "react";
function useReveal(threshold = 0.1) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
el.classList.add("visible");
observer.disconnect();
}
},
{ threshold },
);
observer.observe(el);
return () => observer.disconnect();
}, [threshold]);
return ref;
}
const CODE_VISUAL =
"https://d2xsxph8kpxj0f.cloudfront.net/310519663439135967/RkRL4wd7YzNoLyaN7suWbR/code-visual-96fdFEYLcFy2ct3UAAmiG6.webp";
interface SkillCategory {
label: string;
terminalLabel: string;
skills: { name: string; level?: "expert" | "proficient" | "learning" }[];
}
const skillCategories: SkillCategory[] = [
{
label: "Languages",
terminalLabel: "languages/",
skills: [
{ name: "Rust", level: "expert" },
{ name: "TypeScript", level: "expert" },
{ name: "JavaScript", level: "expert" },
{ name: "Java", level: "proficient" },
{ name: "Python", level: "proficient" },
{ name: "C#", level: "proficient" },
{ name: "Shell / Bash", level: "proficient" },
{ name: "Go", level: "learning" },
],
},
{
label: "Frontend",
terminalLabel: "frontend/",
skills: [
{ name: "React", level: "expert" },
{ name: "Next.js", level: "expert" },
{ name: "Tailwind CSS", level: "expert" },
{ name: "Framer Motion", level: "proficient" },
{ name: "WebSockets", level: "expert" },
{ name: "Vite", level: "expert" },
],
},
{
label: "Backend & Systems",
terminalLabel: "backend/",
skills: [
{ name: "Node.js", level: "expert" },
{ name: "Express", level: "expert" },
{ name: "Linux", level: "expert" },
{ name: "Docker", level: "proficient" },
{ name: "TUI / CLI", level: "expert" },
{ name: "Low-level Systems", level: "expert" },
{ name: "WebAssembly", level: "proficient" },
],
},
{
label: "Tools & Platforms",
terminalLabel: "tools/",
skills: [
{ name: "Git / GitHub", level: "expert" },
{ name: "Kali Linux", level: "proficient" },
{ name: "Neovim", level: "proficient" },
{ name: "PostgreSQL", level: "proficient" },
{ name: "Redis", level: "proficient" },
{ name: "Figma", level: "proficient" },
{ name: "Solana / Web3", level: "proficient" },
],
},
];
const levelColors: Record<string, string> = {
expert: "rgba(255,255,255,0.12)",
proficient: "rgba(255,255,255,0.06)",
learning: "rgba(255,255,255,0.03)",
};
const levelBorders: Record<string, string> = {
expert: "rgba(255,255,255,0.2)",
proficient: "rgba(255,255,255,0.1)",
learning: "rgba(255,255,255,0.06)",
};
export default function SkillsSection() {
const headerRef = useReveal();
const contentRef = useReveal(0.05);
return (
<section
id="skills"
className="py-32 relative"
style={{ background: "#080808" }}
>
<div className="absolute top-0 left-0 right-0 h-px bg-linear-to-r from-transparent via-white/10 to-transparent" />
<div className="max-w-6xl mx-auto px-6">
{/* Header */}
<div ref={headerRef} className="reveal mb-16">
<div className="terminal-label mb-4">&gt; cat skills.json</div>
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4">
<h2 className="section-heading text-4xl sm:text-5xl text-white leading-tight">
Tech I Work <span className="text-white/40">With</span>
</h2>
<div className="flex items-center gap-4 terminal-label text-xs">
<span className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full inline-block bg-white/50" />
Expert
</span>
<span className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full inline-block bg-white/25" />
Proficient
</span>
<span className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full inline-block bg-white/10" />
Learning
</span>
</div>
</div>
</div>
<div ref={contentRef} className="reveal">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left: skill categories */}
<div className="lg:col-span-2 space-y-8">
{skillCategories.map((cat) => (
<div key={cat.label}>
<div className="flex items-center gap-3 mb-4">
<span className="terminal-label text-xs">
{cat.terminalLabel}
</span>
<div className="flex-1 h-px bg-white/5" />
</div>
<div className="flex flex-wrap gap-2">
{cat.skills.map((skill) => (
<span
key={skill.name}
className="skill-chip"
style={{
background: skill.level
? levelColors[skill.level]
: undefined,
borderColor: skill.level
? levelBorders[skill.level]
: undefined,
}}
>
{skill.name}
</span>
))}
</div>
</div>
))}
</div>
{/* Right: code visual + interests */}
<div className="space-y-4">
{/* Code visual */}
<div className="glass rounded-2xl overflow-hidden">
<img
src={CODE_VISUAL}
alt="Code visualization"
className="w-full h-48 object-cover opacity-70"
/>
<div className="p-4">
<div className="terminal-label text-xs mb-1">
$ git log --oneline
</div>
<div
className="text-white/50 text-xs leading-relaxed"
style={{ fontFamily: "'JetBrains Mono', monospace" }}
>
<div>214 commits this month</div>
<div>2,670 contributions/year</div>
<div>35+ repositories</div>
</div>
</div>
</div>
{/* Interests */}
<div className="glass rounded-2xl p-5">
<div className="terminal-label text-xs mb-4">
&gt; interests/
</div>
<div className="space-y-3">
{[
{
icon: <ShieldCheckIcon size={20} />,
label: "Hacking & Security",
},
{ icon: <CpuIcon size={20} />, label: "Low-level Systems" },
{ icon: <PaletteIcon size={20} />, label: "UI/UX Design" },
{
icon: <RocketIcon size={20} />,
label: "SaaS & Startups",
},
{ icon: <ZapIcon size={20} />, label: "Rust Ecosystem" },
{
icon: <TerminalIcon size={20} />,
label: "Linux & Open Source",
},
].map((item) => (
<div
key={item.label}
className="flex items-center gap-2 text-sm text-white/60"
style={{ fontFamily: "'DM Sans', sans-serif" }}
>
<span className="text-base">{item.icon}</span>
{item.label}
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
-60
View File
@@ -1,60 +0,0 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-transparent shadow-xs hover:bg-accent dark:bg-transparent dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost:
"hover:bg-accent dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "button";
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };
-92
View File
@@ -1,92 +0,0 @@
import * as React from "react";
import { cn } from "@/lib/utils";
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
)}
{...props}
/>
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
)}
{...props}
/>
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-6", className)}
{...props}
/>
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};
-23
View File
@@ -1,23 +0,0 @@
import { useTheme } from "next-themes";
import { Toaster as Sonner, type ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
style={
{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
} as React.CSSProperties
}
{...props}
/>
);
};
export { Toaster };
-59
View File
@@ -1,59 +0,0 @@
import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils";
function TooltipProvider({
delayDuration = 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delayDuration={delayDuration}
{...props}
/>
);
}
function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return (
<TooltipProvider>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipProvider>
);
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
function TooltipContent({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
className
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px]" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
);
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
-17
View File
@@ -1,17 +0,0 @@
export { COOKIE_NAME, ONE_YEAR_MS } from "@shared/const";
// Generate login URL at runtime so redirect URI reflects the current origin.
export const getLoginUrl = () => {
const oauthPortalUrl = import.meta.env.VITE_OAUTH_PORTAL_URL;
const appId = import.meta.env.VITE_APP_ID;
const redirectUri = `${window.location.origin}/api/oauth/callback`;
const state = btoa(redirectUri);
const url = new URL(`${oauthPortalUrl}/app-auth`);
url.searchParams.set("appId", appId);
url.searchParams.set("redirectUri", redirectUri);
url.searchParams.set("state", state);
url.searchParams.set("type", "signIn");
return url.toString();
};
-64
View File
@@ -1,64 +0,0 @@
import React, { createContext, useContext, useEffect, useState } from "react";
type Theme = "light" | "dark";
interface ThemeContextType {
theme: Theme;
toggleTheme?: () => void;
switchable: boolean;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
interface ThemeProviderProps {
children: React.ReactNode;
defaultTheme?: Theme;
switchable?: boolean;
}
export function ThemeProvider({
children,
defaultTheme = "light",
switchable = false,
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(() => {
if (switchable) {
const stored = localStorage.getItem("theme");
return (stored as Theme) || defaultTheme;
}
return defaultTheme;
});
useEffect(() => {
const root = document.documentElement;
if (theme === "dark") {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
if (switchable) {
localStorage.setItem("theme", theme);
}
}, [theme, switchable]);
const toggleTheme = switchable
? () => {
setTheme(prev => (prev === "light" ? "dark" : "light"));
}
: undefined;
return (
<ThemeContext.Provider value={{ theme, toggleTheme, switchable }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within ThemeProvider");
}
return context;
}
-21
View File
@@ -1,21 +0,0 @@
import * as React from "react";
const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined
);
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
};
mql.addEventListener("change", onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener("change", onChange);
}, []);
return !!isMobile;
}
-280
View File
@@ -1,280 +0,0 @@
/*
* DESIGN SYSTEM — selimaj.dev
* Philosophy: Glassmorphic Terminal
* Background: near-black (#080808), Glass cards: rgba(255,255,255,0.04) + blur
* Typography: Syne (headings) + JetBrains Mono (labels/code) + Geist (body)
* Accent: White only — no color distractions
*/
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
}
:root {
--radius: 0.75rem;
--background: oklch(0.06 0 0);
--foreground: oklch(0.95 0 0);
--card: oklch(0.09 0 0);
--card-foreground: oklch(0.95 0 0);
--popover: oklch(0.09 0 0);
--popover-foreground: oklch(0.95 0 0);
--primary: oklch(0.95 0 0);
--primary-foreground: oklch(0.06 0 0);
--secondary: oklch(0.12 0 0);
--secondary-foreground: oklch(0.85 0 0);
--muted: oklch(0.14 0 0);
--muted-foreground: oklch(0.55 0 0);
--accent: oklch(0.14 0 0);
--accent-foreground: oklch(0.95 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 8%);
--input: oklch(1 0 0 / 10%);
--ring: oklch(0.95 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
html {
scroll-behavior: smooth;
}
body {
@apply bg-background text-foreground;
font-family: 'Geist', 'DM Sans', sans-serif;
background-color: #080808;
color: #f0f0f0;
overflow-x: hidden;
}
button:not(:disabled),
[role="button"]:not([aria-disabled="true"]),
[type="button"]:not(:disabled),
[type="submit"]:not(:disabled),
a[href],
select:not(:disabled) {
@apply cursor-pointer;
}
}
@layer components {
.container {
width: 100%;
margin-left: auto;
margin-right: auto;
padding-left: 1rem;
padding-right: 1rem;
}
.flex {
min-height: 0;
min-width: 0;
}
@media (min-width: 640px) {
.container {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
}
@media (min-width: 1024px) {
.container {
padding-left: 2rem;
padding-right: 2rem;
max-width: 1280px;
}
}
/* Glass card utility */
.glass {
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.glass-strong {
background: rgba(255, 255, 255, 0.07);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.12);
}
/* Navbar glass */
.navbar-glass {
background: rgba(8, 8, 8, 0.6);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.navbar-glass-solid {
background: rgba(8, 8, 8, 0.92);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
/* Terminal label style */
.terminal-label {
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.45);
letter-spacing: 0.05em;
}
/* Section heading style */
.section-heading {
font-family: 'Syne', sans-serif;
font-weight: 700;
letter-spacing: -0.02em;
}
/* Subtle glow on hover for cards */
.card-hover {
transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}
.card-hover:hover {
border-color: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 30px rgba(255, 255, 255, 0.04), 0 8px 32px rgba(0,0,0,0.4);
transform: translateY(-2px);
}
/* Animated gradient text */
.gradient-text {
background: linear-gradient(135deg, #ffffff 0%, rgba(255,255,255,0.6) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Noise overlay */
.noise-overlay::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.03'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
}
/* Scroll reveal animation */
.reveal {
opacity: 0;
transform: translateY(24px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
}
/* Skill chip */
.skill-chip {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.875rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 9999px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.75);
transition: background 0.2s, border-color 0.2s, color 0.2s;
}
.skill-chip:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
/* Cursor blink */
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.cursor-blink {
animation: blink 1s step-end infinite;
}
/* Floating animation */
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-8px); }
}
.float-anim {
animation: float 4s ease-in-out infinite;
}
/* Fade in up */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in-up {
opacity: 0;
animation: fadeInUp 0.7s ease forwards;
}
/* Stagger delays */
.delay-100 { animation-delay: 0.1s; }
.delay-200 { animation-delay: 0.2s; }
.delay-300 { animation-delay: 0.3s; }
.delay-400 { animation-delay: 0.4s; }
.delay-500 { animation-delay: 0.5s; }
.delay-600 { animation-delay: 0.6s; }
.delay-700 { animation-delay: 0.7s; }
.delay-800 { animation-delay: 0.8s; }
/* White dot separator */
.dot-sep::after {
content: '·';
margin: 0 0.5rem;
color: rgba(255,255,255,0.3);
}
/* Project language badge */
.lang-badge {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
color: rgba(255,255,255,0.5);
}
.lang-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
}
-6
View File
@@ -1,6 +0,0 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
-5
View File
@@ -1,5 +0,0 @@
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";
createRoot(document.getElementById("root")!).render(<App />);
-30
View File
@@ -1,30 +0,0 @@
/*
* HOME PAGE — selimaj.dev
* Design: Glassmorphic Terminal
* Assembles all sections: Navbar, Hero, About, Projects, Skills, Contact, Footer
*/
import Navbar from "@/components/Navbar";
import HeroSection from "@/components/HeroSection";
import AboutSection from "@/components/AboutSection";
import ProjectsSection from "@/components/ProjectsSection";
import SkillsSection from "@/components/SkillsSection";
import ContactSection from "@/components/ContactSection";
import Footer from "@/components/Footer";
export default function Home() {
return (
<div
className="min-h-screen"
style={{ background: "#080808", color: "#f0f0f0" }}
>
<Navbar />
<HeroSection />
<AboutSection />
<ProjectsSection />
<SkillsSection />
<ContactSection />
<Footer />
</div>
);
}
-44
View File
@@ -1,44 +0,0 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { AlertCircle, Home } from "lucide-react";
import { useLocation } from "wouter";
export default function NotFound() {
const [, setLocation] = useLocation();
const handleGoHome = () => setLocation("/");
return (
<div className="min-h-screen w-full flex items-center justify-center bg-background">
<Card className="w-full max-w-lg mx-4 shadow-lg border-none">
<CardContent className="pt-8 pb-8 text-center">
<div className="flex justify-center mb-6">
<div className="relative">
<div className="absolute inset-0 rounded-full animate-pulse bg-destructive/20" />
<AlertCircle className="relative h-16 w-16 text-destructive" />
</div>
</div>
<h1 className="text-4xl font-bold text-foreground mb-2">404</h1>
<h2 className="text-xl font-semibold text-muted-foreground mb-4">
Page Not Found
</h2>
<p className="text-muted-foreground mb-8 leading-relaxed">
Sorry, the page you are looking for doesn't exist.
<br />
It may have been moved or deleted.
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<Button variant="default" size="lg" onClick={handleGoHome}>
<Home className="w-4 h-4 mr-2" />
Go Home
</Button>
</div>
</CardContent>
</Card>
</div>
);
}
-97
View File
@@ -1,97 +0,0 @@
/** @type {import('tailwindcss').Config} */
const plugin = require('tailwindcss/plugin');
module.exports = {
darkMode: 'class', // supports .dark class
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {
colors: {
background: 'var(--color-background)',
foreground: 'var(--color-foreground)',
card: 'var(--color-card)',
'card-foreground': 'var(--color-card-foreground)',
popover: 'var(--color-popover)',
'popover-foreground': 'var(--color-popover-foreground)',
primary: 'var(--color-primary)',
'primary-foreground': 'var(--color-primary-foreground)',
secondary: 'var(--color-secondary)',
'secondary-foreground': 'var(--color-secondary-foreground)',
muted: 'var(--color-muted)',
'muted-foreground': 'var(--color-muted-foreground)',
accent: 'var(--color-accent)',
'accent-foreground': 'var(--color-accent-foreground)',
destructive: 'var(--color-destructive)',
'destructive-foreground': 'var(--color-destructive-foreground)',
border: 'var(--color-border)',
input: 'var(--color-input)',
ring: 'var(--color-ring)',
},
borderRadius: {
sm: 'var(--radius-sm)',
md: 'var(--radius-md)',
lg: 'var(--radius-lg)',
xl: 'var(--radius-xl)',
},
fontFamily: {
heading: ['Syne', 'sans-serif'],
body: ['Geist', 'DM Sans', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
},
backdropBlur: {
sm: '12px',
md: '20px',
},
keyframes: {
blink: {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0' },
},
float: {
'0%, 100%': { transform: 'translateY(0px)' },
'50%': { transform: 'translateY(-8px)' },
},
fadeInUp: {
'0%': { opacity: '0', transform: 'translateY(30px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
},
animation: {
blink: 'blink 1s step-end infinite',
float: 'float 4s ease-in-out infinite',
fadeInUp: 'fadeInUp 0.7s ease forwards',
},
},
},
plugins: [
require('tw-animate-css'),
plugin(function({ addUtilities }) {
addUtilities({
'.glass': {
background: 'rgba(255, 255, 255, 0.04)',
'backdrop-filter': 'blur(12px)',
'-webkit-backdrop-filter': 'blur(12px)',
border: '1px solid rgba(255, 255, 255, 0.08)',
},
'.glass-strong': {
background: 'rgba(255, 255, 255, 0.07)',
'backdrop-filter': 'blur(20px)',
'-webkit-backdrop-filter': 'blur(20px)',
border: '1px solid rgba(255, 255, 255, 0.12)',
},
'.navbar-glass': {
background: 'rgba(8, 8, 8, 0.6)',
'backdrop-filter': 'blur(20px)',
'-webkit-backdrop-filter': 'blur(20px)',
'border-bottom': '1px solid rgba(255, 255, 255, 0.06)',
},
'.navbar-glass-solid': {
background: 'rgba(8, 8, 8, 0.92)',
'backdrop-filter': 'blur(20px)',
'-webkit-backdrop-filter': 'blur(20px)',
'border-bottom': '1px solid rgba(255, 255, 255, 0.08)',
},
});
}),
],
};
-32
View File
@@ -1,32 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2023",
"useDefineForClassFields": true,
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"types": ["vite/client"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}
-7
View File
@@ -1,7 +0,0 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
-30
View File
@@ -1,30 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["vite.config.ts"]
}
-13
View File
@@ -1,13 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})