diff --git a/src/components/splitLayout/README.md b/src/components/splitLayout/README.md new file mode 100644 index 0000000000000..049fefcbbcf1e --- /dev/null +++ b/src/components/splitLayout/README.md @@ -0,0 +1,95 @@ +# SplitLayout Component + +A two-column layout component designed for getting started guides that places explanatory text on the left and code samples on the right. + +## Features + +- **Responsive Design**: Automatically stacks on mobile (under 1024px) +- **Sticky Code**: Code samples stick to viewport on desktop for easy reference while scrolling +- **Clean Separation**: Clear visual separation between explanation and implementation +- **Flexible Content**: Support for any MDX content in both sections + +## Usage + +### Basic Example + +**Note:** MDX requires direct component names, not dot notation. + +````mdx + + + + ### Your Heading Explanatory text goes here. You can use any markdown: - + Lists - **Bold text** - Links This content appears on the left side. + + + + ```javascript // Your code sample goes here const example = "This appears + on the right"; ``` + + + + +```` + +### Multiple Sections + +You can stack multiple split sections within one layout: + +````mdx + + + + ### First Topic + Explanation for the first topic... + + + ```javascript + const first = "code"; + ``` + + + + + + ### Second Topic + Explanation for the second topic... + + + ```javascript + const second = "code"; + ``` + + + +```` + +## Component Structure + +- **``**: Container for one or more split sections +- **``**: Individual split section wrapper +- **``**: Left side text content (use this, not ``) +- **``**: Right side code samples (use this, not ``) + +## Styling + +The component uses CSS Grid for layout and is fully responsive: + +- **Desktop (>1024px)**: Two columns (50/50 split) +- **Mobile (≤1024px)**: Single column (stacked) + +The code section uses `position: sticky` on desktop to keep code visible while scrolling through long explanations. + +## Best Practices + +1. **Keep explanations concise**: The left column is limited in width, so focus on key points +2. **Use headings**: Start each Text section with a heading (h3 or h4) +3. **Code relevance**: Ensure code samples directly relate to the adjacent text +4. **Progressive complexity**: Order sections from simple to complex +5. **Mobile consideration**: Remember content stacks on mobile, so ensure reading flow makes sense + +## Examples in Use + +See it in action: + +- [Next.js Getting Started - Essential Configuration](/platforms/javascript/guides/nextjs/getting-started/#essential-configuration) diff --git a/src/components/splitLayout/index.tsx b/src/components/splitLayout/index.tsx new file mode 100644 index 0000000000000..b3b0e5140a8c6 --- /dev/null +++ b/src/components/splitLayout/index.tsx @@ -0,0 +1,72 @@ +'use client'; + +/** + * Component: SplitLayout / SplitSection + * + * Creates a two-column layout with text content on the left and code samples on the right. + * Ideal for getting started guides where you want to explain concepts alongside code examples. + * + * Usage in MDX: + * + * + * + * ## Your Heading + * Explanatory text goes here... + * + * + * ```javascript + * // Your code sample + * ``` + * + * + * + * + * Props: + * - SplitLayout: Container for one or more split sections + * - SplitSection: Individual split section wrapper + * - SplitSectionText: Left side text content + * - SplitSectionCode: Right side code samples + * + * Note: While SplitSection.Text and SplitSection.Code are available as properties + * for TypeScript convenience, MDX requires using the direct component names. + */ + +import {ReactNode} from 'react'; + +import styles from './style.module.scss'; + +type SplitLayoutProps = { + children: ReactNode; +}; + +type SplitSectionProps = { + children: ReactNode; +}; + +type SplitSectionTextProps = { + children: ReactNode; +}; + +type SplitSectionCodeProps = { + children: ReactNode; +}; + +export function SplitLayout({children}: SplitLayoutProps) { + return
{children}
; +} + +export function SplitSectionText({children}: SplitSectionTextProps) { + return
{children}
; +} + +export function SplitSectionCode({children}: SplitSectionCodeProps) { + return
{children}
; +} + +export function SplitSection({children}: SplitSectionProps) { + return
{children}
; +} + +// Attach Text and Code as properties of SplitSection for dot notation usage +SplitSection.Text = SplitSectionText; +SplitSection.Code = SplitSectionCode; diff --git a/src/components/splitLayout/style.module.scss b/src/components/splitLayout/style.module.scss new file mode 100644 index 0000000000000..fd2cbcb6dd009 --- /dev/null +++ b/src/components/splitLayout/style.module.scss @@ -0,0 +1,112 @@ +.splitLayoutContainer { + display: flex; + flex-direction: column; + gap: 2rem; + margin: 2rem 0; +} + +.splitSection { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 2rem; + align-items: start; + margin-bottom: 3rem; // Default spacing between sections (mb-5 equivalent) + + @media (max-width: 1024px) { + grid-template-columns: 1fr; + gap: 1.5rem; + margin-bottom: 2rem; // Reduced spacing on mobile + } + + // Remove margin from last section in a layout + &:last-child { + margin-bottom: 0; + } +} + +.splitText { + min-width: 0; // Allow flex item to shrink below content size + overflow-wrap: break-word; + + // Create a container that's 80% width + > * { + max-width: 80%; + } + + @media (max-width: 1024px) { + // Full width on mobile + > * { + max-width: 100%; + } + } + + // Ensure headings and paragraphs have appropriate spacing + > *:first-child { + margin-top: 0; + } + + > *:last-child { + margin-bottom: 0; + } + + // Style headings within the text section + h2, h3, h4 { + margin-top: 0; + margin-bottom: 0.75rem; + } + + p { + margin-bottom: 1rem; + line-height: 1.6; + } + + ul, ol { + margin-bottom: 1rem; + } +} + +.splitCode { + position: sticky; + top: calc(var(--header-height, 80px) + 1rem); + min-width: 0; // Allow flex item to shrink below content size + overflow: hidden; // Prevent content from expanding the column + + @media (max-width: 1024px) { + position: relative; + top: auto; + overflow: visible; + } + + // Ensure code blocks fill the space + > *:first-child { + margin-top: 0; + } + + > *:last-child { + margin-bottom: 0; + } + + // Override global code block styles to enable wrapping + // Use !important to ensure these override the global styles + :global(pre), + :global(pre[class*='language-']) { + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-wrap: break-word !important; + overflow-x: visible !important; + } + + :global(code), + :global(code[class*='language-']) { + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-wrap: break-word !important; + } + + :global(.code-line) { + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-wrap: break-word !important; + } +} + diff --git a/src/mdxComponents.ts b/src/mdxComponents.ts index 48bbccc71fc6a..e9d31ee401f64 100644 --- a/src/mdxComponents.ts +++ b/src/mdxComponents.ts @@ -44,6 +44,12 @@ import {SdkApi} from './components/sdkApi'; import {SdkOption} from './components/sdkOption'; import {SignInNote} from './components/signInNote'; import {SmartLink} from './components/smartLink'; +import { + SplitLayout, + SplitSection, + SplitSectionCode, + SplitSectionText, +} from './components/splitLayout'; import {StepComponent, StepConnector} from './components/stepConnector'; import {TableOfContents} from './components/tableOfContents'; import {VersionRequirement} from './components/version-requirement'; @@ -98,6 +104,10 @@ export function mdxComponents( RelayMetrics, SandboxLink, SignInNote, + SplitLayout, + SplitSection, + SplitSectionText, + SplitSectionCode, StepComponent, StepConnector, VimeoEmbed,