Enabling Sticky TOC for Mobile Display in Minimal Mistakes
Currently Minimal Mistakes doesn’t support sticky Table of Contents for mobile display, even if you have set toc_sticky: true (see Table of contents). Now I want to enable it for mobile with minimal changes so that there is a sticky TOC icon on the screen and when clicked it shows/hides the TOC. This should not affect the desktop display and should apply regardless of the drafting format in use (Org or Markdown).
The fastest, most efficient approach to implement this is to rip the .toc out of the standard mobile document flow, apply fixed positioning to turn it into a popup, and inject a floating toggle button using JavaScript.
Because Minimal Mistakes builds the toc_sticky sidebar via Liquid layouts, this DOM-level manipulation works perfectly regardless of whether the post was drafted in Org-mode or Markdown.
Here are the specific updates you need to make to your files.
-
Update your Stylesheet
Append this CSS to the bottom of
assets/css/main.scss. This code uses Minimal Mistakes’ standard$largebreakpoint (64em or 1024px) to transform the TOC into a floating modal, while inheriting the native light/dark theme colors already defined by the.tocclass.// Mobile Sticky TOC Toggle .toc-mobile-btn { display: none; } @media (max-width: 64em) { // 1. The floating button .has-toc .toc-mobile-btn { display: flex; position: fixed; bottom: 25px; right: 25px; z-index: 9999; background: rgba(128, 128, 128, 0.2); // Light/Dark mode agnostic backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); color: inherit; border: 1px solid rgba(128, 128, 128, 0.3); border-radius: 50%; width: 50px; height: 50px; font-size: 1.2rem; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); cursor: pointer; align-items: center; justify-content: center; transition: transform 0.2s ease; &:active { transform: scale(0.95); } } // 2. The TOC Popup Panel .sidebar__right.sticky .toc { position: fixed; bottom: 90px; right: 25px; width: 320px; max-width: calc(100vw - 50px); max-height: 60vh; overflow-y: auto; z-index: 9998; box-shadow: 0 5px 25px rgba(0, 0, 0, 0.2); margin: 0; // Animation states opacity: 0; visibility: hidden; transform: translateY(20px); transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); &.is-active { opacity: 1; visibility: visible; transform: translateY(0); } } } -
Inject the Button via JavaScript
To ensure the button doesn’t clutter pages that don’t have a TOC, we dynamically inject it. Append this snippet to the very end of your
_includes/head/custom.htmlfile:<script> function initMobileTOC() { var toc = document.querySelector('.sidebar__right.sticky .toc'); // Only initialize if a sticky TOC exists and the button hasn't been added yet if (toc && !document.querySelector('.toc-mobile-btn')) { document.body.classList.add('has-toc'); var btn = document.createElement('button'); btn.className = 'toc-mobile-btn'; btn.innerHTML = '<i class="fas fa-list-ul"></i>'; btn.setAttribute('aria-label', 'Toggle Table of Contents'); document.body.appendChild(btn); // Toggle TOC visibility on button click btn.addEventListener('click', function(e) { e.stopPropagation(); toc.classList.toggle('is-active'); }); // Close TOC when clicking anywhere outside of it document.addEventListener('click', function(e) { if (toc.classList.contains('is-active') && !toc.contains(e.target) && e.target !== btn) { toc.classList.remove('is-active'); } }); // Automatically close the TOC popup after tapping a link inside it // var tocLinks = toc.querySelectorAll('a'); // tocLinks.forEach(function(link) { // link.addEventListener('click', function() { // if (window.innerWidth <= 1024) { // Matches the 64em media query // toc.classList.remove('is-active'); // } // }); // }); } } document.addEventListener('DOMContentLoaded', initMobileTOC); </script> -
Handle Encrypted Posts (Optional Safety Net)
Just in case you ever decide to disable
toc_stickyon an encrypted post (which moves the TOC completely inside the encryptedsection.page__contentcontainer), it is best practice to wake this script up after decryption.Open
_includes/secure_ui.htmland drop this block right next to your Gumshoe initialization:// Re-initialize Mobile TOC Toggle try { if (typeof initMobileTOC === 'function') { initMobileTOC(); } } catch (e) { console.warn("Mobile TOC failed to load", e); }
Comments