New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Allow unlimited multi-level navigation #1431
base: main
Are you sure you want to change the base?
Conversation
This PR is intended to supersede just-the-docs#462. The only user-level difference from just-the-docs#462 is that disambiguation of parent pages has to use either `grand_parent` or `ancestor` titles: the somewhat unnatural `section_id` and `in_section` fields are not supported. The implementation has been significantly simplified by the changes introduced in v0.7.0 of the theme. This initial draft has not yet been rigorously tested nor profiled.
Unclear to me why adding this gem causes CI failures.
HTML Proofer failed:
When building the theme docs locally, the following link appears in the <script type="application/ld+json">
{..., "url":"http://localhost:.../docs/navigation-structure/recursion/X/"}
</script> It seems that HTML Proofer assumes the site is being built at Is there a work-around for avoiding such failures when adding new files in PRs? |
A page should not have a parent or ancestor with the same title. If it does, the location of the repeated link is marked by ∞, to facilitate debugging the navigation (and an unbounded loop leading to a build exception is avoided). It might be preferable to emit a warning in the Jekyll build log, but that doesn't appear to be supported by Liquid/Jekyll tags.
Reuses `nav_breadcrumbs`.
In Jekyll 3, Liquid’s binary `and` and `or` operations cannot be used in the `where_exp` filter...
Avoid diffs from sites built with v0.8.0
For backward compatibility with 3-level sites
For backward compatibility with 3-level sites
This is for backwards compatibility with sites that use `has_children: false` to suppress navigation links to child pages.
The current version of the draft appears to be backwards compatible with v0.8.0: it produces essentially the same HTML when building the Just the Docs Tests site, based on running the following command after building just-the-docs-tests: diff -rw -x "*.js*" -x "*.map" -I "published_time" -I "dateModified" -I "<meta property=" -I "<link rel=" -I "@context" _site-pr _site-0.8.0 | more
diff -rw -x *.js* -x *.map -I published_time -I dateModified -I <meta property= -I <link rel= -I @context _site-pr/index.html _site-0.8.0/index.html
303,305c303
< <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>remote: https://github.com/pdmosses/just-the-docs.git
< revision: a649a473b591fa8b64c7ce972d949c9147758155
< ref: multi-level
---
> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>remote: ../just-the-docs-v0.8.0
Initial profiling indicates that the current draft is up to 10% slower than v0.8.0 with Jekyll 4 on larger 3-level sites. With Jekyll 3, the implementation of the auto-generated TOCs is currently quite inefficient on one large 3-level site. It should be possible to fix that for non-excluded pages, by deconstructing the cached site-nav. Footnotes
|
Thanks for opening this PR up! I will try to take a more in-depth look in a bit. But, quickly:
My off-the-cuff suggestion is to replace the I can take a quick look at this! |
Takes account of ancestors. To test, add to 404.html front matter: ```yaml parent: T grand_parent: S ancestor: Y ``` Then check that 404.html has the correct breadcrumbs.
When activated by `nav_error_report: true` in `_config.yml`, displays warnings about pages with the same title as their parent page or an ancestral page.
The breadcrumbs for excluded pages were incorrect in previous commits, due to interference between assignments in recursive inclusions of ancestry.html.
No rush: I intend to remove all the new files before requesting a review. That will avoid the HTML-Proofer issue. |
It appears that: - `nav_levels[1] == nil` holds only when not `site_nav contains nav_list_link`; - `nav_levels[2] == nil and nav_levels[3]` never holds.
* Generate rules for any number of levels * Improve layout of generated rules * Improve layout of Liquid tags
On a large site where 100s of pages are excluded from the main navigation, the current multi-level implementation of breadcrumbs is too inefficient. A significantly more efficient implementation will have to be developed. |
Revert to the original code for nav/ancestry. The previous draft increased the build time 5x with Jekyll 3 for a site with about 550 pages, of which 150 are excluded from the main navigation.
The latest commit has improved the efficiency of the implementation of breadcrumbs for excluded patges. However, when building a site with about 550 pages – of which about 150 are (implicitly. or explicitly) excluded from the main navigation – the updated profiling results show that the rendering of the breadcrumbs and TOCs takes roughly 35 seconds each with this PR and Jekyll 3. This is significantly longer than when building the same site with v0.8.1 and Jekyll 3, where rendering the breadcrumbs takes about 18 seconds, and the TOCs only 2 seconds. (With this PR and Jekyll 4, the breadcrumbs and TOCs take 2–3 seconds each, which seems reasonable.) |
An alternative would be to ensure that the site_nav includes links to all pages. The display of links to pages that are supposed to be excluded from the main navigation would then need to be suppressed using CSS.
Locally profiling a preliminary test of this alternative approach indicates that it would reduce the Jekyll 3 build time from 105 seconds to 55 seconds (the same as the v0.8.1 build time). The inclusion of the extra 150 links in site_nav significantly increases the size of all pages, from about 32MB to 42MB, but it should be possible to avoid that by caching two versions of the site_nav. |
The extra cached site-nav is used for determining breadcrumbs and children navigation, which may involve pages that are excluded from the main navigation. The inefficient code previously used for excluded pages is now redundant, and to be removed in a subsequent commit.
After that change, the updated profiling results for this PR are close to those for v0.8.1. |
Replace code for determining children by inclusion of components/nav/children.html Profiling locally, this doesn't appear to lose efficiency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I apologize for the extraordinarily long turn around time for this PR - I'm realizing I am continually grossly underestimating how challenging my new job can be (at times).
I've done a first pass over the general logic of this PR, the motivation (thank you for the great summary document and comparison to #192, as well as an explanation of the title-based assumptions), and briefly looked at the performance results. I will admit that I have not yet looked at edge/corner cases nor thought about how this could adversely impact legacy sites. However, broadly speaking, I'm pretty supportive of this change and would like to merge it in.
I will do a more thorough look "when I'm free" (which is ideally, this or next weekend - have some time on a plane to review code). I've left a handful of broad implementation questions & little things that were unclear to me on first read; none of these necessitate response until my next review, but let me know if you have any thoughts!
{%- for i in (2..nav_page_level) %}, | ||
{{ activation_collection_prefix }} > li.nav-list-item:nth-child({{ nav_levels[1] }}) > | ||
{%- for j in (2..i) %} ul.nav-list > li.nav-list-item:nth-child({{ nav_levels[j] }}) > | ||
{%- endfor %} ul.nav-list | ||
{%- endfor %} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Selectors that look like this are linear in the size of nav_page_level
, right? I am wondering at what point CSS selector performance will become an issue. My guess is that most of our existing client sites won't have issues here (since they've been artificially limited to a depth of 2-3), but I'm curious how large sites with many more levels of nesting will do. I don't think that should be a barrier to implementing this change, but may be a future roadblock.
(I'm also cognizant that backwards-compatibility makes it even more challenging to implement this change well)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious how large sites with many more levels of nesting will do.
I think that sites with large numbers of pages tend to have high average branching. For 1000 pages with average branching 10, the average depth would probably be about 3. Even for a very slim site with 1000 pages and binary branching, the average depth wouldn't be much more than 10. Do you know of any sites with more than 10 navigation levels?
I know nothing about CSS selector efficiency. Would 10 levels of selectors make a website significantly less responsive? Note that most of the generated selectors are for direct descendants (using >
) which ought to be quick to test.
In any case, the generated page-dependent CSS rules are disabled when JS is active, so the vast majority of visitors would never be affected by any inefficiency with the selectors.
(I'm also cognizant that backwards-compatibility makes it even more challenging to implement this change well)
Removing the restriction to 3 levels actually simplified the navigation link generation. I think the only tricky bit with backwards-compatibility was to treat a missing grand_parent
field as indicating that the page indeed has no grandparent page when disambiguating parent pages.
{%- comment -%} | ||
not site_nav contains nav_list_link | ||
{%- endcomment -%} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be missing something, but what does this comment mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It merely asserts the condition that holds when the else
branch is used: it is the negation of the condition tested on line 46:
{%- if site_nav contains nav_list_link -%}
{%- capture nav_error_report -%} | ||
<blockquote class="warning"> | ||
A page has the same title as its parent page or one of its ancestral pages!<br> | ||
This causes an incorrect link in the main navigation panel.<br> | ||
Page title: <code>{{ node.title }}</code>, location: <code>{{ node.path }}</code>. | ||
</blockquote> | ||
{%- endcapture -%} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this would also be helpful to log in the JS console?
|
||
<li class="nav-list-item"> | ||
{%- if nav_children.size >= 1 -%} | ||
<button class="nav-list-expander btn-reset" aria-label="toggle items in {{ node.title }} category" aria-pressed="false"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am just realizing now that we may not be properly setting this aria-pressed
value when the expander is open by default (looking at the JS, ariaPressed
is only set by an event listener - so never fired on first load). This is not a regression in this PR so this doesn't need to be addressed right now.
This PR is intended to supersede both #192 and #462. It aims to fix #219, and to respond to the discussion in #425 and in the comments on #192.
A summary of the background for this PR is available here.1
The only user-level difference from #462 is that disambiguation of parent pages has to use either
grand_parent
orancestor
titles: the somewhat unnaturalsection_id
andin_section
fields are not supported.Note
Multi-level navigation is to be backwards-compatible with existing sites that use JtD v0.8.*.2
Tasks
ancestor
fields into account.Overview of changes and additions
_includes/components/breadcrumbs
:_includes/components/children_nav
:_includes/components/nav/*
:_includes/components/nav.html
_includes/components/site_nav
:_layouts/default
:has_children
To simplify review and testing, the temporary docs-related changes have been moved to PR #1440.
Footnotes
The original overview of the implementation is outdated – the implementation has been significantly simplified by the navigation changes introduced in v0.7.0 of the theme. ↩
Sites that currently use modified copies of the changed files will need to update those copies. ↩