diff --git a/src/assets/header/header.css b/src/assets/header/header.css new file mode 100644 index 0000000..6fb79d4 --- /dev/null +++ b/src/assets/header/header.css @@ -0,0 +1,277 @@ +/* +M M AAAAA X X L W W AAAAA RRRR EEEEE +MM MM A A X X L W W A A R R E +M M M AAAAA X L W W W AAAAA RRRR EEE +M M A A X X L WW WW A A R R E +M M A A X X LLLLL W W A A R R EEEEE +*/ + +.site-header { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 2000; + background: rgba(11, 11, 14, 0.82); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + border-bottom: 1px solid #1a1a22; + transform: translateY(-100%); + opacity: 0; + pointer-events: none; + transition: transform 0.35s ease, opacity 0.3s ease; + font-family: var(--police-big), var(--police); +} + +.site-header.visible { + transform: translateY(0); + opacity: 1; + pointer-events: auto; +} + +.site-header-inner { + max-width: 1100px; + margin: 0 auto; + padding: 0.7rem 2rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + position: relative; +} + +.site-header-brand { + display: flex; + align-items: center; + gap: 0.6rem; + text-decoration: none; + flex-shrink: 0; +} + +.site-header-brand:hover { + text-decoration: none; +} + +.site-header-logo { + width: 28px; + height: 28px; + object-fit: contain; +} + +.site-header-name { + font-family: 'Syne', sans-serif; + font-weight: 700; + font-size: 1.1rem; + letter-spacing: 0.5px; + color: var(--light); +} + +.site-header-nav { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.site-header-item { + position: relative; +} + +.site-header-link { + display: inline-flex; + align-items: center; + gap: 0.35rem; + background: none; + border: none; + color: #ccc; + font-family: var(--police-big), var(--police); + font-size: 0.92rem; + padding: 0.5rem 0.7rem; + cursor: pointer; + border-radius: 4px; + transition: color 0.2s ease, background 0.2s ease; + text-decoration: none; +} + +.site-header-link:hover, +.site-header-item.open > .site-header-link { + color: var(--light); + background: rgba(255, 255, 255, 0.05); + text-decoration: none; +} + +.site-header-caret { + display: inline-block; + font-size: 0.65rem; + transition: transform 0.2s ease; + opacity: 0.6; +} + +.site-header-item.open .site-header-caret { + transform: rotate(180deg); +} + +.site-header-dropdown { + position: absolute; + top: calc(100% + 10px); + left: 0; + min-width: 230px; + background: #0e0e13; + border: 1px solid #222; + border-radius: 8px; + padding: 0.6rem; + display: flex; + flex-direction: column; + gap: 0.15rem; + opacity: 0; + visibility: hidden; + transform: translateY(-6px); + transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s; + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35); +} + +.site-header-item.open > .site-header-dropdown { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.site-header-dropdown a, +.site-header-dropdown .site-header-sublink { + display: flex; + align-items: center; + gap: 0.6rem; + color: #bbb; + font-size: 0.88rem; + padding: 0.5rem 0.6rem; + border-radius: 5px; + text-decoration: none; + transition: background 0.15s ease, color 0.15s ease; +} + +.site-header-dropdown a:hover, +.site-header-dropdown .site-header-sublink:hover { + background: rgba(255, 255, 255, 0.06); + color: var(--light); + text-decoration: none; +} + +.site-header-net-icon { + width: 18px; + height: 18px; + flex-shrink: 0; + color: var(--secondary); +} + +.site-header-dropdown-full { + position: fixed; + top: var(--site-header-h, 64px); + left: 0; + right: 0; + width: 100%; + min-width: 0; + border-radius: 0; + border-left: none; + border-right: none; + background: #0b0b0f; + padding: 1.4rem 2rem; + flex-direction: row; + flex-wrap: wrap; + gap: 0.4rem 1rem; + max-width: none; +} + +.site-header-dropdown-full-inner { + max-width: 1100px; + margin: 0 auto; + display: flex; + flex-wrap: wrap; + gap: 0.4rem 1.2rem; + width: 100%; +} + +.site-header-dropdown-full a { + flex: 0 0 auto; +} + +.site-header-subgroup { + display: flex; + flex-direction: column; +} + +.site-header-sublink[aria-expanded="true"] .site-header-caret-sm { + transform: rotate(180deg); +} + +.site-header-caret-sm { + margin-left: auto; + font-size: 0.6rem; + opacity: 0.6; + transition: transform 0.2s ease; +} + +.site-header-subpanel { + display: none; + flex-direction: column; + padding-left: 1.1rem; + border-left: 1px solid #222; + margin: 0.1rem 0 0.3rem 1rem; +} + +.site-header-subpanel.open { + display: flex; +} + +body.scrolled-past-hero .diapo-container { + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; +} + +body.scrolled-past-hero .notification-banner { + top: calc(var(--site-header-h, 64px) + 15px); + transition: top 0.3s ease; +} + +@media (max-width: 860px) { + .site-header-inner { + flex-direction: column; + gap: 0.6rem; + padding: 0.8rem 1rem 1rem; + } + + .site-header-brand { + order: 1; + } + + .site-header-nav { + order: 2; + width: 100%; + justify-content: center; + flex-wrap: wrap; + gap: 0.2rem; + } + + .site-header-item { + position: static; + } + + .site-header-dropdown { + position: fixed; + left: 1rem; + right: 1rem; + top: calc(var(--site-header-h, 96px) + 10px); + width: auto; + min-width: 0; + } + + .site-header-dropdown-full { + top: auto; + position: fixed; + left: 0; + right: 0; + bottom: 0; + max-height: 70vh; + overflow-y: auto; + border-top: 1px solid #222; + } +} diff --git a/src/assets/header/header.js b/src/assets/header/header.js new file mode 100644 index 0000000..a2461bd --- /dev/null +++ b/src/assets/header/header.js @@ -0,0 +1,175 @@ +// M M AAAAA X X L W W AAAAA RRRR EEEEE +// MM MM A A X X L W W A A R R E +// M M M AAAAA X L W W W AAAAA RRRR EEE +// M M A A X X L WW WW A A R R E +// M M A A X X LLLLL W W A A R R EEEEE + +(function () { + + const lang = (document.documentElement.lang || 'fr').toLowerCase().startsWith('fr') ? 'fr' : 'en'; + const HOME_PATH = lang === 'fr' ? '/fr_fr.html' : '/en_en.html'; + + const PORTFOLIO_LINKS = [ + { id: 'apropos', label: lang === 'fr' ? 'À propos' : 'About' }, + { id: 'projets', label: lang === 'fr' ? 'Projets' : 'Projects' }, + { id: 'stack', label: 'Stack' }, + { id: 'activite', label: lang === 'fr' ? 'Activité' : 'Activity' }, + ]; + + const NET_ICON = (label) => ``; + + function portfolioLinksHTML() { + return PORTFOLIO_LINKS.map(l => + `${l.label}` + ).join(''); + } + + const HEADER_HTML = ` +
+ + + Maxlware_ + + + +
+ `; + + function init() { + let header = document.getElementById('site-header'); + if (!header) { + header = document.createElement('div'); + document.body.insertBefore(header, document.body.firstChild); + } + header.id = 'site-header'; + header.classList.add('site-header'); + header.innerHTML = HEADER_HTML; + + function updateHeaderHeightVar() { + document.documentElement.style.setProperty('--site-header-h', header.offsetHeight + 'px'); + } + updateHeaderHeightVar(); + window.addEventListener('resize', updateHeaderHeightVar); + + header.querySelectorAll('[data-onpage-id]').forEach((a) => { + const id = a.getAttribute('data-onpage-id'); + if (!document.getElementById(id)) { + a.setAttribute('href', HOME_PATH + '#' + id); + } + }); + + const hero = document.querySelector('.hero'); + let threshold = hero ? hero.offsetHeight * 0.9 : window.innerHeight * 0.9; + + function onScroll() { + const scrolled = window.scrollY > threshold; + header.classList.toggle('visible', scrolled); + document.body.classList.toggle('scrolled-past-hero', scrolled); + } + window.addEventListener('scroll', onScroll, { passive: true }); + window.addEventListener('resize', () => { + threshold = hero ? hero.offsetHeight * 0.9 : window.innerHeight * 0.9; + onScroll(); + }); + onScroll(); + + const items = Array.from(header.querySelectorAll('.site-header-item')); + + function closeAll(except) { + items.forEach((it) => { + if (it !== except) { + it.classList.remove('open'); + const btn = it.querySelector(':scope > .site-header-link'); + if (btn) btn.setAttribute('aria-expanded', 'false'); + } + }); + } + + items.forEach((item) => { + const btn = item.querySelector(':scope > .site-header-link'); + if (!btn) return; + btn.addEventListener('click', (e) => { + e.stopPropagation(); + const isOpen = item.classList.contains('open'); + closeAll(item); + item.classList.toggle('open', !isOpen); + btn.setAttribute('aria-expanded', String(!isOpen)); + }); + }); + + header.querySelectorAll('[data-subtoggle]').forEach((sub) => { + sub.addEventListener('click', (e) => { + e.stopPropagation(); + const key = sub.getAttribute('data-subtoggle'); + const panel = header.querySelector(`[data-subpanel="${key}"]`); + const isOpen = panel.classList.contains('open'); + panel.classList.toggle('open', !isOpen); + sub.setAttribute('aria-expanded', String(!isOpen)); + }); + }); + + document.addEventListener('click', () => closeAll(null)); + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closeAll(null); + }); + + header.querySelectorAll('.site-header-dropdown a').forEach((a) => { + a.addEventListener('click', () => closeAll(null)); + }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(); diff --git a/src/assets/index/style.css b/src/assets/index/style.css index 0799688..7311f91 100644 --- a/src/assets/index/style.css +++ b/src/assets/index/style.css @@ -19,6 +19,10 @@ M M A A X X LLLLL W W A A R R EEEEE --police: 'sans-serif' } + html { + scroll-behavior: smooth; + } + body { background-color: var(--dark); margin: 0; @@ -209,6 +213,7 @@ M M A A X X LLLLL W W A A R R EEEEE .section { background-color: var(--dark); padding: 6vh 0 8vh; + scroll-margin-top: 90px; } .section-inner { @@ -796,12 +801,12 @@ footer { } .footer-inner { - max-width: 960px; + max-width: 1140px; margin: 0 auto; padding: 0 2rem; display: grid; - grid-template-columns: 1.4fr 1.6fr 1fr; - gap: 3rem; + grid-template-columns: 1.1fr 2.1fr 0.9fr; + gap: 2.5rem; align-items: start; } @@ -856,9 +861,9 @@ footer { } .footer-links { - display: flex; - gap: 2rem; - flex-wrap: wrap; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.6rem; } .footer-links-group { @@ -971,13 +976,31 @@ footer { text-align: center; } +@media (max-width: 1000px) { + .footer-inner { + grid-template-columns: 1fr 1fr; + gap: 2rem; + } + + .footer-weather { + grid-column: 1 / -1; + } +} + @media (max-width: 768px) { .footer-inner { grid-template-columns: 1fr; - gap: 2rem; + gap: 2.2rem; } .footer-links { - gap: 1.5rem; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.2rem; + } +} + +@media (max-width: 480px) { + .footer-links { + grid-template-columns: 1fr 1fr; } } diff --git a/src/en_en.html b/src/en_en.html index 897a97e..205f076 100644 --- a/src/en_en.html +++ b/src/en_en.html @@ -12,6 +12,7 @@ + Maxlware - Independent developer in France. @@ -276,6 +277,7 @@

Activity_

+ diff --git a/src/fr_fr.html b/src/fr_fr.html index 410b7e2..f1401f3 100644 --- a/src/fr_fr.html +++ b/src/fr_fr.html @@ -12,6 +12,7 @@ + Maxlware - Développeur indépendant en France. @@ -276,6 +277,7 @@

Activité_

+ diff --git a/src/index.html b/src/index.html index e933d3b..e668b48 100644 --- a/src/index.html +++ b/src/index.html @@ -5,6 +5,7 @@ Maxlware - + @@ -131,5 +132,6 @@ JavaScript n’est pas activé. +