Skip to content

Commit 2332fd6

Browse files
committed
feat(many): introduce new spacing tokens; add margin prop for more components
1 parent 1ea3f33 commit 2332fd6

File tree

23 files changed

+453
-35
lines changed

23 files changed

+453
-35
lines changed

docs/contributor-docs/v10-upgrade-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Upgrade Guide for Version 10.0
33
category: Guides
4-
order: 7
4+
order: 98
55
---
66

77
# Upgrade Guide for Version 10

docs/guides/layout-spacing.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
title: Layout Spacing
3+
category: Guides
4+
order: 8
5+
---
6+
7+
# Layout Spacing
8+
9+
Our design system provides a set of spacing tokens for consistent layouts and components. Some tokens share values but should be used semantically. For instance, while both `tags` and `buttons` are 0.75rem, `buttons` should be used for spacing between buttons.
10+
11+
## Tokens
12+
13+
| Key | Value | Value in pixels |
14+
| ----------------- | -------- | --------------- |
15+
| space0 | 0rem | 0px |
16+
| space2 | 0.125rem | 2px |
17+
| space4 | 0.25rem | 4px |
18+
| space8 | 0.5rem | 8px |
19+
| space12 | 0.75rem | 12px |
20+
| space16 | 1rem | 16px |
21+
| space24 | 1.5rem | 24px |
22+
| space36 | 2.25rem | 36px |
23+
| space48 | 3rem | 48px |
24+
| space60 | 3.75rem | 60px |
25+
| sections | 2.25rem | 36px |
26+
| sectionElements | 1.5em | 24px |
27+
| trayElements | 1.5em | 24px |
28+
| modalElements | 1.5em | 24px |
29+
| moduleElements | 1em | 16px |
30+
| paddingCardLarge | 1.5rem | 24px |
31+
| paddingCardMedium | 1rem | 16px |
32+
| paddingCardSmall | 0.75rem | 12px |
33+
| selects | 1rem | 16px |
34+
| textAreas | 1rem | 16px |
35+
| inputFields | 1rem | 16px |
36+
| checkboxes | 1rem | 16px |
37+
| radios | 1rem | 16px |
38+
| toggles | 1rem | 16px |
39+
| buttons | 0.75rem | 12px |
40+
| tags | 0.75rem | 12px |
41+
| statusIndicators | 0.75rem | 12px |
42+
| dataPoints | 0.75rem | 12px |
43+
44+
## Applying Spacing
45+
46+
There are three main ways to apply spacing in our component library:
47+
48+
### 1. Using the `margin` Prop
49+
50+
Most components in the library support a `margin` prop that works similarly to the CSS margin property. You can specify a single value or fine-tune individual margins (e.g., top, right, bottom, left).
51+
52+
```ts
53+
---
54+
type: example
55+
---
56+
<div>
57+
<Button margin="0 buttons 0 0">Button 1</Button>
58+
<Button>Button 2</Button>
59+
</div>
60+
```
61+
62+
### 2. Using a Container Component with the `gap` Prop
63+
64+
For layouts, container components like `Flex` and `Grid` can be used with the gap prop to manage spacing between child elements.
65+
66+
```ts
67+
---
68+
type: example
69+
---
70+
<Flex gap="buttons">
71+
<Button>Button 1</Button>
72+
<Button>Button 2</Button>
73+
</Flex>
74+
```
75+
76+
### 3. Importing Values from the Theme
77+
78+
If you need to directly reference spacing values, you can import them from the theme. This approach is useful for applying spacing in inline styles or custom components.
79+
80+
```ts
81+
---
82+
type: code
83+
---
84+
// import the canvas theme
85+
import canvas from '@instructure/ui-themes'
86+
87+
// use spacing values
88+
<div style={{display: 'flex', gap: canvas.spacing.buttons}}>
89+
<button>Button 1</button>
90+
<button>Button 2</button>
91+
</div>
92+
```
93+
94+
## Legacy tokens
95+
96+
For compatibility reasons we still provide the legacy spacing tokens (`xxLarge`, `medium`, etc.) so old layouts don't break when updating InstUI but these tokens shouldn't be used when creating new layouts.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import React from 'react'
26+
import {
27+
Button,
28+
ColorPicker,
29+
TextInput,
30+
TextArea,
31+
NumberInput,
32+
DateInput2
33+
} from '@instructure/ui'
34+
35+
function MarginProp() {
36+
return (
37+
<div>
38+
<Button margin="space12">hello</Button>
39+
<TextInput margin="space12" />
40+
<TextArea margin="space12" label="label" />
41+
<NumberInput margin="space12" renderLabel="label" />
42+
<ColorPicker
43+
placeholderText="placeholder"
44+
label="label"
45+
margin="space12"
46+
/>
47+
<DateInput2
48+
margin="space12"
49+
renderLabel="label"
50+
screenReaderLabels={{
51+
calendarIcon: 'asdf',
52+
prevMonthButton: 'asdf',
53+
nextMonthButton: 'asdf'
54+
}}
55+
/>
56+
</div>
57+
)
58+
}
59+
60+
export default MarginProp
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
import React from 'react'
26+
import { Button } from '@instructure/ui'
27+
28+
function SpacingTokens() {
29+
const spaceTokens = [
30+
'space0',
31+
'space2',
32+
'space4',
33+
'space8',
34+
'space12',
35+
'space16',
36+
'space24',
37+
'space36',
38+
'space48',
39+
'space60',
40+
'sections',
41+
'sectionElrements',
42+
'trayElrements',
43+
'modalElrements',
44+
'moduleElrements',
45+
'paddingCardLarge',
46+
'paddingCardMedium',
47+
'paddingCardSmall',
48+
'selects',
49+
'textareas',
50+
'inputFields',
51+
'checkboxes',
52+
'radios',
53+
'toggles',
54+
'buttons',
55+
'tags',
56+
'statusIndicators',
57+
'dataPoints'
58+
]
59+
60+
return (
61+
<div>
62+
{spaceTokens.map((token) => (
63+
<Button margin={token} key={token}>
64+
{token}
65+
</Button>
66+
))}
67+
</div>
68+
)
69+
}
70+
71+
export default SpacingTokens

packages/__examples__/.storybook/stories/stories.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { renderPage } from './renderPage'
3636
import propJSONData from '../../prop-data.json'
3737
import TooltipPositioning from './TooltipPositioning'
3838
import FormErrors from './FormErrors'
39+
import SpacingTokens from './SpacingTokens'
3940
import SourceCodeEditorExamples from './SourceCodeEditorExamples'
4041

4142
type AdditionalExample = {
@@ -78,6 +79,15 @@ const additionalExamples: AdditionalExample[] = [
7879
}
7980
]
8081
},
82+
{
83+
title: 'Spacing tokens',
84+
stories: [
85+
{
86+
storyName: 'Spacing tokens',
87+
storyFn: () => SpacingTokens()
88+
}
89+
]
90+
},
8191
// TODO: try to fix the editor not rendering fully on chromatic screenshot,
8292
// even with delay
8393
{

packages/emotion/src/styleUtils/ThemeablePropValues.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,40 @@ const ThemeablePropValues = {
8989
} as const
9090

9191
// SPACING
92-
type SpacingKeys = keyof typeof ThemeablePropValues.SPACING
93-
type SpacingValues = (typeof ThemeablePropValues.SPACING)[SpacingKeys]
94-
type Spacing = CSSShorthandValue<SpacingValues>
92+
type OldSpacingKeys = keyof typeof ThemeablePropValues.SPACING
93+
type OldSpacingValues = (typeof ThemeablePropValues.SPACING)[OldSpacingKeys]
94+
type NewSpacingValues =
95+
| 'space0'
96+
| 'space2'
97+
| 'space4'
98+
| 'space8'
99+
| 'space12'
100+
| 'space16'
101+
| 'space24'
102+
| 'space36'
103+
| 'space48'
104+
| 'space60'
105+
| 'sections'
106+
| 'sectionElrements'
107+
| 'trayElrements'
108+
| 'modalElrements'
109+
| 'moduleElrements'
110+
| 'paddingCardLarge'
111+
| 'paddingCardMedium'
112+
| 'paddingCardSmall'
113+
| 'selects'
114+
| 'textareas'
115+
| 'inputFields'
116+
| 'checkboxes'
117+
| 'radios'
118+
| 'toggles'
119+
| 'buttons'
120+
| 'tags'
121+
| 'statusIndicators'
122+
| 'dataPoints'
123+
type SpacingValues = OldSpacingValues | NewSpacingValues
124+
// adding `(string & {})` allows to use any string while still allowing specific string values to be suggested by IDEs
125+
type Spacing = SpacingValues | (string & {})
95126

96127
// SHADOW_TYPES
97128
type ShadowKeys = keyof typeof ThemeablePropValues.SHADOW_TYPES

packages/ui-color-picker/src/ColorPicker/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,15 @@ class ColorPicker extends Component<ColorPickerProps, ColorPickerState> {
399399
}}
400400
>
401401
<Tooltip renderTip={<span aria-hidden={true}>{tooltip}</span>}>
402-
<IconButton withBackground={false} withBorder={false} screenReaderLabel={tooltip} size="small" shape="circle" width="auto" renderIcon={IconInfoLine}/>
402+
<IconButton
403+
withBackground={false}
404+
withBorder={false}
405+
screenReaderLabel={tooltip}
406+
size="small"
407+
shape="circle"
408+
width="auto"
409+
renderIcon={IconInfoLine}
410+
/>
403411
</Tooltip>
404412
</InstUISettingsProvider>
405413
</span>
@@ -602,7 +610,8 @@ class ColorPicker extends Component<ColorPickerProps, ColorPickerState> {
602610
)
603611

604612
render() {
605-
const { disabled, isRequired, placeholderText, width, id } = this.props
613+
const { disabled, isRequired, placeholderText, width, id, margin } =
614+
this.props
606615

607616
return (
608617
<div
@@ -627,6 +636,7 @@ class ColorPicker extends Component<ColorPickerProps, ColorPickerState> {
627636
onPaste={(event) => this.handleOnPaste(event)}
628637
onBlur={() => this.handleOnBlur()}
629638
messages={this.renderMessages()}
639+
margin={margin}
630640
/>
631641
{!this.isSimple && (
632642
<div

packages/ui-color-picker/src/ColorPicker/props.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ import React from 'react'
2626
import PropTypes from 'prop-types'
2727

2828
import type { FormMessage } from '@instructure/ui-form-field'
29-
import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
29+
import type {
30+
WithStyleProps,
31+
ComponentStyle,
32+
Spacing
33+
} from '@instructure/emotion'
3034
import type {
3135
ColorPickerTheme,
3236
OtherHTMLAttributes,
@@ -225,6 +229,11 @@ type ColorPickerOwnProps = {
225229
* If true, alpha slider will be rendered. Defaults to false
226230
*/
227231
withAlpha?: boolean
232+
233+
/**
234+
* Margin around the component. Accepts a `Spacing` token. See token values and example usage in [this guide](https://instructure.design/#layout-spacing).
235+
*/
236+
margin?: Spacing
228237
}
229238

230239
type ColorPickerState = {
@@ -316,7 +325,8 @@ const propTypes: PropValidators<PropKeys> = {
316325
id: PropTypes.string,
317326
value: PropTypes.string,
318327
width: PropTypes.string,
319-
withAlpha: PropTypes.bool
328+
withAlpha: PropTypes.bool,
329+
margin: PropTypes.string
320330
}
321331

322332
const allowedProps: AllowedPropKeys = [
@@ -339,7 +349,8 @@ const allowedProps: AllowedPropKeys = [
339349
'tooltip',
340350
'value',
341351
'width',
342-
'withAlpha'
352+
'withAlpha',
353+
'margin'
343354
]
344355

345356
export type {

0 commit comments

Comments
 (0)