diff --git a/src/marketing/content/site.ts b/src/marketing/content/site.ts index 1f557c9..2730d43 100644 --- a/src/marketing/content/site.ts +++ b/src/marketing/content/site.ts @@ -49,6 +49,8 @@ export const CHROME: Record< | 'footer.license' | 'lang.switch' | 'theme.toggle' + | 'menu.open' + | 'menu.close' | 'skipToContent' | 'breadcrumb.home', Localized @@ -77,6 +79,8 @@ export const CHROME: Record< 'footer.license': { en: 'MIT licensed', ar: 'رخصة MIT' }, 'lang.switch': { en: 'العربية', ar: 'English' }, 'theme.toggle': { en: 'Switch light/dark theme', ar: 'بدِّل بين الوضع الفاتح والداكن' }, + 'menu.open': { en: 'Open menu', ar: 'افتح القائمة' }, + 'menu.close': { en: 'Close menu', ar: 'أغلق القائمة' }, skipToContent: { en: 'Skip to content', ar: 'تخطَّ إلى المحتوى' }, 'breadcrumb.home': { en: 'Home', ar: 'الرئيسية' }, } diff --git a/src/marketing/layout/MarketingLayout.tsx b/src/marketing/layout/MarketingLayout.tsx index 769aed2..44e33ed 100644 --- a/src/marketing/layout/MarketingLayout.tsx +++ b/src/marketing/layout/MarketingLayout.tsx @@ -14,7 +14,7 @@ import { SiteHeader } from './SiteHeader' * content is simply visible — never hidden. */ const ENHANCE_SCRIPT = - "(function(){var d=document.documentElement;if(d.getAttribute('data-mk-enhanced'))return;d.setAttribute('data-mk-enhanced','1');try{var s=localStorage.getItem('scripto:mk-theme');if(s==='light')d.classList.remove('dark');else if(s==='dark')d.classList.add('dark')}catch(e){}d.classList.add('js');document.addEventListener('click',function(ev){var t=ev.target;var c=t&&t.closest?t.closest('[data-copy]'):null;if(c){var box=c.closest('.mk-codebox');var pre=box&&box.querySelector('pre');if(pre&&navigator.clipboard){navigator.clipboard.writeText(pre.innerText).then(function(){var o=c.textContent;c.textContent='Copied!';setTimeout(function(){c.textContent=o},1400)})}return}var b=t&&t.closest?t.closest('[data-theme-toggle]'):null;if(!b)return;var k=d.classList.toggle('dark');try{localStorage.setItem('scripto:mk-theme',k?'dark':'light')}catch(e){}});var o='IntersectionObserver'in window?new IntersectionObserver(function(t){t.forEach(function(n){n.isIntersecting&&(n.target.classList.add('in'),o.unobserve(n.target))})},{rootMargin:'0px 0px -8% 0px',threshold:0.08}):null;function scan(){var e=document.querySelectorAll('.mk-reveal:not([data-obs])');for(var i=0;i=2)ok();else v.addEventListener('loadeddata',ok,{once:true})}}if(document.readyState==='loading')addEventListener('DOMContentLoaded',scan);scan();addEventListener('load',scan);setTimeout(scan,400);setTimeout(scan,1600)})();" + "(function(){var d=document.documentElement;if(d.getAttribute('data-mk-enhanced'))return;d.setAttribute('data-mk-enhanced','1');try{var s=localStorage.getItem('scripto:mk-theme');if(s==='light')d.classList.remove('dark');else if(s==='dark')d.classList.add('dark')}catch(e){}d.classList.add('js');document.addEventListener('click',function(ev){var t=ev.target;var c=t&&t.closest?t.closest('[data-copy]'):null;if(c){var box=c.closest('.mk-codebox');var pre=box&&box.querySelector('pre');if(pre&&navigator.clipboard){navigator.clipboard.writeText(pre.innerText).then(function(){var o=c.textContent;c.textContent='Copied!';setTimeout(function(){c.textContent=o},1400)})}return}var b=t&&t.closest?t.closest('[data-theme-toggle]'):null;if(!b)return;var k=d.classList.toggle('dark');try{localStorage.setItem('scripto:mk-theme',k?'dark':'light')}catch(e){}});function mkMenu(){return document.querySelector('details.mk-menu')}document.addEventListener('click',function(ev){var t=ev.target;var m=mkMenu();if(!m||!m.open||!t||!t.closest)return;if(t.closest('.mk-menu-panel a')){m.open=false;return}if(!t.closest('details.mk-menu'))m.open=false});document.addEventListener('keydown',function(ev){if(ev.key!=='Escape')return;var m=mkMenu();if(m&&m.open){m.open=false;var s=m.querySelector('summary');if(s)s.focus()}});addEventListener('resize',function(){if(innerWidth>=900){var m=mkMenu();if(m&&m.open)m.open=false}});document.addEventListener('toggle',function(ev){var m=ev.target;if(!m||!m.classList||!m.classList.contains('mk-menu'))return;var s=m.querySelector('summary');if(s){s.setAttribute('aria-expanded',m.open?'true':'false');var lb=s.getAttribute(m.open?'data-label-close':'data-label-open');if(lb)s.setAttribute('aria-label',lb)}d.classList.toggle('mk-menu-open',m.open)},true);var o='IntersectionObserver'in window?new IntersectionObserver(function(t){t.forEach(function(n){n.isIntersecting&&(n.target.classList.add('in'),o.unobserve(n.target))})},{rootMargin:'0px 0px -8% 0px',threshold:0.08}):null;function scan(){var e=document.querySelectorAll('.mk-reveal:not([data-obs])');for(var i=0;i=2)ok();else v.addEventListener('loadeddata',ok,{once:true})}}if(document.readyState==='loading')addEventListener('DOMContentLoaded',scan);scan();addEventListener('load',scan);setTimeout(scan,400);setTimeout(scan,1600)})();" interface MarketingLayoutProps { lang: MarketingLang diff --git a/src/marketing/layout/SiteHeader.tsx b/src/marketing/layout/SiteHeader.tsx index 471957b..73120f2 100644 --- a/src/marketing/layout/SiteHeader.tsx +++ b/src/marketing/layout/SiteHeader.tsx @@ -14,6 +14,57 @@ const NAV_ITEMS: { to: string; key: 'nav.features' | 'nav.templates' | 'nav.skin { to: '/blog', key: 'nav.blog' }, ] +function ThemeToggle({ lang }: { lang: MarketingLang }) { + return ( + + ) +} + +function GithubLink({ lang }: { lang: MarketingLang }) { + return ( + + + {chrome('nav.github', lang)} + + ) +} + +function LangSwitch({ lang, altPath }: { lang: MarketingLang; altPath: string }) { + return ( + + {chrome('lang.switch', lang)} + + ) +} + export function SiteHeader({ lang, altPath }: SiteHeaderProps) { const brand = lang === 'ar' ? SITE_NAME_AR : SITE_NAME @@ -25,8 +76,9 @@ export function SiteHeader({ lang, altPath }: SiteHeaderProps) {
- {brand} + {brand} + + + {/* Desktop action cluster (≥900px) */}
- - {altPath ? ( - - {chrome('lang.switch', lang)} - - ) : null} - - - {chrome('nav.github', lang)} - + + {altPath ? : null} + {chrome('openApp', lang)}
+ + {/* Compact cluster (<900px): primary CTA stays 1-tap; nav + secondary actions collapse into the menu. + zero-JS: native
opens the menu, ENHANCE_SCRIPT adds the niceties (never React state). */} +
+ + {chrome('openApp', lang)} + +
+ + + + +
+ +
+
+ + {altPath ? : null} + +
+
+
+
) diff --git a/src/marketing/marketing.css b/src/marketing/marketing.css index 1221d57..8b3fa7d 100644 --- a/src/marketing/marketing.css +++ b/src/marketing/marketing.css @@ -195,12 +195,26 @@ html.dark { } .mk-header-inner { + position: relative; display: flex; align-items: center; - gap: 1.25rem; + gap: 0.75rem; block-size: 4rem; } +@media (min-width: 900px) { + .mk-header-inner { + gap: 1.25rem; + } +} + +/* Hide the wordmark on the very narrowest screens; keep the logo icon. */ +@media (max-width: 359px) { + .mk-logo-word { + display: none; + } +} + .mk-logo { display: inline-flex; align-items: center; @@ -244,19 +258,164 @@ html.dark { background: var(--mk-surface-3); } +/* Desktop action cluster — collapses into the menu below the nav breakpoint. */ .mk-header-actions { - display: flex; + display: none; align-items: center; gap: 0.625rem; margin-inline-start: auto; } +@media (min-width: 900px) { + .mk-header-actions { + display: flex; + } +} + +/* Compact cluster (CTA + hamburger) shown below the nav breakpoint. */ +.mk-header-compact { + display: flex; + align-items: center; + gap: 0.5rem; + margin-inline-start: auto; +} + +@media (min-width: 900px) { + .mk-header-compact { + display: none; + } +} + @media (max-width: 640px) { .mk-gh-label { display: none; } } +/* ---------- mobile menu (zero-JS: native
) ---------- */ + +.mk-menu { + display: inline-flex; +} + +.mk-menu-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + min-inline-size: 2.75rem; + min-block-size: 2.75rem; + border-radius: 0.75rem; + color: var(--mk-fg); + background: var(--mk-chip-bg); + border: 1px solid var(--mk-border-strong); + cursor: pointer; + list-style: none; + transition: background-color 0.15s ease, border-color 0.15s ease; +} + +.mk-menu-toggle::-webkit-details-marker { + display: none; +} + +.mk-menu-toggle:hover { + border-color: var(--mk-accent-border); + background: var(--mk-surface-3); +} + +.mk-menu-toggle:focus-visible { + outline: 2px solid var(--mk-accent-fg); + outline-offset: 2px; +} + +/* hamburger ↔ X reflects the open state */ +.mk-icon-close { + display: none; +} + +.mk-menu[open] .mk-icon-open { + display: none; +} + +.mk-menu[open] .mk-icon-close { + display: block; +} + +.mk-menu-panel { + position: absolute; + inset-block-start: calc(100% + 0.5rem); + inset-inline: 0; + z-index: 60; + display: flex; + flex-direction: column; + max-block-size: calc(100dvh - 5rem); + overflow-y: auto; + padding: 0.75rem; + border: 1px solid var(--mk-border); + border-radius: 1rem; + background: var(--mk-bg); + box-shadow: var(--mk-card-shadow); +} + +.mk-menu[open] .mk-menu-panel { + animation: mk-menu-in 0.16s cubic-bezier(0.16, 1, 0.3, 1); +} + +@keyframes mk-menu-in { + from { + opacity: 0; + transform: translateY(-0.5rem); + } +} + +.mk-menu-nav { + display: flex; + flex-direction: column; + gap: 0.125rem; +} + +.mk-menu-nav a { + padding: 0.75rem 0.875rem; + border-radius: 0.75rem; + font-size: 1rem; + font-weight: 550; + color: var(--mk-fg); + transition: background-color 0.15s ease; +} + +.mk-menu-nav a:hover { + background: var(--mk-surface-3); +} + +.mk-menu-divider { + block-size: 1px; + border: 0; + margin: 0.625rem 0; + background: var(--mk-border); +} + +.mk-menu-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; +} + +/* Always label GitHub inside the menu, even under the 640px cutoff. */ +.mk-menu-actions .mk-gh-label { + display: inline; +} + +/* Lock background scroll while the menu is open (ENHANCE_SCRIPT toggles the class). */ +html.mk-menu-open body { + overflow: hidden; +} + +@media (prefers-reduced-motion: reduce) { + .mk-menu[open] .mk-menu-panel { + animation: none; + } +} + .mk-skip-link { position: absolute; inset-inline-start: 1rem; @@ -315,6 +474,11 @@ html:not(.dark) .mk-theme-toggle .tt-sun { font-size: 0.9375rem; } +.mk-btn-sm { + padding: 0.5rem 0.875rem; + font-size: 0.875rem; +} + .mk-btn-primary { color: white; background: linear-gradient(135deg, hsl(239 84% 63%), hsl(266 80% 60%));