Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 11 additions & 1 deletion packages/gitbook/src/components/DocumentView/Blocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
ancestorBlocks: props.ancestorBlocks,
});

// Determine if this block should expand to full width or use readable width
// Text blocks (paragraphs, headings, lists) use a readable max-width
// Visual blocks (code, tables, images, etc.) expand to full width
const isFullWidthBlock = FULL_WIDTH_BLOCKS.includes(node.type);

return (
<Block
key={node.key || `${node.type}-${index}`}
Expand All @@ -94,7 +99,12 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
? 'max-w-screen-xl'
: 'max-w-3xl',
!LIST_BLOCKS.includes(node.type) && 'print:break-inside-avoid',
FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-full',
isFullWidthBlock && 'page-width-wide:max-w-full',
// In OpenAPI mode, all blocks expand to full width
// In full-width mode, content is capped at 64rem (1024px) and centered
'layout-openapi:max-w-full',
'layout-full-width:max-w-5xl',
'layout-full-width:mx-auto',
blockStyle,
]}
isEstimatedOffscreen={isOffscreen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ export function DocumentView(
style,
// Preserve adjacent whitespace and new lines.
'whitespace-pre-wrap',
// In OpenAPI mode, add left padding for sidebar offset
'layout-openapi:pl-12',
]}
context={context}
isOffscreen={isOffscreen}
Expand All @@ -85,7 +87,7 @@ export function DocumentViewSkeleton(props: { document: JSONDocument; blockStyle
const { document, blockStyle } = props;

return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4 layout-openapi:pl-12">
{document.nodes.map((block) => (
<BlockSkeleton
key={block.key!}
Expand All @@ -95,6 +97,10 @@ export function DocumentViewSkeleton(props: { document: JSONDocument; blockStyle
block.data && 'fullWidth' in block.data && block.data.fullWidth
? 'max-w-screen-xl'
: 'max-w-3xl',
// Expand in OpenAPI mode, cap at 64rem in full-width mode
'layout-openapi:max-w-full',
'layout-full-width:max-w-5xl',
'layout-full-width:mx-auto',
blockStyle,
]}
/>
Expand Down
11 changes: 10 additions & 1 deletion packages/gitbook/src/components/DocumentView/StepperStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,16 @@ 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(
'mx-auto flex w-full max-w-3xl flex-row gap-4 md:gap-8',
// Expand in OpenAPI mode, cap at 64rem in full-width mode
'layout-openapi:max-w-full',
'layout-full-width:max-w-5xl',
'layout-full-width:mx-auto',
style
)}
>
<div className="relative select-none">
<div
className={tcls(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ const TabPanel = memo(function TabPanel(props: {
aria-labelledby={getTabButtonId(tab.id)}
className="scroll-mt-[calc(var(--content-scroll-margin)+var(--spacing)*20)]"
>
<div className="p-4" hidden={!isActive}>
<div className={tcls('p-4')} hidden={!isActive}>
{tab.body}
</div>
</div>
Expand Down
26 changes: 17 additions & 9 deletions packages/gitbook/src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,12 @@ 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',
'mx-auto flex @xs:grid @4xl:max-w-none! layout-full-width:max-w-screen-2xl max-w-3xl 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]'
'@4xl:layout-full-width:grid-cols-[minmax(auto,90rem)_auto]',
'@7xl:layout-full-width:grid-cols-[14rem_minmax(auto,90rem)_14rem]'
)}
>
{
Expand Down Expand Up @@ -115,10 +111,22 @@ export function Footer(props: { context: GitBookSiteContext }) {
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'
'col-span-2',
'@4xl:layout-default:col-span-1',
'@4xl:layout-default:col-start-2',
'@4xl:layout-openapi:col-span-1',
'@4xl:layout-openapi:col-start-2',
'@7xl:layout-full-width:col-span-1',
'@7xl:layout-full-width: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(
'mx-auto flex max-w-3xl flex-col gap-10',
'layout-full-width:max-w-screen-2xl',
'@xl:flex-row @xl:gap-6'
)}
>
{partition(customization.footer.groups, FOOTER_COLUMNS).map(
(column, columnIndex) => (
<div
Expand Down
2 changes: 1 addition & 1 deletion packages/gitbook/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function Header(props: {
'hover:theme-bold:bg-header-link/3',
variants.generic.length > 1
? 'lg:hidden'
: 'page-no-toc:hidden lg:hidden'
: 'layout-full-width:hidden lg:hidden'
)}
/>
<HeaderLogo context={context} />
Expand Down
80 changes: 48 additions & 32 deletions packages/gitbook/src/components/PageAside/PageAside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,39 @@ export function PageAside(props: {
className={tcls(
'group/aside',
'order-last',
'hidden',
'max-w-0',
'pt-8',
'pb-4',
'opacity-0',

'xl:flex',
// Hide by default
'hidden',

// Show on xl screens for default layout (normal sidebar)
'xl:layout-default:flex',
'xl:layout-default:max-w-56',
'xl:layout-default:opacity-11',
'xl:layout-default:ml-12',

// Show on xl screens for OpenAPI layout (floating overlay)
'xl:layout-openapi:flex',
'xl:layout-openapi:opacity-100',
'xl:layout-openapi:z-10',
'xl:layout-openapi:fixed',
'xl:layout-openapi:right-8',
'xl:layout-openapi:w-60',
'xl:layout-openapi:max-w-60',
'xl:layout-openapi:pb-8',
'xl:layout-openapi:pt-10',
'xl:layout-openapi:ml-0',

// Always hide for full-width layout
'layout-full-width:hidden!',

'overflow-hidden',

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

// Hide when chat is open
'xl:max-3xl:chat-open:hidden',
'xl:max-3xl:chat-open:max-w-0',
'xl:max-3xl:chat-open:opacity-0',
Expand Down Expand Up @@ -85,17 +104,7 @@ 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
Expand All @@ -104,17 +113,19 @@ export function PageAside(props: {
'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'

// OpenAPI layout: floating card styles
'xl:layout-openapi:rounded-md',
'xl:layout-openapi:circular-corners:rounded-xl',
'xl:layout-openapi:border',
'xl:layout-openapi:border-tint',
'xl:layout-openapi:bg-tint/9',
'xl:layout-openapi:backdrop-blur-lg',
'xl:layout-openapi:contrast-more:bg-tint',
'xl:layout-openapi:hover:shadow-lg',
'xl:layout-openapi:hover:shadow-tint-12/1',
'xl:dark:layout-openapi:hover:shadow-tint-1/1',
'xl:layout-openapi:not-hover:*:hidden'
)}
>
<PageAsideHeader context={context} />
Expand All @@ -136,6 +147,10 @@ export function PageAside(props: {
);
}

/**
* Header for the aside that shows "ON THIS PAGE" label.
* Only visible in OpenAPI layout (floating overlay mode).
*/
function PageAsideHeader(props: { context: GitBookSiteContext }) {
const { context } = props;
const language = getSpaceLanguage(context);
Expand All @@ -144,7 +159,7 @@ function PageAsideHeader(props: { context: GitBookSiteContext }) {
<div
className={tcls(
'hidden',
'xl:max-2xl:page-api-block:flex!',
'xl:layout-openapi:flex!',
'text-xs',
'tracking-wide',
'font-semibold',
Expand Down Expand Up @@ -187,7 +202,8 @@ 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',
'xl:layout-openapi:p-5',
'empty:hidden'
)}
>
Expand All @@ -209,7 +225,7 @@ async function PageAsideFooter(props: { context: GitBookSiteContext }) {
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 xl:layout-openapi:border-t xl:layout-openapi:p-2',
'pt-4'
)}
>
Expand Down
65 changes: 57 additions & 8 deletions packages/gitbook/src/components/PageBody/PageBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import type { JSONDocument, RevisionPageDocument, SiteInsightsDisplayContext } f

import { getSpaceLanguage } from '@/intl/server';
import { t } from '@/intl/translate';
import { hasFullWidthBlock, hasMoreThan, hasTopLevelBlock, isNodeEmpty } from '@/lib/document';
import {
hasFullWidthBlock,
hasMoreThan,
hasOpenAPIBlock,
hasTopLevelBlock,
isNodeEmpty,
} from '@/lib/document';
import type { AncestorRevisionPage } from '@/lib/pages';
import { tcls } from '@/lib/tailwind';
import { DocumentView, DocumentViewSkeleton } from '../DocumentView';
Expand Down Expand Up @@ -41,6 +47,7 @@ export function PageBody(props: {
const { customization } = context;

const contentFullWidth = document ? hasFullWidthBlock(document) : false;
const contentHasOpenAPI = document ? hasOpenAPIBlock(document) : false;

// Update blocks can only be at the top level of the document, so we optimize the check.
const contentHasUpdates = document
Expand All @@ -55,8 +62,7 @@ export function PageBody(props: {
LINK_PREVIEW_MAX_COUNT
)
: false;
const pageWidthWide = page.layout.width === 'wide';
const siteWidthWide = pageWidthWide || contentFullWidth;

const language = getSpaceLanguage(context);
const updatedAt = page.updatedAt ?? page.createdAt;

Expand All @@ -65,23 +71,49 @@ export function PageBody(props: {
(page) => page.type !== 'document' || (page.type === 'document' && !page.hidden)
).length > 0;

const pageHasToc = page.layout.tableOfContents && hasVisibleTOCItems;
const hasTOC = page.layout.tableOfContents && hasVisibleTOCItems;

// Determine layout mode:
// 1. Full-width: No TOC
// 2. OpenAPI: Has TOC + (OpenAPI block OR wide property)
// 3. Default: Has TOC, no OpenAPI blocks, not wide
const layoutMode = !hasTOC
? 'layout-full-width'
: contentHasOpenAPI || page.layout.width === 'wide'
? 'layout-openapi'
: 'layout-default';

// Site-wide width only applies to full-width mode
const siteWidthWide = !hasTOC && (page.layout.width === 'wide' || contentFullWidth);

return (
<CurrentPageProvider page={{ spaceId: context.space.id, pageId: page.id }}>
<main
className={tcls(
'relative min-w-0 flex-1',
'max-w-screen-2xl py-8',
// In full-width layout, expand main to allow cover to go full width
'layout-full-width:max-w-full',
'layout-full-width:px-0',
// Allow words to break if they are too long.
'break-anywhere',
'@container',
pageWidthWide ? 'page-width-wide 3xl:px-8' : 'page-width-default',
// Layout mode class for CSS variants
layoutMode,
// Keep existing classes for backward compatibility
hasTOC ? 'page-has-toc' : 'page-no-toc',
siteWidthWide ? 'site-width-wide' : 'site-width-default',
pageHasToc ? 'page-has-toc' : 'page-no-toc'
// Only apply page-width-wide in full-width mode
!hasTOC && page.layout.width === 'wide'
? 'page-width-wide 3xl:px-8'
: 'page-width-default'
)}
>
<PreservePageLayout siteWidthWide={siteWidthWide} pageHasToc={pageHasToc} />
<PreservePageLayout
siteWidthWide={siteWidthWide}
layoutMode={layoutMode}
hasTOC={hasTOC}
/>
{page.cover && page.layout.cover && page.layout.coverSize === 'hero' ? (
<PageCover as="hero" page={page} cover={page.cover} context={context} />
) : null}
Expand Down Expand Up @@ -128,7 +160,24 @@ export function PageBody(props: {
{
// TODO: after 25/07/2025, we can chage it to a true check as the cache will be updated
page.layout.metadata !== false ? (
<div className="mx-auto mt-6 page-api-block:ml-0 flex max-w-3xl page-full-width:max-w-screen-2xl flex-row flex-wrap items-center gap-4 text-tint contrast-more:text-tint-strong">
<div
className={tcls(
'mx-auto',
'mt-6',
'flex',
'max-w-3xl',
'flex-row',
'flex-wrap',
'items-center',
'gap-4',
'text-tint',
'contrast-more:text-tint-strong',
'layout-openapi:max-w-full',
'layout-openapi:pl-12',
'layout-full-width:max-w-5xl',
'layout-full-width:mx-auto'
)}
>
{updatedAt ? (
<p className="mr-auto text-sm ">
{t(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export async function PageBodyBlankslate(props: {
'grid',
'max-w-3xl',
'page-width-wide:max-w-screen-2xl',
// Expand to full width in OpenAPI and full-width layout modes
'layout-openapi:max-w-full',
'layout-full-width:max-w-full',
'w-full',
'mx-auto',
'gap-4',
Expand Down
Loading
Loading