torch-glare 1.2.8 → 1.3.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 (41) hide show
  1. package/apps/lib/components/TableDnDWrapper.ts +495 -0
  2. package/apps/lib/components/TextEditor.tsx +53 -1
  3. package/dist/bin/index.js +5 -0
  4. package/dist/bin/index.js.map +1 -1
  5. package/dist/src/commands/mcp.d.ts +2 -0
  6. package/dist/src/commands/mcp.d.ts.map +1 -0
  7. package/dist/src/commands/mcp.js +91 -0
  8. package/dist/src/commands/mcp.js.map +1 -0
  9. package/dist/src/shared/configureFonts.d.ts +6 -0
  10. package/dist/src/shared/configureFonts.d.ts.map +1 -0
  11. package/dist/src/shared/configureFonts.js +106 -0
  12. package/dist/src/shared/configureFonts.js.map +1 -0
  13. package/dist/src/shared/configureGlobalCss.d.ts +6 -0
  14. package/dist/src/shared/configureGlobalCss.d.ts.map +1 -0
  15. package/dist/src/shared/configureGlobalCss.js +154 -0
  16. package/dist/src/shared/configureGlobalCss.js.map +1 -0
  17. package/dist/src/shared/configureTailwind.d.ts +7 -0
  18. package/dist/src/shared/configureTailwind.d.ts.map +1 -0
  19. package/dist/src/shared/configureTailwind.js +163 -0
  20. package/dist/src/shared/configureTailwind.js.map +1 -0
  21. package/dist/src/shared/detectFramework.d.ts +23 -0
  22. package/dist/src/shared/detectFramework.d.ts.map +1 -0
  23. package/dist/src/shared/detectFramework.js +119 -0
  24. package/dist/src/shared/detectFramework.js.map +1 -0
  25. package/dist/src/shared/getDependenciesAndInstallNestedComponents.d.ts.map +1 -1
  26. package/dist/src/shared/getDependenciesAndInstallNestedComponents.js +18 -2
  27. package/dist/src/shared/getDependenciesAndInstallNestedComponents.js.map +1 -1
  28. package/dist/src/shared/installBaseUtils.d.ts +5 -0
  29. package/dist/src/shared/installBaseUtils.d.ts.map +1 -0
  30. package/dist/src/shared/installBaseUtils.js +79 -0
  31. package/dist/src/shared/installBaseUtils.js.map +1 -0
  32. package/dist/src/shared/resolveAliases.d.ts +24 -0
  33. package/dist/src/shared/resolveAliases.d.ts.map +1 -0
  34. package/dist/src/shared/resolveAliases.js +98 -0
  35. package/dist/src/shared/resolveAliases.js.map +1 -0
  36. package/docs/components/breadcrumb.md +955 -0
  37. package/docs/components/button-group.md +647 -0
  38. package/docs/components/text-editor.md +711 -0
  39. package/docs/components/toggle-button.md +640 -0
  40. package/docs/tutorials/getting-started.md +123 -431
  41. package/package.json +1 -1
@@ -0,0 +1,647 @@
1
+ ---
2
+ name: ButtonGroup
3
+ version: 1.1.15
4
+ status: stable
5
+ category: components/buttons
6
+ tags: [toggle-group, button-group, selection, radix-ui, accessible, compound]
7
+ last-reviewed: 2024-11-05
8
+ bundle-size: 3.8kb
9
+ dependencies:
10
+ - "@radix-ui/react-toggle-group": "^1.0.0"
11
+ - "class-variance-authority": "^0.7.0"
12
+ ---
13
+
14
+ # ButtonGroup
15
+
16
+ > A compound component built on Radix UI Toggle Group that enables single or multiple selection within a group of toggle buttons. Supports three visual variants, four sizes, and automatic variant/size inheritance from parent to child items.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install torch-glare
22
+ ```
23
+
24
+ ## Import
25
+
26
+ ```typescript
27
+ import { ButtonGroup, ButtonGroupItem } from 'torch-glare/lib/components/ButtonGroup'
28
+ // or
29
+ import { ButtonGroup, ButtonGroupItem } from 'torch-glare/lib/components'
30
+
31
+ // Also re-exports ToggleButton from ToggleButton.tsx
32
+ import { ToggleButton } from 'torch-glare/lib/components/ButtonGroup'
33
+ ```
34
+
35
+ ## Quick Examples
36
+
37
+ ### Basic Usage (Single Selection)
38
+
39
+ ```typescript
40
+ import { ButtonGroup, ButtonGroupItem } from 'torch-glare/lib/components/ButtonGroup'
41
+
42
+ function Example() {
43
+ const [value, setValue] = useState('left')
44
+
45
+ return (
46
+ <ButtonGroup
47
+ type="single"
48
+ value={value}
49
+ onValueChange={setValue}
50
+ >
51
+ <ButtonGroupItem value="left">Left</ButtonGroupItem>
52
+ <ButtonGroupItem value="center">Center</ButtonGroupItem>
53
+ <ButtonGroupItem value="right">Right</ButtonGroupItem>
54
+ </ButtonGroup>
55
+ )
56
+ }
57
+ ```
58
+
59
+ ### Multiple Selection
60
+
61
+ ```typescript
62
+ function MultipleExample() {
63
+ const [values, setValues] = useState<string[]>(['bold'])
64
+
65
+ return (
66
+ <ButtonGroup
67
+ type="multiple"
68
+ value={values}
69
+ onValueChange={setValues}
70
+ >
71
+ <ButtonGroupItem value="bold">
72
+ <i className="ri-bold" />
73
+ </ButtonGroupItem>
74
+ <ButtonGroupItem value="italic">
75
+ <i className="ri-italic" />
76
+ </ButtonGroupItem>
77
+ <ButtonGroupItem value="underline">
78
+ <i className="ri-underline" />
79
+ </ButtonGroupItem>
80
+ </ButtonGroup>
81
+ )
82
+ }
83
+ ```
84
+
85
+ ### All Variants
86
+
87
+ ```typescript
88
+ // PrimeStyle (default)
89
+ <ButtonGroup type="single" variant="PrimeStyle" defaultValue="a">
90
+ <ButtonGroupItem value="a">Option A</ButtonGroupItem>
91
+ <ButtonGroupItem value="b">Option B</ButtonGroupItem>
92
+ </ButtonGroup>
93
+
94
+ // BorderStyle
95
+ <ButtonGroup type="single" variant="BorderStyle" defaultValue="a">
96
+ <ButtonGroupItem value="a">Option A</ButtonGroupItem>
97
+ <ButtonGroupItem value="b">Option B</ButtonGroupItem>
98
+ </ButtonGroup>
99
+
100
+ // SystemStyle
101
+ <ButtonGroup type="single" variant="SystemStyle" defaultValue="a">
102
+ <ButtonGroupItem value="a">Option A</ButtonGroupItem>
103
+ <ButtonGroupItem value="b">Option B</ButtonGroupItem>
104
+ </ButtonGroup>
105
+ ```
106
+
107
+ ### With Sizes
108
+
109
+ ```typescript
110
+ <ButtonGroup type="single" size="S" defaultValue="a">
111
+ <ButtonGroupItem value="a">Small</ButtonGroupItem>
112
+ <ButtonGroupItem value="b">Items</ButtonGroupItem>
113
+ </ButtonGroup>
114
+
115
+ <ButtonGroup type="single" size="M" defaultValue="a">
116
+ <ButtonGroupItem value="a">Medium</ButtonGroupItem>
117
+ <ButtonGroupItem value="b">Items</ButtonGroupItem>
118
+ </ButtonGroup>
119
+
120
+ <ButtonGroup type="single" size="L" defaultValue="a">
121
+ <ButtonGroupItem value="a">Large</ButtonGroupItem>
122
+ <ButtonGroupItem value="b">Items</ButtonGroupItem>
123
+ </ButtonGroup>
124
+
125
+ <ButtonGroup type="single" size="XL" defaultValue="a">
126
+ <ButtonGroupItem value="a">Extra Large</ButtonGroupItem>
127
+ <ButtonGroupItem value="b">Items</ButtonGroupItem>
128
+ </ButtonGroup>
129
+ ```
130
+
131
+ ### Full Width
132
+
133
+ ```typescript
134
+ <ButtonGroup type="single" fullWidth defaultValue="monthly">
135
+ <ButtonGroupItem value="monthly">Monthly</ButtonGroupItem>
136
+ <ButtonGroupItem value="yearly">Yearly</ButtonGroupItem>
137
+ </ButtonGroup>
138
+ ```
139
+
140
+ ### With Icons
141
+
142
+ ```typescript
143
+ function ViewToggle() {
144
+ const [view, setView] = useState('grid')
145
+
146
+ return (
147
+ <ButtonGroup type="single" value={view} onValueChange={setView} size="M">
148
+ <ButtonGroupItem value="grid" aria-label="Grid view">
149
+ <i className="ri-grid-fill" />
150
+ </ButtonGroupItem>
151
+ <ButtonGroupItem value="list" aria-label="List view">
152
+ <i className="ri-list-unordered" />
153
+ </ButtonGroupItem>
154
+ <ButtonGroupItem value="kanban" aria-label="Kanban view">
155
+ <i className="ri-kanban-view" />
156
+ </ButtonGroupItem>
157
+ </ButtonGroup>
158
+ )
159
+ }
160
+ ```
161
+
162
+ ### With Theme Override
163
+
164
+ ```typescript
165
+ <ButtonGroup type="single" theme="dark" variant="PrimeStyle" defaultValue="a">
166
+ <ButtonGroupItem value="a">Dark A</ButtonGroupItem>
167
+ <ButtonGroupItem value="b">Dark B</ButtonGroupItem>
168
+ </ButtonGroup>
169
+ ```
170
+
171
+ ### Item Variant/Size Override
172
+
173
+ ```typescript
174
+ <ButtonGroup type="single" variant="PrimeStyle" size="M" defaultValue="a">
175
+ {/* This item overrides to BorderStyle and L size */}
176
+ <ButtonGroupItem value="a" variant="BorderStyle" size="L">
177
+ Custom
178
+ </ButtonGroupItem>
179
+ {/* This item inherits PrimeStyle and M from parent */}
180
+ <ButtonGroupItem value="b">Inherited</ButtonGroupItem>
181
+ </ButtonGroup>
182
+ ```
183
+
184
+ ### Disabled Items
185
+
186
+ ```typescript
187
+ <ButtonGroup type="single" defaultValue="a">
188
+ <ButtonGroupItem value="a">Active</ButtonGroupItem>
189
+ <ButtonGroupItem value="b" disabled>Disabled</ButtonGroupItem>
190
+ <ButtonGroupItem value="c">Active</ButtonGroupItem>
191
+ </ButtonGroup>
192
+ ```
193
+
194
+ ## API Reference
195
+
196
+ ### ButtonGroup Props
197
+
198
+ | Prop | Type | Default | Description |
199
+ |------|------|---------|-------------|
200
+ | `type` | `'single' \| 'multiple'` | Required | Selection mode |
201
+ | `value` | `string \| string[]` | - | Controlled selected value(s) |
202
+ | `defaultValue` | `string \| string[]` | - | Uncontrolled default value(s) |
203
+ | `onValueChange` | `(value: string \| string[]) => void` | - | Called when selection changes |
204
+ | `variant` | `'PrimeStyle' \| 'BorderStyle' \| 'SystemStyle'` | `'PrimeStyle'` | Visual style variant |
205
+ | `size` | `'S' \| 'M' \| 'L' \| 'XL'` | `'M'` | Size of the group and items |
206
+ | `fullWidth` | `boolean` | `false` | Makes the group span full width |
207
+ | `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme for this component |
208
+ | `className` | `string` | - | Additional CSS classes |
209
+ | `children` | `React.ReactNode` | - | ButtonGroupItem children |
210
+
211
+ ### ButtonGroupItem Props
212
+
213
+ | Prop | Type | Default | Description |
214
+ |------|------|---------|-------------|
215
+ | `value` | `string` | Required | Unique value for this item |
216
+ | `variant` | `'PrimeStyle' \| 'BorderStyle' \| 'SystemStyle'` | Inherited | Override parent variant |
217
+ | `size` | `'S' \| 'M' \| 'L' \| 'XL'` | Inherited | Override parent size |
218
+ | `disabled` | `boolean` | `false` | Disables this item |
219
+ | `theme` | `'light' \| 'dark' \| 'default'` | - | Override theme for this item |
220
+ | `className` | `string` | - | Additional CSS classes |
221
+ | `children` | `React.ReactNode` | - | Item content |
222
+
223
+ ### Size Variants
224
+
225
+ | Size | Height | Padding | Typography | Icon Size |
226
+ |------|--------|---------|------------|-----------|
227
+ | S | 22px | 8px | Small Medium | 12px |
228
+ | M | 28px | 12px | Large Medium | 18px |
229
+ | L | 34px | 16px | Large Medium | 20px |
230
+ | XL | 40px | 20px | Headers Medium | 22px |
231
+
232
+ ### TypeScript
233
+
234
+ ```typescript
235
+ import { ComponentPropsWithoutRef } from 'react'
236
+ import { VariantProps } from 'class-variance-authority'
237
+ import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'
238
+
239
+ type Themes = 'light' | 'dark' | 'default'
240
+
241
+ type ButtonGroupSingleProps = {
242
+ type: 'single'
243
+ value?: string
244
+ defaultValue?: string
245
+ onValueChange?: (value: string) => void
246
+ }
247
+
248
+ type ButtonGroupMultipleProps = {
249
+ type: 'multiple'
250
+ value?: string[]
251
+ defaultValue?: string[]
252
+ onValueChange?: (value: string[]) => void
253
+ }
254
+
255
+ type ButtonGroupProps = (ButtonGroupSingleProps | ButtonGroupMultipleProps) &
256
+ Omit<ComponentPropsWithoutRef<'div'>, 'type' | 'value' | 'defaultValue'> &
257
+ VariantProps<typeof buttonGroupStyles> & {
258
+ theme?: Themes
259
+ children?: React.ReactNode
260
+ }
261
+
262
+ type ButtonGroupItemProps =
263
+ ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
264
+ VariantProps<typeof buttonGroupItemStyles> & {
265
+ theme?: Themes
266
+ children?: React.ReactNode
267
+ _groupVariant?: 'PrimeStyle' | 'BorderStyle' | 'SystemStyle' | null
268
+ _groupSize?: 'S' | 'M' | 'L' | 'XL' | null
269
+ }
270
+
271
+ export const ButtonGroup: React.ForwardRefExoticComponent<
272
+ ButtonGroupProps & React.RefAttributes<HTMLDivElement>
273
+ >
274
+
275
+ export const ButtonGroupItem: React.ForwardRefExoticComponent<
276
+ ButtonGroupItemProps & React.RefAttributes<HTMLButtonElement>
277
+ >
278
+ ```
279
+
280
+ ## Common Patterns
281
+
282
+ ### Text Alignment Toolbar
283
+
284
+ ```typescript
285
+ function AlignmentToolbar() {
286
+ const [alignment, setAlignment] = useState('left')
287
+
288
+ return (
289
+ <ButtonGroup type="single" value={alignment} onValueChange={setAlignment} size="S">
290
+ <ButtonGroupItem value="left" aria-label="Align left">
291
+ <i className="ri-align-left" />
292
+ </ButtonGroupItem>
293
+ <ButtonGroupItem value="center" aria-label="Align center">
294
+ <i className="ri-align-center" />
295
+ </ButtonGroupItem>
296
+ <ButtonGroupItem value="right" aria-label="Align right">
297
+ <i className="ri-align-right" />
298
+ </ButtonGroupItem>
299
+ <ButtonGroupItem value="justify" aria-label="Justify">
300
+ <i className="ri-align-justify" />
301
+ </ButtonGroupItem>
302
+ </ButtonGroup>
303
+ )
304
+ }
305
+ ```
306
+
307
+ ### Pricing Toggle
308
+
309
+ ```typescript
310
+ function PricingToggle() {
311
+ const [billing, setBilling] = useState('monthly')
312
+
313
+ return (
314
+ <div className="text-center">
315
+ <ButtonGroup
316
+ type="single"
317
+ value={billing}
318
+ onValueChange={setBilling}
319
+ variant="BorderStyle"
320
+ size="L"
321
+ >
322
+ <ButtonGroupItem value="monthly">Monthly</ButtonGroupItem>
323
+ <ButtonGroupItem value="yearly">
324
+ Yearly
325
+ <span className="ml-1 text-xs text-green-500">-20%</span>
326
+ </ButtonGroupItem>
327
+ </ButtonGroup>
328
+ </div>
329
+ )
330
+ }
331
+ ```
332
+
333
+ ### Formatting Toolbar (Multiple Selection)
334
+
335
+ ```typescript
336
+ function FormattingToolbar() {
337
+ const [formats, setFormats] = useState<string[]>([])
338
+
339
+ return (
340
+ <ButtonGroup
341
+ type="multiple"
342
+ value={formats}
343
+ onValueChange={setFormats}
344
+ size="S"
345
+ variant="BorderStyle"
346
+ >
347
+ <ButtonGroupItem value="bold" aria-label="Bold">
348
+ <i className="ri-bold" />
349
+ </ButtonGroupItem>
350
+ <ButtonGroupItem value="italic" aria-label="Italic">
351
+ <i className="ri-italic" />
352
+ </ButtonGroupItem>
353
+ <ButtonGroupItem value="underline" aria-label="Underline">
354
+ <i className="ri-underline" />
355
+ </ButtonGroupItem>
356
+ <ButtonGroupItem value="strikethrough" aria-label="Strikethrough">
357
+ <i className="ri-strikethrough" />
358
+ </ButtonGroupItem>
359
+ </ButtonGroup>
360
+ )
361
+ }
362
+ ```
363
+
364
+ ### Tab-like Navigation
365
+
366
+ ```typescript
367
+ function TabNavigation() {
368
+ const [tab, setTab] = useState('overview')
369
+
370
+ return (
371
+ <ButtonGroup
372
+ type="single"
373
+ value={tab}
374
+ onValueChange={setTab}
375
+ fullWidth
376
+ variant="PrimeStyle"
377
+ size="L"
378
+ >
379
+ <ButtonGroupItem value="overview">Overview</ButtonGroupItem>
380
+ <ButtonGroupItem value="analytics">Analytics</ButtonGroupItem>
381
+ <ButtonGroupItem value="settings">Settings</ButtonGroupItem>
382
+ </ButtonGroup>
383
+ )
384
+ }
385
+ ```
386
+
387
+ ### System Style (Dark UI)
388
+
389
+ ```typescript
390
+ function DarkToolbar() {
391
+ const [tool, setTool] = useState('select')
392
+
393
+ return (
394
+ <div className="bg-gray-900 p-4 rounded">
395
+ <ButtonGroup
396
+ type="single"
397
+ value={tool}
398
+ onValueChange={setTool}
399
+ variant="SystemStyle"
400
+ size="M"
401
+ >
402
+ <ButtonGroupItem value="select" aria-label="Select tool">
403
+ <i className="ri-cursor-fill" />
404
+ </ButtonGroupItem>
405
+ <ButtonGroupItem value="draw" aria-label="Draw tool">
406
+ <i className="ri-pencil-fill" />
407
+ </ButtonGroupItem>
408
+ <ButtonGroupItem value="eraser" aria-label="Eraser tool">
409
+ <i className="ri-eraser-fill" />
410
+ </ButtonGroupItem>
411
+ </ButtonGroup>
412
+ </div>
413
+ )
414
+ }
415
+ ```
416
+
417
+ ## Testing
418
+
419
+ ### Unit Test Example
420
+
421
+ ```typescript
422
+ import { render, screen, fireEvent } from '@testing-library/react'
423
+ import { ButtonGroup, ButtonGroupItem } from 'torch-glare/lib/components/ButtonGroup'
424
+
425
+ describe('ButtonGroup', () => {
426
+ it('selects a single value', () => {
427
+ const handleChange = jest.fn()
428
+ render(
429
+ <ButtonGroup type="single" onValueChange={handleChange}>
430
+ <ButtonGroupItem value="a">A</ButtonGroupItem>
431
+ <ButtonGroupItem value="b">B</ButtonGroupItem>
432
+ </ButtonGroup>
433
+ )
434
+
435
+ fireEvent.click(screen.getByText('B'))
436
+ expect(handleChange).toHaveBeenCalledWith('b')
437
+ })
438
+
439
+ it('supports multiple selection', () => {
440
+ const handleChange = jest.fn()
441
+ render(
442
+ <ButtonGroup type="multiple" onValueChange={handleChange}>
443
+ <ButtonGroupItem value="bold">B</ButtonGroupItem>
444
+ <ButtonGroupItem value="italic">I</ButtonGroupItem>
445
+ </ButtonGroup>
446
+ )
447
+
448
+ fireEvent.click(screen.getByText('B'))
449
+ expect(handleChange).toHaveBeenCalledWith(['bold'])
450
+
451
+ fireEvent.click(screen.getByText('I'))
452
+ expect(handleChange).toHaveBeenCalledWith(['bold', 'italic'])
453
+ })
454
+
455
+ it('disables individual items', () => {
456
+ const handleChange = jest.fn()
457
+ render(
458
+ <ButtonGroup type="single" onValueChange={handleChange}>
459
+ <ButtonGroupItem value="a">A</ButtonGroupItem>
460
+ <ButtonGroupItem value="b" disabled>B</ButtonGroupItem>
461
+ </ButtonGroup>
462
+ )
463
+
464
+ fireEvent.click(screen.getByText('B'))
465
+ expect(handleChange).not.toHaveBeenCalled()
466
+ })
467
+
468
+ it('applies active state styling', () => {
469
+ render(
470
+ <ButtonGroup type="single" value="a">
471
+ <ButtonGroupItem value="a">A</ButtonGroupItem>
472
+ <ButtonGroupItem value="b">B</ButtonGroupItem>
473
+ </ButtonGroup>
474
+ )
475
+
476
+ expect(screen.getByText('A').closest('button')).toHaveAttribute('data-state', 'on')
477
+ expect(screen.getByText('B').closest('button')).toHaveAttribute('data-state', 'off')
478
+ })
479
+ })
480
+ ```
481
+
482
+ ### Accessibility Test
483
+
484
+ ```typescript
485
+ import { axe } from '@axe-core/react'
486
+
487
+ test('ButtonGroup meets WCAG standards', async () => {
488
+ const { container } = render(
489
+ <ButtonGroup type="single" defaultValue="a">
490
+ <ButtonGroupItem value="a">Option A</ButtonGroupItem>
491
+ <ButtonGroupItem value="b">Option B</ButtonGroupItem>
492
+ </ButtonGroup>
493
+ )
494
+
495
+ const results = await axe(container)
496
+ expect(results).toHaveNoViolations()
497
+ })
498
+ ```
499
+
500
+ ## Accessibility
501
+
502
+ ### Keyboard Support
503
+
504
+ - **Tab**: Move focus to/from the button group
505
+ - **Arrow Left/Right**: Navigate between items within the group
506
+ - **Space**: Toggle the focused item
507
+ - **Enter**: Toggle the focused item
508
+
509
+ ### ARIA Attributes
510
+
511
+ Radix UI Toggle Group automatically provides:
512
+
513
+ ```html
514
+ <!-- Single selection -->
515
+ <div role="group">
516
+ <button
517
+ role="radio"
518
+ aria-checked="true"
519
+ data-state="on"
520
+ >Selected</button>
521
+ <button
522
+ role="radio"
523
+ aria-checked="false"
524
+ data-state="off"
525
+ >Not Selected</button>
526
+ </div>
527
+
528
+ <!-- Multiple selection -->
529
+ <div role="group">
530
+ <button
531
+ aria-pressed="true"
532
+ data-state="on"
533
+ >Active</button>
534
+ <button
535
+ aria-pressed="false"
536
+ data-state="off"
537
+ >Inactive</button>
538
+ </div>
539
+ ```
540
+
541
+ ### Screen Reader Support
542
+
543
+ - Announces group role and item count
544
+ - Communicates selected/pressed state per item
545
+ - Reads aria-label or text content for each item
546
+ - Announces state changes on toggle
547
+
548
+ ### Best Practices
549
+
550
+ ```typescript
551
+ // Always provide aria-label for icon-only items
552
+ <ButtonGroupItem value="grid" aria-label="Grid view">
553
+ <i className="ri-grid-fill" />
554
+ </ButtonGroupItem>
555
+
556
+ // Or include screen-reader text
557
+ <ButtonGroupItem value="grid">
558
+ <i className="ri-grid-fill" />
559
+ <span className="sr-only">Grid view</span>
560
+ </ButtonGroupItem>
561
+ ```
562
+
563
+ ## Styling
564
+
565
+ ### Variant Styles
566
+
567
+ Each variant provides different visual styles for the group container and items:
568
+
569
+ - **PrimeStyle**: Secondary background with disabled border, hover and active highlights
570
+ - **BorderStyle**: Border-style background with disabled border, same hover/active as PrimeStyle
571
+ - **SystemStyle**: Dark/transparent background with white text, white alpha hover/active states
572
+
573
+ ### Active State
574
+
575
+ Items use `data-[state=on]` for active styling:
576
+
577
+ ```css
578
+ /* PrimeStyle / BorderStyle active */
579
+ data-[state=on]:bg-background-presentation-action-hover
580
+ data-[state=on]:text-content-presentation-action-hover
581
+
582
+ /* SystemStyle active */
583
+ data-[state=on]:bg-white/20
584
+ data-[state=on]:text-white
585
+ ```
586
+
587
+ ### Focus Visible
588
+
589
+ Items display a focus ring on keyboard navigation:
590
+
591
+ ```css
592
+ focus-visible:ring-2 focus-visible:ring-inset
593
+ focus-visible:ring-border-presentation-state-focus
594
+ ```
595
+
596
+ ### Custom Styles
597
+
598
+ ```typescript
599
+ <ButtonGroup
600
+ type="single"
601
+ className="rounded-full shadow-md"
602
+ defaultValue="a"
603
+ >
604
+ <ButtonGroupItem value="a" className="first:rounded-l-full">A</ButtonGroupItem>
605
+ <ButtonGroupItem value="b" className="last:rounded-r-full">B</ButtonGroupItem>
606
+ </ButtonGroup>
607
+ ```
608
+
609
+ ## Performance
610
+
611
+ | Metric | Value |
612
+ |--------|-------|
613
+ | Bundle size (gzip) | 3.8kb |
614
+ | First render | <8ms |
615
+ | Re-render | <3ms |
616
+ | Tree-shakeable | Yes |
617
+
618
+ ### Optimization Tips
619
+
620
+ 1. Use `defaultValue` for uncontrolled mode when possible
621
+ 2. Memoize `onValueChange` handlers with `useCallback`
622
+ 3. Avoid recreating children arrays on each render
623
+
624
+ ## Related Components
625
+
626
+ - [ToggleButton](/docs/components/toggle-button.md) - Standalone toggle button (re-exported from ButtonGroup)
627
+ - [Toggle](/docs/components/toggle.md) - Individual toggle with more variant options
628
+ - [Button](/docs/components/button.md) - Standard action buttons
629
+ - [ActionsGroup](/docs/components/actions-group.md) - Group of action buttons
630
+
631
+ ## Browser Support
632
+
633
+ - Chrome 90+
634
+ - Firefox 88+
635
+ - Safari 14+
636
+ - Edge 90+
637
+ - Mobile browsers
638
+
639
+ ## Changelog
640
+
641
+ ### v1.1.15
642
+ - Initial stable release
643
+ - 3 visual variants (PrimeStyle, BorderStyle, SystemStyle)
644
+ - 4 size variants (S, M, L, XL)
645
+ - Single and multiple selection modes
646
+ - Automatic variant/size inheritance from parent to items
647
+ - Re-exports ToggleButton component