# Layout components Primitive components for composing consistent spacing and layout structures without writing custom CSS. Section: components Docs: https://nebula.kaluza.com/components/layout-components --- Layout components are low-level primitives that handle spacing and structural composition using theme tokens. They exist so that components and pages can use consistent spacing without writing one-off CSS. ## When to use - To add vertical spacing between a sequence of elements: use [Stack](#stack) - To group short items that wrap across lines, such as buttons or badges: use [Cluster](#cluster) - To apply theme-token margin to any element without modifying its styles directly: use [Margin](#margin) - To pull an element outside its parent's padding, for example a full-width image inside a padded card: use [Bleed](#bleed) - To create a responsive grid where columns are determined by a minimum item width, not a fixed column count: use [ContentGrid](#contentgrid) - To arrange two or more items side by side above a container-width threshold, falling back to stacked below: use Stack with `horizontalAbove` ## When not to use - **For complex responsive page layouts** - use [Row and Col](#row-and-col) only as a last resort when no other layout component meets the need. Prefer Stack, Cluster, and ContentGrid. - **For one-off spacing adjustments** - do not add custom `margin` or `padding` styles directly to components. Use [Margin](#margin) with a theme token instead. - **To make an element full-width on small screens** - use the component's own `fullWidth` prop, for example on [CTA Button](/components/cta-button#full-width), rather than writing custom responsive width styles. See [Stretch](#stretch) for how this works internally. ## Stack ### Anatomy 1. **Container** (the root `div` that owns the spacing contract) 2. **Items** (direct children; spacing is applied between consecutive items) ### Basic usage Use `spaceBetween` with a theme token. The value can be a numeric scale key (1–12) or a semantic space token. A numeric scale key: ```tsx Content Content Content ``` A semantic space token, which keeps the spacing tied to its meaning rather than a fixed step: ```tsx Content Content Content ``` ### Responsive spacing Pass an array of space keys to change the gap at each breakpoint. The array maps to `[default, smallAndUp, mediumAndUp, largeAndUp]`. ```tsx Content Content Content ``` ### Horizontal above a threshold Pass a `horizontalAbove` container-width string to switch Stack from vertical to horizontal layout when the container is wide enough. This is container-based, not viewport-based, so it works correctly inside sidebars or narrow panels. Drag the handle to resize the container and cross the 600px threshold: ```tsx Content Content Content ``` When `horizontalAbove` is set, items grow equally to fill available space. If you need unequal widths, use flexbox or a Grid directly. --- ## ContentStack ContentStack is a pre-configured Stack using the `betweenCards` spacing token. Use it for collections of cards or similar content blocks where you always want that token applied. ```tsx Account one Account two Account three ``` Pass `horizontal` to switch to a side-by-side layout above 750px. ```tsx Account one Account two ``` --- ## Cluster ### Anatomy 1. **Container** (a `ul` element using flexbox with wrapping) 2. **ClusterItem** (an `li` element wrapping each child) Cluster renders a `ul`/`li` structure. Wrap each child in a `ClusterItem`. ### Basic usage ```tsx Submit meter reading Cancel ``` ### Justification Use the `justify` prop to control horizontal alignment. The default is `flex-start`. ```tsx Active Non-communicating Peak ``` ### Row gap When items wrap to a second line, the vertical gap defaults to the same value as `spaceBetween`. Pass `rowGap` to use a different value. ```tsx Submit meter reading Cancel ``` --- ## Margin Margin applies theme-token spacing as margin to any child element. Use it when you need to offset a single element without changing its own styles or creating a wrapper with custom CSS. ### Anatomy 1. **Container** (a `div`, or inline wrapper when `inline` is set, that applies margin) Use `top`, `bottom`, `left`, or `right` to apply margin on a single side: ```tsx <> Content Content ``` Use `horizontal`, `vertical`, or `all` for shorthand. ```tsx Content ``` --- ## Bleed Bleed applies negative margin to pull an element outside its parent's padding. Use it when you need something to appear full-width inside a padded container, such as a full-bleed image inside a Card or a divider that touches the card edges. ### Anatomy 1. **Container** (a `div` that uses negative margin to extend beyond its parent's padding) The bled element extends past the Card's horizontal padding to touch its edges: ```tsx

This paragraph sits inside the card's padding.

Content
``` Pass `inline` to make the Bleed container `display: inline-block` rather than block. --- ## Stretch Stretch controls how wide an element is at different screen sizes. Use it when you need a button or other element to be full-width on small screens and auto-width on larger screens. Note: `Stretch` is not exported from the Nebula public API. It is documented here because Nebula's own field and button components use it internally to implement their `fullWidth` prop. In application code, use the `fullWidth` prop on the component itself — for example [CTA Button](/components/cta-button#full-width) — rather than reaching for `Stretch`. ### Anatomy 1. **Container** (a `div` that sets width and max-width based on the `fullWidth` prop) This example is not live, because `Stretch` is internal to Nebula and cannot be imported. ```tsx // Internal to Nebula - not importable from '@kaluza-platform/nebula'. Continue ``` `fullWidth` values: - `always`: always 100% wide - `never`: always `defaultWidth` - `small`: 100% on small screens, `defaultWidth` on `smallAndUp` --- ## ContentGrid ContentGrid uses CSS Grid with `auto-fit` and `minmax` to fit as many columns as possible at a minimum item width, without fixed column counts or media queries. ### Anatomy 1. **Container** (a `div` using CSS Grid) 2. **Items** (direct children placed into auto-sized columns) ```tsx Account one Account two Account three ``` Orphaned items on the last row expand to fill available space rather than leaving gaps. Pass `rowGap` to use a different vertical spacing value to `spaceBetween`. --- ## Row and Col Row and Col form a 12-column grid system. Use them only as a last resort when Stack, Cluster, and ContentGrid cannot meet the layout need. ### Anatomy 1. **Row** (the grid container, centred with a max-width set to `breakpoints.large`) 2. **Col** (a column cell; defaults to full width, span 12) ```tsx Left content Right content ``` Use the `small`, `medium`, and `large` props on Col to change the column span at each breakpoint. ```tsx Sidebar Main content ``` Nest rows by passing `isNested` to the inner Row. ## Rules and guidance ### Usage - **All layout components** MUST use a SpaceKey token (numeric scale 1-12 or semantic name) for all spacing props, never a raw pixel or rem value. Rationale: Raw values bypass the design system spacing scale and break cross-component consistency. [id: layout-components.use-theme-tokens-for-spacing; testability: auto] - **Consuming code** MUST NOT apply custom margin or padding directly to Nebula components via inline styles or CSS overrides. Rationale: Use Margin or Stack to add spacing. Applying it directly creates specificity conflicts and makes spacing invisible to reviewers. [id: layout-components.no-custom-margin-on-components; testability: context] - **Stack** MUST receive a spaceBetween prop. Rationale: There is no default; omitting it produces no spacing. [id: layout-components.space-between-required; testability: auto] - **Stack horizontalAbove** MUST receive a container-width string value, not a viewport-width media query string. Rationale: Stack uses container width via the intrinsic sizing trick. A viewport value will not work as expected. [id: layout-components.horizontal-above-uses-container-width; testability: auto] - **Stack spaceBetween** MUST order responsive array values from smallest breakpoint to largest ([default, smallAndUp, mediumAndUp, largeAndUp]). [id: layout-components.responsive-array-order; testability: auto] - **Stretch** MUST receive a defaultWidth value when fullWidth is set to never or small. Rationale: Without defaultWidth the element falls back to auto, which may not match the intended size. [id: layout-components.default-width-required-when-not-always; testability: auto] - **ContentGrid** MUST receive both min and spaceBetween props. [id: layout-components.min-and-space-between-required; testability: auto] - **Consuming code** SHOULD use Stack or Cluster before writing a custom flexbox wrapper for spacing. Rationale: Primitives enforce token use and communicate intent. Raw flexbox wrappers can use arbitrary pixel values. [id: layout-components.prefer-primitives-over-raw-flexbox; testability: context] - **Stack** SHOULD use a semantic space token (betweenFormFields, betweenCards) rather than a numeric key when the content matches a named pattern. Rationale: Semantic tokens can be updated globally without touching individual usage sites. [id: layout-components.use-semantic-tokens-for-known-patterns; testability: context] - **ContentStack** SHOULD be used for collections of Card components or similarly sized content blocks. Rationale: ContentStack is pre-configured with betweenCards spacing. Using raw Stack with betweenCards achieves the same result but is less expressive. [id: layout-components.for-card-collections; testability: context] - **Cluster** SHOULD be used for groups of items that may wrap to multiple lines and require consistent gap spacing in both axes. [id: layout-components.for-wrapping-inline-groups; testability: context] - **Cluster** SHOULD use rowGap when the vertical gap between wrapped rows should differ from spaceBetween. [id: layout-components.row-gap-for-wrap; testability: context] - **Margin** SHOULD be used to add token-based spacing around a single element that does not participate in a Stack or Cluster layout. [id: layout-components.use-for-single-element-offset; testability: context] - **Bleed spacing value** SHOULD match the spacing token used for the parent container's padding. Rationale: Mismatched values leave a visible gap or overlap. [id: layout-components.match-parent-padding; testability: context] - **Stretch** SHOULD be used when an element needs to be full-width on small screens and a fixed width on larger screens, rather than writing custom responsive CSS. [id: layout-components.use-for-responsive-width; testability: context] - **Col** SHOULD have span values across sibling columns that sum to 12 at each breakpoint. Rationale: Spans that sum to more than 12 cause overflow; less than 12 leave unintended blank space. [id: layout-components.spans-sum-to-twelve; testability: context] - **ContentStack horizontal** MAY be used to switch to a side-by-side layout at 750px. Rationale: The threshold is fixed at 750px. If a different threshold is needed, use Stack with horizontalAbove directly. [id: layout-components.horizontal-at-750px; testability: auto] - **Cluster** MAY omit justify when left-aligned behaviour is needed, as flex-start is the default. [id: layout-components.justify-default; testability: auto] - **Margin** SHOULD NOT be used as a substitute for Stack or Cluster when spacing multiple siblings. Rationale: Margin on individual items is harder to read and maintain than a shared spacing contract on a parent. [id: layout-components.not-as-layout-substitute; testability: context] - **ContentGrid** SHOULD NOT be used when a fixed column count is required regardless of container width. Rationale: ContentGrid uses auto-fit. If a design requires exactly two or three columns at all sizes, use Row/Col or a custom grid. [id: layout-components.not-for-fixed-column-counts; testability: context] - **Row / Col** SHOULD NOT be used when Stack, Cluster, or ContentGrid can achieve the same layout. Rationale: Row/Col is a legacy 12-column system. The other primitives are more expressive and easier to reason about. [id: layout-components.last-resort-only; testability: context] ### Placement - **Bleed** SHOULD be used only inside a padded container, where the bleed amount matches the container's padding token. Rationale: Bleed uses negative margin. Applying it outside a padded container produces unexpected layout shifts. [id: layout-components.only-inside-padded-container; testability: context] ### Composition - **Cluster** MUST wrap each direct child in a ClusterItem element. Rationale: Cluster renders a ul/li structure. Children placed directly inside Cluster without a ClusterItem produce invalid HTML. [id: layout-components.wrap-children-in-cluster-item; testability: auto] - **Col** MUST be a direct child of Row. Rationale: Col uses negative-margin-compensating padding that only works correctly inside a Row. [id: layout-components.col-inside-row; testability: auto] - **Row** MUST set isNested to true when placed inside a Col. Rationale: Nested rows need adjusted negative margins to compensate for the parent Col's padding. [id: layout-components.nested-row-uses-is-nested; testability: auto] ## Accessibility ### In brief The layout components shape the page and say nothing about it. `Cluster` is a list, and every other one is a plain `div`, so a screen reader announces the content and none of the layout. ### What Nebula provides - None of the layout components change the order of their children, so the reading order is the order in the code. - `Stack`, `ContentGrid` and `Row` wrap their children when the container is narrow, and `ContentGrid` never makes a column wider than its container. - `Cluster` renders a `ul` and `ClusterItem` an `li`. ### Accessible design #### Keep the visual order and the reading order the same The components lay children out in the order of the code, and a keyboard or screen reader user moves through the page in that order. Design each layout so that reading it left to right and top to bottom, at every width, gives the same order. WCAG: [1.3.2 Meaningful Sequence](https://www.w3.org/WAI/WCAG22/Understanding/meaningful-sequence.html) [2.4.3 Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) #### Check each layout at 320 pixels wide A layout that holds only at a desktop width forces a low-vision user who zooms in to scroll sideways as well as down. `Stack` and `ContentStack` switch to a row by the width of their own container, not the screen. WCAG: [1.4.10 Reflow](https://www.w3.org/WAI/WCAG22/Understanding/reflow.html) ### Accessible development #### Always add the landmark elements yourself The layout components are `div` elements and add no landmarks, so a page built only from them has none. Put `main`, `nav` and the other landmark elements around them, keep to one `main` per page and label a landmark that appears more than once. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) [2.4.1 Bypass Blocks](https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html) #### Pass only `ClusterItem` as children of `Cluster` `Cluster` is a `ul`, and a list only holds `li` elements. Anything else inside it breaks the list for a screen reader. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) #### Don't reorder children with CSS Setting `order` or a reverse flex direction on a child moves it on screen but not in the reading order or the tab order. WCAG: [1.3.2 Meaningful Sequence](https://www.w3.org/WAI/WCAG22/Understanding/meaningful-sequence.html) [2.4.3 Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) ### Accessibility testing status Reviewed on 9 October 2026 against v17.0.1. | Test | Status | Tested with | | --- | --- | --- | | Default state | Tested | axe-core via the Storybook test runner (CI) | | Advanced states | Partially tested | axe-core via the Storybook test runner (CI) | | Keyboard navigation | Manually tested | keyboard (reported by the owner) | | Screen reader | Partially tested | VoiceOver with Chrome (reported by the owner) | What each test covers: - Default state: The component as it first appears, before anything is clicked or changed. - Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering. - Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring. - Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state. What these statuses mean: - Tested: Every check in this row passed in an automated run that repeats on every pull request. - Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change. - Partially tested: Some checks in this row were run once and passed; the rest are still to do. - Issue found: A check in this row failed. The ticket is shown beside the status. - Not tested: No check in this row has been run yet. - Not applicable: This row does not apply to the component, for the reason given beside it. The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used. ## Properties ### Stack Also accepts all props from `HTMLAttributes`. - `spaceBetween` (type `SpaceValue`, required). Gap between children, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `horizontalAbove` (type `string`, optional). CSS width (for example `750px`) of the stack's own container above which children sit side by side in a row. Below it they stack vertically. Without it the stack is always vertical. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### ContentStack - `horizontal` (type `boolean`, optional). Lay children out side by side when the container is wider than `750px`, stacking them below that. Spacing is always `betweenCards`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Margin Also accepts all props from `HTMLAttributes`. - `bottom` (type `SpaceValue`, optional). Margin below the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `top` (type `SpaceValue`, optional). Margin above the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `left` (type `SpaceValue`, optional). Margin to the left of the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `right` (type `SpaceValue`, optional). Margin to the right of the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `inline` (type `boolean`, optional). Renders as `display: inline-block` instead of a block-level element. - `horizontal` (type `SpaceValue`, optional). Margin on both the left and right, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `vertical` (type `SpaceValue`, optional). Margin on both the top and bottom, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `all` (type `SpaceValue`, optional). Margin on all four sides, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). Takes precedence over the other margin props. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Bleed Also accepts all props from `HTMLAttributes`. - `bottom` (type `SpaceValue`, optional). Negative margin below the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `top` (type `SpaceValue`, optional). Negative margin above the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `left` (type `SpaceValue`, optional). Negative margin to the left of the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `right` (type `SpaceValue`, optional). Negative margin to the right of the content, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `inline` (type `boolean`, optional). Renders as `display: inline-block` instead of a block-level element. - `horizontal` (type `SpaceValue`, optional). Negative margin on both the left and right, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `vertical` (type `SpaceValue`, optional). Negative margin on both the top and bottom, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). - `all` (type `SpaceValue`, optional). Negative margin on all four sides, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). Takes precedence over the other margin props. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Cluster Also accepts all props from `HTMLAttributes`. - `justify` (type `CSSProperties['justifyContent']`, optional, default `'flex-start'`). Horizontal alignment of items within each row, as a CSS `justify-content` value. - `spaceBetween` (type `SpaceValue`, required). Gap between items, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). Applies to both rows and columns unless `rowGap` is set. - `rowGap` (type `SpaceValue`, optional). Gap between wrapped rows, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). Overrides the row part of `spaceBetween`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### ClusterItem Also accepts all props from `HTMLAttributes`, `LiHTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### ContentGrid Also accepts all props from `HTMLAttributes`. - `min` (type `string`, required). Minimum width of each column as a CSS length (for example `20rem`). As many equal columns as fit are created, and a column never exceeds the container's width. - `spaceBetween` (type `SpaceValue`, required). Gap between grid items, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). Applies to both rows and columns unless `rowGap` is set. - `rowGap` (type `SpaceValue`, optional). Gap between rows, as a spacing key (`1`-`12` or a semantic key such as `betweenCards`) or an array of keys for responsive values (base, then `small`, `medium`, `large` breakpoints and up). Overrides the row part of `spaceBetween`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Row Also accepts all props from `HTMLAttributes`. - `isNested` (type `boolean`, optional, default `false`). Set when the row sits inside a `Col`. Removes the fixed width and applies negative side margins to offset the parent column's padding. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Col Also accepts all props from `HTMLAttributes`. - `span` (type `number`, optional, default `12`). Number of columns (out of 12) the column spans below the `small` breakpoint, and at larger sizes unless `small`, `medium` or `large` override it. - `large` (type `number`, optional). Number of columns (out of 12) to span from the `large` breakpoint up. Falls back to the next smaller value that is set. - `small` (type `number`, optional). Number of columns (out of 12) to span from the `small` breakpoint up. Falls back to `span` if not set. - `medium` (type `number`, optional). Number of columns (out of 12) to span from the `medium` breakpoint up. Falls back to the next smaller value that is set. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ## Styles Reference only; you do not need this to use the component. This is the CSS its styled elements use, with theme tokens shown by name. `${...}` is an interpolation (a theme token, a mixin or another value from the code). A `&[prop]` or `@when ` block holds CSS that applies only under that condition; these are notation, not valid CSS. ### stack ```css &[horizontalAbove] { display: flex; flex-wrap: wrap; ${useResponsiveStyle('gap', useSpaceValue(spaceBetween))} > * { flex-grow: 1; flex-basis: calc((${horizontalAbove} - 100%) * 999); } } @when !horizontalAbove { > * + * { ${useResponsiveStyle('margin-top', useSpaceValue(spaceBetween))} } } ``` ### margin ```css &[horizontal] { ${useResponsiveStyle('margin-left', useSpaceValue(horizontal))} ${useResponsiveStyle('margin-right', useSpaceValue(horizontal))} } &[vertical] { ${useResponsiveStyle('margin-top', useSpaceValue(vertical))} ${useResponsiveStyle('margin-bottom', useSpaceValue(vertical))} } &[top] { ${useResponsiveStyle('margin-top', useSpaceValue(top))} } &[bottom] { ${useResponsiveStyle('margin-bottom', useSpaceValue(bottom))} } &[left] { ${useResponsiveStyle('margin-left', useSpaceValue(left))} } &[right] { ${useResponsiveStyle('margin-right', useSpaceValue(right))} } &[all] { ${useResponsiveStyle('margin', useSpaceValue(all))} } &[inline] { display: inline-block; } ``` ### bleed ```css &[horizontal] { ${useResponsiveStyle( 'margin-left', useSpaceValue(horizontal, formatNegative), )} ${useResponsiveStyle( 'margin-right', useSpaceValue(horizontal, formatNegative), )} } &[vertical] { ${useResponsiveStyle( 'margin-top', useSpaceValue(vertical, formatNegative), )} ${useResponsiveStyle( 'margin-bottom', useSpaceValue(vertical, formatNegative), )} } &[top] { ${useResponsiveStyle('margin-top', useSpaceValue(top, formatNegative))} } &[bottom] { ${useResponsiveStyle( 'margin-bottom', useSpaceValue(bottom, formatNegative), )} } &[left] { ${useResponsiveStyle('margin-left', useSpaceValue(left, formatNegative))} } &[right] { ${useResponsiveStyle('margin-right', useSpaceValue(right, formatNegative))} } &[all] { ${useResponsiveStyle('margin', useSpaceValue(all, formatNegative))} } &[inline] { display: inline-block; } ``` ### cluster ```css display: flex; flex-wrap: wrap; justify-content: ${justify}; align-items: center; ${useResponsiveStyle('gap', useSpaceValue(spaceBetween))} ${useResponsiveStyle('row-gap', useSpaceValue(rowGap))} ``` ### col ```css flex: 0 0 auto; flex-basis: ${(span / 12) * 100}%; max-width: ${(span / 12) * 100}%; box-sizing: border-box; padding-left: ${halfGutter[0]}; padding-right: ${halfGutter[0]}; margin-bottom: ${space[4]}; width: 100%; ${mediaQueries.smallAndUp} { padding-left: ${halfGutter[1]}; padding-right: ${halfGutter[1]}; margin-bottom: ${space[6]}; @when smallPercentage { flex-basis: ${small ? (small / 12) * 100 : null}%; max-width: ${small ? (small / 12) * 100 : null}%; } } @when mediumPercentage { ${mediaQueries.mediumAndUp} { flex-basis: ${medium ? (medium / 12) * 100 : null}%; max-width: ${medium ? (medium / 12) * 100 : null}%; } } @when largePercentage { ${mediaQueries.largeAndUp} { flex-basis: ${large ? (large / 12) * 100 : null}%; max-width: ${large ? (large / 12) * 100 : null}%; } } ``` ### content-grid ```css display: grid; ${useResponsiveStyle('gap', useSpaceValue(spaceBetween))} ${useResponsiveStyle('row-gap', useSpaceValue(rowGap))} @supports (width: min(${min}, 100%)) { grid-template-columns: repeat(auto-fit, minmax(min(${min}, 100%), 1fr)); } ``` ### row ```css display: flex; box-sizing: border-box; flex: 0 1 auto; flex-direction: row; flex-wrap: wrap; padding-left: ${halfGutter[0]}; padding-right: ${halfGutter[0]}; margin: 0 auto; width: ${breakpoints.large}; max-width: 100%; margin-bottom: -${space[4]}; & + & { margin-top: ${space[4]}; } &[isNested] { margin-left: -${space[4]}; margin-right: -${space[4]}; width: auto; max-width: none; } ${mediaQueries.smallAndUp} { padding-left: ${halfGutter[1]}; padding-right: ${halfGutter[1]}; margin-bottom: -${space[8]}; & + & { margin-top: ${space[8]}; } &[isNested] { margin-left: -${space[8]}; margin-right: -${space[8]}; } } ```