UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

543 lines (470 loc) 13.7 kB
--- title: 'Space' description: 'The Space component provides margins within the provided spacing patterns.' version: 10.104.0 generatedAt: 2026-04-17T18:46:12.781Z checksum: 090b7d977ba4be5e2c4c04d199a30a4048416c59f443a56985df2f80629d9c40 --- # Space ## Import ```tsx import { Space } from '@dnb/eufemia' ``` ## Description The Space component provides `margins` within the [provided spacing patterns](/uilib/usage/layout/spacing#spacing-helpers). The reason this exists is to make your syntax as clean as possible. This way, you see directly in words what the spacing is for every affected component. ### Spacing Table | Pixel | Type | Rem | Custom Property | | ----- | ---------- | ------- | -------------------- | | 8 | `x-small` | **0.5** | `--spacing-x-small` | | 16 | `small` | **1** | `--spacing-small` | | 24 | `medium` | **1.5** | `--spacing-medium` | | 32 | `large` | **2** | `--spacing-large` | | 48 | `x-large` | **3** | `--spacing-x-large` | | 56 | `xx-large` | **3.5** | `--spacing-xx-large` | **NB:** In some circumstances you may be in need of using **0.25rem** (4px) - therefore `xx-small` also exists, but as a single type. So, combining `xx-small` and `small` would not result in 0.25rem, but still remain 1rem. ### Value Format There are a couple of different ways you can define the spacing types and values: - **Types:** `small small x-small` (combine types up to _10rem_) - **number:** `2.5` (equivalent to `rem`) - **string(rem):** `2.5rem` - **string(px):** `40px` (gets converted to `rem`) - **boolean:** `true` (equivalent to `small`), `false` (equivalent to `zero`) To get a spacing of e.g. **2.5rem** (40px), you may combine types `large` and `x-small`. ```tsx { /* All of these methods will result in the same spacing */ } ;<Space top="large x-small" right="2.5" bottom="2.5rem" left="40px" /> ``` With React, you can also use an object with the different directions: ```tsx { /* All of these methods will result in the same spacing */ } ;<Space space={{ top: 'large x-small', right: '2.5', bottom: '2.5rem', left: '40px', }} /> ``` ### Components and Spacing Every component supports the spacing patterns, so it's possible to send in the `top`, `right`, `bottom`, `left` and `space` properties directly, like: ```tsx <Button top="large x-small medium" /> <Button space={{ top: 'large x-small medium', }} /> ``` ### Spacing shorthands A shorthand for getting 1rem (most used) is to simply send in a boolean set as true. No given value in JSX means true, so you only need the property key: ```tsx { /* Equivalent to top="small" */ } ;<Button top /> { /* Equivalent to top="small" right="small" bottom="small" left="small" */ } ;<Button space /> ``` In order to set all four directions at once, you can provide a string as the `space` value: ```tsx render(<Button space="large x-small medium" />) ``` ### Does it not work as expected? Is `margin` not giving the expected spacing? That may be due to **Margin Collapsing**. Margins collapse in the following situations: - Adjacent siblings - Completely empty boxes - Parent and first or last child element The best solution is to only use one direction of margins, e.g. `bottom`. Or you can set the [collapse property](/uilib/layout/space/properties) to `false`. ### Margin collapsing In order to help handle unwanted margin collapsing in typography elements, see [this example](/uilib/elements/heading#example-of-margin-collapsing). ### Conditional Reset For resetting spacing (`margin: 0`) only when no spacing is defined, you can make use of `dnb-space__reset`. The following example will result in `margin: 0.5rem 0 0 0`: ```html <ul class="my-list dnb-space__reset dnb-space__top--small"> <!-- some content --> </ul> ``` More details: 1. Because of the browser default styles, our list has some margin. 1. If we would want to "reset" these styles to a margin of 0 directly on `.my-list`, we would not be able to use `dnb-space__top--small` because of the CSS specificity is lower. ## Demos ### Spacing method #1 `Space` component. The RedBox is only to visualize the result. ```tsx render( <TestStyles> <ComponentBox data-visual-test="spacing-method-space" scope={{ RedBox, }} > <RedBox> <Space top="large x-small"> <Input label="Input" /> </Space> </RedBox> </ComponentBox> </TestStyles> ) ``` ### Spacing method #2 Define the space directly. ```tsx render( <TestStyles> <ComponentBox data-visual-test="spacing-method-component"> <Input label="Input A" right="small" /> <Input label="Input B" /> </ComponentBox> </TestStyles> ) ``` ### Spacing method #3 Using `createSpacingClasses` or `createSpacingProperties`. ```tsx render( <TestStyles> <ComponentBox scope={{ RedBox, createSpacingClasses, createSpacingProperties, removeSpaceProps, classnames, }} data-visual-test="spacing-method-form-row" > {() => { const Component = ({ className = null, style = null, ...props }) => { const spacingClasses = createSpacingClasses(props) const spacingProperties = createSpacingProperties(props) const cn = classnames( 'my-component', 'dnb-space', spacingClasses, className ) const st = { ...style, ...spacingProperties, } return ( <div className={cn} style={st} {...removeSpaceProps(props)} /> ) } return ( <> <RedBox> <Component top="small medium large">Space A</Component> </RedBox> <RedBox> <Component top>Space B</Component> </RedBox> <RedBox> <Component innerSpace="large">Inner Space</Component> </RedBox> <RedBox> <Component innerSpace={{ large: true, }} > Has space when breakpoint is large </Component> </RedBox> </> ) }} </ComponentBox> </TestStyles> ) ``` ### Responsive `innerSpace` For "padding" inside the space component, you can use the property `innerSpace`. It supports the same API as the `space` property. But in addition it supports [media query breakpoints](/uilib/usage/layout/media-queries), such as `small`, `medium` and `large`. For `innerSpace` object values, you can also use `block` and `inline` shorthand. ```tsx render( <TestStyles> <ComponentBox data-visual-test="inner-spacing" scope={{ RedBox, }} > <RedBox> <Space innerSpace={{ small: 'large x-small', medium: true, large: { top: '2rem', left: '16px', bottom: 'large', right: '5rem', }, }} > <P>Content</P> </Space> </RedBox> </ComponentBox> </TestStyles> ) ``` ### Spacing with no margin collapse, due to the flex usage ```tsx render( <TestStyles> <ComponentBox hideCode scope={{ RedBox, Vertical, }} > <Vertical> <RedBox> <Space bottom="small"> <> I have <code className="dnb-code">bottom="small"</code> </> </Space> </RedBox> <RedBox> <Space top="large"> <> I have <code className="dnb-code">top="large"</code> </> </Space> </RedBox> </Vertical> </ComponentBox> </TestStyles> ) ``` ### All four values will result in an equivalent margin ```tsx render( <TestStyles> <ComponentBox data-visual-test="spacing-margins" hideCode> <Space top="large x-small" right="2.5" bottom="2.5rem" left="40px"> <details> <summary> I have four <code className="dnb-code">2.5rem</code> margins! </summary> And this are my CSS classes: <code className="dnb-code">dnb-space dnb-space__top--large dnb-space__top--x-small dnb-space__right--large dnb-space__right--x-small dnb-space__bottom--large dnb-space__bottom--x-small dnb-space__left--large dnb-space__left--x-small</code> </details> </Space> </ComponentBox> </TestStyles> ) ``` ### Visual space testing ```tsx render( <TestStyles> <ComponentBox data-visual-test="spacing-patterns" scope={{ MagicBox, CustomStyle, }} hideCode > {() => { const TestCase = (props) => { return ( <CustomStyle {...props}> {listOfBoxes.map((v) => ( <Space key={v} top={v}> <MagicBox /> </Space> ))} </CustomStyle> ) } const listOfBoxes = [] for (let i = 0, c = 0, l = 20; i <= l; i++) { listOfBoxes.push(String(c)) c += 0.5 } return ( <div className="spacing-patterns"> <P bottom> With <Code>dnb-core-style</Code> </P> <TestCase className="dnb-core-style" /> <P top bottom> Without </P> <TestCase /> </div> ) }} </ComponentBox> </TestStyles> ) ``` ```tsx render( <TestStyles> <ComponentBox data-visual-test="spacing-elements" scope={{ MagicBox, CustomStyle, }} hideCode > {() => { const listOfBoxes = [] for (let i = 0, c = 0, l = 10; i <= l; i++) { listOfBoxes.push(String(c)) c += 1 } const TestCase = (props) => { return ( <CustomStyle {...props}> {listOfBoxes.map((v) => ( <Button key={v} left="x-small" top={v} size="small" custom_content={<MagicBox />} /> ))} </CustomStyle> ) } return ( <div className="spacing-elements"> <P bottom> With <Code>dnb-core-style</Code> </P> <TestCase className="dnb-core-style" /> <P top bottom> Without </P> <TestCase /> </div> ) }} </ComponentBox> </TestStyles> ) ``` ```tsx const BlueBox = styled.div` display: inline-block; padding: 0.5rem; background: blue; ul { background: white; } ` render( <BlueBox> <ul className="dnb-space__reset dnb-space__top--small dnb-space__right--small dnb-space__bottom--small dnb-space__left--small"> <li> </li> </ul> </BlueBox> ) ``` ## Global Properties These properties are available in many other components and elements. ```json { "props": { "space": { "doc": "Has to be an object with either: `top`, `right`, `bottom` or `left`. Use spacing values like: `small`, `1rem`, `1` or , `16px`.", "type": ["object"], "status": "optional" }, "top": { "doc": "Use spacing values like: `small`, `1rem`, `1` or , `16px`. Will use `margin-top`.", "type": ["string", "number", "boolean"], "status": "optional" }, "right": { "doc": "Use spacing values like: `small`, `1rem`, `1` or , `16px`. will use `margin-right`.", "type": ["string", "number", "boolean"], "status": "optional" }, "bottom": { "doc": "Use spacing values like: `small`, `1rem`, `1` or , `16px`. will use `margin-bottom`.", "type": ["string", "number", "boolean"], "status": "optional" }, "left": { "doc": "Use spacing values like: `small`, `1rem`, `1` or , `16px`. will use `margin-left`.", "type": ["string", "number", "boolean"], "status": "optional" } } } ``` ## Component Properties ```json { "props": { "element": { "doc": "Defines the HTML element used. Defaults to `div`.", "type": "React.Element", "status": "optional" }, "stretch": { "doc": "If set to `true`, then the space element will be 100% in width.", "type": "boolean", "status": "optional" }, "inline": { "doc": "If set to `true`, then `display: inline-block;` is used, so the HTML elements get aligned horizontally. Defaults to `false`.", "type": "boolean", "status": "optional" }, "innerSpace": { "doc": "Will add a padding around the content. Supports also media query breakpoints like `{small: { top: 'medium' }}` and shorthand directions `inline`/`block`.", "type": ["object", "string", "number", "boolean"], "status": "optional" }, "no_collapse": { "doc": "If set to `true`, then a wrapper with `display: flow-root;` is used. This way you avoid **Margin Collapsing**. Defaults to `false`. _Note:_ You can't use `inline={true}` in combination.", "type": "boolean", "status": "optional" } } } ``` ## Zero Use either `0` or `false` (as a number/boolean or string) to set a `margin` of 0. ## Provider Also, Provider is supporting the `collapse` property. ```tsx render( <Provider space={{ no_collapse: true, }} > <Space>I do not collapse</Space> <Space>I do not collapse</Space> </Provider> ) ```