Skip to content

Commit b72d35b

Browse files
m-hulbertclaude
andcommitted
fix(nav): polish sidebar and layout alignment
- Remove left sidebar from homepage - Fix left nav scroll so it works independently of main content - Adjust nav item padding (4px top/bottom) and heading spacing (20px/6px) - Align first nav heading with breadcrumb row - Left border/indent only on layer 2+ nav items - Remove horizontal rule between language selector and "On this page" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 339120e commit b72d35b

8 files changed

Lines changed: 249 additions & 253 deletions

File tree

data/onCreatePage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export type LayoutOptions = {
1212
const mdxWrapper = path.resolve('src/components/Layout/MDXWrapper.tsx');
1313

1414
const pageLayoutOptions: Record<string, LayoutOptions> = {
15-
'/docs': { leftSidebar: true, rightSidebar: false, template: 'index', mdx: false },
15+
'/docs': { leftSidebar: false, rightSidebar: false, template: 'index', mdx: false },
1616
'/docs/api/control-api': {
1717
leftSidebar: false,
1818
rightSidebar: false,

src/components/Layout/Breadcrumbs.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ describe('Breadcrumbs', () => {
7070
render(<Breadcrumbs />);
7171

7272
// Current page (last item) should be disabled
73-
expect(screen.getByText('Current Page')).toHaveClass('text-gui-unavailable');
73+
expect(screen.getByText('Current Page')).toHaveClass('text-neutral-700');
7474
expect(screen.getByText('Current Page')).toHaveClass('pointer-events-none');
7575

7676
// Non-linked nodes (link='#') should be disabled
77-
expect(screen.getByText('Subsection 1')).toHaveClass('text-gui-unavailable');
77+
expect(screen.getByText('Subsection 1')).toHaveClass('text-neutral-700');
7878
expect(screen.getByText('Subsection 1')).toHaveClass('pointer-events-none');
7979

8080
// Active links should not be disabled
81-
expect(screen.getByText('Section 1')).not.toHaveClass('text-gui-unavailable');
81+
expect(screen.getByText('Section 1')).not.toHaveClass('text-neutral-700');
8282
expect(screen.getByText('Section 1')).not.toHaveClass('pointer-events-none');
8383
});
8484

src/components/Layout/Breadcrumbs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const Breadcrumbs: React.FC = () => {
6666
<Link
6767
to={node.page.link}
6868
className={cn(linkStyles, {
69-
'text-gui-unavailable dark:text-gui-unavailable-dark pointer-events-none':
69+
'text-neutral-700 dark:text-neutral-700 pointer-events-none':
7070
index === activePage.tree.length - 1 || node.page.link === '#',
7171
'hidden sm:flex': index !== lastActiveNodeIndex,
7272
})}

src/components/Layout/Header.tsx

Lines changed: 218 additions & 207 deletions
Large diffs are not rendered by default.

src/components/Layout/Layout.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,17 @@ const Layout: React.FC<LayoutProps> = ({ children, pageContext }) => {
4646

4747
return (
4848
<GlobalLoading template={template}>
49-
5049
<Header />
5150
{showProductBar && <ProductBar className="hidden md:block" />}
5251
<div className="max-w-[1856px] mx-auto pt-16">
53-
<div
54-
className={cn(
55-
'flex px-8',
56-
!leftSidebar && 'md:px-12',
57-
)}
58-
>
52+
<div className={cn('flex px-8', !leftSidebar && 'md:px-12')}>
5953
<LeftSidebar className={cn(!leftSidebar && 'md:hidden')} />
6054
<Container
6155
as="main"
6256
className={cn(
6357
'flex-1 min-w-0',
64-
{ 'max-w-[704px] mx-auto': !isRedocPage && leftSidebar },
65-
{ 'overflow-x-hidden sm:overflow-x-auto': !isRedocPage },
58+
{ 'max-w-[860px] mx-auto': !isRedocPage && leftSidebar },
59+
{ 'overflow-x-clip': !isRedocPage },
6660
)}
6761
>
6862
{leftSidebar ? (

src/components/Layout/LeftSidebar.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const accordionTriggerClassName = cn(
5151
'[&[data-state=open]>svg]:rotate-90',
5252
);
5353

54-
const accordionLinkClassName = 'pl-3 py-[6px]';
54+
const accordionLinkClassName = 'pl-3 py-1';
5555

5656
const iconClassName = 'text-neutral-1300 dark:text-neutral-000 transition-transform';
5757

@@ -66,10 +66,7 @@ const ChildAccordion = ({ content, tree }: { content: (NavProductPage | NavProdu
6666
.map((page, index) => 'expand' in page && page.expand && `item-${tree.join('-')}-${index}`)
6767
.filter((item) => typeof item === 'string');
6868

69-
const [openSections, setOpenSections] = useState<string[]>([
70-
`item-${previousTree.join('-')}`,
71-
...preExpandedItems,
72-
]);
69+
const [openSections, setOpenSections] = useState<string[]>([`item-${previousTree.join('-')}`, ...preExpandedItems]);
7370

7471
useEffect(() => {
7572
if (activeTriggerRef.current) {
@@ -131,19 +128,20 @@ const ChildAccordion = ({ content, tree }: { content: (NavProductPage | NavProdu
131128
className={cn(accordionTriggerClassName, 'font-medium rounded-lg', {
132129
'border-l border-neutral-300 dark:border-neutral-1000 hover:border-neutral-500 dark:hover:border-neutral-800 rounded-l-none':
133130
layer > 1,
131+
'-ml-2 pl-2': layer <= 1,
134132
'text-neutral-1300 dark:text-neutral-000 font-bold': isActive,
135133
'border-orange-600 bg-orange-100 hover:bg-orange-100': isSelected,
136134
})}
137135
>
138136
{hasDeeperLayer ? (
139-
<div className={cn(accordionLinkClassName, 'flex-1')}>
137+
<div className={cn(layer > 1 ? accordionLinkClassName : 'py-1', 'flex-1')}>
140138
<span>{page.name}</span>
141139
</div>
142140
) : (
143141
'link' in page && (
144142
<Link
145143
className={cn(
146-
accordionLinkClassName,
144+
layer > 1 ? accordionLinkClassName : 'py-1',
147145
'ui-text-label3 font-medium w-full h-full pr-5 flex justify-between items-center gap-2',
148146
isActive && 'text-neutral-1300 dark:text-neutral-000 font-bold',
149147
)}
@@ -184,13 +182,18 @@ const ChildAccordion = ({ content, tree }: { content: (NavProductPage | NavProdu
184182
/** Render top-level nav sections as static headings with their content always expanded. */
185183
const SectionNav = ({ content, tree }: { content: (NavProductPage | NavProductContent)[]; tree: number[] }) => {
186184
return (
187-
<div className="px-3 pt-8 pb-3">
185+
<div className="px-3 pt-9 pb-3">
188186
{content.map((page, index) => {
189187
const hasDeeperLayer = 'pages' in page && page.pages;
190188

191189
return (
192-
<div key={page.name} className={cn(index > 0 && 'mt-2 pt-2')}>
193-
<div className="ui-text-label2 font-bold text-neutral-1300 dark:text-neutral-000 py-[6px]">
190+
<div key={page.name}>
191+
<div
192+
className={cn(
193+
'ui-text-label2 font-bold text-neutral-1300 dark:text-neutral-000 pb-1.5',
194+
index > 0 ? 'pt-5' : 'pt-0',
195+
)}
196+
>
194197
{page.name}
195198
</div>
196199
{hasDeeperLayer && <ChildAccordion content={page.pages} tree={[...tree, index]} />}
@@ -236,12 +239,12 @@ const LeftSidebar = ({ className, inHeader = false }: LeftSidebarProps) => {
236239
// When the product bar is visible (non-platform products), add its height to the sticky offset
237240
const hasProductBar = activeProductKey !== null && activeProductKey !== 'platform';
238241
const stickyTopPx = HEADER_HEIGHT + (hasProductBar ? PRODUCT_BAR_HEIGHT : 0);
239-
const stickyTopStyle = inHeader ? undefined : { top: `${stickyTopPx}px` };
242+
const stickyTopStyle = inHeader ? undefined : { top: `${stickyTopPx}px`, height: `calc(100dvh - ${stickyTopPx}px)` };
240243
const stickyTopClass = inHeader ? 'top-16' : '';
241244

242245
return (
243246
<div
244-
className={cn('sticky h-full', stickyTopClass, inHeader ? 'w-full' : 'w-[312px] hidden md:block', className)}
247+
className={cn('sticky', stickyTopClass, inHeader ? 'w-full' : 'w-[312px] hidden md:block', className)}
245248
style={stickyTopStyle}
246249
>
247250
{inHeader && (
@@ -258,10 +261,7 @@ const LeftSidebar = ({ className, inHeader = false }: LeftSidebarProps) => {
258261
'bg-neutral-000 dark:bg-neutral-1300 overflow-x-hidden overflow-y-auto',
259262
inHeader
260263
? 'w-full h-[calc(100dvh-64px-128px)]'
261-
: [
262-
'w-[312px] border-r border-neutral-300 dark:border-neutral-1000',
263-
`h-[calc(100dvh-${stickyTopPx}px)]`,
264-
],
264+
: 'w-[312px] h-full border-r border-neutral-300 dark:border-neutral-1000',
265265
)}
266266
>
267267
{content.length > 0 ? (

src/components/Layout/ProductBar.tsx

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,14 @@ const buildNavBarItems = (): NavBarItem[] => {
4646

4747
const tabBaseClassName = cn(
4848
'flex items-center gap-1.5 px-3 py-2.5 whitespace-nowrap rounded-lg transition-colors',
49-
'ui-text-label3 font-medium',
49+
'ui-text-label3 font-semibold',
5050
'focus-base',
5151
);
5252

5353
const activeTabClassName = 'text-neutral-1300 dark:text-neutral-000 bg-orange-100 dark:bg-orange-1000';
5454

5555
const inactiveTabClassName = cn(
56-
'text-neutral-800 dark:text-neutral-500',
56+
'text-neutral-1000 dark:text-neutral-500',
5757
'hover:text-neutral-1300 dark:hover:text-neutral-000',
5858
'hover:bg-neutral-100 dark:hover:bg-neutral-1200',
5959
'cursor-pointer',
@@ -76,7 +76,7 @@ const ProductBar = ({ className }: ProductBarProps) => {
7676
className,
7777
)}
7878
>
79-
<div className="flex items-center gap-0.5 px-3 py-1.5 overflow-x-auto scrollbar-none max-w-[1856px] mx-auto">
79+
<div className="flex items-center gap-0.5 px-8 py-1.5 overflow-x-auto scrollbar-none max-w-[1856px] mx-auto">
8080
{items.map((item, index) => {
8181
if (item.type === 'divider') {
8282
return (
@@ -88,9 +88,7 @@ const ProductBar = ({ className }: ProductBarProps) => {
8888
}
8989

9090
const isProduct = item.type === 'product';
91-
const isActive = isProduct
92-
? activePage.product === item.key
93-
: activePage.page.link === item.link;
91+
const isActive = isProduct ? activePage.product === item.key : activePage.page.link === item.link;
9492

9593
const iconName = isProduct
9694
? isActive
@@ -104,10 +102,7 @@ const ProductBar = ({ className }: ProductBarProps) => {
104102
<Link
105103
key={itemKey}
106104
to={item.link}
107-
className={cn(
108-
tabBaseClassName,
109-
isActive ? activeTabClassName : inactiveTabClassName,
110-
)}
105+
className={cn(tabBaseClassName, isActive ? activeTabClassName : inactiveTabClassName)}
111106
{...(!isProduct &&
112107
(item as CustomBarItem).external && {
113108
target: '_blank',
@@ -118,11 +113,7 @@ const ProductBar = ({ className }: ProductBarProps) => {
118113
<Icon
119114
name={iconName}
120115
size="16px"
121-
additionalCSS={cn(
122-
isActive
123-
? 'text-orange-600'
124-
: 'text-neutral-700 dark:text-neutral-600',
125-
)}
116+
additionalCSS={cn(isActive ? 'text-orange-600' : 'text-neutral-700 dark:text-neutral-600')}
126117
/>
127118
)}
128119
<span>{item.name}</span>

src/components/Layout/RightSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ const RightSidebar = () => {
269269
>
270270
<div className="flex flex-col h-full overflow-y-auto pt-8">
271271
{showLanguageSelector && (
272-
<div className="mb-6 pb-6 border-b border-neutral-300 dark:border-neutral-1000">
272+
<div className="mb-6">
273273
<LanguageSelector />
274274
</div>
275275
)}

0 commit comments

Comments
 (0)