-
-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Technical Review & Feature Comparison
Date: February 2026
Repo: quantecon-theme-src (MyST/Jupyter Book 2.0 theme)
Compared against: quantecon-book-theme v0.18.0 (Sphinx/Jupyter Book ≤2 theme)
Part 1 — Technical Review of quantecon-theme-src
1.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-theme for use by downstream projects via myst start.
| Layer | Technology |
|---|---|
| Framework | Remix 1.17 (CJS, Vercel-compatible) |
| UI | React 18, TailwindCSS 3, Radix UI primitives |
| Content | @myst-theme/* v0.14.0 suite |
| Compute | Thebe (via @myst-theme/jupyter) |
| Search | MiniSearch (via @myst-theme/search-minisearch) |
| Icons | Lucide React |
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
| # | Issue | Location | Recommendation |
|---|---|---|---|
| 1 | Missing JSDoc / code comments | All files | Almost no function, component, or module has documentation comments. The only comments are in remix.config.dev.js and a few CSS comments. Add JSDoc to all exported functions/components and purpose comments to modules. |
| 2 | No tests | Project root | There are zero test files. No unit tests, no integration tests, no visual regression tests. The old theme has tox, pytest, and Playwright tests. Add at minimum component unit tests (Vitest/Testing Library) and consider Playwright visual regression tests. |
| 3 | No CI/CD pipeline | .github/ missing |
No GitHub Actions workflows. The old theme has extensive CI. Add workflows for: lint, typecheck, build, test, and deploy. |
| 4 | No .gitignore visible |
Project root | Verify .gitignore exists and covers node_modules/, build/, .deploy/, public/build/, app/styles/app.css (generated). |
| 5 | No ESLint config | Project root | Despite @remix-run/eslint-config being a devDependency, there is no .eslintrc or equivalent configuration file, and no lint script in package.json. |
| 6 | Remix v1 is end-of-life | package.json |
Remix 1.17 is very old (2023). The ecosystem has moved to Remix v2 / React Router v7. While this may be constrained by @myst-theme compatibility, plan for migration. V2 future flags are enabled which is good. |
| 7 | Deep import from @myst-theme/site |
Toolbar.tsx |
import { Search } from '@myst-theme/site/src/components/Navigation/Search' imports from src/ which is fragile and could break on upstream updates. Use a public export or contribute one upstream. |
Medium Priority
| # | Issue | Location | Recommendation |
|---|---|---|---|
| 8 | node-fetch v2 used |
loaders.server.ts |
Using node-fetch@2 (CJS) instead of the native fetch available in Node 18+. If minimum Node version is raised (currently >=14, should be >=18), replace with native fetch. |
| 9 | node: ">=14" engine requirement |
package.json |
Node 14 is long EOL. Set to >=18 (current LTS baseline). |
| 10 | Hardcoded CDN URLs in root.tsx |
root.tsx lines 48-55 |
jupyter-matplotlib CSS v0.11.3 and font-awesome 4.7.0 are loaded via external CDN with no SRI. Consider: (a) adding integrity attributes, (b) bundling locally, or (c) removing if unused. |
| 11 | Font loaded via Google Fonts | app.css line 1 |
Source Sans 3 is loaded via @import url(...) to Google Fonts which blocks rendering. Consider self-hosting the font or using <link rel="preload">. |
| 12 | Missing key prop in author list |
ProjectFrontmatter.tsx |
The <span> elements wrapping author names inside the .reduce() have no key prop. This triggers React warnings. |
| 13 | Typo: aira-label |
ProjectFrontmatter.tsx line 52 |
aira-label should be aria-label. |
| 14 | Swapped ARIA labels | SidebarToggle.tsx |
The X icon has aria-label="Show table of contents" (should be "Hide") and the Menu icon has aria-label="Hide table of contents" (should be "Show"). These are reversed. |
| 15 | FontScaleListItems SSR safety |
FontScaleListItems.tsx |
document.documentElement.style.fontSize is accessed in useMemo without an SSR guard beyond the typeof document check. Consider using useEffect for the initial value. The default state index 1 doesn't guarantee alignment with the actual font size. |
| 16 | FullScreen API edge cases | FullscreenButton.tsx |
No handling for fullscreen API unavailability (e.g., some mobile browsers, iframes). Add a document.fullscreenEnabled check to conditionally render. |
| 17 | No error handling on download trigger | DownloadButton.tsx |
handleDownload calls triggerDirectDownload with await but has no try/catch. Failed downloads will silently error. |
Low Priority
| # | Issue | Location | Recommendation |
|---|---|---|---|
| 18 | hide_toc / hideSearch props unused |
NavigationAndArticleWrapper.tsx |
The hide_toc and hideSearch props are accepted but never passed through or used to conditionally render the sidebar or search. |
| 19 | GridGuide is dead code |
GridGuide.tsx |
Not imported anywhere. Either remove or document as a development debugging utility. |
| 20 | Patches directory | patches/ |
Three patch files exist for @jupyter-widgets/controls, @types/react-syntax-highlighter, and jupyterlab-plotly. Document why each patch exists and track upstream fix status in a comment or table. |
| 21 | Duplicated "Development" section | README.md |
The README has two "## Development" sections (lines 67 and 86). Consolidate. |
| 22 | template.yml authorship |
template.yml |
Lists only Curvenote contributors as authors. Consider adding QuantEcon as an organizational author. |
| 23 | Missing CONTRIBUTING.md |
Project root | The old theme has a detailed contributing guide. Add one for the new theme. |
| 24 | Missing LICENSE file completeness |
template/LICENSE |
Verify the template LICENSE is appropriate and consistent with the root LICENSE. |
Part 2 — Feature Comparison: Old Theme vs. New Theme
2.1 Feature Parity Matrix
| Feature | Old Theme (quantecon-book-theme) |
New Theme (quantecon-theme-src) |
Status |
|---|---|---|---|
| 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) | 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 | |
| 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) | |
| Code Syntax Highlighting Config | ✅ qetheme_code_style toggle, Pygments style override support |
❌ Not configurable (relies on myst-theme defaults) | |
| 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 | |
| Persistent Sidebar | ✅ persistent_sidebar option keeps sidebar always visible |
❌ Sidebar slides in/out only | |
| 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 | |
| MathJax Macros | ✅ Custom LaTeX macros (EE, PP, RR, etc.) built into theme | ❌ Not configured at theme level | |
| 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 | |
| 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 |
2.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
// ProjectFrontmatter.tsx — Fix typo: "aira-label" → "aria-label"
<div aria-label="Author names and links">
// ProjectFrontmatter.tsx — Add key prop to span elements
// Inside the reduce, add key={a.id ?? a.name ?? i} to each <span>
// SidebarToggle.tsx — Fix swapped ARIA labels
<X ... aria-label="Hide table of contents" />
<Menu ... aria-label="Show table of contents" />3.2 Suggested File Organization
app/
├── backend/
│ └── loaders.server.ts ✅ Good
├── components/
│ ├── layout/ ← Group layout components
│ │ ├── ContentsSidebar.tsx
│ │ ├── NavigationAndArticleWrapper.tsx
│ │ └── GridGuide.tsx
│ ├── content/ ← Group content components
│ │ ├── PageContent.tsx
│ │ ├── ProjectFrontmatter.tsx
│ │ ├── Outline.tsx
│ │ └── SiteFooter.tsx
│ ├── toolbar/ ✅ Good
│ ├── ErrorPage.tsx
│ ├── Page.tsx
│ └── PageProvider.tsx
├── hooks/ ✅ Good
├── routes/ ✅ Good
├── styles/ (move to app/ or keep at root)
└── types.ts ✅ Good
3.3 Missing Infrastructure
| Item | Recommendation | Status |
|---|---|---|
.github/workflows/ci.yml |
TypeScript check, build test, lint | ✅ Added in PR #31 |
.eslintrc.js |
Use @remix-run/eslint-config |
Open |
vitest.config.ts |
Unit tests with React Testing Library | Open |
playwright.config.ts |
Visual regression tests | Open |
.gitignore |
Ensure complete | Open |
CONTRIBUTING.md |
Development setup, PR guidelines | ✅ Added in PR #31 |
.prettierrc |
Codify formatting rules (Prettier is in devDeps) | Open |
.nvmrc |
Pin Node version (e.g., 18) |
✅ Added in PR #31 |
Part 4 — Overall Assessment
What the New Theme Does Well
- Modern stack: React 18 + Tailwind + Radix UI is a much cleaner foundation than the jQuery + Bootstrap 4 + Jinja2 templates of the old theme.
- MyST integration: Deeply integrated with the @myst-theme ecosystem, leveraging its providers, search, and Jupyter compute capabilities.
- Dark mode: More robust implementation using Tailwind's class-based dark mode rather than manual CSS class toggling.
- Mobile: The responsive mobile menu with Radix DropdownMenu is a significant improvement.
- SEO/Web standards: Sitemap, robots.txt, and proper meta tags are built in at the route level.
- Configurable footer: Content-driven rather than hardcoded.
- Downloads: Flexible configuration through MyST's download/export system.
What Still Needs Work
- Testing — Zero tests remain the biggest gap. CI pipeline now runs typecheck + build (PR fix: quick wins from technical review (bugs, CI, Node, docs) #31), but no unit or integration tests yet.
- Documentation — CONTRIBUTING.md added (PR fix: quick wins from technical review (bugs, CI, Node, docs) #31), but inline code comments and JSDoc are still missing.
- Feature gaps — Prev/Next navigation, git metadata, BinderHub support are significant omissions.
Minor bugs— Fixed in PR #31.- Dependency modernization — Remix v1 EOL and node-fetch v2 remain. Node engine updated to >=18 (PR fix: quick wins from technical review (bugs, CI, Node, docs) #31).
Recommended Action Items (Ordered)
Fix the bugs identified in Section 3.1— ✅ PR #31- Add prev/next page navigation UI
Set up CI pipeline (typecheck + build)— ✅ PR #31- Add at least smoke-level tests
- Add code comments/JSDoc to all components
- Evaluate git metadata feature need
Update Node engine requirement— ✅ PR #31 (>=18, .nvmrc)Add CONTRIBUTING.md— ✅ PR #31Remix v2 migration— tracked in #28
Completed During Review
- ✅ Resolve all 16 open PRs (14 merged, 2 closed) — see Part 6
- ✅ Fix CI ERESOLVE failure — PR #29
- ✅ Upgrade @myst-theme/* to v1.1.2 — PR #30
- ✅ Reduce npm vulnerabilities from 60 → 34 (PRs Fix CI: bump @remix-run/* from 1.17 to 1.19 (peer dependency conflict) #29, Upgrade @myst-theme/* from 0.14.x to 1.1.2 #30)
- ✅ Document ecosystem alignment (all myst projects on Remix v1)
- ✅ Fix bugs, add CI pipeline, Node engine update, CONTRIBUTING.md — PR #31
- ✅ Fix pre-existing type-check errors caught by new CI — PR #32
Part 5 — Download & Export: Theme Gaps
The upstream
myst build --ipynbwork (PR jupyter-book/mystmd#1882) is tracked separately in QuantEcon/meta#292. This section covers only theme-level gaps related to downloads and exports.
5.1 Notebook Download Button
The theme's DownloadButton component already supports configurable downloads via MyST's exports + downloads frontmatter. Once myst build --ipynb is 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 appropriate myst.yml configuration:
# page frontmatter
downloads:
- id: lecture-notebook
title: Notebook (.ipynb)Status: ✅ Ready (blocked on upstream myst build --ipynb merging)
5.2 PDF Download Parity
mystmd supports per-page and whole-book PDF export via both LaTeX and Typst. The theme's Downloads dropdown will surface these when configured in frontmatter:
downloads:
- id: lecture-pdf
title: Lecture (PDF)
- id: book-pdf
title: Book (PDF)| Aspect | Old Theme (Sphinx/LaTeX) | New Theme (mystmd) |
|---|---|---|
| Per-page PDF | ✅ via LaTeX build + pdf_book_path |
✅ via exports + downloads frontmatter |
| Whole-book PDF | ✅ via latex_documents config |
✅ via multi-article exports config |
| Print fallback | ✅ window.print() on toolbar icon |
❌ Not implemented |
| PDF engine options | LaTeX only | LaTeX or Typst |
5.3 Missing: window.print() Fallback
The old theme provided a window.print() fallback when no pre-built PDF was available. The new theme's DownloadButton does 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 in DownloadButton.tsx.
Part 6 — PR Triage & Issues Opened
As part of this review (February 2026), all open PRs were triaged and resolved:
PRs Merged
| PR | Title | Type |
|---|---|---|
| #8 | v1.1.1 — Colab .myst suffix fix, version bump, patch cleanup | Release |
| #23 | Bump vm2 3.9.19 → 3.10.3 | Security (critical — sandbox escape) |
| #14 | Bump vite 5.4.14 → 5.4.21 | Security (XSS/SSRF) |
| #25 | Bump qs + express | Security (prototype pollution) |
| #22 | Bump lodash 4.17.21 → 4.17.23 | Security (prototype pollution) |
| #21 | Bump lodash-es 4.17.21 → 4.17.23 | Security |
| #13 | Bump tar-fs 2.1.2 → 2.1.4 | Security (path traversal) |
| #24 | Bump webpack 5.98.0 → 5.105.0 | Security |
| #27 | Bump minimatch 3.1.2 → 3.1.3 | Security (ReDoS) |
| #5 | Bump @babel/runtime 7.26.9 → 7.27.6 | Dependency |
| #6 | Bump @babel/helpers 7.26.9 → 7.27.6 | Dependency |
| #9 | Bump brace-expansion 1.1.11 → 1.1.12 | Dependency |
| #10 | Bump on-headers, compression, morgan | Dependency |
| #11 | Bump form-data | Dependency |
| #15 | Bump js-yaml | Dependency |
| #20 | Bump diff | Dependency |
PRs Closed (Not Merged)
| PR | Title | Reason |
|---|---|---|
| #18 | Bump @remix-run/node 1.17.1 → 2.17.2 | Remix v1→v2 requires coordinated migration, not individual bumps |
| #19 | Bump @remix-run/react 1.17.1 → 2.17.3 | Same — tracked in #28 |
PRs Created & Merged (During Review)
| PR | Title | Impact |
|---|---|---|
| #29 | Fix CI: bump @remix-run/* 1.17→1.19, security fixes | Fixed ERESOLVE CI failure, reduced vulns 60→37, added npm overrides |
| #30 | Upgrade @myst-theme/* 0.14.x→1.1.2 | Major dep upgrade, aligned with upstream, reduced vulns 37→34 |
| #31 | Quick wins: bug fixes, CI, Node engine, CONTRIBUTING.md | Fix 3 bugs, add CI pipeline, .nvmrc, contributor guide |
| #32 | Fix CI type-check errors (lodash types, Buffer→Uint8Array) | Resolve 3 pre-existing type errors caught by new CI pipeline |
Issues Opened
| Issue | Repo | Title |
|---|---|---|
| #26 | quantecon-theme-src | BinderHub launch button support |
| #28 | quantecon-theme-src | Migrate from Remix v1 to v2 |
| meta#292 | meta | Notebook export: contribute to mystmd PR #1882 (myst build --ipynb) |
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.
| Blocker | Vulns | Status |
|---|---|---|
| Remix v2 migration | ~13 | Tracked in #28. @remix-run/vercel deprecated in v2, needs @vercel/remix. |
| @myst-theme upstream (nanoid, markdown-it) | ~9 | Awaiting upstream fixes in myst-to-react, myst-transforms |
| Remix dev toolchain (tar, cacache, esbuild, vite, cookie) | ~7 | Dev-only deps, not in production bundles |
| @vercel/node v2 transitive (ajv, path-to-regexp, esbuild) | ~5 | v5 upgrade makes it worse; blocked on ecosystem |
Ecosystem Alignment (as of Feb 2026)
| Aspect | quantecon-theme-src | myst-theme/book (upstream) | curvenote/overlay |
|---|---|---|---|
| @remix-run/* | ~1.19.0 |
~1.17.0 |
^1.19.3 |
| @myst-theme/* | ^1.1.2 |
^1.1.2 |
^0.13.3 |
| @vercel/node | ^2.15.1 |
^2.15.1 |
^2.15.9 |
| packageManager | npm@8.10.0 |
npm@8.10.0 |
npm@8.10.0 |
| Remix version | v1 + v2 future flags | v1 + v2 future flags | v1 + v2 future flags |