@jobber/components-native 0.101.5 → 0.101.6

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 (67) hide show
  1. package/dist/docs/ActionItem/ActionItem.md +65 -0
  2. package/dist/docs/ActionItemGroup/ActionItemGroup.md +33 -0
  3. package/dist/docs/ActionLabel/ActionLabel.md +43 -0
  4. package/dist/docs/ActivityIndicator/ActivityIndicator.md +116 -0
  5. package/dist/docs/Animation/Animation.md +71 -0
  6. package/dist/docs/AtlantisThemeContext/AtlantisThemeContext.md +256 -0
  7. package/dist/docs/AutoLink/AutoLink.md +47 -0
  8. package/dist/docs/Banner/Banner.md +390 -0
  9. package/dist/docs/Borders/Borders.md +45 -0
  10. package/dist/docs/BottomSheet/BottomSheet.md +67 -0
  11. package/dist/docs/Button/Button.md +918 -0
  12. package/dist/docs/ButtonGroup/ButtonGroup.md +89 -0
  13. package/dist/docs/Card/Card.md +270 -0
  14. package/dist/docs/Checkbox/Checkbox.md +69 -0
  15. package/dist/docs/Chip/Chip.md +371 -0
  16. package/dist/docs/Colors/Colors.md +217 -0
  17. package/dist/docs/Content/Content.md +67 -0
  18. package/dist/docs/ContentOverlay/ContentOverlay.md +64 -0
  19. package/dist/docs/Disclosure/Disclosure.md +161 -0
  20. package/dist/docs/Divider/Divider.md +84 -0
  21. package/dist/docs/Elevations/Elevations.md +76 -0
  22. package/dist/docs/EmptyState/EmptyState.md +72 -0
  23. package/dist/docs/Flex/Flex.md +37 -0
  24. package/dist/docs/Form/Form.md +126 -0
  25. package/dist/docs/FormField/FormField.md +57 -0
  26. package/dist/docs/FormatFile/FormatFile.md +56 -0
  27. package/dist/docs/Glimmer/Glimmer.md +143 -0
  28. package/dist/docs/Heading/Heading.md +132 -0
  29. package/dist/docs/Icon/Icon.md +585 -0
  30. package/dist/docs/IconButton/IconButton.md +25 -0
  31. package/dist/docs/InputCurrency/InputCurrency.md +61 -0
  32. package/dist/docs/InputDate/InputDate.md +133 -0
  33. package/dist/docs/InputEmail/InputEmail.md +69 -0
  34. package/dist/docs/InputFieldWrapper/InputFieldWrapper.md +70 -0
  35. package/dist/docs/InputNumber/InputNumber.md +72 -0
  36. package/dist/docs/InputPassword/InputPassword.md +61 -0
  37. package/dist/docs/InputPressable/InputPressable.md +64 -0
  38. package/dist/docs/InputSearch/InputSearch.md +49 -0
  39. package/dist/docs/InputText/InputText.md +324 -0
  40. package/dist/docs/InputTime/InputTime.md +54 -0
  41. package/dist/docs/Opacity/Opacity.md +12 -0
  42. package/dist/docs/ProgressBar/ProgressBar.md +39 -0
  43. package/dist/docs/Radii/Radii.md +23 -0
  44. package/dist/docs/ResponsiveBreakpoint/ResponsiveBreakpoint.md +74 -0
  45. package/dist/docs/Select/Select.md +213 -0
  46. package/dist/docs/Spacing/Spacing.md +103 -0
  47. package/dist/docs/StatusLabel/StatusLabel.md +119 -0
  48. package/dist/docs/Switch/Switch.md +54 -0
  49. package/dist/docs/Text/Text.md +368 -0
  50. package/dist/docs/TextList/TextList.md +29 -0
  51. package/dist/docs/ThumbnailList/ThumbnailList.md +16 -0
  52. package/dist/docs/Toast/Toast.md +71 -0
  53. package/dist/docs/Typography/Typography.md +170 -0
  54. package/dist/docs/choosing-components/choosing-components.md +76 -0
  55. package/dist/docs/customizing-components/customizing-components.md +167 -0
  56. package/dist/docs/disabled-states/disabled-states.md +86 -0
  57. package/dist/docs/empty-states/empty-states.md +126 -0
  58. package/dist/docs/errors/errors.md +114 -0
  59. package/dist/docs/index.md +64 -0
  60. package/dist/docs/interaction/interaction.md +109 -0
  61. package/dist/docs/page-layouts/page-layouts.md +323 -0
  62. package/dist/docs/scaffolding/scaffolding.md +109 -0
  63. package/dist/docs/settings/settings.md +58 -0
  64. package/dist/docs/usage-guidelines/usage-guidelines.md +177 -0
  65. package/dist/package.json +8 -4
  66. package/dist/tsconfig.build.tsbuildinfo +1 -1
  67. package/package.json +8 -4
@@ -0,0 +1,918 @@
1
+ # Button
2
+
3
+ ## Summary
4
+
5
+ Buttons are a core user interface component. They allow service providers (SPs)
6
+ to initiate, complete, or reverse actions such as submitting forms, triggering
7
+ workflows, or navigating between screens. At Jobber, Buttons empower SPs to
8
+ complete work quickly and confidently, whether they're logging in, saving a job,
9
+ or taking action from a modal.
10
+
11
+ The right button to use depends on:
12
+
13
+ * The type of action the SP is attempting to complete
14
+ * The hierarchy of the interface the button lives in
15
+
16
+ ## Anatomy
17
+
18
+ A Button typically includes:
19
+
20
+ * Label (required): Clear, concise verb describing the action
21
+ * Icon (optional): Reinforces meaning or improves scanability
22
+
23
+ ## Behaviour
24
+
25
+ Buttons should be easy to find and easy to use. They should appear near the
26
+ thing they affect, or in a place that makes sense for the next step.
27
+
28
+ * In modals: Buttons usually sit at the bottom right. The main action comes
29
+ last, and cancel is placed before it. In some cases tertiary buttons can be
30
+ placed on the far left.
31
+
32
+ * On pages: Buttons should line up with related content, like form fields or
33
+ cards. Avoid placing actions too far away from the thing they’re acting on.
34
+
35
+ * When tapped or clicked: buttons should respond right away. Changing state to
36
+ show that something is happening. If an action takes time, use a loading state
37
+ so SPs know it’s in progress and don’t tap again by mistake.
38
+
39
+ Buttons don’t dismiss things unless they’re meant to. If tapping a button closes
40
+ a modal or banner, that should feel intentional rather than surprising. An
41
+ example of this would be in confirmation modals. Secondary buttons that act as a
42
+ way for the SP to reverse the action they've states they want to take, such as
43
+ "Go Back", would dismiss the modal as this is the expected action.
44
+
45
+ #### Sizes
46
+
47
+ Buttons come in three sizes. `Base` is the default and should be used for almost
48
+ every use case.
49
+
50
+ Only use `large` for extremely spacious interfaces such as a login form, and
51
+ `small` when the interface has extremely tight size constraints.
52
+
53
+ ```tsx
54
+ import React from "react";
55
+ import { Button } from "@jobber/components/Button";
56
+ import { Content } from "@jobber/components/Content";
57
+
58
+ export function ButtonSizesExample() {
59
+ return (
60
+ <Content>
61
+ <Content>
62
+ <Button label="Small" size="small" />
63
+ </Content>
64
+ <Content>
65
+ <Button label="Base" />
66
+ </Content>
67
+ <Content>
68
+ <Button label="Large" size="large" />
69
+ </Content>
70
+ </Content>
71
+ );
72
+ }
73
+ ```
74
+
75
+ #### Disabled Buttons
76
+
77
+ As a best practice, do not design with disabled button states. This has negative
78
+ impacts on accessibility as well as an increase in complexity for users to
79
+ understand why the interface is disabled and how to resolve it.
80
+
81
+ Before progressing,
82
+ [review Jobber's guidance on disabled states](../disabled-states/disabled-states.md) to
83
+ ensure you can't design a flow without it.
84
+
85
+ If, after reviewing the documentation, you still believe you can't design a flow
86
+ without disabling a button, this is how you do it:
87
+
88
+ ```tsx
89
+ import React from "react";
90
+ import { Button } from "@jobber/components/Button";
91
+
92
+ export function ButtonDisabledExample() {
93
+ return <Button label="Do the Thing" disabled />;
94
+ }
95
+ ```
96
+
97
+ #### Use of icons
98
+
99
+ You can use icons inside the buttons. They can stand-alone or appear alongside
100
+ the button label.
101
+
102
+ **Note:** If an icon is used in a stand-alone method, an `ariaLabel` must be
103
+ present to describe the button.
104
+
105
+ ```tsx
106
+ import React from "react";
107
+ import { Button } from "@jobber/components/Button";
108
+ import { Content } from "@jobber/components/Content";
109
+
110
+ export function ButtonIconsExample() {
111
+ return (
112
+ <Content>
113
+ <Content>
114
+ <Button type="secondary" icon="user" ariaLabel="I'm a person" />
115
+ </Content>
116
+ <Content>
117
+ <Button label="More" type="secondary" icon="more" />
118
+ </Content>
119
+ <Content>
120
+ <Button
121
+ label="Actions"
122
+ type="secondary"
123
+ icon="arrowDown"
124
+ iconOnRight={true}
125
+ />
126
+ </Content>
127
+ </Content>
128
+ );
129
+ }
130
+ ```
131
+
132
+ #### Loading states
133
+
134
+ Use a loading state on a button when clicking it triggers a background action
135
+ that takes more than a moment to complete. This helps SPs understand that
136
+ something is happening, prevents them from clicking again, and avoids duplicated
137
+ actions.
138
+
139
+ Only show the loading state **after** the button is clicked, not pre-emptively
140
+ or passively.
141
+
142
+ ```tsx
143
+ import React from "react";
144
+ import { Button } from "@jobber/components/Button";
145
+ import { Content } from "@jobber/components/Content";
146
+
147
+ export function ButtonLoadingExample() {
148
+ return (
149
+ <Content>
150
+ <Content>
151
+ <Button
152
+ label="Deleting..."
153
+ type="primary"
154
+ variation="destructive"
155
+ loading={true}
156
+ />
157
+ </Content>
158
+ <Content>
159
+ <Button
160
+ label="Loading..."
161
+ type="tertiary"
162
+ variation="learning"
163
+ loading={true}
164
+ />
165
+ </Content>
166
+ <Content>
167
+ <Button label="Canceling..." variation="subtle" loading={true} />
168
+ </Content>
169
+ </Content>
170
+ );
171
+ }
172
+ ```
173
+
174
+ ## Variants
175
+
176
+ ### Work
177
+
178
+ Work buttons help SPs move through their workflows with clarity and confidence.
179
+ They can start or finish a task, go back, cancel, or confirm—depending on where
180
+ the SP is and what they need to do next.
181
+
182
+ All work buttons should feel purposeful, with labels that clearly communicate
183
+ outcomes.
184
+
185
+ Work buttons come in three visual variants:\
186
+ **primary**, **secondary**, and **tertiary**.
187
+
188
+ #### Primary
189
+
190
+ The most important action in a view. Examples include the Save Job button, Log
191
+ In, or the main action in a dialog. There should be one Primary action per view,
192
+ at most.
193
+
194
+ ```tsx
195
+ import React from "react";
196
+ import { Button } from "@jobber/components/Button";
197
+ import { Content } from "@jobber/components/Content";
198
+
199
+ export function ButtonWorkExample() {
200
+ return (
201
+ <Content>
202
+ <Content>
203
+ <Button label="Do the Thing" />
204
+ </Content>
205
+ <Content>
206
+ <Button label="Do the Thing" type="secondary" />
207
+ </Content>
208
+ <Content>
209
+ <Button label="Do the Thing" type="tertiary" />
210
+ </Content>
211
+ </Content>
212
+ );
213
+ }
214
+ ```
215
+
216
+ | ✅ Do | ❌ Don’t |
217
+ | ----------------------------------- | ------------------------------------ |
218
+ | Use for high-value, forward actions | Use to answer a question |
219
+ | Use a clear, verb-first label | Use conversational labels like “Yes” |
220
+ | Keep the label short and specific | Use ellipses (e.g. “Save and...”) |
221
+ | Use one primary per view | Stack multiple primary buttons |
222
+
223
+ **Please note:** At Jobber, we want to avoid use of elipsis in buttons. Going
224
+ forward, buttons with grouped actions should be handled by Splitbutton.
225
+
226
+ #### Secondary
227
+
228
+ Use a secondary button when there are one or more alternate actions available
229
+ that are less important than the primary one. Secondary buttons support the main
230
+ task—they don’t compete with it.
231
+
232
+ They’re used for actions like going back, or previewing before sending.
233
+
234
+ | ✅ Do | ❌ Don’t |
235
+ | ------------------------------------ | ----------------------------------- |
236
+ | Use for optional or supporting steps | Use as a second primary |
237
+ | Pair with a primary button | Stack multiple secondaries together |
238
+ | Keep labels short and outcome-based | Use vague text like “Maybe Later” |
239
+
240
+ #### Tertiary
241
+
242
+ Use a tertiary button for low-emphasis actions that happen within a view—like
243
+ editing a section, showing more details, or navigating between tabs or steps.
244
+
245
+ These buttons are unobtrusive and helpful, but shouldn’t pull attention away
246
+ from the task at hand.
247
+
248
+ | ✅ Do | ❌ Don’t |
249
+ | -------------------------------- | ------------------------------------- |
250
+ | Use for in-context interactions | Use for key tasks or submissions |
251
+ | Pair with a primary or secondary | Use as a workaround for layout issues |
252
+ | Reserve for non-critical actions | Make them the only path forward |
253
+
254
+ ### Learning
255
+
256
+ Use learning buttons to help SPs understand Jobber—whether that’s exploring a
257
+ feature, discovering a workflow, or seeing how Jobber can support their
258
+ business.
259
+
260
+ These buttons are for education. They often appear in onboarding, product tours,
261
+ or marketing surfaces.
262
+
263
+ Learning buttons follow the same hierarchy as work buttons:\
264
+ **primary**, **secondary**, and **tertiary**, depending on emphasis.
265
+
266
+ #### Primary, Secondary, Tertiary
267
+
268
+ The usage pattern for Learning Actions should follow the same logic as Work
269
+ Actions for all three levels.
270
+
271
+ ```tsx
272
+ import React from "react";
273
+ import { Button } from "@jobber/components/Button";
274
+ import { Content } from "@jobber/components/Content";
275
+
276
+ export function ButtonLearningExample() {
277
+ return (
278
+ <Content>
279
+ <Content>
280
+ <Button
281
+ label="Learn More"
282
+ variation="learning"
283
+ url="//getjobber.com"
284
+ external={true}
285
+ />
286
+ </Content>
287
+ <Content>
288
+ <Button
289
+ label="Learn More"
290
+ variation="learning"
291
+ type="secondary"
292
+ url="//getjobber.com"
293
+ external={true}
294
+ />
295
+ </Content>
296
+ <Content>
297
+ <Button
298
+ label="Learn More"
299
+ variation="learning"
300
+ type="tertiary"
301
+ url="//getjobber.com"
302
+ external={true}
303
+ />
304
+ </Content>
305
+ </Content>
306
+ );
307
+ }
308
+ ```
309
+
310
+ | ✅ Do | ❌ Don’t |
311
+ | ----------------------------------- | ------------------------------------ |
312
+ | Use in onboarding or educational UI | Use in job, quote, or invoice flows |
313
+ | Use when the goal is understanding | Use when the goal is completing work |
314
+ | Pair with clear, relevant messaging | Drop into views without context |
315
+
316
+ **Please note:** "Learn More" is the most common CTA for learning buttons. It
317
+ should be the default text. It's important to ensure that the accompanying
318
+ content is clear enough so that it's obvious to SPs what they'll be learning
319
+ about when they interact with learning button with a "Learn More" CTA.
320
+
321
+ ### Destructive
322
+
323
+ Destructive buttons are used to remove something from Jobber. They should be
324
+ clear, deliberate, and hard to activate by mistake.
325
+
326
+ Destructive actions come in **primary**, **secondary**, and **tertiary** styles
327
+ depending on impact and context.
328
+
329
+ #### Primary
330
+
331
+ A Primary destructive action will permanently destroy an object carrying
332
+ significant data, such as a job, quote, or client. This destructive action
333
+ should be contained in a [ConfirmationModal](/components/ConfirmationModal)
334
+ triggered by clicking a secondary or tertiary destructive button.
335
+
336
+ #### Secondary or Tertiary
337
+
338
+ Use a secondary or tertiary destructive button when:
339
+
340
+ * The deletion is low-stakes or easily reversible
341
+ * The button is a trigger for a **confirmation modal** that holds the primary
342
+ destructive action
343
+
344
+ In both cases, make sure it’s doesn’t compete with core workflow actions.
345
+
346
+ ```tsx
347
+ import React from "react";
348
+ import { Button } from "@jobber/components/Button";
349
+ import { Content } from "@jobber/components/Content";
350
+
351
+ export function ButtonDestructiveExample() {
352
+ return (
353
+ <Content>
354
+ <Content>
355
+ <Button label="Delete" variation="destructive" />
356
+ </Content>
357
+ <Content>
358
+ <Button label="Delete" variation="destructive" type="secondary" />
359
+ </Content>
360
+ <Content>
361
+ <Button label="Delete" variation="destructive" type="tertiary" />
362
+ </Content>
363
+ </Content>
364
+ );
365
+ }
366
+ ```
367
+
368
+ ### Subtle
369
+
370
+ Use a "subtle" button when you want the visual appearance to be more *subtle.*
371
+ This variation of button uses subdued color, allowing it to sit comfortably
372
+ alongside more prominent content.
373
+
374
+ Subtle buttons are visually de-emphasized and designed for low-priority or
375
+ non-blocking actions. They use minimal styling so they sit quietly next to more
376
+ prominent content—ideal for secondary navigation, opt-outs, or dismissing
377
+ lightweight UI.
378
+
379
+ Think of icon actions in a navigation bar, buttons to dismiss a modal, or opting
380
+ out of completing an action they have triggered.
381
+
382
+ *Note: This is still known as "Cancel" on mobile. See
383
+ [Mobile/Cancel example](/storybook/mobile/?path=/story/components-actions-button--cancel).*
384
+
385
+ #### Primary, Secondary, and Tertiary
386
+
387
+ A key distinction between subtle buttons and the other variations is that the
388
+ primary/secondary/tertiary scale is styled differently, but the conceptual
389
+ hierarchy is the same. Notably, the tertiary subtle button has a transparent
390
+ background, allowing it to be *extra* subtle when placed overtop
391
+ `surface--background` elements such as a Modal header.
392
+
393
+ ```tsx
394
+ import React from "react";
395
+ import { Button } from "@jobber/components/Button";
396
+ import { Content } from "@jobber/components/Content";
397
+
398
+ export function ButtonSubtleExample() {
399
+ return (
400
+ <Content>
401
+ <Content>
402
+ <Content>
403
+ <Button label="Cancel" type="primary" variation="subtle" />
404
+ </Content>
405
+ <Content>
406
+ <Button label="Dismiss" type="secondary" variation="subtle" />
407
+ </Content>
408
+ <Content>
409
+ <Button label="Maybe Later" type="tertiary" variation="subtle" />
410
+ </Content>
411
+ </Content>
412
+ <div
413
+ style={{
414
+ backgroundColor: "var(--color-surface--background)",
415
+ padding: "var(--space-base)",
416
+ }}
417
+ >
418
+ <Button
419
+ variation="subtle"
420
+ type="tertiary"
421
+ icon="search"
422
+ aria-label="search"
423
+ />
424
+ <Button
425
+ variation="subtle"
426
+ type="tertiary"
427
+ icon="cog"
428
+ aria-label="settings"
429
+ />
430
+ <Button
431
+ variation="subtle"
432
+ type="tertiary"
433
+ icon="help"
434
+ aria-label="help"
435
+ />
436
+ <Button
437
+ variation="subtle"
438
+ type="tertiary"
439
+ icon="more"
440
+ aria-label="more"
441
+ />
442
+ </div>
443
+ </Content>
444
+ );
445
+ }
446
+ ```
447
+
448
+ ## Content guidelines
449
+
450
+ Effective button labels are clear, concise, and action-driven. They help SPs
451
+ feel confident about what will happen next. These guidelines are focused on text
452
+ inside buttons, not hover labels or tooltip content.
453
+
454
+ #### Mental model: What will happen when I click this
455
+
456
+ Use **verb-first phrasing** to emphasize action and reduce hesitation.
457
+
458
+ Button text should reflect the outcome of the action. Not necessarily the
459
+ system's implementation.
460
+
461
+ Remember that SPs are often working quickly; on mobile, between visits, or in
462
+ the field. That means buttons should support fast, confident decision-making.
463
+
464
+ | ✅ Do | ❌ Don’t |
465
+ | ------------ | ---------------- |
466
+ | Send Invoice | Continue to Send |
467
+ | Save Job | Click Here |
468
+ | Mark as Done | Done |
469
+
470
+ #### Reflect the outcome
471
+
472
+ It's a good idea to focus on the outcome of the action with buttons, rather than
473
+ exposing technical complexity (unless it's particularly meaningful to SPs).
474
+
475
+ | ✅ Do | ❌ Don’t |
476
+ | --------------- | ------------------------ |
477
+ | Download Report | Export to CSV |
478
+ | Send Quote | Generate PDF |
479
+ | Schedule Visit | Create Visit in Schedule |
480
+
481
+ SPs think in terms of **visits, quotes, jobs, and invoices**. Not CSV files or
482
+ data exports. At Jobber, we prefer terminology grounded in the workflow of
483
+ running a service business.
484
+
485
+ #### Keep it concise
486
+
487
+ * Aim for **2–3 words**
488
+ * Don’t abbreviate unless the meaning is universally known
489
+ * Avoid truncation, ellipses, or filler words like “Please” or “Now”
490
+
491
+ | ✅ Do | ❌ Don’t |
492
+ | ---------- | ------------------ |
493
+ | Add Client | Add a New Client |
494
+ | Delete | Delete Permanently |
495
+ | Sign Up | Sign Up Now |
496
+
497
+ Try to default to being concise. It helps reduce visual noise, especially on
498
+ mobile. Key for SPs using Jobber on the go.
499
+
500
+ #### Use title case
501
+
502
+ Capitalize all major words in button labels. Don't capitalize:
503
+
504
+ * Articles: *a, an, the*
505
+ * Coordinating conjunctions: *and, but, or*
506
+ * Short prepositions: *to, by, at*
507
+
508
+ | ✅ Do | ❌ Don’t |
509
+ | ------------ | ------------ |
510
+ | Go to Visits | Go To Visits |
511
+ | Save Job | SAVE JOB |
512
+
513
+ Title case supports scannability and will help us maintain visual consistency
514
+ across SP workflows.
515
+
516
+ #### Avoid ampersands
517
+
518
+ Avoid using ampersands in buttons. They’re often introduced to save space, but
519
+ they create inconsistency and visual clutter. Put simply, they draw attention to
520
+ the most irrelevant part of the sentence.
521
+
522
+ Use “and” unless there’s a strong reason not to—and even then, consider
523
+ simplifying the label instead.
524
+
525
+ | ✅ Do | ❌ Don’t |
526
+ | -------------------- | ------------------ |
527
+ | Review and Send | Review & Send |
528
+ | Approve and Schedule | Approve & Schedule |
529
+
530
+ #### Be specific
531
+
532
+ Generic CTAs like “Submit” or “Continue” generally don’t give SPs confidence.
533
+ That said, they are fine if there's another visual indicator of what comes next
534
+ E.g. a stepper.
535
+
536
+ When there's no other indication of what is coming next, try to be explicit
537
+ about what the button does or what's to come.
538
+
539
+ | ✅ Do | ❌ Don’t |
540
+ | ---------------- | -------- |
541
+ | Book Appointment | Submit |
542
+ | Send Quote | Continue |
543
+
544
+ #### Buttons in confirmation modals
545
+
546
+ In confirmation modals, button labels should reinforce the decision the SP is
547
+ making—not just echo the UI element that triggered it.
548
+
549
+ **Primary action buttons should restate the consequence of the action.** This
550
+ helps avoid second-guessing and reduces accidental taps.
551
+
552
+ | ✅ Do | ❌ Don’t |
553
+ | ---------------- | ----------- |
554
+ | Delete Visit | Yes, Delete |
555
+ | Delete Timesheet | Confirm |
556
+ | Send Invoice | Submit |
557
+ | Remove Line Item | OK |
558
+
559
+ **Secondary buttons** should be clear but low-friction. Stick with **Go Back**,
560
+ unless you need to clarify the context.
561
+
562
+ We want to avoid questions as titles in confirmation modals, as this can add to
563
+ visual clutter and increase the cognitive load. As a result, the button should
564
+ reflect that. No answering a question, just confirming the action.
565
+
566
+ ## Do's and Don'ts
567
+
568
+ In summary, here are some general rules to follow when working with the button
569
+ component:
570
+
571
+ #### Do:
572
+
573
+ * ✅ Use clear, verb-first labels that describe the outcome
574
+ * ✅ Keep button text short (1–3 words max)
575
+ * ✅ Use title case for button labels
576
+ * ✅ Show a loading state for async actions
577
+ * ✅ Use one primary button per view
578
+ * ✅ Use hierarchy (primary, secondary, tertiary) to guide attention
579
+ * ✅ Use destructive styling only for real deletions
580
+ * ✅ Place buttons near the content or object they act on
581
+ * ✅ Pair “Learn More” learning buttons with clear supporting content
582
+ * ✅ Ensure icon-only buttons have accessible labels
583
+
584
+ #### Don't:
585
+
586
+ * ❌ Use vague or conversational labels like “Yes” or “Okay”
587
+ * ❌ Stack multiple primary buttons together
588
+ * ❌ Style non-destructive actions (like “Archive”) as destructive
589
+ * ❌ Position buttons too far away from the related content
590
+ * ❌ Rely on the button label alone to explain what it does
591
+ * ❌ Assume icons are self-explanatory without text or `aria-label`
592
+
593
+ ## Accessibility notes
594
+
595
+ * Avoid using disabled buttons when possible—they increase complexity and make
596
+ it harder for SPs to understand what to do next
597
+
598
+ * If a button uses only an icon, include an `aria-label` so screen readers can
599
+ describe the action
600
+
601
+ * Show clear feedback when a button is tapped or clicked. Use a loading state
602
+ where applicable to prevent repeated taps
603
+
604
+
605
+ ## Configuration
606
+
607
+ ### Client-side routing (web only)
608
+
609
+ A Button can be used for client-side routing as well. When using a Button to
610
+ handle the client-side routing, use the `to` prop. Notice when you click below
611
+ that the URL will change to the appropriate route. See
612
+ [Web/Client Side Routing example](/storybook/web/?path=/story/components-actions-button--client-side-routing).
613
+
614
+ ### Form submit (web only)
615
+
616
+ Passing the `submit` prop will allow a Button to submit a
617
+ [Form](../Form/Form.md). Since submitting a form is a specific action, only a
618
+ form is a specific action, only `variation="work"` and `type="primary"` are
619
+ allowed. See
620
+ [Web/Form Submit example](/storybook/web/?path=/story/components-actions-button--form-submit)
621
+
622
+ Since this type of `Button` will only be used to submit a form, it does not make
623
+ sense to allow the `external`, `onClick`, `to`, or `url` props.
624
+
625
+ ## Component customization
626
+
627
+ ### Composable usage (web only)
628
+
629
+ The Button component is built with the `Button.Label`, `Button.Icon` components.
630
+ This composition is the recommended way to customize the Button component
631
+ instead of the prop driven usages.
632
+
633
+ `Button.Label` is used to display the label of the button.
634
+
635
+ `Button.Icon` is used to display an icon on the button.
636
+
637
+ Buttons with icons and labels can be converted using the following example:
638
+
639
+ ```tsx
640
+ <Button icon="add" label="Add" />
641
+ ```
642
+
643
+ becomes
644
+
645
+ ```tsx
646
+ <Button>
647
+ <Button.Icon name="add" />
648
+ <Button.Label>Add</Button.Label>
649
+ </Button>
650
+ ```
651
+
652
+ To convert a button with an icon on the right, use the following example:
653
+
654
+ ```tsx
655
+ <Button icon="add" label="Add" iconOnRight />
656
+ ```
657
+
658
+ becomes
659
+
660
+ ```tsx
661
+ <Button>
662
+ <Button.Label>Add</Button.Label>
663
+ <Button.Icon name="add" />
664
+ </Button>
665
+ ```
666
+
667
+ ### Styling another component with Button styles
668
+
669
+ Using the `useButtonStyles` hook, the styles for a button can be applied to
670
+ another component. This is useful for styling Client-side routing links to look
671
+ like a Button.
672
+
673
+ `useButtonStyles` is a hook that provides three sets of styles:
674
+
675
+ * `wrapper`: The styles for the button wrapper. This handles the background
676
+ color, border radius, and padding.
677
+ * `children`: The styles for the button children. This handles the icon and
678
+ label spacing.
679
+ * `combined`: The combined styles of `wrapper` and `children`. This is useful if
680
+ you don't want to customize the styles
681
+
682
+ The following example shows how to do this with React Router:
683
+
684
+ ```tsx
685
+ import { Link } from "react-router-dom";
686
+
687
+ function LinkButton() {
688
+ const buttonStyles = useButtonStyles({
689
+ type: "tertiary",
690
+ variation: "subtle",
691
+ });
692
+
693
+ return (
694
+ <Link
695
+ to="/"
696
+ className={classnames(buttonStyles.wrapper, buttonStyles.children)}
697
+ // or
698
+ className={buttonStyles.combined}
699
+ >
700
+ <Button.Label>Navigate Home</Button.Label>
701
+ <Button.Icon name="home" />
702
+ </Link>
703
+ );
704
+ }
705
+ ```
706
+
707
+ ### UNSAFE\_ props (advanced usage)
708
+
709
+ General information for using `UNSAFE_` props can be found
710
+ [here](../customizing-components/customizing-components.md).
711
+
712
+ **Note**: Use of `UNSAFE_` props is **at your own risk** and should be
713
+ considered a **last resort**. Future Button updates may lead to unintended
714
+ breakages.
715
+
716
+ **Use of !important** You will see that `!important` is used for `path.fill` of
717
+ Icon's UNSAFE props. This is needed since Button's children element `fill` is
718
+ set to `inherit !important`. *However*, in React `!important` is unreliable with
719
+ `style` attribute when used with non css variable colors.
720
+
721
+ #### UNSAFE\_className and UNSAFE\_style (web)
722
+
723
+ The Button component has multiple elements that can be targeted with classes or
724
+ styles:
725
+
726
+ * `container`: The root button element
727
+ * `buttonLabel`: The text label element of the Button
728
+ * `buttonIcon`: The icon element of the Button
729
+
730
+ **Note** that `buttonLabel` and `buttonIcon` are to be used in a non-composed
731
+ Button. If you're using the composed Button with children, you should use the
732
+ UNSAFE props on the sub-components.
733
+
734
+ ### UNSAFE\_className
735
+
736
+ Use `UNSAFE_className` to apply custom classes to the Button. This can be useful
737
+ for applying styles via CSS Modules.
738
+
739
+ ##### Non composed UNSAFE\_className usage
740
+
741
+ ```tsx
742
+ <Button
743
+ label="Custom styling with className"
744
+ icon="add"
745
+ UNSAFE_className={{
746
+ container: styles.customButton,
747
+ buttonLabel: { textStyle: styles.customLabel },
748
+ buttonIcon: { svg: styles.customIcon, path: styles.customIconPath },
749
+ }}
750
+ />
751
+
752
+ // css
753
+ .customButton {
754
+ border-radius: 24px;
755
+ }
756
+
757
+ .customLabel {
758
+ color: var(--color-blue);
759
+ text-transform: uppercase;
760
+ }
761
+
762
+ .customIcon {
763
+ background-color: var(--color-red);
764
+ }
765
+
766
+ .customIconPath {
767
+ fill: var(--color-green) !important;
768
+ }
769
+ ```
770
+
771
+ ##### Composed UNSAFE\_className usage
772
+
773
+ ```tsx
774
+ <Button>
775
+ <Button.Icon
776
+ name="sparkles"
777
+ UNSAFE_className={{ svg: styles.customIcon, path: styles.customIconPath }}
778
+ />
779
+ <Button.Label
780
+ UNSAFE_className={{
781
+ textStyle: styles.customLabel,
782
+ }}
783
+ >
784
+ label 1
785
+ </Button.Label>
786
+ </Button>
787
+ ```
788
+
789
+ ### UNSAFE\_style
790
+
791
+ The `UNSAFE_style` prop provides granular control over the Button's appearance
792
+ through inline styles. The structure mirrors `UNSAFE_className` to allow for
793
+ consistent styling patterns.
794
+
795
+ ##### Non composed UNSAFE\_style usage
796
+
797
+ ```tsx
798
+ <Button
799
+ label="Custom styling with style"
800
+ icon="sparkles"
801
+ UNSAFE_style={{
802
+ container: {
803
+ borderRadius: "24px",
804
+ },
805
+ buttonLabel: {
806
+ textStyle: {
807
+ color: "var(--color-blue)",
808
+ textTransform: "uppercase",
809
+ },
810
+ },
811
+ buttonIcon: {
812
+ svg: {
813
+ backgroundColor: "var(--color-red)",
814
+ },
815
+ path: {
816
+ fill: "var(--color-green) !important",
817
+ },
818
+ },
819
+ }}
820
+ />
821
+ ```
822
+
823
+ ##### Composed UNSAFE\_style usage
824
+
825
+ ```tsx
826
+ <Button UNSAFE_style={{ borderRadius: "24px" }}>
827
+ <Button.Icon
828
+ name="sparkles"
829
+ UNSAFE_style={{
830
+ svg: { backgroundColor: "var(--color-red)" },
831
+ path: { fill: "var(--color-green) !important" },
832
+ }}
833
+ />
834
+ <Button.Label
835
+ UNSAFE_style={{
836
+ textStyle: {
837
+ color: "var(--color-red)",
838
+ textDecoration: "underline",
839
+ },
840
+ }}
841
+ >
842
+ label 1
843
+ </Button.Label>
844
+ </Button>
845
+ ```
846
+
847
+ #### UNSAFE\_style (mobile)
848
+
849
+ The mobile Button component has four elements that can be targeted with styles.
850
+ These are the container, content container, icon container, and action label
851
+ container.
852
+
853
+ React Native does not support className. Instead, you can use `UNSAFE_style` to
854
+ apply styles either inline or through a StyleSheet.
855
+
856
+ ##### Inline styles
857
+
858
+ ```tsx
859
+ UNSAFE_style: {
860
+ container: { backgroundColor: tokens["color-purple--light"] },
861
+ contentContainer: {
862
+ backgroundColor: tokens["color-purple--lighter"],
863
+ borderRadius: tokens["radius-large"],
864
+ },
865
+ iconContainer: { backgroundColor: tokens["color-purple"] },
866
+ actionLabelContainer: { paddingLeft: tokens["space-larger"] },
867
+ },
868
+ ```
869
+
870
+ ##### StyleSheet
871
+
872
+ ```tsx
873
+ // Button.tsx
874
+ UNSAFE_style={{
875
+ container: styles.customContainer,
876
+ contentContainer: styles.customContentContainer,
877
+ iconContainer: styles.customIconContainer,
878
+ actionLabelContainer: styles.customActionLabelContainer,
879
+ }}
880
+
881
+ // Button.style.ts
882
+ export const styles = StyleSheet.create({
883
+ customContainer: {
884
+ backgroundColor: tokens["color-purple--light"],
885
+ },
886
+ customContentContainer: {
887
+ borderRadius: tokens["radius-large"],
888
+ },
889
+ customIconContainer: {
890
+ backgroundColor: tokens["color-purple--lighter"],
891
+ },
892
+ customActionLabelContainer: {
893
+ paddingLeft: tokens["space-larger"],
894
+ },
895
+ });
896
+ ```
897
+
898
+
899
+ ## Props
900
+
901
+ ### Mobile
902
+
903
+ | Prop | Type | Required | Default | Description |
904
+ |------|------|----------|---------|-------------|
905
+ | `accessibilityHint` | `string` | No | — | Accessibility hint to help users understand what will happen when they press the button |
906
+ | `accessibilityLabel` | `string` | No | — | Accessibility label for the component. This is required for components that have an `icon` but not a `label`. If the... |
907
+ | `disabled` | `boolean` | No | `false` | Makes the button un-clickable |
908
+ | `fullHeight` | `boolean` | No | `false` | Will make the button scale to take up all the available height |
909
+ | `fullWidth` | `boolean` | No | `true` | Will make the button scale to take up all of the available width |
910
+ | `icon` | `IconNames` | No | — | Adds an leading icon beside the label. |
911
+ | `label` | `string` | No | — | Text to be displayed on the button |
912
+ | `loading` | `boolean` | No | `false` | Changes the button interface to imply loading and prevents the press callback |
913
+ | `onPress` | `() => void` | No | — | Press handler |
914
+ | `size` | `ButtonSize` | No | `base` | Defines the size of the button |
915
+ | `testID` | `string` | No | — | Used to locate this view in end-to-end tests. |
916
+ | `type` | `ButtonType` | No | `primary` | Sets the visual hierarchy |
917
+ | `UNSAFE_style` | `ButtonUnsafeStyle` | No | — | **Use at your own risk:** Custom style for specific elements. This should only be used as a **last resort**. Using th... |
918
+ | `variation` | `ButtonVariation` | No | `work` | Themes the button to the type of action it performs |