Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0cf494f
Rework page layout
zenoachtig Feb 16, 2026
5779b72
Update TableOfContents.tsx
zenoachtig Feb 16, 2026
5ccd2dc
Comments & rename
zenoachtig Feb 16, 2026
87443db
Update PageCover.tsx
zenoachtig Feb 16, 2026
4c552c5
Merge branch 'main' into page-layout-rework
zenoachtig Feb 24, 2026
1282c3f
Add pageaside sidesheet
zenoachtig Feb 24, 2026
42f2076
Update EmbeddableDocsPage.tsx
zenoachtig Feb 24, 2026
081f9e6
Merge branch 'main' into page-layout-rework
zenoachtig Mar 16, 2026
f5902a6
Improved version
zenoachtig Mar 16, 2026
3397549
Refactor
zenoachtig Mar 16, 2026
4ca020c
Format
zenoachtig Mar 16, 2026
0a26acf
Merge branch 'main' into page-layout-rework
zenoachtig Mar 18, 2026
2be16ce
First naive pass
zenoachtig Mar 31, 2026
656510f
Update PageCover.tsx
zenoachtig Mar 31, 2026
c93d777
Finish refactor
zenoachtig Apr 1, 2026
9ebad52
Merge branch 'main' into page-layout-rework
zenoachtig Apr 1, 2026
ae0b730
Try-out showing the outline earlier for pages with API block
zenoachtig Apr 1, 2026
984a822
Cleanup / refactor
zenoachtig Apr 1, 2026
e813918
Tweaks to cover and page actions
zenoachtig Apr 1, 2026
2e921c2
Update PageAside.tsx
zenoachtig Apr 1, 2026
1823d42
"On this page" header & mobile TOC margin
zenoachtig Apr 1, 2026
db846c9
Fix animations that were causing header issues
zenoachtig Apr 1, 2026
83bfe42
Update tests
zenoachtig Apr 2, 2026
1ec9bc6
Update internal.spec.ts
zenoachtig Apr 2, 2026
87ded02
Update PageHeader.tsx
zenoachtig Apr 2, 2026
6709ca7
Update PageHeader.tsx
zenoachtig Apr 8, 2026
e45c6e9
Merge branch 'main' into page-layout-rework
zenoachtig Apr 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/metal-ghosts-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gitbook": minor
---

Rework page layout
27 changes: 3 additions & 24 deletions packages/gitbook/src/components/DocumentView/Blocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DocumentBlock, JSONDocument } from '@gitbook/api';

import { type ClassValue, tcls } from '@/lib/tailwind';

import { CONTENT_STYLE } from '../layout';
import { Block } from './Block';
import type { DocumentContextProps } from './DocumentView';
import { isBlockOffscreen } from './utils';
Expand Down Expand Up @@ -47,25 +48,6 @@ type UnwrappedBlocksProps<TBlock extends DocumentBlock> = DocumentContextProps &
isOffscreen?: boolean;
};

/* Blocks that can be full width are automatically expanded on full-width pages.
* Ideally we'd rely on the block type to determine if it can be full width, but
* the block's `fullWidth` property does not differentiate between `undefined` and `false`.
* So instead we hardcode a list of blocks that can be full width. */
const FULL_WIDTH_BLOCKS: DocumentBlock['type'][] = [
'table',
'tabs',
'integration',
'openapi-operation',
'openapi-schemas',
'openapi-webhook',
'images',
'embed',
'columns',
'code',
'content-ref',
'hint',
];

const LIST_BLOCKS: DocumentBlock['type'][] = ['list-ordered', 'list-tasks', 'list-unordered'];

/**
Expand All @@ -89,12 +71,9 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
key={node.key || `${node.type}-${index}`}
block={node}
style={[
'mx-auto page-width-wide:mx-0 w-full decoration-primary/6',
node.data && 'fullWidth' in node.data && node.data.fullWidth
? 'max-w-screen-xl'
: 'max-w-3xl',
CONTENT_STYLE,
'decoration-primary/6',
!LIST_BLOCKS.includes(node.type) && 'print:break-inside-avoid',
FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-full',
blockStyle,
]}
isEstimatedOffscreen={isOffscreen}
Expand Down
2 changes: 1 addition & 1 deletion packages/gitbook/src/components/DocumentView/Divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import type { BlockProps } from './Block';
export function Divider(props: BlockProps<DocumentBlockDivider>) {
const { style } = props;

return <hr className={tcls(style, 'page-width-wide:max-w-full border-tint-subtle')} />;
return <hr className={tcls(style, 'border-tint-subtle')} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function DocumentViewSkeleton(props: { document: JSONDocument; blockStyle
style={[
'mx-auto w-full decoration-primary/6',
block.data && 'fullWidth' in block.data && block.data.fullWidth
? 'max-w-screen-xl'
? 'max-w-6xl'
: 'max-w-3xl',
blockStyle,
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function InlineActionButton(
inline
label={buttonProps.label as string}
sizing="medium"
className="inline-flex max-w-full grow"
className="inline-flex max-w-3xl grow" // Limit input to a reasonable width
submitButton={{
label: tString(language, action === 'ask' ? 'send' : 'search'),
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function StepperStep(props: BlockProps<DocumentBlockStepperStep>) {
})();

return (
<div className={tcls('mx-auto flex w-full max-w-3xl flex-row gap-4 md:gap-8', style)}>
<div className={tcls('flex flex-row gap-4 md:gap-8', style)}>
<div className="relative select-none">
<div
className={tcls(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function EmbeddableDocsPage(
<EmbeddableFrameMain data-testid="embed-docs-page">
<div className="relative flex flex-col border-tint-subtle border-b theme-bold:bg-header-background">
<EmbeddableFrameHeader className="theme-bold:text-header-link">
<HeaderMobileMenu className="-ml-2 page-no-toc:hidden theme-bold:text-header-link hover:theme-bold:bg-header-link/3 hover:theme-bold:text-header-link lg:hidden" />
<HeaderMobileMenu className="-ml-2 layout-wide:hidden theme-bold:text-header-link hover:theme-bold:bg-header-link/3 hover:theme-bold:text-header-link lg:hidden" />
<EmbeddableFrameHeaderMain>
<EmbeddableFrameTitle>{context.site.title}</EmbeddableFrameTitle>
</EmbeddableFrameHeaderMain>
Expand Down
21 changes: 6 additions & 15 deletions packages/gitbook/src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { partition } from '@/lib/arrays';
import { tcls } from '@/lib/tailwind';

import { ThemeToggler } from '../ThemeToggler';
import { CONTAINER_STYLE } from '../layout';
import { CONTAINER_STYLE, CONTENT_STYLE } from '../layout';
import { FooterLinksGroup } from './FooterLinksGroup';
import { SocialLink } from './SocialLink';

Expand Down Expand Up @@ -48,16 +48,11 @@ export function Footer(props: { context: GitBookSiteContext }) {
>
<div
className={tcls(
'mx-auto flex @xs:grid @4xl:max-w-none! max-w-3xl site-width-wide:max-w-screen-2xl flex-col justify-between gap-12',
CONTENT_STYLE,
'flex @xs:grid @4xl:max-w-none! flex-col justify-between gap-12',
'grid-cols-[auto_auto]',
'@4xl:grid-cols-[18rem_minmax(auto,48rem)_auto]',
'@7xl:grid-cols-[18rem_minmax(auto,48rem)_14rem]',
'@4xl:site-width-wide:grid-cols-[18rem_minmax(auto,80rem)_auto]',
'@7xl:site-width-wide:grid-cols-[18rem_minmax(auto,80rem)_14rem]',
'@4xl:page-no-toc:grid-cols-[minmax(auto,48rem)_auto]',
'@7xl:page-no-toc:grid-cols-[14rem_minmax(auto,48rem)_14rem]',
'@4xl:[body:has(.site-width-wide,.page-no-toc)_&]:grid-cols-[minmax(auto,90rem)_auto]',
'@7xl:[body:has(.site-width-wide,.page-no-toc)_&]:grid-cols-[14rem_minmax(auto,90rem)_14rem]'
'@7xl:grid-cols-[18rem_minmax(auto,48rem)_14rem]'
)}
>
{
Expand Down Expand Up @@ -113,12 +108,8 @@ export function Footer(props: { context: GitBookSiteContext }) {
{
// Navigation groups (split into equal columns)
customization.footer.groups?.length > 0 ? (
<div
className={tcls(
'@4xl:page-has-toc:col-span-1 @7xl:page-no-toc:col-span-1 col-span-2 @4xl:page-has-toc:col-start-2 @7xl:page-no-toc:col-start-2'
)}
>
<div className="mx-auto flex max-w-3xl site-width-wide:max-w-screen-2xl @xl:flex-row flex-col @xl:gap-6 gap-10">
<div className={tcls('col-span-2')}>
<div className="mx-auto flex @xl:flex-row flex-col @xl:gap-6 gap-10">
{partition(customization.footer.groups, FOOTER_COLUMNS).map(
(column, columnIndex) => (
<div
Expand Down
108 changes: 25 additions & 83 deletions packages/gitbook/src/components/PageAside/PageAside.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import type { GitBookSiteContext } from '@/lib/context';
import { getDocumentSections } from '@/lib/document-sections';
import { tcls } from '@/lib/tailwind';
import {
type JSONDocument,
type RevisionPageDocument,
SiteAdsStatus,
SiteInsightsAdPlacement,
} from '@gitbook/api';
import { Icon } from '@gitbook/icons';
import React from 'react';

import { getSpaceLanguage, t } from '@/intl/server';
import { getDocumentSections } from '@/lib/document-sections';
import { tcls } from '@/lib/tailwind';

import { Ad } from '../Ads';
import { PageFeedbackForm } from '../PageFeedback';
import { ThemeToggler } from '../ThemeToggler';
Expand All @@ -37,36 +34,37 @@ export function PageAside(props: {
'group/aside',
'order-last',
'hidden',
'max-w-0',
'pt-8',
'pb-4',
'opacity-0',
'ml-12',

'xl:flex',

'overflow-hidden',

'xl:max-w-56',
'xl:opacity-11',
'xl:ml-12',

'xl:max-3xl:chat-open:hidden',
'xl:max-3xl:chat-open:max-w-0',
'xl:max-3xl:chat-open:opacity-0',
'xl:max-3xl:chat-open:ml-0',

'hydrated:starting:ml-0',
'hydrated:starting:max-w-0',
'hydrated:starting:opacity-0',

'transition-[margin,max-width,opacity,display] duration-300',
'transition-discrete',

'basis-56',
'grow-0',
'shrink-0',
'break-anywhere', // To prevent long words in headings from breaking the layout

'xl:max-3xl:chat-open:hidden',
'mr-0',

// In layout-wide mode (2-column), hide outline when viewport is too narrow
// or when chat is open and viewport is narrow, to prevent layout overflow
'layout-wide:-mr-68',
'layout-wide:max-4xl:hidden',
'layout-wide:chat-open:max-[2416px]:hidden',

// In layout-full mode (1-column, no TOC), position outline as a fixed sidebar on the right
// Hide it on narrow viewports (< 3xl) to prevent overlap with content
'layout-full:max-3xl:hidden',
'layout-full:w-56',
'layout-full:fixed',
'layout-full:right-4',
'layout-full:h-full',
'layout-full:z-30',

'text-tint',
'contrast-more:text-tint-strong',
'sticky',
Expand All @@ -85,39 +83,12 @@ export function PageAside(props: {

// Client-side dynamic positioning (CSS vars applied by script)
'lg:[html[style*="--outline-top-offset"]_&]:top-(--outline-top-offset)!',
'lg:[html[style*="--outline-height"]_&]:max-h-(--outline-height)!',

// When in api page mode, we display it as an overlay on non-large resolutions
'xl:max-2xl:page-api-block:z-10',
'xl:max-2xl:page-api-block:fixed',
'xl:max-2xl:page-api-block:right-8',
'xl:max-2xl:page-api-block:w-60',
'xl:max-2xl:page-api-block:max-w-60',
'xl:max-2xl:page-api-block:pb-8',
'xl:max-2xl:page-api-block:pt-10',
'xl:max-2xl:[body:has(.openapi-block):has(.page-has-ancestors)_&]:pt-6.5'
'lg:[html[style*="--outline-height"]_&]:max-h-(--outline-height)!'
)}
>
<div
className={tcls(
'flex flex-col',
'min-w-56 shrink-0',
'overflow-hidden',
'w-full',
'xl:max-2xl:rounded-corners:page-api-block:rounded-md',
'xl:max-2xl:circular-corners:page-api-block:rounded-xl',
'xl:max-2xl:page-api-block:border',
'xl:max-2xl:page-api-block:border-tint',
'xl:max-2xl:page-api-block:bg-tint/9',
'xl:max-2xl:page-api-block:backdrop-blur-lg',
'xl:max-2xl:contrast-more:page-api-block:bg-tint',
'xl:max-2xl:page-api-block:hover:shadow-lg',
'xl:max-2xl:page-api-block:hover:shadow-tint-12/1',
'xl:max-2xl:dark:page-api-block:hover:shadow-tint-1/1',
'xl:max-2xl:page-api-block:not-hover:*:hidden'
)}
className={tcls('flex flex-col', 'min-w-56 shrink-0', 'overflow-hidden', 'w-full')}
>
<PageAsideHeader context={context} />
{page.layout.outline ? (
<div className="flex shrink flex-col overflow-hidden">
{document ? (
Expand All @@ -136,34 +107,6 @@ export function PageAside(props: {
);
}

function PageAsideHeader(props: { context: GitBookSiteContext }) {
const { context } = props;
const language = getSpaceLanguage(context);

return (
<div
className={tcls(
'hidden',
'xl:max-2xl:page-api-block:flex!',
'text-xs',
'tracking-wide',
'font-semibold',
'uppercase',
'px-2',
'py-1.5',

'flex-row',
'items-center',
'gap-2'
)}
>
<Icon icon="block-quote" className={tcls('size-3')} />
{t(language, 'on_this_page')}
<Icon icon="chevron-down" className={tcls('size-3', 'opacity-6', 'ml-auto')} />
</div>
);
}

async function PageAsideSections(props: { document: JSONDocument; context: GitBookSiteContext }) {
const { document, context } = props;

Expand All @@ -187,7 +130,7 @@ function PageAsideActions(props: {
className={tcls(
'flex flex-col gap-3',
'border-tint-subtle border-t first:border-none',
'sidebar-list-default:px-3 pt-5 first:pt-0 xl:max-2xl:page-api-block:p-5',
'sidebar-list-default:px-3 pt-5 first:pt-0',
'empty:hidden'
)}
>
Expand All @@ -208,8 +151,7 @@ async function PageAsideFooter(props: { context: GitBookSiteContext }) {
<div
className={tcls(
'sticky bottom-0 z-10 mt-auto flex flex-col',
'bg-tint-base theme-gradient-tint:bg-gradient-tint theme-gradient:bg-gradient-primary theme-muted:bg-tint-subtle [html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
'border-tint-subtle xl:max-2xl:page-api-block:border-t xl:max-2xl:page-api-block:p-2',
'border-tint-subtle',
'pt-4'
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function ScrollSectionsList({ sections }: { sections: DocumentSection[] }

return (
<ul
className="relative flex flex-col border-tint-subtle sidebar-list-line:border-l pb-5 xl:max-2xl:page-api-block:mt-0 xl:max-2xl:page-api-block:p-2"
className="relative flex flex-col border-tint-subtle sidebar-list-line:border-l pb-5"
ref={scrollContainerRef}
>
{sections.map((section) => (
Expand Down
Loading
Loading