Skip to content

Commit 048c902

Browse files
committed
feat(many): introduce new spacing tokens; add margin prop for more components
1 parent 87dc52d commit 048c902

File tree

30 files changed

+548
-75
lines changed

30 files changed

+548
-75
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.

packages/__docs__/src/ComponentTheme/index.tsx

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ import { withStyle, jsx } from '@instructure/emotion'
2929
import { Table } from '@instructure/ui-table'
3030
import { View } from '@instructure/ui-view'
3131

32-
import type { BaseColors } from '@instructure/shared-types'
33-
3432
import { ColorSwatch } from '../ColorSwatch'
3533

3634
import generateStyle from './styles'
@@ -42,51 +40,49 @@ class ComponentTheme extends Component<ComponentThemeProps> {
4240
static propTypes = propTypes
4341
static allowedProps = allowedProps
4442

45-
mapColors(colorKey: BaseColors) {
46-
const map: Record<string, string> = {}
47-
;(Object.keys(colorKey) as Array<keyof BaseColors>).forEach((color) => {
48-
const hex = colorKey[color]
49-
if (typeof map[hex] === 'undefined') {
50-
map[hex] = color
51-
}
52-
})
53-
return map
43+
renderValueCell(
44+
value: undefined | string | object | number,
45+
colorPrimitives: object
46+
) {
47+
if (!value) {
48+
return <code>$aposundefined$apos</code>
49+
}
50+
if (typeof value === 'object') {
51+
return <code>{JSON.stringify(value)}</code>
52+
}
53+
if (typeof value === 'object') {
54+
return <code>{JSON.stringify(value)}</code>
55+
}
56+
if (
57+
value.toString().charAt(0) === '#' ||
58+
value.toString().substring(0, 3) === 'rgb'
59+
) {
60+
// find color primitive name from hex value
61+
const color = Object.entries(colorPrimitives).find(([, v]) => v === value)
62+
return (
63+
<span>
64+
<View margin="0 xx-small 0 0">
65+
<ColorSwatch color={value} />
66+
</View>
67+
<code>{color?.[0] ?? value}</code>
68+
</span>
69+
)
70+
}
71+
return <code>{value}</code>
5472
}
5573

5674
renderRows() {
5775
const { componentTheme, themeVariables } = this.props
58-
const colorKey = themeVariables.colors.values
59-
? themeVariables.colors.values
60-
: themeVariables.colors
61-
const map = this.mapColors(colorKey)
76+
const colorPrimitives = themeVariables.colors.primitives
6277

6378
return Object.keys(componentTheme).map((name) => {
64-
const value = componentTheme[name] || 'undefined'
65-
const color = value.toString().charAt(0) === '#' ? map[value] : null
66-
6779
return (
6880
<Table.Row key={name}>
6981
<Table.Cell>
7082
<code>{name}</code>
7183
</Table.Cell>
7284
<Table.Cell>
73-
{value.toString().charAt(0) === '#' ? (
74-
<span>
75-
<View margin="0 xx-small 0 0">
76-
<ColorSwatch color={value} />
77-
</View>
78-
<code>{color}</code>
79-
</span>
80-
) : value.toString().substring(0, 3) === 'rgb' ? (
81-
<span>
82-
<View margin="0 xx-small 0 0">
83-
<ColorSwatch color={value} />
84-
</View>
85-
<code>{value}</code>
86-
</span>
87-
) : (
88-
<code>{value}</code>
89-
)}
85+
{this.renderValueCell(componentTheme[name], colorPrimitives)}
9086
</Table.Cell>
9187
</Table.Row>
9288
)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
const exampleMargin = 'space36 21px 0 buttons'
36+
37+
function MarginProp() {
38+
return (
39+
<div>
40+
<Button margin={exampleMargin}>hello</Button>
41+
<TextInput margin={exampleMargin} />
42+
<TextArea margin={exampleMargin} label="label" />
43+
<NumberInput margin={exampleMargin} renderLabel="label" />
44+
<ColorPicker
45+
placeholderText="placeholder"
46+
label="label"
47+
margin={exampleMargin}
48+
/>
49+
<DateInput2
50+
margin={exampleMargin}
51+
renderLabel="label"
52+
screenReaderLabels={{
53+
calendarIcon: 'asdf',
54+
prevMonthButton: 'asdf',
55+
nextMonthButton: 'asdf'
56+
}}
57+
/>
58+
</div>
59+
)
60+
}
61+
62+
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: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ 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'
40+
import MarginProp from './MarginProp'
3941
import SourceCodeEditorExamples from './SourceCodeEditorExamples'
4042

4143
type AdditionalExample = {
@@ -78,6 +80,24 @@ const additionalExamples: AdditionalExample[] = [
7880
}
7981
]
8082
},
83+
{
84+
title: 'Spacing tokens',
85+
stories: [
86+
{
87+
storyName: 'Spacing tokens',
88+
storyFn: () => SpacingTokens()
89+
}
90+
]
91+
},
92+
{
93+
title: 'Margin prop',
94+
stories: [
95+
{
96+
storyName: 'Margin prop',
97+
storyFn: () => MarginProp()
98+
}
99+
]
100+
},
81101
// TODO: try to fix the editor not rendering fully on chromatic screenshot,
82102
// even with delay
83103
{

packages/emotion/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export {
3333
makeThemeVars,
3434
getShorthandPropValue,
3535
mirrorShorthandCorners,
36-
mirrorShorthandEdges
36+
mirrorShorthandEdges,
37+
mapSpacingToShorthand
3738
} from './styleUtils'
3839

3940
export type { ComponentStyle, StyleObject, Overrides } from './EmotionTypes'

0 commit comments

Comments
 (0)