@discourser/design-system 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/dist/styles.css +5126 -0
- package/guidelines/Guidelines.md +92 -41
- package/guidelines/components/accordion.md +732 -0
- package/guidelines/components/avatar.md +1015 -0
- package/guidelines/components/badge.md +728 -0
- package/guidelines/components/button.md +75 -40
- package/guidelines/components/card.md +84 -25
- package/guidelines/components/checkbox.md +671 -0
- package/guidelines/components/dialog.md +619 -31
- package/guidelines/components/drawer.md +1616 -0
- package/guidelines/components/heading.md +576 -0
- package/guidelines/components/icon-button.md +92 -37
- package/guidelines/components/input-addon.md +685 -0
- package/guidelines/components/input-group.md +830 -0
- package/guidelines/components/input.md +92 -37
- package/guidelines/components/popover.md +1271 -0
- package/guidelines/components/progress.md +836 -0
- package/guidelines/components/radio-group.md +852 -0
- package/guidelines/components/select.md +1662 -0
- package/guidelines/components/skeleton.md +802 -0
- package/guidelines/components/slider.md +911 -0
- package/guidelines/components/spinner.md +783 -0
- package/guidelines/components/switch.md +105 -38
- package/guidelines/components/tabs.md +1488 -0
- package/guidelines/components/textarea.md +495 -0
- package/guidelines/components/toast.md +784 -0
- package/guidelines/components/tooltip.md +912 -0
- package/guidelines/design-tokens/colors.md +309 -72
- package/guidelines/design-tokens/elevation.md +615 -45
- package/guidelines/design-tokens/spacing.md +654 -74
- package/guidelines/design-tokens/typography.md +432 -50
- package/guidelines/overview-components.md +60 -8
- package/guidelines/overview-imports.md +314 -0
- package/guidelines/overview-patterns.md +3852 -0
- package/package.json +4 -2
|
@@ -4,22 +4,24 @@ The design system uses a consistent spacing scale based on an 8px grid system fo
|
|
|
4
4
|
|
|
5
5
|
## Spacing Scale
|
|
6
6
|
|
|
7
|
-
| Token
|
|
8
|
-
|
|
9
|
-
| `none` | 0px
|
|
10
|
-
| `xxs`
|
|
11
|
-
| `xs`
|
|
12
|
-
| `sm`
|
|
13
|
-
| `md`
|
|
14
|
-
| `lg`
|
|
15
|
-
| `xl`
|
|
16
|
-
| `xxl`
|
|
17
|
-
| `xxxl` | 64px
|
|
7
|
+
| Token | Value | Usage |
|
|
8
|
+
| ------ | ----- | ------------------------------------ |
|
|
9
|
+
| `none` | 0px | No spacing, reset margins/padding |
|
|
10
|
+
| `xxs` | 2px | Minimal gaps, icon spacing, borders |
|
|
11
|
+
| `xs` | 4px | Very tight spacing, inline elements |
|
|
12
|
+
| `sm` | 8px | Small gaps, compact layouts |
|
|
13
|
+
| `md` | 16px | Default spacing, most common use |
|
|
14
|
+
| `lg` | 24px | Larger sections, comfortable spacing |
|
|
15
|
+
| `xl` | 32px | Major sections, page margins |
|
|
16
|
+
| `xxl` | 48px | Large sections, page padding |
|
|
17
|
+
| `xxxl` | 64px | Maximum spacing, hero sections |
|
|
18
18
|
|
|
19
19
|
## Usage Principles
|
|
20
20
|
|
|
21
21
|
### The 8px Grid
|
|
22
|
+
|
|
22
23
|
All spacing values (except `xxs` and `xs`) are multiples of 8px. This creates:
|
|
24
|
+
|
|
23
25
|
- Visual rhythm and consistency
|
|
24
26
|
- Predictable alignment
|
|
25
27
|
- Easier mental math for designers and developers
|
|
@@ -28,59 +30,62 @@ All spacing values (except `xxs` and `xs`) are multiples of 8px. This creates:
|
|
|
28
30
|
### Common Patterns
|
|
29
31
|
|
|
30
32
|
#### Component Internal Spacing
|
|
33
|
+
|
|
31
34
|
```typescript
|
|
32
35
|
import { css } from '@discourser/design-system/styled-system/css';
|
|
33
36
|
|
|
34
37
|
// ✅ Button padding (handled by component)
|
|
35
38
|
const button = css({
|
|
36
|
-
px: 'md',
|
|
37
|
-
py: 'sm'
|
|
39
|
+
px: 'md', // 16px horizontal
|
|
40
|
+
py: 'sm', // 8px vertical
|
|
38
41
|
});
|
|
39
42
|
|
|
40
43
|
// ✅ Card padding
|
|
41
44
|
const card = css({
|
|
42
|
-
p: 'lg'
|
|
45
|
+
p: 'lg', // 24px all sides
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
// ✅ Input field
|
|
46
49
|
const input = css({
|
|
47
|
-
px: 'md',
|
|
48
|
-
py: 'sm'
|
|
50
|
+
px: 'md', // 16px horizontal
|
|
51
|
+
py: 'sm', // 8px vertical
|
|
49
52
|
});
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
#### Layout Spacing
|
|
56
|
+
|
|
53
57
|
```typescript
|
|
54
58
|
// ✅ Stack of elements
|
|
55
59
|
const stack = css({
|
|
56
60
|
display: 'flex',
|
|
57
61
|
flexDirection: 'column',
|
|
58
|
-
gap: 'md'
|
|
62
|
+
gap: 'md', // 16px between items
|
|
59
63
|
});
|
|
60
64
|
|
|
61
65
|
// ✅ Grid layout
|
|
62
66
|
const grid = css({
|
|
63
67
|
display: 'grid',
|
|
64
|
-
gap: 'lg'
|
|
68
|
+
gap: 'lg', // 24px between grid items
|
|
65
69
|
});
|
|
66
70
|
|
|
67
71
|
// ✅ Container with padding
|
|
68
72
|
const container = css({
|
|
69
|
-
px: { base: 'md', lg: 'xl' },
|
|
70
|
-
py: 'lg'
|
|
73
|
+
px: { base: 'md', lg: 'xl' }, // Responsive: 16px mobile, 32px desktop
|
|
74
|
+
py: 'lg', // 24px vertical
|
|
71
75
|
});
|
|
72
76
|
```
|
|
73
77
|
|
|
74
78
|
#### Margin Between Sections
|
|
79
|
+
|
|
75
80
|
```typescript
|
|
76
81
|
// ✅ Section spacing
|
|
77
82
|
const section = css({
|
|
78
|
-
mb: 'xxl'
|
|
83
|
+
mb: 'xxl', // 48px bottom margin
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
// ✅ Page-level spacing
|
|
82
87
|
const page = css({
|
|
83
|
-
p: { base: 'lg', lg: 'xxl' }
|
|
88
|
+
p: { base: 'lg', lg: 'xxl' }, // 24px mobile, 48px desktop
|
|
84
89
|
});
|
|
85
90
|
```
|
|
86
91
|
|
|
@@ -88,33 +93,33 @@ const page = css({
|
|
|
88
93
|
|
|
89
94
|
### Component Spacing
|
|
90
95
|
|
|
91
|
-
| Use Case
|
|
92
|
-
|
|
93
|
-
| Button padding (horizontal) | `md`
|
|
94
|
-
| Button padding (vertical)
|
|
95
|
-
| Input padding
|
|
96
|
-
| Card padding
|
|
97
|
-
| Dialog padding
|
|
98
|
-
| Icon margins
|
|
96
|
+
| Use Case | Token | Example |
|
|
97
|
+
| --------------------------- | ------------ | ------- |
|
|
98
|
+
| Button padding (horizontal) | `md` | 16px |
|
|
99
|
+
| Button padding (vertical) | `sm` | 8px |
|
|
100
|
+
| Input padding | `md` | 16px |
|
|
101
|
+
| Card padding | `lg` or `xl` | 24-32px |
|
|
102
|
+
| Dialog padding | `lg` or `xl` | 24-32px |
|
|
103
|
+
| Icon margins | `xs` or `sm` | 4-8px |
|
|
99
104
|
|
|
100
105
|
### Layout Spacing
|
|
101
106
|
|
|
102
|
-
| Use Case
|
|
103
|
-
|
|
104
|
-
| Gap between form fields | `md`
|
|
105
|
-
| Gap between cards
|
|
106
|
-
| Section margins
|
|
107
|
-
| Page margins
|
|
108
|
-
| Hero section padding
|
|
107
|
+
| Use Case | Token | Example |
|
|
108
|
+
| ----------------------- | ------------- | ------- |
|
|
109
|
+
| Gap between form fields | `md` | 16px |
|
|
110
|
+
| Gap between cards | `lg` | 24px |
|
|
111
|
+
| Section margins | `xl` or `xxl` | 32-48px |
|
|
112
|
+
| Page margins | `xl` or `xxl` | 32-48px |
|
|
113
|
+
| Hero section padding | `xxxl` | 64px |
|
|
109
114
|
|
|
110
115
|
### Content Spacing
|
|
111
116
|
|
|
112
|
-
| Use Case
|
|
113
|
-
|
|
114
|
-
| Paragraph margins | `md`
|
|
115
|
-
| List item spacing | `sm` or `md` | 8-16px
|
|
116
|
-
| Icon-text gap
|
|
117
|
-
| Button group gap
|
|
117
|
+
| Use Case | Token | Example |
|
|
118
|
+
| ----------------- | ------------ | ------- |
|
|
119
|
+
| Paragraph margins | `md` | 16px |
|
|
120
|
+
| List item spacing | `sm` or `md` | 8-16px |
|
|
121
|
+
| Icon-text gap | `xs` or `sm` | 4-8px |
|
|
122
|
+
| Button group gap | `sm` | 8px |
|
|
118
123
|
|
|
119
124
|
## Usage in Code
|
|
120
125
|
|
|
@@ -124,20 +129,20 @@ const page = css({
|
|
|
124
129
|
import { css } from '@discourser/design-system/styled-system/css';
|
|
125
130
|
|
|
126
131
|
// All sides
|
|
127
|
-
const box = css({ p: 'md' });
|
|
132
|
+
const box = css({ p: 'md' }); // 16px all sides
|
|
128
133
|
|
|
129
134
|
// Individual sides
|
|
130
135
|
const box = css({
|
|
131
|
-
pt: 'lg',
|
|
132
|
-
pr: 'md',
|
|
133
|
-
pb: 'lg',
|
|
134
|
-
pl: 'md'
|
|
136
|
+
pt: 'lg', // padding-top: 24px
|
|
137
|
+
pr: 'md', // padding-right: 16px
|
|
138
|
+
pb: 'lg', // padding-bottom: 24px
|
|
139
|
+
pl: 'md', // padding-left: 16px
|
|
135
140
|
});
|
|
136
141
|
|
|
137
142
|
// Horizontal/Vertical
|
|
138
143
|
const box = css({
|
|
139
|
-
px: 'md',
|
|
140
|
-
py: 'sm'
|
|
144
|
+
px: 'md', // padding-left + padding-right: 16px
|
|
145
|
+
py: 'sm', // padding-top + padding-bottom: 8px
|
|
141
146
|
});
|
|
142
147
|
```
|
|
143
148
|
|
|
@@ -145,20 +150,20 @@ const box = css({
|
|
|
145
150
|
|
|
146
151
|
```typescript
|
|
147
152
|
// All sides
|
|
148
|
-
const box = css({ m: 'md' });
|
|
153
|
+
const box = css({ m: 'md' }); // 16px all sides
|
|
149
154
|
|
|
150
155
|
// Individual sides
|
|
151
156
|
const box = css({
|
|
152
|
-
mt: 'lg',
|
|
153
|
-
mr: 'md',
|
|
154
|
-
mb: 'lg',
|
|
155
|
-
ml: 'md'
|
|
157
|
+
mt: 'lg', // margin-top: 24px
|
|
158
|
+
mr: 'md', // margin-right: 16px
|
|
159
|
+
mb: 'lg', // margin-bottom: 24px
|
|
160
|
+
ml: 'md', // margin-left: 16px
|
|
156
161
|
});
|
|
157
162
|
|
|
158
163
|
// Horizontal/Vertical
|
|
159
164
|
const box = css({
|
|
160
|
-
mx: 'auto',
|
|
161
|
-
my: 'lg'
|
|
165
|
+
mx: 'auto', // margin-left + margin-right: auto (centering)
|
|
166
|
+
my: 'lg', // margin-top + margin-bottom: 24px
|
|
162
167
|
});
|
|
163
168
|
```
|
|
164
169
|
|
|
@@ -169,14 +174,14 @@ const box = css({
|
|
|
169
174
|
const stack = css({
|
|
170
175
|
display: 'flex',
|
|
171
176
|
flexDirection: 'column',
|
|
172
|
-
gap: 'md'
|
|
177
|
+
gap: 'md', // 16px between all children
|
|
173
178
|
});
|
|
174
179
|
|
|
175
180
|
// Different horizontal/vertical gaps
|
|
176
181
|
const grid = css({
|
|
177
182
|
display: 'grid',
|
|
178
|
-
rowGap: 'lg',
|
|
179
|
-
columnGap: 'md'
|
|
183
|
+
rowGap: 'lg', // 24px between rows
|
|
184
|
+
columnGap: 'md', // 16px between columns
|
|
180
185
|
});
|
|
181
186
|
```
|
|
182
187
|
|
|
@@ -188,13 +193,13 @@ Use different spacing values at different breakpoints:
|
|
|
188
193
|
const container = css({
|
|
189
194
|
// Mobile: 16px padding
|
|
190
195
|
// Desktop: 32px padding
|
|
191
|
-
p: { base: 'md', lg: 'xl' }
|
|
196
|
+
p: { base: 'md', lg: 'xl' },
|
|
192
197
|
});
|
|
193
198
|
|
|
194
199
|
const section = css({
|
|
195
200
|
// Mobile: 24px margin
|
|
196
201
|
// Desktop: 48px margin
|
|
197
|
-
mb: { base: 'lg', lg: 'xxl' }
|
|
202
|
+
mb: { base: 'lg', lg: 'xxl' },
|
|
198
203
|
});
|
|
199
204
|
```
|
|
200
205
|
|
|
@@ -218,35 +223,610 @@ const correct = css({ p: 'md', gap: 'lg' });
|
|
|
218
223
|
## Special Cases
|
|
219
224
|
|
|
220
225
|
### Zero Spacing
|
|
226
|
+
|
|
221
227
|
```typescript
|
|
222
228
|
// Reset spacing
|
|
223
229
|
const reset = css({
|
|
224
|
-
m: 'none',
|
|
225
|
-
p: 'none'
|
|
230
|
+
m: 'none', // margin: 0
|
|
231
|
+
p: 'none', // padding: 0
|
|
226
232
|
});
|
|
227
233
|
```
|
|
228
234
|
|
|
229
235
|
### Negative Margins
|
|
236
|
+
|
|
230
237
|
```typescript
|
|
231
238
|
// Negative margins (use sparingly)
|
|
232
239
|
const overlap = css({
|
|
233
|
-
mt: 'calc(var(--spacing-md) * -1)'
|
|
240
|
+
mt: 'calc(var(--spacing-md) * -1)', // -16px
|
|
234
241
|
});
|
|
235
242
|
```
|
|
236
243
|
|
|
237
244
|
### Custom Spacing (Rare)
|
|
245
|
+
|
|
238
246
|
Only use custom spacing when absolutely necessary and none of the tokens fit:
|
|
239
247
|
|
|
240
248
|
```typescript
|
|
241
249
|
// Last resort - custom spacing
|
|
242
250
|
const custom = css({
|
|
243
|
-
padding: 'calc(var(--spacing-sm) + var(--spacing-xs))'
|
|
251
|
+
padding: 'calc(var(--spacing-sm) + var(--spacing-xs))', // 8px + 4px = 12px
|
|
244
252
|
});
|
|
245
253
|
```
|
|
246
254
|
|
|
255
|
+
## How Spacing Tokens Work With Other Tokens
|
|
256
|
+
|
|
257
|
+
Spacing tokens are rarely used in isolation. Here are real-world examples showing how spacing creates rhythm and hierarchy when combined with color, typography, and elevation tokens:
|
|
258
|
+
|
|
259
|
+
### Card Grid Layout
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { Card } from '@discourser/design-system';
|
|
263
|
+
import { css } from '@discourser/design-system/styled-system/css';
|
|
264
|
+
|
|
265
|
+
<div className={css({
|
|
266
|
+
display: 'grid',
|
|
267
|
+
gridTemplateColumns: { base: '1fr', md: 'repeat(2, 1fr)', lg: 'repeat(3, 1fr)' },
|
|
268
|
+
gap: 'lg', // Spacing - 24px between cards
|
|
269
|
+
p: { base: 'md', lg: 'xl' } // Spacing - 16px mobile, 32px desktop padding
|
|
270
|
+
})}>
|
|
271
|
+
{items.map(item => (
|
|
272
|
+
<Card variant="elevated" key={item.id}>
|
|
273
|
+
{/* Card combines: */}
|
|
274
|
+
{/* bg: 'surfaceContainerLow' - Color token */}
|
|
275
|
+
{/* boxShadow: 'level1' - Elevation token */}
|
|
276
|
+
|
|
277
|
+
<div className={css({
|
|
278
|
+
p: 'lg', // Spacing - 24px internal padding
|
|
279
|
+
display: 'flex',
|
|
280
|
+
flexDirection: 'column',
|
|
281
|
+
gap: 'md' // Spacing - 16px between content sections
|
|
282
|
+
})}>
|
|
283
|
+
|
|
284
|
+
<h3 className={css({
|
|
285
|
+
textStyle: 'titleLarge', // Typography - 22px/28px
|
|
286
|
+
color: 'onSurface', // Color - primary text
|
|
287
|
+
mb: 'sm' // Spacing - 8px bottom margin
|
|
288
|
+
})}>
|
|
289
|
+
{item.title}
|
|
290
|
+
</h3>
|
|
291
|
+
|
|
292
|
+
<p className={css({
|
|
293
|
+
textStyle: 'bodyMedium', // Typography - 14px/20px
|
|
294
|
+
color: 'onSurfaceVariant', // Color - secondary text
|
|
295
|
+
mb: 'md' // Spacing - 16px bottom margin
|
|
296
|
+
})}>
|
|
297
|
+
{item.description}
|
|
298
|
+
</p>
|
|
299
|
+
|
|
300
|
+
<div className={css({
|
|
301
|
+
display: 'flex',
|
|
302
|
+
gap: 'sm', // Spacing - 8px between chips
|
|
303
|
+
mt: 'auto' // Push to bottom
|
|
304
|
+
})}>
|
|
305
|
+
{item.tags.map(tag => (
|
|
306
|
+
<span key={tag} className={css({
|
|
307
|
+
bg: 'secondaryContainer', // Color - chip background
|
|
308
|
+
color: 'onSecondaryContainer', // Color - chip text
|
|
309
|
+
textStyle: 'labelSmall', // Typography - 11px/16px
|
|
310
|
+
px: 'sm', // Spacing - 8px horizontal padding
|
|
311
|
+
py: 'xs', // Spacing - 4px vertical padding
|
|
312
|
+
borderRadius: 'full' // Border radius - pill shape
|
|
313
|
+
})}>
|
|
314
|
+
{tag}
|
|
315
|
+
</span>
|
|
316
|
+
))}
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
</Card>
|
|
320
|
+
))}
|
|
321
|
+
</div>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Form Layout with Field Grouping
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { Input, Button, Select } from '@discourser/design-system';
|
|
328
|
+
import { css } from '@discourser/design-system/styled-system/css';
|
|
329
|
+
|
|
330
|
+
<form className={css({
|
|
331
|
+
maxWidth: '600px',
|
|
332
|
+
p: { base: 'lg', lg: 'xl' }, // Spacing - 24px mobile, 32px desktop padding
|
|
333
|
+
bg: 'surface', // Color - background
|
|
334
|
+
borderRadius: 'l3', // Border radius - 12px
|
|
335
|
+
boxShadow: 'level1' // Elevation - subtle shadow
|
|
336
|
+
})}>
|
|
337
|
+
|
|
338
|
+
<h2 className={css({
|
|
339
|
+
textStyle: 'headlineSmall', // Typography - 24px/32px
|
|
340
|
+
color: 'onSurface', // Color - primary text
|
|
341
|
+
mb: 'lg' // Spacing - 24px bottom margin
|
|
342
|
+
})}>
|
|
343
|
+
Contact Information
|
|
344
|
+
</h2>
|
|
345
|
+
|
|
346
|
+
<div className={css({
|
|
347
|
+
display: 'flex',
|
|
348
|
+
flexDirection: 'column',
|
|
349
|
+
gap: 'lg' // Spacing - 24px between form fields
|
|
350
|
+
})}>
|
|
351
|
+
|
|
352
|
+
<Input
|
|
353
|
+
label="Full Name"
|
|
354
|
+
{/* Input internally uses: */}
|
|
355
|
+
{/* textStyle: 'bodyLarge' - Typography (16px/24px) */}
|
|
356
|
+
{/* px: 'md', py: 'sm' - Spacing (16px horizontal, 8px vertical) */}
|
|
357
|
+
{/* borderColor: 'outline' - Color token */}
|
|
358
|
+
{/* color: 'onSurface' - Color for text */}
|
|
359
|
+
/>
|
|
360
|
+
|
|
361
|
+
<div className={css({
|
|
362
|
+
display: 'grid',
|
|
363
|
+
gridTemplateColumns: { base: '1fr', md: '1fr 1fr' },
|
|
364
|
+
gap: 'md' // Spacing - 16px between email/phone
|
|
365
|
+
})}>
|
|
366
|
+
<Input label="Email" type="email" />
|
|
367
|
+
<Input label="Phone" type="tel" />
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<Select label="Country">
|
|
371
|
+
{/* Select uses: */}
|
|
372
|
+
{/* px: 'md', py: 'sm' - Spacing for padding */}
|
|
373
|
+
{/* textStyle: 'bodyLarge' - Typography */}
|
|
374
|
+
</Select>
|
|
375
|
+
|
|
376
|
+
<div className={css({
|
|
377
|
+
mt: 'xl', // Spacing - 32px top margin (section break)
|
|
378
|
+
pt: 'lg', // Spacing - 24px top padding
|
|
379
|
+
borderTopWidth: '1px',
|
|
380
|
+
borderTopColor: 'outlineVariant' // Color - subtle divider
|
|
381
|
+
})}>
|
|
382
|
+
<h3 className={css({
|
|
383
|
+
textStyle: 'titleMedium', // Typography - 16px/24px
|
|
384
|
+
color: 'onSurface', // Color - primary text
|
|
385
|
+
mb: 'md' // Spacing - 16px bottom margin
|
|
386
|
+
})}>
|
|
387
|
+
Additional Details
|
|
388
|
+
</h3>
|
|
389
|
+
|
|
390
|
+
<Input
|
|
391
|
+
label="Message"
|
|
392
|
+
multiline
|
|
393
|
+
rows={4}
|
|
394
|
+
{/* Multiline input uses: */}
|
|
395
|
+
{/* p: 'md' - Spacing (16px all sides) */}
|
|
396
|
+
/>
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<div className={css({
|
|
400
|
+
display: 'flex',
|
|
401
|
+
gap: 'sm', // Spacing - 8px between buttons
|
|
402
|
+
justifyContent: 'flex-end',
|
|
403
|
+
mt: 'lg' // Spacing - 24px top margin
|
|
404
|
+
})}>
|
|
405
|
+
<Button variant="text">
|
|
406
|
+
{/* px: 'lg' - Spacing (24px horizontal) */}
|
|
407
|
+
{/* textStyle: 'labelLarge' - Typography */}
|
|
408
|
+
Cancel
|
|
409
|
+
</Button>
|
|
410
|
+
<Button variant="filled">
|
|
411
|
+
{/* px: 'lg', height: '40px' - Spacing/size */}
|
|
412
|
+
{/* bg: 'primary', color: 'onPrimary' - Color tokens */}
|
|
413
|
+
Submit
|
|
414
|
+
</Button>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
</form>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Navigation Menu with Hierarchical Spacing
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
import { css } from '@discourser/design-system/styled-system/css';
|
|
424
|
+
|
|
425
|
+
<nav className={css({
|
|
426
|
+
bg: 'surfaceContainerLow', // Color - elevated surface
|
|
427
|
+
borderRadius: 'l2', // Border radius - 8px
|
|
428
|
+
p: 'sm', // Spacing - 8px outer padding
|
|
429
|
+
boxShadow: 'level2' // Elevation - menu shadow
|
|
430
|
+
})}>
|
|
431
|
+
|
|
432
|
+
<ul className={css({
|
|
433
|
+
display: 'flex',
|
|
434
|
+
flexDirection: 'column',
|
|
435
|
+
gap: 'xxs' // Spacing - 2px between menu items (minimal)
|
|
436
|
+
})}>
|
|
437
|
+
|
|
438
|
+
<li>
|
|
439
|
+
<a className={css({
|
|
440
|
+
display: 'flex',
|
|
441
|
+
alignItems: 'center',
|
|
442
|
+
gap: 'md', // Spacing - 16px between icon and text
|
|
443
|
+
px: 'md', // Spacing - 16px horizontal padding
|
|
444
|
+
py: 'sm', // Spacing - 8px vertical padding
|
|
445
|
+
borderRadius: 'l1', // Border radius - 4px
|
|
446
|
+
textStyle: 'labelLarge', // Typography - 14px/20px
|
|
447
|
+
color: 'onSurface', // Color - primary text
|
|
448
|
+
_hover: {
|
|
449
|
+
bg: 'surfaceContainerHighest' // Color - hover background
|
|
450
|
+
}
|
|
451
|
+
})}>
|
|
452
|
+
<Icon /> {/* Icon size: 24px */}
|
|
453
|
+
Dashboard
|
|
454
|
+
</a>
|
|
455
|
+
</li>
|
|
456
|
+
|
|
457
|
+
<li>
|
|
458
|
+
<a className={css({
|
|
459
|
+
display: 'flex',
|
|
460
|
+
alignItems: 'center',
|
|
461
|
+
gap: 'md', // Spacing - consistent with above
|
|
462
|
+
px: 'md',
|
|
463
|
+
py: 'sm',
|
|
464
|
+
borderRadius: 'l1',
|
|
465
|
+
textStyle: 'labelLarge',
|
|
466
|
+
color: 'onSurface',
|
|
467
|
+
bg: 'secondaryContainer', // Color - active state background
|
|
468
|
+
_hover: {
|
|
469
|
+
bg: 'secondaryContainer'
|
|
470
|
+
}
|
|
471
|
+
})}>
|
|
472
|
+
<Icon />
|
|
473
|
+
Projects
|
|
474
|
+
</a>
|
|
475
|
+
|
|
476
|
+
{/* Nested submenu */}
|
|
477
|
+
<ul className={css({
|
|
478
|
+
mt: 'xxs', // Spacing - 2px top margin
|
|
479
|
+
ml: 'xl', // Spacing - 32px left margin (indent)
|
|
480
|
+
display: 'flex',
|
|
481
|
+
flexDirection: 'column',
|
|
482
|
+
gap: 'xxs' // Spacing - 2px between subitems
|
|
483
|
+
})}>
|
|
484
|
+
<li>
|
|
485
|
+
<a className={css({
|
|
486
|
+
display: 'block',
|
|
487
|
+
px: 'md', // Spacing - 16px horizontal
|
|
488
|
+
py: 'xs', // Spacing - 4px vertical (smaller than parent)
|
|
489
|
+
borderRadius: 'l1',
|
|
490
|
+
textStyle: 'bodyMedium', // Typography - 14px/20px
|
|
491
|
+
color: 'onSurfaceVariant', // Color - secondary text
|
|
492
|
+
_hover: {
|
|
493
|
+
bg: 'surfaceContainerHighest'
|
|
494
|
+
}
|
|
495
|
+
})}>
|
|
496
|
+
My Projects
|
|
497
|
+
</a>
|
|
498
|
+
</li>
|
|
499
|
+
</ul>
|
|
500
|
+
</li>
|
|
501
|
+
|
|
502
|
+
<li className={css({
|
|
503
|
+
mt: 'sm', // Spacing - 8px top margin (section break)
|
|
504
|
+
pt: 'sm', // Spacing - 8px top padding
|
|
505
|
+
borderTopWidth: '1px',
|
|
506
|
+
borderTopColor: 'outlineVariant' // Color - divider
|
|
507
|
+
})}>
|
|
508
|
+
<a className={css({
|
|
509
|
+
display: 'flex',
|
|
510
|
+
alignItems: 'center',
|
|
511
|
+
gap: 'md',
|
|
512
|
+
px: 'md',
|
|
513
|
+
py: 'sm',
|
|
514
|
+
borderRadius: 'l1',
|
|
515
|
+
textStyle: 'labelLarge',
|
|
516
|
+
color: 'onSurface'
|
|
517
|
+
})}>
|
|
518
|
+
<Icon />
|
|
519
|
+
Settings
|
|
520
|
+
</a>
|
|
521
|
+
</li>
|
|
522
|
+
</ul>
|
|
523
|
+
</nav>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Dialog with Structured Content Spacing
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
import { Dialog, Button } from '@discourser/design-system';
|
|
530
|
+
import { css } from '@discourser/design-system/styled-system/css';
|
|
531
|
+
|
|
532
|
+
<Dialog.Root>
|
|
533
|
+
<Dialog.Backdrop
|
|
534
|
+
bg: 'scrim' // Color - overlay background
|
|
535
|
+
opacity: 0.32
|
|
536
|
+
/>
|
|
537
|
+
|
|
538
|
+
<Dialog.Content className={css({
|
|
539
|
+
bg: 'surfaceContainerHigh', // Color - high elevation surface
|
|
540
|
+
borderRadius: 'l4', // Border radius - 28px
|
|
541
|
+
boxShadow: 'level3', // Elevation - dialog shadow
|
|
542
|
+
p: 'xl', // Spacing - 32px padding
|
|
543
|
+
maxWidth: '560px',
|
|
544
|
+
display: 'flex',
|
|
545
|
+
flexDirection: 'column',
|
|
546
|
+
gap: 'lg' // Spacing - 24px between major sections
|
|
547
|
+
})}>
|
|
548
|
+
|
|
549
|
+
<div>
|
|
550
|
+
<Dialog.Title className={css({
|
|
551
|
+
textStyle: 'headlineSmall', // Typography - 24px/32px
|
|
552
|
+
color: 'onSurface', // Color - primary text
|
|
553
|
+
mb: 'md' // Spacing - 16px bottom margin
|
|
554
|
+
})}>
|
|
555
|
+
Confirm Changes
|
|
556
|
+
</Dialog.Title>
|
|
557
|
+
|
|
558
|
+
<Dialog.Description className={css({
|
|
559
|
+
textStyle: 'bodyMedium', // Typography - 14px/20px
|
|
560
|
+
color: 'onSurfaceVariant' // Color - secondary text
|
|
561
|
+
})}>
|
|
562
|
+
The following changes will be applied to your account:
|
|
563
|
+
</Dialog.Description>
|
|
564
|
+
</div>
|
|
565
|
+
|
|
566
|
+
<div className={css({
|
|
567
|
+
bg: 'surfaceContainerHighest', // Color - nested surface
|
|
568
|
+
borderRadius: 'l2', // Border radius - 8px
|
|
569
|
+
p: 'md' // Spacing - 16px padding
|
|
570
|
+
})}>
|
|
571
|
+
<ul className={css({
|
|
572
|
+
display: 'flex',
|
|
573
|
+
flexDirection: 'column',
|
|
574
|
+
gap: 'sm' // Spacing - 8px between list items
|
|
575
|
+
})}>
|
|
576
|
+
{changes.map((change, index) => (
|
|
577
|
+
<li key={index} className={css({
|
|
578
|
+
display: 'flex',
|
|
579
|
+
alignItems: 'center',
|
|
580
|
+
gap: 'xs', // Spacing - 4px between icon and text
|
|
581
|
+
textStyle: 'bodySmall', // Typography - 12px/16px
|
|
582
|
+
color: 'onSurface' // Color - primary text
|
|
583
|
+
})}>
|
|
584
|
+
<CheckIcon size="16px" />
|
|
585
|
+
{change}
|
|
586
|
+
</li>
|
|
587
|
+
))}
|
|
588
|
+
</ul>
|
|
589
|
+
</div>
|
|
590
|
+
|
|
591
|
+
<div className={css({
|
|
592
|
+
bg: 'primaryContainer', // Color - info background
|
|
593
|
+
borderRadius: 'l2', // Border radius - 8px
|
|
594
|
+
p: 'md', // Spacing - 16px padding
|
|
595
|
+
display: 'flex',
|
|
596
|
+
gap: 'sm' // Spacing - 8px between icon and text
|
|
597
|
+
})}>
|
|
598
|
+
<InfoIcon className={css({
|
|
599
|
+
color: 'onPrimaryContainer', // Color - icon color
|
|
600
|
+
flexShrink: 0
|
|
601
|
+
})} />
|
|
602
|
+
<p className={css({
|
|
603
|
+
textStyle: 'bodySmall', // Typography - 12px/16px
|
|
604
|
+
color: 'onPrimaryContainer' // Color - text color
|
|
605
|
+
})}>
|
|
606
|
+
This action can be reverted within 30 days.
|
|
607
|
+
</p>
|
|
608
|
+
</div>
|
|
609
|
+
|
|
610
|
+
<div className={css({
|
|
611
|
+
display: 'flex',
|
|
612
|
+
gap: 'sm', // Spacing - 8px between action buttons
|
|
613
|
+
justifyContent: 'flex-end',
|
|
614
|
+
mt: 'md' // Spacing - 16px top margin
|
|
615
|
+
})}>
|
|
616
|
+
<Button variant="text">
|
|
617
|
+
{/* px: 'lg' - Spacing (24px) */}
|
|
618
|
+
{/* textStyle: 'labelLarge' - Typography */}
|
|
619
|
+
Cancel
|
|
620
|
+
</Button>
|
|
621
|
+
<Button variant="filled">
|
|
622
|
+
{/* px: 'lg' - Spacing (24px) */}
|
|
623
|
+
{/* bg: 'primary', color: 'onPrimary' - Color */}
|
|
624
|
+
Confirm
|
|
625
|
+
</Button>
|
|
626
|
+
</div>
|
|
627
|
+
</Dialog.Content>
|
|
628
|
+
</Dialog.Root>
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Button Group with Icon Spacing
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
import { Button } from '@discourser/design-system';
|
|
635
|
+
import { css } from '@discourser/design-system/styled-system/css';
|
|
636
|
+
|
|
637
|
+
<div className={css({
|
|
638
|
+
display: 'flex',
|
|
639
|
+
gap: 'sm', // Spacing - 8px between buttons
|
|
640
|
+
flexWrap: 'wrap'
|
|
641
|
+
})}>
|
|
642
|
+
<Button variant="filled">
|
|
643
|
+
{/* Internal button structure: */}
|
|
644
|
+
<span className={css({
|
|
645
|
+
display: 'flex',
|
|
646
|
+
alignItems: 'center',
|
|
647
|
+
gap: 'xs', // Spacing - 4px between icon and text
|
|
648
|
+
px: 'lg', // Spacing - 24px horizontal padding
|
|
649
|
+
height: '40px' // Size - md variant
|
|
650
|
+
})}>
|
|
651
|
+
<SaveIcon /> {/* Icon size: 18px */}
|
|
652
|
+
<span className={css({
|
|
653
|
+
textStyle: 'labelLarge', // Typography - 14px/20px
|
|
654
|
+
color: 'onPrimary' // Color - button text
|
|
655
|
+
})}>
|
|
656
|
+
Save
|
|
657
|
+
</span>
|
|
658
|
+
</span>
|
|
659
|
+
</Button>
|
|
660
|
+
|
|
661
|
+
<Button variant="outlined">
|
|
662
|
+
{/* bg: transparent - Color */}
|
|
663
|
+
{/* borderColor: 'outline' - Color */}
|
|
664
|
+
{/* px: 'lg' - Spacing */}
|
|
665
|
+
<span className={css({
|
|
666
|
+
display: 'flex',
|
|
667
|
+
alignItems: 'center',
|
|
668
|
+
gap: 'xs' // Spacing - consistent icon-text gap
|
|
669
|
+
})}>
|
|
670
|
+
<DownloadIcon />
|
|
671
|
+
<span>Download</span>
|
|
672
|
+
</span>
|
|
673
|
+
</Button>
|
|
674
|
+
|
|
675
|
+
<Button variant="text">
|
|
676
|
+
{/* color: 'primary' - Color */}
|
|
677
|
+
{/* px: 'lg' - Spacing */}
|
|
678
|
+
Preview
|
|
679
|
+
</Button>
|
|
680
|
+
</div>
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### List Item with Avatar and Actions
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
import { Avatar, IconButton } from '@discourser/design-system';
|
|
687
|
+
import { css } from '@discourser/design-system/styled-system/css';
|
|
688
|
+
|
|
689
|
+
<li className={css({
|
|
690
|
+
display: 'flex',
|
|
691
|
+
alignItems: 'center',
|
|
692
|
+
gap: 'md', // Spacing - 16px between major sections
|
|
693
|
+
p: 'md', // Spacing - 16px padding
|
|
694
|
+
borderRadius: 'l2', // Border radius - 8px
|
|
695
|
+
bg: 'surface', // Color - background
|
|
696
|
+
_hover: {
|
|
697
|
+
bg: 'surfaceContainerHighest' // Color - hover state
|
|
698
|
+
}
|
|
699
|
+
})}>
|
|
700
|
+
|
|
701
|
+
<Avatar.Root size="md">
|
|
702
|
+
{/* Avatar size: 40px */}
|
|
703
|
+
{/* Avatar has built-in bg: 'primary', color: 'onPrimary' */}
|
|
704
|
+
<Avatar.Image src="/avatar.jpg" />
|
|
705
|
+
<Avatar.Fallback>JD</Avatar.Fallback>
|
|
706
|
+
</Avatar.Root>
|
|
707
|
+
|
|
708
|
+
<div className={css({ flex: 1 })}>
|
|
709
|
+
<div className={css({
|
|
710
|
+
display: 'flex',
|
|
711
|
+
alignItems: 'center',
|
|
712
|
+
gap: 'xs', // Spacing - 4px between name and badge
|
|
713
|
+
mb: 'xxs' // Spacing - 2px bottom margin
|
|
714
|
+
})}>
|
|
715
|
+
<h4 className={css({
|
|
716
|
+
textStyle: 'titleMedium', // Typography - 16px/24px
|
|
717
|
+
color: 'onSurface' // Color - primary text
|
|
718
|
+
})}>
|
|
719
|
+
Jane Doe
|
|
720
|
+
</h4>
|
|
721
|
+
<span className={css({
|
|
722
|
+
bg: 'tertiaryContainer', // Color - badge background
|
|
723
|
+
color: 'onTertiaryContainer', // Color - badge text
|
|
724
|
+
textStyle: 'labelSmall', // Typography - 11px/16px
|
|
725
|
+
px: 'xs', // Spacing - 4px horizontal padding
|
|
726
|
+
py: '2px', // Spacing - minimal vertical
|
|
727
|
+
borderRadius: 'full' // Border radius - pill
|
|
728
|
+
})}>
|
|
729
|
+
Pro
|
|
730
|
+
</span>
|
|
731
|
+
</div>
|
|
732
|
+
|
|
733
|
+
<p className={css({
|
|
734
|
+
textStyle: 'bodySmall', // Typography - 12px/16px
|
|
735
|
+
color: 'onSurfaceVariant', // Color - secondary text
|
|
736
|
+
mb: 'xs' // Spacing - 4px bottom margin
|
|
737
|
+
})}>
|
|
738
|
+
jane.doe@example.com
|
|
739
|
+
</p>
|
|
740
|
+
|
|
741
|
+
<span className={css({
|
|
742
|
+
textStyle: 'labelSmall', // Typography - 11px/16px
|
|
743
|
+
color: 'onSurfaceVariant' // Color - tertiary text
|
|
744
|
+
})}>
|
|
745
|
+
Last active 2 hours ago
|
|
746
|
+
</span>
|
|
747
|
+
</div>
|
|
748
|
+
|
|
749
|
+
<div className={css({
|
|
750
|
+
display: 'flex',
|
|
751
|
+
gap: 'xs' // Spacing - 4px between icon buttons
|
|
752
|
+
})}>
|
|
753
|
+
<IconButton variant="ghost" size="sm">
|
|
754
|
+
{/* IconButton uses: */}
|
|
755
|
+
{/* p: 'sm' - Spacing (8px padding) */}
|
|
756
|
+
{/* size: '32px' - Size token */}
|
|
757
|
+
{/* borderRadius: 'full' - Border radius */}
|
|
758
|
+
<EditIcon />
|
|
759
|
+
</IconButton>
|
|
760
|
+
<IconButton variant="ghost" size="sm">
|
|
761
|
+
<DeleteIcon className={css({
|
|
762
|
+
color: 'error' // Color - error icon
|
|
763
|
+
})} />
|
|
764
|
+
</IconButton>
|
|
765
|
+
</div>
|
|
766
|
+
</li>
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### Multi-Token Spacing Pattern Summary
|
|
770
|
+
|
|
771
|
+
When creating layouts, spacing tokens combine with other tokens in these patterns:
|
|
772
|
+
|
|
773
|
+
**Container Pattern:**
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
{
|
|
777
|
+
p: 'md-xxl', // Spacing - internal padding
|
|
778
|
+
gap: 'sm-lg', // Spacing - between children
|
|
779
|
+
bg: 'surface*', // Color - background
|
|
780
|
+
borderRadius: 'l1-l4', // Border radius
|
|
781
|
+
boxShadow: 'level*' // Elevation (if needed)
|
|
782
|
+
}
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
**Content Flow Pattern:**
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
{
|
|
789
|
+
display: 'flex|grid',
|
|
790
|
+
flexDirection: 'column',
|
|
791
|
+
gap: 'md-lg', // Spacing - primary content flow
|
|
792
|
+
mb: 'xl-xxl' // Spacing - section separation
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**Interactive Element Pattern:**
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
{
|
|
800
|
+
px: 'md-lg', // Spacing - horizontal padding
|
|
801
|
+
py: 'xs-sm', // Spacing - vertical padding (usually smaller)
|
|
802
|
+
gap: 'xs-sm', // Spacing - icon-text gap
|
|
803
|
+
textStyle: 'label*', // Typography
|
|
804
|
+
borderRadius: 'full|l1', // Border radius
|
|
805
|
+
_hover: {
|
|
806
|
+
bg: 'surface*' // Color - hover state
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
**Hierarchical Text Pattern:**
|
|
812
|
+
|
|
813
|
+
```typescript
|
|
814
|
+
{
|
|
815
|
+
textStyle: 'headline*|title*', // Typography - heading
|
|
816
|
+
color: 'onSurface', // Color - primary text
|
|
817
|
+
mb: 'sm-md', // Spacing - heading-to-content gap
|
|
818
|
+
|
|
819
|
+
// Followed by:
|
|
820
|
+
textStyle: 'body*', // Typography - body text
|
|
821
|
+
color: 'onSurfaceVariant', // Color - secondary text
|
|
822
|
+
mb: 'md-lg' // Spacing - paragraph separation
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
247
826
|
## Accessibility
|
|
248
827
|
|
|
249
828
|
Proper spacing improves:
|
|
829
|
+
|
|
250
830
|
- **Touch targets**: Minimum 44x44px (use appropriate padding)
|
|
251
831
|
- **Readability**: Adequate spacing between text blocks
|
|
252
832
|
- **Scannability**: Clear visual separation between sections
|
|
@@ -259,13 +839,13 @@ Proper spacing improves:
|
|
|
259
839
|
const button = css({
|
|
260
840
|
minHeight: '44px',
|
|
261
841
|
px: 'md',
|
|
262
|
-
py: 'sm'
|
|
842
|
+
py: 'sm',
|
|
263
843
|
});
|
|
264
844
|
|
|
265
845
|
// ✅ Spacing between interactive elements
|
|
266
846
|
const buttonGroup = css({
|
|
267
847
|
display: 'flex',
|
|
268
|
-
gap: 'sm'
|
|
848
|
+
gap: 'sm', // Minimum 8px between buttons
|
|
269
849
|
});
|
|
270
850
|
```
|
|
271
851
|
|
|
@@ -280,10 +860,10 @@ const buttonGroup = css({
|
|
|
280
860
|
|
|
281
861
|
## Common Spacing Mistakes
|
|
282
862
|
|
|
283
|
-
| ❌ Wrong
|
|
284
|
-
|
|
285
|
-
| `gap: 'xs'` for cards
|
|
286
|
-
| `p: 'xxxl'` for button
|
|
287
|
-
| `mb: 'sm'` for sections
|
|
288
|
-
| `padding: '20px'`
|
|
863
|
+
| ❌ Wrong | ✅ Right | Why |
|
|
864
|
+
| ------------------------- | --------------------- | -------------------------------- |
|
|
865
|
+
| `gap: 'xs'` for cards | `gap: 'lg'` | Cards need breathing room |
|
|
866
|
+
| `p: 'xxxl'` for button | `px: 'md', py: 'sm'` | Too much padding overwhelms |
|
|
867
|
+
| `mb: 'sm'` for sections | `mb: 'xl'` or `'xxl'` | Sections need clear separation |
|
|
868
|
+
| `padding: '20px'` | `p: 'lg'` | Use tokens, not arbitrary values |
|
|
289
869
|
| `gap: 'lg'` between icons | `gap: 'xs'` or `'sm'` | Icons are small, need less space |
|