mirror of
https://github.com/thecyberlearn/quantum-website-2026.git
synced 2026-08-18 10:23:22 +00:00
headerfix
This commit is contained in:
parent
953e6f107b
commit
f6fd1a769a
@ -146,103 +146,6 @@ body {
|
|||||||
font-variant-ligatures: common-ligatures contextual;
|
font-variant-ligatures: common-ligatures contextual;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================================
|
|
||||||
HEADER COMPONENT
|
|
||||||
================================================================ */
|
|
||||||
|
|
||||||
.hdr {
|
|
||||||
background: var(--header-bg);
|
|
||||||
border-bottom: 1px solid var(--header-border);
|
|
||||||
box-shadow: var(--header-shadow);
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: var(--z-fixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-inner {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 14px 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-logo img {
|
|
||||||
height: var(--logo-height);
|
|
||||||
width: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-nav {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-nav a {
|
|
||||||
color: var(--nav-text);
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: var(--font-medium);
|
|
||||||
padding: 8px 14px;
|
|
||||||
border-radius: 6px;
|
|
||||||
text-decoration: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: color .15s, background .15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-nav a:hover {
|
|
||||||
color: var(--nav-text-hover);
|
|
||||||
background: rgba(59, 130, 246, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-nav a.active {
|
|
||||||
color: var(--nav-text-active);
|
|
||||||
background: rgba(59, 130, 246, 0.08);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-nav a:focus-visible {
|
|
||||||
outline: 2px solid var(--nav-text-hover);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-burger {
|
|
||||||
display: none;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 6px;
|
|
||||||
color: var(--nav-text);
|
|
||||||
transition: background .15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hdr-burger:hover { background: rgba(59, 130, 246, 0.05); }
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.hdr-burger { display: flex; }
|
|
||||||
.hdr-inner { position: relative; }
|
|
||||||
.hdr-logo img { height: var(--logo-height-mobile); }
|
|
||||||
.hdr-nav {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: var(--header-bg);
|
|
||||||
border-top: 1px solid var(--header-border);
|
|
||||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
padding: 12px 16px;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
.hdr-nav.open { display: flex; }
|
|
||||||
.hdr-nav a { padding: 10px 14px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
HERO SECTIONS
|
HERO SECTIONS
|
||||||
|
|||||||
@ -57,24 +57,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Header -->
|
<!-- Shared header -->
|
||||||
<header class="hdr">
|
<script src="/header.js"></script>
|
||||||
<div class="hdr-inner">
|
|
||||||
<a href="index.html" class="hdr-logo">
|
|
||||||
<img src="img/logo.png" alt="Quantum Tasks AI">
|
|
||||||
</a>
|
|
||||||
<nav class="hdr-nav" id="hdr-nav" aria-label="Main navigation">
|
|
||||||
<a href="index.html">Home</a>
|
|
||||||
<a href="digital-branding.html" class="active">AI Digital Branding</a>
|
|
||||||
<a href="https://blog.quantumtaskai.com/">Blog</a>
|
|
||||||
</nav>
|
|
||||||
<button class="hdr-burger" id="hdr-burger" aria-label="Toggle navigation" aria-expanded="false" aria-controls="hdr-nav">
|
|
||||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" aria-hidden="true">
|
|
||||||
<path d="M3 5h16M3 11h16M3 17h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
253
header.js
253
header.js
@ -10,177 +10,121 @@
|
|||||||
if (document.getElementById('sh-header')) return;
|
if (document.getElementById('sh-header')) return;
|
||||||
|
|
||||||
// ─── Config: update nav links here ──────────────────────────────
|
// ─── Config: update nav links here ──────────────────────────────
|
||||||
|
var LOGO_URL = 'https://quantumtaskai.com/img/logo.png';
|
||||||
|
var HOME_URL = 'https://quantumtaskai.com';
|
||||||
|
|
||||||
var NAV_LINKS = [
|
var NAV_LINKS = [
|
||||||
{ label: 'Home', url: 'https://quantumtaskai.com' },
|
{ label: 'Home', url: 'https://quantumtaskai.com' },
|
||||||
{ label: 'AI Digital Branding', url: 'https://quantumtaskai.com/digital-branding.html' },
|
{ label: 'AI Digital Branding', url: 'https://quantumtaskai.com/digital-branding.html' },
|
||||||
{ label: 'Blog', url: 'https://blog.quantumtaskai.com/' },
|
{ label: 'Blog', url: 'https://blog.quantumtaskai.com/' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ─── Styles ──────────────────────────────────────────────────────
|
// ─── Styles (matches static site css/style.css header) ──────────
|
||||||
var css = `
|
var css = `
|
||||||
:root {
|
|
||||||
--sh-bg: #0a0e1a;
|
|
||||||
--sh-accent: #3b82f6;
|
|
||||||
--sh-text: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sh-header {
|
#sh-header {
|
||||||
|
background: #ffffff;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
z-index: 1030;
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
background: var(--sh-bg);
|
|
||||||
z-index: 99999;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: 'Inter', Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-inner {
|
.sh-inner {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: 100%;
|
padding: 14px 24px;
|
||||||
padding: 0 24px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-logo {
|
.sh-logo img {
|
||||||
font-size: 1.2rem;
|
height: 60px;
|
||||||
font-weight: 700;
|
width: auto;
|
||||||
color: var(--sh-accent);
|
display: block;
|
||||||
text-decoration: none;
|
|
||||||
letter-spacing: 0.02em;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-logo:hover {
|
|
||||||
color: var(--sh-accent);
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-nav {
|
.sh-nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 4px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-nav a {
|
.sh-nav a {
|
||||||
color: var(--sh-text);
|
color: #6b7280;
|
||||||
text-decoration: none;
|
font-size: 15px;
|
||||||
font-size: 0.92rem;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding: 6px 12px;
|
padding: 8px 14px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
transition: background 0.18s, color 0.18s;
|
text-decoration: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
transition: color .15s, background .15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-nav a:hover {
|
.sh-nav a:hover {
|
||||||
background: rgba(59, 130, 246, 0.15);
|
color: #3b82f6;
|
||||||
color: var(--sh-accent);
|
background: rgba(59,130,246,0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-nav a.sh-active {
|
.sh-nav a.sh-active {
|
||||||
color: var(--sh-accent);
|
color: #1d4ed8;
|
||||||
background: rgba(59, 130, 246, 0.12);
|
background: rgba(59,130,246,0.08);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sh-nav a:focus-visible {
|
||||||
|
outline: 2px solid #3b82f6;
|
||||||
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Hamburger ── */
|
/* ── Hamburger ── */
|
||||||
.sh-burger {
|
.sh-burger {
|
||||||
display: none;
|
display: none;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 5px;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
cursor: pointer;
|
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 4px;
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
transition: background 0.18s;
|
color: #6b7280;
|
||||||
|
transition: background .15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-burger:hover {
|
.sh-burger:hover { background: rgba(59,130,246,0.05); }
|
||||||
background: rgba(255,255,255,0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-burger span {
|
|
||||||
display: block;
|
|
||||||
width: 22px;
|
|
||||||
height: 2px;
|
|
||||||
background: var(--sh-text);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: transform 0.22s, opacity 0.22s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-burger.sh-open span:nth-child(1) {
|
|
||||||
transform: translateY(7px) rotate(45deg);
|
|
||||||
}
|
|
||||||
.sh-burger.sh-open span:nth-child(2) {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
.sh-burger.sh-open span:nth-child(3) {
|
|
||||||
transform: translateY(-7px) rotate(-45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Mobile menu ── */
|
/* ── Mobile menu ── */
|
||||||
.sh-mobile-menu {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 64px;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
background: var(--sh-bg);
|
|
||||||
padding: 12px 0 20px;
|
|
||||||
border-top: 1px solid rgba(255,255,255,0.08);
|
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-mobile-menu.sh-open {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-mobile-menu a {
|
|
||||||
display: block;
|
|
||||||
color: var(--sh-text);
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.98rem;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 12px 28px;
|
|
||||||
transition: background 0.15s, color 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-mobile-menu a:hover {
|
|
||||||
background: rgba(59, 130, 246, 0.12);
|
|
||||||
color: var(--sh-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-mobile-menu a.sh-active {
|
|
||||||
color: var(--sh-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
#sh-header {
|
.sh-burger { display: flex; }
|
||||||
height: 56px;
|
|
||||||
}
|
.sh-inner { position: relative; }
|
||||||
|
|
||||||
|
.sh-logo img { height: 40px; }
|
||||||
|
|
||||||
.sh-nav {
|
.sh-nav {
|
||||||
display: none;
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 12px 16px;
|
||||||
|
gap: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sh-burger {
|
.sh-nav.sh-open { display: flex; }
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sh-mobile-menu {
|
.sh-nav a { padding: 10px 14px; }
|
||||||
top: 56px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -193,72 +137,69 @@
|
|||||||
// ─── Detect active link ──────────────────────────────────────────
|
// ─── Detect active link ──────────────────────────────────────────
|
||||||
var currentUrl = window.location.href;
|
var currentUrl = window.location.href;
|
||||||
|
|
||||||
function buildNavItems(isMobile) {
|
function isActive(url) {
|
||||||
return NAV_LINKS.map(function (link) {
|
if (url === HOME_URL) {
|
||||||
var isActive = currentUrl === link.url ||
|
return currentUrl === url || currentUrl === url + '/' || currentUrl === url + '/index.html';
|
||||||
(link.url !== 'https://quantumtaskai.com' && currentUrl.indexOf(link.url) === 0);
|
|
||||||
var activeClass = isActive ? ' sh-active' : '';
|
|
||||||
if (isMobile) {
|
|
||||||
return '<a href="' + link.url + '" class="' + activeClass.trim() + '">' + link.label + '</a>';
|
|
||||||
}
|
}
|
||||||
return '<li><a href="' + link.url + '" class="' + activeClass.trim() + '">' + link.label + '</a></li>';
|
return currentUrl.indexOf(url) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildNavItems() {
|
||||||
|
return NAV_LINKS.map(function (link) {
|
||||||
|
var cls = isActive(link.url) ? 'sh-active' : '';
|
||||||
|
return '<li><a href="' + link.url + '"' + (cls ? ' class="' + cls + '"' : '') + '>' + link.label + '</a></li>';
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Build HTML ──────────────────────────────────────────────────
|
// ─── Build HTML ──────────────────────────────────────────────────
|
||||||
var headerHTML = `
|
var headerHTML =
|
||||||
<header id="sh-header">
|
'<header id="sh-header">' +
|
||||||
<div class="sh-inner">
|
'<div class="sh-inner">' +
|
||||||
<a href="https://quantumtaskai.com" class="sh-logo">Quantum Tasks AI</a>
|
'<a href="' + HOME_URL + '" class="sh-logo">' +
|
||||||
<nav aria-label="Main navigation">
|
'<img src="' + LOGO_URL + '" alt="Quantum Tasks AI" height="60">' +
|
||||||
<ul class="sh-nav">` + buildNavItems(false) + `</ul>
|
'</a>' +
|
||||||
</nav>
|
'<nav aria-label="Main navigation">' +
|
||||||
<button class="sh-burger" id="sh-burger" aria-label="Toggle menu" aria-expanded="false" aria-controls="sh-mobile-menu">
|
'<ul class="sh-nav" id="sh-nav">' + buildNavItems() + '</ul>' +
|
||||||
<span></span>
|
'</nav>' +
|
||||||
<span></span>
|
'<button class="sh-burger" id="sh-burger" aria-label="Toggle navigation" aria-expanded="false">' +
|
||||||
<span></span>
|
'<svg width="22" height="22" viewBox="0 0 22 22" fill="none" aria-hidden="true">' +
|
||||||
</button>
|
'<path d="M3 5h16M3 11h16M3 17h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>' +
|
||||||
</div>
|
'</svg>' +
|
||||||
<nav class="sh-mobile-menu" id="sh-mobile-menu" aria-label="Mobile navigation">
|
'</button>' +
|
||||||
` + buildNavItems(true) + `
|
'</div>' +
|
||||||
</nav>
|
'</header>';
|
||||||
</header>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// ─── Inject header at top of body ───────────────────────────────
|
// ─── Inject header at top of body ───────────────────────────────
|
||||||
document.body.insertAdjacentHTML('afterbegin', headerHTML);
|
document.body.insertAdjacentHTML('afterbegin', headerHTML);
|
||||||
|
|
||||||
// ─── Hamburger logic ─────────────────────────────────────────────
|
// ─── Hamburger logic ─────────────────────────────────────────────
|
||||||
var burger = document.getElementById('sh-burger');
|
var burger = document.getElementById('sh-burger');
|
||||||
var mobileMenu = document.getElementById('sh-mobile-menu');
|
var nav = document.getElementById('sh-nav');
|
||||||
|
|
||||||
function openMenu() {
|
burger.addEventListener('click', function () {
|
||||||
burger.classList.add('sh-open');
|
var open = nav.classList.toggle('sh-open');
|
||||||
mobileMenu.classList.add('sh-open');
|
burger.setAttribute('aria-expanded', open);
|
||||||
burger.setAttribute('aria-expanded', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeMenu() {
|
|
||||||
burger.classList.remove('sh-open');
|
|
||||||
mobileMenu.classList.remove('sh-open');
|
|
||||||
burger.setAttribute('aria-expanded', 'false');
|
|
||||||
}
|
|
||||||
|
|
||||||
burger.addEventListener('click', function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
mobileMenu.classList.contains('sh-open') ? closeMenu() : openMenu();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close on outside click
|
|
||||||
document.addEventListener('click', function (e) {
|
document.addEventListener('click', function (e) {
|
||||||
if (!document.getElementById('sh-header').contains(e.target)) {
|
if (!document.getElementById('sh-header').contains(e.target)) {
|
||||||
closeMenu();
|
nav.classList.remove('sh-open');
|
||||||
|
burger.setAttribute('aria-expanded', 'false');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close on ESC
|
|
||||||
document.addEventListener('keydown', function (e) {
|
document.addEventListener('keydown', function (e) {
|
||||||
if (e.key === 'Escape') closeMenu();
|
if (e.key === 'Escape') {
|
||||||
|
nav.classList.remove('sh-open');
|
||||||
|
burger.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nav.querySelectorAll('a').forEach(function (a) {
|
||||||
|
a.addEventListener('click', function () {
|
||||||
|
nav.classList.remove('sh-open');
|
||||||
|
burger.setAttribute('aria-expanded', 'false');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Shared header loaded ✓');
|
console.log('Shared header loaded ✓');
|
||||||
|
|||||||
20
index.html
20
index.html
@ -63,24 +63,8 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Header -->
|
<!-- Shared header — update nav links at /header.js -->
|
||||||
<header class="hdr">
|
<script src="/header.js"></script>
|
||||||
<div class="hdr-inner">
|
|
||||||
<a href="index.html" class="hdr-logo">
|
|
||||||
<img src="img/logo.png" alt="Quantum Tasks AI">
|
|
||||||
</a>
|
|
||||||
<nav class="hdr-nav" id="hdr-nav" aria-label="Main navigation">
|
|
||||||
<a href="index.html" class="active">Home</a>
|
|
||||||
<a href="digital-branding.html">AI Digital Branding</a>
|
|
||||||
<a href="https://blog.quantumtaskai.com/">Blog</a>
|
|
||||||
</nav>
|
|
||||||
<button class="hdr-burger" id="hdr-burger" aria-label="Toggle navigation" aria-expanded="false" aria-controls="hdr-nav">
|
|
||||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" aria-hidden="true">
|
|
||||||
<path d="M3 5h16M3 11h16M3 17h16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
371
js/script.js
371
js/script.js
@ -2,32 +2,6 @@
|
|||||||
Quantum Tasks AI - Static Website JavaScript
|
Quantum Tasks AI - Static Website JavaScript
|
||||||
================================================================ */
|
================================================================ */
|
||||||
|
|
||||||
// Mobile navigation
|
|
||||||
(function() {
|
|
||||||
var btn = document.getElementById('hdr-burger');
|
|
||||||
var nav = document.getElementById('hdr-nav');
|
|
||||||
if (!btn || !nav) return;
|
|
||||||
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
var open = nav.classList.toggle('open');
|
|
||||||
btn.setAttribute('aria-expanded', open);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('click', function(e) {
|
|
||||||
if (!nav.contains(e.target) && !btn.contains(e.target)) {
|
|
||||||
nav.classList.remove('open');
|
|
||||||
btn.setAttribute('aria-expanded', 'false');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
nav.querySelectorAll('a').forEach(function(a) {
|
|
||||||
a.addEventListener('click', function() {
|
|
||||||
nav.classList.remove('open');
|
|
||||||
btn.setAttribute('aria-expanded', 'false');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Smooth scrolling for anchor links
|
// Smooth scrolling for anchor links
|
||||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
anchor.addEventListener('click', function (e) {
|
anchor.addEventListener('click', function (e) {
|
||||||
@ -560,349 +534,4 @@ const style = document.createElement('style');
|
|||||||
style.textContent = rippleCSS;
|
style.textContent = rippleCSS;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|
||||||
// ================================================================
|
|
||||||
// BLOG FUNCTIONALITY
|
|
||||||
// ================================================================
|
|
||||||
|
|
||||||
let blogData = null;
|
|
||||||
let filteredPosts = [];
|
|
||||||
let currentCategory = 'all';
|
|
||||||
let searchQuery = '';
|
|
||||||
|
|
||||||
// Load blog data
|
|
||||||
async function loadBlogData() {
|
|
||||||
try {
|
|
||||||
// Check if we're on a blog post page (in /blog/ directory)
|
|
||||||
const isInBlogDirectory = window.location.pathname.includes('/blog/');
|
|
||||||
const dataPath = isInBlogDirectory ? '../blog-data.json' : 'blog-data.json';
|
|
||||||
|
|
||||||
const response = await fetch(dataPath);
|
|
||||||
blogData = await response.json();
|
|
||||||
return blogData;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load blog data:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load and display blog posts
|
|
||||||
async function loadBlogPosts() {
|
|
||||||
const loadingElement = document.getElementById('blogLoading');
|
|
||||||
const gridElement = document.getElementById('blogGrid');
|
|
||||||
const emptyElement = document.getElementById('blogEmpty');
|
|
||||||
|
|
||||||
if (!loadingElement || !gridElement) return;
|
|
||||||
|
|
||||||
// Show skeleton loading state
|
|
||||||
loadingElement.innerHTML = `
|
|
||||||
<div class="skeleton">
|
|
||||||
<div class="skeleton-item">
|
|
||||||
<div class="skeleton-line sm"></div>
|
|
||||||
<div class="skeleton-line lg"></div>
|
|
||||||
<div class="skeleton-line md"></div>
|
|
||||||
</div>
|
|
||||||
<div class="skeleton-item">
|
|
||||||
<div class="skeleton-line sm"></div>
|
|
||||||
<div class="skeleton-line lg"></div>
|
|
||||||
<div class="skeleton-line md"></div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
loadingElement.style.display = 'block';
|
|
||||||
gridElement.style.display = 'none';
|
|
||||||
if (emptyElement) emptyElement.style.display = 'none';
|
|
||||||
|
|
||||||
// Load data if not already loaded
|
|
||||||
if (!blogData) {
|
|
||||||
blogData = await loadBlogData();
|
|
||||||
if (!blogData) {
|
|
||||||
loadingElement.innerHTML = '<p>Failed to load blog posts.</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter and display posts
|
|
||||||
filterAndDisplayPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter posts based on category and search
|
|
||||||
function filterAndDisplayPosts() {
|
|
||||||
if (!blogData) return;
|
|
||||||
|
|
||||||
let posts = blogData.posts;
|
|
||||||
|
|
||||||
// Filter by category
|
|
||||||
if (currentCategory !== 'all') {
|
|
||||||
posts = posts.filter(post => post.category === currentCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter by search query
|
|
||||||
if (searchQuery) {
|
|
||||||
const query = searchQuery.toLowerCase();
|
|
||||||
posts = posts.filter(post =>
|
|
||||||
post.title.toLowerCase().includes(query) ||
|
|
||||||
post.excerpt.toLowerCase().includes(query) ||
|
|
||||||
post.tags.some(tag => tag.toLowerCase().includes(query)) ||
|
|
||||||
post.content.toLowerCase().includes(query)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredPosts = posts;
|
|
||||||
displayBlogPosts(posts);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display blog posts in minimal list format
|
|
||||||
function displayBlogPosts(posts) {
|
|
||||||
const loadingElement = document.getElementById('blogLoading');
|
|
||||||
const gridElement = document.getElementById('blogGrid');
|
|
||||||
const emptyElement = document.getElementById('blogEmpty');
|
|
||||||
|
|
||||||
if (!gridElement) return;
|
|
||||||
|
|
||||||
// Hide loading
|
|
||||||
if (loadingElement) loadingElement.style.display = 'none';
|
|
||||||
|
|
||||||
if (posts.length === 0) {
|
|
||||||
gridElement.style.display = 'none';
|
|
||||||
if (emptyElement) emptyElement.style.display = 'block';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show grid and hide empty state
|
|
||||||
gridElement.style.display = 'block';
|
|
||||||
if (emptyElement) emptyElement.style.display = 'none';
|
|
||||||
|
|
||||||
// Generate HTML for posts in minimal format
|
|
||||||
gridElement.innerHTML = posts.map((post, index) => {
|
|
||||||
const categoryInfo = blogData.categories[post.category] || { name: post.category };
|
|
||||||
const isFeatured = post.featured && index < 2;
|
|
||||||
|
|
||||||
return `
|
|
||||||
<article class="article-item ${isFeatured ? 'featured' : ''}" data-category="${post.category}">
|
|
||||||
<div class="article-meta">
|
|
||||||
<span class="article-category">${categoryInfo.name}</span>
|
|
||||||
<span>${formatDate(post.publishDate)}</span>
|
|
||||||
<span>${post.readTime}</span>
|
|
||||||
</div>
|
|
||||||
<h2 class="article-title">
|
|
||||||
<a href="blog/${post.slug}.html" class="article-link" style="color: inherit; text-decoration: none;">
|
|
||||||
${post.title}
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
<p class="article-excerpt">${post.excerpt}</p>
|
|
||||||
<div class="article-footer">
|
|
||||||
<span class="article-author">${post.author}</span>
|
|
||||||
<a href="blog/${post.slug}.html" class="article-link">Read article →</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
`;
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format date for display
|
|
||||||
function formatDate(dateString) {
|
|
||||||
const options = { year: 'numeric', month: 'long', day: 'numeric' };
|
|
||||||
return new Date(dateString).toLocaleDateString('en-US', options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter by category
|
|
||||||
function filterCategory(category) {
|
|
||||||
currentCategory = category;
|
|
||||||
|
|
||||||
// Update active category button
|
|
||||||
document.querySelectorAll('.category-link').forEach(btn => {
|
|
||||||
btn.classList.remove('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
event.target.classList.add('active');
|
|
||||||
|
|
||||||
// Filter and display posts
|
|
||||||
filterAndDisplayPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search functionality
|
|
||||||
function searchBlog() {
|
|
||||||
const searchInput = document.getElementById('blogSearch');
|
|
||||||
if (!searchInput) return;
|
|
||||||
|
|
||||||
searchQuery = searchInput.value.trim();
|
|
||||||
filterAndDisplayPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup search input listener
|
|
||||||
function setupBlogSearch() {
|
|
||||||
const searchInput = document.getElementById('blogSearch');
|
|
||||||
if (!searchInput) return;
|
|
||||||
|
|
||||||
// Live search with debounce (no separate button needed)
|
|
||||||
let searchTimeout;
|
|
||||||
searchInput.addEventListener('input', function() {
|
|
||||||
clearTimeout(searchTimeout);
|
|
||||||
searchTimeout = setTimeout(() => {
|
|
||||||
searchQuery = this.value.trim();
|
|
||||||
filterAndDisplayPosts();
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Newsletter form handler
|
|
||||||
function setupNewsletterForm() {
|
|
||||||
const forms = document.querySelectorAll('#newsletterForm');
|
|
||||||
|
|
||||||
forms.forEach(form => {
|
|
||||||
form.addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const emailInput = this.querySelector('input[type="email"]');
|
|
||||||
const submitBtn = this.querySelector('button[type="submit"]');
|
|
||||||
|
|
||||||
if (!emailInput || !submitBtn) return;
|
|
||||||
|
|
||||||
const email = emailInput.value.trim();
|
|
||||||
|
|
||||||
if (!email) {
|
|
||||||
alert('Please enter your email address.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate newsletter signup
|
|
||||||
const originalText = submitBtn.textContent;
|
|
||||||
submitBtn.textContent = 'Subscribing...';
|
|
||||||
submitBtn.disabled = true;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
submitBtn.textContent = '✓ Subscribed!';
|
|
||||||
emailInput.value = '';
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
submitBtn.textContent = originalText;
|
|
||||||
submitBtn.disabled = false;
|
|
||||||
}, 2000);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load related articles for blog post pages
|
|
||||||
async function loadRelatedArticles() {
|
|
||||||
const relatedContainer = document.getElementById('relatedArticles');
|
|
||||||
if (!relatedContainer) return;
|
|
||||||
|
|
||||||
// Load blog data if not already loaded
|
|
||||||
if (!blogData) {
|
|
||||||
blogData = await loadBlogData();
|
|
||||||
if (!blogData) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get random 2 posts (excluding current post if applicable)
|
|
||||||
const currentSlug = window.location.pathname.split('/').pop().replace('.html', '');
|
|
||||||
const availablePosts = blogData.posts.filter(post => post.slug !== currentSlug);
|
|
||||||
const relatedPosts = shuffleArray(availablePosts).slice(0, 2);
|
|
||||||
|
|
||||||
// Display related posts
|
|
||||||
relatedContainer.innerHTML = relatedPosts.map(post => {
|
|
||||||
const categoryInfo = blogData.categories[post.category] || { name: post.category };
|
|
||||||
|
|
||||||
return `
|
|
||||||
<article class="related-card">
|
|
||||||
<div class="related-meta">
|
|
||||||
<span class="related-category">${categoryInfo.name}</span>
|
|
||||||
<span>${formatDate(post.publishDate)}</span>
|
|
||||||
<span>${post.readTime}</span>
|
|
||||||
</div>
|
|
||||||
<h4 class="related-title">
|
|
||||||
<a href="${post.slug}.html" class="related-link">${post.title}</a>
|
|
||||||
</h4>
|
|
||||||
<p class="related-excerpt">${post.excerpt}</p>
|
|
||||||
<div class="related-footer">
|
|
||||||
<span class="related-author">${post.author}</span>
|
|
||||||
<a href="${post.slug}.html" class="related-read">Read article</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
`;
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to shuffle array
|
|
||||||
function shuffleArray(array) {
|
|
||||||
const shuffled = [...array];
|
|
||||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
||||||
const j = Math.floor(Math.random() * (i + 1));
|
|
||||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
||||||
}
|
|
||||||
return shuffled;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading progress for blog posts
|
|
||||||
function setupReadingProgress() {
|
|
||||||
const bar = document.getElementById('readingProgress') || document.getElementById('readingProgressBar');
|
|
||||||
if (!bar) return;
|
|
||||||
|
|
||||||
const update = () => {
|
|
||||||
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
||||||
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
||||||
const progress = Math.max(0, Math.min(1, docHeight ? scrollTop / docHeight : 0));
|
|
||||||
bar.style.width = (progress * 100) + '%';
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('scroll', update, { passive: true });
|
|
||||||
window.addEventListener('resize', update);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup social sharing
|
|
||||||
function setupSocialSharing() {
|
|
||||||
document.querySelectorAll('.share-btn').forEach(btn => {
|
|
||||||
btn.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const platform = this.getAttribute('data-platform');
|
|
||||||
const url = encodeURIComponent(window.location.href);
|
|
||||||
const title = encodeURIComponent(document.title);
|
|
||||||
|
|
||||||
let shareUrl = '';
|
|
||||||
|
|
||||||
switch (platform) {
|
|
||||||
case 'twitter':
|
|
||||||
shareUrl = `https://twitter.com/intent/tweet?url=${url}&text=${title}`;
|
|
||||||
break;
|
|
||||||
case 'linkedin':
|
|
||||||
shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${url}`;
|
|
||||||
break;
|
|
||||||
case 'facebook':
|
|
||||||
shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${url}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shareUrl) {
|
|
||||||
window.open(shareUrl, 'share', 'width=600,height=400');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize blog functionality
|
|
||||||
function initBlogFunctionality() {
|
|
||||||
// Setup search
|
|
||||||
setupBlogSearch();
|
|
||||||
|
|
||||||
// Setup newsletter forms
|
|
||||||
setupNewsletterForm();
|
|
||||||
|
|
||||||
// Setup social sharing
|
|
||||||
setupSocialSharing();
|
|
||||||
|
|
||||||
// Load related articles if on blog post page
|
|
||||||
if (document.getElementById('relatedArticles')) {
|
|
||||||
loadRelatedArticles();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup reading progress on article pages if present
|
|
||||||
if (document.getElementById('readingProgress')) {
|
|
||||||
setupReadingProgress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize blog when DOM is loaded
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
initBlogFunctionality();
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue
Block a user