From 7a1c3dad958f762b7cf739e5b02969729ec4d658 Mon Sep 17 00:00:00 2001 From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:42:58 +0100 Subject: [PATCH 1/7] fix(nav): align first product tab with content gutter Remove 12px of left padding from the product bar's inner container (px-5 to pl-2 pr-5) so the leading product tab's icon lines up with the header logo and page content gutter at 20px. DX-1371 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/Layout/ProductBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/ProductBar.tsx b/src/components/Layout/ProductBar.tsx index 51703ed53d..41eab92409 100644 --- a/src/components/Layout/ProductBar.tsx +++ b/src/components/Layout/ProductBar.tsx @@ -76,7 +76,7 @@ const ProductBar = ({ className }: ProductBarProps) => { className, )} > -
+
{items.map((item, index) => { if (item.type === 'divider') { return ( From 98d056b1e8b999e0be39c4fcc5f9f882efce727c Mon Sep 17 00:00:00 2001 From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:43:10 +0100 Subject: [PATCH 2/7] fix(nav): set header and product nav buttons to 36px height Pin the main header nav links (Platform/Products/Examples) to h-9 and swap the product bar tabs from py-2 to h-9 so both rows are exactly 36px tall. Previously the header links were ~32px and the product tabs ~35.6px (padding + label3 line-height), so they didn't quite match. DX-1371 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/Layout/Header.tsx | 3 ++- src/components/Layout/ProductBar.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index 2ed0d6799b..7977680880 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -25,7 +25,8 @@ const MD_BREAKPOINT = 1040; const CLI_ENABLED = false; const MAX_MOBILE_MENU_WIDTH = '560px'; -const headerLinkClassName = 'px-4 py-1.5 rounded-lg ui-text-label3 font-semibold transition-colors'; +const headerLinkClassName = + 'inline-flex items-center h-9 px-4 rounded-lg ui-text-label3 font-semibold transition-colors'; const activeHeaderLinkClassName = 'text-neutral-1300 dark:text-neutral-000 bg-orange-100 dark:bg-orange-1000'; const inactiveHeaderLinkClassName = 'text-neutral-900 dark:text-neutral-500 hover:text-neutral-1300 dark:hover:text-neutral-000 hover:bg-neutral-100 dark:hover:bg-neutral-1200'; diff --git a/src/components/Layout/ProductBar.tsx b/src/components/Layout/ProductBar.tsx index 41eab92409..535b717955 100644 --- a/src/components/Layout/ProductBar.tsx +++ b/src/components/Layout/ProductBar.tsx @@ -45,7 +45,7 @@ const buildNavBarItems = (): NavBarItem[] => { // --- Styles --- const tabBaseClassName = cn( - 'flex items-center gap-1.5 px-3 py-2 whitespace-nowrap rounded-lg transition-colors', + 'flex items-center gap-1.5 h-9 px-3 whitespace-nowrap rounded-lg transition-colors', 'ui-text-label3 font-semibold', 'focus-base', ); From 406298be4777d8b5f16763e53e456cbacfc2bff0 Mon Sep 17 00:00:00 2001 From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:43:19 +0100 Subject: [PATCH 3/7] fix(nav): restore 2px gap on sub-section menu The accordion content wrapper used p-1 with -m-1, cancelling out to a 0px gap. Reduce the negative margin to -m-0.5 so the padding nets a 2px gap, matching the design, while keeping enough padding to avoid clipping focus rings and hover backgrounds during the open/close animation. DX-1371 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/Layout/LeftSidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/LeftSidebar.tsx b/src/components/Layout/LeftSidebar.tsx index 3acf78ca73..5ca55a0f7f 100644 --- a/src/components/Layout/LeftSidebar.tsx +++ b/src/components/Layout/LeftSidebar.tsx @@ -41,7 +41,7 @@ type LeftSidebarProps = { }; const accordionContentClassName = - 'overflow-hidden data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up p-1 -m-1'; + 'overflow-hidden data-[state=open]:animate-accordion-down data-[state=closed]:animate-accordion-up p-1 -m-0.5'; const accordionTriggerClassName = cn( interactiveButtonClassName, From c62a06d2f2b1bb00e19d1c11ffc09c1b03060efb Mon Sep 17 00:00:00 2001 From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:43:28 +0100 Subject: [PATCH 4/7] fix(nav): increase menu section title padding-bottom to 8px Bump the left sidebar section heading padding-bottom from pb-1.5 (6px) to pb-2 (8px) to match the design. DX-1371 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/Layout/LeftSidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/LeftSidebar.tsx b/src/components/Layout/LeftSidebar.tsx index 5ca55a0f7f..a3d7e74218 100644 --- a/src/components/Layout/LeftSidebar.tsx +++ b/src/components/Layout/LeftSidebar.tsx @@ -195,7 +195,7 @@ const SectionNav = ({ content, tree }: { content: (NavProductPage | NavProductCo return (
-
+
{page.name}
{hasDeeperLayer && } From f7c4125403e2a8a806f858bc2ec58282581d4f60 Mon Sep 17 00:00:00 2001 From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:43:52 +0100 Subject: [PATCH 5/7] fix(nav): show static label when only one language is available When a page exposes a single SDK language there is nothing to choose between, so render a static, non-interactive element instead of a disabled dropdown. It keeps the same bordered styling (icon, label, version badge) but drops the Select wrapper and chevron. Applied to both the single-language selector and each Client/Agent dual-language dropdown. DX-1371 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Layout/LanguageSelector.test.tsx | 19 ++++++++++ src/components/Layout/LanguageSelector.tsx | 35 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/components/Layout/LanguageSelector.test.tsx b/src/components/Layout/LanguageSelector.test.tsx index 114718324b..46fca66280 100644 --- a/src/components/Layout/LanguageSelector.test.tsx +++ b/src/components/Layout/LanguageSelector.test.tsx @@ -142,6 +142,25 @@ describe('LanguageSelector', () => { expect(screen.getByText('Python')).toBeInTheDocument(); }); + it('renders a static label without a dropdown when only one language is available', () => { + mockUseLayoutContext.mockReturnValue({ + activePage: { + tree: [0], + languages: ['javascript'], + language: 'javascript', + }, + products: [['pubsub']], + }); + + render(); + + expect(screen.getByText('JavaScript')).toBeInTheDocument(); + expect(screen.getByText('icon-tech-javascript')).toBeInTheDocument(); + // No dropdown chevron and no combobox trigger when there is nothing to choose between. + expect(screen.queryByText('icon-gui-chevron-down-solid')).not.toBeInTheDocument(); + expect(screen.queryByRole('combobox')).not.toBeInTheDocument(); + }); + it('navigates when a language option is selected', async () => { render(); const trigger = screen.getByRole('combobox', { name: /select code language/i }); diff --git a/src/components/Layout/LanguageSelector.tsx b/src/components/Layout/LanguageSelector.tsx index cedb1d444d..ae4c725f54 100644 --- a/src/components/Layout/LanguageSelector.tsx +++ b/src/components/Layout/LanguageSelector.tsx @@ -69,6 +69,23 @@ const SingleLanguageSelector = () => { const selectedLang = languageInfo[selectedOption.label]; + // With only one language available there is nothing to choose between, so render a + // static, non-interactive element. It keeps the same bordered styling as the dropdown + // trigger but drops the chevron and any dropdown behaviour. + if (options.length <= 1) { + return ( +
+
+ + {selectedLang?.label} + + v{selectedOption.version} + +
+
+ ); + } + return (
@@ -212,6 +229,24 @@ const DualLanguageDropdown = ({ label, paramName, languages, selectedLanguage }: const selectedLang = languageInfo[selectedOption.label]; + // With only one language available there is nothing to choose between, so render a + // static, non-interactive element. It keeps the same bordered styling as the dropdown + // trigger but drops the chevron and any dropdown behaviour. + if (options.length <= 1) { + return ( +
+ {label} +
+ + {selectedLang?.label} + + v{selectedOption.version} + +
+
+ ); + } + return (
{label} From bc69492e0873f400c0d74d3dbb7712b54b406a6e Mon Sep 17 00:00:00 2001 From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:44:18 +0100 Subject: [PATCH 6/7] fix(nav): fix language dropdown width to prevent resizing Pin the single-language selector trigger to a fixed 195px width with left-aligned content and the chevron pushed to the right edge (ml-auto), so switching languages no longer resizes the control or shifts the right sidebar. Match the loading skeleton width so there is no hydration jump. DX-1371 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/Layout/LanguageSelector.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/Layout/LanguageSelector.tsx b/src/components/Layout/LanguageSelector.tsx index ae4c725f54..d698114cd2 100644 --- a/src/components/Layout/LanguageSelector.tsx +++ b/src/components/Layout/LanguageSelector.tsx @@ -64,7 +64,7 @@ const SingleLanguageSelector = () => { }; if (!selectedOption) { - return ; + return ; } const selectedLang = languageInfo[selectedOption.label]; @@ -91,9 +91,8 @@ const SingleLanguageSelector = () => { + ); + } + + return ( + + {tabContent} ); })}