@discourser/design-system 0.2.2 → 0.3.1

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.
Files changed (34) hide show
  1. package/README.md +53 -0
  2. package/dist/index.cjs +121 -95
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +203 -62
  5. package/dist/index.d.ts +203 -62
  6. package/dist/index.js +118 -96
  7. package/dist/index.js.map +1 -1
  8. package/guidelines/Guidelines.md +88 -0
  9. package/guidelines/components/button.md +314 -0
  10. package/guidelines/components/card.md +353 -0
  11. package/guidelines/components/dialog.md +465 -0
  12. package/guidelines/components/icon-button.md +417 -0
  13. package/guidelines/components/input.md +499 -0
  14. package/guidelines/components/switch.md +457 -0
  15. package/guidelines/design-tokens/colors.md +187 -0
  16. package/guidelines/design-tokens/elevation.md +274 -0
  17. package/guidelines/design-tokens/spacing.md +289 -0
  18. package/guidelines/design-tokens/typography.md +226 -0
  19. package/guidelines/overview-components.md +156 -0
  20. package/package.json +9 -2
  21. package/styled-system/recipes/heading.d.ts +34 -0
  22. package/styled-system/recipes/heading.mjs +40 -0
  23. package/styled-system/recipes/index.d.ts +3 -0
  24. package/styled-system/recipes/index.mjs +3 -0
  25. package/styled-system/recipes/input-addon.d.ts +38 -0
  26. package/styled-system/recipes/input-addon.mjs +40 -0
  27. package/styled-system/recipes/input-group.d.ts +34 -0
  28. package/styled-system/recipes/input-group.mjs +49 -0
  29. package/styled-system/recipes/radio-group.d.ts +4 -0
  30. package/styled-system/recipes/radio-group.mjs +8 -2
  31. package/styled-system/tokens/index.mjs +352 -220
  32. package/styled-system/tokens/tokens.d.ts +13 -10
  33. package/styled-system/types/prop-type.d.ts +16 -1
  34. package/styled-system/types/style-props.d.ts +20 -20
@@ -0,0 +1,274 @@
1
+ # Elevation Tokens
2
+
3
+ The design system uses Material Design 3 elevation system combining surface tints and shadows to create depth and hierarchy.
4
+
5
+ ## What is Elevation?
6
+
7
+ Elevation creates visual hierarchy by making elements appear to float above the background. M3 uses two techniques:
8
+ 1. **Surface Tints**: Background color changes (via `surfaceContainer*` tokens)
9
+ 2. **Shadows**: Subtle shadows for additional depth (optional)
10
+
11
+ **Important**: M3 primarily uses surface tints, not heavy shadows like older Material Design versions.
12
+
13
+ ## Elevation Levels
14
+
15
+ | Token | Shadow Value | Usage |
16
+ |-------|--------------|-------|
17
+ | `level0` | none | Flat elements, no elevation |
18
+ | `level1` | `0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)` | Cards (low elevation) |
19
+ | `level2` | `0px 1px 2px rgba(0,0,0,0.3), 0px 2px 6px 2px rgba(0,0,0,0.15)` | Raised cards, FAB (resting) |
20
+ | `level3` | `0px 4px 8px 3px rgba(0,0,0,0.15), 0px 1px 3px rgba(0,0,0,0.3)` | Dialogs, menus |
21
+ | `level4` | `0px 6px 10px 4px rgba(0,0,0,0.15), 0px 2px 3px rgba(0,0,0,0.3)` | FAB (hover), navigation drawers |
22
+ | `level5` | `0px 8px 12px 6px rgba(0,0,0,0.15), 0px 4px 4px rgba(0,0,0,0.3)` | Modals, navigation bars |
23
+
24
+ ## M3 Elevation Strategy
25
+
26
+ ### Primary Method: Surface Containers
27
+
28
+ M3 primarily uses surface container colors for elevation. Higher elevations get different background colors:
29
+
30
+ ```typescript
31
+ import { css } from '@discourser/design-system/styled-system/css';
32
+
33
+ // Level 0 (flat, on page)
34
+ const flat = css({ bg: 'surface' });
35
+
36
+ // Level 1 (low elevation - cards)
37
+ const card = css({ bg: 'surfaceContainerLow' });
38
+
39
+ // Level 3 (default containers)
40
+ const container = css({ bg: 'surfaceContainer' });
41
+
42
+ // Level 4 (high elevation - dialogs)
43
+ const dialog = css({ bg: 'surfaceContainerHigh' });
44
+
45
+ // Level 5 (highest elevation)
46
+ const modal = css({ bg: 'surfaceContainerHighest' });
47
+ ```
48
+
49
+ ### Secondary Method: Shadows (Optional)
50
+
51
+ Shadows can be added for additional depth, but use sparingly:
52
+
53
+ ```typescript
54
+ // Card with subtle shadow
55
+ const card = css({
56
+ bg: 'surfaceContainerLow',
57
+ boxShadow: 'level1' // Optional shadow
58
+ });
59
+
60
+ // Dialog with shadow
61
+ const dialog = css({
62
+ bg: 'surfaceContainerHigh',
63
+ boxShadow: 'level3'
64
+ });
65
+ ```
66
+
67
+ ## Component Elevation Mapping
68
+
69
+ | Component | Surface Container | Shadow Level | Elevation |
70
+ |-----------|-------------------|--------------|-----------|
71
+ | Page background | `surface` | `level0` | 0dp |
72
+ | Filled Card | `surface` | `level0` | 0dp |
73
+ | Outlined Card | `surface` | `level0` | 0dp |
74
+ | Elevated Card | `surfaceContainerLow` | `level1` | 1dp |
75
+ | Input (filled) | `surfaceContainerHighest` | `level0` | 0dp |
76
+ | Button (filled) | `primary` | `level0` | 0dp |
77
+ | Button (elevated) | `surfaceContainerLow` | `level1` | 1dp |
78
+ | Dialog | `surfaceContainerHigh` | `level3` | 3dp |
79
+ | Menu | `surfaceContainer` | `level2` | 2dp |
80
+ | Navigation Drawer | `surfaceContainerLow` | `level1` | 1dp |
81
+
82
+ ## Usage Patterns
83
+
84
+ ### Cards
85
+
86
+ ```typescript
87
+ // Filled card (no elevation)
88
+ const filledCard = css({
89
+ bg: 'surface',
90
+ borderColor: 'outlineVariant',
91
+ borderWidth: '1px'
92
+ });
93
+
94
+ // Elevated card
95
+ const elevatedCard = css({
96
+ bg: 'surfaceContainerLow',
97
+ boxShadow: 'level1' // Optional
98
+ });
99
+
100
+ // Outlined card (no elevation)
101
+ const outlinedCard = css({
102
+ bg: 'surface',
103
+ borderColor: 'outline',
104
+ borderWidth: '1px'
105
+ });
106
+ ```
107
+
108
+ ### Dialogs and Modals
109
+
110
+ ```typescript
111
+ // Dialog
112
+ const dialog = css({
113
+ bg: 'surfaceContainerHigh',
114
+ boxShadow: 'level3',
115
+ borderRadius: 'large'
116
+ });
117
+
118
+ // Full-screen modal
119
+ const modal = css({
120
+ bg: 'surfaceContainerHighest',
121
+ boxShadow: 'level5'
122
+ });
123
+
124
+ // Scrim (overlay behind dialog)
125
+ const scrim = css({
126
+ bg: 'scrim',
127
+ opacity: 0.32
128
+ });
129
+ ```
130
+
131
+ ### Interactive States
132
+
133
+ Elevation can change on interaction:
134
+
135
+ ```typescript
136
+ // Button hover (increase elevation)
137
+ const button = css({
138
+ bg: 'surfaceContainerLow',
139
+ boxShadow: 'level1',
140
+ _hover: {
141
+ boxShadow: 'level2' // Increase shadow on hover
142
+ }
143
+ });
144
+
145
+ // Card hover
146
+ const card = css({
147
+ bg: 'surfaceContainerLow',
148
+ boxShadow: 'level1',
149
+ transition: 'box-shadow 0.2s',
150
+ _hover: {
151
+ boxShadow: 'level2'
152
+ }
153
+ });
154
+ ```
155
+
156
+ ## What NOT to Do
157
+
158
+ ```typescript
159
+ // ❌ NEVER use arbitrary shadow values
160
+ const wrong = css({
161
+ boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
162
+ });
163
+
164
+ // ❌ NEVER use heavy shadows (old Material Design style)
165
+ const wrong = css({
166
+ boxShadow: '0 10px 40px rgba(0,0,0,0.5)'
167
+ });
168
+
169
+ // ❌ NEVER use elevation without surface tints
170
+ const wrong = css({
171
+ bg: '#FFFFFF', // Raw color
172
+ boxShadow: 'level1'
173
+ });
174
+
175
+ // ✅ ALWAYS use surface containers + optional shadows
176
+ const correct = css({
177
+ bg: 'surfaceContainerLow',
178
+ boxShadow: 'level1'
179
+ });
180
+ ```
181
+
182
+ ## Elevation Hierarchy Rules
183
+
184
+ 1. **Higher elements** should have higher elevation
185
+ 2. **Overlays and modals** should be highest (level 4-5)
186
+ 3. **Dialogs and menus** should be mid-high (level 2-3)
187
+ 4. **Cards and containers** should be low (level 0-1)
188
+ 5. **Page backgrounds** should be level 0
189
+
190
+ ### Layering Example
191
+
192
+ ```
193
+ Page Background (surface, level0)
194
+ ├─ Filled Card (surface, level0)
195
+ ├─ Elevated Card (surfaceContainerLow, level1)
196
+ └─ Dialog (surfaceContainerHigh, level3)
197
+ └─ Button in Dialog (primary, level0)
198
+ ```
199
+
200
+ ## Accessibility
201
+
202
+ ### Color Contrast
203
+ Surface tint changes must maintain adequate contrast:
204
+ - Text on elevated surfaces still uses `onSurface` or `onSurfaceVariant`
205
+ - The design system automatically handles this
206
+
207
+ ### Motion and Transitions
208
+ When elevation changes on interaction:
209
+
210
+ ```typescript
211
+ const card = css({
212
+ transition: 'box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
213
+ _hover: {
214
+ boxShadow: 'level2'
215
+ }
216
+ });
217
+ ```
218
+
219
+ Use M3 motion tokens for consistent timing:
220
+ - `fast` (100ms) - Subtle changes
221
+ - `normal` (200ms) - Standard transitions
222
+ - `slow` (300ms) - Emphasized changes
223
+
224
+ ## Dark Mode
225
+
226
+ Elevation behaves differently in dark mode:
227
+ - **Light mode**: Higher elevation = lighter background
228
+ - **Dark mode**: Higher elevation = lighter background (more tint)
229
+
230
+ The surface container tokens handle this automatically. No code changes needed.
231
+
232
+ ```typescript
233
+ // Automatically adapts to dark mode
234
+ const card = css({ bg: 'surfaceContainerLow' });
235
+ ```
236
+
237
+ ## Component Usage
238
+
239
+ Most components handle elevation automatically:
240
+
241
+ ```typescript
242
+ import { Card, Dialog, Button } from '@discourser/design-system';
243
+
244
+ // Card handles elevation via variant
245
+ <Card variant="elevated">Content</Card> // Auto uses surfaceContainerLow + level1
246
+
247
+ // Dialog handles elevation automatically
248
+ <Dialog.Content>Dialog content</Dialog.Content> // Auto uses surfaceContainerHigh + level3
249
+
250
+ // Button elevated variant
251
+ <Button variant="elevated">Action</Button> // Auto uses surfaceContainerLow + level1
252
+ ```
253
+
254
+ Only use manual elevation tokens when creating custom layouts outside of components.
255
+
256
+ ## Best Practices
257
+
258
+ 1. **Prefer surface containers** over shadows alone
259
+ 2. **Use minimal shadows** - M3 is more subtle than older Material Design
260
+ 3. **Maintain hierarchy** - overlays above dialogs above cards
261
+ 4. **Animate elevation changes** for smooth interactions
262
+ 5. **Test in dark mode** to ensure proper contrast
263
+ 6. **Let components handle elevation** when possible
264
+ 7. **Use level 0-1 for most content** - save higher levels for overlays
265
+
266
+ ## Common Elevation Mistakes
267
+
268
+ | ❌ Wrong | ✅ Right | Why |
269
+ |---------|---------|-----|
270
+ | Dialog at level1 | Dialog at level3 | Dialogs should float above content |
271
+ | Heavy `boxShadow: '0 10px 50px'` | `boxShadow: 'level1'` | M3 uses subtle shadows |
272
+ | Card at level5 | Card at level1 | Cards shouldn't float too high |
273
+ | `bg: '#FFF', boxShadow: 'level1'` | `bg: 'surfaceContainerLow'` | Use surface containers, not raw colors |
274
+ | No elevation for dialogs | `bg: 'surfaceContainerHigh'` | Dialogs need visual separation |
@@ -0,0 +1,289 @@
1
+ # Spacing Tokens
2
+
3
+ The design system uses a consistent spacing scale based on an 8px grid system for predictable, harmonious layouts.
4
+
5
+ ## Spacing Scale
6
+
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
+
19
+ ## Usage Principles
20
+
21
+ ### The 8px Grid
22
+ All spacing values (except `xxs` and `xs`) are multiples of 8px. This creates:
23
+ - Visual rhythm and consistency
24
+ - Predictable alignment
25
+ - Easier mental math for designers and developers
26
+ - Better cross-browser rendering
27
+
28
+ ### Common Patterns
29
+
30
+ #### Component Internal Spacing
31
+ ```typescript
32
+ import { css } from '@discourser/design-system/styled-system/css';
33
+
34
+ // ✅ Button padding (handled by component)
35
+ const button = css({
36
+ px: 'md', // 16px horizontal
37
+ py: 'sm' // 8px vertical
38
+ });
39
+
40
+ // ✅ Card padding
41
+ const card = css({
42
+ p: 'lg' // 24px all sides
43
+ });
44
+
45
+ // ✅ Input field
46
+ const input = css({
47
+ px: 'md', // 16px horizontal
48
+ py: 'sm' // 8px vertical
49
+ });
50
+ ```
51
+
52
+ #### Layout Spacing
53
+ ```typescript
54
+ // ✅ Stack of elements
55
+ const stack = css({
56
+ display: 'flex',
57
+ flexDirection: 'column',
58
+ gap: 'md' // 16px between items
59
+ });
60
+
61
+ // ✅ Grid layout
62
+ const grid = css({
63
+ display: 'grid',
64
+ gap: 'lg' // 24px between grid items
65
+ });
66
+
67
+ // ✅ Container with padding
68
+ const container = css({
69
+ px: { base: 'md', lg: 'xl' }, // Responsive: 16px mobile, 32px desktop
70
+ py: 'lg' // 24px vertical
71
+ });
72
+ ```
73
+
74
+ #### Margin Between Sections
75
+ ```typescript
76
+ // ✅ Section spacing
77
+ const section = css({
78
+ mb: 'xxl' // 48px bottom margin
79
+ });
80
+
81
+ // ✅ Page-level spacing
82
+ const page = css({
83
+ p: { base: 'lg', lg: 'xxl' } // 24px mobile, 48px desktop
84
+ });
85
+ ```
86
+
87
+ ## Common Use Cases
88
+
89
+ ### Component Spacing
90
+
91
+ | Use Case | Token | Example |
92
+ |----------|-------|---------|
93
+ | Button padding (horizontal) | `md` | 16px |
94
+ | Button padding (vertical) | `sm` | 8px |
95
+ | Input padding | `md` | 16px |
96
+ | Card padding | `lg` or `xl` | 24-32px |
97
+ | Dialog padding | `lg` or `xl` | 24-32px |
98
+ | Icon margins | `xs` or `sm` | 4-8px |
99
+
100
+ ### Layout Spacing
101
+
102
+ | Use Case | Token | Example |
103
+ |----------|-------|---------|
104
+ | Gap between form fields | `md` | 16px |
105
+ | Gap between cards | `lg` | 24px |
106
+ | Section margins | `xl` or `xxl` | 32-48px |
107
+ | Page margins | `xl` or `xxl` | 32-48px |
108
+ | Hero section padding | `xxxl` | 64px |
109
+
110
+ ### Content Spacing
111
+
112
+ | Use Case | Token | Example |
113
+ |----------|-------|---------|
114
+ | Paragraph margins | `md` | 16px |
115
+ | List item spacing | `sm` or `md` | 8-16px |
116
+ | Icon-text gap | `xs` or `sm` | 4-8px |
117
+ | Button group gap | `sm` | 8px |
118
+
119
+ ## Usage in Code
120
+
121
+ ### Padding
122
+
123
+ ```typescript
124
+ import { css } from '@discourser/design-system/styled-system/css';
125
+
126
+ // All sides
127
+ const box = css({ p: 'md' }); // 16px all sides
128
+
129
+ // Individual sides
130
+ const box = css({
131
+ pt: 'lg', // padding-top: 24px
132
+ pr: 'md', // padding-right: 16px
133
+ pb: 'lg', // padding-bottom: 24px
134
+ pl: 'md' // padding-left: 16px
135
+ });
136
+
137
+ // Horizontal/Vertical
138
+ const box = css({
139
+ px: 'md', // padding-left + padding-right: 16px
140
+ py: 'sm' // padding-top + padding-bottom: 8px
141
+ });
142
+ ```
143
+
144
+ ### Margin
145
+
146
+ ```typescript
147
+ // All sides
148
+ const box = css({ m: 'md' }); // 16px all sides
149
+
150
+ // Individual sides
151
+ const box = css({
152
+ mt: 'lg', // margin-top: 24px
153
+ mr: 'md', // margin-right: 16px
154
+ mb: 'lg', // margin-bottom: 24px
155
+ ml: 'md' // margin-left: 16px
156
+ });
157
+
158
+ // Horizontal/Vertical
159
+ const box = css({
160
+ mx: 'auto', // margin-left + margin-right: auto (centering)
161
+ my: 'lg' // margin-top + margin-bottom: 24px
162
+ });
163
+ ```
164
+
165
+ ### Gap (Flexbox/Grid)
166
+
167
+ ```typescript
168
+ // Flex/Grid gap
169
+ const stack = css({
170
+ display: 'flex',
171
+ flexDirection: 'column',
172
+ gap: 'md' // 16px between all children
173
+ });
174
+
175
+ // Different horizontal/vertical gaps
176
+ const grid = css({
177
+ display: 'grid',
178
+ rowGap: 'lg', // 24px between rows
179
+ columnGap: 'md' // 16px between columns
180
+ });
181
+ ```
182
+
183
+ ## Responsive Spacing
184
+
185
+ Use different spacing values at different breakpoints:
186
+
187
+ ```typescript
188
+ const container = css({
189
+ // Mobile: 16px padding
190
+ // Desktop: 32px padding
191
+ p: { base: 'md', lg: 'xl' }
192
+ });
193
+
194
+ const section = css({
195
+ // Mobile: 24px margin
196
+ // Desktop: 48px margin
197
+ mb: { base: 'lg', lg: 'xxl' }
198
+ });
199
+ ```
200
+
201
+ ## What NOT to Do
202
+
203
+ ```typescript
204
+ // ❌ NEVER use arbitrary pixel values
205
+ const wrong = css({ padding: '17px' });
206
+ const wrong = css({ margin: '13px' });
207
+
208
+ // ❌ NEVER use rem/em values directly
209
+ const wrong = css({ padding: '1.5rem' });
210
+
211
+ // ❌ NEVER use non-token spacing
212
+ const wrong = css({ gap: '20px' });
213
+
214
+ // ✅ ALWAYS use spacing tokens
215
+ const correct = css({ p: 'md', gap: 'lg' });
216
+ ```
217
+
218
+ ## Special Cases
219
+
220
+ ### Zero Spacing
221
+ ```typescript
222
+ // Reset spacing
223
+ const reset = css({
224
+ m: 'none', // margin: 0
225
+ p: 'none' // padding: 0
226
+ });
227
+ ```
228
+
229
+ ### Negative Margins
230
+ ```typescript
231
+ // Negative margins (use sparingly)
232
+ const overlap = css({
233
+ mt: 'calc(var(--spacing-md) * -1)' // -16px
234
+ });
235
+ ```
236
+
237
+ ### Custom Spacing (Rare)
238
+ Only use custom spacing when absolutely necessary and none of the tokens fit:
239
+
240
+ ```typescript
241
+ // Last resort - custom spacing
242
+ const custom = css({
243
+ padding: 'calc(var(--spacing-sm) + var(--spacing-xs))' // 8px + 4px = 12px
244
+ });
245
+ ```
246
+
247
+ ## Accessibility
248
+
249
+ Proper spacing improves:
250
+ - **Touch targets**: Minimum 44x44px (use appropriate padding)
251
+ - **Readability**: Adequate spacing between text blocks
252
+ - **Scannability**: Clear visual separation between sections
253
+ - **Focus indicators**: Enough space for focus outlines
254
+
255
+ ### Minimum Touch Target Spacing
256
+
257
+ ```typescript
258
+ // ✅ Adequate touch target (Button component handles this)
259
+ const button = css({
260
+ minHeight: '44px',
261
+ px: 'md',
262
+ py: 'sm'
263
+ });
264
+
265
+ // ✅ Spacing between interactive elements
266
+ const buttonGroup = css({
267
+ display: 'flex',
268
+ gap: 'sm' // Minimum 8px between buttons
269
+ });
270
+ ```
271
+
272
+ ## Best Practices
273
+
274
+ 1. **Prefer fewer, larger gaps** over many small gaps
275
+ 2. **Use consistent spacing** within similar components
276
+ 3. **Increase spacing** for more important separations
277
+ 4. **Follow the 8px grid** whenever possible
278
+ 5. **Test on mobile** to ensure adequate touch targets
279
+ 6. **Use responsive spacing** for better mobile/desktop experiences
280
+
281
+ ## Common Spacing Mistakes
282
+
283
+ | ❌ Wrong | ✅ Right | Why |
284
+ |---------|---------|-----|
285
+ | `gap: 'xs'` for cards | `gap: 'lg'` | Cards need breathing room |
286
+ | `p: 'xxxl'` for button | `px: 'md', py: 'sm'` | Too much padding overwhelms |
287
+ | `mb: 'sm'` for sections | `mb: 'xl'` or `'xxl'` | Sections need clear separation |
288
+ | `padding: '20px'` | `p: 'lg'` | Use tokens, not arbitrary values |
289
+ | `gap: 'lg'` between icons | `gap: 'xs'` or `'sm'` | Icons are small, need less space |