| Left Sidebar (Table of Contents) |
✅ Collapsible sidebar with hierarchical ToC, auto-expand option, persistent option |
✅ Slide-in sidebar with grouped headings, toggle button |
✅ Parity (different UX, functional equivalent) |
| Right Sidebar (On This Page) |
✅ Within-page ToC with toc-hN classes, sticky option with scroll highlighting |
✅ Outline component in margin column with useHeaders hook |
✅ Parity |
| Toolbar |
✅ Fixed top toolbar with all action buttons |
✅ Fixed top toolbar (50px) with all action buttons |
✅ Parity |
| Home Button |
✅ Feather icon link to master_doc |
✅ Lucide House icon link to baseurl |
✅ Parity |
| QuantEcon Logo/Link |
✅ Logo linking to organisation URL |
✅ Logo linking to quantecon.org |
✅ Parity |
| Search |
✅ In-toolbar search form (Sphinx search) |
✅ MiniSearch-based search component |
✅ Parity (better: client-side fuzzy search) |
| Fullscreen Toggle |
✅ Feather maximize icon |
✅ Lucide Maximize/Minimize icons with state |
✅ Parity |
| Font Size +/− |
✅ Plus/minus circle icons |
✅ CirclePlus/CircleMinus with 3-step scale |
✅ Parity |
| Dark/Light Mode |
✅ sunset icon, localStorage toggle, dark-theme class |
✅ Sunset icon, useThemeSwitcher from providers, Tailwind dark: |
✅ Parity (improved: system-aware via myst-theme) |
| Download (Notebook) |
✅ Download cloud icon, links to /_notebooks/{page}.ipynb |
✅ CloudDownload dropdown with project + page downloads via MyST config |
✅ Parity (improved: configurable via myst.yml) |
| Download (PDF) |
✅ PDF icon with modal for Lecture PDF / Book PDF, with window.print() fallback |
✅ Combined into Downloads dropdown (PDF downloads configured via myst.yml exports) |
⚠️ Partial — No separate PDF icon or window.print() fallback |
| Launch Notebook |
✅ Play icon with modal: Public (BinderHub/Colab/JupyterHub select) + Private input |
✅ Play icon with Popover: Google Colab + Private JupyterHub input |
⚠️ Partial — BinderHub deferred (see issue) |
| GitHub Link |
✅ GitHub icon linking to repository URL |
✅ GitHub icon linking to edit_url from frontmatter |
✅ Parity (improved: edit-specific URL) |
| Mobile Responsive |
✅ Basic responsive layout |
✅ Full responsive with mobile action menu (ellipsis dropdown) |
✅ Parity (improved) |
| Footer |
✅ Hardcoded Creative Commons + "A theme by QuantEcon" |
✅ Dynamic MyST content via config.parts.footer |
✅ Parity (improved: configurable) |
| Prev/Next Navigation |
✅ In sidebar footer with "Previous topic"/"Next topic" links |
✅ Via getFooterLinks in loader (page data), but no visible UI component renders them |
❌ Missing |
| Page Header (Title/Authors) |
✅ Project title, author names with links |
✅ ProjectFrontmatter with title, authors, affiliations |
✅ Parity |
| Git Last Modified Date |
✅ "Last changed: {date}" button with changelog dropdown |
❌ Not implemented |
❌ Missing |
| Git Changelog |
✅ Expandable commit history list per page |
❌ Not implemented |
❌ Missing |
| Collapsible Stderr Warnings |
✅ Auto-wraps verbose warnings in notebook cells with expandable interface |
❌ Not implemented (may be handled by @myst-theme/jupyter) |
⚠️ Needs verification |
| Code Syntax Highlighting Config |
✅ qetheme_code_style toggle, Pygments style override support |
❌ Not configurable (relies on myst-theme defaults) |
⚠️ Missing (may not be needed if myst defaults are acceptable) |
| Color Scheme Options |
✅ color_scheme option (seoul256, gruvbox, none), custom CSS auto-detection |
❌ Not implemented |
❌ Missing |
| RTL Support |
✅ enable_rtl option with dir="rtl" on body |
❌ Not implemented |
❌ Missing |
| Edit Page Button |
✅ use_edit_page_button configuration |
✅ GitHub button links to edit_url when available |
✅ Parity |
| Repository/Issues Buttons |
✅ use_repository_button, use_issues_button |
❌ Only edit URL supported, no separate repo/issues links |
⚠️ Missing |
| Persistent Sidebar |
✅ persistent_sidebar option keeps sidebar always visible |
❌ Sidebar slides in/out only |
⚠️ Missing |
| Sticky ToC |
✅ sticky_contents with back-to-top and scroll spy |
✅ Outline is absolute positioned, BackToTop appears on scroll |
✅ Parity |
| Contents Auto-Expand |
✅ contents_autoexpand for sidebar sections |
❌ All sections are expanded by default |
⚠️ Missing |
| MathJax Macros |
✅ Custom LaTeX macros (EE, PP, RR, etc.) built into theme |
❌ Not configured at theme level |
⚠️ Needs verification — may be handled by MyST build |
| Thebe / Live Code |
✅ Via launch_buttons.thebe flag |
✅ Via ComputeOptionsProvider with notebookCompute / figureCompute |
✅ Parity (improved) |
| SEO Meta Tags |
✅ Twitter Cards, OpenGraph |
✅ Via getMetaTagsForSite / getMetaTagsForArticle |
✅ Parity |
| Sitemap |
❌ Not included |
✅ [sitemap.xml].tsx route with XSL stylesheet |
✅ Improved |
| robots.txt |
❌ Not included |
✅ [robots.txt].tsx route |
✅ Improved |
| objects.inv |
❌ Handled by Sphinx |
✅ [objects.inv].tsx proxy route |
✅ Equivalent |
| Cross-references |
❌ Sphinx native |
✅ myst.xref.json support |
✅ Equivalent |
| Plugins System |
✅ plugins_list for adding custom JS |
❌ Not implemented |
⚠️ Missing |
| Visual Regression Tests |
✅ Playwright-based |
❌ None |
❌ Missing |
| Unit Tests |
✅ pytest + tox |
❌ None |
❌ Missing |
| CI/CD |
✅ GitHub Actions |
❌ None |
❌ Missing |
| Documentation |
✅ ReadTheDocs site, contributing guide |
❌ README only |
❌ Missing |
Technical Review & Feature Comparison
Date: February 2026
Repo:
quantecon-theme-src(MyST/Jupyter Book 2.0 theme)Compared against:
quantecon-book-themev0.18.0 (Sphinx/Jupyter Book ≤2 theme)Part 1 — Technical Review of
quantecon-theme-src1.1 Architecture Overview
The new theme is a Remix v1.17 application built on the @myst-theme ecosystem. It serves as a MyST Markdown site template that is bundled and deployed to
QuantEcon/quantecon-themefor use by downstream projects viamyst start.1.2 Code Quality Assessment
Strengths
Clean component decomposition — Toolbar, Sidebar, Outline, Footer, etc. are well-separated single-responsibility components. The
toolbar/directory groups all toolbar-related buttons cleanly.Good use of myst-theme primitives — The theme correctly leverages
@myst-theme/providers(e.g.,useLinkProvider,useBaseurl,useNavOpen,useThemeSwitcher),@myst-theme/site(e.g.,useHeaders,useSidebarHeight), and@myst-theme/jupyter(e.g.,ComputeOptionsProvider,ThebeLoaderAndServer).TypeScript throughout — All source files are
.tsx/.tswith proper type imports. Thetsconfig.jsonenforcesstrict: true.Zero lint/compile errors — The codebase compiles cleanly with no TypeScript errors detected.
Accessible tooltips and ARIA — Radix UI
Tooltip,DropdownMenu, andPopovercomponents provide sensible accessibility defaults.SkipTonavigation is included inroot.tsx.Dark mode support — Uses Tailwind
darkMode: 'class'with a custom color palette (qepage-dark,qetoolbar-dark, etc.) that is consistently applied across components.Custom grid system — A well-designed
simple-center-gridusing CSS Grid named lines (screen-start,body-start,body-end,margin-start,margin-end,screen-end) provides clean body + margin layout.Proper React patterns —
React.memoonPageContent,useCallbackfor search factory,useMemoinPageProvider.Build pipeline — Clean separation between
devandprodRemix configs with aMakefilefor automated deployment to the bundled theme repo.Issues & Recommendations
High Priority
remix.config.dev.jsand a few CSS comments. Add JSDoc to all exported functions/components and purpose comments to modules.tox,pytest, and Playwright tests. Add at minimum component unit tests (Vitest/Testing Library) and consider Playwright visual regression tests..github/missing.gitignorevisible.gitignoreexists and coversnode_modules/,build/,.deploy/,public/build/,app/styles/app.css(generated).@remix-run/eslint-configbeing a devDependency, there is no.eslintrcor equivalent configuration file, and nolintscript inpackage.json.package.json@myst-themecompatibility, plan for migration. V2 future flags are enabled which is good.@myst-theme/siteToolbar.tsximport { Search } from '@myst-theme/site/src/components/Navigation/Search'imports fromsrc/which is fragile and could break on upstream updates. Use a public export or contribute one upstream.Medium Priority
node-fetchv2 usedloaders.server.tsnode-fetch@2(CJS) instead of the nativefetchavailable in Node 18+. If minimum Node version is raised (currently>=14, should be>=18), replace with native fetch.node: ">=14"engine requirementpackage.json>=18(current LTS baseline).root.tsxroot.tsxlines 48-55integrityattributes, (b) bundling locally, or (c) removing if unused.app.cssline 1Source Sans 3is loaded via@import url(...)to Google Fonts which blocks rendering. Consider self-hosting the font or using<link rel="preload">.keyprop in author listProjectFrontmatter.tsx<span>elements wrapping author names inside the.reduce()have nokeyprop. This triggers React warnings.aira-labelProjectFrontmatter.tsxline 52aira-labelshould bearia-label.SidebarToggle.tsxXicon hasaria-label="Show table of contents"(should be "Hide") and theMenuicon hasaria-label="Hide table of contents"(should be "Show"). These are reversed.FontScaleListItemsSSR safetyFontScaleListItems.tsxdocument.documentElement.style.fontSizeis accessed inuseMemowithout an SSR guard beyond thetypeof documentcheck. Consider usinguseEffectfor the initial value. The default state index1doesn't guarantee alignment with the actual font size.FullscreenButton.tsxdocument.fullscreenEnabledcheck to conditionally render.DownloadButton.tsxhandleDownloadcallstriggerDirectDownloadwithawaitbut has no try/catch. Failed downloads will silently error.Low Priority
hide_toc/hideSearchprops unusedNavigationAndArticleWrapper.tsxhide_tocandhideSearchprops are accepted but never passed through or used to conditionally render the sidebar or search.GridGuideis dead codeGridGuide.tsxpatches/@jupyter-widgets/controls,@types/react-syntax-highlighter, andjupyterlab-plotly. Document why each patch exists and track upstream fix status in a comment or table.README.mdtemplate.ymlauthorshiptemplate.ymlCONTRIBUTING.mdLICENSEfile completenesstemplate/LICENSEPart 2 — Feature Comparison: Old Theme vs. New Theme
2.1 Feature Parity Matrix
quantecon-book-theme)quantecon-theme-src)useHeadershookHouseicon link to baseurlmaximizeiconMaximize/Minimizeicons with stateCirclePlus/CircleMinuswith 3-step scalesunseticon, localStorage toggle,dark-themeclassSunseticon,useThemeSwitcherfrom providers, Tailwinddark:/_notebooks/{page}.ipynbCloudDownloaddropdown with project + page downloads via MyST configwindow.print()fallbackwindow.print()fallbackedit_urlfrom frontmatterconfig.parts.footergetFooterLinksin loader (page data), but no visible UI component renders themProjectFrontmatterwith title, authors, affiliationsqetheme_code_styletoggle, Pygments style override supportcolor_schemeoption (seoul256, gruvbox, none), custom CSS auto-detectionenable_rtloption withdir="rtl"on bodyuse_edit_page_buttonconfigurationedit_urlwhen availableuse_repository_button,use_issues_buttonpersistent_sidebaroption keeps sidebar always visiblesticky_contentswith back-to-top and scroll spyabsolutepositioned,BackToTopappears on scrollcontents_autoexpandfor sidebar sectionslaunch_buttons.thebeflagComputeOptionsProviderwithnotebookCompute/figureComputegetMetaTagsForSite/getMetaTagsForArticle[sitemap.xml].tsxroute with XSL stylesheet[robots.txt].tsxroute[objects.inv].tsxproxy routemyst.xref.jsonsupportplugins_listfor adding custom JS2.2 Summary of Missing Features (Prioritized)
Must-Have for Parity
These features exist in the old theme and are actively used by QuantEcon lecture sites:
Prev/Next Page Navigation — Footer links data is loaded (
getFooterLinks) but never rendered in the UI. Need to add aPageFooterNavcomponent.Git Last Modified Date + Changelog — The old theme shows "Last changed: {date}" with an expandable commit history. This is a significant UX feature for academic content. Implementation approach: either integrate with
gitvia MyST build metadata or create a separate API route.PDF Download with Print Fallback — The old theme has a dedicated PDF icon with a modal offering "Lecture PDF" and "Book PDF" options, plus a
window.print()fallback. The new theme's Downloads dropdown may not make this as discoverable.Should-Have
Color Scheme Options — Seoul256, Gruvbox, and custom CSS support. Evaluate if this is still needed or if the Tailwind-based dark mode is sufficient.
Persistent Sidebar Option — Allow the sidebar to remain visible by configuration rather than always being a slide-in overlay.
Repository/Issues Buttons — Separate toolbar buttons for viewing the repository and opening issues, distinct from the edit URL.
Collapsible Stderr Warnings — Check if
@myst-theme/jupyteralready provides this. If not, implement collapsible output for stderr cells.Nice-to-Have
RTL Support — Needed if QuantEcon plans multilingual content.
Contents Auto-Expand — Sidebar section expand/collapse behavior configuration.
Plugins System — The old theme's
plugins_listallows custom JS injection. Evaluate if this is needed in the MyST ecosystem.Configurable Code Highlighting — The old theme allows switching between custom QuantEcon and Pygments styles. Check if MyST defaults are adequate.
BinderHub Launch Option — Deferred to a future release (tracked in GitHub issue). The new theme currently supports Colab and private JupyterHub only.
Part 3 — Specific Code Fixes Recommended
3.1 Bug Fixes — ✅ Fixed in PR #31
3.2 Suggested File Organization
3.3 Missing Infrastructure
.github/workflows/ci.yml.eslintrc.js@remix-run/eslint-configvitest.config.tsplaywright.config.ts.gitignoreCONTRIBUTING.md.prettierrc.nvmrc18)Part 4 — Overall Assessment
What the New Theme Does Well
What Still Needs Work
Minor bugs— Fixed in PR #31.Recommended Action Items (Ordered)
Fix the bugs identified in Section 3.1— ✅ PR #31Set up CI pipeline (typecheck + build)— ✅ PR #31Update Node engine requirement— ✅ PR #31 (>=18, .nvmrc)Add CONTRIBUTING.md— ✅ PR #31Remix v2 migration— tracked in #28Completed During Review
Part 5 — Download & Export: Theme Gaps
5.1 Notebook Download Button
The theme's
DownloadButtoncomponent already supports configurable downloads via MyST'sexports+downloadsfrontmatter. Oncemyst build --ipynbis available (see meta#292), notebooks will be built at CI time and served as static assets. The download button will work with no theme changes — just the appropriatemyst.ymlconfiguration:Status: ✅ Ready (blocked on upstream
myst build --ipynbmerging)5.2 PDF Download Parity
mystmdsupports per-page and whole-book PDF export via both LaTeX and Typst. The theme's Downloads dropdown will surface these when configured in frontmatter:pdf_book_pathexports+downloadsfrontmatterlatex_documentsconfigexportsconfigwindow.print()on toolbar icon5.3 Missing:
window.print()FallbackThe old theme provided a
window.print()fallback when no pre-built PDF was available. The new theme'sDownloadButtondoes not offer this.Recommendation: Add a "Print this page" option to the Downloads dropdown that calls
window.print(). This is a small theme-level change inDownloadButton.tsx.Part 6 — PR Triage & Issues Opened
As part of this review (February 2026), all open PRs were triaged and resolved:
PRs Merged
PRs Closed (Not Merged)
PRs Created & Merged (During Review)
Issues Opened
Part 7 — Security & Dependency Upgrade Summary
Work Completed
Remix peer dependency fix (PR Fix CI: bump @remix-run/* from 1.17 to 1.19 (peer dependency conflict) #29): Bumped
@remix-run/*from~1.17.0to~1.19.0to satisfy@myst-theme/site@0.14.0peer dependency. Fixed CIERESOLVEfailure.Non-breaking audit fixes (PR Fix CI: bump @remix-run/* from 1.17 to 1.19 (peer dependency conflict) #29): Ran
npm audit fix, added npm overrides forprismjs(^1.30.0) andkatex(^0.16.21). Reduced vulnerabilities from 60 → 37.@myst-theme v1 upgrade (PR Upgrade @myst-theme/* from 0.14.x to 1.1.2 #30): Bumped all 7
@myst-theme/*packages +myst-to-react+myst-common+myst-configfrom 0.14.x to 1.1.2. Removedcytoscapeoverride (mermaid 9→11 resolved the incompatibility). Reduced vulnerabilities from 37 → 34.@vercel/node upgrade investigated: Tested
@vercel/nodev2→v5. Result: v5 introduced more vulnerabilities (38 vs 34). Reverted — staying on v2.Remaining 34 Vulnerabilities (Ecosystem-Wide)
Surveyed all projects in the myst-theme ecosystem (upstream myst-theme/book, curvenote/overlay, curvenote/landing). No project has migrated to Remix v2. The remaining 34 vulnerabilities affect every myst-theme consumer equally.
@remix-run/verceldeprecated in v2, needs@vercel/remix.Ecosystem Alignment (as of Feb 2026)
~1.19.0~1.17.0^1.19.3^1.1.2^1.1.2^0.13.3^2.15.1^2.15.1^2.15.9npm@8.10.0npm@8.10.0npm@8.10.0