Skip to content

Commit 516335d

Browse files
committed
Modernize site: Bootstrap 5, dark mode, SEO, animations
- Upgrade Bootstrap 3.4.1 → 5.3.3, remove jQuery dependency - Rewrite js/main.js to vanilla JS with debounced scroll - Add dark mode toggle with localStorage persistence - Add Open Graph + Twitter Card meta tags on all pages - Add JSON-LD Person schema on index.html - Add scroll-reveal animations (IntersectionObserver) - Add navbar shadow-on-scroll effect and hover lift on cards - Slim favicon declarations from 16 to 3 essential ones - Fix intro image to use <picture> with AVIF + PNG fallback - Add loading=lazy to below-fold images - Re-enable Vitae nav link, remove Teaching placeholder - Remove empty Facebook/Twitter social links from footer - Add rel=noopener to external links - School logos wrap on mobile instead of horizontal scroll - Update Font Awesome 5 → 6, modernize Google Fonts API
1 parent 7df19eb commit 516335d

File tree

9 files changed

+949
-930
lines changed

9 files changed

+949
-930
lines changed

css/main.css

Lines changed: 257 additions & 273 deletions
Large diffs are not rendered by default.

footer.html

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,13 @@
88
</p>
99
</div>
1010
<div class="col-sm-6">
11-
12-
<!-- Social professional networking icons -->
13-
<ul class="social-icons pull-right">
14-
<li><a href="https://github.com/jichuantang" target="_blank" rel="noopener noreferrer"><i class="fab fa-github fa-2x"></i></a></li>
15-
<li><a href="https://www.linkedin.com/in/jichuan-tang-1281b1218/" target="_blank" rel="noopener noreferrer"><i class="fab fa-linkedin fa-2x"></i></a></li>
16-
<li><a href="https://www.researchgate.net/profile/Jichuan-Tang/" target="_blank" rel="noopener noreferrer"><i class="fab fa-researchgate fa-2x"></i></a></li>
17-
<li><a href="https://scholar.google.com/citations?user=e0d84u0AAAAJ&hl=en" target="_blank" rel="noopener noreferrer"><i class="ai ai-google-scholar ai-2x"></i></a></li>
18-
<!-- <li><a href="" target="_blank" rel="noopener noreferrer"><i class="ai ai-academia-square ai-2x"></i></a></li> -->
11+
<ul class="social-icons float-end">
12+
<li><a href="https://github.com/jichuantang" target="_blank" rel="noopener"><i class="fab fa-fw fa-github"></i></a></li>
13+
<li><a href="https://www.linkedin.com/in/jichuan-tang-1281b1218/" target="_blank" rel="noopener"><i class="fab fa-linkedin fa-2x"></i></a></li>
14+
<li><a href="https://www.researchgate.net/profile/Jichuan-Tang/" target="_blank" rel="noopener"><i class="fab fa-researchgate fa-2x"></i></a></li>
15+
<li><a href="https://scholar.google.com/citations?user=e0d84u0AAAAJ&hl=en" target="_blank" rel="noopener"><i class="ai ai-google-scholar-square ai-2x"></i></a></li>
1916
</ul>
20-
2117
</div>
2218
</div>
2319
</div>
24-
</footer><!-- jQuery Javascript Library -->
20+
</footer>

index.html

Lines changed: 85 additions & 88 deletions
Large diffs are not rendered by default.

js/main.js

Lines changed: 127 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,140 @@
1-
$(document).ready(function() {
2-
3-
// Scroll to top button ----------------------------------------------------------
4-
// When the user scrolls down 20px from the top of the document, show the button
5-
window.onscroll = function() {
6-
scrollFunction()
7-
};
8-
9-
function scrollFunction() {
10-
if (document.body.scrollTop > 350 || document.documentElement.scrollTop > 350) {
11-
document.getElementById("topper").style.display = "block";
12-
} else {
13-
document.getElementById("topper").style.display = "none";
14-
}
1+
/* ── Apply saved theme immediately to prevent flash ── */
2+
(function () {
3+
var saved = localStorage.getItem('theme');
4+
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
5+
document.documentElement.setAttribute('data-theme', 'dark');
6+
}
7+
})();
8+
9+
document.addEventListener('DOMContentLoaded', function () {
10+
11+
/* ── Scroll-to-top button (debounced via rAF) ── */
12+
var topper = document.getElementById('topper');
13+
if (topper) {
14+
var ticking = false;
15+
window.addEventListener('scroll', function () {
16+
if (!ticking) {
17+
window.requestAnimationFrame(function () {
18+
topper.style.display =
19+
document.documentElement.scrollTop > 350 ? 'block' : 'none';
20+
ticking = false;
21+
});
22+
ticking = true;
23+
}
24+
}, { passive: true });
25+
26+
topper.addEventListener('click', function (e) {
27+
e.preventDefault();
28+
window.scrollTo({ top: 0, behavior: 'smooth' });
29+
});
1530
}
1631

17-
// When the user clicks on the button, scroll to the top of the document
18-
function topFunction() {
19-
document.body.scrollTop = 0; // For Safari
20-
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
32+
/* ── Read-more toggle ── */
33+
var readMore = document.getElementById('read-more');
34+
var badgeMore = document.getElementById('badge-more');
35+
if (readMore && badgeMore) {
36+
readMore.style.display = 'none';
37+
badgeMore.addEventListener('click', function () {
38+
var hidden = readMore.style.display === 'none';
39+
readMore.style.display = hidden ? 'block' : 'none';
40+
badgeMore.textContent = hidden ? 'less' : 'more';
41+
});
2142
}
2243

23-
$("#topper").on("click", function() {
24-
$("html").animate({
25-
scrollTop: 0
26-
}, 400);
44+
/* ── Accordion show-all / hide-all ── */
45+
document.querySelectorAll('.expander').forEach(function (btn) {
46+
btn.addEventListener('click', function () {
47+
var showAll = btn.textContent.trim() === 'show all';
48+
document.querySelectorAll('.panel-collapse').forEach(function (panel) {
49+
var inst = bootstrap.Collapse.getOrCreateInstance(panel, { toggle: false });
50+
showAll ? inst.show() : inst.hide();
51+
});
52+
document.querySelectorAll('.accordion-plus-toggle').forEach(function (a) {
53+
a.setAttribute('aria-expanded', showAll ? 'true' : 'false');
54+
showAll ? a.classList.remove('collapsed') : a.classList.add('collapsed');
55+
});
56+
btn.textContent = showAll ? 'hide all' : 'show all';
57+
});
2758
});
2859

29-
// tooltips function
30-
$(function() {
31-
$('[data-toggle="tooltip"]').tooltip()
32-
})
33-
34-
// Initally hide the read more div
35-
$("#read-more").css("display", "none");
36-
37-
// Show more on click
38-
$("#badge-more").on("click", function() {
39-
40-
// Show/hide the div
41-
$("#read-more").fadeToggle("fast");
42-
43-
// Change the button
44-
if ($("#badge-more").text() == "more") {
45-
$("#badge-more").text("less");
46-
} else {
47-
$("#badge-more").text("more");
48-
}
49-
50-
});
51-
52-
// popover function
53-
$('[data-toggle="popover"]').popover();
54-
55-
// open all accordion panels for possible rinting
56-
$(".expander").on("click", function() {
57-
58-
// Change the button
59-
if ($(".expander").text() == "show all") {
60-
$(".expander").text("hide all");
61-
$(".panel-collapse").addClass("in");
62-
$(".panel-default a").attr("aria-expanded", "true").removeClass("collapsed");
63-
} else {
64-
$(".expander").text("show all");
65-
$(".panel-collapse").removeClass("in");
66-
$(".panel-default a").attr("aria-expanded", "false").addClass("collapsed");
60+
/* ── Fade-in on scroll (IntersectionObserver) ── */
61+
var fadeEls = document.querySelectorAll('.fade-in');
62+
if (fadeEls.length && 'IntersectionObserver' in window) {
63+
var observer = new IntersectionObserver(function (entries) {
64+
entries.forEach(function (entry) {
65+
if (entry.isIntersecting) {
66+
entry.target.classList.add('visible');
67+
observer.unobserve(entry.target);
6768
}
69+
});
70+
}, { threshold: 0.12 });
71+
fadeEls.forEach(function (el) { observer.observe(el); });
72+
}
6873

69-
});
70-
71-
$(".accordion-toggle").on("click", function() {
74+
/* ── Navbar shadow on scroll ── */
75+
var nav = document.getElementById('main-nav');
76+
if (nav) {
77+
window.addEventListener('scroll', function () {
78+
nav.classList.toggle('navbar-scrolled', document.documentElement.scrollTop > 10);
79+
}, { passive: true });
80+
}
7281

73-
$(".panel-collapse").removeClass("in");
74-
$(".panel-default a").attr("aria-expanded", "false").addClass("collapsed");
75-
$(".expander").text("show all");
82+
/* ── News gallery click-to-open ── */
83+
document.querySelectorAll('.news-gallery img').forEach(function (img) {
84+
img.style.cursor = 'pointer';
85+
img.addEventListener('click', function () {
86+
window.open(img.src, '_blank');
87+
});
88+
});
7689

77-
});
90+
});
7891

79-
// Project +/- toggle for descriptions
80-
$(document).on("click", ".project-toggle", function() {
81-
var $item = $(this).closest(".news-item");
82-
var $details = $item.find(".project-details");
83-
$details.slideToggle(200);
84-
$(this).text($(this).text() === "+" ? "\u2212" : "+");
85-
});
92+
/* ── Called by w3IncludeHTML callback after navbar/footer are loaded ── */
93+
function initPage() {
94+
95+
/* Active nav link based on current page */
96+
var page = location.pathname.split('/').pop() || 'index.html';
97+
var map = {
98+
'index.html': 'about',
99+
'news.html': 'news',
100+
'projects.html': 'projects',
101+
'publication.html': 'publication',
102+
'vitae.html': 'vitae'
103+
};
104+
var id = map[page];
105+
if (id) {
106+
var li = document.getElementById(id);
107+
if (li) {
108+
var a = li.querySelector('a');
109+
if (a) {
110+
a.classList.add('active');
111+
a.classList.add('hvr-bubble-bottom');
112+
}
113+
}
114+
}
86115

116+
/* Dark-mode toggle button (lives inside included navbar) */
117+
var toggle = document.getElementById('theme-toggle');
118+
if (toggle) {
119+
var html = document.documentElement;
120+
function updateIcon() {
121+
var icon = toggle.querySelector('i');
122+
if (icon) {
123+
icon.className = html.getAttribute('data-theme') === 'dark'
124+
? 'fas fa-sun' : 'fas fa-moon';
125+
}
126+
}
127+
updateIcon();
128+
toggle.addEventListener('click', function () {
129+
var dark = html.getAttribute('data-theme') !== 'dark';
130+
html.setAttribute('data-theme', dark ? 'dark' : 'light');
131+
localStorage.setItem('theme', dark ? 'dark' : 'light');
132+
updateIcon();
133+
});
134+
}
87135

88-
});
136+
/* Bootstrap 5 tooltips */
137+
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) {
138+
new bootstrap.Tooltip(el);
139+
});
140+
}

navbar.html

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
<nav class="navbar navbar-inverse">
2-
<div class="container">
3-
<!-- Brand and toggle get grouped for better mobile display -->
4-
<div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span
5-
class="icon-bar"></span><span class="icon-bar"></span></button>
6-
<a class="navbar-brand montserrat dark-blue" href="index.html">Jichuan Tang</a>
7-
</div> <!-- Collect the nav links, forms, and other content for toggling -->
8-
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
9-
<ul class="nav navbar-nav navbar-right">
10-
<li id="about"><a class="montserrat" href="index.html">About</a></li>
11-
<li id="news"><a class="montserrat" href="news.html">News</a></li>
12-
<li id="projects"><a class="montserrat" href="projects.html">Projects</a></li>
13-
<li id="publication"><a class="montserrat" href="publication.html">Publication</a></li>
14-
<!-- <li id="teaching"><a class="montserrat" href="teaching.html">Teaching</a></li> -->
15-
<!-- <li id="vitae"><a class="montserrat" href="vitae.html">Vitae</a></li> -->
16-
</ul>
17-
</div><!-- /.navbar-collapse -->
18-
</div><!-- /.container-fluid -->
19-
</nav>
1+
<nav class="navbar navbar-expand-md navbar-dark" id="main-nav">
2+
<div class="container">
3+
<a class="navbar-brand montserrat" href="index.html">Jichuan Tang</a>
4+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
5+
<span class="navbar-toggler-icon"></span>
6+
</button>
7+
<div class="collapse navbar-collapse" id="navbarNav">
8+
<ul class="navbar-nav ms-auto">
9+
<li class="nav-item" id="about"><a class="nav-link montserrat" href="index.html">About</a></li>
10+
<li class="nav-item" id="news"><a class="nav-link montserrat" href="news.html">News</a></li>
11+
<li class="nav-item" id="projects"><a class="nav-link montserrat" href="projects.html">Projects</a></li>
12+
<li class="nav-item" id="publication"><a class="nav-link montserrat" href="publication.html">Publication</a></li>
13+
<li class="nav-item" id="vitae"><a class="nav-link montserrat" href="vitae.html">Vitae</a></li>
14+
</ul>
15+
<button class="btn btn-link nav-link ms-md-2" id="theme-toggle" aria-label="Toggle dark mode" title="Toggle theme">
16+
<i class="fas fa-moon"></i>
17+
</button>
18+
</div>
19+
</div>
20+
</nav>

0 commit comments

Comments
 (0)