UNPKG

@loke/design-system

Version:

A design system with individually importable components

290 lines (276 loc) 12.8 kB
var AGENTS_default=`<!-- Parent: ../AGENTS.md --> <!-- Generated: 2026-04-07 | Updated: 2026-04-07 --> <h1>layout</h1> <h2>Purpose</h2> <p>This directory contains 6 layout primitive components designed for composing page structure and spacing. Unlike presentational components, layout primitives expose low-level control over flex/grid layout, spacing, sizing, and positioning. They form the foundation for responsive layouts and can be combined to build complex UIs.</p> <h2>Key Components</h2> <table> <thead> <tr><th>Component</th><th>Purpose</th></tr> </thead> <tbody> <tr><td><strong>Box</strong></td><td>Universal layout primitive. Wraps any element with flexible layout and spacing props. Foundation for all other layout components.</td></tr> <tr><td><strong>Stack</strong></td><td>Flex column with default gap. Shorthand for vertical layouts.</td></tr> <tr><td><strong>Columns</strong></td><td>Grid-based multi-column layout. Responsive grid with automatic column count.</td></tr> <tr><td><strong>Inline</strong></td><td>Flex row with wrapping. Horizontally stacks items with gap and wrap support.</td></tr> <tr><td><strong>MaxWidthWrapper</strong></td><td>Constrains content to max-width and centers. Typical page-width container.</td></tr> <tr><td><strong>PageLayout</strong></td><td>Complete page structure primitive. Combines Box with semantic layout divisions.</td></tr> </tbody> </table> <h2>Directory Structure</h2> <p>Each layout component follows the standard structure:</p> <pre><code>src/layout/ComponentName/ ├── component-name.tsx # Implementation (usually minimal wrapper) ├── component-name.stories.tsx # Storybook stories ├── index.ts # Named export └── README.mdx # Documentation (optional for simple components) </code></pre> <p><strong>Note:</strong> Layout components typically have simple implementations and may skip test files since they compose Box and other primitives.</p> <h2>For AI Agents</h2> <h3>Component Characteristics</h3> <p>Layout components differ from UI components in key ways:</p> <ul> <li><strong>Minimal styling:</strong> Mostly expose Tailwind utilities as props</li> <li><strong>Composition-first:</strong> Designed to wrap other components</li> <li><strong>Prop-heavy:</strong> Many optional spacing/sizing props</li> <li><strong>Responsive:</strong> Use ResponsiveProps for breakpoint-aware values</li> <li><strong>Flexible:</strong> Use <code>as</code> prop to render different HTML elements</li> </ul> <h3>Box — The Foundation</h3> <p>Box is the most powerful layout component. All others build on it.</p> <pre><code class="language-tsx">import { Box, type BoxProps } from &quot;@loke/design-system/box&quot;; // Box exposes nearly all Tailwind utilities as props: &lt;Box display=&quot;flex&quot; flexDirection=&quot;col&quot; gap={4} padding={2} marginTop=&quot;auto&quot; alignItems=&quot;center&quot; justifyContent=&quot;between&quot; width=&quot;full&quot; height=&quot;screen&quot; className=&quot;custom-classes&quot; as=&quot;section&quot; asChild={false} style={{ zIndex: 10 }} &gt; {children} &lt;/Box&gt; </code></pre> <p><strong>Key Box Props:</strong></p> <table> <thead> <tr><th>Category</th><th>Props</th></tr> </thead> <tbody> <tr><td><strong>Display</strong></td><td>display, flexDirection, flexWrap, gap, gapX, gapY, spaceX, spaceY</td></tr> <tr><td><strong>Alignment</strong></td><td>alignItems, justifyContent</td></tr> <tr><td><strong>Sizing</strong></td><td>width, height, minWidth, minHeight, maxWidth, maxHeight</td></tr> <tr><td><strong>Spacing</strong></td><td>padding, paddingX, paddingY, margin, marginX, marginY</td></tr> <tr><td><strong>Positioning</strong></td><td>position, top, left, right, bottom, zIndex</td></tr> <tr><td><strong>Styling</strong></td><td>background, border, borderColor, borderRadius, boxShadow</td></tr> <tr><td><strong>Behavior</strong></td><td>overflow, overflowX, overflowY, pointerEvents, cursor, container</td></tr> <tr><td><strong>Polymorphism</strong></td><td>as (HTML element), asChild (Slot composition), className, style</td></tr> </tbody> </table> <p>Box variants are generated at build time using macros (<code>getSizeVariants</code>, <code>getDimensionVariants</code>, etc.).</p> <h3>Stack — Vertical Flex</h3> <p>Simplest layout component. Wraps Box with sensible defaults for column layout.</p> <pre><code class="language-tsx">import { Stack, type StackProps } from &quot;@loke/design-system/stack&quot;; &lt;Stack gap={2}&gt; &lt;div&gt;Item 1&lt;/div&gt; &lt;div&gt;Item 2&lt;/div&gt; &lt;div&gt;Item 3&lt;/div&gt; &lt;/Stack&gt; </code></pre> <p><strong>Implementation:</strong></p> <pre><code class="language-tsx">type StackProps = Omit&lt;BoxProps, &quot;flexDirection&quot; | &quot;display&quot;&gt;; const Stack = ({ gap = 2, ...props }: StackProps) =&gt; { return &lt;Box display=&quot;flex&quot; flexDirection=&quot;col&quot; gap={gap} {...props} /&gt;; }; </code></pre> <p>Stack allows any Box prop except flexDirection and display (which are locked to column).</p> <h3>Columns — Responsive Grid</h3> <p>Multi-column layout that responds to container width.</p> <pre><code class="language-tsx">import { Columns, type ColumnsProps } from &quot;@loke/design-system/columns&quot;; &lt;Columns columns={{ initial: 1, sm: 2, md: 3, lg: 4 }} gap={4}&gt; {items.map((item) =&gt; &lt;div key={item.id}&gt;{item}&lt;/div&gt;)} &lt;/Columns&gt; </code></pre> <p><strong>Responsive Prop Pattern:</strong></p> <pre><code class="language-tsx">interface ResponsiveValue&lt;T&gt; { initial?: T; sm?: T; md?: T; lg?: T; xl?: T; } columns: ResponsiveValue&lt;number&gt; </code></pre> <h3>Inline — Horizontal Flex with Wrapping</h3> <p>Flexbox row that wraps items with gap.</p> <pre><code class="language-tsx">import { Inline, type InlineProps } from &quot;@loke/design-system/inline&quot;; &lt;Inline gap={2}&gt; &lt;Button&gt;Button 1&lt;/Button&gt; &lt;Button&gt;Button 2&lt;/Button&gt; &lt;Button&gt;Button 3&lt;/Button&gt; &lt;/Inline&gt; </code></pre> <p>Useful for button groups, tag lists, and horizontal navigation.</p> <h3>MaxWidthWrapper — Page Width Container</h3> <p>Constrains content to a max-width and centers horizontally.</p> <pre><code class="language-tsx">import { MaxWidthWrapper } from &quot;@loke/design-system/max-width-wrapper&quot;; &lt;MaxWidthWrapper&gt; &lt;h1&gt;Page content&lt;/h1&gt; &lt;p&gt;Automatically centered and max-widthed&lt;/p&gt; &lt;/MaxWidthWrapper&gt; </code></pre> <p>Typical use: Wrap page sections to maintain consistent content width.</p> <h3>PageLayout — Complete Page Structure</h3> <p>Higher-level component for page-level layout divisions.</p> <pre><code class="language-tsx">import { PageLayout } from &quot;@loke/design-system/page-layout&quot;; &lt;PageLayout&gt; {/* Automatically provides semantic structure */} &lt;/PageLayout&gt; </code></pre> <p>Check the component source for full prop interface.</p> <h3>Responsive Props Pattern</h3> <p>Layout components support responsive values via <code>ResponsiveProps&lt;T&gt;</code>:</p> <pre><code class="language-tsx">&lt;Box display={{ initial: &quot;block&quot;, md: &quot;flex&quot; }} gap={{ initial: 2, md: 4 }} width={{ initial: &quot;full&quot;, lg: &quot;90%&quot; }} /&gt; </code></pre> <p>This generates media query rules for each breakpoint. The <code>createResponsiveComponent</code> utility handles this at compile time.</p> <h3>Macros and Build-Time Optimization</h3> <p>Layout variants are generated using build-time macros (defined in <code>#macros/variants</code>):</p> <pre><code class="language-tsx">// These are evaluated at build time, not runtime import { getColorVariants, // Returns Tailwind color classes getDimensionVariants, // Width/height utilities getSizeVariants, // Spacing utilities (gap, padding, margin) getNegativeSizeVariants, // Negative margins/positions } from &quot;#macros/variants&quot; with { type: &quot;macro&quot; }; const boxVariants = cva(&quot;&quot;, { variants: { background: getColorVariants(&quot;bg&quot;), gap: getSizeVariants(&quot;gap&quot;), width: getDimensionVariants(&quot;w&quot;), }, }); </code></pre> <p>This ensures the generated CSS is minimal—only classes actually used are included.</p> <h3>Adding a New Layout Component</h3> <p>Layout components are much simpler than UI components. Most are thin wrappers over Box.</p> <ol> <li> <p><strong>Create folder:</strong> <code>src/layout/ComponentName/</code></p> </li> <li> <p><strong>Create implementation</strong> (<code>component-name.tsx</code>):</p> <pre><code class="language-tsx">import { Box, type BoxProps } from &quot;@loke/design-system/box&quot;; type YourComponentProps = Omit&lt;BoxProps, &quot;some-locked-prop&quot;&gt;; const YourComponent = ({ defaultProp = &quot;value&quot;, ...props }: YourComponentProps) =&gt; { return &lt;Box display=&quot;flex&quot; defaultProp={defaultProp} {...props} /&gt;; }; export { YourComponent }; export type { YourComponentProps }; </code></pre> </li> <li> <p><strong>Create stories</strong> (<code>component-name.stories.tsx</code>):</p> <ul> <li>Show default usage</li> <li>Show responsive variants</li> <li>Show different content types</li> <li>Keep stories minimal (layout components are mostly about composition)</li> </ul> </li> <li> <p><strong>Create index</strong> (<code>index.ts</code>):</p> <pre><code class="language-tsx">export { YourComponent } from &quot;./your-component&quot;; export type { YourComponentProps } from &quot;./your-component&quot;; </code></pre> </li> <li> <p><strong>Optional: Create README</strong> (<code>README.mdx</code>):</p> <ul> <li>Explain the layout metaphor</li> <li>Show typical use cases</li> <li>Document responsive patterns</li> </ul> </li> <li> <p><strong>Update package.json exports</strong> if needed</p> </li> </ol> <h3>Spacing and Sizing Tokens</h3> <p>All spacing props use Tailwind-compatible tokens:</p> <pre><code>0, 1, 2, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, ... </code></pre> <p>Use numbers directly as prop values:</p> <pre><code class="language-tsx">&lt;Box gap={4} padding={2} margin={{ initial: 2, md: 4 }} /&gt; </code></pre> <h3>Common Patterns</h3> <h4>Centered Layout</h4> <pre><code class="language-tsx">&lt;Box display=&quot;flex&quot; alignItems=&quot;center&quot; justifyContent=&quot;center&quot; width=&quot;full&quot; height=&quot;screen&quot; &gt; {children} &lt;/Box&gt; </code></pre> <h4>Two-Column Layout</h4> <pre><code class="language-tsx">&lt;Box display=&quot;grid&quot; gridTemplateColumns=&quot;1fr 2fr&quot; gap={4}&gt; &lt;Sidebar /&gt; &lt;MainContent /&gt; &lt;/Box&gt; </code></pre> <h4>Responsive Grid</h4> <pre><code class="language-tsx">&lt;Columns columns={{ initial: 1, sm: 2, lg: 3 }} gap={4}&gt; {items.map((item) =&gt; &lt;Card key={item.id}&gt;{item}&lt;/Card&gt;)} &lt;/Columns&gt; </code></pre> <h4>Spacer/Flex Grow</h4> <pre><code class="language-tsx">&lt;Box display=&quot;flex&quot; gap={2}&gt; &lt;div&gt;Start&lt;/div&gt; &lt;div style={{ flex: 1 }} /&gt; {/* Pushes to end */} &lt;div&gt;End&lt;/div&gt; &lt;/Box&gt; </code></pre> <p>Or use Box's flexGrow:</p> <pre><code class="language-tsx">&lt;Box display=&quot;flex&quot; gap={2}&gt; &lt;div&gt;Start&lt;/div&gt; &lt;Box flexGrow /&gt; &lt;div&gt;End&lt;/div&gt; &lt;/Box&gt; </code></pre> <h3>Testing Layout Components</h3> <p>Layout components are tested through visual inspection in Storybook. Functional tests verify that props are applied correctly:</p> <pre><code class="language-tsx">// Example smoke test for layout component import { createSmokeTests } from &quot;@loke/design-system/test&quot;; import * as stories from &quot;./stack.stories&quot;; createSmokeTests(stories, &quot;Stack&quot;); </code></pre> <h3>Debugging Layout Issues</h3> <ul> <li><strong>Unintended wrapping:</strong> Check gap and parent width</li> <li><strong>Items not centered:</strong> Verify alignItems and justifyContent are set</li> <li><strong>Responsive not working:</strong> Ensure prop is ResponsiveValue format</li> <li><strong>Spacing inconsistent:</strong> Check margin/padding conflicting; use gap instead</li> <li><strong>Elements overflow:</strong> Check maxWidth, minWidth constraints</li> </ul> <h3>Performance Considerations</h3> <ul> <li>Layout components are very cheap—they're mostly utility props</li> <li>Responsive props generate CSS at build time, not runtime</li> <li>No re-renders triggered by prop changes (pure styling)</li> <li>Safe to use liberally for layout structure</li> </ul> <!-- MANUAL: --> `;export{AGENTS_default as default};