Migrate onto the Moderne design system and rework the header - #538
Merged
Conversation
…eader Depend on @moderneinc/design-system-tokens@7.3.0 and bridge Infima's --ifm-* variables onto its --mod-* tokens, replacing 48 colour literals. Rework the header into the two-row layout docs.moderne.io uses — navbar above a section nav — and scope the sidebar to the selected section. Colours baked inside data-URI SVGs cannot take a CSS variable, so the carets, breadcrumb separator, pagination arrows and footer mark are redrawn as mask-image, which let three hand-written dark-mode overrides be deleted. The header's height is measured rather than declared: the announcement bar wraps on mobile, so any constant is wrong there. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
Raise the navbar to 72px so the logo clears the announcement bar by 20px rather than 12px. Pin card-hover text to --mod-ink-800. The mint hover ground is identical in both themes, so text following the colour mode went near-white on green in dark mode — 1.11:1, unreadable. Pinned it is 11.96:1, matching how the announcement bar already handles the same ground. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
zieka
force-pushed
the
refactor/migrate-moderne-design
branch
from
August 31, 2026 15:26
51a1884 to
73df896
Compare
Member
Author
|
…ing page Nav bar: Training moves under Authoring Recipes in the sidebar tree, Lists takes its place as a top-level section, and the More menu becomes Releases — the three most recent releases plus All releases. The release list is read from the Changelog category in sidebars.ts rather than hand-written, since an automated job adds an entry on every release. Licensing moves out of the nav into the footer, matching docs.moderne.io. The introduction page reads as a landing page: no sidebar, green accents. The sidebar is detached with `displayed_sidebar: null` rather than hidden in CSS, which also releases the width Docusaurus reserves for the column. Accents are a mode-aware pair because no single green clears AA on both grounds: mint-700 is 5.12:1 on the light ground, the brand mint 14.54:1 on the dark one and 1.11:1 on the light. Adds src/theme/Root.tsx to publish the route as data-route. Docusaurus's own docs-doc-id-* classes exist only in a production build, so styling one page through them renders differently under `yarn start`. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
…ault Swizzle Footer to match docs.moderne.io: copyright and links left, social right, over a hairline. The Infima default stacked its single link above a centred copyright and the Moderne mark, right-aligned against the page edge. Adds Terms, Privacy and Contact us alongside Licensing, and X/LinkedIn/YouTube — the same set docs.moderne.io carries, consistent with the Moderne copyright this site already showed. Icons are inline SVG rather than an icon package; three glyphs used once each do not justify a dependency. Drops the Moderne mark that the old footer rendered as a mask, since the reference treatment has no mark. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
theme-classic pulls the sidebar container up by --ifm-navbar-height and pads the sidebar back down by the same amount, so the menu clears a navbar the container sits beneath. This layout already positions the container below the whole fixed header, so the padding only showed as a 72px gap above the first item. 86px from header to heading, now 14px. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
…inator buttons The generated-index pages painted a 1322x1070 decorative image behind the card grid, which cut across the cards at most widths and carried no meaning. Breadcrumbs lose the pill on the active crumb: they mark position rather than offering an action, so they read as text, with the current page muted. Previous/next become plain text links with an inline arrow, replacing the pill buttons with masked arrow artwork — a way out of the page, not a call to action. Swizzles DocPaginator and removes ~70 lines of CSS along with the two arrow masks that only it used. static/img/gems-bg.webp is now unreferenced but left on disk; removing an asset is a separate call. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
The search bar ran on Algolia's stock palette — an rgba(0,0,0,.65) ground, #212139 border and #36395a keys — which reads as a foreign component beside the warm/ink set. Only two variables were bound before, and both were scoped to html[data-theme=light], so dark mode was entirely unthemed. Binds the button and the modal to --mod-* tokens in one :root block: every one is mode-aware, so no light/dark pair is needed. Rounds the button to 8px, which the package hard-codes at 4px, and gives the keys a border so they read as keys rather than a darker patch of the button. Matches docs.moderne.io. The modal shares these variables, so it is themed alongside rather than left to open in the package's colours. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
zieka
marked this pull request as ready for review
September 1, 2026 21:08
sjungling
approved these changes
Sep 2, 2026
sjungling
left a comment
Contributor
There was a problem hiding this comment.
nits and notes. no blockers
Comment on lines
+16
to
+32
| const XIcon: FunctionComponent = () => ( | ||
| <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> | ||
| <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" /> | ||
| </svg> | ||
| ); | ||
|
|
||
| const LinkedInIcon: FunctionComponent = () => ( | ||
| <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> | ||
| <path d="M20.45 20.45h-3.56v-5.57c0-1.33-.03-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.63-1.85 3.36-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zm1.78 13.02H3.55V9h3.57v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z" /> | ||
| </svg> | ||
| ); | ||
|
|
||
| const YouTubeIcon: FunctionComponent = () => ( | ||
| <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> | ||
| <path d="M23.5 6.19a3.02 3.02 0 0 0-2.12-2.14C19.5 3.55 12 3.55 12 3.55s-7.5 0-9.38.5A3.02 3.02 0 0 0 .5 6.19C0 8.08 0 12 0 12s0 3.92.5 5.81a3.02 3.02 0 0 0 2.12 2.14c1.88.5 9.38.5 9.38.5s7.5 0 9.38-.5a3.02 3.02 0 0 0 2.12-2.14C24 15.92 24 12 24 12s0-3.92-.5-5.81zM9.55 15.57V8.43L15.82 12l-6.27 3.57z" /> | ||
| </svg> | ||
| ); |
Contributor
There was a problem hiding this comment.
we don't have an icon package do we? that rebundles lucide and our custom icons?
Member
Author
There was a problem hiding this comment.
nope; might be worth putting that in the design system
Comment on lines
+20
to
+31
| const Chevron: FunctionComponent<{ open: boolean }> = ({ open }) => ( | ||
| <svg | ||
| className={clsx(styles.chevron, open && styles.chevronOpen)} | ||
| width="10" | ||
| height="6" | ||
| viewBox="0 0 10 6" | ||
| fill="none" | ||
| aria-hidden="true" | ||
| > | ||
| <path d="M1 1L5 5L9 1" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" /> | ||
| </svg> | ||
| ); |
Contributor
There was a problem hiding this comment.
wasn't the chevron also inlined as a data+svg in custom.css?
| border-bottom: 1px solid var(--mod-color-border-primary); | ||
| } | ||
|
|
||
| @media (min-width: 997px) { |
Contributor
There was a problem hiding this comment.
nit i think we have a breakpoint design token?
mike-solomon
approved these changes
Sep 2, 2026
mike-solomon
left a comment
Contributor
There was a problem hiding this comment.
Approving - two minor things we talked about in perma zoom to fix:
- Scrolling down on a page makes the side nav bar go under the header.
- Info/warning/tip/etc boxes have a strange design (lowercase text, weird spacing, etc).
…f contents Addresses review feedback. Bumps @moderneinc/design-system-tokens to 7.4.0, the current `latest`. All 43 tokens this repo uses still exist there and every value is byte-identical, so the bump carries no visual change. Replaces the chevron inlined in SecondaryNav with the --docs-chevron-down mask already defined for the sidebar caret. One definition of the glyph instead of two at different sizes and stroke widths; the mask follows currentColor exactly as the inline SVG did. Re-derives the table of contents' sticky offset from --docs-header-height. theme-classic computes it from --ifm-navbar-height alone, which here is only the first of the header's three rows, so the TOC pinned 72px behind the fixed header and its entries slid underneath on scroll. Its max-height overshot the viewport by the same amount. Now pins at 176px, clearing the 160px header. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
… tree Admonitions were laid out as a row, so an untitled one ran its body beside the type label and every line after the first was indented to the width of the word "info". They are now always stacked: heading on its own line, body starting at the left edge. The heading takes the size and weight of the heading it is, and the icons render filled again — they are drawn as fill-rule="evenodd" paths, and forcing fill:none with a stroke had turned each into a hollow outline. Separately, section detection matched on URL prefix, which cannot work here. Concepts & explanations is served at /concepts-explanations while its documents live at /concepts-and-explanations, so none of them resolved: no scoped sidebar and no active nav item for the whole section. Lists and Reference both serve documents from /reference, so a prefix cannot tell them apart either, and every Lists document resolved to Reference. Sections are now resolved from the sidebar tree, which is the only thing that knows which section a document belongs to, falling back to prefix matching for the index pages themselves. Safe to derive from doc ids: routeBasePath is "/" and no document in a section overrides its slug. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
":::info" now renders "Info" rather than "info", for all six types and the three legacy aliases. An author's own title is passed through exactly as written. Wraps @theme/Admonition rather than the two more obvious routes, both of which fail here: - A translation override in i18n/en/code.json is the documented mechanism, but creating an i18n directory switches on Docusaurus's translation machinery for the whole site, and the generated recipe-catalog sidebar has duplicate category labels (Camel x3, Upgrade x2) that then fail the build. - text-transform: capitalize cannot be scoped to the generic label. In this version a custom title renders as a bare text node in the heading, exactly like the default, so CSS cannot tell them apart and would re-case ":::info[Which filters require symbol data]" word by word. The wrapper runs the theme's own processAdmonitionProps, so a title supplied either as ":::info[Title]" or as a legacy mdxAdmonitionTitle child is detected and left alone; the label is injected only when there is none. Claude-Session: https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
docs.openrewrite.orgshares a Docusaurus ancestor withdocs.moderne.iobut never moved onto the design system, so the two sites have drifted apart visually while still carrying each other's leftovers (this repo'scustom.cssandmoderne-docs' both still declare the same Beausite@font-faceand mint#85FE99).Objectives
@moderneinc/design-system-tokensinstead of literals.docs.moderne.iouses, with the sidebar scoped to the selected section.Assumptions
@moderneinc/design-system-tokens@7.3.0is public on npm and installs without auth, so this repo's public CI can build it. Pinned exact; the installedmoderne.cssis byte-identical to the publishedlatest.moderne.cssis imported whole rather than per-module. Importing only the four files currently used is ~33% smaller, but a token added later would resolve to nothing with no error — worth 7KB gzip to avoid.#85FE99has no semantic token. It exists in the palette only as a raw entry, and consuming raw palette entries is the misuse the system warns about, so it stays a brand literal — asdocs.moderne.ioalso keeps it.docs.moderne.iosplits it the same way.--ifm-navbar-heightis 56px, not the 37px upstream uses: this site's logo is 33px and its search field 38px, against a smaller lockup there.useWindowSize()is the supported way to tell the desktop and mobile sidebars apart — both render throughDocSidebarItemsat level 1.navSections.tshrefs must match generated-index slugs insidebars.ts. Nothing checks this at build time; a mismatch falls back to the full sidebar rather than failing.Changes
(1)package.json,docusaurus.config.ts— add the tokens dependency;customCssloadsmoderne.cssthencustom.css; swap the Inter webfont for Geist + Geist Mono.(1)src/css/custom.css— Infima bridge mapping--ifm-*onto--mod-*. 48 colour literals become 5.(1)src/components/button.module.css,src/theme/DocCard/styles.module.css— onto--mod-button-primary-*and text/border/shadow tokens.(1)src/css/custom.css— admonitions take the mode-aware--mod-color-feedback-*tints and the reference's rounded hairline treatment, replacing Infima's coloured left stripe.(2)src/css/custom.css— sidebar carets, breadcrumb separator, pagination arrows and the footer mark redrawn asmask-image; the SVG supplies the shape, a token the colour. Deletes 3 dark-mode overrides and one duplicated 1.4KB path.(3)src/theme/Navbar/Layout/*— fixed wrapper holding announcement bar, navbar and section nav as one block.(3)src/theme/Navbar/Content/*— three zones: logo left, search and colour-mode toggle centred, external links right.(3)src/components/SecondaryNav/*,src/config/navSections.ts— the section bar; six sections left, three behind aMoredropdown mirroring upstream's right-alignedReleases.(3)src/theme/DocSidebarItems/*— scopes the sidebar to the active section, headed by its name. Desktop only.(4)src/theme/Navbar/Layout/index.tsx— aResizeObserverpublishes the header's measured height as--docs-header-height; the CSS value is a first-paint default only.(5)src/css/custom.css— mint kept as--ifm-color-mintfor the announcement bar, card hover and dark-mode active section.No tests: this repo has no test framework or test files, and this PR adds no testable logic.
tscand the production build are the available checks.Notes
--mod-ink-800(defined only in the package's:root, never its dark block) it is 11.96:1.autothere and its copy wraps to ~3 lines against a 30px reserve, hiding content under the fixed header. Measuring is correct at any width and makes a third row a zero-CSS change.@font-facewent too. The doch1is 36px Geist, matchingdocs.moderne.io.--mod-color-text-warningis a different colour with a different meaning), andgap: 15pxinbutton.module.css(14 and 16 bracket it on the scale). A near-miss substituted silently is worse than a literal left in place.semanticTypographytops out at 28px so there is no display size for a docsh1; package shadows have no dark variant, though--mod-color-surface-shadow-neutralsupplies the mode-aware colour.SearchBarinstances mounted below 997px, soCmd+Kfired twice; and the header reserve above. Also reused@theme/Icon/ExternalLinkand@theme/Navbar/MobileSidebar/Togglerather than the reimplementations first written, restoring the translated "(opens in new tab)" label and thetouchstart/focusindismissal the theme's dropdown uses.yarn typecheckclean; production build succeeds; all 43--mod-*/--docs-*references resolve in the built CSS with none dangling; light and dark checked in-browser; 23 contrast pairings pass AA for normal text in both themes, worst 5.39.<p>minifier warnings come from<ReactPlayer>inline in markdown (no.mdfile is touched here). Separately,yarn typecheckOOMs locally if you build first —tsconfig.jsonsetsallowJs: truewith noexclude, sotscwalks 1.3GB ofbuild/. CI is unaffected (typecheck runs before build); a one-lineexcludewould fix it.https://claude.ai/code/session_019z4aUYg8gVhnMY8vStEdia