You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On the MyST lecture sites the contents drawer loads closed on every page. A reader who opens it to move between lectures has to reopen it after each link, and within a page it closes at the first click or text selection outside it. The book theme's persistent_sidebar option keeps its sidebar open across pages on screens wider than 1340px. This theme has no counterpart, and no issue tracks one.
Why it loads closed, at quantecon-theme.mystmd d504555:
The open state is the browser's popover state and nothing else. The drawer is popover="auto" with no React state (ContentsSidebar.tsx L113-L117), and the toggle has no handler and no state by design (SidebarToggle.tsx L4-L11). The only browser-storage use under app/ is the colour theme's (Document.tsx L85-L92).
Static builds render every internal link with reloadDocument (Document.tsx L74-L78), switched on by MODE === 'static' (root.tsx L236-L237). Every navigation is a full page load, so the popover starts closed.
The drawer is light-dismissed at every width, because popover="auto" is set with no width condition. Escape, or a click or text selection anywhere outside it, closes it, "where before it stayed open until toggled" (CHANGELOG.md L338-L341).
No option changes this. The layout options are only hide_toc and hide_search (template.yml L99-L105), and the CLI drops any site option the template does not declare (template.yml L23-L25). A persistent_sidebar copied into myst.yml would be discarded.
docs/layout.md L14-L19 describes the drawer without mentioning persistence, although docs/index.md L42-L45 promises that where a Sphinx option has no counterpart, its page says so and why.
What the book theme does, at quantecon-book-theme 649cc37:
Every open and every close writes localStorage.setSidebar (sidebar.js L10-L26).
Nothing stored
Page load
On DOMContentLoaded (index.js L24-L33), reopens when the stored flag is 1, the class is present and the window is wider than 1340px (sidebar.js L28-L40).
Loads closed
Click outside
A close-on-click handler is bound only when the toggle is clicked at 1340px or narrower (sidebar.js L43-L59), so a sidebar opened wider, or restored on load, stays open.
Closes the drawer at every width
Scale: one lecture site, not yet on this theme. lecture-datascience.myst sets persistent_sidebar: true (lectures/_config.yml L70). It pins quantecon-book-theme 0.8.0 (environment.yml L11), which has the same restore logic (index.js L114-L142 at v0.8.0). A code search of the QuantEcon organisation for persistent_sidebar on 2026-09-11 found no other lecture repository that sets it. The site still builds with the Sphinx theme and is repointed to this one when it cuts over (PLAN.md L195-L198), and its readers lose the behaviour at that point.
PR #144, the Popover API rebuild, set persistence aside rather than deciding against it. Its 2026-08-25 review noted that light dismiss "ends its old role as a persistent side map while reading — including on 2xl viewports where the 350px panel overlaps nothing". The 2026-09-03 decisions comment accepted popover="auto": "If persistence turns out to matter on wide screens it is a one-attribute change plus an Escape handler, so it can be revisited on the demo site rather than here." It also found that the open state was already lost across navigation before the rebuild, and said "if persistence across navigation is wanted it is a follow-up issue, not a condition on this PR". Both statements are conditional, and persistence is not on that comment's own "Not done here, for follow-up" list. No such issue exists. On 2026-09-11, searching this repository's issues for "persistent sidebar" and "persistent_sidebar" finds only #33 (closed in June 2026). That issue's comparison table marked Persistent Sidebar as Missing, and its recommendation 5 proposed an option to keep the sidebar visible.
Proposed change
Add an opt-in counterpart to persistent_sidebar. Leave the default behaviour as it is.
Declare a boolean persistent_sidebar site option in template.yml, so lecture-datascience.myst can carry its setting across unchanged. Give it no default:, for the reason given at template.yml L31-L33. Document it next to hide_toc in docs/configuration.md L26-L27.
When the option is set, store the drawer's open or closed state in browser storage whenever the popover toggles, with every read and write guarded. On page load, reopen the drawer if the stored state is open and the viewport is wide enough that the drawer does not cover the reading column. The book theme's threshold is 1340px. The refactor: rebuild the contents drawer on the Popover API #144 review pointed to 2xl (1536px), where the drawer is 350px wide (styles/app.css L118-L122). The PR should settle which width to use.
At those widths, a restored drawer has to stay open while the reader works in the article, because under popover="auto" the first click there closes it. This reopens the refactor: rebuild the contents drawer on the Popover API #144 light-dismiss decision, but only for sites that opt in and only on wide viewports, which that decision left open to revisit. The refactor: rebuild the contents drawer on the Popover API #144 review and decision describe the change: popover="manual" there, plus an Escape handler. Keep the existing hook that hides the drawer when a dialog opens (ContentsSidebar.tsx L44-L76).
Two details to settle in the PR:
Opening the popover on load plays its @starting-style slide-in (styles/app.css L133-L137). Decide whether a restored drawer slides in after load or appears already open. The book theme's restore removes .inactive, which is animated by a 0.2s transform transition (_sidebar.scss L38-L40, L61-L65).
The dialog hook closes the drawer when search opens. Decide whether that close is stored. If it is, a reader who jumps to a lecture through search lands with the drawer closed. In the book theme, no script outside sidebar.js touches the sidebar state.
If the maintainers decide against the option, docs/layout.md should instead name persistent_sidebar as having no counterpart and say why, as docs/index.md promises.
Acceptance criteria
persistent_sidebar (boolean) is declared in template.yml and documented in docs/configuration.md, and a value set in myst.yml reaches the theme.
With the option set, on a viewport above the chosen width: after opening the drawer and following a link in it, the next page loads with the drawer open. After closing it and reloading, the page loads with it closed.
With the option set, on those viewports a click or text selection in the article leaves the drawer open. The toggle, Escape and opening search still close it.
Below the chosen width, the drawer loads closed and is still light-dismissed, whatever the stored state.
With the option unset, behaviour is unchanged and nothing is written to browser storage.
When a storage read or write throws, the new code catches it and the drawer loads closed. Whole-page hydration with storage blocked is outside this change: the upstream colour-theme hook reads localStorage without a guard (@myst-theme/site 1.3.0, src/hooks/theme.tsx line 57).
A Playwright test covers restoring the drawer after navigation with the option set, and the chosen behaviour after search closes the drawer. drawer-opens-without-javascript, drawer-closes-when-search-opens and npm run test:fouc still pass.
The "Contents drawer" section of docs/layout.md describes the option and the width it applies at.
Context
Source: the 2026-09-11 book-theme parity study, row F005 ("Persistent sidebar state (persistent_sidebar)"). Verdict missing_untracked, relevance medium, and the review pass agreed. Section 3.3 (Layout and navigation) gives the action "File the promised follow-up issue". Section 3.4 lists persistent_sidebar among the book-theme keys the docs should name as having no counterpart (tracked in PLAN.md and the user docs claim book-theme parity and behaviour that the code does not deliver #210).
Filed without a parent. The study does not propose it as a cutover gate item for Book-theme parity cutover — tracking #147. Section 3.3 says its untracked rows do not block cutover and belong on a follow-up tracker.
Neighbouring study rows: F002 (light dismiss at every width, decided in refactor: rebuild the contents drawer on the Popover API #144), F004 (at 1024–1300px the open drawer covers part of the body column, where the book theme pushes the content aside), and F028 (the unguarded storage read in the upstream colour-theme hook).
Checked on 2026-09-11 against quantecon-theme.mystmd d504555, quantecon-book-theme 649cc37 (and tag v0.8.0, 19b3c59), and lecture-datascience.myst 5a054b8.
Problem
On the MyST lecture sites the contents drawer loads closed on every page. A reader who opens it to move between lectures has to reopen it after each link, and within a page it closes at the first click or text selection outside it. The book theme's
persistent_sidebaroption keeps its sidebar open across pages on screens wider than 1340px. This theme has no counterpart, and no issue tracks one.Why it loads closed, at quantecon-theme.mystmd
d504555:popover="auto"with no React state (ContentsSidebar.tsx L113-L117), and the toggle has no handler and no state by design (SidebarToggle.tsx L4-L11). The only browser-storage use underapp/is the colour theme's (Document.tsx L85-L92).reloadDocument(Document.tsx L74-L78), switched on byMODE === 'static'(root.tsx L236-L237). Every navigation is a full page load, so the popover starts closed.popover="auto"is set with no width condition. Escape, or a click or text selection anywhere outside it, closes it, "where before it stayed open until toggled" (CHANGELOG.md L338-L341).hide_tocandhide_search(template.yml L99-L105), and the CLI drops any site option the template does not declare (template.yml L23-L25). Apersistent_sidebarcopied intomyst.ymlwould be discarded.What the book theme does, at quantecon-book-theme
649cc37:persistent_sidebar: trueFalse(theme.conf L37). Only a real booleantrueadds thepersistentclass (layout.html L367-L371).localStorage.setSidebar(sidebar.js L10-L26).DOMContentLoaded(index.js L24-L33), reopens when the stored flag is 1, the class is present and the window is wider than 1340px (sidebar.js L28-L40).Scale: one lecture site, not yet on this theme. lecture-datascience.myst sets
persistent_sidebar: true(lectures/_config.yml L70). It pins quantecon-book-theme 0.8.0 (environment.yml L11), which has the same restore logic (index.js L114-L142 at v0.8.0). A code search of the QuantEcon organisation forpersistent_sidebaron 2026-09-11 found no other lecture repository that sets it. The site still builds with the Sphinx theme and is repointed to this one when it cuts over (PLAN.md L195-L198), and its readers lose the behaviour at that point.PR #144, the Popover API rebuild, set persistence aside rather than deciding against it. Its 2026-08-25 review noted that light dismiss "ends its old role as a persistent side map while reading — including on 2xl viewports where the 350px panel overlaps nothing". The 2026-09-03 decisions comment accepted
popover="auto": "If persistence turns out to matter on wide screens it is a one-attribute change plus an Escape handler, so it can be revisited on the demo site rather than here." It also found that the open state was already lost across navigation before the rebuild, and said "if persistence across navigation is wanted it is a follow-up issue, not a condition on this PR". Both statements are conditional, and persistence is not on that comment's own "Not done here, for follow-up" list. No such issue exists. On 2026-09-11, searching this repository's issues for "persistent sidebar" and "persistent_sidebar" finds only #33 (closed in June 2026). That issue's comparison table marked Persistent Sidebar as Missing, and its recommendation 5 proposed an option to keep the sidebar visible.Proposed change
Add an opt-in counterpart to
persistent_sidebar. Leave the default behaviour as it is.persistent_sidebarsite option in template.yml, so lecture-datascience.myst can carry its setting across unchanged. Give it nodefault:, for the reason given at template.yml L31-L33. Document it next tohide_tocin docs/configuration.md L26-L27.popover="auto"the first click there closes it. This reopens the refactor: rebuild the contents drawer on the Popover API #144 light-dismiss decision, but only for sites that opt in and only on wide viewports, which that decision left open to revisit. The refactor: rebuild the contents drawer on the Popover API #144 review and decision describe the change:popover="manual"there, plus an Escape handler. Keep the existing hook that hides the drawer when a dialog opens (ContentsSidebar.tsx L44-L76).@starting-styleslide-in (styles/app.css L133-L137). Decide whether a restored drawer slides in after load or appears already open. The book theme's restore removes.inactive, which is animated by a 0.2s transform transition (_sidebar.scss L38-L40, L61-L65).If the maintainers decide against the option, docs/layout.md should instead name
persistent_sidebaras having no counterpart and say why, as docs/index.md promises.Acceptance criteria
persistent_sidebar(boolean) is declared in template.yml and documented in docs/configuration.md, and a value set inmyst.ymlreaches the theme.localStoragewithout a guard (@myst-theme/site 1.3.0, src/hooks/theme.tsx line 57).drawer-opens-without-javascript,drawer-closes-when-search-opensandnpm run test:foucstill pass.Context
persistent_sidebaramong the book-theme keys the docs should name as having no counterpart (tracked in PLAN.md and the user docs claim book-theme parity and behaviour that the code does not deliver #210).d504555, quantecon-book-theme649cc37(and tag v0.8.0,19b3c59), and lecture-datascience.myst5a054b8.