Headline: the toolbar and lecture typography now match the existing Sphinx builds — an icon-only search trigger, PT Serif headings on the Sphinx scale, and QuantEcon-blue content links — and every toolbar control gets a proper accessible name in place of a nested button. The notebook launcher collapses to a direct Colab link, dropping the Private JupyterHub option no lecture site ever configured. The "back to top" button no longer fades out of the page margin on load, and the WebKit FOUC guard is hardened against the hydration race that could turn it red for reasons unrelated to the critical CSS.
Changed
- The toolbar search trigger is now a plain magnifier icon, matching the search affordance on the existing lecture sites and the styling of the neighbouring toolbar icons, in place of the boxed "Search ⌘K" placeholder. The ⌘K / Ctrl K keyboard hint moves into the search dialog, overlaid on the right edge of the text input. Because
@myst-theme/siteexports only the top-levelSearchcomponent,Search.tsxis forked verbatim from@myst-theme/site@1.3.0intoapp/components/toolbar/apart from the trigger and hint placement; search behaviour, keyboard navigation and platform detection are unchanged (#165). - Lecture headings now follow the Sphinx builds' typography.
h1–h3are set in PT Serif withh4/h5kept in the sans face, sized on the Sphinx scale (h12em, h21.7em, h31.4em, h41.2emof the content size, so the scale tracks the font-scale controls), with every level at line-height 1.15 andh4/h5at weight 900 to match the Sphinx build's measured values. All of it is mirrored into the critical CSS so the first WebKit paint on static-build navigations is already serif at the final sizes rather than painting the title small and swapping (#166). - Content links are coloured to match the Sphinx lecture builds:
#0072bc(5.05:1 on white, WCAG AA) with#004979on hover, and white in both states in dark mode as the Sphinx dark theme does. Both of myst-to-react's anchor classes are covered —.linkfor plain links and.hover-linkfor cross-references, citations and footnotes — via--qe-link-color/--qe-link-hover-colortokens instyles/quantecon.css, overriding the upstream blue-700/blue-400 at matched specificity, with the underline drawn incurrentColorso it always follows the text. Colour only: the Sphinx builds' hover-only underline and:visitedcolour are deliberately not matched here. Site chrome (footer badge, back-to-top) keeps its own rules (#167).
Removed
- The Private JupyterHub launch option. The toolbar's Launch control is now a direct link to Google Colab instead of a popover with a radio group and a hand-typed service URL. The demand signal this was gated on came back empty: the book-theme only renders its hub button when
jupyterhub_urlis set in_config.yml, and an org-wide search finds that key in no lecture repo at all — every flagship series configurescolab_urlalone — so the button has never appeared on a live QuantEcon site. The MyST version was also not the same feature: it had no config key, rendered unconditionally, and asked the reader to type a hub URL into a box whose value wasReact.useState('')and so was discarded when the popover closed. Verified against a live build, it also hardcoded a/jupyter/hub/path segment the Sphinx theme leaves to the configured URL, so it only ever addressed hubs at<host>/jupyter/hub/and could not reach a stock JupyterHub, let alone a local Jupyter server. Removing it dropsbuildJupyterHubUrl, the Radix popover and radio group, and thelaunch-open.pngvisual baseline;launch-colabnow asserts the anchor'shref, which also pins that the control is a link rather than a chooser. Re-adding hub, Binder and local-server targets is tracked as future work (#87).
Fixed
- Every toolbar control was nesting a second
<button>inside its real button or link, because the Radix tooltip trigger renders its own element by default: invalid HTML and a duplicate tab and screen-reader stop on each control. Tooltips now merge onto the real interactive element (asChild), icon-only triggers that had no accessible name get anaria-label(the mobile "more actions" menu no longer announces itself as "Downloads"), two straytabIndexattributes on decorative icons are dropped, and tooltips open on keyboard focus of the actual control. Closing the search dialog no longer leaves its tooltip open over the trigger (#165). - The "back to top" button no longer fades out of the page margin on load. It is hidden by
opacity-0and carries atransition-opacity, so on any frame where the stylesheet is absent it painted at full opacity and then animated away once the sheet landed. That frame is not the first paint alone: the React hydration recovery (#126) re-renders the head and briefly drops the stylesheet after the component has mounted, which is why gating the transition on mount cannot stop it. The critical CSS now pins the button toopacity: 0on the same zero-specificity terms as the toggle's close icon, and the WebKit FOUC guard asserts it — with the rule the button never paints, without it the control sees it at full opacity. Measured with the stylesheet delayed: 17–18 animating frames before, none after. Supersedes theuseMountedapproach in #141. - The WebKit FOUC guard no longer goes red for reasons unrelated to the critical CSS. Hydration currently fails on every page load (React #418/#423), and the recovery re-render restores the inlined critical
<style>150–240ms afterDOMContentLoaded— after the control test has deliberately stripped it from the served HTML to prove the guard is meaningful. The measurement used to run from the test afterdomcontentloaded, so on a loaded runner every control assertion flipped at once. It now samples from an init script that fires in-page onDOMContentLoaded, before hydration is even scheduled. Both cases also assert their own preconditions — that the strip actually matched, and whether the inline block is present as sampled — so a reshapedCRITICAL_CSSreports as a stale strip pattern instead of as a run of confusing failures about grid layout. The underlying hydration failure remains open in #126; carried over from #141.
Dependencies
@fontsource/pt-serif5.3.0 self-hosts the PT Serif heading face (400/700, upright and italic), routed through the same Remix/esbuild pipeline as Source Sans 3, so no Google Fonts request (#166).