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
Part of #147 (parity study 2026-09-11 — content correctness).
Problem
Plots produced by a lecture's code cells sit at the left edge of the content column, with all the spare width on their right. The Sphinx lecture sites centre the same plots. The lecture-python-programming deploy preview is built on v2.7.0. On its /matplotlib page at 1440px, each 568px-wide matplotlib image sits flush left in its 800px container: 0px gap on the left, 232px on the right (verified 2026-09-11). That page has 16 image outputs, all rendered with the same markup.
The same code path renders every code-cell output whose MIME bundle holds only PNG, GIF, JPEG or BMP data plus text/plain, which is what an inline matplotlib figure stores. It applies whenever live compute is not running. Every such plot narrower than the column will therefore sit flush left on the lecture sites that move to this theme.
The server-rendered markup on that page is three nested elements: div[data-name="outputs-container"] carrying text-left, then div[data-name="safe-output-image"], then a bare <img> with no class or style. Each layer comes from upstream or the CLI, and the theme changes none of them:
Layer
Effect on an output image
Evidence
Tailwind preflight
Every img is display: block with max-width: 100%, and preflight gives it no side margins
tailwindcss 3.4.19, src/css/preflight.css lines 361-371 and 377-381
Outputs container
text-left unless the outputs node has an align
@myst-theme/jupyter 1.3.0, dist/outputs.js lines 24 and 41-46
Output router
While live compute is not running, an output whose MIME types are all text/plain, PNG, GIF, JPEG or BMP goes to SafeOutput
@myst-theme/jupyter 1.3.0, dist/output.js lines 17-40 and 70-72
SafeOutput
A bare <img> with only src and alt, inside <div data-name="safe-output-image">
@myst-theme/jupyter 1.3.0, dist/safe.js lines 39-42 and 55-56
mystmd fork
Only two places build an outputs node, and neither sets align, so the container gets text-left
Changing the container's text alignment would not help, because text-align does not move a block-level element. Markdown and {figure} images are already centred: myst-to-react defaults their align to center and applies it as auto side margins (myst-to-react 1.3.0, dist/image.js lines 23-34 and 40-44). Output images never go through that renderer.
The book theme centres output images with auto side margins inside .qe-page__content. Two rules do it: .output_png img and .cell_output img. On python-programming.quantecon.org, the matplotlib lecture's 16 plots are bare <img> elements directly inside div.cell_output. The live stylesheet ships the compiled rule .qe-page__content .cell_output img{display:block;margin-left:auto;margin-right:auto} (checked 2026-09-11).
The visual suite cannot catch this. The fixture notebook behind the notebook.png snapshot stores only a stdout stream, a text/plain result and an error (fixture/notebook.ipynb L13-L71; see also the note at theme.spec.ts L3-L8). The no-thebe fixture's copy adds only a stderr cell. Neither notebook stores an image output.
Proposed change
Add a rule to styles/quantecon.css that gives an img inside a notebook outputs container auto left and right margins, matching the book theme's .cell_output img. Select on the upstream container attributes, so no renderer override is needed:
[data-name="outputs-container"] img for static output.
[data-name="active-outputs-container"] img for output once live compute starts. Starting compute re-renders the stored outputs inside that container (@myst-theme/jupyter 1.3.0, dist/active.js lines 36-39 and 74).
Like the book theme's rule, this applies to every img in a cell's outputs, not only to matplotlib figures.
It is an element rule, so the file's layering note (styles/quantecon.css L36-L44) places it in the @layer base block that opens at L116. Its selector names no class, so Tailwind always emits it, whatever it finds while scanning content (tailwindcss 3.4.19, lib/lib/setupContextUtils.js lines 230-255, 344-356 and 701-708). Preflight already supplies display: block and max-width: 100%.
Cover it in the visual suite:
Add a code cell to tests/visual/fixture/notebook.ipynb with a stored image/png and text/plain output. Make the image narrower than the desktop column but wider than the mobile one. Then notebook.png shows an image that has room to centre on desktop and has to cap on mobile.
Add a DOM assertion that loads /notebook on the thebe-enabled fixture, the one behind notebook.png, and measures the image's left and right gaps inside its outputs container. Follow the shape of the existing stderr-collapsed test but not its URL: that test loads the no-thebe fixture, which will not carry the new cell.
Acceptance criteria
In the desktop-chrome project, the fixture's image output has equal left and right gaps inside its outputs container (within 1px), asserted in tests/visual/theme.spec.ts.
In the mobile-chrome project, the image is no wider than its outputs container and the page does not scroll horizontally.
In the refreshed notebook.png, the existing stream, plain-text and error outputs are unchanged and still left-aligned; the new image cell is the only change.
The intro, features and lists snapshots pass without a baseline update.
The notebook.png baselines for desktop-chrome and mobile-chrome are refreshed: -darwin locally, -linux via /update-snapshots.
After live compute is started on the thebe-enabled fixture's notebook page, the stored image, now inside [data-name="active-outputs-container"], is also centred (manual check).
On a lecture-python-programming deploy preview built with the change, the /matplotlib plots at 1440px have equal left and right gaps.
Context
Source: the 2026-09-11 book-theme parity study, row F043 (figures, captions and output images; verdict partial, relevance high).
The study's section 1 lists left-aligned plots among the six gaps that break or visibly degrade most sites. Its section 3.1 recommends adding F043 to the Book-theme parity cutover — tracking #147 gate once it is confirmed on a rendered page. Its verification addendum records that confirmation on the lecture-python-programming preview.
Out of scope: F043 also notes that the caption number renders at weight 600 (font-semibold), where the book theme uses 700. That is a separate typography question.
Part of #147 (parity study 2026-09-11 — content correctness).
Problem
Plots produced by a lecture's code cells sit at the left edge of the content column, with all the spare width on their right. The Sphinx lecture sites centre the same plots. The lecture-python-programming deploy preview is built on v2.7.0. On its /matplotlib page at 1440px, each 568px-wide matplotlib image sits flush left in its 800px container: 0px gap on the left, 232px on the right (verified 2026-09-11). That page has 16 image outputs, all rendered with the same markup.
The same code path renders every code-cell output whose MIME bundle holds only PNG, GIF, JPEG or BMP data plus
text/plain, which is what an inline matplotlib figure stores. It applies whenever live compute is not running. Every such plot narrower than the column will therefore sit flush left on the lecture sites that move to this theme.The server-rendered markup on that page is three nested elements:
div[data-name="outputs-container"]carryingtext-left, thendiv[data-name="safe-output-image"], then a bare<img>with no class or style. Each layer comes from upstream or the CLI, and the theme changes none of them:imgisdisplay: blockwithmax-width: 100%, and preflight gives it no side marginstext-leftunless the outputs node has analigntext/plain, PNG, GIF, JPEG or BMP goes toSafeOutputSafeOutput<img>with onlysrcandalt, inside<div data-name="safe-output-image">align, so the container getstext-leftoutputoverride wraps stderr streams and passes every other output to upstream. Its onlyimgrule targets the footer licence badge.Changing the container's text alignment would not help, because
text-aligndoes not move a block-level element. Markdown and{figure}images are already centred: myst-to-react defaults theiraligntocenterand applies it as auto side margins (myst-to-react 1.3.0, dist/image.js lines 23-34 and 40-44). Output images never go through that renderer.The book theme centres output images with auto side margins inside
.qe-page__content. Two rules do it:.output_png imgand.cell_output img. On python-programming.quantecon.org, the matplotlib lecture's 16 plots are bare<img>elements directly insidediv.cell_output. The live stylesheet ships the compiled rule.qe-page__content .cell_output img{display:block;margin-left:auto;margin-right:auto}(checked 2026-09-11).The visual suite cannot catch this. The fixture notebook behind the
notebook.pngsnapshot stores only a stdout stream, atext/plainresult and an error (fixture/notebook.ipynb L13-L71; see also the note at theme.spec.ts L3-L8). The no-thebe fixture's copy adds only a stderr cell. Neither notebook stores an image output.Proposed change
Add a rule to
styles/quantecon.cssthat gives animginside a notebook outputs container auto left and right margins, matching the book theme's.cell_output img. Select on the upstream container attributes, so no renderer override is needed:[data-name="outputs-container"] imgfor static output.[data-name="active-outputs-container"] imgfor output once live compute starts. Starting compute re-renders the stored outputs inside that container (@myst-theme/jupyter 1.3.0, dist/active.js lines 36-39 and 74).Like the book theme's rule, this applies to every
imgin a cell's outputs, not only to matplotlib figures.It is an element rule, so the file's layering note (styles/quantecon.css L36-L44) places it in the
@layer baseblock that opens at L116. Its selector names no class, so Tailwind always emits it, whatever it finds while scanningcontent(tailwindcss 3.4.19, lib/lib/setupContextUtils.js lines 230-255, 344-356 and 701-708). Preflight already suppliesdisplay: blockandmax-width: 100%.Cover it in the visual suite:
tests/visual/fixture/notebook.ipynbwith a storedimage/pngandtext/plainoutput. Make the image narrower than the desktop column but wider than the mobile one. Thennotebook.pngshows an image that has room to centre on desktop and has to cap on mobile./notebookon the thebe-enabled fixture, the one behindnotebook.png, and measures the image's left and right gaps inside its outputs container. Follow the shape of the existingstderr-collapsedtest but not its URL: that test loads the no-thebe fixture, which will not carry the new cell.Acceptance criteria
desktop-chromeproject, the fixture's image output has equal left and right gaps inside its outputs container (within 1px), asserted intests/visual/theme.spec.ts.mobile-chromeproject, the image is no wider than its outputs container and the page does not scroll horizontally.notebook.png, the existing stream, plain-text and error outputs are unchanged and still left-aligned; the new image cell is the only change.intro,featuresandlistssnapshots pass without a baseline update.notebook.pngbaselines fordesktop-chromeandmobile-chromeare refreshed:-darwinlocally,-linuxvia/update-snapshots.[data-name="active-outputs-container"], is also centred (manual check).Context
font-semibold), where the book theme uses 700. That is a separate typography question.