Search code examples
buttonquarto

Change position theme switch button in Quarto


When we want to have two different themes in a Quarto HTML document, we can use a button to switch between light and dark mode for example. Standard the button is in the top right corner of the document. I would like to have this document below the toc of the document. Here is some reproducible code:

---
title: "Change position theme button"
format: 
  html:
    toc: true
    theme: 
      light: default
      dark: darkly
---

## Chapter

Some text to make it reproducible

### Test

Output:

enter image description here

As you can see the theme switch button is in the top right corner. I was wondering how we can place it below the toc?


Solution

  • This is slightly trickier than I expected as the button is not placed after the table of contents (TOC) in the DOM. You can change the CSS rules, but while it will work for the example, if you have enough headings that your TOC needs to scroll, the toggle button will not float.

    To achieve floating, we need to change the DOM structure to place the toggle button directly after the TOC. The toggle button is added dynamically after the page is loaded, so we need some JavaScript that watches for changes made to the DOM, e.g. a MutationObserver.

    The first thing to do is add include-in-header: move-toggle.html to your Quarto yaml. Then create move-toggle.html which looks for changes in the DOM and moves the button if it finds it (and the table of contents is also loaded):

    <script>
        // function to move toggle
        const moveToggle = (mutations, obs) => {
            const toggleButton = document.querySelector('.quarto-color-scheme-toggle');
            const toc = document.querySelector('#TOC');
    
            // check if table-of-contents and button are loaded
            if (toggleButton && toc) {
    
                // move button (adjust as desired)
                toc.after(toggleButton);
                toggleButton.style.position = 'relative';
                toggleButton.style.top = '0';
                toggleButton.style.right = '0';
                toggleButton.style.margin = '1em 0';
    
                obs.disconnect(); // no need to keep observing
            }
        }
    
        // function to observe DOM for changes
        const observeDom = () => {
            const observer = new MutationObserver(moveToggle);
            observer.observe(document.body, { childList: true, subtree: true });
        }
    
        // observe the DOM after it has loaded
        document.addEventListener("DOMContentLoaded", observeDom);
    </script>
    

    Output

    Light mode

    page with button moved (light mode)

    Dark mode

    page with button moved (dark mode)

    Full Quarto document

    For reproducibility here is the full qmd file. The only additional line is include-in-header: move-toggle.html.

    ---
    title: "Change position theme button"
    format: 
      html:
        toc: true
        theme: 
          light: default
          dark: darkly
        include-in-header: move-toggle.html
    ---
    
    ## Chapter
    
    Some text to make it reproducible
    
    ### Test