Astryx Layout Component Architecture: How Header, Footer, Panel, and Content Slots Work
The Astryx Layout component orchestrates four visual slots—Header, Footer, Panel, and Content—through a root shell that uses React contexts to coordinate slot presence, padding inheritance, and divider behavior without manual CSS management.
The Astryx Layout component provides the structural foundation for applications in the facebook/astryx design system. It manages a flexible grid system composed of LayoutHeader, LayoutFooter, LayoutPanel (sidebars), and LayoutContent areas through a combination of CSS custom properties and context providers. This architecture enables developers to build responsive, RTL-aware page shells while the framework handles complex concerns like height management, padding collapse, and nested divider inheritance.
Core Architecture of the Layout Component
The Root Layout Shell (Layout.tsx)
At the center of the system is the Layout component in packages/core/src/Layout/Layout.tsx. This root shell accepts five primary slot props: header, footer, start, end, and content (where children acts as a shorthand for content).
The component implements two distinct height modes controlled via the height prop:
'fill'– The layout fills its container and handles internal scrolling'auto'– The container manages scrolling while the layout expands naturally
To achieve edge-to-edge designs, Layout applies negative margins through styles.layoutOuter to cancel container padding, while styles.layoutInner resets CSS custom properties for descendants. The outer padding is controlled via CSS variables --layout-padding-outer-x and --layout-padding-outer-y, which map to the padding prop values defined in packages/core/src/Layout/padding.stylex.ts.
Slot Detection via LayoutSlotsContext
The LayoutSlotsContext (defined in packages/core/src/Layout/LayoutSlotsContext.ts) propagates slot presence information throughout the tree. Created via useMemo in the Layout component, this context exposes a simple object structure:
{
hasHeader: boolean,
hasFooter: boolean,
hasStart: boolean,
hasEnd: boolean
}
Descendant components consume this context to conditionally render dividers, collapse padding, or apply side-specific styling without prop drilling.
Divider Inheritance with LayoutDividerContext
When the Layout component receives the defaultHasDividers prop, it wraps the tree with LayoutDividerContext from packages/core/src/Layout/LayoutDividerContext.ts. This boolean flag allows nested LayoutHeader and LayoutFooter components to inherit default border-bottom styles automatically, ensuring consistent visual separation across nested layout hierarchies.
Area Identification via LayoutAreaContext
Each slot receives its positional identity through LayoutAreaContext (packages/core/src/Layout/LayoutAreaContext.ts). The AreaProvider wraps individual slots with values of 'header', 'start', 'content', 'end', or 'footer', enabling child components to detect which layout region they occupy for context-aware styling.
The Four Visual Slots
LayoutHeader and LayoutFooter
LayoutHeader (packages/core/src/Layout/LayoutHeader.tsx) renders the top-most slot with flex-shrink: 0 (via styles.header) to maintain fixed positioning during scroll. It utilizes themeProps('layout-header') for theming and applies dynamic height styles. The inner wrapper (styles.inner) controls content width through CSS custom properties and respects the contentWidth constraint.
When padding={0} is specified, the component switches to a "full-bleed" variant that removes all container padding. The optional hasDivider prop renders a bottom border, which can be inherited from LayoutDividerContext.
LayoutFooter (packages/core/src/Layout/LayoutFooter.tsx) mirrors this implementation for the bottom slot, supporting identical padding controls and divider behavior.
LayoutPanel for Sidebars
LayoutPanel (packages/core/src/Layout/LayoutPanel.tsx) represents sidebar regions positioned via the start (left) or end (right) props. It utilizes stackItem({size: 'fit'}) to size itself to content or accept a fixed width prop.
The panel accepts a role attribute for accessibility (typically "navigation") and automatically collapses its internal padding against the layout's outer padding variables, ensuring seamless edge-to-edge designs. Responsive RTL support is achieved through logical CSS properties (margin-inline, padding-inline) that automatically flip in right-to-left environments.
LayoutContent for the Main Area
LayoutContent (packages/core/src/Layout/LayoutContent.tsx) serves as the central scrollable region with role="main" by default. Its styles include styles.middle (applying flex: 1 and min-height: 0) to ensure the content occupies remaining vertical space while respecting flex container constraints.
When height='fill', the outer wrapper calculates content height by adding the container's block padding to prevent scroll-jank during layout calculations.
Styling and Spacing System
CSS Custom Properties and Padding Control
The padding.stylex.ts file (packages/core/src/Layout/padding.stylex.ts) defines CSS variable maps for spacing steps (0, 0.5, 1 through 10). These variables propagate throughout the layout system:
--layout-padding-outer-xand--layout-padding-outer-ycontrol container spacing--layout-content-widthconstrains maximum content width when thecontentWidthprop is provided
Height Modes and Content Width
The contentWidth prop on the root Layout component sets --layout-content-width, centering the inner content area when specified. Combined with padding={0}, this enables full-bleed layouts where background colors touch viewport edges while text content remains constrained.
Practical Implementation Examples
Basic Page Structure
This example demonstrates a standard application shell with header, navigation sidebar, and main content:
import {Layout, LayoutHeader, LayoutPanel, LayoutContent} from '@astryxdesign/core';
function SimplePage() {
return (
<Layout
header={<LayoutHeader hasDivider>My App</LayoutHeader>}
start={
<LayoutPanel width={240} role="navigation">
{/* navigation items */}
</LayoutPanel>
}
content={
<LayoutContent>
{/* main page content */}
</LayoutContent>
}
/>
);
}
The header and start slots render with automatic padding management, while LayoutContent fills the remaining space with proper scroll behavior.
Full-Bleed Layouts
For dashboards requiring edge-to-edge backgrounds with constrained content:
<Layout
padding={0}
contentWidth={960}
header={<LayoutHeader>Dashboard</LayoutHeader>}
footer={<LayoutFooter>© 2026</LayoutFooter>}
content={<LayoutContent>{/* ... */}</LayoutContent>}
/>
Setting padding={0} activates the full-bleed mode (styles.fullBleed), while contentWidth={960} centers content at a maximum width of 960 pixels.
Nested Layouts with Inherited Dividers
Context propagation enables consistent divider behavior across nested layouts:
<Layout defaultHasDividers>
<LayoutHeader>Outer Header</LayoutHeader>
<Layout
header={<LayoutHeader>Inner Header</LayoutHeader>}
content={<LayoutContent>Nested content</LayoutContent>}
/>
</Layout>
Because the outer Layout specifies defaultHasDividers, nested LayoutHeader and LayoutFooter components automatically render bottom borders unless explicitly overridden with hasDivider={false}.
Using Children as Content Shorthand
The Layout component treats direct children as the content slot when the explicit content prop is omitted:
<Layout header={<LayoutHeader>Title</LayoutHeader>}>
<p>This renders inside LayoutContent automatically</p>
</Layout>
Summary
- The Astryx Layout component (
Layout.tsx) serves as the root shell coordinating Header, Footer, Panel, and Content slots through explicit props or children. - Context providers (
LayoutSlotsContext,LayoutAreaContext,LayoutDividerContext) eliminate prop drilling by broadcasting slot presence, area identity, and divider defaults throughout the React tree. - CSS custom properties defined in
padding.stylex.tsenable dynamic spacing control, supporting both constrained content widths and full-bleed edge-to-edge designs. - Height management offers
'fill'mode for internal scrolling or'auto'mode for container-scrolled layouts, withLayoutContentreceivingflex: 1to occupy remaining space. - RTL support is built into the architecture through logical CSS properties, ensuring sidebars and padding automatically adapt to right-to-left layouts without additional configuration.
Frequently Asked Questions
How does the Astryx Layout component handle responsive sidebar widths?
The LayoutPanel component accepts a width prop that applies a fixed pixel width to the sidebar. When no width is specified, it uses stackItem({size: 'fit'}) to size according to content. The layout system relies on CSS Flexbox for distribution, so panels automatically adjust their dimensions within the flex container while maintaining the specified constraints.
Can nested Layout components inherit divider styles from parent layouts?
Yes. When a parent Layout component receives the defaultHasDividers prop, it renders LayoutDividerContext around its children. Any nested LayoutHeader or LayoutFooter that does not explicitly define the hasDivider prop will automatically inherit the divider behavior from this context, ensuring consistent visual separation across complex, nested interface hierarchies.
What is the difference between the 'fill' and 'auto' height modes?
The height='fill' mode causes the Layout component to expand to fill its container while managing scrolling internally within LayoutContent. Conversely, height='auto' allows the layout to expand naturally with its content, delegating scrolling responsibility to the parent container. The 'fill' mode uses min-height: 0 constraints on flex children to prevent overflow issues, while 'auto' permits content-driven expansion.
How does the Layout component support full-bleed designs without custom CSS?
Setting padding={0} on the root Layout component activates the full-bleed mode via styles.fullBleed, which removes outer container padding and allows backgrounds to touch viewport edges. Simultaneously, the contentWidth prop can constrain the actual content area by setting --layout-content-width, creating a common pattern where visual elements span the full width while text remains readable and centered.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →