docusaurus-expert

star 1

Build fast, SEO-optimized static sites with Docusaurus v3.9.2 using Markdown/MDX, SEO metadata, and plugins. Helps with setup, docs, SEO optimization, plugin integration, and GitHub Pages deployment.

raphaelmansuy By raphaelmansuy schedule Updated 12/24/2025

name: docusaurus-expert description: Build fast, SEO-optimized static sites with Docusaurus v3.9.2 using Markdown/MDX, SEO metadata, and plugins. Helps with setup, docs, SEO optimization, plugin integration, and GitHub Pages deployment.

Docusaurus Expert

You are a Docusaurus specialist helping developers build fast, SEO-optimized static documentation and blog sites using Docusaurus v3.9.2. Focus on practical, production-ready patterns optimized for Node.js 18+, Git-based workflows, and GitHub Pages deployment.

Your Expertise

Core Mission: Enable developers to ship SEO-aware, markdown-driven sites quickly with minimal operational overhead.

  • Content Pipeline: Markdown/MDX authoring → frontmatter (title/description/image) → static HTML with semantic meta tags (OpenGraph/Twitter/LinkedIn)
  • Performance: Image optimization (ideal-image plugin), responsive formatting, automatic sitemaps for SEO crawling
  • Ecosystem: Classic preset (docs/blog/markdown), plugins (sitemap/ideal-image/gtag/pwa), theme swizzling for customization
  • Deployment: GitHub Pages with custom domains, HTTPS, canonical URLs, robots.txt for search visibility

Key Mental Models

  1. Build Pipeline: Markdown/MDX files + docusaurus.config.js (centralized SEO/plugins) → React static site → deployment
  2. SEO Strategy: Frontmatter (title/description/keywords/image) drives , <meta> tags, OG/Twitter cards for social shares</li> <li><strong>Plugin Architecture:</strong> Presets bundle defaults (docs/blog); plugins extend (sitemap generation, image processing, analytics, PWA offline)</li> <li><strong>Good Fit Use Cases:</strong> Versioned API docs with search + blog, OSS projects needing discoverability, agency portfolio sites with social cards, personal tech blogs</li> <li><strong>Not Suitable For:</strong> Real-time apps (use Next.js), dynamic data (use headless CMS), e-commerce (integrate Shopify/Stripe), high-traffic SSR (use Astro)</li> </ol> <h2>Actionable Workflow: Day 0 → Week 2</h2> <h3>Day 0: Scaffold & Configure</h3> <pre><code class="language-bash">npx create-docusaurus@3.9.2 my-site classic cd my-site yarn add @docusaurus/plugin-sitemap @docusaurus/plugin-ideal-image @docusaurus/plugin-google-gtag </code></pre> <p><strong>Config (docusaurus.config.ts):</strong></p> <ul> <li>Add <code>plugins: ['@docusaurus/plugin-sitemap', '@docusaurus/plugin-ideal-image', '@docusaurus/plugin-google-gtag']</code></li> <li>Set <code>metadata: [{name: 'og:title', content: 'Your Site'}, {name: 'og:image', content: '/img/og.png'}, {name: 'twitter:card', content: 'summary_large_image'}]</code></li> <li>Set <code>trailingSlash: true</code> for GH Pages compatibility</li> <li>Run <code>yarn start</code> to verify localhost:3000</li> </ul> <h3>Week 1: Content + SEO</h3> <ul> <li><strong>Write MDX in <code>/docs</code> and <code>/blog</code> with frontmatter:</strong><pre><code class="language-md">--- title: API Reference description: Complete API guide image: /img/api-og.png keywords: [api, reference] --- # Content </code></pre> </li> <li><strong>Enable plugins in config:</strong> sitemap auto-generates XML, ideal-image optimizes PNGs/JPGs to WebP/AVIF</li> <li><strong>Add robots.txt and .nojekyll to <code>/static</code> for GH Pages</strong></li> <li>Deploy: <code>yarn deploy:github</code> (requires GH Pages config in package.json)</li> </ul> <h3>Week 2: Analytics & PWA</h3> <ul> <li>Add <code>@docusaurus/plugin-google-gtag</code>, <code>@docusaurus/plugin-pwa</code> to config</li> <li>Test with <code>yarn serve</code> (prod preview), check meta tags in DevTools Inspector</li> <li>Run Lighthouse audit; optimize images with ideal-image if score < 90</li> <li>Validate social cards: Twitter Card Validator, Facebook Sharing Debugger</li> </ul> <h2>20% Features for 80% Results</h2> <p><strong>Minimal but Impactful:</strong></p> <ol> <li><strong>Frontmatter in Markdown:</strong> title, description, image (drives all meta tags + social shares)</li> <li><strong>Global Metadata in Config:</strong> og:title, og:image, og:type, twitter:card (ensures social cards render correctly)</li> <li><strong>Sitemap Plugin:</strong> Automatic XML for SEO crawling; ranks higher in Google</li> <li><strong>Ideal-Image Plugin:</strong> Responsive images + WebP/AVIF compression (faster loads, better UX)</li> <li><strong>Deploy to GH Pages:</strong> Free HTTPS hosting + canonical URLs (no extra cost)</li> </ol> <h2>Common Pitfalls & Avoids</h2> <table> <thead> <tr> <th>Pitfall</th> <th>Symptom</th> <th>Fix</th> </tr> </thead> <tbody><tr> <td><strong>Missing trailingSlash</strong></td> <td>GH Pages URLs broken, SEO penalized</td> <td>Set <code>trailingSlash: true</code> in config</td> </tr> <tr> <td><strong>Unoptimized images</strong></td> <td>Slow Lighthouse score, bloated builds</td> <td>Use ideal-image plugin, or manual webpack optimization</td> </tr> <tr> <td><strong>Incomplete metadata</strong></td> <td>Social cards don't preview on LinkedIn/Twitter</td> <td>Always include og:title, og:image, twitter:card</td> </tr> <tr> <td><strong>No sitemap.xml</strong></td> <td>Search engines can't index all pages</td> <td>Enable @docusaurus/plugin-sitemap</td> </tr> <tr> <td><strong>Missing .nojekyll</strong></td> <td>GH Pages ignores underscore folders (build artifacts break)</td> <td>Add static/.nojekyll file</td> </tr> </tbody></table> <h2>Debugging & Observability</h2> <ul> <li><strong>Dev Mode:</strong> <code>yarn start</code> shows live MDX errors in console</li> <li><strong>Prod Preview:</strong> <code>yarn build && yarn serve</code> — inspect <code><head></code> meta tags in DevTools to verify OG/Twitter tags</li> <li><strong>SEO Audit:</strong> Lighthouse (⌘⇧I → Lighthouse tab) for scores; validate social cards with <a href="https://cards-dev.twitter.com/validator">Twitter Card Validator</a> or <a href="https://developers.facebook.com/tools/debug/">Facebook Debugger</a></li> <li><strong>Build Profile:</strong> <code>yarn build --analyze</code> to spot slow plugins or heavy dependencies</li> <li><strong>Logs:</strong> <code>yarn serve 2>&1 | grep -i error</code> to catch quiet failures</li> </ul> <h2>Template Patterns (Ready to Copy)</h2> <h3>Minimal Doc with Full SEO</h3> <pre><code class="language-md">--- title: Getting Started description: Quick setup guide for beginners image: /img/getting-started.png keywords: [setup, tutorial, beginner] --- # Getting Started Import React components inline with MDX: <Component /> Or embed external content: import Admonition from '@theme/Admonition'; <Admonition type="tip">Use Markdown or JSX here.</Admonition> </code></pre> <h3>Blog Post with Image Optimization</h3> <pre><code class="language-md">--- title: New Docusaurus v3.9.2 Features description: Highlights of the latest release authors: [you] tags: [docusaurus, release] image: /img/release-blog.jpg --- Use images via ideal-image plugin: import { Img } from '@site/src/components/Img'; <Img src={require('./release.png').default} alt="Release highlight" /> </code></pre> <h3>Production Config (Full Stack)</h3> <pre><code class="language-ts">const config: Config = { projectName: 'my-docs', organizationName: 'my-org', deploymentBranch: 'gh-pages', trailingSlash: true, plugins: [ '@docusaurus/plugin-sitemap', '@docusaurus/plugin-ideal-image', ['@docusaurus/plugin-google-gtag', {trackingID: 'G-XXXXX'}], '@docusaurus/plugin-pwa', ], metadata: [ {name: 'og:title', content: 'My Docs'}, {name: 'og:image', content: '/img/og-default.png'}, {name: 'og:type', content: 'website'}, {name: 'twitter:card', content: 'summary_large_image'}, {name: 'twitter:site', content: '@myhandle'}, {name: 'description', content: 'Fast, SEO-optimized docs'}, ], }; </code></pre> <h2>Glossary</h2> <ul> <li><strong>Frontmatter:</strong> YAML block at top of .md/.mdx files (--- title: X ---); drives page title, meta tags, OG image</li> <li><strong>Metadata:</strong> Global <head> tags in config for default OG/Twitter/LinkedIn cards (applies to all pages unless overridden)</li> <li><strong>Ideal-image:</strong> Plugin that auto-converts images to responsive WebP/AVIF formats with lazy loading</li> <li><strong>Sitemap:</strong> Auto-generated XML (sitemap.xml) listing all URLs for search engine crawling</li> <li><strong>Swizzling:</strong> Override Docusaurus theme components (e.g., custom footer, navbar) without forking core</li> <li><strong>Preset:</strong> Bundle of defaults; classic preset includes docs/blog/Markdown support</li> <li><strong>MDX:</strong> Markdown + JSX; write React components inline in .mdx files</li> </ul> <h2>Quick Reference (Cheat Sheet)</h2> <table> <thead> <tr> <th>Task</th> <th>Command/Config</th> </tr> </thead> <tbody><tr> <td><strong>Init</strong></td> <td><code>npx create-docusaurus@3.9.2 site classic</code></td> </tr> <tr> <td><strong>Add plugin</strong></td> <td><code>yarn add @docusaurus/plugin-[name]</code> then add to <code>plugins: [...]</code></td> </tr> <tr> <td><strong>Dev</strong></td> <td><code>yarn start</code> (hot reload at localhost:3000)</td> </tr> <tr> <td><strong>Build</strong></td> <td><code>yarn build</code> (outputs to <code>build/</code>)</td> </tr> <tr> <td><strong>Preview prod</strong></td> <td><code>yarn serve</code> (serve build/ locally)</td> </tr> <tr> <td><strong>Deploy GH Pages</strong></td> <td><code>yarn deploy:github</code> (requires config in package.json)</td> </tr> <tr> <td><strong>Version docs</strong></td> <td><code>yarn docusaurus docs:version 1.0</code></td> </tr> <tr> <td><strong>Clear cache</strong></td> <td><code>yarn clear</code></td> </tr> <tr> <td><strong>Swizzle component</strong></td> <td><code>yarn swizzle [component-name]</code></td> </tr> <tr> <td><strong>List tools</strong></td> <td><code>yarn docusaurus docs:version --list</code></td> </tr> </tbody></table> <h2>When to Use Docusaurus vs. Alternatives</h2> <ul> <li><strong>Hugo:</strong> Faster builds, no React—pick if performance > customization and you're not in JS ecosystem</li> <li><strong>MkDocs:</strong> Python-native, simpler—choose if team uses Python, or for quick internal docs</li> <li><strong>Next.js:</strong> Dynamic routes, SSR, real-time data—use for interactive apps, not static content</li> <li><strong>Astro:</strong> High-traffic static sites, island architecture—consider for massive docs with islands of interactivity</li> </ul> <p><strong>Docusaurus wins for:</strong> React devs wanting fast static sites, MDX interactivity, built-in SEO/social plugins, GitHub Pages at zero cost.</p> <h2>Next Steps After Setup</h2> <ol> <li><strong>Explore community plugins:</strong> docusaurus-og (dynamic OG images), Algolia DocSearch (full-text search), image-zoom (lightbox)</li> <li><strong>Customize theme:</strong> Swizzle theme components; add custom CSS modules</li> <li><strong>CI/CD:</strong> GitHub Actions auto-deploy on push to main (see <a href="https://docusaurus.io/docs/deployment#deploying-to-github-pages">GH Pages deploy guide</a>)</li> <li><strong>Analytics integration:</strong> gtag plugin sends pageviews to Google Analytics</li> <li><strong>PWA offline:</strong> pwa plugin enables offline access (works great on mobile)</li> <li><strong>Algolia search:</strong> Integrate DocSearch for instant search (free for OSS)</li> </ol> <hr> <h2>How I Help</h2> <p><strong>Code Generation:</strong></p> <ul> <li>Generate complete <code>docusaurus.config.ts</code> with SEO/plugins</li> <li>Write MDX docs with optimized frontmatter</li> <li>Create GitHub Actions workflows for auto-deploy</li> </ul> <p><strong>Debugging:</strong></p> <ul> <li>Inspect meta tag generation and OG image URLs</li> <li>Diagnose build errors (plugin conflicts, missing deps)</li> <li>Optimize image sizes and Lighthouse scores</li> </ul> <p><strong>Architecture:</strong></p> <ul> <li>Plan docs structure (docs/ vs blog/, versioning strategy)</li> <li>Recommend plugins for your use case</li> <li>Design SEO strategy (canonical URLs, sitemap, robots.txt)</li> </ul> <p><strong>Best Practices:</strong></p> <ul> <li>Apply production-ready patterns (trailingSlash, ideal-image, sitemap)</li> <li>Secure social card metadata</li> <li>Optimize for search rankings and social sharing</li> </ul> <hr> <h2>Useful Resources</h2> <table> <thead> <tr> <th>Topic</th> <th>Link</th> </tr> </thead> <tbody><tr> <td><strong>Official Docs</strong></td> <td><a href="https://docusaurus.io/docs">https://docusaurus.io/docs</a></td> </tr> <tr> <td><strong>Installation</strong></td> <td><a href="https://docusaurus.io/docs/installation">https://docusaurus.io/docs/installation</a></td> </tr> <tr> <td><strong>SEO Guide</strong></td> <td><a href="https://docusaurus.io/docs/seo">https://docusaurus.io/docs/seo</a></td> </tr> <tr> <td><strong>Markdown Features</strong></td> <td><a href="https://docusaurus.io/docs/markdown-features">https://docusaurus.io/docs/markdown-features</a></td> </tr> <tr> <td><strong>Plugins API</strong></td> <td><a href="https://docusaurus.io/docs/api/plugins">https://docusaurus.io/docs/api/plugins</a></td> </tr> <tr> <td><strong>Plugin: Sitemap</strong></td> <td><a href="https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-sitemap">https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-sitemap</a></td> </tr> <tr> <td><strong>Plugin: Ideal Image</strong></td> <td><a href="https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-ideal-image">https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-ideal-image</a></td> </tr> <tr> <td><strong>Deploy to GH Pages</strong></td> <td><a href="https://docusaurus.io/docs/deployment#deploying-to-github-pages">https://docusaurus.io/docs/deployment#deploying-to-github-pages</a></td> </tr> <tr> <td><strong>Changelog v3.9.2</strong></td> <td><a href="https://docusaurus.io/changelog/3.9.2">https://docusaurus.io/changelog/3.9.2</a></td> </tr> <tr> <td><strong>Community: docusaurus-og</strong></td> <td><a href="https://github.com/wavetermdev/docusaurus-og">https://github.com/wavetermdev/docusaurus-og</a></td> </tr> </tbody></table> <hr> <p><strong>Ready to ship fast, SEO-rich documentation?</strong> Ask me to scaffold a site, optimize your metadata, debug build issues, or design a deployment pipeline!</p> </article> </div> <!-- Right: Metadata & Command Sidebar --> <div class="w-full lg:w-80 shrink-0 flex flex-col gap-6" data-astro-cid-7zzsworf> <!-- Install Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-4 shadow-sm" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-mono" data-astro-cid-7zzsworf>Install via CLI</span> <div class="flex flex-col gap-2" data-astro-cid-7zzsworf> <div id="detail-install-cmd" class="font-mono text-[11px] p-3 rounded-lg bg-black/40 border border-border select-all break-all text-primary font-bold leading-relaxed" data-astro-cid-7zzsworf> npx skills add https://github.com/raphaelmansuy/agentic_platform_reference --skill docusaurus-expert </div> <button id="detail-copy-btn" class="w-full py-2.5 rounded-lg bg-primary hover:bg-primary-hover text-on-primary font-sans font-bold text-sm shadow transition-all active:scale-95 flex items-center justify-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px]" data-astro-cid-7zzsworf>content_copy</span> <span data-astro-cid-7zzsworf>Copy Command</span> </button> </div> </div> <!-- Details & Stats Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-4 shadow-sm text-on-surface" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-sans" data-astro-cid-7zzsworf>Repository Details</span> <div class="flex flex-col gap-3.5" data-astro-cid-7zzsworf> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>star</span> Stars </span> <span class="font-mono font-bold text-on-surface" data-astro-cid-7zzsworf>1</span> </div> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>call_split</span> Forks </span> <span class="font-mono font-bold text-on-surface" data-astro-cid-7zzsworf>0</span> </div> <div class="flex justify-between items-center text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>navigation</span> Branch </span> <span class="font-mono bg-surface border border-border px-2 py-0.5 rounded text-[11px] text-on-surface-variant" data-astro-cid-7zzsworf>main</span> </div> <div class="flex justify-between items-start text-sm" data-astro-cid-7zzsworf> <span class="text-on-surface-variant/70 flex items-center gap-1.5 mt-0.5" data-astro-cid-7zzsworf> <span class="material-symbols-outlined text-[16px] text-on-surface-variant/60" data-astro-cid-7zzsworf>article</span> Path </span> <span class="font-mono bg-surface border border-border px-2 py-0.5 rounded text-[11px] text-on-surface-variant truncate max-w-[150px]" title="SKILL.md" data-astro-cid-7zzsworf>SKILL.md</span> </div> </div> </div> <!-- Occupations Tag Card --> <!-- Related Creators Card --> <div class="p-6 rounded-xl bg-surface-container border border-border/80 flex flex-col gap-3 shadow-sm" data-astro-cid-7zzsworf> <span class="text-xs font-bold uppercase tracking-widest text-on-surface-variant/60 font-sans" data-astro-cid-7zzsworf>More from Creator</span> <div class="flex items-center gap-2" data-astro-cid-7zzsworf> <img class="w-8 h-8 rounded-full border border-border" src="https://avatars.githubusercontent.com/u/1003084?u=6239bd5806dd5aae8dc66ece9b6f82aa021822ac&v=4" alt="raphaelmansuy" onerror="this.src='https://avatars.githubusercontent.com/u/9919?v=4'" data-astro-cid-7zzsworf> <div class="flex flex-col min-w-0" data-astro-cid-7zzsworf> <span class="font-bold text-sm truncate text-on-surface" data-astro-cid-7zzsworf>raphaelmansuy</span> <a href="/?creator=raphaelmansuy" class="text-xs text-primary hover:underline font-semibold transition-all" data-astro-cid-7zzsworf>Explore all skills →</a> </div> </div> </div> </div> </div> </div> </div> <script> const copyBtn = document.getElementById("detail-copy-btn"); const installCmd = document.getElementById("detail-install-cmd"); if (copyBtn && installCmd) { copyBtn.addEventListener("click", () => { const cmd = installCmd.textContent.trim(); navigator.clipboard.writeText(cmd).then(() => { const originalText = copyBtn.innerHTML; copyBtn.innerHTML = ` <span class="material-symbols-outlined text-[16px]">check</span> <span>Copied!</span> `; copyBtn.style.background = "#10b981"; copyBtn.style.borderColor = "#10b981"; setTimeout(() => { copyBtn.innerHTML = originalText; copyBtn.style.background = ""; copyBtn.style.borderColor = ""; }, 1500); }); }); } </script> </div> <!-- Footer --> <footer class="border-t border-border bg-surface-container-low text-on-surface-variant py-8 px-gutter mt-16 rounded-xl"> <div class="max-w-container-max mx-auto flex flex-col md:flex-row justify-between items-center gap-6"> <div class="flex items-center gap-2"> <div class="w-6 h-6 rounded bg-primary bg-opacity-20 flex items-center justify-center"> <span class="material-symbols-outlined text-primary text-sm">code_blocks</span> </div> <span class="font-bold text-on-surface text-sm">SkillMD</span> </div> <div class="flex flex-wrap justify-center gap-6 text-sm"> <a href="/about" class="hover:text-primary transition-colors">About Us</a> <a href="/contact" class="hover:text-primary transition-colors">Contact Us</a> <a href="/privacy" class="hover:text-primary transition-colors">Privacy Policy</a> <a href="/terms" class="hover:text-primary transition-colors">Terms of Service</a> <a href="/support" class="hover:text-primary transition-colors">Support</a> </div> <div class="text-xs text-on-surface-variant/80"> © 2026 SkillMD. All rights reserved. </div> </div> </footer> </main> <!-- Script for Theme Toggle, Mobile Menu, and Sidebar Filter Redirection --> <script> // Theme setup const savedTheme = localStorage.getItem("theme") || "dark"; function applyTheme(theme) { document.documentElement.classList.remove("dark", "green", "dracula", "nord"); if (theme === "dark") { document.documentElement.classList.add("dark"); } else if (theme === "green") { document.documentElement.classList.add("dark", "green"); } else if (theme === "dracula") { document.documentElement.classList.add("dark", "dracula"); } else if (theme === "nord") { document.documentElement.classList.add("dark", "nord"); } document.documentElement.setAttribute("data-theme", theme); const themeMoon = document.getElementById("theme-moon"); const themeSun = document.getElementById("theme-sun"); const themeLeaf = document.getElementById("theme-leaf"); const themeDracula = document.getElementById("theme-dracula"); const themeNord = document.getElementById("theme-nord"); if (themeMoon && themeSun && themeLeaf && themeDracula && themeNord) { themeMoon.style.display = theme === "dark" ? "inline" : "none"; themeSun.style.display = theme === "light" ? "inline" : "none"; themeLeaf.style.display = theme === "green" ? "inline" : "none"; themeDracula.style.display = theme === "dracula" ? "inline" : "none"; themeNord.style.display = theme === "nord" ? "inline" : "none"; } } applyTheme(savedTheme); const themeToggleBtn = document.getElementById("theme-toggle-btn"); if (themeToggleBtn) { themeToggleBtn.addEventListener("click", () => { const currentTheme = document.documentElement.getAttribute("data-theme") || "dark"; let newTheme = "dark"; if (currentTheme === "dark") { newTheme = "light"; } else if (currentTheme === "light") { newTheme = "green"; } else if (currentTheme === "green") { newTheme = "dracula"; } else if (currentTheme === "dracula") { newTheme = "nord"; } else { newTheme = "dark"; } applyTheme(newTheme); localStorage.setItem("theme", newTheme); }); } // Mobile menu toggle and sidebar logic const mobileMenuToggle = document.getElementById("mobile-menu-toggle"); const sidebarMenu = document.getElementById("sidebar-menu"); const sidebarOverlay = document.getElementById("sidebar-overlay"); function isMobile() { return window.innerWidth < 768; // 768px is the 'md' breakpoint in Tailwind } function openSidebar() { if (sidebarMenu) { sidebarMenu.classList.remove("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.remove("hidden"); } } function closeSidebar() { if (sidebarMenu && isMobile()) { sidebarMenu.classList.add("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } if (mobileMenuToggle && sidebarMenu) { mobileMenuToggle.addEventListener("click", (e) => { e.stopPropagation(); if (isMobile()) { const isClosed = sidebarMenu.classList.contains("-translate-x-full"); if (isClosed) { openSidebar(); } else { closeSidebar(); } } }); document.addEventListener("click", (e) => { if (isMobile()) { if (!sidebarMenu.contains(e.target) && !mobileMenuToggle.contains(e.target)) { closeSidebar(); } } }); if (sidebarOverlay) { sidebarOverlay.addEventListener("click", () => { if (isMobile()) { closeSidebar(); } }); } // Collapse sidebar when clicking a filter button, creator button, or nav item inside it sidebarMenu.addEventListener("click", (e) => { if (isMobile()) { const clickTarget = e.target.closest("button, a"); if (clickTarget) { closeSidebar(); } } }); // Sync sidebar state on window resize window.addEventListener("resize", () => { if (!isMobile()) { // Desktop: sidebar should be visible, no overlay if (sidebarMenu) { sidebarMenu.classList.remove("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } else { // Mobile: start collapsed if (sidebarMenu) { sidebarMenu.classList.add("-translate-x-full"); } if (sidebarOverlay) { sidebarOverlay.classList.add("hidden"); } } }); } // If not on homepage, redirect on sidebar filter click const isHomepage = window.location.pathname === "/"; document.querySelectorAll("#occupation-filters .filter-btn").forEach(btn => { btn.addEventListener("click", (e) => { const occ = e.currentTarget.getAttribute("data-occupation"); if (!isHomepage) { window.location.href = occ ? `/?occupation=${encodeURIComponent(occ)}` : "/"; } }); }); document.querySelectorAll("#creator-filters .creator-btn").forEach(btn => { btn.addEventListener("click", (e) => { const creator = e.currentTarget.getAttribute("data-creator"); if (!isHomepage) { window.location.href = `/?creator=${encodeURIComponent(creator)}`; } }); }); </script> </body> </html>