@@ -28,11 +28,12 @@ const MINIMAL_RENDER = Boolean(JSON.parse(process.env.MINIMAL_RENDER || 'false')
2828
2929type Props = {
3030 children ?: React . ReactNode
31- // Whether this page renders the right-rail "In this article" drawer (article +
32- // automated pages do; REST reference pages do not). Controls whether the
33- // secondary bar's collapsed Overview menu yields to the drawer at xxl.
31+ // Article and automated pages render the right-rail drawer; REST pages do not.
32+ // The secondary bar's collapsed Overview menu yields to that drawer at xxl.
3433 hasDrawer ?: boolean
3534}
35+ // The non-homepage branch wraps the secondary bar and article content in SelectionProvider
36+ // so the collapsed Overview menu and article body share platform/tool selection.
3637export const DefaultLayout = ( props : Props ) => {
3738 const mainContext = useMainContext ( )
3839 const {
@@ -52,17 +53,14 @@ export const DefaultLayout = (props: Props) => {
5253 const { languages } = useLanguages ( )
5354 const [ isNarrowMenuOpen , setIsNarrowMenuOpen ] = useState ( false )
5455
55- // This is only true when we do search indexing which renders every page
56- // just to be able to `cheerio` load the main body (and the meta
57- // keywords tag).
56+ // Search indexing renders every page so Cheerio can read the body and meta keywords.
5857 if ( MINIMAL_RENDER ) {
5958 return (
6059 < div >
6160 < Head >
6261 < title > { page . fullTitle } </ title >
6362 </ Head >
6463
65- { /* For local site search indexing */ }
6664 < div className = "d-none d-xl-block" data-search = "breadcrumbs" >
6765 < Breadcrumbs />
6866 </ div >
@@ -143,7 +141,6 @@ export const DefaultLayout = (props: Props) => {
143141 < title > { page . fullTitle } </ title >
144142 ) : null }
145143
146- { /* For Google and Bots */ }
147144 < meta name = "description" content = { metaDescription } />
148145 { page . hidden && < meta name = "robots" content = "noindex" /> }
149146 { Object . values ( languages )
@@ -161,7 +158,6 @@ export const DefaultLayout = (props: Props) => {
161158 )
162159 } ) }
163160
164- { /* For analytics events */ }
165161 { router . locale && < meta name = "path-language" content = { router . locale } /> }
166162 { currentVersion && < meta name = "path-version" content = { currentVersion } /> }
167163 { currentProduct && < meta name = "path-product" content = { currentProduct . id } /> }
@@ -178,7 +174,6 @@ export const DefaultLayout = (props: Props) => {
178174 ) }
179175 { status && < meta name = "status" content = { status . toString ( ) } /> }
180176
181- { /* OpenGraph data */ }
182177 { page . fullTitle && (
183178 < >
184179 < meta property = "og:site_name" content = "GitHub Docs" />
@@ -188,15 +183,13 @@ export const DefaultLayout = (props: Props) => {
188183 < meta property = "og:image" content = { getSocialCardImage ( ) } />
189184 </ >
190185 ) }
191- { /* Twitter Meta Tags */ }
192186 < meta name = "twitter:card" content = "summary" />
193187 < meta property = "twitter:domain" content = { new URL ( fullUrl ) . hostname } />
194188 < meta property = "twitter:url" content = { fullUrl } />
195189 < meta name = "twitter:title" content = { page . fullTitle } />
196190 { page . introPlainText && < meta name = "twitter:description" content = { page . introPlainText } /> }
197191 < meta name = "twitter:image" content = { getSocialCardImage ( ) } />
198192
199- { /* LLM-friendly alternate formats */ }
200193 < link
201194 rel = "alternate"
202195 type = "text/markdown"
@@ -220,7 +213,6 @@ export const DefaultLayout = (props: Props) => {
220213 />
221214 </ Head >
222215
223- { /* a11y */ }
224216 < a
225217 href = "#main-content"
226218 className = { cx ( 'visually-hidden skip-button' , styles . skipButton ) }
@@ -246,10 +238,6 @@ export const DefaultLayout = (props: Props) => {
246238 </ div >
247239 </ div >
248240 ) : (
249- // SelectionProvider wraps both the secondary bar and the content so the
250- // bar's collapsed "In this article" menu (OverviewMenu) sees the same
251- // platform/tool selection as the article body and filters its headings
252- // accordingly.
253241 < SelectionProvider >
254242 < ActiveSectionProvider >
255243 < DocsSecondaryBar />
@@ -263,52 +251,45 @@ export const DefaultLayout = (props: Props) => {
263251 )
264252}
265253
266- // The doc-tree rail + content column, split out so it can read the collapse
267- // context that DefaultLayout provides. On desktop the rail shows unless
268- // collapsed; on mobile it shows inline (in the page flow, like desktop) only
269- // when the nav is opened from the secondary bar. The content column (flex-1)
270- // fills the row when the rail is absent.
254+ // LayoutBody reads SidebarCollapseContext after DefaultLayout provides it.
255+ // On mobile, the inline rail shows only when opened from the secondary bar.
256+ // LayoutBody matches SidebarNav's search-page gate instead of router.route
257+ // because src/pages/search.tsx and src/pages/[versionId]/search.tsx must agree
258+ // on the facet rail.
259+ // It mirrors OverviewSubBar's render gate so sticky-stack classes describe a bar
260+ // that renders.
261+ // Search results split the facet rail beside results at Brand's medium
262+ // breakpoint; route gating keeps other pages on d-lg-flex at 1012px.
263+ // The desktop rail-collapse cookie does not hide the open mobile nav. Otherwise
264+ // the content column hides with no drawer visible and shows a blank area instead
265+ // of the doc tree.
266+ // Search ignores the collapse cookie because it has no DocsSecondaryBar toggle
267+ // to restore filters.
268+ // Sticky elements need an explicit height because their scroll container sets
269+ // overflow:auto.
270+ // The sticky-stack class publishes the header and bar offset for descendants
271+ // such as article table headers.
272+ // Keeping OverviewSubBar inside main lets it start at the doc-tree drawer's
273+ // right edge and share that band with the drawer; mainContent uses overflow-x:clip,
274+ // so sticky still resolves against the viewport.
271275type LayoutBodyProps = {
272276 children ?: React . ReactNode
273277 hasDrawer ?: boolean
274278}
275279const LayoutBody = ( { children, hasDrawer } : LayoutBodyProps ) => {
276280 const { collapsed, mobileNavOpen } = useSidebarCollapsed ( )
277281 const { currentProduct } = useMainContext ( )
278- // Matches SidebarNav's own gate rather than testing router.route. There are two search
279- // pages, src/pages/search.tsx and src/pages/[versionId]/search.tsx, so a route test
280- // for '/search' misses every versioned search URL, and this check would then disagree
281- // with SidebarNav about whether the rail is a facet rail.
282282 const isSearchResultsPage = currentProduct ?. id === 'search'
283- // Mirrors OverviewSubBar's own render gate (it returns null at <= 1 item), so
284- // the sticky-stack classes below describe the bar that actually renders.
285283 const miniTocItems = useMiniTocItems ( )
286284 const hasSubBar = miniTocItems . length > 1
287285 return (
288- // `d-lg-flex` only goes side-by-side at 1012px. The search page's facet rail
289- // is meant to sit beside the results from brand's `medium` breakpoint, so it
290- // gets an earlier split of its own. Route-gated, so no other page moves.
291286 < div className = { cx ( 'd-lg-flex' , isSearchResultsPage && styles . searchColumns ) } >
292- { /* `collapsed` is the desktop rail-collapse state (persisted). The inline
293- mobile nav is independent, so still render the sidebar when it's open.
294- Otherwise opening the mobile nav while the desktop rail is collapsed
295- hides the content column (contentHiddenForNav) with no drawer to show,
296- so the open nav displays a blank area instead of the doc tree.
297-
298- Search is exempt: the cookie is shared with the doc-tree rail, but the
299- search page has no toggle to undo it (DocsSecondaryBar returns null
300- there), so honouring it would strand the filters with no way back. */ }
301287 { collapsed && ! mobileNavOpen && ! isSearchResultsPage ? null : (
302288 < SidebarNav mobileOpen = { mobileNavOpen } />
303289 ) }
304- { /* Need to set an explicit height for sticky elements since we also
305- set overflow to auto */ }
306290 < div
307291 className = { cx (
308292 'flex-column flex-1 min-width-0' ,
309- // Publish the sticky-stack height to everything in the column (article
310- // table headers read it). Driven by the same values as OverviewSubBar's
311- // visibility modifier just below, so the offset and the bar agree.
312293 styles . stickyStack ,
313294 hasSubBar && styles . stickyStackWithSubBar ,
314295 hasSubBar &&
@@ -318,14 +299,6 @@ const LayoutBody = ({ children, hasDrawer }: LayoutBodyProps) => {
318299 ) }
319300 >
320301 < main id = "main-content" className = { styles . mainContent } >
321- { /* Inside <main>, not before it: as a preceding sibling the "Skip to
322- main content" link jumped the reader straight past the page's only
323- in-article navigation. Still within the content column, so on
324- desktop it starts at the doc-tree drawer's right edge and runs to
325- the screen edge, sharing that band with the drawer rather than
326- cutting across above it. (.mainContent uses `overflow-x: clip`,
327- which creates no scroll container, so sticky still resolves against
328- the viewport.) */ }
329302 < OverviewSubBar hasDrawer = { hasDrawer } />
330303 < DeprecationBanner />
331304 < RestBanner />
0 commit comments