-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
120 lines (110 loc) · 4.41 KB
/
Copy pathscript.js
File metadata and controls
120 lines (110 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
(function(){
const root = document.documentElement;
const themeToggle = document.getElementById('themeToggle');
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
const navMenu = document.getElementById('navMenu');
const siteHeader = document.getElementById('siteHeader');
const galleryGrid = document.getElementById('galleryGrid');
const lightbox = document.getElementById('lightbox');
const lightboxImg = document.getElementById('lightboxImg');
const lightboxClose = document.getElementById('lightboxClose');
const contactForm = document.getElementById('contactForm');
// Theme init
const savedTheme = localStorage.getItem('theme');
if(savedTheme === 'dark') document.documentElement.classList.add('dark');
updateThemeIcon();
function updateThemeIcon(){
themeToggle.textContent = document.documentElement.classList.contains('dark') ? '☀️' : '🌙';
}
themeToggle.addEventListener('click',()=>{
document.documentElement.classList.toggle('dark');
const isDark = document.documentElement.classList.contains('dark');
localStorage.setItem('theme', isDark ? 'dark' : 'light');
updateThemeIcon();
});
// Mobile menu
mobileMenuBtn.addEventListener('click',()=>{
navMenu.classList.toggle('open');
});
// Smooth scroll for internal links
document.querySelectorAll('a[href^="#"]').forEach(a=>{
a.addEventListener('click',function(e){
const target = document.querySelector(this.getAttribute('href'));
if(target){
e.preventDefault();
const top = target.getBoundingClientRect().top + window.scrollY - (siteHeader.offsetHeight + 8);
window.scrollTo({top,behavior:'smooth'});
// close mobile nav
navMenu.classList.remove('open');
}
});
});
// Header hide/show on scroll
let lastScroll = window.scrollY;
window.addEventListener('scroll', ()=>{
const current = window.scrollY;
if(current > lastScroll && current > 100){
siteHeader.style.transform = 'translateY(-120%)';
} else {
siteHeader.style.transform = 'translateY(0)';
}
lastScroll = current;
},{passive:true});
// Gallery lightbox
galleryGrid.querySelectorAll('.gallery-item').forEach(item=>{
item.addEventListener('click',e=>{
e.preventDefault();
const href = item.getAttribute('href');
lightboxImg.src = href;
lightbox.style.display = 'flex';
lightbox.setAttribute('aria-hidden','false');
});
});
lightboxClose.addEventListener('click',()=>{
lightbox.style.display='none';
lightbox.setAttribute('aria-hidden','true');
lightboxImg.src = '';
});
lightbox.addEventListener('click',e=>{
if(e.target === lightbox) lightboxClose.click();
});
// Contact form validation
contactForm.addEventListener('submit', function(e){
const name = document.getElementById('name');
const email = document.getElementById('email');
const message = document.getElementById('message');
if(!name.value.trim() || !email.value.trim() || !message.value.trim()){
e.preventDefault();
alert('Please fill in all fields before sending.');
return false;
}
// Allow default submit to chithi.me target
});
// Accessible focus outline for keyboard users
function handleFirstTab(e) {
if (e.key === 'Tab') {
document.body.classList.add('user-is-tabbing');
window.removeEventListener('keydown', handleFirstTab);
}
}
window.addEventListener('keydown', handleFirstTab);
// Reveal-on-scroll using IntersectionObserver (light-weight AOS alternative)
try{
const revealTargets = document.querySelectorAll('.hero-content h1, .subtitle, .lead, .image-frame, .project-card, .skill-card, .timeline-item, .gallery-item, .card.soft, .contact-grid form');
revealTargets.forEach((el, i)=>{
el.classList.add('reveal-on-scroll');
// stagger with small inline delay
el.style.transitionDelay = (i * 45) + 'ms';
});
const io = new IntersectionObserver((entries)=>{
entries.forEach(entry=>{
if(entry.isIntersecting){
entry.target.classList.add('reveal');
// optionally unobserve to keep it simple
io.unobserve(entry.target);
}
});
},{threshold:0.12});
revealTargets.forEach(el=>io.observe(el));
}catch(err){console.warn('Reveal observer failed',err)}
})();