The mkdocs.yml nav file I am working with includes a hierarchical tree of a manuals website with several categories and subcategories the content articles fall into; the tre
This is not supported by MkDocs at this time, but may be possible with a custom theme.
In #1042, you can find an attempt at a solution which was ultimately rejected for a number of reasons. That said, you should be able to simulate something similar with a custom theme.
MkDocs does not care if the structure of the nav
matches any actual file structure. So you can arrangement the nesting structure however you want. Just make sure the first child of any "section" points at an index file. Perhaps something like this:
nav:
- 'TOP LEVEL CATEGORY':
- 'toplevel1/index.md'
- 'BOTTOM LEVEL CATEGORY':
- 'bottomlevel1/index.md'
- Manual 1: 'manuals/Manual1.md'
- Manual 2: 'manuals/Manual2.md'
- 'BOTTOM LEVEL 2':
- 'bottomlevel2/index.md'
Note that each section includes a unique index file. Of course index files must be named index.md
so the only way to make them unique is for each one to be in a unique subdirectory. Also notice that there is no title assigned to the index pages. Presumably, the section title will be used.
Then in your theme template you need to check for children and if the first child is an index, use that as the link for that section. Then when looping through the children, be sure to skip the index in the nested level.
Perhaps something like this:
{% if nav|length>1 %}
<ul>
{% for nav_item in nav %}
{% if nav_item.children %}
<li>{% if nav_item.children[0].is_index %}
<a href="{{ nav_item.children[0].url|url }}">{{ nav_item.title }}</a>
{% else %}
{{ nav_item.title }}
{% endif %}
<ul>
{% for nav_item in nav_item.children[1:] %}
<li class="{% if nav_item.active%}current{% endif %}">
<a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li class="{% if nav_item.active%}current{% endif %}">
<a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
By way of explanation, the above is simply a slightly modified version of the example in the docs. Where the section title was displayed in the original ({{ nav_item.title }}
), we instead check if the first child is an index file and, if so, display a link to the index page instead. Of course, we include a fallback for those occasions where there is no index.
{% if nav_item.children[0].is_index %}
<a href="{{ nav_item.children[0].url|url }}">{{ nav_item.title }}</a>
{% else %}
{{ nav_item.title }}
{% endif %}
Then when stepping through the children, we want to avoid including the index file. I used {% for nav_item in nav_item.children[1:] %}
in my example ([1:]
slices the list to exclude the first item), but that assumes there always is an index file. Some other solutions may be better and left as a exercise for the reader. The Jinja documentation may be helpful here.
Also note that the above template only accounts for one level of nesting. A more sophisticated solution would need to be developed to handle multiple nesting levels. For example, you might want to define a Jinja macro, which each level calls recursively.
Also of note is that you don't need to develop a complete custom theme. You might want to instead override a template block of an existing theme which your own custom navigation.
I have implemented a plugin for this functionality, "mkdocs-section-index", and it works similarly to what @Waylan describes -- or actually more similar to the dropped pull request #1042, because most of the logic is outside of the theme/template. The special sections with a link are explicitly put into the nav and presented as such to the theme, so it doesn't need to find and exclude this "first child" itself.
mkdocs.yml:
plugins:
- section-index
nav:
- Frob: index.md
- Baz: baz.md
- Borgs:
- borgs/index.md
- Bar: borgs/bar.md
- Foo: borgs/foo.md
That gives you a nav like this:
- Frob
- Baz
- Borgs
- Bar
- Foo
It makes sense to me that this functionality couldn't be included by default, but a plugin as a way to opt in seems perfectly fine.