Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions docs/guides/layout-spacing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: Layout Spacing
category: Guides
order: 7
relevantForAI: true
---

## Layout Spacing

```js
---
type: embed
---
<Alert variant="warning" margin="0 0 medium">
The spacing tokens documented on this page (such as <code>general.spaceMd</code>) require <strong>v11.7+</strong> components and are applied through the <code>margin</code> and <code>padding</code> props. If you are viewing the v11.6 version, <Link href={window.location.pathname.match(/v\d+_\d+/) ? window.location.pathname.replace(/v\d+_\d+/, 'v11_7') : `/v11_7${window.location.pathname}`}>switch to v11.7</Link> to see the examples working correctly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are viewing the v11.6 version, switch to v11.7 to see the examples working correctly.

no need for this text, it always shows 11.7

</Alert>
```

Our design system provides a set of spacing tokens for consistent layouts and components. The current tokens are organized into a general t-shirt scale plus a handful of semantic tokens. Some tokens share a value but carry different meaning — prefer the semantically correct token for the context (e.g. use `gap.buttons` for spacing between buttons).

The `margin` and `padding` props on InstUI components accept these tokens via **dot-path notation** (for example `margin="general.spaceMd"` or `padding="padding.card.lg"`), and support the familiar CSS-like 1–4 value shorthand.

## Tokens

### General scale

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add that the actual values are theme-dependent


| Key | Value | Value in pixels |
| ----------------- | -------- | --------------- |
| general.spaceNone | 0rem | 0px |
| general.space2xs | 0.125rem | 2px |
| general.spaceXs | 0.25rem | 4px |
| general.spaceSm | 0.5rem | 8px |
| general.spaceMd | 0.75rem | 12px |
| general.spaceLg | 1rem | 16px |
| general.spaceXl | 1.5rem | 24px |
| general.space2xl | 2rem | 32px |

### Semantic tokens

| Key | Value | Value in pixels |
| ----------------------------- | ------- | --------------- |
| gap.sections | 3rem | 48px |
| gap.buttons | 0.75rem | 12px |
| gap.cards.sm | 0.75rem | 12px |
| gap.cards.md | 1rem | 16px |
| gap.cards.lg | 1.5rem | 24px |
| gap.cards.nestedContainers.sm | 0.5rem | 8px |
| gap.cards.nestedContainers.md | 0.75rem | 12px |
| gap.cards.nestedContainers.lg | 1rem | 16px |
| gap.inputs.horizontal | 0.75rem | 12px |
| gap.inputs.vertical | 1rem | 16px |
| padding.card.sm | 0.5rem | 8px |
| padding.card.md | 0.75rem | 12px |
| padding.card.lg | 1rem | 16px |

## Applying spacing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also mention which part of our themes contain these tokens (is it in sharedTokens?)


### Using the `margin` prop

Most components support a `margin` prop that works like the CSS `margin` property. Pass a single token or use the 1–4 value shorthand to fine-tune individual edges.

```js
---
type: example
---
<View as="div" display="block" borderWidth="small" padding="general.spaceSm">
<Button margin="0 general.spaceSm 0 0">Button 1</Button>
<Button>Button 2</Button>
</View>
```

### Using the `padding` prop

Components that render their own surface accept a `padding` prop, which resolves the same tokens. Semantic `padding.card.*` tokens are a good fit for card-like containers.

```js
---
type: example
---
<View as="div" display="block" borderWidth="small" padding="padding.card.lg">
This container uses <code>padding.card.lg</code>.
</View>
```

## Deprecated tokens

For compatibility reasons we still resolve two earlier generations of spacing tokens at runtime, but they should **not** be used when creating new layouts — prefer the current tokens above.

### Phased-out tokens

The flat `space0`–`space60` scale and the flat semantic tokens (`sections`, `buttons`, `paddingCardLarge`, `selects`, `tags`, etc.) were an interim set and have been superseded by the current general scale and dot-path semantic tokens.

### Legacy tokens

The original legacy keywords (`xxxSmall`, `small`, `medium`, `large`, `xxLarge`, etc.) remain so old layouts don't break when updating InstUI.
45 changes: 44 additions & 1 deletion packages/emotion/src/styleUtils/ThemeablePropValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,18 @@ const ThemeablePropValues = {

// SPACING
type OldSpacingKeys = keyof typeof ThemeablePropValues.SPACING
/**
* @deprecated Era-1 (legacy) spacing keywords. Still resolved at runtime, but
* prefer the current `CurrentSpacingValues` dot-path tokens (e.g. `general.spaceMd`).
* See https://instructure.design/layout-spacing.
*/
type OldSpacingValues = (typeof ThemeablePropValues.SPACING)[OldSpacingKeys]
/**
* @deprecated Era-2 spacing tokens (the `space0`–`space60` and flat semantic set)
* have been phased out. Still resolved at runtime, but prefer the current
* `CurrentSpacingValues` dot-path tokens (e.g. `general.spaceMd`, `gap.cards.md`).
* See https://instructure.design/layout-spacing.
*/
type NewSpacingValues =
| 'space0'
| 'space2'
Expand Down Expand Up @@ -120,7 +131,38 @@ type NewSpacingValues =
| 'tags'
| 'statusIndicators'
| 'dataPoints'
type SpacingValues = OldSpacingValues | NewSpacingValues
/**
* Current (era-3) spacing tokens. Referenced via dot-path notation and resolved
* against the new theme's `sharedTokens.spacing` map (see
* `@instructure/ui-themes` `newThemeTokens`). These are the recommended values
* for the `margin`/`padding` props. See https://instructure.design/layout-spacing.
*/
type CurrentSpacingValues =
// general t-shirt scale
| 'general.spaceNone'
| 'general.space2xs'
| 'general.spaceXs'
| 'general.spaceSm'
| 'general.spaceMd'
| 'general.spaceLg'
| 'general.spaceXl'
| 'general.space2xl'
// semantic gap tokens
| 'gap.sections'
| 'gap.buttons'
| 'gap.cards.sm'
| 'gap.cards.md'
| 'gap.cards.lg'
| 'gap.cards.nestedContainers.sm'
| 'gap.cards.nestedContainers.md'
| 'gap.cards.nestedContainers.lg'
| 'gap.inputs.horizontal'
| 'gap.inputs.vertical'
// semantic padding tokens
| 'padding.card.sm'
| 'padding.card.md'
| 'padding.card.lg'
type SpacingValues = CurrentSpacingValues | OldSpacingValues | NewSpacingValues
// adding `(string & {})` allows to use any string while still allowing specific string values to be suggested by IDEs
type Spacing = SpacingValues | (string & {})

Expand Down Expand Up @@ -152,6 +194,7 @@ export default ThemeablePropValues
export { ThemeablePropValues }
export type {
SpacingValues,
CurrentSpacingValues,
Spacing,
Shadow,
Stacking,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,70 @@ describe('calcSpacingFromShorthand', () => {
consoleWarnSpy.mockRestore()
})
})

// Era-3 (current) tokens are referenced via dot-path notation and resolved
// against the new theme's nested `sharedTokens.spacing` map. This block mirrors
// that shape to verify the real consumer syntax (e.g. `margin="general.spaceMd"`).
describe('era-3 (current) spacing tokens', () => {
const eraThreeMap = {
general: {
spaceNone: '0rem',
space2xs: '0.125rem',
spaceXs: '0.25rem',
spaceSm: '0.5rem',
spaceMd: '0.75rem',
spaceLg: '1rem',
spaceXl: '1.5rem',
space2xl: '2rem'
},
gap: {
sections: '3rem',
buttons: '0.75rem',
cards: {
sm: '0.75rem',
md: '1rem',
lg: '1.5rem',
nestedContainers: { sm: '0.5rem', md: '0.75rem', lg: '1rem' }
},
inputs: { horizontal: '0.75rem', vertical: '1rem' }
},
padding: {
card: { sm: '0.5rem', md: '0.75rem', lg: '1rem' }
}
}

it('resolves a general scale token', () => {
expect(calcSpacingFromShorthand('general.spaceMd', eraThreeMap)).toBe('0.75rem')
})

it('resolves general.spaceNone', () => {
expect(calcSpacingFromShorthand('general.spaceNone', eraThreeMap)).toBe('0rem')
})

it('resolves a deeply nested semantic gap token', () => {
expect(
calcSpacingFromShorthand('gap.cards.nestedContainers.md', eraThreeMap)
).toBe('0.75rem')
})

it('resolves a semantic padding token', () => {
expect(calcSpacingFromShorthand('padding.card.lg', eraThreeMap)).toBe('1rem')
})

it('handles CSS-like shorthand mixing the general scale with auto', () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})

expect(
calcSpacingFromShorthand('general.spaceLg auto general.spaceXl', eraThreeMap)
).toBe('1rem auto 1.5rem')

consoleWarnSpy.mockRestore()
})

it('mixes semantic gap and padding tokens', () => {
expect(
calcSpacingFromShorthand('gap.cards.sm padding.card.md', eraThreeMap)
).toBe('0.75rem 0.75rem')
})
})
})
22 changes: 11 additions & 11 deletions packages/ui-alerts/src/Alert/v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type: example
<Alert
variant="success"
renderCloseButtonLabel="Close"
margin="small"
margin="general.spaceMd"
transition="none"
variantScreenReaderLabel="Success, "
>
Expand All @@ -33,15 +33,15 @@ type: example
<Alert
variant="info"
renderCloseButtonLabel="Close"
margin="small"
margin="general.spaceMd"
variantScreenReaderLabel="Information, "
>
Sample info text. I will fade out if you close me.
</Alert>
<Alert
variant="error"
renderCloseButtonLabel="Close"
margin="small"
margin="general.spaceMd"
variantScreenReaderLabel="Error, "
>
Sample error text that continues for a while
Expand All @@ -51,7 +51,7 @@ type: example
</Alert>
<Alert
variant="warning"
margin="small"
margin="general.spaceMd"
variantScreenReaderLabel="Warning, "
>
Sample warning text. This alert is not dismissible and cannot be closed.
Expand All @@ -67,7 +67,7 @@ type: example
---
<Alert
variant="info"
margin="small"
margin="general.spaceMd"
timeout={5000}
variantScreenReaderLabel="Information, "
>
Expand Down Expand Up @@ -119,14 +119,14 @@ const Example = () => {
<Button onClick={addAlert}>Add Alert</Button>
{alerts.map((alert) => {
return (
<View key={alert.key} display="block" margin="small 0">
<View key={alert.key} display="block" margin="general.spaceMd 0">
<Alert
variant={alert.variant}
renderCloseButtonLabel="Close"
onDismiss={() => closeAlert(alert.key)}
liveRegion={() => document.getElementById('flash-messages')}
liveRegionPoliteness={alert.politeness}
margin="small 0"
margin="general.spaceMd 0"
>
This is {alert.politeness === 'polite' ? 'a' : 'an'}{' '}
{alert.politeness} {alert.variant} alert
Expand Down Expand Up @@ -164,7 +164,7 @@ const Example = () => {
return (
<div>
<Button onClick={changeMessage}>Change Message</Button>
<Button onClick={clearMessage} margin="0 0 0 small">
<Button onClick={clearMessage} margin="0 0 0 general.spaceMd">
Clear Message
</Button>
<Alert
Expand Down Expand Up @@ -194,13 +194,13 @@ type: example
padding="small medium"
borderWidth="small"
borderRadius="small"
margin="x-small 0"
margin="general.spaceSm 0"
>
{lorem.paragraph()}
</View>
<Alert
variant="info"
margin="x-small 0"
margin="general.spaceSm 0"
renderCloseButtonLabel="Close"
hasShadow={false}
>
Expand All @@ -212,7 +212,7 @@ type: example
padding="small medium"
borderWidth="small"
borderRadius="small"
margin="x-small 0"
margin="general.spaceSm 0"
>
{lorem.paragraph()}
</View>
Expand Down
6 changes: 3 additions & 3 deletions packages/ui-alerts/src/Alert/v2/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ type AlertOwnProps = {
*/
timeout?: number
/**
* Valid values are `0`, `none`, `auto`, `xxx-small`, `xx-small`, `x-small`,
* `small`, `medium`, `large`, `x-large`, `xx-large`. Apply these values via
* familiar CSS-like shorthand. For example: `margin="small auto large"`.
* Valid values are `0`, `none`, `auto`, and Spacing token values,
* see https://instructure.design/layout-spacing. Apply these values via
* familiar CSS-like shorthand. For example, `margin="general.spaceMd auto"`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In v2 Avatar, Flex, FormFieldLayout, Link still recommend the legacy margin="small auto large"

*/
margin?: Spacing
/**
Expand Down
2 changes: 1 addition & 1 deletion packages/ui-avatar/src/Avatar/v2/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ type AvatarOwnProps = {
/**
* Valid values are `0`, `none`, `auto`, and Spacing token values,
* see https://instructure.design/layout-spacing. Apply these values via
* familiar CSS-like shorthand. For example, `margin="small auto large"`.
* familiar CSS-like shorthand. For example, `margin="general.spaceMd auto"`.
*/
margin?: Spacing
/**
Expand Down
Loading
Loading