@xsolla/xui-button 0.99.0 → 0.100.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 (2) hide show
  1. package/README.md +507 -47
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,79 +1,539 @@
1
- # @xsolla/xui-button
1
+ ---
2
+ title: Button
3
+ subtitle: A themed button with multiple variants and tones.
4
+ description: A cross-platform React button component with primary/secondary variants, multiple color tones, sizes, loading states, and icon support.
5
+ ---
2
6
 
3
- Accessible button components with multiple visual variants, sizes, and states.
7
+ # Button
8
+
9
+ A cross-platform React button component with primary/secondary variants, multiple color tones, sizes, loading states, and icon support. Works on both React (web) and React Native.
4
10
 
5
11
  ## Installation
6
12
 
7
13
  ```bash
14
+ npm install @xsolla/xui-button
15
+ # or
8
16
  yarn add @xsolla/xui-button
9
17
  ```
10
18
 
11
- ## Usage
19
+ ## Demo
20
+
21
+ ### Basic Button
22
+
23
+ ```tsx
24
+ import * as React from 'react';
25
+ import { Button } from '@xsolla/xui-button';
26
+
27
+ export default function BasicButton() {
28
+ return (
29
+ <Button onPress={() => console.log('Pressed!')}>
30
+ Click me
31
+ </Button>
32
+ );
33
+ }
34
+ ```
35
+
36
+ ### Button Variants
37
+
38
+ ```tsx
39
+ import * as React from 'react';
40
+ import { Button } from '@xsolla/xui-button';
41
+
42
+ export default function ButtonVariants() {
43
+ return (
44
+ <div style={{ display: 'flex', gap: 16 }}>
45
+ <Button variant="primary">Primary</Button>
46
+ <Button variant="secondary">Secondary</Button>
47
+ <Button variant="tertiary">Tertiary</Button>
48
+ </div>
49
+ );
50
+ }
51
+ ```
52
+
53
+ ### Button Tones
54
+
55
+ ```tsx
56
+ import * as React from 'react';
57
+ import { Button } from '@xsolla/xui-button';
58
+
59
+ export default function ButtonTones() {
60
+ return (
61
+ <div style={{ display: 'flex', gap: 16 }}>
62
+ <Button tone="brand">Brand</Button>
63
+ <Button tone="brandExtra">Brand Extra</Button>
64
+ <Button tone="alert">Alert</Button>
65
+ <Button tone="mono">Mono</Button>
66
+ </div>
67
+ );
68
+ }
69
+ ```
70
+
71
+ ### Button Sizes
72
+
73
+ ```tsx
74
+ import * as React from 'react';
75
+ import { Button } from '@xsolla/xui-button';
76
+
77
+ export default function ButtonSizes() {
78
+ return (
79
+ <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
80
+ <Button size="xs">Extra Small</Button>
81
+ <Button size="sm">Small</Button>
82
+ <Button size="md">Medium</Button>
83
+ <Button size="lg">Large</Button>
84
+ <Button size="xl">Extra Large</Button>
85
+ </div>
86
+ );
87
+ }
88
+ ```
89
+
90
+ ### Button with Icons
91
+
92
+ ```tsx
93
+ import * as React from 'react';
94
+ import { Button } from '@xsolla/xui-button';
95
+ import { Plus, ChevronDown, FileDownloadOut } from '@xsolla/xui-icons-base';
96
+ import { ArrowRight } from '@xsolla/xui-icons';
97
+
98
+ export default function ButtonWithIcons() {
99
+ return (
100
+ <div style={{ display: 'flex', gap: 16 }}>
101
+ <Button iconLeft={<Plus />}>
102
+ Add Item
103
+ </Button>
104
+ <Button iconRight={<ArrowRight />}>
105
+ Next Step
106
+ </Button>
107
+ <Button
108
+ iconLeft={<FileDownloadOut />}
109
+ iconRight={<ChevronDown />}
110
+ >
111
+ Download
112
+ </Button>
113
+ </div>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### Loading Button
119
+
120
+ ```tsx
121
+ import * as React from 'react';
122
+ import { Button } from '@xsolla/xui-button';
123
+
124
+ export default function LoadingButton() {
125
+ const [loading, setLoading] = React.useState(false);
126
+
127
+ const handlePress = () => {
128
+ setLoading(true);
129
+ setTimeout(() => setLoading(false), 2000);
130
+ };
131
+
132
+ return (
133
+ <Button loading={loading} onPress={handlePress}>
134
+ {loading ? 'Processing...' : 'Submit'}
135
+ </Button>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ## Anatomy
141
+
142
+ Import the component and use it directly:
143
+
144
+ ```jsx
145
+ import { Button, IconButton, FlexButton, ButtonGroup } from '@xsolla/xui-button';
146
+ import { Plus } from '@xsolla/xui-icons-base';
147
+
148
+ // Basic button
149
+ <Button>Label</Button>
150
+
151
+ // Icon-only button
152
+ <IconButton icon={<Plus />} aria-label="Add item" />
153
+
154
+ // Flexible styling button
155
+ <FlexButton variant="brand">Label</FlexButton>
156
+
157
+ // Group of buttons
158
+ <ButtonGroup>
159
+ <Button>First</Button>
160
+ <Button>Second</Button>
161
+ </ButtonGroup>
162
+ ```
163
+
164
+ ## Examples
165
+
166
+ ### Full Width Button
167
+
168
+ Use the `fullWidth` prop to make the button span the entire container width.
12
169
 
13
170
  ```tsx
171
+ import * as React from 'react';
14
172
  import { Button } from '@xsolla/xui-button';
15
173
 
16
- <Button variant="primary" tone="brand" onPress={() => console.log('clicked')}>
17
- Buy Now
18
- </Button>
174
+ export default function FullWidthButton() {
175
+ return (
176
+ <div style={{ width: 300 }}>
177
+ <Button fullWidth>Full Width Button</Button>
178
+ </div>
179
+ );
180
+ }
19
181
  ```
20
182
 
21
- ## Components
183
+ ### Disabled Button
22
184
 
23
- - **Button** — Standard button with optional icons, sublabel, and loading state.
24
- - **IconButton** Square icon-only button; requires `aria-label`.
25
- - **FlexButton** Compact inline button designed for use inside modals and popups.
26
- - **ButtonGroup** — Container for grouping related buttons with optional description and error text.
185
+ ```tsx
186
+ import * as React from 'react';
187
+ import { Button } from '@xsolla/xui-button';
188
+
189
+ export default function DisabledButton() {
190
+ return (
191
+ <div style={{ display: 'flex', gap: 16 }}>
192
+ <Button disabled>Disabled Primary</Button>
193
+ <Button variant="secondary" disabled>Disabled Secondary</Button>
194
+ </div>
195
+ );
196
+ }
197
+ ```
198
+
199
+ ### Icon Button
200
+
201
+ Use `IconButton` for buttons that contain only an icon.
202
+
203
+ ```tsx
204
+ import * as React from 'react';
205
+ import { IconButton } from '@xsolla/xui-button';
206
+ import { Plus, TrashCan } from '@xsolla/xui-icons-base';
207
+ import { Settings } from '@xsolla/xui-icons';
208
+
209
+ export default function IconButtonExample() {
210
+ return (
211
+ <div style={{ display: 'flex', gap: 16 }}>
212
+ <IconButton
213
+ icon={<Plus />}
214
+ aria-label="Add"
215
+ size="md"
216
+ />
217
+ <IconButton
218
+ icon={<TrashCan />}
219
+ aria-label="Delete"
220
+ tone="alert"
221
+ />
222
+ <IconButton
223
+ icon={<Settings />}
224
+ aria-label="Settings"
225
+ variant="secondary"
226
+ />
227
+ </div>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ### Button Group
233
+
234
+ Use `ButtonGroup` to group related buttons together.
235
+
236
+ ```tsx
237
+ import * as React from 'react';
238
+ import { Button, ButtonGroup } from '@xsolla/xui-button';
239
+
240
+ export default function ButtonGroupExample() {
241
+ return (
242
+ <ButtonGroup orientation="horizontal" size="md">
243
+ <Button variant="secondary">Cancel</Button>
244
+ <Button>Confirm</Button>
245
+ </ButtonGroup>
246
+ );
247
+ }
248
+ ```
27
249
 
28
- ## Props
250
+ ### Vertical Button Group
251
+
252
+ ```tsx
253
+ import * as React from 'react';
254
+ import { Button, ButtonGroup } from '@xsolla/xui-button';
255
+
256
+ export default function VerticalButtonGroup() {
257
+ return (
258
+ <ButtonGroup orientation="vertical" size="md">
259
+ <Button fullWidth>Option 1</Button>
260
+ <Button fullWidth>Option 2</Button>
261
+ <Button fullWidth>Option 3</Button>
262
+ </ButtonGroup>
263
+ );
264
+ }
265
+ ```
266
+
267
+ ### Flex Button
268
+
269
+ `FlexButton` provides more flexible styling options with different background modes.
270
+
271
+ ```tsx
272
+ import * as React from 'react';
273
+ import { FlexButton } from '@xsolla/xui-button';
274
+ import { Link } from '@xsolla/xui-icons-base';
275
+
276
+ export default function FlexButtonExample() {
277
+ return (
278
+ <div style={{ display: 'flex', gap: 16 }}>
279
+ <FlexButton variant="brand" background>
280
+ With Background
281
+ </FlexButton>
282
+ <FlexButton variant="brand">
283
+ Text Only
284
+ </FlexButton>
285
+ <FlexButton variant="tertiary" iconLeft={<Link />}>
286
+ Link Style
287
+ </FlexButton>
288
+ </div>
289
+ );
290
+ }
291
+ ```
292
+
293
+ ### Form Submit Button
294
+
295
+ ```tsx
296
+ import * as React from 'react';
297
+ import { Button } from '@xsolla/xui-button';
298
+
299
+ export default function FormSubmitButton() {
300
+ return (
301
+ <form onSubmit={(e) => {
302
+ e.preventDefault();
303
+ console.log('Form submitted');
304
+ }}>
305
+ <Button type="submit">Submit Form</Button>
306
+ </form>
307
+ );
308
+ }
309
+ ```
310
+
311
+ ### Button with Sublabel
312
+
313
+ Use the `sublabel` prop to add secondary text inline with the main label.
314
+
315
+ ```tsx
316
+ import * as React from 'react';
317
+ import { Button } from '@xsolla/xui-button';
318
+ import { Apple } from '@xsolla/xui-icons-brand';
319
+
320
+ export default function ButtonWithSublabel() {
321
+ return (
322
+ <Button
323
+ iconLeft={<Apple />}
324
+ sublabel="$39.99"
325
+ labelAlignment="left"
326
+ >
327
+ Buy Now
328
+ </Button>
329
+ );
330
+ }
331
+ ```
332
+
333
+ ### Button with Divider
334
+
335
+ The `divider` prop controls whether a separator line appears between icons and content.
336
+
337
+ ```tsx
338
+ import * as React from 'react';
339
+ import { Button } from '@xsolla/xui-button';
340
+ import { ArrowRight } from '@xsolla/xui-icons-base';
341
+
342
+ export default function ButtonWithDivider() {
343
+ return (
344
+ <div style={{ display: 'flex', gap: 16 }}>
345
+ <Button iconRight={<ArrowRight />} divider>
346
+ With Divider
347
+ </Button>
348
+ <Button iconRight={<ArrowRight />} divider={false}>
349
+ No Divider
350
+ </Button>
351
+ </div>
352
+ );
353
+ }
354
+ ```
355
+
356
+ ### Button with Custom Content
357
+
358
+ Use `customContent` to add badges, tags, or other elements inside the button.
359
+
360
+ ```tsx
361
+ import * as React from 'react';
362
+ import { Button } from '@xsolla/xui-button';
363
+ import { Tag } from '@xsolla/xui-tag';
364
+ import { ArrowRight } from '@xsolla/xui-icons-base';
365
+
366
+ export default function ButtonWithCustomContent() {
367
+ return (
368
+ <Button
369
+ iconRight={<ArrowRight />}
370
+ customContent={<Tag size="sm">5x</Tag>}
371
+ divider
372
+ >
373
+ Claim Reward
374
+ </Button>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ### Tertiary Variant
380
+
381
+ The tertiary variant provides a minimal, text-like button style.
382
+
383
+ ```tsx
384
+ import * as React from 'react';
385
+ import { Button, IconButton } from '@xsolla/xui-button';
386
+ import { Settings } from '@xsolla/xui-icons-base';
387
+
388
+ export default function TertiaryButtons() {
389
+ return (
390
+ <div style={{ display: 'flex', gap: 16 }}>
391
+ <Button variant="tertiary">Learn More</Button>
392
+ <IconButton
393
+ variant="tertiary"
394
+ icon={<Settings />}
395
+ aria-label="Settings"
396
+ />
397
+ </div>
398
+ );
399
+ }
400
+ ```
401
+
402
+ ## API Reference
29
403
 
30
404
  ### Button
31
405
 
406
+ The main button component. Renders a semantic `<button>` element.
407
+
408
+ **Button Props:**
409
+
32
410
  | Prop | Type | Default | Description |
33
- |------|------|---------|-------------|
34
- | `variant` | `'primary' \| 'secondary' \| 'tertiary'` | `'primary'` | Visual style variant |
35
- | `tone` | `'brand' \| 'brandExtra' \| 'alert' \| 'mono'` | `'brand'` | Colour tone |
36
- | `size` | `'xl' \| 'lg' \| 'md' \| 'sm' \| 'xs'` | `'md'` | Button size |
37
- | `disabled` | `boolean` | `false` | Disables the button |
38
- | `loading` | `boolean` | `false` | Shows a spinner and blocks interaction |
39
- | `onPress` | `() => void` | | Click handler |
40
- | `iconLeft` | `React.ReactNode` | | Icon displayed on the left |
41
- | `iconRight` | `React.ReactNode` | | Icon displayed on the right |
42
- | `sublabel` | `string` | | Secondary inline text shown at reduced opacity |
43
- | `fullWidth` | `boolean` | `false` | Stretches button to fill its container |
411
+ | :--- | :--- | :------ | :---------- |
412
+ | children | `ReactNode` | - | The button label content. |
413
+ | variant | `"primary" \| "secondary" \| "tertiary"` | `"primary"` | The visual style variant. |
414
+ | tone | `"brand" \| "brandExtra" \| "alert" \| "mono"` | `"brand"` | The color tone of the button. |
415
+ | size | `"xl" \| "lg" \| "md" \| "sm" \| "xs"` | `"md"` | The size of the button. |
416
+ | disabled | `boolean` | `false` | Whether the button is disabled. |
417
+ | loading | `boolean` | `false` | Whether to show loading spinner. Disables interaction when true. |
418
+ | onPress | `() => void` | - | Callback fired when button is pressed. |
419
+ | iconLeft | `ReactNode` | - | Icon to display on the left side. |
420
+ | iconRight | `ReactNode` | - | Icon to display on the right side. |
421
+ | divider | `boolean` | `true` when icon present | Whether to show divider between icon and label. |
422
+ | sublabel | `string` | - | Secondary label text displayed inline with main label at 40% opacity. |
423
+ | labelIcon | `ReactNode` | - | Small icon displayed directly next to the label text. |
424
+ | labelAlignment | `"left" \| "center"` | `"center"` | Alignment of the label content within the button. |
425
+ | customContent | `ReactNode` | - | Custom content slot for badges, tags, or other elements. |
426
+ | fullWidth | `boolean` | `false` | Whether button should span full container width. |
427
+ | type | `"button" \| "submit" \| "reset"` | `"button"` | The HTML button type attribute. |
428
+ | aria-label | `string` | - | Accessible label for the button. |
429
+ | aria-describedby | `string` | - | ID of element that describes the button. |
430
+ | aria-expanded | `boolean` | - | Indicates if controlled element is expanded. |
431
+ | aria-haspopup | `boolean \| "menu" \| "listbox" \| "tree" \| "grid" \| "dialog"` | - | Indicates popup type triggered by button. |
432
+ | aria-pressed | `boolean \| "mixed"` | - | Indicates pressed state for toggle buttons. |
433
+ | aria-controls | `string` | - | ID of element controlled by this button. |
434
+ | testID | `string` | - | Test identifier for testing frameworks. |
435
+ | id | `string` | - | HTML id attribute. |
436
+
437
+ ---
44
438
 
45
439
  ### IconButton
46
440
 
441
+ A button variant that displays only an icon. Requires `aria-label` for accessibility.
442
+
443
+ **IconButton Props:**
444
+
47
445
  | Prop | Type | Default | Description |
48
- |------|------|---------|-------------|
49
- | `icon` | `React.ReactNode` | | Icon to display (required) |
50
- | `aria-label` | `string` | | Accessible label (required) |
51
- | `variant` | `'primary' \| 'secondary' \| 'tertiary'` | `'primary'` | Visual style variant |
52
- | `tone` | `'brand' \| 'brandExtra' \| 'alert' \| 'mono'` | `'brand'` | Colour tone |
53
- | `size` | `'xl' \| 'lg' \| 'md' \| 'sm' \| 'xs'` | `'md'` | Button size |
54
- | `disabled` | `boolean` | `false` | Disables the button |
55
- | `loading` | `boolean` | `false` | Shows a spinner and blocks interaction |
56
- | `onPress` | `() => void` | | Click handler |
446
+ | :--- | :--- | :------ | :---------- |
447
+ | icon | `ReactNode` | - | **Required.** The icon to display. |
448
+ | aria-label | `string` | - | **Required.** Accessible label for the button. |
449
+ | variant | `"primary" \| "secondary" \| "tertiary"` | `"primary"` | The visual style variant. |
450
+ | tone | `"brand" \| "brandExtra" \| "alert" \| "mono"` | `"brand"` | The color tone of the button. |
451
+ | size | `"xl" \| "lg" \| "md" \| "sm" \| "xs"` | `"md"` | The size of the button. |
452
+ | disabled | `boolean` | `false` | Whether the button is disabled. |
453
+ | loading | `boolean` | `false` | Whether to show loading spinner. |
454
+ | onPress | `() => void` | - | Callback fired when button is pressed. |
455
+ | type | `"button" \| "submit" \| "reset"` | `"button"` | The HTML button type attribute. |
456
+
457
+ ---
57
458
 
58
459
  ### FlexButton
59
460
 
461
+ A flexible button with more granular control over background and styling.
462
+
463
+ **FlexButton Props:**
464
+
60
465
  | Prop | Type | Default | Description |
61
- |------|------|---------|-------------|
62
- | `variant` | `'brand' \| 'primary' \| 'secondary' \| 'tertiary' \| 'brandExtra' \| 'inverse'` | `'brand'` | Visual style variant |
63
- | `size` | `'xl' \| 'lg' \| 'md' \| 'sm' \| 'xs'` | `'md'` | Button size |
64
- | `background` | `boolean` | `false` | Adds a filled background |
65
- | `disabled` | `boolean` | `false` | Disables the button |
66
- | `loading` | `boolean` | `false` | Shows a spinner and blocks interaction |
67
- | `iconLeft` | `React.ReactNode` | | Icon on the left |
68
- | `iconRight` | `React.ReactNode` | | Icon on the right |
69
- | `onPress` | `() => void` | | Click handler |
466
+ | :--- | :--- | :------ | :---------- |
467
+ | children | `ReactNode` | - | The button label content. |
468
+ | variant | `"brand" \| "primary" \| "secondary" \| "tertiary" \| "brandExtra" \| "inverse"` | `"brand"` | The visual style variant. |
469
+ | size | `"xl" \| "lg" \| "md" \| "sm" \| "xs"` | `"md"` | The size of the button. |
470
+ | background | `boolean` | `false` | Whether to show background fill. |
471
+ | disabled | `boolean` | `false` | Whether the button is disabled. |
472
+ | loading | `boolean` | `false` | Whether to show loading spinner. |
473
+ | iconLeft | `ReactNode` | - | Icon to display on the left side. |
474
+ | iconRight | `ReactNode` | - | Icon to display on the right side. |
475
+ | onPress | `() => void` | - | Callback fired when button is pressed. |
476
+ | type | `"button" \| "submit" \| "reset"` | `"button"` | The HTML button type attribute. |
477
+
478
+ ---
70
479
 
71
480
  ### ButtonGroup
72
481
 
482
+ A container for grouping related buttons together.
483
+
484
+ **ButtonGroup Props:**
485
+
73
486
  | Prop | Type | Default | Description |
74
- |------|------|---------|-------------|
75
- | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction |
76
- | `size` | `'xl' \| 'lg' \| 'md' \| 'sm' \| 'xs'` | `'md'` | Controls default gap between buttons |
77
- | `gap` | `number` | | Custom gap in pixels (overrides size default) |
78
- | `description` | `string` | | Helper text below the group |
79
- | `error` | `string` | | Error message below the group |
487
+ | :--- | :--- | :------ | :---------- |
488
+ | children | `ReactNode` | - | **Required.** Button children to group. |
489
+ | orientation | `"horizontal" \| "vertical"` | `"horizontal"` | Layout direction of the buttons. |
490
+ | size | `"xl" \| "lg" \| "md" \| "sm" \| "xs"` | `"md"` | Size applied to the group spacing. |
491
+ | gap | `number` | - | Custom gap between buttons (overrides size default). |
492
+ | description | `string` | - | Description text shown below the group. |
493
+ | error | `string` | - | Error message (replaces description when present). |
494
+ | aria-label | `string` | - | Accessible label for the button group. |
495
+ | aria-labelledby | `string` | - | ID of element labeling the group. |
496
+ | aria-describedby | `string` | - | ID of element describing the group. |
497
+ | id | `string` | - | HTML id attribute. |
498
+ | testID | `string` | - | Test identifier for testing frameworks. |
499
+
500
+ **ButtonGroup Gap Defaults:**
501
+
502
+ | Size | Vertical Gap | Horizontal Gap |
503
+ | :--- | :----------- | :------------- |
504
+ | xl | 16 | 16 |
505
+ | lg | 16 | 16 |
506
+ | md | 12 | 16 |
507
+ | sm | 8 | 12 |
508
+ | xs | 4 | 12 |
509
+
510
+ ## Theming
511
+
512
+ Button components use the design system theme for colors and sizing:
513
+
514
+ ```typescript
515
+ // Colors accessed via theme
516
+ theme.colors.control.[tone].[variant].bg // Background color
517
+ theme.colors.control.[tone].[variant].bgHover // Hover background
518
+ theme.colors.control.[tone].[variant].bgPress // Pressed background
519
+ theme.colors.control.[tone].[variant].text // Text color
520
+ theme.colors.control.[tone].[variant].border // Border color
521
+
522
+ // Sizing accessed via theme
523
+ theme.sizing.button(size).height
524
+ theme.sizing.button(size).paddingHorizontal
525
+ theme.sizing.button(size).fontSize
526
+ theme.sizing.button(size).iconSize
527
+
528
+ // Border radius
529
+ theme.radius.button
530
+ ```
531
+
532
+ ## Accessibility
533
+
534
+ - All buttons use semantic `<button>` elements
535
+ - `IconButton` requires `aria-label` for screen reader support
536
+ - `ButtonGroup` uses `role="group"` with proper ARIA attributes
537
+ - Focus indicators follow WCAG guidelines
538
+ - Disabled buttons are properly announced to assistive technology
539
+ - Loading state is communicated via `aria-busy` attribute
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsolla/xui-button",
3
- "version": "0.99.0",
3
+ "version": "0.100.0",
4
4
  "main": "./web/index.js",
5
5
  "module": "./web/index.mjs",
6
6
  "types": "./web/index.d.ts",
@@ -13,9 +13,9 @@
13
13
  "test:coverage": "vitest run --coverage"
14
14
  },
15
15
  "dependencies": {
16
- "@xsolla/xui-core": "0.99.0",
17
- "@xsolla/xui-icons": "0.99.0",
18
- "@xsolla/xui-primitives-core": "0.99.0"
16
+ "@xsolla/xui-core": "0.100.0",
17
+ "@xsolla/xui-icons": "0.100.0",
18
+ "@xsolla/xui-primitives-core": "0.100.0"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "react": ">=16.8.0",