Spaces:
Running
Running
The animated code text is a bit broken, i mean the indentation is broken, fix it. Also the switch button works but the UI is a bit broken, the bullet wont go the other side while switching theme
Browse files- index.html +30 -33
- script.js +31 -11
index.html
CHANGED
|
@@ -93,8 +93,8 @@
|
|
| 93 |
<button id="theme-toggle" class="group relative p-1 rounded-full border border-white/10 bg-white/5 hover:bg-white/10 transition-all duration-300 hover:border-white/20" aria-label="Toggle theme">
|
| 94 |
<div class="relative w-11 h-6 bg-white/10 rounded-full transition-all duration-300 group-hover:bg-white/20">
|
| 95 |
<div id="theme-toggle-knob" class="absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow-lg transition-all duration-300 transform group-hover:scale-105">
|
| 96 |
-
<i
|
| 97 |
-
<i
|
| 98 |
</div>
|
| 99 |
</div>
|
| 100 |
<div class="absolute inset-0 bg-gradient-to-r from-indigo-500/10 via-purple-500/10 to-emerald-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full"></div>
|
|
@@ -187,36 +187,34 @@
|
|
| 187 |
</div>
|
| 188 |
</div>
|
| 189 |
<div class="mt-6 rounded-xl bg-black/50 text-zinc-300 p-4 overflow-x-auto text-sm leading-relaxed font-mono relative min-h-[140px]">
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
</div>
|
| 198 |
-
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
|
| 199 |
-
<div class="text-2xl font-extrabold text-zinc-100">40+</div>
|
| 200 |
-
<div class="text-xs text-zinc-400">Projects</div>
|
| 201 |
-
</div>
|
| 202 |
-
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
|
| 203 |
-
<div class="text-2xl font-extrabold text-zinc-100">100ms</div>
|
| 204 |
-
<div class="text-xs text-zinc-400">TTI target</div>
|
| 205 |
-
</div>
|
| 206 |
-
</div>
|
| 207 |
</div>
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
<div class="
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
</div>
|
| 221 |
</div>
|
| 222 |
</div>
|
|
@@ -224,8 +222,7 @@
|
|
| 224 |
</div>
|
| 225 |
</div>
|
| 226 |
</section>
|
| 227 |
-
|
| 228 |
-
<!-- About -->
|
| 229 |
<section id="about" class="relative py-16 md:py-24">
|
| 230 |
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
| 231 |
<div class="grid lg:grid-cols-3 gap-8 items-start">
|
|
|
|
| 93 |
<button id="theme-toggle" class="group relative p-1 rounded-full border border-white/10 bg-white/5 hover:bg-white/10 transition-all duration-300 hover:border-white/20" aria-label="Toggle theme">
|
| 94 |
<div class="relative w-11 h-6 bg-white/10 rounded-full transition-all duration-300 group-hover:bg-white/20">
|
| 95 |
<div id="theme-toggle-knob" class="absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow-lg transition-all duration-300 transform group-hover:scale-105">
|
| 96 |
+
<i data-feather="moon" class="absolute inset-0 w-3 h-3 m-1 text-zinc-600 transition-all duration-300 opacity-100 rotate-0"></i>
|
| 97 |
+
<i data-feather="sun" class="absolute inset-0 w-3 h-3 m-1 text-yellow-500 transition-all duration-300 opacity-0 -rotate-90"></i>
|
| 98 |
</div>
|
| 99 |
</div>
|
| 100 |
<div class="absolute inset-0 bg-gradient-to-r from-indigo-500/10 via-purple-500/10 to-emerald-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-full"></div>
|
|
|
|
| 187 |
</div>
|
| 188 |
</div>
|
| 189 |
<div class="mt-6 rounded-xl bg-black/50 text-zinc-300 p-4 overflow-x-auto text-sm leading-relaxed font-mono relative min-h-[140px]">
|
| 190 |
+
<div id="typing-code"></div>
|
| 191 |
+
<div class="typing-cursor"></div>
|
| 192 |
+
</div>
|
| 193 |
+
<div class="mt-6 grid grid-cols-3 gap-3 text-center">
|
| 194 |
+
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
|
| 195 |
+
<div class="text-2xl font-extrabold text-zinc-100">5+ yrs</div>
|
| 196 |
+
<div class="text-xs text-zinc-400">Experience</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
</div>
|
| 198 |
+
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
|
| 199 |
+
<div class="text-2xl font-extrabold text-zinc-100">40+</div>
|
| 200 |
+
<div class="text-xs text-zinc-400">Projects</div>
|
| 201 |
+
</div>
|
| 202 |
+
<div class="rounded-xl bg-white/5 border border-white/10 p-3">
|
| 203 |
+
<div class="text-2xl font-extrabold text-zinc-100">100ms</div>
|
| 204 |
+
<div class="text-xs text-zinc-400">TTI target</div>
|
| 205 |
+
</div>
|
| 206 |
+
</div>
|
| 207 |
+
</div>
|
| 208 |
+
<!-- Floating card -->
|
| 209 |
+
<div class="absolute -right-6 -bottom-6 hidden lg:block">
|
| 210 |
+
<div class="glass-card rounded-2xl border border-white/10 bg-[color:rgb(var(--bg)_/_0.75)] p-4 shadow-soft">
|
| 211 |
+
<div class="flex items-center gap-3">
|
| 212 |
+
<span class="h-9 w-9 rounded-lg bg-[color:rgb(var(--color-secondary-500)_/_0.15)] text-[color:rgb(var(--color-secondary-500))] flex items-center justify-center">
|
| 213 |
+
<i data-feather="zap"></i>
|
| 214 |
+
</span>
|
| 215 |
+
<div>
|
| 216 |
+
<div class="text-sm font-semibold text-zinc-100">Perf minded</div>
|
| 217 |
+
<div class="text-xs text-zinc-400">Core Web Vitals A+</div>
|
| 218 |
</div>
|
| 219 |
</div>
|
| 220 |
</div>
|
|
|
|
| 222 |
</div>
|
| 223 |
</div>
|
| 224 |
</section>
|
| 225 |
+
<!-- About -->
|
|
|
|
| 226 |
<section id="about" class="relative py-16 md:py-24">
|
| 227 |
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
| 228 |
<div class="grid lg:grid-cols-3 gap-8 items-start">
|
script.js
CHANGED
|
@@ -1,30 +1,53 @@
|
|
|
|
|
| 1 |
// Theme handling
|
| 2 |
(function initTheme() {
|
| 3 |
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
| 4 |
const saved = localStorage.getItem('theme');
|
| 5 |
const html = document.documentElement;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
const applyTheme = (mode) => {
|
| 8 |
if (mode === 'light') {
|
| 9 |
html.classList.add('light');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
} else {
|
| 11 |
html.classList.remove('light');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
}
|
| 13 |
-
const icon = document.getElementById('theme-icon');
|
| 14 |
-
if (icon) icon.setAttribute('data-feather', mode === 'light' ? 'sun' : 'moon');
|
| 15 |
};
|
| 16 |
|
| 17 |
const initial = saved || (prefersDark ? 'dark' : 'dark');
|
| 18 |
applyTheme(initial);
|
| 19 |
|
| 20 |
-
|
| 21 |
const isLight = html.classList.toggle('light');
|
| 22 |
localStorage.setItem('theme', isLight ? 'light' : 'dark');
|
| 23 |
-
|
| 24 |
-
if (icon) icon.setAttribute('data-feather', isLight ? 'sun' : 'moon');
|
| 25 |
});
|
| 26 |
})();
|
| 27 |
-
|
| 28 |
// Mobile menu
|
| 29 |
const menuToggle = document.getElementById('menu-toggle');
|
| 30 |
const mobileMenu = document.getElementById('mobile-menu');
|
|
@@ -382,7 +405,6 @@ function initTypingAnimation() {
|
|
| 382 |
let timeoutId;
|
| 383 |
|
| 384 |
const cursor = document.querySelector('.typing-cursor');
|
| 385 |
-
|
| 386 |
function typeCharacter() {
|
| 387 |
if (currentLine >= codeLines.length) {
|
| 388 |
// Finished typing all lines
|
|
@@ -415,7 +437,6 @@ function initTypingAnimation() {
|
|
| 415 |
else if (char.type === 'keyword' || char.type === 'function') delay = 120;
|
| 416 |
else if (char.type === 'string') delay = 150;
|
| 417 |
else if (char.text === ' ') delay = 30;
|
| 418 |
-
else if (char.text === '\n') delay = 500;
|
| 419 |
|
| 420 |
// Add some random variation
|
| 421 |
delay += Math.random() * 50;
|
|
@@ -424,15 +445,14 @@ function initTypingAnimation() {
|
|
| 424 |
|
| 425 |
// If we finished the current line, move to next line
|
| 426 |
if (currentChar >= line.chars.length) {
|
| 427 |
-
// Add line break
|
| 428 |
const lineBreak = document.createElement('br');
|
| 429 |
codeContainer.appendChild(lineBreak);
|
| 430 |
currentLine++;
|
| 431 |
currentChar = 0;
|
| 432 |
}
|
| 433 |
}
|
| 434 |
-
|
| 435 |
-
function eraseCharacters() {
|
| 436 |
const lastChild = codeContainer.lastElementChild;
|
| 437 |
if (!lastChild || (lastChild.tagName === 'BR' && !lastChild.previousElementSibling)) {
|
| 438 |
// If only line breaks left, restart typing
|
|
|
|
| 1 |
+
|
| 2 |
// Theme handling
|
| 3 |
(function initTheme() {
|
| 4 |
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
| 5 |
const saved = localStorage.getItem('theme');
|
| 6 |
const html = document.documentElement;
|
| 7 |
+
const toggle = document.getElementById('theme-toggle');
|
| 8 |
+
const knob = document.getElementById('theme-toggle-knob');
|
| 9 |
+
const sunIcon = toggle?.querySelector('[data-feather="sun"]');
|
| 10 |
+
const moonIcon = toggle?.querySelector('[data-feather="moon"]');
|
| 11 |
|
| 12 |
const applyTheme = (mode) => {
|
| 13 |
if (mode === 'light') {
|
| 14 |
html.classList.add('light');
|
| 15 |
+
// Move knob to the right for light mode
|
| 16 |
+
if (knob) {
|
| 17 |
+
knob.style.left = 'calc(100% - 1.25rem - 0.125rem)'; // Right position (w-5 = 1.25rem, top-0.5 = 0.125rem)
|
| 18 |
+
}
|
| 19 |
+
// Update icons
|
| 20 |
+
if (moonIcon && sunIcon) {
|
| 21 |
+
moonIcon.style.opacity = '0';
|
| 22 |
+
moonIcon.style.transform = 'rotate(90deg)';
|
| 23 |
+
sunIcon.style.opacity = '1';
|
| 24 |
+
sunIcon.style.transform = 'rotate(0deg)';
|
| 25 |
+
}
|
| 26 |
} else {
|
| 27 |
html.classList.remove('light');
|
| 28 |
+
// Move knob to the left for dark mode
|
| 29 |
+
if (knob) {
|
| 30 |
+
knob.style.left = '0.125rem'; // Left position (top-0.5 = 0.125rem)
|
| 31 |
+
}
|
| 32 |
+
// Update icons
|
| 33 |
+
if (moonIcon && sunIcon) {
|
| 34 |
+
moonIcon.style.opacity = '1';
|
| 35 |
+
moonIcon.style.transform = 'rotate(0deg)';
|
| 36 |
+
sunIcon.style.opacity = '0';
|
| 37 |
+
sunIcon.style.transform = 'rotate(-90deg)';
|
| 38 |
+
}
|
| 39 |
}
|
|
|
|
|
|
|
| 40 |
};
|
| 41 |
|
| 42 |
const initial = saved || (prefersDark ? 'dark' : 'dark');
|
| 43 |
applyTheme(initial);
|
| 44 |
|
| 45 |
+
toggle?.addEventListener('click', () => {
|
| 46 |
const isLight = html.classList.toggle('light');
|
| 47 |
localStorage.setItem('theme', isLight ? 'light' : 'dark');
|
| 48 |
+
applyTheme(isLight ? 'light' : 'dark');
|
|
|
|
| 49 |
});
|
| 50 |
})();
|
|
|
|
| 51 |
// Mobile menu
|
| 52 |
const menuToggle = document.getElementById('menu-toggle');
|
| 53 |
const mobileMenu = document.getElementById('mobile-menu');
|
|
|
|
| 405 |
let timeoutId;
|
| 406 |
|
| 407 |
const cursor = document.querySelector('.typing-cursor');
|
|
|
|
| 408 |
function typeCharacter() {
|
| 409 |
if (currentLine >= codeLines.length) {
|
| 410 |
// Finished typing all lines
|
|
|
|
| 437 |
else if (char.type === 'keyword' || char.type === 'function') delay = 120;
|
| 438 |
else if (char.type === 'string') delay = 150;
|
| 439 |
else if (char.text === ' ') delay = 30;
|
|
|
|
| 440 |
|
| 441 |
// Add some random variation
|
| 442 |
delay += Math.random() * 50;
|
|
|
|
| 445 |
|
| 446 |
// If we finished the current line, move to next line
|
| 447 |
if (currentChar >= line.chars.length) {
|
| 448 |
+
// Add line break with proper spacing
|
| 449 |
const lineBreak = document.createElement('br');
|
| 450 |
codeContainer.appendChild(lineBreak);
|
| 451 |
currentLine++;
|
| 452 |
currentChar = 0;
|
| 453 |
}
|
| 454 |
}
|
| 455 |
+
function eraseCharacters() {
|
|
|
|
| 456 |
const lastChild = codeContainer.lastElementChild;
|
| 457 |
if (!lastChild || (lastChild.tagName === 'BR' && !lastChild.previousElementSibling)) {
|
| 458 |
// If only line breaks left, restart typing
|