@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.
Files changed (36) hide show
  1. package/README.md +12 -4
  2. package/dist/styles.css +5126 -0
  3. package/guidelines/Guidelines.md +92 -41
  4. package/guidelines/components/accordion.md +732 -0
  5. package/guidelines/components/avatar.md +1015 -0
  6. package/guidelines/components/badge.md +728 -0
  7. package/guidelines/components/button.md +75 -40
  8. package/guidelines/components/card.md +84 -25
  9. package/guidelines/components/checkbox.md +671 -0
  10. package/guidelines/components/dialog.md +619 -31
  11. package/guidelines/components/drawer.md +1616 -0
  12. package/guidelines/components/heading.md +576 -0
  13. package/guidelines/components/icon-button.md +92 -37
  14. package/guidelines/components/input-addon.md +685 -0
  15. package/guidelines/components/input-group.md +830 -0
  16. package/guidelines/components/input.md +92 -37
  17. package/guidelines/components/popover.md +1271 -0
  18. package/guidelines/components/progress.md +836 -0
  19. package/guidelines/components/radio-group.md +852 -0
  20. package/guidelines/components/select.md +1662 -0
  21. package/guidelines/components/skeleton.md +802 -0
  22. package/guidelines/components/slider.md +911 -0
  23. package/guidelines/components/spinner.md +783 -0
  24. package/guidelines/components/switch.md +105 -38
  25. package/guidelines/components/tabs.md +1488 -0
  26. package/guidelines/components/textarea.md +495 -0
  27. package/guidelines/components/toast.md +784 -0
  28. package/guidelines/components/tooltip.md +912 -0
  29. package/guidelines/design-tokens/colors.md +309 -72
  30. package/guidelines/design-tokens/elevation.md +615 -45
  31. package/guidelines/design-tokens/spacing.md +654 -74
  32. package/guidelines/design-tokens/typography.md +432 -50
  33. package/guidelines/overview-components.md +60 -8
  34. package/guidelines/overview-imports.md +314 -0
  35. package/guidelines/overview-patterns.md +3852 -0
  36. package/package.json +4 -2
@@ -0,0 +1,728 @@
1
+ # Badge
2
+
3
+ **Purpose:** Compact visual indicator for displaying status, labels, counts, or categories following Material Design 3 patterns.
4
+
5
+ ## When to Use This Component
6
+
7
+ Use Badge when you need to **display compact visual indicators for status, counts, labels, or categories** as non-interactive elements.
8
+
9
+ ### Decision Tree
10
+
11
+ | Scenario | Use Badge? | Alternative | Reasoning |
12
+ | ------------------------------------------------ | ---------- | -------------- | ---------------------------------------------- |
13
+ | Displaying status labels (Active, Pending, etc.) | ✅ Yes | - | Badge provides clear visual categorization |
14
+ | Notification counts on icons or buttons | ✅ Yes | - | Perfect for unread messages, alerts |
15
+ | Category tags or labels | ✅ Yes | - | Compact visual grouping |
16
+ | Clickable filters or selections | ❌ No | Button or Chip | Badge is non-interactive |
17
+ | Primary call-to-action | ❌ No | Button | Button is designed for actions |
18
+ | User profile images | ❌ No | Avatar | Avatar is specifically for user representation |
19
+
20
+ ### Component Comparison
21
+
22
+ ```typescript
23
+ // ✅ Badge - Status indicator
24
+ <Stack gap="2" direction="row">
25
+ <Badge colorPalette="success">Active</Badge>
26
+ <Badge colorPalette="warning">Pending</Badge>
27
+ <Badge colorPalette="error">Expired</Badge>
28
+ </Stack>
29
+
30
+ // ❌ Don't use Badge for interactive elements - Use Button
31
+ <Badge onClick={handleClick} colorPalette="primary">
32
+ Click me
33
+ </Badge>
34
+
35
+ // ✅ Better: Use Button for clickable actions
36
+ <Button size="sm" variant="outlined">
37
+ Click me
38
+ </Button>
39
+
40
+ // ❌ Don't use Badge for user avatars - Use Avatar
41
+ <Badge colorPalette="primary" size="2xl">
42
+ JD
43
+ </Badge>
44
+
45
+ // ✅ Better: Use Avatar for user representation
46
+ <Avatar.Root colorPalette="primary" size="md">
47
+ <Avatar.Fallback name="John Doe" />
48
+ <Avatar.Image src="/avatar.jpg" alt="John Doe" />
49
+ </Avatar.Root>
50
+
51
+ // ✅ Badge - Notification count on icon
52
+ <Box position="relative">
53
+ <IconButton><BellIcon /></IconButton>
54
+ <Badge
55
+ position="absolute"
56
+ top="-1"
57
+ right="-1"
58
+ colorPalette="error"
59
+ size="sm"
60
+ >
61
+ 5
62
+ </Badge>
63
+ </Box>
64
+ ```
65
+
66
+ ## Import
67
+
68
+ ```typescript
69
+ import { Badge } from '@discourser/design-system';
70
+ ```
71
+
72
+ ## Variants
73
+
74
+ The Badge component supports 4 visual variants, each with specific use cases:
75
+
76
+ | Variant | Visual Style | Usage | When to Use |
77
+ | --------- | -------------------------------------------- | ------------------------- | -------------------------------------------------- |
78
+ | `subtle` | Light background with colored text | Default status indicators | General labels, categories, non-critical status |
79
+ | `solid` | Solid color background with contrasting text | High emphasis indicators | Important status, featured items, primary labels |
80
+ | `surface` | Surface background with border | Outlined status | Secondary emphasis, grouped badges, neutral labels |
81
+ | `outline` | Transparent background with border | Minimal emphasis | Tertiary labels, tags, filters |
82
+
83
+ ### Visual Characteristics
84
+
85
+ - **subtle**: Uses `colorPalette.subtle.bg` background with `colorPalette.subtle.fg` text
86
+ - **solid**: Uses `colorPalette.solid.bg` background with `colorPalette.solid.fg` text (highest contrast)
87
+ - **surface**: Uses `colorPalette.surface.bg` background with 1px border and `colorPalette.surface.fg` text
88
+ - **outline**: Transparent background with 1px `colorPalette.outline.border` and `colorPalette.outline.fg` text
89
+
90
+ ## Sizes
91
+
92
+ | Size | Height | Padding (Horizontal) | Font Size | Icon Size | Gap | Usage |
93
+ | ----- | ---------- | -------------------- | --------- | ---------- | --------- | ------------------------------------- |
94
+ | `sm` | 18px (4.5) | 6px (1.5) | xs | 10px (2.5) | 2px (0.5) | Compact UI, dense tables, inline text |
95
+ | `md` | 20px (5) | 8px (2) | xs | 12px (3) | 4px (1) | Default, most use cases |
96
+ | `lg` | 22px (5.5) | 10px (2.5) | xs | 14px (3.5) | 4px (1) | Prominent labels, touch targets |
97
+ | `xl` | 24px (6) | 10px (2.5) | sm | 16px (4) | 6px (1.5) | Large displays, featured items |
98
+ | `2xl` | 28px (7) | 12px (3) | md | 18px (4.5) | 6px (1.5) | Hero sections, marketing emphasis |
99
+
100
+ **Recommendation:** Use `md` for most cases. Use `sm` for dense layouts or inline badges. Use `lg` or larger for touch interfaces.
101
+
102
+ ## Props
103
+
104
+ | Prop | Type | Default | Description |
105
+ | ----------- | ----------------------------------------------- | ---------- | ------------------------------------------- |
106
+ | `variant` | `'subtle' \| 'solid' \| 'surface' \| 'outline'` | `'subtle'` | Visual style variant |
107
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | `'md'` | Badge size |
108
+ | `children` | `ReactNode` | Required | Badge content (text, icons, or combination) |
109
+ | `className` | `string` | - | Additional CSS classes (use sparingly) |
110
+
111
+ **Note:** Badge extends `HTMLAttributes<HTMLDivElement>`, so all standard HTML div attributes are supported.
112
+
113
+ ## Color Palettes
114
+
115
+ Badges support dynamic color palettes using Panda CSS color palette system:
116
+
117
+ ```typescript
118
+ // Use colorPalette prop to change badge colors
119
+ <Badge colorPalette="primary">Primary</Badge>
120
+ <Badge colorPalette="success">Success</Badge>
121
+ <Badge colorPalette="warning">Warning</Badge>
122
+ <Badge colorPalette="danger">Danger</Badge>
123
+ <Badge colorPalette="info">Info</Badge>
124
+ <Badge colorPalette="neutral">Neutral</Badge>
125
+ ```
126
+
127
+ ## Examples
128
+
129
+ ### Basic Usage
130
+
131
+ ```typescript
132
+ // Default badge (subtle variant)
133
+ <Badge>New</Badge>
134
+
135
+ // Different variants
136
+ <Badge variant="subtle">Pending</Badge>
137
+ <Badge variant="solid">Active</Badge>
138
+ <Badge variant="surface">Draft</Badge>
139
+ <Badge variant="outline">Optional</Badge>
140
+ ```
141
+
142
+ ### Semantic Status Badges
143
+
144
+ ```typescript
145
+ // Success status
146
+ <Badge colorPalette="success" variant="subtle">
147
+ Completed
148
+ </Badge>
149
+
150
+ // Warning status
151
+ <Badge colorPalette="warning" variant="solid">
152
+ Attention Required
153
+ </Badge>
154
+
155
+ // Error status
156
+ <Badge colorPalette="danger" variant="surface">
157
+ Failed
158
+ </Badge>
159
+
160
+ // Info status
161
+ <Badge colorPalette="info" variant="outline">
162
+ Information
163
+ </Badge>
164
+ ```
165
+
166
+ ### Different Sizes
167
+
168
+ ```typescript
169
+ // Small badges for compact layouts
170
+ <Badge size="sm">Small</Badge>
171
+
172
+ // Default size
173
+ <Badge size="md">Medium</Badge>
174
+
175
+ // Large badges for emphasis
176
+ <Badge size="lg">Large</Badge>
177
+
178
+ // Extra large for hero sections
179
+ <Badge size="xl">Extra Large</Badge>
180
+
181
+ // Maximum size
182
+ <Badge size="2xl">Huge</Badge>
183
+ ```
184
+
185
+ ### With Icons
186
+
187
+ ```typescript
188
+ import { CheckIcon, ClockIcon, XIcon, InfoIcon } from 'your-icon-library';
189
+
190
+ // Icon before text
191
+ <Badge>
192
+ <CheckIcon /> Verified
193
+ </Badge>
194
+
195
+ // Icon after text
196
+ <Badge>
197
+ In Progress <ClockIcon />
198
+ </Badge>
199
+
200
+ // Icon only (ensure accessible label)
201
+ <Badge aria-label="Completed">
202
+ <CheckIcon />
203
+ </Badge>
204
+
205
+ // Multiple elements
206
+ <Badge colorPalette="success">
207
+ <CheckIcon />
208
+ <span>Success</span>
209
+ </Badge>
210
+ ```
211
+
212
+ ### Counts and Numbers
213
+
214
+ ```typescript
215
+ // Notification count
216
+ <Badge variant="solid" colorPalette="danger">
217
+ 5
218
+ </Badge>
219
+
220
+ // Quantity indicator
221
+ <Badge variant="subtle">
222
+ 12 items
223
+ </Badge>
224
+
225
+ // Numeric status
226
+ <Badge colorPalette="primary">
227
+ +99
228
+ </Badge>
229
+
230
+ // Percentage
231
+ <Badge variant="surface" colorPalette="success">
232
+ +15%
233
+ </Badge>
234
+ ```
235
+
236
+ ### Category Labels
237
+
238
+ ```typescript
239
+ // Product categories
240
+ <Badge colorPalette="primary">Technology</Badge>
241
+ <Badge colorPalette="purple">Design</Badge>
242
+ <Badge colorPalette="green">Marketing</Badge>
243
+
244
+ // Priority levels
245
+ <Badge variant="solid" colorPalette="danger">High Priority</Badge>
246
+ <Badge variant="subtle" colorPalette="warning">Medium Priority</Badge>
247
+ <Badge variant="outline" colorPalette="neutral">Low Priority</Badge>
248
+ ```
249
+
250
+ ### Tag Groups
251
+
252
+ ```typescript
253
+ // Multiple tags
254
+ <div className={css({ display: 'flex', gap: '2', flexWrap: 'wrap' })}>
255
+ <Badge variant="outline">JavaScript</Badge>
256
+ <Badge variant="outline">React</Badge>
257
+ <Badge variant="outline">TypeScript</Badge>
258
+ <Badge variant="outline">Node.js</Badge>
259
+ </div>
260
+
261
+ // Removable tags
262
+ <div className={css({ display: 'flex', gap: '2' })}>
263
+ <Badge variant="surface">
264
+ Design
265
+ <button aria-label="Remove Design tag">
266
+ <XIcon />
267
+ </button>
268
+ </Badge>
269
+ <Badge variant="surface">
270
+ Development
271
+ <button aria-label="Remove Development tag">
272
+ <XIcon />
273
+ </button>
274
+ </Badge>
275
+ </div>
276
+ ```
277
+
278
+ ## Common Patterns
279
+
280
+ ### Status Indicators in Lists
281
+
282
+ ```typescript
283
+ // List with status badges
284
+ <ul>
285
+ {items.map(item => (
286
+ <li key={item.id} className={css({ display: 'flex', alignItems: 'center', gap: '3' })}>
287
+ <span>{item.name}</span>
288
+ <Badge
289
+ colorPalette={item.status === 'active' ? 'success' : 'neutral'}
290
+ variant="subtle"
291
+ >
292
+ {item.status}
293
+ </Badge>
294
+ </li>
295
+ ))}
296
+ </ul>
297
+ ```
298
+
299
+ ### Notification Badge
300
+
301
+ ```typescript
302
+ // Icon with notification count
303
+ <div className={css({ position: 'relative', display: 'inline-block' })}>
304
+ <IconButton aria-label="Notifications">
305
+ <BellIcon />
306
+ </IconButton>
307
+ <Badge
308
+ variant="solid"
309
+ colorPalette="danger"
310
+ size="sm"
311
+ className={css({ position: 'absolute', top: '-1', right: '-1' })}
312
+ >
313
+ 3
314
+ </Badge>
315
+ </div>
316
+ ```
317
+
318
+ ### Card with Badge
319
+
320
+ ```typescript
321
+ // Card featuring a badge
322
+ <Card>
323
+ <div className={css({ display: 'flex', justifyContent: 'space-between', alignItems: 'start' })}>
324
+ <Heading as="h3" size="md">Premium Plan</Heading>
325
+ <Badge variant="solid" colorPalette="primary">
326
+ Popular
327
+ </Badge>
328
+ </div>
329
+ <p>Best value for growing teams</p>
330
+ <Button>Choose Plan</Button>
331
+ </Card>
332
+ ```
333
+
334
+ ### Table Cell Badges
335
+
336
+ ```typescript
337
+ // Status column in table
338
+ <table>
339
+ <thead>
340
+ <tr>
341
+ <th>Name</th>
342
+ <th>Status</th>
343
+ <th>Priority</th>
344
+ </tr>
345
+ </thead>
346
+ <tbody>
347
+ {rows.map(row => (
348
+ <tr key={row.id}>
349
+ <td>{row.name}</td>
350
+ <td>
351
+ <Badge
352
+ variant="subtle"
353
+ colorPalette={row.status === 'complete' ? 'success' : 'warning'}
354
+ size="sm"
355
+ >
356
+ {row.status}
357
+ </Badge>
358
+ </td>
359
+ <td>
360
+ <Badge size="sm" variant="outline">
361
+ {row.priority}
362
+ </Badge>
363
+ </td>
364
+ </tr>
365
+ ))}
366
+ </tbody>
367
+ </table>
368
+ ```
369
+
370
+ ### User Profile Badges
371
+
372
+ ```typescript
373
+ // Profile with role badges
374
+ <div className={css({ display: 'flex', alignItems: 'center', gap: '3' })}>
375
+ <Avatar src={user.avatar} />
376
+ <div>
377
+ <div className={css({ display: 'flex', alignItems: 'center', gap: '2' })}>
378
+ <span className={css({ fontWeight: 'bold' })}>{user.name}</span>
379
+ <Badge variant="solid" colorPalette="primary" size="sm">
380
+ Admin
381
+ </Badge>
382
+ {user.isVerified && (
383
+ <Badge variant="subtle" colorPalette="success" size="sm">
384
+ <CheckIcon /> Verified
385
+ </Badge>
386
+ )}
387
+ </div>
388
+ <p className={css({ fontSize: 'sm', opacity: 0.7 })}>{user.email}</p>
389
+ </div>
390
+ </div>
391
+ ```
392
+
393
+ ### Filter Tags
394
+
395
+ ```typescript
396
+ // Active filters with badges
397
+ <div>
398
+ <span className={css({ fontWeight: 'medium', mr: '3' })}>Active Filters:</span>
399
+ <div className={css({ display: 'inline-flex', gap: '2', flexWrap: 'wrap' })}>
400
+ {activeFilters.map(filter => (
401
+ <Badge
402
+ key={filter.id}
403
+ variant="surface"
404
+ colorPalette="primary"
405
+ >
406
+ {filter.label}
407
+ <button
408
+ onClick={() => removeFilter(filter.id)}
409
+ aria-label={`Remove ${filter.label} filter`}
410
+ className={css({ ml: '1', cursor: 'pointer' })}
411
+ >
412
+ <XIcon />
413
+ </button>
414
+ </Badge>
415
+ ))}
416
+ </div>
417
+ </div>
418
+ ```
419
+
420
+ ### Feature Badges
421
+
422
+ ```typescript
423
+ // Product features with badges
424
+ <div className={css({ display: 'grid', gridTemplateColumns: '2', gap: '4' })}>
425
+ <div>
426
+ <Badge variant="solid" colorPalette="success" size="sm">
427
+ New
428
+ </Badge>
429
+ <Heading as="h3" size="md" className={css({ mt: '2' })}>
430
+ Dark Mode
431
+ </Heading>
432
+ <p>Beautiful dark theme for reduced eye strain</p>
433
+ </div>
434
+ <div>
435
+ <Badge variant="subtle" colorPalette="warning" size="sm">
436
+ Beta
437
+ </Badge>
438
+ <Heading as="h3" size="md" className={css({ mt: '2' })}>
439
+ AI Assistant
440
+ </Heading>
441
+ <p>Intelligent help powered by machine learning</p>
442
+ </div>
443
+ </div>
444
+ ```
445
+
446
+ ## DO NOT
447
+
448
+ ```typescript
449
+ // ❌ Don't use badges for interactive buttons
450
+ <Badge onClick={handleClick}>Click Me</Badge> // Use Button instead
451
+
452
+ // ❌ Don't overuse solid variant (reduces emphasis)
453
+ <div>
454
+ <Badge variant="solid">Tag 1</Badge>
455
+ <Badge variant="solid">Tag 2</Badge>
456
+ <Badge variant="solid">Tag 3</Badge> // Too much emphasis
457
+ </div>
458
+
459
+ // ❌ Don't use long text in badges
460
+ <Badge>This is a very long label that doesn't fit well in a badge</Badge>
461
+
462
+ // ❌ Don't mix too many color palettes
463
+ <div>
464
+ <Badge colorPalette="red">A</Badge>
465
+ <Badge colorPalette="blue">B</Badge>
466
+ <Badge colorPalette="green">C</Badge>
467
+ <Badge colorPalette="yellow">D</Badge> // Too chaotic
468
+ </div>
469
+
470
+ // ❌ Don't use badges as primary navigation
471
+ <Badge onClick={() => navigate('/page')}>Go to Page</Badge> // Use Link
472
+
473
+ // ❌ Don't forget accessible labels for icon-only badges
474
+ <Badge><CheckIcon /></Badge> // No text alternative
475
+
476
+ // ❌ Don't override semantic colors inappropriately
477
+ <Badge colorPalette="success">Error</Badge> // Misleading color
478
+
479
+ // ✅ Use appropriate variants for emphasis
480
+ <div className={css({ display: 'flex', gap: '2' })}>
481
+ <Badge variant="solid">Featured</Badge>
482
+ <Badge variant="subtle">Tag 1</Badge>
483
+ <Badge variant="subtle">Tag 2</Badge>
484
+ <Badge variant="outline">Optional</Badge>
485
+ </div>
486
+
487
+ // ✅ Keep badge text concise
488
+ <Badge>New</Badge>
489
+ <Badge>Beta</Badge>
490
+ <Badge>Coming Soon</Badge>
491
+
492
+ // ✅ Use consistent color palette
493
+ <div className={css({ display: 'flex', gap: '2' })}>
494
+ <Badge colorPalette="primary" variant="outline">JavaScript</Badge>
495
+ <Badge colorPalette="primary" variant="outline">React</Badge>
496
+ <Badge colorPalette="primary" variant="outline">TypeScript</Badge>
497
+ </div>
498
+
499
+ // ✅ Match colors to meaning
500
+ <Badge colorPalette="success">Completed</Badge>
501
+ <Badge colorPalette="danger">Failed</Badge>
502
+ <Badge colorPalette="warning">Pending</Badge>
503
+
504
+ // ✅ Always provide accessible labels
505
+ <Badge aria-label="Verified user">
506
+ <CheckIcon />
507
+ </Badge>
508
+ ```
509
+
510
+ ## Accessibility
511
+
512
+ The Badge component follows WCAG 2.1 Level AA standards:
513
+
514
+ - **Color Independence**: Don't rely solely on color to convey meaning
515
+ - **Text Alternative**: Provide text or aria-label for icon-only badges
516
+ - **Color Contrast**: All variants meet 4.5:1 contrast ratio
517
+ - **Non-interactive**: Badges are display elements, not interactive controls
518
+ - **Readable Text**: Minimum font size ensures legibility
519
+
520
+ ### Accessibility Best Practices
521
+
522
+ ```typescript
523
+ // ✅ Include text with icons
524
+ <Badge colorPalette="success">
525
+ <CheckIcon /> Verified
526
+ </Badge>
527
+
528
+ // ✅ Provide aria-label for icon-only badges
529
+ <Badge colorPalette="success" aria-label="Verified">
530
+ <CheckIcon />
531
+ </Badge>
532
+
533
+ // ✅ Use semantic meaning beyond color
534
+ <Badge colorPalette="danger">Failed - Try Again</Badge> // Text clarifies status
535
+
536
+ // ✅ Screen reader friendly counts
537
+ <Badge aria-label="5 unread notifications">5</Badge>
538
+
539
+ // ✅ Accessible removable badges
540
+ <Badge variant="surface">
541
+ Design
542
+ <button
543
+ aria-label="Remove Design tag"
544
+ onClick={() => removeTag('design')}
545
+ >
546
+ <XIcon />
547
+ </button>
548
+ </Badge>
549
+
550
+ // ✅ Status badges with clear meaning
551
+ <Badge
552
+ colorPalette="warning"
553
+ aria-label="Status: Pending approval"
554
+ >
555
+ Pending
556
+ </Badge>
557
+ ```
558
+
559
+ ### Screen Reader Considerations
560
+
561
+ ```typescript
562
+ // Badge content is read by screen readers
563
+ <Badge>New</Badge> // Announces: "New"
564
+
565
+ // Combine with context for clarity
566
+ <article aria-label="New feature: Dark mode">
567
+ <Badge colorPalette="primary">New</Badge>
568
+ <Heading as="h3" size="md">Dark Mode</Heading>
569
+ </article>
570
+
571
+ // Use aria-hidden for decorative badges
572
+ <Badge aria-hidden="true" size="sm">
573
+ <StarIcon />
574
+ </Badge>
575
+ <span className="sr-only">Featured item</span>
576
+ ```
577
+
578
+ ## Variant Selection Guide
579
+
580
+ | Scenario | Recommended Variant | Color Palette | Reasoning |
581
+ | ------------------ | ------------------- | --------------------------------- | ---------------------------------------- |
582
+ | Status indicator | `subtle` | Semantic (success/warning/danger) | Clear status without overwhelming |
583
+ | Featured item | `solid` | `primary` | Maximum emphasis for important items |
584
+ | Category tags | `outline` | `neutral` or `primary` | Minimal emphasis, clean grouping |
585
+ | Notification count | `solid` | `danger` | High visibility for urgent items |
586
+ | Filter tags | `surface` | `primary` | Medium emphasis, distinct from content |
587
+ | Priority labels | `subtle` or `solid` | Semantic | Matches urgency level |
588
+ | Beta/New labels | `solid` or `subtle` | `primary` or `warning` | Draws attention to new features |
589
+ | Role badges | `subtle` | `primary` or `neutral` | Clear identification without distraction |
590
+
591
+ ## State Behaviors
592
+
593
+ | State | Visual Change | Behavior |
594
+ | -------------------------- | ------------------------- | ----------------------------------------------- |
595
+ | **Default** | Standard appearance | Non-interactive, pure visual indicator |
596
+ | **With interactive child** | Child element interactive | Button or link within badge can be clicked |
597
+ | **Disabled** | N/A | Badges don't have disabled state (hide instead) |
598
+
599
+ **Note:** Badges themselves are not interactive. If you need an interactive badge-like element, consider using a Button with badge styling or wrap the badge in a clickable element.
600
+
601
+ ## Size Selection Guide
602
+
603
+ | Context | Recommended Size | Reasoning |
604
+ | --------------------- | ---------------- | ----------------------------------------- |
605
+ | Inline with text | `sm` | Matches text baseline, minimal disruption |
606
+ | Table cells | `sm` | Compact display in dense layouts |
607
+ | Card headers | `md` | Balanced emphasis with card content |
608
+ | List items | `md` | Clear visibility without overwhelming |
609
+ | Feature highlights | `lg` or `xl` | Prominent display for marketing |
610
+ | Navigation indicators | `sm` or `md` | Visible but not distracting |
611
+ | Notification bubbles | `sm` | Compact count display |
612
+ | Hero sections | `xl` or `2xl` | Large format for emphasis |
613
+
614
+ ## Responsive Considerations
615
+
616
+ ```typescript
617
+ // Responsive badge sizes
618
+ <Badge size={{ base: 'sm', md: 'md', lg: 'lg' }}>
619
+ Responsive
620
+ </Badge>
621
+
622
+ // Hide badges on mobile for cleaner layout
623
+ <Badge className={css({ display: { base: 'none', md: 'inline-flex' } })}>
624
+ Desktop Only
625
+ </Badge>
626
+
627
+ // Responsive badge groups
628
+ <div className={css({
629
+ display: 'flex',
630
+ gap: { base: '1', md: '2' },
631
+ flexWrap: 'wrap'
632
+ })}>
633
+ <Badge size={{ base: 'sm', md: 'md' }}>Tag 1</Badge>
634
+ <Badge size={{ base: 'sm', md: 'md' }}>Tag 2</Badge>
635
+ <Badge size={{ base: 'sm', md: 'md' }}>Tag 3</Badge>
636
+ </div>
637
+ ```
638
+
639
+ ## Testing
640
+
641
+ When testing Badge components:
642
+
643
+ ```typescript
644
+ import { render, screen } from '@testing-library/react';
645
+
646
+ test('renders badge with text content', () => {
647
+ render(<Badge>New</Badge>);
648
+
649
+ const badge = screen.getByText('New');
650
+ expect(badge).toBeInTheDocument();
651
+ });
652
+
653
+ test('applies correct variant styles', () => {
654
+ const { container } = render(<Badge variant="solid">Featured</Badge>);
655
+
656
+ const badge = container.firstChild;
657
+ expect(badge).toHaveClass('badge');
658
+ });
659
+
660
+ test('applies correct size', () => {
661
+ render(<Badge size="lg">Large Badge</Badge>);
662
+
663
+ const badge = screen.getByText('Large Badge');
664
+ expect(badge).toBeInTheDocument();
665
+ });
666
+
667
+ test('applies color palette', () => {
668
+ render(<Badge colorPalette="success">Success</Badge>);
669
+
670
+ const badge = screen.getByText('Success');
671
+ expect(badge).toBeInTheDocument();
672
+ });
673
+
674
+ test('renders icon-only badge with aria-label', () => {
675
+ const { container } = render(
676
+ <Badge aria-label="Verified">
677
+ <svg data-testid="check-icon" />
678
+ </Badge>
679
+ );
680
+
681
+ const badge = container.firstChild;
682
+ expect(badge).toHaveAttribute('aria-label', 'Verified');
683
+ expect(screen.getByTestId('check-icon')).toBeInTheDocument();
684
+ });
685
+
686
+ test('renders badge with icon and text', () => {
687
+ render(
688
+ <Badge>
689
+ <svg data-testid="icon" />
690
+ <span>Verified</span>
691
+ </Badge>
692
+ );
693
+
694
+ expect(screen.getByTestId('icon')).toBeInTheDocument();
695
+ expect(screen.getByText('Verified')).toBeInTheDocument();
696
+ });
697
+
698
+ test('accepts custom className', () => {
699
+ const { container } = render(
700
+ <Badge className="custom-badge">Custom</Badge>
701
+ );
702
+
703
+ const badge = container.firstChild;
704
+ expect(badge).toHaveClass('custom-badge');
705
+ });
706
+
707
+ test('badge is not interactive by default', () => {
708
+ const handleClick = vi.fn();
709
+ const { container } = render(
710
+ <Badge onClick={handleClick}>Click Me</Badge>
711
+ );
712
+
713
+ const badge = container.firstChild as HTMLElement;
714
+ badge.click();
715
+
716
+ expect(handleClick).toHaveBeenCalled(); // onClick is passed through but not recommended
717
+ });
718
+ ```
719
+
720
+ ## Related Components
721
+
722
+ - **Button**: For interactive badge-like elements
723
+ - **Chip**: For interactive, removable tags (if implemented)
724
+ - **Tag**: Alternative name for similar component patterns
725
+ - **IconButton**: For interactive icon elements
726
+ - **Heading**: Often paired with badges in headers
727
+ - **Card**: Frequently contains status badges
728
+ - **Avatar**: Often displayed with role or status badges