Fix: Remove Alpine, wire JS for mobile sidebar

This commit is contained in:
2025-09-14 22:28:24 -07:00
parent 87607b2425
commit 68185d96ac

View File

@@ -1,7 +1,7 @@
<!doctype html><html lang=en>
<head>
<meta name=viewport content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Hi Language</title>
<link rel=preconnect href=https://cdn.jsdelivr.net>
<script src=https://cdn.tailwindcss.com></script>
@@ -14,10 +14,8 @@
.markdown-body pre{overflow:auto}
.markdown-body code:not(pre code){background:rgba(175,184,193,.2);padding:.2em .4em;border-radius:6px}
.no-scrollbar::-webkit-scrollbar{display:none}.no-scrollbar{scrollbar-width:none}
.btn{transition:.15s transform}
.btn:active{transform:scale(.98)}
.btn{transition:.15s transform}.btn:active{transform:scale(.98)}
.anchor{scroll-margin-top:76px}
.menu-card{position:fixed;z-index:60;min-width:12rem;border-radius:.75rem;border:1px solid #e5e7eb;background:#fff;box-shadow:0 10px 20px rgba(0,0,0,.08)}
.code-actions{position:absolute;top:.5rem;right:.5rem;display:flex;gap:.5rem;align-items:center}
.copy-btn{background:#0f172a;color:#fff;border-radius:.5rem;padding:.2rem .5rem;font-size:.75rem;opacity:.9}
.char-c{font-size:.75rem;color:#64748b}
@@ -25,12 +23,11 @@ pre{position:relative;border:1px solid #e5e7eb;border-radius:.75rem}
@media (min-width:1024px){.lg\:grid-cols-layout{grid-template-columns:var(--sbw) 1fr}}
</style>
<script defer src="https://cdn.jsdelivr.net/npm/cash-dom/dist/cash.min.js"></script>
<script defer src="https://unpkg.com/alpinejs" ></script>
</head>
<body class="bg-white text-gray-900 selection:bg-black/10" x-data="{left:false}" @keydown.window.escape="left=false">
<body class="bg-white text-gray-900 selection:bg-black/10">
<header class="sticky top-0 z-30 bg-white/80 backdrop-blur border-b border-gray-200">
<div class="mx-auto w-full px-4 py-3 grid grid-cols-3 items-center">
<button class="h-9 w-9 rounded-xl bg-gray-100 hover:bg-gray-200 flex items-center justify-center btn" title="Menu" @click="left=true"><i data-lucide=panel-left class="h-5 w-5"></i></button>
<button id=menuBtn class="h-9 w-9 rounded-xl bg-gray-100 hover:bg-gray-200 flex items-center justify-center btn" title="Menu"><i data-lucide=panel-left class="h-5 w-5"></i></button>
<div class="justify-self-center text-base font-semibold select-none pointer-events-none">Hi</div>
<div class="justify-self-end"></div>
</div>
@@ -49,7 +46,7 @@ pre{position:relative;border:1px solid #e5e7eb;border-radius:.75rem}
<p class="mt-3 text-gray-600">Inspired by JavaScript. Few keywords, many symbols. Designed so the core reads naturally across languages, for the whole world.</p>
<div class="mt-4 flex flex-wrap gap-2">
<a href="#hello" class="px-3 py-1.5 rounded-full bg-gray-900 text-white text-sm btn">Get started</a>
<a href="https://github.com/hi-language" class="px-3 py-1.5 rounded-full bg-gray-100 hover:bg-gray-200 text-sm btn" target=_blank rel=noopener><i data-lucide=github class="h-4 w-4 inline -mt-0.5 mr-1"></i>GitHub</a>
<a href="https://github.com/hi-language" class="px-3 py-1.5 rounded-full bg-gray-100 hover:bg-gray-200 text-sm btn" target=_blank rel=noopener><svg xmlns="http://www.w3.org/2000/svg" class="inline -mt-0.5 mr-1" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .5A12 12 0 0 0 0 12.64a12.15 12.15 0 0 0 8.2 11.55c.6.11.82-.27.82-.6v-2.17c-3.34.74-4.05-1.65-4.05-1.65a3.29 3.29 0 0 0-1.36-1.82c-1.1-.78.09-.77.09-.77a2.6 2.6 0 0 1 1.89 1.29 2.64 2.64 0 0 0 3.61 1 2.66 2.66 0 0 1 .78-1.65c-2.67-.31-5.47-1.36-5.47-6.06A4.8 4.8 0 0 1 6 7.57a4.45 4.45 0 0 1 .12-3.29s1-.33 3.3 1.26a11.39 11.39 0 0 1 6 0c2.3-1.59 3.3-1.26 3.3-1.26.44 1.03.48 2.2.12 3.29a4.79 4.79 0 0 1 1.27 3.32c0 4.72-2.8 5.75-5.47 6.06a2.97 2.97 0 0 1 .84 2.31v3.43c0 .34.22.73.83.6A12.16 12.16 0 0 0 24 12.64 12 12 0 0 0 12 .5Z"/></svg>GitHub</a>
</div>
</div>
</section>
@@ -58,8 +55,8 @@ pre{position:relative;border:1px solid #e5e7eb;border-radius:.75rem}
</div>
<!-- Mobile left sidebar -->
<div class="fixed inset-0 z-40 bg-black/30 transition-opacity" :class="left?'opacity-100':'opacity-0 pointer-events-none'" @click="left=false"></div>
<aside class="fixed inset-y-0 left-0 z-50 w-[min(85vw,var(--sbw))] max-w-[85vw] bg-white border-r border-gray-200 shadow-xl transform transition-transform duration-200 ease-out" :class="left?'translate-x-0':'-translate-x-full'">
<div id="overlay" class="fixed inset-0 z-40 bg-black/30 transition-opacity opacity-0 pointer-events-none"></div>
<aside id="sidebarMobile" class="fixed inset-y-0 left-0 z-50 w-[min(85vw,var(--sbw))] max-w-[85vw] bg-white border-r border-gray-200 shadow-xl transform transition-transform duration-200 ease-out -translate-x-full">
<div class="p-3 border-b flex items-center gap-2"><span class="h-7 w-7 rounded-full bg-gray-900 text-white inline-flex items-center justify-center"></span><span class="font-medium truncate">Hi</span></div>
<nav id="sideNavMobile" class="p-2 text-sm overflow-y-auto no-scrollbar"></nav>
</aside>
@@ -291,60 +288,66 @@ document.addEventListener('DOMContentLoaded',()=>{
const md=window.markdownit({html:false,linkify:true,typographer:true,breaks:false})
const icons=()=>window.lucide&&lucide.createIcons()
const slug=s=>s.toLowerCase().trim().replace(/[^\w\- ]+/g,'').replace(/\s+/g,'-')
const render=()=>{
const src=$('#md')?.textContent||''
const html=md.render(src)
const root=$('#suneHtml')
root.className='markdown-body max-w-3xl mx-auto'
root.innerHTML=html
// add anchors + ids
$$('#suneHtml h2, #suneHtml h3').forEach(h=>{
if(!h.id){h.id=slug(h.textContent)}
h.classList.add('anchor')
const a=document.createElement('a');a.href='#'+h.id;a.className='ml-2 text-gray-300 hover:text-gray-500 align-middle';a.innerHTML='#';h.appendChild(a)
// Render markdown into suneHtml
const src=$('#md')?.textContent||'', html=md.render(src), root=$('#suneHtml')
root.className='markdown-body max-w-3xl mx-auto'; root.innerHTML=html
// Headings: ids + anchors
$$('#suneHtml h2, #suneHtml h3').forEach(h=>{
if(!h.id)h.id=slug(h.textContent)
h.classList.add('anchor')
const a=document.createElement('a');a.href='#'+h.id;a.className='ml-2 text-gray-300 hover:text-gray-500 align-middle';a.innerHTML='#';h.appendChild(a)
})
// Highlight + copy buttons
document.querySelectorAll('#suneHtml pre>code').forEach(code=>{
if(window.hljs) hljs.highlightElement(code)
const pre=code.parentElement, t=code.textContent||'', btn=document.createElement('button'), meta=document.createElement('span'), box=document.createElement('div')
btn.className='copy-btn'; btn.textContent='Copy'
btn.onclick=async e=>{e.stopPropagation();try{await navigator.clipboard.writeText(code.innerText);btn.textContent='Copied';setTimeout(()=>btn.textContent='Copy',1100)}catch{}}
meta.className='char-c'; meta.textContent=(t.length>=1e3?(t.length/1e3).toFixed(1)+'K':t.length)+' chars'
box.className='code-actions'; box.append(meta,btn); pre.appendChild(box)
})
// Build side nav (desktop + mobile)
const buildNav=(navSel)=>{
const nav=$(navSel); if(!nav)return; nav.innerHTML=''
const frag=document.createDocumentFragment()
$$('#suneHtml h2,h3').forEach(h=>{
const lvl=h.tagName==='H2'?0:1, a=document.createElement('a')
a.href='#'+h.id; a.className=(lvl?'pl-6 ':'')+'block px-2 py-1 rounded-lg hover:bg-gray-100 text-gray-700'
a.textContent=h.textContent.replace('#','').trim(); frag.appendChild(a)
})
// highlight + copy buttons
document.querySelectorAll('#suneHtml pre>code').forEach(code=>{
hljs.highlightElement(code)
const pre=code.parentElement, t=code.textContent||'', btn=document.createElement('button'), meta=document.createElement('span'), box=document.createElement('div')
btn.className='copy-btn';btn.textContent='Copy'
btn.onclick=async e=>{e.stopPropagation();try{await navigator.clipboard.writeText(code.innerText);btn.textContent='Copied';setTimeout(()=>btn.textContent='Copy',1100)}catch{}}
meta.className='char-c';meta.textContent=(t.length>=1e3?(t.length/1e3).toFixed(1)+'K':t.length)+' chars'
box.className='code-actions'
box.append(meta,btn);pre.appendChild(box)
})
buildNav()
icons()
}
const buildNav=()=>{
const heads=$$('#suneHtml h2,h3'), list=sec=>{
const nav=sec?$('#sideNavMobile'):$('#sideNav');if(!nav)return;nav.innerHTML=''
const ul=document.createElement('div')
heads.forEach(h=>{
const lvl=h.tagName==='H2'?0:1
const a=document.createElement('a')
a.href='#'+h.id
a.className=(lvl? 'pl-6 ':'')+'block px-2 py-1 rounded-lg hover:bg-gray-100 text-gray-700'
a.textContent=h.textContent.replace('#','').trim()
ul.appendChild(a)
nav.appendChild(frag)
const links=[...nav.querySelectorAll('a')]
const io=new IntersectionObserver(es=>{
es.forEach(e=>{
if(e.isIntersecting){
const id='#'+e.target.id
links.forEach(L=>L.classList.toggle('bg-gray-100 font-medium',L.getAttribute('href')===id))
}
})
nav.appendChild(ul)
// scroll spy
const links=[...ul.querySelectorAll('a')]
const io=new IntersectionObserver(es=>{
es.forEach(e=>{
if(e.isIntersecting){
const id='#'+e.target.id
links.forEach(L=>L.classList.toggle('bg-gray-100 font-medium',L.getAttribute('href')===id))
}
})
},{rootMargin:'-50% 0px -40% 0px',threshold:[0,1]})
$$('#suneHtml h2, #suneHtml h3').forEach(h=>io.observe(h))
links.forEach(L=>L.addEventListener('click',()=>{if(window.innerWidth<1024){document.body.__x?.$data.left=false}}))
}
list(false);list(true)
},{rootMargin:'-50% 0px -40% 0px',threshold:[0,1]})
$$('#suneHtml h2, #suneHtml h3').forEach(h=>io.observe(h))
links.forEach(L=>L.addEventListener('click',()=>{if(window.innerWidth<1024) setLeft(false)}))
}
render()
buildNav('#sideNav'); buildNav('#sideNavMobile')
// Mobile sidebar controls (no Alpine)
const overlay=$('#overlay'), side=$('#sidebarMobile'), menuBtn=$('#menuBtn')
const setLeft=v=>{
overlay.classList.toggle('opacity-100',v)
overlay.classList.toggle('opacity-0',!v)
overlay.classList.toggle('pointer-events-none',!v)
side.classList.toggle('-translate-x-full',!v)
side.classList.toggle('translate-x-0',v)
}
menuBtn?.addEventListener('click',()=>setLeft(true))
overlay?.addEventListener('click',()=>setLeft(false))
document.addEventListener('keydown',e=>{if(e.key==='Escape')setLeft(false)})
icons()
})
</script>
</body>