// Top10AI — shared header / footer / bottom-nav / sticky compare // Mobile-first: below 860px the top header collapses into a hamburger button // that opens a full-screen drawer, and a persistent 5-item bottom nav pins // the most-tapped destinations to the thumb zone. (function () { const path = location.pathname; const locale = window.__T10_LOCALE || (document.body && document.body.getAttribute('data-locale')) || 'en'; const prefix = locale === 'en' ? '' : '/' + locale; // The language switcher is only shown on pages that actually have locale // twins — i.e. blog posts (on top10ai.com, review/category/compare pages // exist only in English, so a switcher on them would just dump the user // on the translated homepage, which is confusing). // The blog-single template sets and // optionally to drive the per-locale URL. const hasTranslations = !!(document.body && document.body.getAttribute('data-has-translations') === 'true'); const translationSlug = (document.body && document.body.getAttribute('data-translation-slug')) || ''; // Given a target locale, return the URL of the CURRENT page in that locale. // Strips any existing locale prefix, then prepends the new one (or none for EN). function localizedUrl(targetLocale) { let path = location.pathname.replace(/^\/([a-z]{2})(\/|$)/, '/'); if (!path.startsWith('/')) path = '/' + path; if (targetLocale === 'en') return path; // Keep trailing slash behavior consistent return '/' + targetLocale + path; } // Chrome-string i18n table (mirrors preview/templates/_i18n.js). const STR = { 'nav.video':{en:'Video',ar:'فيديو',de:'Video',el:'Βίντεο',es:'Vídeo',fr:'Vidéo',it:'Video',nl:'Video',pt:'Vídeo',ru:'Видео'}, 'nav.image':{en:'Image',ar:'صورة',de:'Bild',el:'Εικόνα',es:'Imagen',fr:'Image',it:'Immagine',nl:'Afbeelding',pt:'Imagem',ru:'Изображение'}, 'nav.logo':{en:'Logo',ar:'شعار',de:'Logo',el:'Λογότυπο',es:'Logo',fr:'Logo',it:'Logo',nl:'Logo',pt:'Logo',ru:'Логотип'}, 'nav.website':{en:'Website',ar:'موقع',de:'Website',el:'Ιστότοπος',es:'Sitio web',fr:'Site web',it:'Sito web',nl:'Website',pt:'Site',ru:'Сайт'}, 'nav.free':{en:'Free Tools',ar:'أدوات مجانية',de:'Gratis-Tools',el:'Δωρεάν εργαλεία',es:'Herramientas gratis',fr:'Outils gratuits',it:'Strumenti gratuiti',nl:'Gratis tools',pt:'Ferramentas grátis',ru:'Бесплатные инструменты'}, 'nav.compare':{en:'Compare',ar:'قارن',de:'Vergleichen',el:'Σύγκριση',es:'Comparar',fr:'Comparer',it:'Confronta',nl:'Vergelijken',pt:'Comparar',ru:'Сравнить'}, 'nav.blog':{en:'Blog',ar:'مدونة',de:'Blog',el:'Ιστολόγιο',es:'Blog',fr:'Blog',it:'Blog',nl:'Blog',pt:'Blog',ru:'Блог'}, 'cta.findTool':{en:'Find my tool →',ar:'ابحث عن أداتي →',de:'Tool finden →',el:'Βρες εργαλείο →',es:'Encontrar mi herramienta →',fr:'Trouver mon outil →',it:'Trova il mio strumento →',nl:'Vind mijn tool →',pt:'Encontrar ferramenta →',ru:'Найти инструмент →'}, 'bnav.home':{en:'Home',ar:'الرئيسية',de:'Start',el:'Αρχική',es:'Inicio',fr:'Accueil',it:'Home',nl:'Home',pt:'Início',ru:'Главная'}, 'bnav.finder':{en:'Finder',ar:'البحث',de:'Finden',el:'Εύρεση',es:'Buscar',fr:'Chercher',it:'Trova',nl:'Zoeken',pt:'Buscar',ru:'Поиск'}, 'bnav.tools':{en:'Tools',ar:'أدوات',de:'Tools',el:'Εργαλεία',es:'Herramientas',fr:'Outils',it:'Strumenti',nl:'Tools',pt:'Ferramentas',ru:'Инструменты'}, 'bnav.compare':{en:'Compare',ar:'قارن',de:'Vergleich',el:'Σύγκριση',es:'Comparar',fr:'Comparer',it:'Confronta',nl:'Vergelijk',pt:'Comparar',ru:'Сравнить'}, 'bnav.blog':{en:'Blog',ar:'مدوне',de:'Blog',el:'Ιστολόγιο',es:'Blog',fr:'Blog',it:'Blog',nl:'Blog',pt:'Blog',ru:'Блог'}, 'label.shortlist':{en:'SHORTLIST',ar:'قائمتي',de:'MERKLISTE',el:'ΕΠΙΛΟΓΕΣ',es:'FAVORITOS',fr:'SÉLECTION',it:'PREFERITI',nl:'SHORTLIST',pt:'FAVORITOS',ru:'ИЗБРАННОЕ'}, }; const t = k => (STR[k] && (STR[k][locale] || STR[k].en)) || k; // ─────────────────────────── HEADER ─────────────────────────── const hdr = document.getElementById('site-header'); if (hdr) { hdr.className = 'site-header'; hdr.innerHTML = `
${({en:'EN',es:'ES',de:'DE',el:'EL',ru:'RU',it:'IT',ar:'AR',fr:'FR',nl:'NL',pt:'PT'}[locale] || 'EN')}
${t('cta.findTool')}
`; // Close the language menu when clicking outside. document.addEventListener('click', (e) => { const el = document.getElementById('t10-lang'); if (el && el.open && !el.contains(e.target)) el.open = false; }); hdr.querySelectorAll('.nav a').forEach(a => { const h = a.getAttribute('href'); if (h && h !== '/' && path.startsWith(h)) { a.classList.add('nav-current'); } }); } // ─────────────────────────── DRAWER (mobile) ─────────────────────────── const drawer = document.createElement('aside'); drawer.id = 't10-drawer'; drawer.className = 't10-drawer'; drawer.setAttribute('aria-hidden', 'true'); drawer.setAttribute('aria-label', 'Mobile navigation'); drawer.innerHTML = `
Categories AI Video AI Image AI Logo AI Website Builder
Free tools Finder quiz Price calculator Prompt generator Compare All free tools →
Read Blog All articles
About About us How we rate Contact Privacy
Find my tool →
`; document.body.appendChild(drawer); const scrim = document.createElement('div'); scrim.id = 't10-scrim'; scrim.className = 't10-scrim'; document.body.appendChild(scrim); function openDrawer() { drawer.classList.add('is-open'); scrim.classList.add('is-open'); document.body.classList.add('t10-drawer-locked'); drawer.setAttribute('aria-hidden', 'false'); const h = document.getElementById('t10-hamburger'); if (h) h.setAttribute('aria-expanded', 'true'); } function closeDrawer() { drawer.classList.remove('is-open'); scrim.classList.remove('is-open'); document.body.classList.remove('t10-drawer-locked'); drawer.setAttribute('aria-hidden', 'true'); const h = document.getElementById('t10-hamburger'); if (h) h.setAttribute('aria-expanded', 'false'); } const hamb = document.getElementById('t10-hamburger'); if (hamb) hamb.addEventListener('click', () => { drawer.classList.contains('is-open') ? closeDrawer() : openDrawer(); }); document.getElementById('t10-drawer-close').addEventListener('click', closeDrawer); scrim.addEventListener('click', closeDrawer); document.addEventListener('keydown', e => { if (e.key === 'Escape') closeDrawer(); }); // ─────────────────────────── BOTTOM NAV (mobile only) ─────────────────────────── // Five thumb-zone destinations — the most-tapped paths. const bottomNav = document.createElement('nav'); bottomNav.className = 't10-bottom-nav'; bottomNav.setAttribute('aria-label', 'Bottom navigation'); const ICONS = { home: '', finder: '', tools: '', compare: '', blog: '', }; const bnItems = [ { href: `${prefix}/`, label: t('bnav.home'), icon: ICONS.home, matchExact: true }, { href: `${prefix}/ai-tool-finder/`, label: t('bnav.finder'), icon: ICONS.finder }, { href: `${prefix}/free-ai-tools/`, label: t('bnav.tools'), icon: ICONS.tools }, { href: `${prefix}/compare/`, label: t('bnav.compare'), icon: ICONS.compare }, { href: `${prefix}/blog/`, label: t('bnav.blog'), icon: ICONS.blog }, ]; bottomNav.innerHTML = bnItems.map(it => { const isCurrent = it.matchExact ? (path === it.href) : path.startsWith(it.href); return ` ${it.icon} ${it.label} `; }).join(''); document.body.appendChild(bottomNav); // Tap feedback — subtle scale on touchstart for native feel bottomNav.querySelectorAll('.t10-bn-item').forEach(el => { const down = () => el.classList.add('is-press'); const up = () => el.classList.remove('is-press'); el.addEventListener('touchstart', down, { passive: true }); el.addEventListener('touchend', up, { passive: true }); el.addEventListener('touchcancel', up, { passive: true }); el.addEventListener('pointerdown', down); el.addEventListener('pointerup', up); el.addEventListener('pointerleave', up); }); // ─────────────────────────── FOOTER ─────────────────────────── const ftr = document.getElementById('site-footer'); if (ftr) { ftr.className = 'site-footer'; ftr.innerHTML = `

Independent reviews of AI tools. We use every tool before we write about it.

Categories

AI Video AI Image AI Logo AI Website

Free tools

Finder quiz Price calculator Prompt generator Compare All free tools →

About

About us How we rate Contact Privacy Terms
© ${new Date().getFullYear()} Top10AI · Built by people inside the AI industry. AFFILIATE DISCLOSURE: We earn commission on links.
`; } // ─────────────────────────── STICKY COMPARE ─────────────────────────── // Rules: // • Hidden by default. Appears on first add with a slide-up animation. // • Shows prominent "Compare N →" CTA pointing at /compare/. // • Hidden on /compare/ (page owns its own UI). // • × button clears the shortlist. No dismiss-without-clear. const KEY = 't10_shortlist'; const shortlist = new Set(JSON.parse(localStorage.getItem(KEY) || '[]')); window.__t10 = window.__t10 || {}; window.__t10.shortlist = shortlist; window.__t10.save = () => localStorage.setItem(KEY, JSON.stringify([...shortlist])); window.__t10.toggle = slug => { const was = shortlist.size; if (shortlist.has(slug)) shortlist.delete(slug); else shortlist.add(slug); window.__t10.save(); updateSticky(was < shortlist.size); document.dispatchEvent(new CustomEvent('shortlist-changed')); }; window.__t10.clear = () => { shortlist.clear(); window.__t10.save(); updateSticky(false); document.dispatchEvent(new CustomEvent('shortlist-changed')); }; const onComparePage = /^\/compare\/?$/.test(location.pathname); function updateSticky(animateIn) { let bar = document.getElementById('sticky-compare'); if (shortlist.size === 0 || onComparePage) { if (bar) bar.remove(); return; } if (!bar) { bar = document.createElement('div'); bar.id = 'sticky-compare'; bar.className = 'sticky-compare'; document.body.appendChild(bar); } if (animateIn) { bar.classList.remove('is-pop'); // next frame requestAnimationFrame(() => bar.classList.add('is-pop')); } const BRANDS = window.__T10?.BRANDS || []; const arr = [...shortlist].map(s => BRANDS.find(b => b.slug === s)).filter(Boolean); const chips = arr.slice(0, 4).map(b => b.logo ? '' : '' + (b.initial || b.name[0]) + '' ).join('') + (arr.length > 4 ? '+' + (arr.length - 4) + '' : ''); const cta = shortlist.size === 1 ? 'Add 1 more to compare →' : 'Compare ' + shortlist.size + ' tools →'; bar.innerHTML = '★ SHORTLIST' + '
' + chips + '
' + cta + ''; bar.querySelector('.sc-close').addEventListener('click', () => window.__t10.clear()); } window.__t10.updateSticky = updateSticky; updateSticky(false); })();