@discourser/design-system 0.3.0 → 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.
@@ -0,0 +1,88 @@
1
+ # Discourser Design System Guidelines
2
+
3
+ This project uses the `@discourser/design-system` package, a Material Design 3 implementation built with Panda CSS and Ark UI.
4
+
5
+ ## IMPORTANT: Always Read These First
6
+
7
+ Before writing any code, follow these steps IN ORDER:
8
+
9
+ ### Step 1: Read Overview Files (REQUIRED)
10
+ Read ALL files with a name that starts with "overview-":
11
+ - `overview-components.md` - Available components and usage patterns
12
+
13
+ ### Step 2: Read Design Token Files (REQUIRED)
14
+ Read ALL files in the `design-tokens/` folder:
15
+ - `design-tokens/colors.md`
16
+ - `design-tokens/typography.md`
17
+ - `design-tokens/spacing.md`
18
+ - `design-tokens/elevation.md`
19
+
20
+ ### Step 3: Plan Components Needed (REQUIRED)
21
+ Identify which components you need to use.
22
+
23
+ ### Step 4: Read Component Guidelines BEFORE Using Components (REQUIRED)
24
+ BEFORE using ANY component, you MUST read its guidelines file first:
25
+ - Using Button? → Read `components/button.md` FIRST
26
+ - Using Dialog? → Read `components/dialog.md` FIRST
27
+ - Using Input? → Read `components/input.md` FIRST
28
+ - Using Card? → Read `components/card.md` FIRST
29
+ - Using IconButton? → Read `components/icon-button.md` FIRST
30
+ - Using Switch? → Read `components/switch.md` FIRST
31
+
32
+ DO NOT write code using a component until you have read its specific guidelines.
33
+
34
+ ## Core Principles
35
+
36
+ - **Always prefer design system components** over native HTML elements
37
+ - **Use semantic tokens** (e.g., `primary`, `onPrimary`) not raw colors
38
+ - **Follow M3 patterns** for variants, sizing, and state layers
39
+ - **Do not override styles** unless absolutely necessary
40
+ - **Never use inline styles** with raw color values
41
+
42
+ ## Package Installation
43
+
44
+ ```bash
45
+ npm install @discourser/design-system react react-dom
46
+ ```
47
+
48
+ ## Package Imports
49
+
50
+ ```typescript
51
+ // Components
52
+ import { Button, Card, Dialog, Input, IconButton, Switch } from '@discourser/design-system';
53
+
54
+ // For advanced styling (use sparingly)
55
+ import { css } from '@discourser/design-system/styled-system/css';
56
+ import { button } from '@discourser/design-system/styled-system/recipes';
57
+ ```
58
+
59
+ ## Quick Reference
60
+
61
+ | Component | Variants | Sizes | Guidelines |
62
+ |-----------|----------|-------|------------|
63
+ | Button | filled, outlined, text, elevated, tonal | sm, md, lg | `components/button.md` |
64
+ | Card | elevated, filled, outlined | - | `components/card.md` |
65
+ | IconButton | standard, filled, tonal, outlined | sm, md, lg | `components/icon-button.md` |
66
+ | Input | filled, outlined | sm, md | `components/input.md` |
67
+ | Dialog | - | sm, md, lg, fullscreen | `components/dialog.md` |
68
+ | Switch | - | sm, md | `components/switch.md` |
69
+
70
+ ## Theme Support
71
+
72
+ The design system supports light and dark themes. The theme is controlled by the `data-theme` attribute on a parent element (typically `html` or `body`):
73
+
74
+ ```tsx
75
+ // Light theme (default)
76
+ <html data-theme="light">
77
+
78
+ // Dark theme
79
+ <html data-theme="dark">
80
+ ```
81
+
82
+ All semantic color tokens automatically adapt to the current theme.
83
+
84
+ ## Getting Help
85
+
86
+ For questions or issues, visit:
87
+ - GitHub: https://github.com/Tasty-Maker-Studio/Discourser-Design-System
88
+ - Documentation: Read the overview and component-specific guidelines in this folder
@@ -0,0 +1,314 @@
1
+ # Button
2
+
3
+ **Purpose:** Primary interactive element for user actions following Material Design 3 patterns.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { Button } from '@discourser/design-system';
9
+ ```
10
+
11
+ ## Variants
12
+
13
+ The Button component supports 5 Material Design 3 variants, each with specific use cases:
14
+
15
+ | Variant | Visual Style | Usage | When to Use |
16
+ |---------|-------------|-------|-------------|
17
+ | `filled` | Solid background with primary color | Primary actions | Submit forms, confirm dialogs, main CTAs |
18
+ | `outlined` | Transparent background with border | Secondary actions | Cancel buttons, back navigation, alternative options |
19
+ | `text` | Transparent background, no border | Tertiary actions | Links, less prominent actions, dialog actions |
20
+ | `elevated` | Elevated surface with subtle shadow | Floating actions | FAB-like buttons, actions that need emphasis but not primary color |
21
+ | `tonal` | Filled with secondary container color | Medium emphasis | Secondary CTAs, soft highlights, supportive actions |
22
+
23
+ ### Visual Characteristics
24
+
25
+ - **filled**: Primary color background, white text, slight shadow on hover
26
+ - **outlined**: Transparent background, primary color text, 1px outline border
27
+ - **text**: Transparent background, primary color text, no border
28
+ - **elevated**: Surface container background, primary color text, level 1 shadow
29
+ - **tonal**: Secondary container background, on-secondary-container text
30
+
31
+ ## Sizes
32
+
33
+ | Size | Height | Padding (Horizontal) | Font Size | Usage |
34
+ |------|--------|---------------------|-----------|-------|
35
+ | `sm` | 32px | 16px (md) | labelMedium | Compact UI, dense layouts, small dialogs |
36
+ | `md` | 40px | 24px (lg) | labelLarge | Default, most use cases |
37
+ | `lg` | 48px | 32px (xl) | labelLarge | Touch targets, mobile emphasis, hero sections |
38
+
39
+ **Recommendation:** Use `md` for most cases. Use `lg` for mobile-first designs or prominent CTAs.
40
+
41
+ ## Props
42
+
43
+ | Prop | Type | Default | Description |
44
+ |------|------|---------|-------------|
45
+ | `variant` | `'filled' \| 'outlined' \| 'text' \| 'elevated' \| 'tonal'` | `'filled'` | Visual style variant |
46
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size |
47
+ | `leftIcon` | `ReactNode` | - | Icon or element before button text |
48
+ | `rightIcon` | `ReactNode` | - | Icon or element after button text |
49
+ | `disabled` | `boolean` | `false` | Disable button interaction |
50
+ | `onClick` | `(event: MouseEvent) => void` | - | Click handler |
51
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | HTML button type |
52
+ | `className` | `string` | - | Additional CSS classes (use sparingly) |
53
+ | `children` | `ReactNode` | Required | Button text content |
54
+
55
+ **Note:** Button extends `ButtonHTMLAttributes<HTMLButtonElement>`, so all standard HTML button attributes are supported.
56
+
57
+ ## Examples
58
+
59
+ ### Basic Usage
60
+
61
+ ```typescript
62
+ // Primary action (default)
63
+ <Button>Submit</Button>
64
+
65
+ // Secondary action
66
+ <Button variant="outlined">Cancel</Button>
67
+
68
+ // Tertiary action
69
+ <Button variant="text">Learn More</Button>
70
+
71
+ // Medium emphasis
72
+ <Button variant="tonal">Save Draft</Button>
73
+
74
+ // Floating action
75
+ <Button variant="elevated">Create</Button>
76
+ ```
77
+
78
+ ### With Icons
79
+
80
+ ```typescript
81
+ import { PlusIcon, ArrowRightIcon } from 'your-icon-library';
82
+
83
+ // Icon on left
84
+ <Button leftIcon={<PlusIcon />}>
85
+ Add Item
86
+ </Button>
87
+
88
+ // Icon on right
89
+ <Button rightIcon={<ArrowRightIcon />}>
90
+ Continue
91
+ </Button>
92
+
93
+ // Both icons (rare, but supported)
94
+ <Button leftIcon={<PlusIcon />} rightIcon={<ArrowRightIcon />}>
95
+ Add and Continue
96
+ </Button>
97
+ ```
98
+
99
+ ### Sizes
100
+
101
+ ```typescript
102
+ // Small button (compact UI)
103
+ <Button size="sm">Save</Button>
104
+
105
+ // Default size
106
+ <Button size="md">Submit</Button>
107
+
108
+ // Large button (mobile-friendly)
109
+ <Button size="lg">Get Started</Button>
110
+ ```
111
+
112
+ ### Form Integration
113
+
114
+ ```typescript
115
+ // Submit button
116
+ <form onSubmit={handleSubmit}>
117
+ <Input label="Email" />
118
+ <Button type="submit">Sign Up</Button>
119
+ </form>
120
+
121
+ // Reset button
122
+ <Button type="reset" variant="text">Reset Form</Button>
123
+ ```
124
+
125
+ ### Disabled State
126
+
127
+ ```typescript
128
+ const [isSubmitting, setIsSubmitting] = useState(false);
129
+
130
+ <Button disabled={isSubmitting}>
131
+ {isSubmitting ? 'Submitting...' : 'Submit'}
132
+ </Button>
133
+ ```
134
+
135
+ ### Event Handling
136
+
137
+ ```typescript
138
+ const handleClick = () => {
139
+ console.log('Button clicked!');
140
+ };
141
+
142
+ <Button onClick={handleClick}>Click Me</Button>
143
+
144
+ // With event parameter
145
+ <Button onClick={(e) => {
146
+ e.preventDefault();
147
+ handleSubmit();
148
+ }}>
149
+ Submit
150
+ </Button>
151
+ ```
152
+
153
+ ## Common Patterns
154
+
155
+ ### Primary/Secondary Button Group
156
+
157
+ ```typescript
158
+ <div className={css({ display: 'flex', gap: 'sm' })}>
159
+ <Button variant="outlined">Cancel</Button>
160
+ <Button variant="filled">Confirm</Button>
161
+ </div>
162
+ ```
163
+
164
+ ### Dialog Actions
165
+
166
+ ```typescript
167
+ <Dialog.Content>
168
+ <Dialog.Title>Confirm Action</Dialog.Title>
169
+ <Dialog.Description>Are you sure you want to proceed?</Dialog.Description>
170
+
171
+ <div className={css({ display: 'flex', gap: 'sm', justifyContent: 'flex-end' })}>
172
+ <Button variant="text">Cancel</Button>
173
+ <Button variant="filled">Confirm</Button>
174
+ </div>
175
+ </Dialog.Content>
176
+ ```
177
+
178
+ ### Loading State
179
+
180
+ ```typescript
181
+ const [loading, setLoading] = useState(false);
182
+
183
+ <Button disabled={loading}>
184
+ {loading && <Spinner />}
185
+ {loading ? 'Loading...' : 'Submit'}
186
+ </Button>
187
+ ```
188
+
189
+ ## DO NOT
190
+
191
+ ```typescript
192
+ // ❌ Don't use native button element
193
+ <button className="...">Submit</button> // Use <Button> instead
194
+
195
+ // ❌ Don't override button styles with inline styles
196
+ <Button style={{ backgroundColor: 'red' }}>Delete</Button>
197
+
198
+ // ❌ Don't use multiple filled buttons next to each other (unclear hierarchy)
199
+ <div>
200
+ <Button variant="filled">Save</Button>
201
+ <Button variant="filled">Delete</Button> // Use outlined or text instead
202
+ </div>
203
+
204
+ // ❌ Don't use button for navigation (use <a> tag or Next.js Link)
205
+ <Button onClick={() => router.push('/page')}>Go to Page</Button> // Bad
206
+
207
+ // ❌ Don't omit text for accessibility (use IconButton instead)
208
+ <Button><TrashIcon /></Button> // Use <IconButton> for icon-only
209
+
210
+ // ✅ Use IconButton for icon-only actions
211
+ <IconButton aria-label="Delete"><TrashIcon /></IconButton>
212
+ ```
213
+
214
+ ## Accessibility
215
+
216
+ The Button component follows WCAG 2.1 Level AA standards:
217
+
218
+ - **Keyboard Navigation**: Focusable via Tab key, activates with Enter/Space
219
+ - **Focus Indicator**: 2px outline on focus-visible
220
+ - **Disabled State**: Uses `disabled` attribute, opacity 0.38, pointer-events: none
221
+ - **Touch Target**: Minimum 44x44px (use `md` or `lg` size)
222
+ - **Color Contrast**: All variants meet 4.5:1 contrast ratio
223
+
224
+ ### Accessibility Best Practices
225
+
226
+ ```typescript
227
+ // ✅ Always provide meaningful text
228
+ <Button>Submit Form</Button>
229
+
230
+ // ✅ Use aria-label for dynamic content
231
+ <Button aria-label={`Delete ${itemName}`}>Delete</Button>
232
+
233
+ // ✅ Indicate loading state
234
+ <Button aria-busy={loading} disabled={loading}>
235
+ {loading ? 'Loading...' : 'Submit'}
236
+ </Button>
237
+
238
+ // ✅ Use proper button types
239
+ <Button type="submit">Submit</Button>
240
+ <Button type="reset">Reset</Button>
241
+ ```
242
+
243
+ ## Variant Selection Guide
244
+
245
+ | Scenario | Recommended Variant | Reasoning |
246
+ |----------|-------------------|-----------|
247
+ | Form submission | `filled` | Primary action, needs highest emphasis |
248
+ | Cancel/Back | `outlined` or `text` | Secondary action, lower emphasis |
249
+ | Dialog confirmation | `filled` | Primary action in dialog |
250
+ | Dialog dismiss | `text` | Tertiary action, minimal emphasis |
251
+ | Save draft | `tonal` | Medium emphasis, not primary action |
252
+ | Delete/Destructive | `filled` or `tonal` | High attention, but consider error colors |
253
+ | Filter/Sort | `text` or `outlined` | Lower emphasis, frequent use |
254
+ | Floating action button | `elevated` | Needs to float above content |
255
+ | Link-like actions | `text` | Minimal emphasis, inline with text |
256
+
257
+ ## State Behaviors
258
+
259
+ | State | Visual Change | Behavior |
260
+ |-------|---------------|----------|
261
+ | **Hover** | Opacity change or shadow | `filled`: 92% opacity + level1 shadow<br />`outlined`: 8% primary background<br />`elevated`: Increase shadow to level2 |
262
+ | **Active** | Further opacity/shadow change | `filled`: 88% opacity<br />`outlined`: 12% primary background |
263
+ | **Focus** | 2px outline | Primary color outline, 2px offset |
264
+ | **Disabled** | 38% opacity, no interaction | Cannot be clicked, greyed out appearance |
265
+
266
+ ## Responsive Considerations
267
+
268
+ ```typescript
269
+ // Mobile-first: Use larger buttons for touch
270
+ <Button size="lg">Submit</Button>
271
+
272
+ // Desktop: Can use smaller sizes
273
+ <Button size={{ base: 'lg', md: 'md' }}>Submit</Button>
274
+
275
+ // Responsive button group
276
+ <div className={css({
277
+ display: 'flex',
278
+ flexDirection: { base: 'column', md: 'row' },
279
+ gap: 'sm'
280
+ })}>
281
+ <Button variant="outlined">Cancel</Button>
282
+ <Button variant="filled">Confirm</Button>
283
+ </div>
284
+ ```
285
+
286
+ ## Testing
287
+
288
+ When testing Button components:
289
+
290
+ ```typescript
291
+ import { render, screen } from '@testing-library/react';
292
+ import userEvent from '@testing-library/user-event';
293
+
294
+ test('button handles click events', async () => {
295
+ const handleClick = vi.fn();
296
+ render(<Button onClick={handleClick}>Click Me</Button>);
297
+
298
+ const button = screen.getByRole('button', { name: 'Click Me' });
299
+ await userEvent.click(button);
300
+
301
+ expect(handleClick).toHaveBeenCalledOnce();
302
+ });
303
+
304
+ test('disabled button cannot be clicked', async () => {
305
+ const handleClick = vi.fn();
306
+ render(<Button disabled onClick={handleClick}>Click Me</Button>);
307
+
308
+ const button = screen.getByRole('button', { name: 'Click Me' });
309
+ await userEvent.click(button);
310
+
311
+ expect(handleClick).not.toHaveBeenCalled();
312
+ expect(button).toBeDisabled();
313
+ });
314
+ ```