Skip to content

Create a custom drop-down or fly-out menu in the Sphinx Alabaster sidebar #269

@jbampton

Description

@jbampton

Creating a custom drop-down or fly-out menu in the Sphinx Alabaster sidebar requires a combination of custom sidebar templates, HTML/Jinja2, and custom CSS/JavaScript, as the Alabaster theme's built-in sidebar primarily uses standard Sphinx toctree and simple links.

Here's the general approach:

1. Create a Custom Sidebar Template

You need to define the structure of your drop-down menu using HTML.

  • Create a _templates directory in your Sphinx source directory (where conf.py is located).
  • Inside _templates, create a new file, for example, custom_dropdown.html.

In custom_dropdown.html, you'll use HTML/CSS/JavaScript to create the menu. A common pattern is to use an unordered list (<ul>) and apply CSS/JS for the expand/collapse behavior.

<div class="sphinxsidebarwrapper custom-sidebar-section">
    <p class="caption">
        <span class="caption-text">External Links</span>
    </p>
    <ul class="current">
        <li class="toctree-l1">
            <a class="reference internal" href="#">
                **My Dropdown Menu**
            </a>
            <ul class="simple-dropdown-content">
                <li class="toctree-l2">
                    <a class="reference internal" href="https://example.com/link1">
                        Link 1
                    </a>
                </li>
                <li class="toctree-l2">
                    <a class="reference internal" href="https://example.com/link2">
                        Link 2
                    </a>
                </li>
            </ul>
        </li>
    </ul>
</div>

2. Configure Sphinx to Use the Custom Template

In your conf.py file, you need to tell Sphinx to include your custom template in the sidebar.

  • Define templates_path:

    templates_path = ['_templates']
  • Update html_sidebars: Include your new template (custom_dropdown.html) in the dictionary, along with the other Alabaster templates you want.

    html_sidebars = {
        '**': [
            'about.html',
            'navigation.html',
            'relations.html',
            'searchbox.html',
            'custom_dropdown.html',  # <-- Add your custom file here
            'donate.html',
        ]
    }

3. Implement Drop-down Behavior with CSS/JS

Since Alabaster is a minimal theme, it doesn't provide built-in drop-down functionality outside of the standard toctree. You'll need to add your own CSS and JavaScript.

3.1. Add Custom CSS (for basic styling)

You'll need CSS to hide the drop-down content by default and style the trigger.

  • Create a _static directory in your Sphinx source directory.

  • Inside _static, create a file like custom.css.

    /* _static/custom.css */
    
    /* Hide the dropdown content initially */
    .custom-sidebar-section .simple-dropdown-content {
        display: none;
        padding-left: 10px; /* Indent for visual hierarchy */
    }
    
    /* Style the main menu item to look like a clickable header */
    .custom-sidebar-section .toctree-l1 a.reference.internal {
        cursor: pointer;
        user-select: none;
        display: block;
        font-weight: bold;
    }
    
    /* Optionally, add a marker (like an arrow) to the header */
    .custom-sidebar-section .toctree-l1 a.reference.internal::after {
        content: " \25B6"; /* Right triangle */
        float: right;
        transition: transform 0.3s;
    }
    
    /* Rotate the marker when the content is open */
    .custom-sidebar-section .toctree-l1.open > a.reference.internal::after {
        content: " \25BC"; /* Down triangle */
    }

3.2. Add Custom JavaScript (for toggling)

You'll need JavaScript to toggle the display property of the drop-down content when the header is clicked.

  • In your _static directory, create a file like custom.js.

    /* _static/custom.js */
    document.addEventListener('DOMContentLoaded', function() {
        // Select all custom dropdown headers
        const dropdownHeaders = document.querySelectorAll('.custom-sidebar-section .toctree-l1 > a.reference.internal');
    
        dropdownHeaders.forEach(header => {
            header.addEventListener('click', function(event) {
                // Prevent the default link behavior if it's just a placeholder
                if (this.getAttribute('href') === '#') {
                    event.preventDefault();
                }
    
                // Get the parent <li> element
                const parentLi = this.closest('.toctree-l1');
    
                // Toggle the 'open' class on the parent <li>
                parentLi.classList.toggle('open');
    
                // Get the dropdown content (the nested <ul>)
                const content = parentLi.querySelector('.simple-dropdown-content');
    
                // Toggle the display
                if (content.style.display === 'block') {
                    content.style.display = 'none';
                } else {
                    content.style.display = 'block';
                }
            });
        });
    });

3.3. Include CSS and JS in conf.py

In conf.py, you must register your static files so Sphinx includes them.

# Add your static paths
html_static_path = ['_static']

# Add your custom CSS and JS files
html_css_files = [
    'custom.css',
]

html_js_files = [
    'custom.js',
]

⚠️ Alternative: Use sphinx-design

A much easier, modern, and theme-agnostic way to add drop-downs to your documentation (though typically used in the main page content, not the sidebar) is to use the sphinx-design extension.

You would install it: pip install sphinx-design and add it to your conf.py:

extensions = [
    # ... other extensions
    'sphinx_design',
]

Then, in any .rst file (including a custom template, if you render it from a file):

.. dropdown:: My Dropdown Menu Title

    This is the content that drops down.

    * Link A
    * Link B

While designed for page content, you could potentially embed this reStructuredText block into a custom sidebar template that uses the rst directive, but the manual HTML/CSS/JS method offers more control over the final sidebar appearance in Alabaster.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cssenhancementNew feature or requesthacktoberfesthelp wantedExtra attention is neededjavascriptPull requests that update javascript codepythonPull requests that update python codevalid

    Projects

    Status

    Todo

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions