Last active
July 19, 2025 04:25
-
-
Save naranyala/c94120391866468e25050437c75912a3 to your computer and use it in GitHub Desktop.
your single-file static blog template with code-highlight and mermaid.js support (mobile-friendly too!)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
<title>Tab-Based Blog with Mermaid</title> | |
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/prism.min.js"></script> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/themes/prism-tomorrow.min.css" rel="stylesheet"/> | |
<style> | |
body { | |
margin: 0; | |
background: #1e1e1e; | |
color: #eee; | |
font-family: system-ui, sans-serif; | |
line-height: 1.6; | |
} | |
header { | |
background: #111; | |
padding: 1rem; | |
text-align: center; | |
font-size: 1.5rem; | |
font-weight: bold; | |
position: sticky; | |
top: 0; | |
z-index: 1000; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
} | |
.menu-toggle { | |
display: none; | |
background: none; | |
border: none; | |
color: #eee; | |
font-size: 1.5rem; | |
cursor: pointer; | |
padding: 0.5rem; | |
} | |
main { | |
display: flex; | |
min-height: calc(100vh - 60px); | |
flex-direction: column; | |
} | |
nav { | |
width: 250px; | |
background: #222; | |
padding: 1rem; | |
box-sizing: border-box; | |
overflow-y: auto; | |
transition: transform 0.3s ease; | |
} | |
nav button { | |
display: block; | |
width: 100%; | |
margin: 0.5rem 0; | |
padding: 0.75rem; | |
background: none; | |
border: none; | |
color: #ccc; | |
text-align: left; | |
font-size: 1rem; | |
cursor: pointer; | |
border-radius: 4px; | |
} | |
nav button:hover, nav button.active { | |
color: #fff; | |
background: #333; | |
font-weight: bold; | |
} | |
.content { | |
flex-grow: 1; | |
padding: 2rem; | |
max-width: 800px; | |
margin: 0 auto; | |
width: 100%; | |
box-sizing: border-box; | |
} | |
pre { | |
background: #1e1e1e; | |
padding: 1rem; | |
overflow-x: auto; | |
} | |
code { | |
font-family: monospace; | |
} | |
.mermaid { | |
display: flex; | |
justify-content: center; /* horizontal */ | |
align-items: center; /* vertical */ | |
background: #fff; | |
color: #000; | |
border-radius: 12px; | |
padding: 1rem; | |
margin: 2rem 0; | |
overflow-x: auto; | |
} | |
h2 { | |
border-bottom: 1px solid #444; | |
padding-bottom: 0.5rem; | |
margin-bottom: 1.5rem; | |
} | |
/* Mobile styles */ | |
@media (max-width: 768px) { | |
main { | |
flex-direction: column; | |
} | |
nav { | |
width: 100%; | |
position: fixed; | |
top: 60px; | |
left: 0; | |
height: calc(100vh - 60px); | |
z-index: 999; | |
transform: translateX(-100%); | |
} | |
nav.open { | |
transform: translateX(0); | |
} | |
.menu-toggle { | |
display: block; | |
} | |
.content { | |
padding: 1rem; | |
margin-top: 0; | |
} | |
} | |
/* Desktop styles */ | |
@media (min-width: 769px) { | |
main { | |
flex-direction: row; | |
} | |
nav { | |
position: sticky; | |
top: 60px; | |
height: calc(100vh - 60px); | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<header> | |
<span>📘 Tab-Based Blog Viewer</span> | |
<button class="menu-toggle" aria-label="Toggle menu">☰</button> | |
</header> | |
<main> | |
<nav id="sidebar"></nav> | |
<section class="content" id="main"></section> | |
</main> | |
<script id="article-data" type="application/json"> | |
[ | |
{ | |
"id": "sample-1", | |
"title": "Sample Mermaid Diagram", | |
"content": "Welcome to Mermaid:\n\n```mermaid\ngraph TD\n A[Start] --> B{Is it working?}\n B -->|Yes| C[Great!]\n B -->|No| D[Fix it]\n D --> B\n```\n\n```javascript\nfunction greet(name) {\n return \"Hello, \" + name;\n}\n```" | |
}, | |
{ | |
"id": "sample-2", | |
"title": "Second Article", | |
"content": "Another article:\n\n```mermaid\nsequenceDiagram\n participant Alice\n participant Bob\n Alice->>Bob: Hello Bob, how are you?\n Bob-->>Alice: I am good thanks!\n```" | |
},{ | |
"id": "sample-3", | |
"title": "Just Awesome", | |
"content": "hello world!" | |
} | |
] | |
</script> | |
<script> | |
const articles = JSON.parse(document.getElementById("article-data").textContent); | |
function highlightAndRender(content) { | |
const container = document.createElement("div"); | |
if (typeof content === 'string' && content.includes('```')) { | |
const parts = content.split('```'); | |
parts.forEach((part, i) => { | |
if (i % 2 === 1) { | |
const langMatch = part.match(/^(\w+)\n/); | |
let language = 'javascript'; | |
let code = part; | |
if (langMatch) { | |
language = langMatch[1]; | |
code = part.replace(langMatch[0], ''); | |
} | |
if (language === 'mermaid') { | |
const mermaidDiv = document.createElement("div"); | |
mermaidDiv.className = "mermaid"; | |
mermaidDiv.textContent = code.trim(); | |
container.appendChild(mermaidDiv); | |
} else { | |
const pre = document.createElement("pre"); | |
const codeElem = document.createElement("code"); | |
codeElem.className = `language-${language}`; | |
codeElem.innerHTML = Prism.highlight(code.trim(), Prism.languages[language] || Prism.languages.javascript, language); | |
pre.appendChild(codeElem); | |
container.appendChild(pre); | |
} | |
} else { | |
const p = document.createElement("p"); | |
p.textContent = part.trim(); | |
container.appendChild(p); | |
} | |
}); | |
} else { | |
const p = document.createElement("p"); | |
p.textContent = content; | |
container.appendChild(p); | |
} | |
return container; | |
} | |
function renderArticle(index) { | |
const article = articles[index]; | |
const main = document.getElementById("main"); | |
main.innerHTML = ""; // clear | |
const h2 = document.createElement("h2"); | |
h2.textContent = article.title; | |
main.appendChild(h2); | |
const parsed = highlightAndRender(article.content); | |
main.appendChild(parsed); | |
// Activate Mermaid diagrams | |
mermaid.init(undefined, main.querySelectorAll(".mermaid")); | |
// Update active tab style | |
[...document.querySelectorAll("nav button")].forEach((btn, i) => { | |
btn.classList.toggle("active", i === index); | |
}); | |
// Close mobile menu after selection | |
if (window.innerWidth <= 768) { | |
document.querySelector('nav').classList.remove('open'); | |
} | |
} | |
// Setup sidebar | |
const sidebar = document.getElementById("sidebar"); | |
articles.forEach((article, index) => { | |
const btn = document.createElement("button"); | |
btn.textContent = article.title; | |
btn.onclick = () => renderArticle(index); | |
sidebar.appendChild(btn); | |
}); | |
// Mobile menu toggle | |
document.querySelector('.menu-toggle').addEventListener('click', function() { | |
document.querySelector('nav').classList.toggle('open'); | |
}); | |
// Close menu when clicking outside on mobile | |
document.addEventListener('click', function(event) { | |
const nav = document.querySelector('nav'); | |
const menuToggle = document.querySelector('.menu-toggle'); | |
if (window.innerWidth <= 768 && | |
!nav.contains(event.target) && | |
event.target !== menuToggle && | |
!menuToggle.contains(event.target)) { | |
nav.classList.remove('open'); | |
} | |
}); | |
// Load first article by default | |
renderArticle(0); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment