Skip to content

Instantly share code, notes, and snippets.

@naranyala
Last active July 19, 2025 04:25
Show Gist options
  • Save naranyala/c94120391866468e25050437c75912a3 to your computer and use it in GitHub Desktop.
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!)
<!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