From b5c2cc831da7e121e211ee1ccf6d760600a1f8e7 Mon Sep 17 00:00:00 2001 From: Jayavel2005 Date: Fri, 22 May 2026 22:55:39 +0530 Subject: [PATCH 1/2] feat(navbar): add responsive animated sidebar navigation --- revati-kadam-portfolio/css/style.css | 388 ++++++++++++++++++++---- revati-kadam-portfolio/index.html | 57 +++- revati-kadam-portfolio/js/navigation.js | 272 ++++++++++++++--- 3 files changed, 598 insertions(+), 119 deletions(-) diff --git a/revati-kadam-portfolio/css/style.css b/revati-kadam-portfolio/css/style.css index 2b76e2b..a661a02 100644 --- a/revati-kadam-portfolio/css/style.css +++ b/revati-kadam-portfolio/css/style.css @@ -39,63 +39,313 @@ body { /* ── NAVBAR ── */ .navbar { position: fixed; - top: 0; left: 0; + top: 0; left: 0; right: 0; + margin: 0 auto; width: 100%; - height: 64px; - background: rgba(10, 17, 32, 0.9); - backdrop-filter: blur(14px); - border-bottom: 1px solid var(--border); + height: 80px; + background: transparent; + z-index: 1000; + display: flex; + justify-content: center; + transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); + will-change: transform, background-color, backdrop-filter, box-shadow, height, width, top, border-radius; +} + +.navbar.scrolled { + top: 20px; + width: calc(100% - 40px); + max-width: 1200px; + height: 70px; + background: rgba(15, 23, 42, 0.65); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: 0 15px 40px -10px rgba(0,0,0,0.5), inset 0 1px 1px rgba(255,255,255,0.15); + border-radius: 40px; + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.nav-bg-gradient { + position: absolute; + inset: 0; + background: linear-gradient(90deg, rgba(59, 130, 246, 0.08) 0%, transparent 50%, rgba(59, 130, 246, 0.08) 100%); + opacity: 0; + transition: opacity 0.4s ease; + pointer-events: none; + border-radius: inherit; +} + +.navbar.scrolled .nav-bg-gradient { + opacity: 1; +} + +.nav-container { display: flex; justify-content: space-between; align-items: center; + width: 100%; + max-width: 1440px; + height: 100%; padding: 0 40px; - z-index: 1000; + position: relative; + transition: padding 0.4s ease; +} + +.navbar.scrolled .nav-container { + padding: 0 30px; } .logo { - font-size: 20px; + font-size: 26px; font-weight: 700; + color: var(--text); + letter-spacing: 1px; + z-index: 1001; + cursor: pointer; +} + +.logo-accent { color: var(--primary); } -.hamburger { - font-size: 26px; - cursor: pointer; - color: var(--text); - display: none; - transition: color 0.2s; +.desktop-nav { + display: flex; + align-items: center; } -.hamburger:hover { color: var(--primary); } .nav-links { list-style: none; display: flex; flex-direction: row; - gap: 4px; - padding: 0; - margin: 0; + gap: 8px; + position: relative; +} + +.nav-indicator { + position: absolute; + top: 50%; + transform: translateY(-50%); + height: 40px; + background: rgba(59, 130, 246, 0.15); + border: 1px solid rgba(59, 130, 246, 0.3); + border-radius: 20px; + z-index: 0; + pointer-events: none; + opacity: 0; /* Updated by GSAP */ + box-shadow: 0 0 15px rgba(59, 130, 246, 0.2); + will-change: transform, width, opacity; } .nav-links li { + position: relative; + z-index: 1; +} + +.nav-link { + color: var(--text-muted-1); + text-decoration: none; + font-size: 15px; + font-weight: 600; + letter-spacing: 0.5px; + padding: 10px 20px; + display: block; + transition: color 0.3s ease, text-shadow 0.3s ease; + position: relative; + border-radius: 20px; +} + +.nav-link::after { + display: none; +} + +.nav-link:hover, .nav-link.active { + color: #fff; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.3); +} + +.hamburger { + display: none; + background: transparent; + border: none; cursor: pointer; + padding: 10px; + z-index: 1001; + width: 44px; + height: 44px; + position: relative; +} + +.hamburger .line { + display: block; + width: 24px; + height: 2px; + background-color: var(--text); + margin-bottom: 5px; + border-radius: 2px; + transition: transform 0.3s ease, opacity 0.3s ease, background-color 0.3s ease; + transform-origin: center; + position: absolute; + left: 10px; +} + +.hamburger .line-1 { top: 14px; } +.hamburger .line-2 { top: 21px; } +.hamburger .line-3 { top: 28px; } + +.hamburger.is-active .line-1 { transform: translateY(7px) rotate(45deg); } +.hamburger.is-active .line-2 { opacity: 0; } +.hamburger.is-active .line-3 { transform: translateY(-7px) rotate(-45deg); } + +.nav-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + z-index: 9998; + opacity: 0; + visibility: hidden; + pointer-events: none; +} + +.sidebar-drawer { + position: fixed; + top: 0; right: 0; + width: 80vw; + max-width: 340px; + height: 100vh; + background: rgba(10, 15, 30, 0.95); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + z-index: 9999; + transform: translateX(100%); + display: flex; + flex-direction: column; + padding: 40px 30px; + border-left: 1px solid rgba(255, 255, 255, 0.05); + will-change: transform; +} + +.sidebar-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 60px; +} + +.sidebar-logo { + font-size: 22px; + font-weight: 700; color: var(--text); - font-size: 15px; - padding: 7px 16px; - border-radius: 8px; - transition: background 0.2s, color 0.2s; - white-space: nowrap; } -.nav-links li a { + +.close-sidebar { + background: transparent; + border: none; color: var(--text); + font-size: 36px; + cursor: pointer; + line-height: 1; + transition: color 0.3s ease; +} + +.close-sidebar:hover { + color: var(--primary); +} + +.sidebar-links { + list-style: none; + display: flex; + flex-direction: column; + gap: 2rem; + margin: 0; + padding: 0; + flex: 1; +} + +.sidebar-link { + color: var(--text-muted-1); text-decoration: none; - display: block; - width: 100%; - height: 100%; + font-size: clamp(1.3rem, 3vw, 1.8rem); + font-weight: 500; + min-height: 56px; + display: flex; + align-items: center; + position: relative; + padding: 0 20px; + margin-bottom: 8px; + border-radius: 16px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; + will-change: transform, background, color; } -.nav-links li:hover, -.nav-links li:hover a { + +.sidebar-link::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(90deg, rgba(59, 130, 246, 0.15), transparent); + opacity: 0; + transition: opacity 0.3s ease; + border-radius: 16px; + z-index: -1; +} + +.sidebar-link::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + height: 0; + width: 4px; background: var(--primary); + border-radius: 0 4px 4px 0; + opacity: 0; + transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease; + box-shadow: 0 0 10px rgba(59, 130, 246, 0); +} + +.sidebar-link:hover, .sidebar-link.active { color: #fff; + transform: translateX(8px); + text-shadow: 0 0 15px rgba(255, 255, 255, 0.3); +} + +.sidebar-link:hover::after, .sidebar-link.active::after { + opacity: 1; +} + +.sidebar-link:hover::before, .sidebar-link.active::before { + height: 60%; + opacity: 1; + box-shadow: 0 0 10px rgba(59, 130, 246, 0.6); +} + +.sidebar-social { + display: flex; + gap: 20px; + margin-top: auto; +} + +.sidebar-social a { + color: var(--text-muted-2); + text-decoration: none; + font-weight: 600; + font-size: 15px; + transition: color 0.3s ease; +} + +.sidebar-social a:hover { + color: var(--primary); +} + +.nav-link:focus-visible, +.sidebar-link:focus-visible, +.hamburger:focus-visible, +.close-sidebar:focus-visible { + outline: 2px solid var(--primary); + outline-offset: 4px; + border-radius: 4px; } /* ── SECTION BASE ── */ @@ -1100,26 +1350,24 @@ form button:disabled { .section { opacity: 1; transform: none; transition: none; } } +/* ── RESPONSIVE STYLES ── */ +@media (max-width: 1024px) { + .nav-container { padding: 0 40px; } + .nav-links { gap: 10px; } + .nav-link { padding: 8px 16px; font-size: 15px; } + .nav-indicator { height: 38px; border-radius: 20px; } + .logo { font-size: 24px; } +} + /* ── MOBILE ── */ -@media (max-width: 768px) { - .navbar { padding: 0 20px; } +@media (max-width: 767px) { + .nav-container { padding: 0 24px; } .hamburger { display: block; } - .nav-links { - position: absolute; - top: 64px; right: 20px; - background: var(--card); - backdrop-filter: blur(12px); - border: 1px solid var(--border); - border-radius: 12px; - flex-direction: column; - padding: 16px; - width: 180px; - box-shadow: 0 10px 30px var(--shadow); - display: none; - } + .desktop-nav { display: none; } - .nav-links.show { display: flex; } + /* Adjust Logo for Mobile */ + .logo { font-size: 22px; } #home { padding: 100px 24px 60px; } .section { padding: 70px 24px; } @@ -1140,9 +1388,16 @@ form button:disabled { .settings { bottom: 16px; right: 16px; } } -@media (min-width: 769px) { +/* ── VERY SMALL MOBILE ── */ +@media (max-width: 380px) { + .nav-container { padding: 0 16px; } + .logo { font-size: 20px; } + .hamburger { transform: scale(0.9); } + .nav-link { font-size: 22px; } +} + +@media (min-width: 768px) { .hamburger { display: none; } - .nav-links { display: flex !important; } } /* ══════════════════════════════════════════ LIGHT THEME OVERRIDES @@ -1162,28 +1417,39 @@ body.light-theme { } /* Navbar stays light-background-aware */ -body.light-theme .navbar { - background: rgba(248,250,252,0.95); - border-bottom: 1px solid rgba(0,0,0,0.1); +body.light-theme .navbar.scrolled { + background: rgba(248, 250, 252, 0.85); + border: 1px solid rgba(0, 0, 0, 0.08); + box-shadow: 0 15px 40px -10px rgba(0,0,0,0.1), inset 0 1px 1px rgba(255,255,255,0.8); } -/* Nav links: dark text on light bg, white on hover/active */ -body.light-theme .nav-links li { - color: #0f172a; +body.light-theme .nav-overlay { + background: rgba(255, 255, 255, 0.3); } -body.light-theme .nav-links li:hover, -body.light-theme .nav-links li.nav-active { - background: var(--primary); - color: #fff; + +body.light-theme .sidebar-drawer { + background: rgba(248, 250, 252, 0.98); + border-left: 1px solid rgba(0, 0, 0, 0.05); } -body.light-theme .hamburger { + +/* Nav links: dark text on light bg, white on hover/active */ +body.light-theme .nav-link, +body.light-theme .sidebar-link { + color: var(--text-muted-1); +} +body.light-theme .nav-link:hover, +body.light-theme .nav-link.active { color: #0f172a; + text-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } -/* Mobile dropdown bg */ -body.light-theme .nav-links { - background: #f1f5f9; - border-color: rgba(0,0,0,0.1); +body.light-theme .sidebar-link:hover, +body.light-theme .sidebar-link.active { + color: #0f172a; + text-shadow: 0 0 15px rgba(0, 0, 0, 0.1); +} +body.light-theme .hamburger .line { + background-color: #0f172a; } /* Section headings (About, Projects, etc.) stay dark, not pure black */ diff --git a/revati-kadam-portfolio/index.html b/revati-kadam-portfolio/index.html index 1bf2a0c..bf615d6 100644 --- a/revati-kadam-portfolio/index.html +++ b/revati-kadam-portfolio/index.html @@ -7,23 +7,60 @@ +
- + @@ -63,39 +69,163 @@
-
-
-
-

HELLO!

-

I am Revati Kadam

-

I am a

-

- A passionate Computer Science student who loves building clean, - responsive web apps and solving real-world problems through code. -

-
- View Projects - Download Resume +
+ + +
+
+
+
+
+ + +
+ +
+ +
+
+ + + WELCOME TO MY PORTFOLIO + +
+ +

+ I'm Revati
Kadam. +

+ +
+ I build digital| +
+ +

+ A passionate Computer Science student who loves building clean, + responsive web apps and solving real-world problems through code. +

+ + + +
+
CONNECT WITH ME
+ +
-
- - GitHub - - - LinkedIn - - - LeetCode - + + +
+ +
+ + +
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ Revati Kadam — Portfolio +
+
+ + +
+ +
+ + +
+
+ +
+
+ STATUS + Available for Opportunities +
+
-
-
-
- Rkimg + + +
+
+
+ +
+
+ 15+ + Projects Completed +
+
+
+
+
+ +
+
+ 2+ + Years Coding +
+
+
+
+
+
-
+
+ 10+ + Technologies +
+
+
+
+
+ +
+
+ Web / DSA + Current Focus +
+
+ + +
+ Scroll Down +
+
+
+
+
@@ -252,5 +382,6 @@

Contact Me

+ \ No newline at end of file diff --git a/revati-kadam-portfolio/js/hero.js b/revati-kadam-portfolio/js/hero.js new file mode 100644 index 0000000..9f60dea --- /dev/null +++ b/revati-kadam-portfolio/js/hero.js @@ -0,0 +1,294 @@ +/* ═══════════════════════════════════════════ + HERO V2 — Immersive Motion Engine (GSAP & Canvas) + Staggered Load, Orbital Physics, Magnetic Hooks, Canvas Repulsion + ═══════════════════════════════════════════ */ + +const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; +const isMobile = window.matchMedia('(max-width: 767px)').matches; + +document.addEventListener('DOMContentLoaded', () => { + setTimeout(initHeroV2, 80); +}); + +function initHeroV2() { + if (prefersReduced) { + showAllInstant(); + return; + } + + // 1. Entrance Staggered Orchestration + const tl = gsap.timeline({ defaults: { ease: 'power3.out' } }); + + // Activate glows and meshes + tl.to('.hv2-radial--a', { opacity: 1, duration: 2.0 }, 0.0) + .to('.hv2-radial--b', { opacity: 1, duration: 2.0 }, 0.2) + .to('.hv2-radial--c', { opacity: 1, duration: 1.5 }, 0.4); + + // Stagger Header Navigation items + tl.fromTo('.navbar .logo', + { y: -20, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 0.2); + + tl.fromTo('.desktop-nav .nav-link', + { y: -20, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.6, stagger: 0.08 }, 0.3); + + tl.fromTo('.nav-actions', + { y: -20, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 0.5); + + // Left Content Text Elements + tl.fromTo('.hv2-eyebrow-wrap', + { y: 24, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 0.4); + + tl.fromTo('.hv2-heading', + { y: 35, opacity: 0 }, + { y: 0, opacity: 1, duration: 1.0 }, 0.6); + + tl.fromTo('.hv2-typing-wrap', + { y: 20, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 0.95); + + tl.fromTo('.hv2-desc', + { y: 20, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 1.1); + + tl.fromTo('#hv2CTA', + { y: 20, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 1.25); + + tl.fromTo('#hv2Social', + { y: 15, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, 1.35); + + // Right Centerpiece Portrait Scene + tl.fromTo('#hv2Portrait', + { scale: 0.88, opacity: 0, rotationY: -15, y: 30 }, + { scale: 1, opacity: 1, rotationY: 0, y: 0, duration: 1.5, ease: 'power4.out' }, + 0.85); + + tl.fromTo('#statusBadge', + { y: 20, opacity: 0, scale: 0.85 }, + { y: 0, opacity: 1, scale: 1, duration: 0.8, ease: 'back.out(1.4)' }, + 1.4); + + // Bottom Stats Panel + tl.fromTo('#heroStatsPanel', + { y: 30, opacity: 0 }, + { y: 0, opacity: 1, duration: 1.0, ease: 'power3.out' }, + 1.4); + + // Scroll Down Indicator + tl.fromTo('#scrollIndicator', + { y: 15, opacity: 0 }, + { y: 0, opacity: 1, duration: 0.8 }, + 1.6); + + // Loop & Ambient Handlers Initialization + tl.add(() => { + initOrbitRotation(); + if (!isMobile) { + initParallax(); + initMagneticInteractivity(); + } + initCanvasMesh(); + }, 1.8); +} + +function showAllInstant() { + gsap.set([ + '.hv2-radial', '.navbar .logo', '.desktop-nav .nav-link', '.nav-actions', + '.hv2-eyebrow-wrap', '.hv2-heading', '.hv2-typing-wrap', '.hv2-desc', + '#hv2CTA', '#hv2Social', '#hv2Portrait', '#statusBadge', + '#heroStatsPanel', '#scrollIndicator' + ], { opacity: 1, y: 0, scale: 1, rotationY: 0, skewX: 0 }); +} + +/* ── GSAP CONTINUOUS ORBIT ROTATION ── */ +function initOrbitRotation() { + gsap.to('#portraitOrbital', { + rotation: 360, + duration: 20, + repeat: -1, + ease: 'none' + }); + gsap.to('#portraitOrbital2', { + rotation: -360, + duration: 30, + repeat: -1, + ease: 'none' + }); +} + +/* ── MULTI-DEPTH MOUSE PARALLAX ── */ +function initParallax() { + const scene = document.querySelector('.hero-v2'); + const chassis = document.getElementById('portraitChassis'); + const glow = document.getElementById('portraitGlow'); + const img = document.getElementById('portraitImg'); + const badge = document.getElementById('statusBadge'); + + if (!scene || !chassis) return; + + let mx = 0, my = 0, raf = null; + + function update() { + gsap.to(chassis, { + rotationY: mx * 12, rotationX: -my * 10, + x: mx * 12, y: my * 12, + duration: 1.2, ease: 'power2.out', overwrite: 'auto' + }); + gsap.to(img, { + x: -mx * 10, y: -my * 8, + duration: 1.4, ease: 'power2.out', overwrite: 'auto' + }); + gsap.to(glow, { + x: mx * 30, y: my * 24, + duration: 1.8, ease: 'power2.out', overwrite: 'auto' + }); + gsap.to(badge, { + x: mx * 20, y: my * 15, + duration: 1.0, ease: 'power2.out', overwrite: 'auto' + }); + raf = null; + } + + scene.addEventListener('mousemove', e => { + const r = scene.getBoundingClientRect(); + mx = ((e.clientX - r.left) / r.width - 0.5) * 2; + my = ((e.clientY - r.top) / r.height - 0.5) * 2; + if (!raf) raf = requestAnimationFrame(update); + }); + + scene.addEventListener('mouseleave', () => { + mx = 0; my = 0; + gsap.to([chassis, img, glow, badge], { + x: 0, y: 0, rotationX: 0, rotationY: 0, + duration: 1.6, ease: 'elastic.out(1, 0.35)', overwrite: 'auto' + }); + }); +} + +/* ── MAGNETIC HOOKS INTERACTIVITY ── */ +function initMagneticInteractivity() { + const magneticElements = document.querySelectorAll('.magnetic-btn, .hv2-circular-frame, .hv2-status-card'); + + magneticElements.forEach(elem => { + elem.addEventListener('mousemove', e => { + const r = elem.getBoundingClientRect(); + const pullStrength = elem.classList.contains('hv2-circular-frame') ? 0.08 : 0.22; + const dx = (e.clientX - r.left - r.width / 2) * pullStrength; + const dy = (e.clientY - r.top - r.height / 2) * pullStrength; + + gsap.to(elem, { + x: dx, + y: dy, + duration: 0.4, + ease: 'power2.out', + overwrite: 'auto' + }); + }); + + elem.addEventListener('mouseleave', () => { + gsap.to(elem, { + x: 0, + y: 0, + duration: 0.9, + ease: 'elastic.out(1, 0.3)', + overwrite: 'auto' + }); + }); + }); +} + +/* ── KINETIC CANVAS PARTICLE MESH (Repulsion Physics) ── */ +function initCanvasMesh() { + const canvas = document.getElementById('heroCanvas'); + if (!canvas) return; + const ctx = canvas.getContext('2d'); + let particles = []; + const spacing = 40; + let mouse = { x: -1000, y: -1000 }; + let animationFrameId; + + function resizeCanvas() { + canvas.width = canvas.parentElement.offsetWidth; + canvas.height = canvas.parentElement.offsetHeight; + generateGrid(); + } + + function generateGrid() { + particles = []; + const cols = Math.floor(canvas.width / spacing) + 2; + const rows = Math.floor(canvas.height / spacing) + 2; + + // We only generate grid points for the bottom half to create a floor grid wave + const startRow = Math.floor(rows * 0.55); + + for (let r = startRow; r < rows; r++) { + for (let c = 0; c < cols; c++) { + particles.push({ + baseX: c * spacing, + baseY: r * spacing, + x: c * spacing, + y: r * spacing, + vx: 0, + vy: 0 + }); + } + } + } + + window.addEventListener('resize', resizeCanvas); + resizeCanvas(); + + const scene = document.querySelector('.hero-v2'); + scene.addEventListener('mousemove', e => { + const r = canvas.getBoundingClientRect(); + mouse.x = e.clientX - r.left; + mouse.y = e.clientY - r.top; + }); + + scene.addEventListener('mouseleave', () => { + mouse.x = -1000; + mouse.y = -1000; + }); + + function animate() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = 'rgba(99, 102, 241, 0.25)'; + + particles.forEach(p => { + const dx = mouse.x - p.x; + const dy = mouse.y - p.y; + const dist = Math.sqrt(dx * dx + dy * dy); + const maxDist = 180; + + if (dist < maxDist) { + const force = (maxDist - dist) / maxDist; + p.vx -= (dx / dist) * force * 1.2; + p.vy -= (dy / dist) * force * 1.2; + } + + // Spring physics back to grid coordinate anchor + p.vx += (p.baseX - p.x) * 0.05; + p.vy += (p.baseY - p.y) * 0.05; + + // Friction damping + p.vx *= 0.83; + p.vy *= 0.83; + + p.x += p.vx; + p.y += p.vy; + + ctx.beginPath(); + ctx.arc(p.x, p.y, 1.5, 0, Math.PI * 2); + ctx.fill(); + }); + + animationFrameId = requestAnimationFrame(animate); + } + + animate(); +} diff --git a/revati-kadam-portfolio/js/navigation.js b/revati-kadam-portfolio/js/navigation.js index a6b64ff..20e16bd 100644 --- a/revati-kadam-portfolio/js/navigation.js +++ b/revati-kadam-portfolio/js/navigation.js @@ -60,10 +60,12 @@ function updateActiveIndicator(activeId) { if (window.innerWidth >= 768 && link.classList.contains("nav-link")) { const linkRect = link.getBoundingClientRect(); const navRect = document.getElementById("desktopNavLinks").getBoundingClientRect(); + const dotWidth = 6; + const centerX = (linkRect.left - navRect.left) + (linkRect.width / 2) - (dotWidth / 2); gsap.to(navIndicator, { - x: linkRect.left - navRect.left, - width: linkRect.width, + x: centerX, + width: dotWidth, opacity: 1, duration: 0.4, ease: "power3.out", @@ -99,10 +101,12 @@ desktopNavLinks.forEach(link => { if (window.innerWidth >= 768) { const linkRect = link.getBoundingClientRect(); const navRect = document.getElementById("desktopNavLinks").getBoundingClientRect(); + const dotWidth = 6; + const centerX = (linkRect.left - navRect.left) + (linkRect.width / 2) - (dotWidth / 2); gsap.to(navIndicator, { - x: linkRect.left - navRect.left, - width: linkRect.width, + x: centerX, + width: dotWidth, opacity: 1, duration: 0.3, ease: "power2.out", @@ -125,9 +129,12 @@ if (desktopNavContainer) { if (activeLink) { const linkRect = activeLink.getBoundingClientRect(); const navRect = desktopNavContainer.getBoundingClientRect(); + const dotWidth = 6; + const centerX = (linkRect.left - navRect.left) + (linkRect.width / 2) - (dotWidth / 2); + gsap.to(navIndicator, { - x: linkRect.left - navRect.left, - width: linkRect.width, + x: centerX, + width: dotWidth, opacity: 1, duration: 0.4, ease: "power3.out", diff --git a/revati-kadam-portfolio/js/script.js b/revati-kadam-portfolio/js/script.js index 660aad5..96a708a 100644 --- a/revati-kadam-portfolio/js/script.js +++ b/revati-kadam-portfolio/js/script.js @@ -2,13 +2,15 @@ function showSection(id) { document.getElementById(id).scrollIntoView({ behavior: "smooth" }); } -const roles = ["Web Developer", "Programmer", "Problem Solver", "Website Designer", "Tech Enthusiast"]; +const roles = ["digital products", "web applications", "modern websites", "interactive UIs", "clean code"]; let i = 0, j = 0, del = false; function type() { + const el = document.getElementById("typing"); + if (!el) return; // Hero V2 uses role drum instead let text = roles[i]; j = del ? j - 1 : j + 1; - document.getElementById("typing").innerText = text.substring(0, j); + el.innerText = text.substring(0, j); if (!del && j === text.length) { del = true; return setTimeout(type, 1500); @@ -21,6 +23,7 @@ function type() { } type(); + (function() { const saved = localStorage.getItem("theme"); if (saved === "light") document.body.classList.add("light-theme");