Skip to content

Instantly share code, notes, and snippets.

@rahuldhole
Last active February 7, 2026 20:42
Show Gist options
  • Select an option

  • Save rahuldhole/78c425c1b44302cdfad40865a85b74b4 to your computer and use it in GitHub Desktop.

Select an option

Save rahuldhole/78c425c1b44302cdfad40865a85b74b4 to your computer and use it in GitHub Desktop.
Publish your markdown docs/ folder with a single html file
<!DOCTYPE html>
<!--
DocsViewer
Source: https://gist.github.com/rahuldhole/78c425c1b44302cdfad40865a85b74b4
This file a readymade template to quick & dirty host markdown files.
Usage:
- add your markdown files in some public folder.
- add links of your markdown files in the `sidebarMenu` array.
- add links of your markdown files in the `topBar` array.
-->
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DocsViewer</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/primeicons@7.0.0/primeicons.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.5.1/github-markdown-dark.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css" />
<script>
window.MathJax = {
tex: { inlineMath: [['$', '$'], ['\\(', '\\)']], displayMath: [['$$', '$$']] },
options: { ignoreHtmlClass: 'tex2jax_ignore', processHtmlClass: 'tex2jax_process' }
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked@12.0.1/lib/marked.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.0/dist/mermaid.min.js"></script>
<style>
body { opacity: 0; transition: opacity 0.2s; background-color: #0d1117; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
.markdown-body { background: transparent !important; font-family: inherit; line-height: 1.6; }
.mermaid { background: #161b22; padding: 1.5rem; border-radius: 12px; display: flex; justify-content: center; margin: 1.5rem 0; border: 1px solid #30363d; overflow-x: auto; }
.markdown-body pre { background-color: #1a1b26 !important; border: 1px solid #30363d; border-radius: 12px; position: relative; }
.mdiv { background: rgba(56, 189, 248, 0.1); border-left: 4px solid #38bdf8; padding: 1rem 1.5rem; margin: 1.5rem 0; border-radius: 0.5rem; color: #e2e8f0; font-size: 0.95em; }
.mdiv::before { content: attr(data-label); display: block; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; color: #38bdf8; margin-bottom: 0.5rem; opacity: 0.8; }
.submenu-grid { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.2s ease-out; }
.submenu-grid.open { grid-template-rows: 1fr; }
.submenu-content { overflow: hidden; }
.chevron { transition: transform 0.2s ease; }
.chevron.rotate { transform: rotate(90deg); }
/* Scrollbar styling */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #30363d; border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: #58a6ff; }
.loader { border: 2px solid #30363d; border-top: 2px solid #58a6ff; border-radius: 50%; width: 20px; height: 20px; animation: spin 1s linear infinite; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
/* TOC Specific Animations */
.toc-item-enter-active, .toc-item-leave-active { transition: all 0.2s ease; max-height: 500px; opacity: 1; overflow: hidden; }
.toc-item-enter-from, .toc-item-leave-to { max-height: 0; opacity: 0; }
</style>
<script type="importmap">
{ "imports": { "petite-vue": "https://unpkg.com/petite-vue@0.4.1/dist/petite-vue.es.js" } }
</script>
</head>
<body @vue:mounted="init" class="flex flex-col h-screen text-gray-300 overflow-hidden bg-[#0d1117]">
<!-- Header -->
<header class="h-14 bg-[#161b22]/95 backdrop-blur-md border-b border-[#30363d] flex items-center justify-between px-4 sticky top-0 z-50 shrink-0">
<div class="flex items-center gap-3">
<!-- Mobile Menu Toggle -->
<button class="md:hidden text-gray-400 hover:text-white p-1" @click="sidebarOpen = !sidebarOpen" v-if="!isSingleDoc">
<i class="pi pi-bars text-lg"></i>
</button>
<!-- Desktop Sidebar Toggle -->
<button @click="desktopSidebarVisible = !desktopSidebarVisible" v-if="!isSingleDoc" class="hidden md:flex items-center justify-center w-8 h-8 text-gray-400 hover:text-white rounded-md hover:bg-[#1f242c] transition-colors mr-1">
<i class="pi" :class="desktopSidebarVisible ? 'pi-align-left' : 'pi-align-justify'"></i>
</button>
<div class="flex items-center gap-2">
<div class="w-7 h-7 bg-blue-500/10 rounded-lg flex items-center justify-center text-blue-400">
<i class="pi pi-book text-sm"></i>
</div>
<span class="font-bold text-white tracking-tight text-sm sm:text-base">DocsViewer</span>
<span class="text-[10px] text-neutral-500 font-bold uppercase tracking-widest mt-1 hidden sm:block">Publish markdown</span>
</div>
</div>
<!-- Top Navigation -->
<div class="flex items-center gap-4">
<!-- Mobile TOC Toggle -->
<button class="xl:hidden text-gray-400 hover:text-blue-400 p-1" @click="mobileTocOpen = !mobileTocOpen" v-if="toc.length > 0">
<i class="pi pi-list text-lg"></i>
</button>
<nav class="hidden md:flex items-center gap-6 text-sm font-medium">
<a v-for="link in topBar" :href="link.link" class="hover:text-blue-400 transition-colors flex items-center gap-2 text-gray-400">
<i :class="link.icon" v-if="link.icon.includes('pi-')"></i>
<span v-else class="font-bold opacity-70">{{link.icon}}</span>
<span>{{link.text}}</span>
</a>
</nav>
</div>
</header>
<!-- Main Workspace -->
<div class="flex flex-1 overflow-hidden relative">
<!-- Mobile Backdrop for Nav Sidebar -->
<div v-if="sidebarOpen" @click="sidebarOpen = false" class="fixed inset-0 bg-black/60 z-30 md:hidden transition-opacity"></div>
<!-- Left Navigation Sidebar -->
<aside
v-if="!isSingleDoc"
class="bg-[#0d1117] border-r border-[#30363d] flex flex-col absolute md:relative h-full z-40 shadow-2xl md:shadow-none overflow-hidden transition-all duration-300 ease-in-out"
:class="[
sidebarOpen ? 'translate-x-0 w-72' : '-translate-x-full w-72', /* Mobile State */
desktopSidebarVisible ? 'md:translate-x-0 md:w-72' : 'md:w-0 md:border-r-0' /* Desktop State */
]"
>
<div class="p-4 flex flex-col h-full w-72">
<!-- Search Bar -->
<div class="mb-4">
<div class="relative">
<i class="pi pi-search absolute left-3 top-2.5 text-gray-500 text-xs"></i>
<input
v-model="searchQuery"
placeholder="Filter docs..."
class="w-full bg-[#161b22] border border-[#30363d] rounded-md py-1.5 pl-8 pr-3 text-xs text-gray-300 focus:border-blue-500 focus:outline-none placeholder-gray-600 transition-colors"
>
</div>
</div>
<div class="overflow-y-auto flex-1 custom-scrollbar pb-6">
<div class="text-[10px] font-bold text-gray-500 uppercase tracking-[0.2em] mb-3 px-2">Explorer</div>
<div class="space-y-1">
<div v-for="item in filteredSidebarMenu" :key="item.id || item.title">
<!-- Regular Link -->
<button v-if="!item.submenu" @click="loadPage(item.link, item.id)"
:class="currentUrl === item.link ? 'bg-blue-500/10 text-blue-400' : 'text-gray-400 hover:bg-[#1f242c] hover:text-gray-100'"
class="w-full text-left px-3 py-2 rounded-lg text-sm font-medium transition-all flex items-center gap-3">
<i class="pi pi-file text-xs opacity-50"></i>
<span class="truncate" v-html="highlightText(item.title)"></span>
</button>
<!-- Submenu -->
<div v-else>
<button @click="toggleSubmenu(item.title)"
class="w-full text-left px-3 py-2 rounded-lg text-sm font-medium text-gray-400 hover:bg-[#1f242c] hover:text-gray-100 transition-all flex items-center justify-between group">
<div class="flex items-center gap-3">
<i class="pi pi-folder text-xs opacity-50"></i>
<span v-html="highlightText(item.title)"></span>
</div>
<i class="pi pi-chevron-right text-[10px] opacity-40 group-hover:opacity-80 transition-transform chevron"
:class="{ 'rotate': searchQuery || openSubmenus.includes(item.title) }"></i>
</button>
<div class="submenu-grid" :class="{ 'open': searchQuery || openSubmenus.includes(item.title) }">
<div class="submenu-content pl-6 pt-1 space-y-1">
<button v-for="sub in item.submenu" :key="sub.id || sub.title" @click="loadPage(sub.link, sub.id)"
:class="currentUrl === sub.link ? 'text-blue-400' : 'text-gray-500 hover:text-gray-200'"
class="w-full text-left px-3 py-1.5 text-[13px] transition-colors truncate">
<span v-html="highlightText(sub.title)"></span>
</button>
</div>
</div>
</div>
</div>
<div v-if="filteredSidebarMenu.length === 0" class="text-center py-4 text-xs text-gray-600 italic">
No matching documents
</div>
</div>
<!-- Mobile Only Top Bar Links -->
<div class="md:hidden mt-6 pt-6 border-t border-[#30363d] space-y-2">
<div class="text-[10px] font-bold text-gray-500 uppercase tracking-[0.2em] mb-2 px-2">Links</div>
<a v-for="link in topBar" :href="link.link" class="block px-3 py-2 text-sm text-gray-400 hover:text-white rounded-lg hover:bg-[#1f242c] transition-colors flex items-center gap-3">
<i :class="link.icon" v-if="link.icon.includes('pi-')"></i>
<span v-else class="font-bold opacity-70">{{link.icon}}</span>
<span>{{link.text}}</span>
</a>
</div>
</div>
</div>
</aside>
<!-- Center Content & Right TOC Wrapper -->
<div class="flex flex-1 overflow-hidden relative">
<!-- Main Content Scroll Area -->
<main id="main-scroll" class="flex-1 overflow-y-auto bg-[#0d1117] scroll-smooth relative">
<div class="max-w-4xl mx-auto px-6 py-10 md:px-12 md:py-16 min-h-full">
<div v-if="loading" class="flex flex-col items-center justify-center py-32 opacity-60">
<div class="loader mb-4"></div>
<p class="text-xs font-mono tracking-widest uppercase">Fetching</p>
</div>
<div v-else-if="error" class="p-8 rounded-2xl border border-red-500/20 bg-red-500/5 text-center mt-10">
<i class="pi pi-exclamation-circle text-3xl text-red-400/50 mb-4"></i>
<h3 class="font-bold text-red-400">Unable to load document</h3>
<p class="text-sm text-gray-500 mt-2">{{error}}</p>
<button @click="loadPage(currentUrl)" class="mt-6 text-xs bg-red-500/10 hover:bg-red-500/20 text-red-400 px-4 py-2 rounded-full transition-colors">Retry Connection</button>
</div>
<div v-show="!loading && !error" id="content" class="markdown-body pb-20"></div>
</div>
</main>
<!-- Right TOC Sidebar (Desktop) -->
<aside
v-if="toc.length > 0"
class="hidden xl:block w-72 border-l border-[#30363d] bg-[#0d1117]/50 backdrop-blur-sm overflow-y-auto shrink-0"
>
<div class="p-6 fixed w-72 top-14 bottom-0 overflow-y-auto custom-scrollbar">
<h5 class="text-xs font-bold text-gray-500 uppercase tracking-widest mb-4">On this page</h5>
<!-- Recursive TOC Render -->
<ul class="space-y-1">
<li v-for="item in toc" :key="item.id" class="relative">
<div class="flex items-start group">
<!-- Expand/Collapse Chevron for Parents -->
<button
v-if="item.children && item.children.length > 0"
@click.stop="item.expanded = !item.expanded"
class="mt-1 mr-1 p-0.5 rounded hover:bg-gray-800 text-gray-500 transition-colors"
>
<i class="pi pi-chevron-right text-[10px] block transition-transform duration-200" :class="{ 'rotate-90': item.expanded }"></i>
</button>
<!-- Spacer if no children to align text -->
<div v-else class="w-4 mr-1"></div>
<a
:href="'#' + item.id"
@click.prevent="scrollToId(item.id)"
class="text-sm py-0.5 block transition-colors leading-snug hover:text-gray-200"
:class="activeTocId === item.id ? 'text-blue-400 font-medium' : 'text-gray-400'"
>
{{ item.text }}
</a>
</div>
<!-- Children (2nd Level) -->
<div v-if="item.children && item.children.length > 0"
class="grid transition-[grid-template-rows] duration-200 ease-out ml-2 border-l border-gray-800"
:class="item.expanded ? 'grid-rows-[1fr] mt-1 mb-2' : 'grid-rows-[0fr]'">
<ul class="overflow-hidden">
<li v-for="child in item.children" :key="child.id" class="pl-4 py-1">
<a
:href="'#' + child.id"
@click.prevent="scrollToId(child.id)"
class="text-xs block transition-colors hover:text-gray-300 truncate"
:class="activeTocId === child.id ? 'text-blue-400' : 'text-gray-500'"
>
{{ child.text }}
</a>
</li>
</ul>
</div>
</li>
</ul>
</div>
</aside>
<!-- Mobile TOC Drawer -->
<div v-if="mobileTocOpen" class="xl:hidden absolute inset-0 z-50 flex justify-end">
<div class="absolute inset-0 bg-black/60" @click="mobileTocOpen = false"></div>
<div class="w-72 bg-[#161b22] h-full shadow-2xl relative flex flex-col border-l border-[#30363d] animate-slide-in">
<div class="p-4 border-b border-[#30363d] flex justify-between items-center">
<span class="text-sm font-bold text-gray-300">Table of Contents</span>
<button @click="mobileTocOpen = false" class="text-gray-400 hover:text-white">
<i class="pi pi-times"></i>
</button>
</div>
<div class="p-4 overflow-y-auto flex-1">
<ul class="space-y-3">
<li v-for="item in toc" :key="item.id">
<a :href="'#' + item.id" @click="mobileTocOpen = false; scrollToId(item.id)"
class="block text-sm font-medium"
:class="activeTocId === item.id ? 'text-blue-400' : 'text-gray-400'">
{{ item.text }}
</a>
<ul v-if="item.children && item.children.length" class="mt-2 ml-4 space-y-2 border-l border-gray-700 pl-3">
<li v-for="child in item.children" :key="child.id">
<a :href="'#' + child.id" @click="mobileTocOpen = false; scrollToId(child.id)"
class="block text-xs"
:class="activeTocId === child.id ? 'text-blue-400' : 'text-gray-500'">
{{ child.text }}
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script type="module">
import { createApp } from 'petite-vue';
createApp({
// UI States
sidebarOpen: false,
desktopSidebarVisible: true,
mobileTocOpen: false,
// Data States
loading: false,
error: null,
currentUrl: '',
openSubmenus: [],
searchQuery: '',
// TOC States
toc: [],
activeTocId: '',
observer: null,
topBar: [
{ text: "Repository", icon: "pi pi-github", link: "#" },
{ text: "Discord", icon: "pi pi-discord", link: "#" },
{ text: "v1.3", icon: "📦", link: "#" }
],
sidebarMenu: [
{
title: "Introduction",
id: "introduction",
link: "https://gist.githubusercontent.com/rahuldhole/35450c3fc2c5b63ec9ad9745d6bc918a/raw/00ec3d91ac9222832a9929c9b902cd75ec13667f/markdown-sample-fully-featured.md"
},
{
title: "Guides",
submenu: [
{
title: "Basics",
id: "basics",
link: "https://gist.githubusercontent.com/rahuldhole/35450c3fc2c5b63ec9ad9745d6bc918a/raw/5ffe4ec7c6d84cf35df0822635239cd8771e14c5/markdown-sample-1.md"
},
{
title: "Advanced",
id: "advanced",
link: "https://gist.githubusercontent.com/rahuldhole/35450c3fc2c5b63ec9ad9745d6bc918a/raw/5ffe4ec7c6d84cf35df0822635239cd8771e14c5/markdown-sample-2.md"
}
]
}
],
get isSingleDoc() {
if (!this.sidebarMenu || this.sidebarMenu.length === 0) return false;
if (this.sidebarMenu.length > 1) return false;
return !this.sidebarMenu[0].submenu;
},
get filteredSidebarMenu() {
if (!this.searchQuery) return this.sidebarMenu;
const q = this.searchQuery.toLowerCase();
return this.sidebarMenu.map(item => {
let filteredSub = [];
if (item.submenu) {
filteredSub = item.submenu.filter(s => s.title.toLowerCase().includes(q));
}
const matchesParent = item.title.toLowerCase().includes(q);
if (matchesParent) return item;
if (filteredSub.length > 0) return { ...item, submenu: filteredSub };
return null;
}).filter(Boolean);
},
init() {
requestAnimationFrame(() => {
mermaid.initialize({ startOnLoad: false, theme: 'dark', securityLevel: 'loose' });
marked.setOptions({ gfm: true, breaks: true });
this.checkHashAndLoad();
document.body.style.opacity = '1';
});
},
// --- TOC Logic ---
generateToc() {
const content = document.getElementById('content');
if (!content) return;
const headers = Array.from(content.querySelectorAll('h1, h2, h3'));
const tocData = [];
let currentParent = null;
headers.forEach((h, index) => {
// Ensure ID
if (!h.id) {
h.id = 'heading-' + index + '-' + h.innerText.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
}
const level = parseInt(h.tagName.substring(1));
const item = {
id: h.id,
text: h.innerText,
level,
children: [],
expanded: true
};
// Simple Logic:
// H1 and H2 are top level. H3 is child of previous H2.
if (level <= 2) {
tocData.push(item);
currentParent = item;
} else if (currentParent) {
currentParent.children.push(item);
} else {
// Orphan H3 (starts doc with H3), treat as top level
tocData.push(item);
currentParent = item;
}
});
this.toc = tocData;
this.setupIntersectionObserver();
},
setupIntersectionObserver() {
if (this.observer) this.observer.disconnect();
const options = {
root: document.getElementById('main-scroll'),
rootMargin: '-80px 0px -80% 0px', // Active when element is near top
threshold: 0
};
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.activeTocId = entry.target.id;
// Auto expand parent if child is active
this.toc.forEach(parent => {
if (parent.children.some(child => child.id === this.activeTocId)) {
// Optional: parent.expanded = true;
}
});
}
});
}, options);
const content = document.getElementById('content');
content.querySelectorAll('h1, h2, h3').forEach(h => {
this.observer.observe(h);
});
},
scrollToId(id) {
const el = document.getElementById(id);
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
// Update hash without jump
history.pushState(null, null, '#' + id);
this.activeTocId = id;
}
},
// --- Navigation & Content Loading ---
findPathToId(items, id, path = []) {
for (const item of items) {
if (item.id === id) return [...path, item];
if (item.submenu) {
const found = this.findPathToId(item.submenu, id, [...path, item.title]);
if (found) return found;
}
}
return null;
},
findFirstItem(items) {
for (const item of items) {
if (item.link) return item;
if (item.submenu) {
const found = this.findFirstItem(item.submenu);
if (found) return found;
}
}
return null;
},
checkHashAndLoad() {
const hash = window.location.hash.slice(1);
// Check if hash matches a sidebar ID first
let targetItem = null;
if (hash) {
const path = this.findPathToId(this.sidebarMenu, hash);
if (path) {
path.slice(0, -1).forEach(p => {
if (typeof p === 'string' && !this.openSubmenus.includes(p)) this.openSubmenus.push(p);
});
targetItem = path[path.length - 1];
}
}
if (!targetItem) targetItem = this.findFirstItem(this.sidebarMenu);
if (targetItem) this.loadPage(targetItem.link, targetItem.id);
},
highlightText(text) {
if (!this.searchQuery) return text;
const regex = new RegExp(`(${this.searchQuery})`, 'gi');
return text.replace(regex, '<span class="text-blue-400 font-bold">$1</span>');
},
toggleSubmenu(title) {
if(this.searchQuery) return;
const index = this.openSubmenus.indexOf(title);
if (index > -1) this.openSubmenus.splice(index, 1);
else this.openSubmenus.push(title);
},
async loadPage(url, id) {
if (!url || url === '#') return;
this.currentUrl = url;
// If clicking a new page but preserving hash might be tricky, reset hash unless it's a deep link
if (id && !window.location.hash.includes(id)) {
// Only push state if meaningful change
}
this.loading = true;
this.error = null;
this.sidebarOpen = false;
this.mobileTocOpen = false;
this.toc = []; // Clear TOC while loading
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`Status: ${res.status}`);
const rawMd = await res.text();
this.renderContent(rawMd);
// Generate TOC after render
this.$nextTick(() => {
this.generateToc();
// Handle scroll to hash if deep link exists (e.g. #heading-id)
const hash = window.location.hash.slice(1);
if (hash) {
setTimeout(() => {
const el = document.getElementById(hash);
if (el) el.scrollIntoView();
}, 100);
}
});
} catch (e) {
this.error = e.message;
} finally {
this.loading = false;
}
},
renderContent(rawMd) {
const processed = rawMd.replace(
/(<mdiv[\s\S]*?<\/mdiv>)/gi,
(match, capture) => capture.replace(/^<mdiv/, `<div class="mdiv"`).replace(/<\/mdiv>$/, '</div>')
);
const el = document.getElementById("content");
if (!el) return;
el.innerHTML = marked.parse(processed);
el.querySelectorAll('pre').forEach(pre => {
const code = pre.querySelector('code');
if (!code) return;
const isMermaid = code.classList.contains('language-mermaid');
if (isMermaid) {
const diagramReq = code.textContent;
pre.innerHTML = '';
const mermaidDiv = document.createElement('div');
mermaidDiv.className = 'mermaid';
mermaidDiv.textContent = diagramReq;
pre.appendChild(mermaidDiv);
pre.style.background = 'transparent';
pre.style.border = 'none';
} else {
hljs.highlightElement(code);
const btn = document.createElement('button');
btn.className = 'absolute top-3 right-3 text-[10px] font-bold uppercase tracking-wider text-gray-500 hover:text-white bg-[#0d1117] border border-[#30363d] px-2 py-1 rounded opacity-0 transition-opacity z-10 cursor-pointer';
btn.innerText = 'Copy';
btn.onclick = (e) => {
e.stopPropagation();
navigator.clipboard.writeText(code.innerText);
btn.innerText = 'Done';
setTimeout(() => btn.innerText = 'Copy', 2000);
};
pre.addEventListener('mouseenter', () => btn.classList.remove('opacity-0'));
pre.addEventListener('mouseleave', () => btn.classList.add('opacity-0'));
pre.appendChild(btn);
}
});
const diagrams = el.querySelectorAll('.mermaid');
if (diagrams.length) mermaid.run({ nodes: diagrams });
if (window.MathJax) MathJax.typesetPromise([el]);
}
}).mount();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Documentation">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<!-- Preload critical resources -->
<link rel="preconnect" href="https://cdn.jsdelivr.net">
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
<!-- Critical inline styles FIRST to prevent FOUC -->
<style>
/* Prevent FOUC: Set background immediately */
html,
body {
background-color: #0d1117;
color: #c9d1d9;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}
/* Hide app until content is injected by Docsify */
#app:empty {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
#app:empty::after {
content: 'Loading documentation...';
color: #8b949e;
font-size: 14px;
}
:root {
/* Base Colors - Matching GitHub Dark Dimmed */
--base-background-color: #0d1117;
--base-color: #c9d1d9;
--base-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
/* Sidebar */
--sidebar-background-color: #0d1117;
--sidebar-border-width: 0;
--sidebar-border-color: #30363d;
--sidebar-nav-link-color: #8b949e;
--sidebar-nav-link-color--active: #58a6ff;
--sidebar-nav-link-font-weight--active: bold;
--sidebar-width: 18rem;
/* Content */
--content-max-width: 60em;
/* Code Blocks */
--code-block-background-color: #161b22;
--code-inline-background-color: #161b22;
--code-inline-color: #e2e8f0;
/* Links */
--link-color: #58a6ff;
--link-text-decoration: none;
--link-text-decoration--hover: underline;
/* Header */
--heading-h2-border-style: none;
/* Blockquote & Callouts */
--blockquote-border-color: #38bdf8;
--blockquote-background-color: rgba(56, 189, 248, 0.1);
--blockquote-color: #e2e8f0;
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #30363d;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: #58a6ff;
}
/* Mermaid Centering */
.mermaid {
display: flex;
justify-content: center;
background: #161b22;
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
}
</style>
<!-- External stylesheets loaded AFTER critical inline styles -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.css">
</head>
<body>
<nav style="display: flex; justify-content: space-between; align-items: center;">
<a href="/" style="color: #c9d1d9;">
<img src="" alt="Logo" style="width: 20px; height: 20px; object-fit: contain; filter: invert(1);">
Home
</a>
<a href="#" style="color: #c9d1d9;">
<img src="https://www.svgrepo.com/show/354420/swagger.svg" height="20"> OpenAPI
</a>
</nav>
<div id="app"></div>
<script>
window.$docsify = {
name: 'Docs',
repo: 'https://github.com/rahuldhole/repo',
homepage: 'html-custom-hidden-markdown.md',
loadSidebar: true,
subMaxLevel: 3,
auto2top: true,
basePath: '/docs/',
// Search Plugin Config
search: {
maxAge: 86400000,
paths: 'auto',
placeholder: 'Search...',
noData: 'No Results!',
depth: 6,
hideOtherSidebarContent: false,
},
// Pagination Plugin
pagination: {
previousText: 'Previous',
nextText: 'Next',
crossChapter: true,
},
// Copy Code Plugin
copyCode: {
buttonText: 'Copy',
errorText: 'Error',
successText: 'Copied'
},
// Mermaid Config
mermaidConfig: {
querySelector: ".mermaid",
startOnLoad: false
},
// Tabs
tabs: {
persist: true,
sync: true,
theme: 'classic',
tabComments: true,
tabHeadings: true
}
}
</script>
<!-- Docsify v4 -->
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
<!-- Plugins -->
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-pagination/dist/docsify-pagination.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1/dist/docsify-tabs.min.js"></script>
<!-- Mermaid Support -->
<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
mermaid.initialize({ startOnLoad: false, theme: 'dark' });
window.mermaid = mermaid;
</script>
<script src="https://cdn.jsdelivr.net/npm/docsify-mermaid@2/dist/docsify-mermaid.min.js"></script>
<!-- Vue Support -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
</script>
</body>
</html>

Custom Hidden HTML

Render html which is hidden/commented from markdown renderers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment