@commercetools/nimbus-mcp 2.11.0 → 3.0.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 (107) hide show
  1. package/data/docs/route-manifest.json +203 -0
  2. package/data/docs/routes/components-data-display-card.json +71 -5
  3. package/data/docs/routes/components-feedback-toast.json +1 -1
  4. package/data/docs/routes/components-layout-defaultpage.json +4 -4
  5. package/data/docs/routes/components-layout-modalpage.json +4 -4
  6. package/data/docs/routes/components-layout-scrollarea.json +3 -3
  7. package/data/docs/routes/components-media-avatar.json +24 -2
  8. package/data/docs/routes/patterns-actions-form-action-bar.json +412 -0
  9. package/data/docs/routes/patterns-dialogs-info-dialog.json +315 -0
  10. package/data/docs/routes/patterns-dialogs.json +78 -0
  11. package/data/docs/search-index.json +1 -1
  12. package/data/docs/types/AccordionRoot.json +2 -2
  13. package/data/docs/types/AlertDescription.json +8 -8
  14. package/data/docs/types/AlertDismissButton.json +2 -2
  15. package/data/docs/types/AlertTitle.json +8 -8
  16. package/data/docs/types/Avatar.json +8 -8
  17. package/data/docs/types/Badge.json +2 -2
  18. package/data/docs/types/Body.json +6 -6
  19. package/data/docs/types/Box.json +6 -6
  20. package/data/docs/types/Button.json +8 -8
  21. package/data/docs/types/Caption.json +6 -6
  22. package/data/docs/types/Card.json +1 -1
  23. package/data/docs/types/{CardContent.json → CardBody.json} +2 -2
  24. package/data/docs/types/CardFooter.json +27 -0
  25. package/data/docs/types/CardRoot.json +18 -48
  26. package/data/docs/types/Cell.json +6 -6
  27. package/data/docs/types/Checkbox.json +2 -2
  28. package/data/docs/types/Code.json +10 -10
  29. package/data/docs/types/Column.json +6 -6
  30. package/data/docs/types/ColumnGroup.json +6 -6
  31. package/data/docs/types/ColumnHeader.json +6 -6
  32. package/data/docs/types/ComboBoxListBox.json +6 -6
  33. package/data/docs/types/ComboBoxPopover.json +8 -8
  34. package/data/docs/types/ComboBoxRoot.json +8 -8
  35. package/data/docs/types/ComboBoxSection.json +6 -6
  36. package/data/docs/types/ComboBoxTrigger.json +6 -6
  37. package/data/docs/types/DataTable.json +2 -2
  38. package/data/docs/types/DataTableBody.json +6 -6
  39. package/data/docs/types/DataTableHeader.json +7 -7
  40. package/data/docs/types/DataTableRoot.json +2 -2
  41. package/data/docs/types/DataTableTable.json +6 -6
  42. package/data/docs/types/DatePicker.json +2 -2
  43. package/data/docs/types/DefaultPageRoot.json +2 -2
  44. package/data/docs/types/DialogCloseTrigger.json +2 -2
  45. package/data/docs/types/DraggableListField.json +10 -10
  46. package/data/docs/types/DraggableListItem.json +6 -6
  47. package/data/docs/types/DraggableListRoot.json +8 -8
  48. package/data/docs/types/DrawerCloseTrigger.json +2 -2
  49. package/data/docs/types/FieldErrors.json +2 -2
  50. package/data/docs/types/Flex.json +22 -22
  51. package/data/docs/types/Footer.json +6 -6
  52. package/data/docs/types/FormActionBar.json +200 -0
  53. package/data/docs/types/FormFieldRoot.json +2 -2
  54. package/data/docs/types/Grid.json +24 -24
  55. package/data/docs/types/Group.json +2 -2
  56. package/data/docs/types/Header.json +6 -6
  57. package/data/docs/types/Heading.json +8 -8
  58. package/data/docs/types/Icon.json +4 -4
  59. package/data/docs/types/IconButton.json +8 -8
  60. package/data/docs/types/Image.json +10 -10
  61. package/data/docs/types/Indicator.json +6 -6
  62. package/data/docs/types/InfoDialog.json +104 -0
  63. package/data/docs/types/InlineSvg.json +2 -2
  64. package/data/docs/types/Item.json +6 -6
  65. package/data/docs/types/Kbd.json +8 -8
  66. package/data/docs/types/Link.json +8 -8
  67. package/data/docs/types/ListIndicator.json +6 -6
  68. package/data/docs/types/ListItem.json +6 -6
  69. package/data/docs/types/ListRoot.json +10 -10
  70. package/data/docs/types/LoadingSpinner.json +2 -2
  71. package/data/docs/types/MultilineTextInput.json +2 -2
  72. package/data/docs/types/MultilineTextInputField.json +2 -2
  73. package/data/docs/types/NumberInput.json +2 -2
  74. package/data/docs/types/NumberInputField.json +2 -2
  75. package/data/docs/types/PageContentColumn.json +6 -6
  76. package/data/docs/types/PageContentRoot.json +6 -6
  77. package/data/docs/types/RichTextInput.json +2 -2
  78. package/data/docs/types/Root.json +10 -10
  79. package/data/docs/types/Row.json +6 -6
  80. package/data/docs/types/ScrollArea.json +6 -6
  81. package/data/docs/types/Separator.json +4 -4
  82. package/data/docs/types/SimpleGrid.json +28 -28
  83. package/data/docs/types/SplitButton.json +2 -2
  84. package/data/docs/types/Stack.json +2 -2
  85. package/data/docs/types/StepsRoot.json +2 -2
  86. package/data/docs/types/Switch.json +2 -2
  87. package/data/docs/types/TabNavItem.json +2 -2
  88. package/data/docs/types/TabNavRoot.json +2 -2
  89. package/data/docs/types/TableBody.json +6 -6
  90. package/data/docs/types/TableCaption.json +6 -6
  91. package/data/docs/types/TableCell.json +6 -6
  92. package/data/docs/types/TableColumn.json +6 -6
  93. package/data/docs/types/TableColumnGroup.json +6 -6
  94. package/data/docs/types/TableColumnHeader.json +6 -6
  95. package/data/docs/types/TableFooter.json +6 -6
  96. package/data/docs/types/TableHeader.json +6 -6
  97. package/data/docs/types/TableRoot.json +18 -18
  98. package/data/docs/types/TableRow.json +6 -6
  99. package/data/docs/types/TableScrollArea.json +6 -6
  100. package/data/docs/types/Text.json +8 -8
  101. package/data/docs/types/ToggleButtonGroupButton.json +6 -6
  102. package/data/docs/types/ToggleButtonGroupRoot.json +6 -6
  103. package/data/docs/types/Toolbar.json +2 -2
  104. package/data/docs/types/TooltipContent.json +2 -2
  105. package/data/docs/types/manifest.json +4 -1
  106. package/data/docs/types/toast.json +0 -15
  107. package/package.json +6 -6
@@ -3476,6 +3476,32 @@
3476
3476
  }
3477
3477
  ]
3478
3478
  },
3479
+ {
3480
+ "path": "/patterns/dialogs",
3481
+ "id": "Patterns-Dialogs",
3482
+ "title": "Dialogs",
3483
+ "description": "Pre-composed dialog patterns built on top of the Dialog primitive for common read-only and confirmation scenarios",
3484
+ "category": "Patterns",
3485
+ "tags": [
3486
+ "patterns",
3487
+ "dialogs",
3488
+ "overlays"
3489
+ ],
3490
+ "menu": [
3491
+ "Patterns",
3492
+ "Dialogs"
3493
+ ],
3494
+ "order": 2,
3495
+ "chunkName": "route-patterns-dialogs",
3496
+ "icon": "OpenInNew",
3497
+ "tabs": [
3498
+ {
3499
+ "key": "overview",
3500
+ "title": "Overview",
3501
+ "order": 0
3502
+ }
3503
+ ]
3504
+ },
3479
3505
  {
3480
3506
  "path": "/home/style-props/display",
3481
3507
  "id": "Style Props-Display",
@@ -4835,6 +4861,74 @@
4835
4861
  }
4836
4862
  ]
4837
4863
  },
4864
+ {
4865
+ "path": "/patterns/actions/form-action-bar",
4866
+ "id": "Patterns-FormActionBar",
4867
+ "title": "Form action bar",
4868
+ "exportName": "FormActionBar",
4869
+ "description": "This pattern component provides standard save/cancel/delete action buttons for form footers, working inside any footer slot.",
4870
+ "lifecycleState": "Beta",
4871
+ "category": "Patterns",
4872
+ "tags": [
4873
+ "component",
4874
+ "pattern",
4875
+ "actions",
4876
+ "Button",
4877
+ "Group"
4878
+ ],
4879
+ "menu": [
4880
+ "Patterns",
4881
+ "Actions",
4882
+ "Form action bar"
4883
+ ],
4884
+ "order": 999,
4885
+ "chunkName": "route-patterns-actions-form-action-bar",
4886
+ "tabs": [
4887
+ {
4888
+ "key": "overview",
4889
+ "title": "Overview",
4890
+ "order": 0
4891
+ },
4892
+ {
4893
+ "key": "dev",
4894
+ "title": "Implementation",
4895
+ "order": 3
4896
+ }
4897
+ ]
4898
+ },
4899
+ {
4900
+ "path": "/patterns/dialogs/info-dialog",
4901
+ "id": "Patterns-InfoDialog",
4902
+ "title": "Info dialog",
4903
+ "exportName": "InfoDialog",
4904
+ "description": "A pre-composed read-only informational dialog with a flat, minimal API.",
4905
+ "category": "Patterns",
4906
+ "tags": [
4907
+ "component",
4908
+ "pattern",
4909
+ "dialog",
4910
+ "InfoDialog"
4911
+ ],
4912
+ "menu": [
4913
+ "Patterns",
4914
+ "Dialogs",
4915
+ "Info dialog"
4916
+ ],
4917
+ "order": 999,
4918
+ "chunkName": "route-patterns-dialogs-info-dialog",
4919
+ "tabs": [
4920
+ {
4921
+ "key": "overview",
4922
+ "title": "Overview",
4923
+ "order": 0
4924
+ },
4925
+ {
4926
+ "key": "dev",
4927
+ "title": "Implementation",
4928
+ "order": 3
4929
+ }
4930
+ ]
4931
+ },
4838
4932
  {
4839
4933
  "path": "/patterns/fields/date-range-picker-field",
4840
4934
  "id": "Patterns-DateRangePickerField",
@@ -9829,6 +9923,32 @@
9829
9923
  "label": "Patterns",
9830
9924
  "order": 999,
9831
9925
  "items": [
9926
+ {
9927
+ "path": "/patterns/dialogs",
9928
+ "id": "Patterns-Dialogs",
9929
+ "title": "Dialogs",
9930
+ "description": "Pre-composed dialog patterns built on top of the Dialog primitive for common read-only and confirmation scenarios",
9931
+ "category": "Patterns",
9932
+ "tags": [
9933
+ "patterns",
9934
+ "dialogs",
9935
+ "overlays"
9936
+ ],
9937
+ "menu": [
9938
+ "Patterns",
9939
+ "Dialogs"
9940
+ ],
9941
+ "order": 2,
9942
+ "chunkName": "route-patterns-dialogs",
9943
+ "icon": "OpenInNew",
9944
+ "tabs": [
9945
+ {
9946
+ "key": "overview",
9947
+ "title": "Overview",
9948
+ "order": 0
9949
+ }
9950
+ ]
9951
+ },
9832
9952
  {
9833
9953
  "path": "/patterns/fields",
9834
9954
  "id": "Patterns-Fields",
@@ -9879,6 +9999,74 @@
9879
9999
  }
9880
10000
  ]
9881
10001
  },
10002
+ {
10003
+ "path": "/patterns/actions/form-action-bar",
10004
+ "id": "Patterns-FormActionBar",
10005
+ "title": "Form action bar",
10006
+ "exportName": "FormActionBar",
10007
+ "description": "This pattern component provides standard save/cancel/delete action buttons for form footers, working inside any footer slot.",
10008
+ "lifecycleState": "Beta",
10009
+ "category": "Patterns",
10010
+ "tags": [
10011
+ "component",
10012
+ "pattern",
10013
+ "actions",
10014
+ "Button",
10015
+ "Group"
10016
+ ],
10017
+ "menu": [
10018
+ "Patterns",
10019
+ "Actions",
10020
+ "Form action bar"
10021
+ ],
10022
+ "order": 999,
10023
+ "chunkName": "route-patterns-actions-form-action-bar",
10024
+ "tabs": [
10025
+ {
10026
+ "key": "overview",
10027
+ "title": "Overview",
10028
+ "order": 0
10029
+ },
10030
+ {
10031
+ "key": "dev",
10032
+ "title": "Implementation",
10033
+ "order": 3
10034
+ }
10035
+ ]
10036
+ },
10037
+ {
10038
+ "path": "/patterns/dialogs/info-dialog",
10039
+ "id": "Patterns-InfoDialog",
10040
+ "title": "Info dialog",
10041
+ "exportName": "InfoDialog",
10042
+ "description": "A pre-composed read-only informational dialog with a flat, minimal API.",
10043
+ "category": "Patterns",
10044
+ "tags": [
10045
+ "component",
10046
+ "pattern",
10047
+ "dialog",
10048
+ "InfoDialog"
10049
+ ],
10050
+ "menu": [
10051
+ "Patterns",
10052
+ "Dialogs",
10053
+ "Info dialog"
10054
+ ],
10055
+ "order": 999,
10056
+ "chunkName": "route-patterns-dialogs-info-dialog",
10057
+ "tabs": [
10058
+ {
10059
+ "key": "overview",
10060
+ "title": "Overview",
10061
+ "order": 0
10062
+ },
10063
+ {
10064
+ "key": "dev",
10065
+ "title": "Implementation",
10066
+ "order": 3
10067
+ }
10068
+ ]
10069
+ },
9882
10070
  {
9883
10071
  "path": "/patterns/fields/date-range-picker-field",
9884
10072
  "id": "Patterns-DateRangePickerField",
@@ -10917,6 +11105,11 @@
10917
11105
  "patterns": {
10918
11106
  "label": "Patterns",
10919
11107
  "items": [
11108
+ {
11109
+ "id": "Patterns-Dialogs",
11110
+ "title": "Dialogs",
11111
+ "path": "/patterns/dialogs"
11112
+ },
10920
11113
  {
10921
11114
  "id": "Patterns-Fields",
10922
11115
  "title": "Fields",
@@ -10927,6 +11120,16 @@
10927
11120
  "title": "Patterns",
10928
11121
  "path": "/patterns"
10929
11122
  },
11123
+ {
11124
+ "id": "Patterns-FormActionBar",
11125
+ "title": "Form action bar",
11126
+ "path": "/patterns/actions/form-action-bar"
11127
+ },
11128
+ {
11129
+ "id": "Patterns-InfoDialog",
11130
+ "title": "Info dialog",
11131
+ "path": "/patterns/dialogs/info-dialog"
11132
+ },
10930
11133
  {
10931
11134
  "id": "Patterns-DateRangePickerField",
10932
11135
  "title": "Date range picker field",
@@ -97,7 +97,7 @@
97
97
  ]
98
98
  },
99
99
  "a11y": {
100
- "mdx": "\n## Accessibility\n\nAccessibility ensures that digital content and functionality are usable by\neveryone, including people with disabilities, by addressing visual, auditory,\ncognitive, and physical limitations.\n\n```jsx live\nconst App = () => (\n <Card.Root cardPadding=\"md\" borderStyle=\"outlined\" elevation=\"elevated\">\n <Box backgroundColor=\"#FDE3E3\" width=\"5600\" height=\"500\"></Box>\n </Card.Root>\n)\n```\n\n### Accessibility standards\n\n- **Non-text content:** Provide descriptive alternative text (`alt` attribute)\n for images within cards.\n- **Info and relationships:** Use semantic HTML to structure the card content\n logically (e.g., headings, lists).\n- **Color contrast:** Ensure sufficient color contrast between text and\n background within the card.\n- **Keyboard accessibility:** Ensure the entire card or its primary interactive\n elements are focusable and operable using only a keyboard.\n- **Focus visible:** Provide a clear visual focus indicator when the card or its\n interactive elements are focused.\n- **Target size:** If the card is interactive, ensure touch target sizes are\n adequate.\n- **Meaningful sequence:** Ensure the content within the card is presented in a\n logical order.\n- **Labels or instructions:** Provide clear labels or instructions for form\n elements within cards.\n- **Semantic HTML:** Use semantic HTML to ensure compatibility with assistive\n technologies.\n- **Avoid complex interactive cards:** Avoid making the entire card a single\n interactive element if it contains multiple distinct actions, to ensure proper\n interaction for assistive technology users.\n",
100
+ "mdx": "\n## Accessibility\n\nAccessibility ensures that digital content and functionality are usable by\neveryone, including people with disabilities, by addressing visual, auditory,\ncognitive, and physical limitations.\n\n### Automatic ARIA wiring\n\nCard supports automatic ARIA wiring via React Aria's slot system. When you place\na `<Heading slot=\"title\">` or `<Text slot=\"description\">` inside a Card, the\nroot element automatically receives `aria-labelledby` and/or `aria-describedby`\npointing to those elements. The card itself stays a plain `<div>` — no implicit\n`role` is applied. If you need a landmark role (e.g., `role=\"article\"` or\n`role=\"region\"`), set it explicitly on `Card.Root`.\n\n```jsx live\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\" role=\"article\">\n <Card.Header>\n <Heading slot=\"title\" as=\"h3\">Sprint Summary</Heading>\n </Card.Header>\n <Card.Body>\n <Text slot=\"description\">\n The team completed 85% of planned story points this sprint.\n </Text>\n </Card.Body>\n <Card.Footer>\n <Button variant=\"solid\" colorPalette=\"primary\" size=\"sm\">View Sprint</Button>\n </Card.Footer>\n </Card.Root>\n)\n```\n\n### Slot behavior\n\n| Slots present | `aria-labelledby` | `aria-describedby` |\n|---|---|---|\n| `Heading slot=\"title\"` + `Text slot=\"description\"` | points to Heading | points to Text |\n| `Heading slot=\"title\"` only | points to Heading | — |\n| `Text slot=\"description\"` only | — | points to Text |\n| No slots | — | — |\n\nThe card never sets a `role` automatically. A `<Heading>` or `<Text>` without\na `slot` prop is not wired up.\n\n### Manual labeling\n\nIf slot-based labeling is not suitable, pass `aria-label` directly to\n`Card.Root`:\n\n```jsx live\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\" aria-label=\"Product summary card\">\n <Card.Body>\n <Text>Content with manual aria-label.</Text>\n </Card.Body>\n </Card.Root>\n)\n```\n\n### Accessibility standards\n\n- **Non-text content:** Provide descriptive alternative text (`alt` attribute)\n for images within cards.\n- **Info and relationships:** Use semantic HTML to structure the card content\n logically (e.g., headings, lists).\n- **Color contrast:** Ensure sufficient color contrast between text and\n background within the card.\n- **Keyboard accessibility:** Ensure the entire card or its primary interactive\n elements are focusable and operable using only a keyboard.\n- **Focus visible:** Provide a clear visual focus indicator when the card or its\n interactive elements are focused.\n- **Target size:** If the card is interactive, ensure touch target sizes are\n adequate.\n- **Meaningful sequence:** Ensure the content within the card is presented in a\n logical order.\n- **Semantic HTML:** Use semantic HTML to ensure compatibility with assistive\n technologies.\n- **Avoid complex interactive cards:** Avoid making the entire card a single\n interactive element if it contains multiple distinct actions, to ensure proper\n interaction for assistive technology users.\n",
101
101
  "toc": [
102
102
  {
103
103
  "value": "Accessibility",
@@ -109,6 +109,39 @@
109
109
  ],
110
110
  "parent": "root"
111
111
  },
112
+ {
113
+ "value": "Automatic ARIA wiring",
114
+ "href": "#automatic-aria-wiring",
115
+ "depth": 3,
116
+ "numbering": [
117
+ 1,
118
+ 1,
119
+ 1
120
+ ],
121
+ "parent": "root"
122
+ },
123
+ {
124
+ "value": "Slot behavior",
125
+ "href": "#slot-behavior",
126
+ "depth": 3,
127
+ "numbering": [
128
+ 1,
129
+ 1,
130
+ 2
131
+ ],
132
+ "parent": "root"
133
+ },
134
+ {
135
+ "value": "Manual labeling",
136
+ "href": "#manual-labeling",
137
+ "depth": 3,
138
+ "numbering": [
139
+ 1,
140
+ 1,
141
+ 3
142
+ ],
143
+ "parent": "root"
144
+ },
112
145
  {
113
146
  "value": "Accessibility standards",
114
147
  "href": "#accessibility-standards",
@@ -116,14 +149,14 @@
116
149
  "numbering": [
117
150
  1,
118
151
  1,
119
- 1
152
+ 4
120
153
  ],
121
154
  "parent": "root"
122
155
  }
123
156
  ]
124
157
  },
125
158
  "dev": {
126
- "mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { Box, Card, Link, type CardProps } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nThe Card component uses a compound pattern to structure content. You must wrap `Card.Header` and `Card.Content` within `Card.Root`.\n\n```jsx live-dev\nconst App = () => (\n <Card.Root>\n <Card.Header>Card Title</Card.Header>\n <Card.Content>\n <Text>This is the main card content.</Text>\n </Card.Content>\n </Card.Root>\n)\n```\n\n## Usage examples\n\n### Size options\n\nThe `cardPadding` prop controls the internal spacing of the card. Available sizes are `sm`, `md`, and `lg`.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n <Card.Root cardPadding=\"sm\">\n <Card.Header>Small Padding</Card.Header>\n <Card.Content>Compact content spacing.</Card.Content>\n </Card.Root>\n\n <Card.Root cardPadding=\"md\">\n <Card.Header>Medium Padding</Card.Header>\n <Card.Content>Standard content spacing (default).</Card.Content>\n </Card.Root>\n\n <Card.Root cardPadding=\"lg\">\n <Card.Header>Large Padding</Card.Header>\n <Card.Content>Spacious content layout.</Card.Content>\n </Card.Root>\n </Stack>\n)\n```\n\n### Visual variants\n\nCustomize the card's appearance using `borderStyle`, `elevation`, and `backgroundStyle`.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n <Card.Root borderStyle=\"outlined\" elevation=\"none\">\n <Card.Header>Outlined (Default)</Card.Header>\n <Card.Content>Standard bordered look.</Card.Content>\n </Card.Root>\n\n <Card.Root borderStyle=\"none\" elevation=\"elevated\">\n <Card.Header>Elevated</Card.Header>\n <Card.Content>Shadow depth without border.</Card.Content>\n </Card.Root>\n\n <Card.Root backgroundStyle=\"muted\" borderStyle=\"none\">\n <Card.Header>Muted Background</Card.Header>\n <Card.Content>Subtle background color for differentiation.</Card.Content>\n </Card.Root>\n </Stack>\n)\n```\n\n### Interactive cards\n\nWhen making a card interactive, avoid placing `onClick` directly on the `Card.Root`. Instead, wrap the card in an anchor tag or use proper ARIA roles to ensure accessibility.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n {/* Option 1: Wrapped in a Nimbus Link (Recommended for navigation) */}\n <Link \n href=\"#\" \n textDecoration=\"none\" \n color=\"inherit\" \n display=\"block\"\n _hover={{ textDecoration: 'none' }}\n aria-label=\"Navigate to details\"\n >\n <Card.Root borderStyle=\"outlined\" elevation=\"elevated\">\n <Card.Header>Navigational Card</Card.Header>\n <Card.Content>\n This entire card acts as a link using the system Link component.\n </Card.Content>\n </Card.Root>\n </Link>\n\n {/* Option 2: Interactive element with ARIA (For actions) */}\n <Box\n role=\"button\"\n tabIndex={0}\n onClick={() => alert(\"Card clicked\")}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n alert(\"Card clicked\");\n }\n }}\n cursor=\"pointer\"\n aria-label=\"Trigger action\"\n >\n <Card.Root borderStyle=\"outlined\">\n <Card.Header>Action Card</Card.Header>\n <Card.Content>\n Behaves like a button with keyboard support (Tab, Enter, Space).\n </Card.Content>\n </Card.Root>\n </Box>\n </Stack>\n)\n```\n\n## Component requirements\n\n## Accessibility\n\nThe `Card` component handles most accessibility requirements internally, rendering semantic `div` elements by default. It acts as a generic container.\n\n- **Headings**: Ensure the content within `Card.Header` follows your page's heading hierarchy (e.g., use `Heading` component or `h3`, `h4` tags).\n- **Interactive Cards**: If a card is interactive (clickable), do not attach `onClick` directly to the card div if possible. Instead, wrap the card in a link or button, or ensure proper ARIA roles (`role=\"button\"`) and keyboard handling (`tabIndex={0}`, Enter/Space key listeners) are implemented.\n\nIf your use case requires tracking and analytics for this component, it is good practice to add a **persistent**, **unique** id to the component:\n\n```tsx\nconst PERSISTENT_ID = \"project-summary-card\";\n\nexport const Example = () => (\n <Card.Root id={PERSISTENT_ID}>\n <Card.Header>Summary</Card.Header>\n <Card.Content>...</Card.Content>\n </Card.Root>\n);\n```\n\n#### Keyboard navigation\n\nStandard Cards are not focusable. Interactive content inside the card (buttons, links) follows the default focus order.\n\n## API reference\n\n<PropsTable id=\"Card\" />\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using Card within your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Rendering Tests\n\nVerify the card displays the correct title and content\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen } from \"@testing-library/react\";\nimport { Card, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Card - Basic rendering\", () => {\n it(\"renders project details\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Header>Project X</Card.Header>\n <Card.Content>Status: Active</Card.Content>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Project X\")).toBeInTheDocument();\n expect(screen.getByText(\"Status: Active\")).toBeInTheDocument();\n });\n\n it(\"renders with header only\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Header>Card Title</Card.Header>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Card Title\")).toBeInTheDocument();\n });\n\n it(\"renders with content only\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Content>This is the main content.</Card.Content>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"This is the main content.\")).toBeInTheDocument();\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-card--docs)\n\n",
159
+ "mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { Button, Card, Link, type CardProps } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nThe Card component uses a compound pattern to structure content. Wrap `Card.Header`, `Card.Body`, and `Card.Footer` within `Card.Root`.\n\n```jsx live-dev\nconst App = () => (\n <Card.Root>\n <Card.Header>Card Title</Card.Header>\n <Card.Body>\n <Text>This is the main card content.</Text>\n </Card.Body>\n </Card.Root>\n)\n```\n\n## Usage examples\n\n### Size options\n\nThe `size` prop controls the internal padding and gap of the card. Available sizes are `sm`, `md`, and `lg`.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n <Card.Root size=\"sm\">\n <Card.Header>Small</Card.Header>\n <Card.Body>Compact content spacing.</Card.Body>\n </Card.Root>\n\n <Card.Root size=\"md\">\n <Card.Header>Medium</Card.Header>\n <Card.Body>Standard content spacing (default).</Card.Body>\n </Card.Root>\n\n <Card.Root size=\"lg\">\n <Card.Header>Large</Card.Header>\n <Card.Body>Spacious content layout.</Card.Body>\n </Card.Root>\n </Stack>\n)\n```\n\n### Visual variants\n\nThe `variant` prop controls the card's visual treatment. Eight variants\nexpose every combination of three independent axes — `outlined` (border),\n`elevated` (shadow), and `muted` (background) — joined with hyphens.\nVariant names omit any axis that is off.\n\n| variant | border | shadow | background |\n| ----------------------------- | :----: | :----: | :--------: |\n| `plain` | | | default |\n| `outlined` _(default)_ | ✓ | | default |\n| `elevated` | | ✓ | default |\n| `outlined-elevated` | ✓ | ✓ | default |\n| `muted` | | | muted |\n| `outlined-muted` | ✓ | | muted |\n| `elevated-muted` | | ✓ | muted |\n| `outlined-elevated-muted` | ✓ | ✓ | muted |\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n <Card.Root variant=\"outlined\">\n <Card.Header>Outlined (Default)</Card.Header>\n <Card.Body>Standard bordered look.</Card.Body>\n </Card.Root>\n\n <Card.Root variant=\"elevated\">\n <Card.Header>Elevated</Card.Header>\n <Card.Body>Shadow depth without border.</Card.Body>\n </Card.Root>\n\n <Card.Root variant=\"outlined-elevated\">\n <Card.Header>Outlined + elevated</Card.Header>\n <Card.Body>Both border and shadow.</Card.Body>\n </Card.Root>\n\n <Card.Root variant=\"muted\">\n <Card.Header>Muted</Card.Header>\n <Card.Body>Muted background for differentiation.</Card.Body>\n </Card.Root>\n\n <Card.Root variant=\"plain\">\n <Card.Header>Plain</Card.Header>\n <Card.Body>Minimal visual treatment.</Card.Body>\n </Card.Root>\n </Stack>\n)\n```\n\n### Card with footer\n\nUse `Card.Footer` for actions and metadata placed below the body content.\n\n```jsx live-dev\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\">\n <Card.Header>Project Summary</Card.Header>\n <Card.Body>\n <Text>The project is on track for the next milestone.</Text>\n </Card.Body>\n <Card.Footer>\n <Button variant=\"solid\" colorPalette=\"primary\" size=\"sm\">View Details</Button>\n </Card.Footer>\n </Card.Root>\n)\n```\n\n### Accessible cards\n\nUse `<Heading slot=\"title\">` and `<Text slot=\"description\">` to automatically wire `aria-labelledby` and `aria-describedby` on the card. The card itself stays a plain `<div>` — set `role` explicitly on the consumer side if a landmark or interactive role is needed.\n\n```jsx live-dev\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\">\n <Card.Header>\n <Heading slot=\"title\" as=\"h3\">Sprint Summary</Heading>\n </Card.Header>\n <Card.Body>\n <Text slot=\"description\">\n The team completed 85% of planned story points this sprint.\n </Text>\n </Card.Body>\n <Card.Footer>\n <Button variant=\"solid\" colorPalette=\"primary\" size=\"sm\">View Sprint</Button>\n </Card.Footer>\n </Card.Root>\n)\n```\n\n### Interactive cards\n\nWhen making a card interactive, avoid placing `onClick` directly on the `Card.Root`. Instead, wrap the card in an anchor tag or use proper ARIA roles to ensure accessibility.\n\n```jsx live-dev\nconst App = () => (\n <Stack direction=\"column\" gap=\"400\">\n {/* Option 1: Wrapped in a Nimbus Link (Recommended for navigation) */}\n <Link \n href=\"#\" \n textDecoration=\"none\" \n color=\"inherit\" \n display=\"block\"\n _hover={{ textDecoration: 'none' }}\n aria-label=\"Navigate to details\"\n >\n <Card.Root variant=\"outlined\">\n <Card.Header>Navigational Card</Card.Header>\n <Card.Body>\n This entire card acts as a link using the system Link component.\n </Card.Body>\n </Card.Root>\n </Link>\n\n {/* Option 2: Wrapped in a native <button> (Recommended for actions) */}\n <Button\n variant=\"ghost\"\n onPress={() => alert(\"Card clicked\")}\n aria-label=\"Trigger action\"\n p=\"0\"\n >\n <Card.Root variant=\"outlined\">\n <Card.Header>Action Card</Card.Header>\n <Card.Body>\n Native button gives you Tab focus, Enter/Space activation, and a focus\n ring for free.\n </Card.Body>\n </Card.Root>\n </Button>\n </Stack>\n)\n```\n\n## Component requirements\n\n## Accessibility\n\nThe `Card` component supports automatic ARIA wiring via React Aria's slot system.\n\n- **Automatic ARIA wiring**: Place `<Heading slot=\"title\">` inside the card for automatic `aria-labelledby`. Place `<Text slot=\"description\">` for `aria-describedby`. The card itself stays a plain `<div>` — no implicit `role` is applied.\n- **Manual labeling**: Pass `aria-label` directly to `Card.Root` when slot-based labeling is not suitable.\n- **Semantic role**: If you need a landmark role (e.g., `role=\"article\"` or `role=\"region\"`), set it explicitly on `Card.Root` or on a wrapping element.\n- **Headings**: Ensure the content within `Card.Header` follows your page's heading hierarchy (e.g., use `Heading` component with appropriate `as` prop).\n- **Interactive Cards**: If a card is interactive (clickable), do not attach `onClick` directly to the card div. Instead, wrap the card in a native link or button, which provides focus, keyboard activation, and the correct accessible name automatically.\n\nIf your use case requires tracking and analytics for this component, it is good practice to add a **persistent**, **unique** id to the component:\n\n```tsx\nconst PERSISTENT_ID = \"project-summary-card\";\n\nexport const Example = () => (\n <Card.Root id={PERSISTENT_ID}>\n <Card.Header>Summary</Card.Header>\n <Card.Body>...</Card.Body>\n </Card.Root>\n);\n```\n\n#### Keyboard navigation\n\nStandard Cards are not focusable. Interactive content inside the card (buttons, links) follows the default focus order.\n\n## API reference\n\n<PropsTable id=\"Card\" />\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using Card within your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Rendering Tests\n\nVerify the card displays the correct title and content\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport { Card, Heading, Text, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Card - Basic rendering\", () => {\n it(\"renders project details\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Header>Project X</Card.Header>\n <Card.Body>Status: Active</Card.Body>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Project X\")).toBeInTheDocument();\n expect(screen.getByText(\"Status: Active\")).toBeInTheDocument();\n });\n\n it(\"renders with header only\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Header>Card Title</Card.Header>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Card Title\")).toBeInTheDocument();\n });\n\n it(\"renders with body only\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Body>This is the main content.</Card.Body>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"This is the main content.\")).toBeInTheDocument();\n });\n\n it(\"renders with footer\", () => {\n render(\n <NimbusProvider>\n <Card.Root>\n <Card.Header>Title</Card.Header>\n <Card.Body>Content</Card.Body>\n <Card.Footer>Footer actions</Card.Footer>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Title\")).toBeInTheDocument();\n expect(screen.getByText(\"Content\")).toBeInTheDocument();\n expect(screen.getByText(\"Footer actions\")).toBeInTheDocument();\n });\n\n it(\"renders with variant and size props\", () => {\n render(\n <NimbusProvider>\n <Card.Root variant=\"elevated\" size=\"lg\">\n <Card.Body>Elevated card</Card.Body>\n </Card.Root>\n </NimbusProvider>\n );\n\n expect(screen.getByText(\"Elevated card\")).toBeInTheDocument();\n });\n});\n```\n\n### Slot-Based Accessibility\n\nUsing Heading and Text slots for automatic ARIA wiring\n\n```tsx\nimport { describe, it, expect } from \"vitest\";\nimport { render, screen, waitFor } from \"@testing-library/react\";\nimport { Card, Heading, Text, NimbusProvider } from \"@commercetools/nimbus\";\n\ndescribe(\"Card - Slot-based accessibility\", () => {\n it(\"wires aria-labelledby and aria-describedby from title and description slots\", async () => {\n render(\n <NimbusProvider>\n <Card.Root data-testid=\"card-slot\">\n <Card.Header>\n <Heading slot=\"title\" as=\"h3\">\n Product Details\n </Heading>\n </Card.Header>\n <Card.Body>\n <Text slot=\"description\">\n Overview of the product's key features.\n </Text>\n </Card.Body>\n </Card.Root>\n </NimbusProvider>\n );\n\n const card = screen.getByTestId(\"card-slot\");\n const heading = screen.getByText(\"Product Details\");\n const description = screen.getByText(/key features/);\n\n // useSlotId completes its registration on a follow-up render.\n await waitFor(() => {\n expect(card).toHaveAttribute(\"aria-labelledby\", heading.id);\n expect(card).toHaveAttribute(\"aria-describedby\", description.id);\n });\n\n // Card is a plain div — set role explicitly if a landmark role is needed.\n expect(card).not.toHaveAttribute(\"role\");\n });\n\n it(\"has no role or ARIA labelling attributes without slots\", () => {\n render(\n <NimbusProvider>\n <Card.Root data-testid=\"card-plain\">\n <Card.Header>Title</Card.Header>\n <Card.Body>Content</Card.Body>\n </Card.Root>\n </NimbusProvider>\n );\n\n const card = screen.getByTestId(\"card-plain\");\n\n expect(card).not.toHaveAttribute(\"role\");\n expect(card).not.toHaveAttribute(\"aria-labelledby\");\n expect(card).not.toHaveAttribute(\"aria-describedby\");\n });\n});\n```\n\n\n## Resources\n\n- [Storybook](https://nimbus-storybook.vercel.app/?path=/docs/components-card--docs)\n",
127
160
  "toc": [
128
161
  {
129
162
  "value": "Getting started",
@@ -189,6 +222,28 @@
189
222
  ],
190
223
  "parent": "root"
191
224
  },
225
+ {
226
+ "value": "Card with footer",
227
+ "href": "#card-with-footer",
228
+ "depth": 3,
229
+ "numbering": [
230
+ 1,
231
+ 2,
232
+ 3
233
+ ],
234
+ "parent": "root"
235
+ },
236
+ {
237
+ "value": "Accessible cards",
238
+ "href": "#accessible-cards",
239
+ "depth": 3,
240
+ "numbering": [
241
+ 1,
242
+ 2,
243
+ 4
244
+ ],
245
+ "parent": "root"
246
+ },
192
247
  {
193
248
  "value": "Interactive cards",
194
249
  "href": "#interactive-cards",
@@ -196,7 +251,7 @@
196
251
  "numbering": [
197
252
  1,
198
253
  2,
199
- 3
254
+ 5
200
255
  ],
201
256
  "parent": "root"
202
257
  },
@@ -263,6 +318,17 @@
263
318
  ],
264
319
  "parent": "root"
265
320
  },
321
+ {
322
+ "value": "Slot-Based Accessibility",
323
+ "href": "#slot-based-accessibility",
324
+ "depth": 3,
325
+ "numbering": [
326
+ 1,
327
+ 6,
328
+ 2
329
+ ],
330
+ "parent": "root"
331
+ },
266
332
  {
267
333
  "value": "Resources",
268
334
  "href": "#resources",
@@ -276,7 +342,7 @@
276
342
  ]
277
343
  },
278
344
  "guidelines": {
279
- "mdx": "\n## Guidelines\n\nCard component guidelines focus on providing a clear, concise, and visually\ndistinct container for related content, ensuring scannability, clear actions,\nand adaptability across various layouts and screen sizes.\n\n### Best practices\n\n- **Clear purpose:** Each card should represent a single, distinct piece of\n content or concept. Avoid combining multiple unrelated ideas into one card.\n- **Concise content:** Keep the information within a card brief and scannable.\n Use clear headings, short descriptions, and only essential details.\n- **Visual hierarchy:** Establish a clear visual hierarchy to guide the user's\n eye. Use appropriate typography, spacing, and visual cues to prioritize\n important information.\n- **Actionable elements:** If the card requires user interaction, clearly\n present the primary action(s) with prominent buttons or links. Limit the\n number of actions to avoid overwhelming the user.\n- **Consistent styling:** Maintain consistent styling for cards. This includes\n borders, shadows, typography, and spacing.\n- **Responsive design:** Ensure cards adapt well to different screen sizes and\n orientations. Consider how the layout will change on mobile devices.\n\n### Usage\n\nThe strength of using cards in a UI lies in their ability to present information\nin concise, digestible chunks, making it easy for users to quickly scan,\nunderstand, and interact with related content, especially in responsive layouts.\n\n> [!TIP]\\\n> When to use\n\n- **Displaying a collection of related items:** When you have multiple, distinct\n items that share a similar structure or theme (e.g., product listings,\n documentation, user profiles).\n- **Presenting scannable summaries:** When you need to provide a quick overview\n of information, allowing users to grasp the key details at a glance.\n- **Highlighting key information:** When you want to draw attention to important\n pieces of content or features.\n- **Facilitating task completion:** When a card can contain actions that allow\n users to quickly interact with or manage the displayed information.\n- **Creating a visually organized layout:** When you need to break down complex\n information into digestible chunks and create a structured, modular design.\n- **Responsive design:** When you need a component that adapts well to various\n screen sizes and can be easily rearranged on different devices.\n- **Content discovery:** When you want to encourage users to explore different\n pieces of content.\n\n> [!CAUTION]\\\n> When not to use\n\n- **Presenting linear processes:** When users need to follow a specific,\n sequential flow (e.g., a step-by-step form or a tutorial). Other patterns like\n steppers are more appropriate.\n- **Displaying single, detailed items:** When you need to present a lot of\n in-depth information about one specific item. A dedicated page is usually\n better.\n- **Primary navigation:** Using cards for main site navigation can be\n overwhelming and make it difficult for users to understand the site's\n structure.\n- **Simple lists:** For basic lists of items with minimal information, a simple\n list or table might be more efficient and less visually heavy.\n- **When content is highly interrelated and needs comparison:** If users need to\n compare specific attributes across multiple items, a table might be a better\n choice.\n- **Overcrowding the UI with too many cards:** Using too many cards on a page\n can lead to visual clutter and make it hard for users to focus on what's\n important.\n- **Forcing content into a card structure:** If the content doesn't naturally\n fit into a card format, trying to force it can lead to awkward design and a\n poor user experience.\n\n### Content\n\nContent within cards should be concise and focused, prioritizing key information\nand clear actions to ensure scannability and quick user comprehension. Consider\nthe card's purpose and limit content to only the most relevant details, using\nclear headings and short descriptions.\n\n> [!TIP]\\\n> **Do**\n>\n> - Cards are best for presenting self-contained pieces of information.\n> - Design cards with clear headings, concise text, and a logical visual\n> hierarchy for easy scanning.\n\n```jsx live\nconst App = () => (\n <Card.Root cardPadding=\"md\" borderStyle=\"outlined\" width=\"8000\">\n <Card.Content>\n <Stack gap=\"400\">\n <img src=\"/images/card/card-discount-template.png\" alt=\"discount template illustration\" />\n <Text fontWeight=\"bold\">\n Discount templates\n </Text>\n <Text>\n Templates allow you to get started quickly and easily by displaying only\n what is needed for creating your discount.\n </Text>\n <Select.Root aria-label=\"Select template\" placeholder=\"Select...\">\n <Select.Options>{/* Options would go here */}</Select.Options>\n </Select.Root>\n <Box>\n <Button colorPalette=\"primary\" variant=\"solid\">\n Select template\n </Button>\n </Box>\n </Stack>\n </Card.Content>\n </Card.Root>\n);\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Avoid cramming too much information into a single card. Each card should\n> focus on a single subject.\n> - Long, scrolling cards can be cumbersome; prioritize key information.\n> - Don't use a card to compare information.\n\n```jsx live\nconst App = () => (\n <Card.Root cardPadding=\"md\" borderStyle=\"outlined\" width=\"1\">\n <Card.Content>\n <Stack direction=\"horizontal\" gap=\"400\">\n <Stack gap=\"400\" width=\"1/2\">\n <img src=\"/images/card/card-discount-template.png\" alt=\"discount template illustration\" />\n <Text fontWeight=\"bold\">\n Discount templates\n </Text>\n <Text>\n Templates allow you to get started quickly and easily by displaying\n only what is needed for creating your discount.\n </Text>\n <Select.Root aria-label=\"Select template\" placeholder=\"Select...\">\n <Select.Options>{/* Options would go here */}</Select.Options>\n </Select.Root>\n <Box>\n <Button colorPalette=\"primary\" variant=\"solid\">\n Select template\n </Button>\n </Box>\n </Stack>\n <Stack gap=\"400\" width=\"1/2\">\n <img src=\"/images/card/card-product-discount.png\" alt=\"product discount illustration\" />\n <Text fontWeight=\"bold\">\n Product discount\n </Text>\n <Text>\n A product discount applies a percentage-off (relative) or amount-ff\n (absolute) discount to a specific product or subset of products\n before adding them to the cart. They are useful for displaying\n discounted or sale prices on a product info page\n </Text>\n <Box>\n <Button colorPalette=\"primary\" variant=\"solid\">\n Add product discount\n </Button>\n </Box>\n </Stack>\n </Stack>\n </Card.Content>\n </Card.Root>\n);\n```\n\n### Actions\n\nActions within a card should be limited and focused, prioritizing the primary\naction related to the card's content to avoid overwhelming the user. Ensure\nactions are clearly labeled and visually prominent to facilitate quick\ninteraction.\n\n> [!TIP]\\\n> **Do**\n>\n> - If interaction is needed, provide obvious and limited actions.\n> - Users often assume cards are clickable, especially when they contain visual\n> cues that suggest interactivity, like buttons, links, or hover effects.\n> However, this assumption isn't universal and can vary depending on the\n> context and user experience.\n\n```jsx live\nconst App = () => (\n <Card.Root cardPadding=\"md\" borderStyle=\"outlined\" width=\"384px\">\n <Card.Content>\n <Stack direction=\"horizontal\" gap=\"400\">\n <Box\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.ViewInAr width=\"24px\" height=\"24px\" />\n </Box>\n <Stack gap=\"100\">\n <Text fontWeight=\"bold\">\n Learn more about products\n </Text>\n <Text color=\"neutral.11\">\n Discover what a product list is and how to manage it.\n </Text>\n </Stack>\n </Stack>\n </Card.Content>\n </Card.Root>\n);\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Don't use too many actions for the user within cards.\n> - Cards are generally not suitable for primary site navigation.\n\n```jsx live\nconst App = () => (\n <Stack gap=\"400\" alignItems=\"start\">\n {/* Main Card */}\n <Card.Root cardPadding=\"md\" borderStyle=\"outlined\" width=\"423px\">\n <Card.Content>\n <Stack direction=\"horizontal\" gap=\"400\">\n <Box\n alignSelf=\"flex-start\"\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.ViewInAr width=\"24px\" height=\"24px\" />\n </Box>\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"center\">\n <Stack gap=\"200\">\n <Flex gap=\"100\" alignItems=\"center\">\n <Text fontWeight=\"bold\" color=\"primary.11\">\n See your products\n </Text>\n <Box color=\"primary.11\" boxSize=\"600\">\n <Icons.ArrowForward width=\"full\" height=\"full\" />\n </Box>\n </Flex>\n <Text color=\"neutral.11\">\n You have modified products that are waiting to be published\n </Text>\n {/* Buttons */}\n <Stack gap=\"200\">\n <Button\n colorPalette=\"primary\"\n variant=\"solid\"\n alignSelf=\"start\"\n >\n Publish Products\n </Button>\n <Stack direction=\"horizontal\" gap=\"300\">\n <Button colorPalette=\"primary\" variant=\"link\" size=\"sm\">\n Review\n </Button>\n <Button colorPalette=\"primary\" variant=\"link\" size=\"sm\">\n Reject\n </Button>\n <Button colorPalette=\"primary\" variant=\"link\" size=\"sm\">\n See drafts\n </Button>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </Card.Content>\n </Card.Root>\n\n {/* Bottom Row Cards */}\n <Stack direction=\"horizontal\" gap=\"400\">\n {/* Products Card */}\n <Card.Root\n cardPadding=\"md\"\n borderStyle=\"outlined\"\n width=\"201.5px\"\n > {/* Half width approx */}\n <Card.Content>\n <Stack direction=\"horizontal\" gap=\"300\" alignItems=\"center\">\n <Box\n alignSelf=\"flex-start\"\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.ViewInAr width=\"24px\" height=\"24px\" />\n </Box>\n <Text textStyle=\"bodyStrong\" fontWeight=\"bold\" color=\"primary.11\">\n Products\n </Text>\n </Stack>\n </Card.Content>\n </Card.Root>\n\n {/* Discounts Card */}\n <Card.Root\n cardPadding=\"md\"\n borderStyle=\"outlined\"\n width=\"201.5px\"\n > {/* Half width approx */}\n <Card.Content>\n <Stack direction=\"horizontal\" gap=\"300\" alignItems=\"center\">\n <Box\n alignSelf=\"flex-start\"\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.Discount width=\"24px\" height=\"24px\" />\n </Box>\n <Text textStyle=\"bodyStrong\" fontWeight=\"bold\" color=\"primary.11\">\n Discounts\n </Text>\n </Stack>\n </Card.Content>\n </Card.Root>\n </Stack>\n </Stack>\n);\n```\n",
345
+ "mdx": "\n## Guidelines\n\nCard component guidelines focus on providing a clear, concise, and visually\ndistinct container for related content, ensuring scannability, clear actions,\nand adaptability across various layouts and screen sizes.\n\n### Best practices\n\n- **Clear purpose:** Each card should represent a single, distinct piece of\n content or concept. Avoid combining multiple unrelated ideas into one card.\n- **Concise content:** Keep the information within a card brief and scannable.\n Use clear headings, short descriptions, and only essential details.\n- **Visual hierarchy:** Establish a clear visual hierarchy to guide the user's\n eye. Use appropriate typography, spacing, and visual cues to prioritize\n important information.\n- **Actionable elements:** If the card requires user interaction, clearly\n present the primary action(s) with prominent buttons or links. Limit the\n number of actions to avoid overwhelming the user.\n- **Consistent styling:** Maintain consistent styling for cards. This includes\n borders, shadows, typography, and spacing.\n- **Responsive design:** Ensure cards adapt well to different screen sizes and\n orientations. Consider how the layout will change on mobile devices.\n\n### Usage\n\nThe strength of using cards in a UI lies in their ability to present information\nin concise, digestible chunks, making it easy for users to quickly scan,\nunderstand, and interact with related content, especially in responsive layouts.\n\n> [!TIP]\\\n> When to use\n\n- **Displaying a collection of related items:** When you have multiple, distinct\n items that share a similar structure or theme (e.g., product listings,\n documentation, user profiles).\n- **Presenting scannable summaries:** When you need to provide a quick overview\n of information, allowing users to grasp the key details at a glance.\n- **Highlighting key information:** When you want to draw attention to important\n pieces of content or features.\n- **Facilitating task completion:** When a card can contain actions that allow\n users to quickly interact with or manage the displayed information.\n- **Creating a visually organized layout:** When you need to break down complex\n information into digestible chunks and create a structured, modular design.\n- **Responsive design:** When you need a component that adapts well to various\n screen sizes and can be easily rearranged on different devices.\n- **Content discovery:** When you want to encourage users to explore different\n pieces of content.\n\n> [!CAUTION]\\\n> When not to use\n\n- **Presenting linear processes:** When users need to follow a specific,\n sequential flow (e.g., a step-by-step form or a tutorial). Other patterns like\n steppers are more appropriate.\n- **Displaying single, detailed items:** When you need to present a lot of\n in-depth information about one specific item. A dedicated page is usually\n better.\n- **Primary navigation:** Using cards for main site navigation can be\n overwhelming and make it difficult for users to understand the site's\n structure.\n- **Simple lists:** For basic lists of items with minimal information, a simple\n list or table might be more efficient and less visually heavy.\n- **When content is highly interrelated and needs comparison:** If users need to\n compare specific attributes across multiple items, a table might be a better\n choice.\n- **Overcrowding the UI with too many cards:** Using too many cards on a page\n can lead to visual clutter and make it hard for users to focus on what's\n important.\n- **Forcing content into a card structure:** If the content doesn't naturally\n fit into a card format, trying to force it can lead to awkward design and a\n poor user experience.\n\n### Content\n\nContent within cards should be concise and focused, prioritizing key information\nand clear actions to ensure scannability and quick user comprehension. Consider\nthe card's purpose and limit content to only the most relevant details, using\nclear headings and short descriptions.\n\n> [!TIP]\\\n> **Do**\n>\n> - Cards are best for presenting self-contained pieces of information.\n> - Design cards with clear headings, concise text, and a logical visual\n> hierarchy for easy scanning.\n\n```jsx live\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\" width=\"8000\">\n <Card.Body>\n <Stack gap=\"400\">\n <img src=\"/images/card/card-discount-template.png\" alt=\"discount template illustration\" />\n <Text fontWeight=\"bold\">\n Discount templates\n </Text>\n <Text>\n Templates allow you to get started quickly and easily by displaying only\n what is needed for creating your discount.\n </Text>\n <Select.Root aria-label=\"Select template\" placeholder=\"Select...\">\n <Select.Options>{/* Options would go here */}</Select.Options>\n </Select.Root>\n <Box>\n <Button colorPalette=\"primary\" variant=\"solid\">\n Select template\n </Button>\n </Box>\n </Stack>\n </Card.Body>\n </Card.Root>\n);\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Avoid cramming too much information into a single card. Each card should\n> focus on a single subject.\n> - Long, scrolling cards can be cumbersome; prioritize key information.\n> - Don't use a card to compare information.\n\n```jsx live\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\" width=\"1\">\n <Card.Body>\n <Stack direction=\"horizontal\" gap=\"400\">\n <Stack gap=\"400\" width=\"1/2\">\n <img src=\"/images/card/card-discount-template.png\" alt=\"discount template illustration\" />\n <Text fontWeight=\"bold\">\n Discount templates\n </Text>\n <Text>\n Templates allow you to get started quickly and easily by displaying\n only what is needed for creating your discount.\n </Text>\n <Select.Root aria-label=\"Select template\" placeholder=\"Select...\">\n <Select.Options>{/* Options would go here */}</Select.Options>\n </Select.Root>\n <Box>\n <Button colorPalette=\"primary\" variant=\"solid\">\n Select template\n </Button>\n </Box>\n </Stack>\n <Stack gap=\"400\" width=\"1/2\">\n <img src=\"/images/card/card-product-discount.png\" alt=\"product discount illustration\" />\n <Text fontWeight=\"bold\">\n Product discount\n </Text>\n <Text>\n A product discount applies a percentage-off (relative) or amount-ff\n (absolute) discount to a specific product or subset of products\n before adding them to the cart. They are useful for displaying\n discounted or sale prices on a product info page\n </Text>\n <Box>\n <Button colorPalette=\"primary\" variant=\"solid\">\n Add product discount\n </Button>\n </Box>\n </Stack>\n </Stack>\n </Card.Body>\n </Card.Root>\n);\n```\n\n### Actions\n\nActions within a card should be limited and focused, prioritizing the primary\naction related to the card's content to avoid overwhelming the user. Ensure\nactions are clearly labeled and visually prominent to facilitate quick\ninteraction.\n\n> [!TIP]\\\n> **Do**\n>\n> - If interaction is needed, provide obvious and limited actions.\n> - Users often assume cards are clickable, especially when they contain visual\n> cues that suggest interactivity, like buttons, links, or hover effects.\n> However, this assumption isn't universal and can vary depending on the\n> context and user experience.\n\n```jsx live\nconst App = () => (\n <Card.Root variant=\"outlined\" size=\"md\" width=\"384px\">\n <Card.Body>\n <Stack direction=\"horizontal\" gap=\"400\">\n <Box\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.ViewInAr width=\"24px\" height=\"24px\" />\n </Box>\n <Stack gap=\"100\">\n <Text fontWeight=\"bold\">\n Learn more about products\n </Text>\n <Text color=\"neutral.11\">\n Discover what a product list is and how to manage it.\n </Text>\n </Stack>\n </Stack>\n </Card.Body>\n </Card.Root>\n);\n```\n\n> [!CAUTION]\\\n> **Don't**\n>\n> - Don't use too many actions for the user within cards.\n> - Cards are generally not suitable for primary site navigation.\n\n```jsx live\nconst App = () => (\n <Stack gap=\"400\" alignItems=\"start\">\n {/* Main Card */}\n <Card.Root variant=\"outlined\" size=\"md\" width=\"423px\">\n <Card.Body>\n <Stack direction=\"horizontal\" gap=\"400\">\n <Box\n alignSelf=\"flex-start\"\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.ViewInAr width=\"24px\" height=\"24px\" />\n </Box>\n <Stack direction=\"horizontal\" gap=\"400\" alignItems=\"center\">\n <Stack gap=\"200\">\n <Flex gap=\"100\" alignItems=\"center\">\n <Text fontWeight=\"bold\" color=\"primary.11\">\n See your products\n </Text>\n <Box color=\"primary.11\" boxSize=\"600\">\n <Icons.ArrowForward width=\"full\" height=\"full\" />\n </Box>\n </Flex>\n <Text color=\"neutral.11\">\n You have modified products that are waiting to be published\n </Text>\n {/* Buttons */}\n <Stack gap=\"200\">\n <Button\n colorPalette=\"primary\"\n variant=\"solid\"\n alignSelf=\"start\"\n >\n Publish Products\n </Button>\n <Stack direction=\"horizontal\" gap=\"300\">\n <Button colorPalette=\"primary\" variant=\"link\" size=\"sm\">\n Review\n </Button>\n <Button colorPalette=\"primary\" variant=\"link\" size=\"sm\">\n Reject\n </Button>\n <Button colorPalette=\"primary\" variant=\"link\" size=\"sm\">\n See drafts\n </Button>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n </Card.Body>\n </Card.Root>\n\n {/* Bottom Row Cards */}\n <Stack direction=\"horizontal\" gap=\"400\">\n {/* Products Card */}\n <Card.Root\n variant=\"outlined\"\n size=\"md\"\n width=\"201.5px\"\n > {/* Half width approx */}\n <Card.Body>\n <Stack direction=\"horizontal\" gap=\"300\" alignItems=\"center\">\n <Box\n alignSelf=\"flex-start\"\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.ViewInAr width=\"24px\" height=\"24px\" />\n </Box>\n <Text textStyle=\"bodyStrong\" fontWeight=\"bold\" color=\"primary.11\">\n Products\n </Text>\n </Stack>\n </Card.Body>\n </Card.Root>\n\n {/* Discounts Card */}\n <Card.Root\n variant=\"outlined\"\n size=\"md\"\n width=\"201.5px\"\n > {/* Half width approx */}\n <Card.Body>\n <Stack direction=\"horizontal\" gap=\"300\" alignItems=\"center\">\n <Box\n alignSelf=\"flex-start\"\n color=\"primary.11\"\n boxSize=\"1000\"\n borderRadius=\"50%\"\n bg=\"primary.3\"\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink=\"0\"\n >\n <Icons.Discount width=\"24px\" height=\"24px\" />\n </Box>\n <Text textStyle=\"bodyStrong\" fontWeight=\"bold\" color=\"primary.11\">\n Discounts\n </Text>\n </Stack>\n </Card.Body>\n </Card.Root>\n </Stack>\n </Stack>\n);\n```\n",
280
346
  "toc": [
281
347
  {
282
348
  "value": "Guidelines",
@@ -697,7 +697,7 @@
697
697
  ]
698
698
  },
699
699
  "dev": {
700
- "mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { toast, type ToastOptions } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nCreate toast notifications using the imperative `toast()` API. Toasts are rendered automatically by the `NimbusProvider`.\n\n```jsx-live-dev\nconst App = () => {\n const handleClick = () => {\n toast({\n title: \"Success\",\n description: \"Your changes have been saved.\",\n type: \"success\"\n });\n };\n\n return (\n <Button onPress={handleClick}>\n Show Toast\n </Button>\n );\n}\n```\n\n## Usage examples\n\n### Toast types\n\nFour types are available, each with semantic meaning and ARIA roles:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast.info({ title: \"Information\", description: \"This is informational.\" })}>\n Info Toast\n </Button>\n <Button onPress={() => toast.success({ title: \"Success\", description: \"Operation completed.\" })}>\n Success Toast\n </Button>\n <Button onPress={() => toast.warning({ title: \"Warning\", description: \"Please review this.\" })}>\n Warning Toast\n </Button>\n <Button onPress={() => toast.error({ title: \"Error\", description: \"An error occurred.\" })}>\n Error Toast\n </Button>\n </Stack>\n)\n```\n\n**ARIA semantics:**\n- `info`, `success`, and `warning`: `role=\"status\"` (polite announcements)\n- `error`: `role=\"alert\"` (assertive announcements)\n- Use the `\"aria-live\"` option to override the default politeness level for any toast type\n\n### Placement options\n\nControl where toasts appear on screen using the `placement` prop. Each placement creates an independent toast region:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast({ title: \"Top Start\", placement: \"top-start\" })}>\n Top Start\n </Button>\n <Button onPress={() => toast({ title: \"Top End (Default)\", placement: \"top-end\" })}>\n Top End\n </Button>\n <Button onPress={() => toast({ title: \"Bottom Start\", placement: \"bottom-start\" })}>\n Bottom Start\n </Button>\n <Button onPress={() => toast({ title: \"Bottom End\", placement: \"bottom-end\" })}>\n Bottom End\n </Button>\n </Stack>\n)\n```\n\n### Auto-dismiss behavior\n\nToasts auto-dismiss after 6 seconds by default. The timer pauses on hover and focus:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast({ title: \"Default (6s)\", description: \"Dismisses after 6 seconds\" })}>\n Default Duration\n </Button>\n <Button onPress={() => toast({ title: \"Custom Duration\", description: \"Dismisses after 10 seconds\", duration: 10000 })}>\n 10 Second Duration\n </Button>\n <Button onPress={() => toast({ title: \"Persistent\", description: \"Never auto-dismisses\", duration: Infinity })}>\n No Auto-Dismiss\n </Button>\n </Stack>\n)\n```\n\n**Duration values:**\n- Default: `6000` (6 seconds)\n- Custom: Any positive number in milliseconds\n- Persistent: `Infinity` (never auto-dismisses)\n\n### Action buttons\n\nAdd action buttons to toasts. Duration is controlled independently — set `duration: Infinity` if the action requires user acknowledgment:\n\n```jsx-live-dev\nconst App = () => {\n const handleUndo = () => {\n toast.success({ title: \"Undo successful\" });\n };\n\n const handleDelete = () => {\n toast({\n title: \"Item deleted\",\n description: \"The item has been removed from your list.\",\n type: \"info\",\n action: {\n label: \"Undo\",\n onPress: handleUndo\n }\n });\n };\n\n return (\n <Button onPress={handleDelete}>\n Delete Item\n </Button>\n );\n}\n```\n\n### Promise pattern\n\nUse `toast.promise()` to show loading, success, and error states for async operations:\n\n```jsx-live-dev\nconst App = () => {\n const simulateAsyncOperation = () => {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n Math.random() > 0.5 ? resolve(\"Success!\") : reject(\"Failed!\");\n }, 2000);\n });\n };\n\n const handleClick = () => {\n toast.promise(\n simulateAsyncOperation(),\n {\n loading: { title: \"Saving...\", description: \"Please wait\", closable: false },\n success: { title: \"Saved\", description: \"Your changes have been saved\", type: \"success\" },\n error: { title: \"Error\", description: \"Failed to save changes\", type: \"error\" }\n }\n );\n };\n\n return (\n <Button onPress={handleClick}>\n Save with Promise\n </Button>\n );\n}\n```\n\n### Programmatic control\n\nUpdate or dismiss toasts programmatically using their IDs:\n\n```jsx-live-dev\nconst App = () => {\n const [toastId, setToastId] = useState<string | null>(null);\n\n const handleCreate = () => {\n const id = toast({\n title: \"Processing...\",\n description: \"Operation in progress\",\n duration: Infinity\n });\n setToastId(id);\n };\n\n const handleUpdate = () => {\n if (toastId) {\n toast.update(toastId, {\n title: \"Updated!\",\n description: \"Toast content has been updated\",\n type: \"success\"\n });\n }\n };\n\n const handleDismiss = () => {\n if (toastId) {\n toast.dismiss(toastId);\n setToastId(null);\n }\n };\n\n return (\n <Stack direction=\"row\" gap=\"200\">\n <Button onPress={handleCreate}>Create Toast</Button>\n <Button onPress={handleUpdate} isDisabled={!toastId}>Update Toast</Button>\n <Button onPress={handleDismiss} isDisabled={!toastId}>Dismiss Toast</Button>\n </Stack>\n );\n}\n```\n\n**API methods:**\n- `toast.update(id, options)`: Update toast content\n- `toast.dismiss(id)`: Dismiss with animation (omit `id` to dismiss all)\n- `toast.remove(id)`: Remove immediately without animation (omit `id` to remove all)\n\n### Custom icon\n\nOverride the default type-based icon with a custom icon element using the `icon` prop:\n\n```jsx-live-dev\nconst App = () => {\n const { Bathtub } = Icons;\n\n return (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast.info({ title: \"Custom Icon\", description: \"This toast uses a custom icon\", icon: <Bathtub /> })}>\n Custom Icon Toast\n </Button>\n <Button onPress={() => toast.info({ title: \"Default Icon\", description: \"This toast uses the default info icon\" })}>\n Default Icon Toast\n </Button>\n </Stack>\n );\n}\n```\n\nWhen the `icon` prop is provided, it replaces the default icon for that toast type. The `loading` type always shows a spinner regardless of the `icon` prop.\n\n### Closable control\n\nControl close button visibility with the `closable` prop. Useful for loading states:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\">\n <Button onPress={() => toast({ title: \"Closable Toast\", description: \"Has close button\", closable: true })}>\n With Close Button\n </Button>\n <Button onPress={() => toast({ title: \"Loading...\", description: \"Processing your request\", closable: false, duration: 3000 })}>\n No Close Button\n </Button>\n </Stack>\n)\n```\n\n### Stacking and queuing\n\nMultiple toasts stack vertically without overlapping. When the maximum (24) is exceeded, additional toasts queue:\n\n```jsx-live-dev\nconst App = () => {\n const handleCreateMultiple = () => {\n for (let i = 1; i <= 5; i++) {\n setTimeout(() => {\n toast({\n title: `Toast ${i}`,\n description: `This is toast notification ${i}`,\n type: i % 2 === 0 ? \"success\" : \"info\"\n });\n }, i * 300);\n }\n };\n\n return (\n <Button onPress={handleCreateMultiple}>\n Create Multiple Toasts\n </Button>\n );\n}\n```\n\n## Component requirements\n\n### Zero setup\n\nToast functionality is automatically available when using `NimbusProvider`. No additional setup is required:\n\n```tsx\nimport { NimbusProvider } from '@commercetools/nimbus';\n\nconst App = () => (\n <NimbusProvider>\n {/* Toast is ready to use anywhere in your app */}\n <YourApp />\n </NimbusProvider>\n);\n```\n\nThe `ToastOutlet` component is rendered internally by `NimbusProvider` and manages all toast regions.\n\n### Accessibility\n\nThe Toast component handles accessibility requirements internally via React Aria and Chakra UI.\n\n**ARIA role differentiation:**\n- Info, success, and warning toasts use `role=\"status\"` with `aria-live=\"polite\"` for non-intrusive announcements\n- Error toasts use `role=\"alert\"` with `aria-live=\"assertive\"` for immediate attention\n- The `ariaLive` option allows overriding the default politeness level for any toast type\n\n**Internationalization:**\nThe close button uses a localized `aria-label` resolved via `useLocalizedStringFormatter` from `@react-aria/i18n`. Translations are provided for English, German, Spanish, French, and Portuguese.\n\n#### Keyboard navigation\n\nToast regions support keyboard interaction:\n\n- `Tab`: Navigate between close button and action button (if present)\n- `Escape`: Dismiss the focused toast\n- `Ctrl+Shift+[1-9]`: Focus specific placement regions (works with both number row and numpad)\n - `Ctrl+Shift+7`: Top-start\n - `Ctrl+Shift+9`: Top-end (default)\n - `Ctrl+Shift+1`: Bottom-start\n - `Ctrl+Shift+3`: Bottom-end\n\nHotkeys are only active for placements with visible toasts.\n\n## API reference\n\n### `toast(options: ToastOptions): string`\n\nCreates a toast and returns its ID. Also available as convenience methods: `toast.info()`, `toast.success()`, `toast.warning()`, `toast.error()`.\n\n### ToastOptions\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `type` | `\"info\" \\| \"success\" \\| \"warning\" \\| \"error\"` | `\"info\"` | Visual style and ARIA semantics |\n| `title` | `string` | — | Title text |\n| `description` | `string` | — | Description text |\n| `action` | `{ label: string; onPress: () => void }` | — | Action button |\n| `icon` | `React.ReactElement` | — | Custom icon replacing the default type-based icon |\n| `duration` | `number` | `6000` | Auto-dismiss in ms (`Infinity` = never) |\n| `placement` | `ToastPlacement` | `\"top-end\"` | Screen position |\n| `closable` | `boolean` | `false` | Show/hide close button (auto-enabled for persistent toasts) |\n| `pauseOnInteraction` | `boolean` | `true` | Pause timer on hover/focus |\n| `variant` | `\"solid\" \\| \"subtle\" \\| \"accent-start\"` | `\"accent-start\"` | Visual variant |\n\n### Control methods\n\n| Method | Description |\n| --- | --- |\n| `toast.update(id, options)` | Update toast content |\n| `toast.dismiss(id?)` | Dismiss with animation (omit `id` for all) |\n| `toast.remove(id?)` | Remove immediately (omit `id` for all) |\n| `toast.promise(promise, options, config?)` | Loading → success/error transitions |\n\n## Common patterns\n\n### Error handling with actions\n\nProvide users with recovery options directly in error toasts:\n\n```jsx-live-dev\nconst App = () => {\n const handleRetry = () => {\n toast.info({ title: \"Retrying...\", description: \"Attempting operation again\" });\n };\n\n const handleError = () => {\n toast.error({\n title: \"Network Error\",\n description: \"Failed to connect to the server\",\n action: {\n label: \"Retry\",\n onPress: handleRetry\n }\n });\n };\n\n return (\n <Button onPress={handleError}>\n Simulate Error\n </Button>\n );\n}\n```\n\n### Success confirmation with undo\n\nAllow users to undo destructive actions:\n\n```jsx-live-dev\nconst App = () => {\n const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);\n const [lastDeleted, setLastDeleted] = useState<string | null>(null);\n\n const handleDelete = (item: string) => {\n setLastDeleted(item);\n setItems(items.filter(i => i !== item));\n\n toast.success({\n title: \"Item deleted\",\n description: `${item} has been removed`,\n action: {\n label: \"Undo\",\n onPress: () => {\n if (lastDeleted) {\n setItems([...items, lastDeleted]);\n setLastDeleted(null);\n toast.info({ title: \"Restored\", description: `${lastDeleted} has been restored` });\n }\n }\n }\n });\n };\n\n return (\n <Stack direction=\"column\" gap=\"200\">\n {items.map(item => (\n <Stack key={item} direction=\"row\" gap=\"200\" alignItems=\"center\">\n <Text>{item}</Text>\n <Button size=\"sm\" onPress={() => handleDelete(item)}>Delete</Button>\n </Stack>\n ))}\n {items.length === 0 && <Text>No items remaining</Text>}\n </Stack>\n );\n}\n```\n\n### Multi-step operations\n\nTrack progress through multi-step processes:\n\n```jsx-live-dev\nconst App = () => {\n const handleMultiStep = async () => {\n const id = toast({\n title: \"Step 1 of 3\",\n description: \"Validating data...\",\n type: \"info\",\n duration: Infinity\n });\n\n await new Promise(resolve => setTimeout(resolve, 1500));\n toast.update(id, {\n title: \"Step 2 of 3\",\n description: \"Processing records...\"\n });\n\n await new Promise(resolve => setTimeout(resolve, 1500));\n toast.update(id, {\n title: \"Step 3 of 3\",\n description: \"Finalizing...\"\n });\n\n await new Promise(resolve => setTimeout(resolve, 1500));\n toast.update(id, {\n title: \"Complete!\",\n description: \"All steps finished successfully\",\n type: \"success\",\n duration: 6000\n });\n };\n\n return (\n <Button onPress={handleMultiStep}>\n Start Multi-Step Process\n </Button>\n );\n}\n```\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using Toast in your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Usage\n\nCreate toast notifications using the imperative toast() API\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Basic usage\", () => {\n it(\"creates toast using toast() function\", () => {\n const id = toast({\n title: \"Imperative Toast\",\n description: \"Created programmatically\",\n type: \"info\",\n });\n\n expect(id).toBeDefined();\n expect(typeof id).toBe(\"string\");\n\n // Clean up\n toast.remove(id);\n });\n\n it(\"creates toasts using convenience methods\", () => {\n const successId = toast.success({\n title: \"Success Toast\",\n description: \"Operation completed successfully\",\n });\n\n const errorId = toast.error({\n title: \"Error Toast\",\n description: \"Something went wrong\",\n });\n\n const warningId = toast.warning({\n title: \"Warning Toast\",\n description: \"Please be careful\",\n });\n\n const infoId = toast.info({\n title: \"Info Toast\",\n description: \"Here is some information\",\n });\n\n expect(successId).toBeDefined();\n expect(errorId).toBeDefined();\n expect(warningId).toBeDefined();\n expect(infoId).toBeDefined();\n\n // Clean up\n toast.remove(successId);\n toast.remove(errorId);\n toast.remove(warningId);\n toast.remove(infoId);\n });\n\n it(\"creates toasts with custom placement\", () => {\n const id = toast({\n title: \"Custom Placement\",\n description: \"This toast appears at bottom-end\",\n placement: \"bottom-end\",\n });\n\n expect(id).toBeDefined();\n\n // Clean up\n toast.remove(id);\n });\n});\n```\n\n### Programmatic Control\n\nUpdate and dismiss toasts programmatically\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Programmatic control\", () => {\n it(\"updates toast content programmatically\", () => {\n const id = toast({\n title: \"Initial Title\",\n description: \"Initial Description\",\n duration: Infinity,\n });\n\n // Update the toast content\n toast.update(id, {\n title: \"Updated Title\",\n description: \"Updated Description\",\n });\n\n // Clean up\n toast.remove(id);\n });\n\n it(\"dismisses toast programmatically\", () => {\n const id = toast({\n title: \"Dismissible Toast\",\n description: \"This toast can be dismissed programmatically\",\n duration: Infinity,\n });\n\n // Dismiss triggers the exit animation\n toast.dismiss(id);\n });\n});\n```\n\n### Promise Pattern\n\nUse toast.promise() for async operation states\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Promise pattern\", () => {\n it(\"shows loading then success states\", async () => {\n const promiseSpy = vi.spyOn(toast, \"promise\");\n\n const asyncOperation = () =>\n new Promise((resolve) => {\n setTimeout(() => resolve(\"Done\"), 10);\n });\n\n const promise = asyncOperation();\n\n toast.promise(promise, {\n loading: {\n title: \"Loading...\",\n description: \"Please wait\",\n closable: false,\n },\n success: {\n title: \"Success!\",\n description: \"Operation completed\",\n type: \"success\",\n },\n error: {\n title: \"Failed\",\n description: \"Something went wrong\",\n type: \"error\",\n },\n });\n\n // Verify toast.promise was called with the correct promise and state titles\n expect(promiseSpy).toHaveBeenCalledWith(\n promise,\n expect.objectContaining({\n loading: expect.objectContaining({ title: \"Loading...\" }),\n success: expect.objectContaining({ title: \"Success!\" }),\n error: expect.objectContaining({ title: \"Failed\" }),\n })\n );\n\n promiseSpy.mockRestore();\n });\n});\n```\n\n### Action Buttons\n\nCreate toasts with action buttons and callbacks\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Action buttons\", () => {\n it(\"creates toast with action button\", () => {\n const handleAction = vi.fn();\n\n const id = toast({\n title: \"Action Required\",\n description: \"Please take action\",\n action: {\n label: \"Retry\",\n onPress: handleAction,\n },\n });\n\n expect(id).toBeDefined();\n\n // Clean up\n toast.remove(id);\n });\n});\n```\n\n\n## Resources\n\n{/* TODO: Add Storybook deep-link once the story is published */}\n- [Chakra UI Toast Documentation](https://www.chakra-ui.com/docs/components/toast)\n- [React Aria Live Regions](https://react-spectrum.adobe.com/react-aria/useLiveAnnouncer.html)\n- [ARIA Live Regions](https://www.w3.org/WAI/ARIA/apg/practices/live-regions/)\n",
700
+ "mdx": "\n## Getting started\n\n### Import\n\n```tsx\nimport { toast, type ToastOptions } from '@commercetools/nimbus';\n```\n\n### Basic usage\n\nCreate toast notifications using the imperative `toast()` API. Toasts are rendered automatically by the `NimbusProvider`.\n\n```jsx-live-dev\nconst App = () => {\n const handleClick = () => {\n toast({\n title: \"Success\",\n description: \"Your changes have been saved.\",\n type: \"success\"\n });\n };\n\n return (\n <Button onPress={handleClick}>\n Show Toast\n </Button>\n );\n}\n```\n\n## Usage examples\n\n### Toast types\n\nFour types are available, each with semantic meaning and ARIA roles:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast.info({ title: \"Information\", description: \"This is informational.\" })}>\n Info Toast\n </Button>\n <Button onPress={() => toast.success({ title: \"Success\", description: \"Operation completed.\" })}>\n Success Toast\n </Button>\n <Button onPress={() => toast.warning({ title: \"Warning\", description: \"Please review this.\" })}>\n Warning Toast\n </Button>\n <Button onPress={() => toast.error({ title: \"Error\", description: \"An error occurred.\" })}>\n Error Toast\n </Button>\n </Stack>\n)\n```\n\n**ARIA semantics:**\n- `info`, `success`, and `warning`: `role=\"status\"` (polite announcements)\n- `error`: `role=\"alert\"` (assertive announcements)\n- Use the `\"aria-live\"` option to override the default politeness level for any toast type\n\n### Placement options\n\nControl where toasts appear on screen using the `placement` prop. Each placement creates an independent toast region:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast({ title: \"Top Start\", placement: \"top-start\" })}>\n Top Start\n </Button>\n <Button onPress={() => toast({ title: \"Top End (Default)\", placement: \"top-end\" })}>\n Top End\n </Button>\n <Button onPress={() => toast({ title: \"Bottom Start\", placement: \"bottom-start\" })}>\n Bottom Start\n </Button>\n <Button onPress={() => toast({ title: \"Bottom End\", placement: \"bottom-end\" })}>\n Bottom End\n </Button>\n </Stack>\n)\n```\n\n### Auto-dismiss behavior\n\nToasts auto-dismiss after 6 seconds by default. The timer pauses on hover and focus:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast({ title: \"Default (6s)\", description: \"Dismisses after 6 seconds\" })}>\n Default Duration\n </Button>\n <Button onPress={() => toast({ title: \"Custom Duration\", description: \"Dismisses after 10 seconds\", duration: 10000 })}>\n 10 Second Duration\n </Button>\n <Button onPress={() => toast({ title: \"Persistent\", description: \"Never auto-dismisses\", duration: Infinity })}>\n No Auto-Dismiss\n </Button>\n </Stack>\n)\n```\n\n**Duration values:**\n- Default: `6000` (6 seconds)\n- Custom: Any positive number in milliseconds\n- Persistent: `Infinity` (never auto-dismisses)\n\n### Action buttons\n\nAdd action buttons to toasts. Duration is controlled independently — set `duration: Infinity` if the action requires user acknowledgment:\n\n```jsx-live-dev\nconst App = () => {\n const handleUndo = () => {\n toast.success({ title: \"Undo successful\" });\n };\n\n const handleDelete = () => {\n toast({\n title: \"Item deleted\",\n description: \"The item has been removed from your list.\",\n type: \"info\",\n action: {\n label: \"Undo\",\n onPress: handleUndo\n }\n });\n };\n\n return (\n <Button onPress={handleDelete}>\n Delete Item\n </Button>\n );\n}\n```\n\n### Promise pattern\n\nUse `toast.promise()` to show loading, success, and error states for async operations:\n\n```jsx-live-dev\nconst App = () => {\n const simulateAsyncOperation = () => {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n Math.random() > 0.5 ? resolve(\"Success!\") : reject(\"Failed!\");\n }, 2000);\n });\n };\n\n const handleClick = () => {\n toast.promise(\n simulateAsyncOperation(),\n {\n loading: { title: \"Saving...\", description: \"Please wait\", closable: false },\n success: { title: \"Saved\", description: \"Your changes have been saved\", type: \"success\" },\n error: { title: \"Error\", description: \"Failed to save changes\", type: \"error\" }\n }\n );\n };\n\n return (\n <Button onPress={handleClick}>\n Save with Promise\n </Button>\n );\n}\n```\n\n### Programmatic control\n\nUpdate or dismiss toasts programmatically using their IDs:\n\n```jsx-live-dev\nconst App = () => {\n const [toastId, setToastId] = useState<string | null>(null);\n\n const handleCreate = () => {\n const id = toast({\n title: \"Processing...\",\n description: \"Operation in progress\",\n duration: Infinity\n });\n setToastId(id);\n };\n\n const handleUpdate = () => {\n if (toastId) {\n toast.update(toastId, {\n title: \"Updated!\",\n description: \"Toast content has been updated\",\n type: \"success\"\n });\n }\n };\n\n const handleDismiss = () => {\n if (toastId) {\n toast.dismiss(toastId);\n setToastId(null);\n }\n };\n\n return (\n <Stack direction=\"row\" gap=\"200\">\n <Button onPress={handleCreate}>Create Toast</Button>\n <Button onPress={handleUpdate} isDisabled={!toastId}>Update Toast</Button>\n <Button onPress={handleDismiss} isDisabled={!toastId}>Dismiss Toast</Button>\n </Stack>\n );\n}\n```\n\n**API methods:**\n- `toast.update(id, options)`: Update toast content\n- `toast.dismiss(id)`: Dismiss with animation (omit `id` to dismiss all)\n- `toast.remove(id)`: Remove immediately without animation (omit `id` to remove all)\n\n### Custom icon\n\nOverride the default type-based icon with a custom icon element using the `icon` prop:\n\n```jsx-live-dev\nconst App = () => {\n const { Bathtub } = Icons;\n\n return (\n <Stack direction=\"row\" gap=\"200\" flexWrap=\"wrap\">\n <Button onPress={() => toast.info({ title: \"Custom Icon\", description: \"This toast uses a custom icon\", icon: <Bathtub /> })}>\n Custom Icon Toast\n </Button>\n <Button onPress={() => toast.info({ title: \"Default Icon\", description: \"This toast uses the default info icon\" })}>\n Default Icon Toast\n </Button>\n </Stack>\n );\n}\n```\n\nWhen the `icon` prop is provided, it replaces the default icon for that toast type. The `loading` type always shows a spinner regardless of the `icon` prop.\n\n### Closable control\n\nControl close button visibility with the `closable` prop. Useful for loading states:\n\n```jsx-live-dev\nconst App = () => (\n <Stack direction=\"row\" gap=\"200\">\n <Button onPress={() => toast({ title: \"Closable Toast\", description: \"Has close button\", closable: true })}>\n With Close Button\n </Button>\n <Button onPress={() => toast({ title: \"Loading...\", description: \"Processing your request\", closable: false, duration: 3000 })}>\n No Close Button\n </Button>\n </Stack>\n)\n```\n\n### Stacking and queuing\n\nMultiple toasts stack vertically without overlapping. When the maximum (24) is exceeded, additional toasts queue:\n\n```jsx-live-dev\nconst App = () => {\n const handleCreateMultiple = () => {\n for (let i = 1; i <= 5; i++) {\n setTimeout(() => {\n toast({\n title: `Toast ${i}`,\n description: `This is toast notification ${i}`,\n type: i % 2 === 0 ? \"success\" : \"info\"\n });\n }, i * 300);\n }\n };\n\n return (\n <Button onPress={handleCreateMultiple}>\n Create Multiple Toasts\n </Button>\n );\n}\n```\n\n## Component requirements\n\n### Zero setup\n\nToast functionality is automatically available when using `NimbusProvider`. No additional setup is required:\n\n```tsx\nimport { NimbusProvider } from '@commercetools/nimbus';\n\nconst App = () => (\n <NimbusProvider>\n {/* Toast is ready to use anywhere in your app */}\n <YourApp />\n </NimbusProvider>\n);\n```\n\nThe `ToastOutlet` component is rendered internally by `NimbusProvider` and manages all toast regions.\n\n### Accessibility\n\nThe Toast component handles accessibility requirements internally via React Aria and Chakra UI.\n\n**ARIA role differentiation:**\n- Info, success, and warning toasts use `role=\"status\"` with `aria-live=\"polite\"` for non-intrusive announcements\n- Error toasts use `role=\"alert\"` with `aria-live=\"assertive\"` for immediate attention\n- The `ariaLive` option allows overriding the default politeness level for any toast type\n\n**Internationalization:**\nThe close button uses a localized `aria-label` resolved via `useLocalizedStringFormatter` from `@react-aria/i18n`. Translations are provided for English, German, Spanish, French, and Portuguese.\n\n#### Keyboard navigation\n\nToast regions support keyboard interaction:\n\n- `Tab`: Navigate between close button and action button (if present)\n- `Escape`: Dismiss the focused toast\n- `Ctrl+Shift+[1-9]`: Focus specific placement regions (works with both number row and numpad)\n - `Ctrl+Shift+7`: Top-start\n - `Ctrl+Shift+9`: Top-end (default)\n - `Ctrl+Shift+1`: Bottom-start\n - `Ctrl+Shift+3`: Bottom-end\n\nHotkeys are only active for placements with visible toasts.\n\n## API reference\n\n### `toast(options: ToastOptions): string`\n\nCreates a toast and returns its ID. Also available as convenience methods: `toast.info()`, `toast.success()`, `toast.warning()`, `toast.error()`.\n\n### ToastOptions\n\n| Prop | Type | Default | Description |\n| --- | --- | --- | --- |\n| `type` | `\"info\" \\| \"success\" \\| \"warning\" \\| \"error\"` | `\"info\"` | Visual style and ARIA semantics |\n| `title` | `string` | — | Title text |\n| `description` | `string` | — | Description text |\n| `action` | `{ label: string; onPress: () => void }` | — | Action button |\n| `icon` | `React.ReactElement` | — | Custom icon replacing the default type-based icon |\n| `duration` | `number` | `6000` | Auto-dismiss in ms (`Infinity` = never) |\n| `placement` | `ToastPlacement` | `\"top-end\"` | Screen position |\n| `closable` | `boolean` | `false` | Show/hide close button (auto-enabled for persistent toasts) |\n| `variant` | `\"solid\" \\| \"subtle\" \\| \"accent-start\"` | `\"accent-start\"` | Visual variant |\n\n### Control methods\n\n| Method | Description |\n| --- | --- |\n| `toast.update(id, options)` | Update toast content |\n| `toast.dismiss(id?)` | Dismiss with animation (omit `id` for all) |\n| `toast.remove(id?)` | Remove immediately (omit `id` for all) |\n| `toast.promise(promise, options, config?)` | Loading → success/error transitions |\n\n## Common patterns\n\n### Error handling with actions\n\nProvide users with recovery options directly in error toasts:\n\n```jsx-live-dev\nconst App = () => {\n const handleRetry = () => {\n toast.info({ title: \"Retrying...\", description: \"Attempting operation again\" });\n };\n\n const handleError = () => {\n toast.error({\n title: \"Network Error\",\n description: \"Failed to connect to the server\",\n action: {\n label: \"Retry\",\n onPress: handleRetry\n }\n });\n };\n\n return (\n <Button onPress={handleError}>\n Simulate Error\n </Button>\n );\n}\n```\n\n### Success confirmation with undo\n\nAllow users to undo destructive actions:\n\n```jsx-live-dev\nconst App = () => {\n const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);\n const [lastDeleted, setLastDeleted] = useState<string | null>(null);\n\n const handleDelete = (item: string) => {\n setLastDeleted(item);\n setItems(items.filter(i => i !== item));\n\n toast.success({\n title: \"Item deleted\",\n description: `${item} has been removed`,\n action: {\n label: \"Undo\",\n onPress: () => {\n if (lastDeleted) {\n setItems([...items, lastDeleted]);\n setLastDeleted(null);\n toast.info({ title: \"Restored\", description: `${lastDeleted} has been restored` });\n }\n }\n }\n });\n };\n\n return (\n <Stack direction=\"column\" gap=\"200\">\n {items.map(item => (\n <Stack key={item} direction=\"row\" gap=\"200\" alignItems=\"center\">\n <Text>{item}</Text>\n <Button size=\"sm\" onPress={() => handleDelete(item)}>Delete</Button>\n </Stack>\n ))}\n {items.length === 0 && <Text>No items remaining</Text>}\n </Stack>\n );\n}\n```\n\n### Multi-step operations\n\nTrack progress through multi-step processes:\n\n```jsx-live-dev\nconst App = () => {\n const handleMultiStep = async () => {\n const id = toast({\n title: \"Step 1 of 3\",\n description: \"Validating data...\",\n type: \"info\",\n duration: Infinity\n });\n\n await new Promise(resolve => setTimeout(resolve, 1500));\n toast.update(id, {\n title: \"Step 2 of 3\",\n description: \"Processing records...\"\n });\n\n await new Promise(resolve => setTimeout(resolve, 1500));\n toast.update(id, {\n title: \"Step 3 of 3\",\n description: \"Finalizing...\"\n });\n\n await new Promise(resolve => setTimeout(resolve, 1500));\n toast.update(id, {\n title: \"Complete!\",\n description: \"All steps finished successfully\",\n type: \"success\",\n duration: 6000\n });\n };\n\n return (\n <Button onPress={handleMultiStep}>\n Start Multi-Step Process\n </Button>\n );\n}\n```\n\n## Testing your implementation\n\nThese examples demonstrate how to test your implementation when using Toast in your application. As the component's internal functionality is already tested by Nimbus, these patterns help you verify your integration and application-specific logic.\n\n### Basic Usage\n\nCreate toast notifications using the imperative toast() API\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Basic usage\", () => {\n it(\"creates toast using toast() function\", () => {\n const id = toast({\n title: \"Imperative Toast\",\n description: \"Created programmatically\",\n type: \"info\",\n });\n\n expect(id).toBeDefined();\n expect(typeof id).toBe(\"string\");\n\n // Clean up\n toast.remove(id);\n });\n\n it(\"creates toasts using convenience methods\", () => {\n const successId = toast.success({\n title: \"Success Toast\",\n description: \"Operation completed successfully\",\n });\n\n const errorId = toast.error({\n title: \"Error Toast\",\n description: \"Something went wrong\",\n });\n\n const warningId = toast.warning({\n title: \"Warning Toast\",\n description: \"Please be careful\",\n });\n\n const infoId = toast.info({\n title: \"Info Toast\",\n description: \"Here is some information\",\n });\n\n expect(successId).toBeDefined();\n expect(errorId).toBeDefined();\n expect(warningId).toBeDefined();\n expect(infoId).toBeDefined();\n\n // Clean up\n toast.remove(successId);\n toast.remove(errorId);\n toast.remove(warningId);\n toast.remove(infoId);\n });\n\n it(\"creates toasts with custom placement\", () => {\n const id = toast({\n title: \"Custom Placement\",\n description: \"This toast appears at bottom-end\",\n placement: \"bottom-end\",\n });\n\n expect(id).toBeDefined();\n\n // Clean up\n toast.remove(id);\n });\n});\n```\n\n### Programmatic Control\n\nUpdate and dismiss toasts programmatically\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Programmatic control\", () => {\n it(\"updates toast content programmatically\", () => {\n const id = toast({\n title: \"Initial Title\",\n description: \"Initial Description\",\n duration: Infinity,\n });\n\n // Update the toast content\n toast.update(id, {\n title: \"Updated Title\",\n description: \"Updated Description\",\n });\n\n // Clean up\n toast.remove(id);\n });\n\n it(\"dismisses toast programmatically\", () => {\n const id = toast({\n title: \"Dismissible Toast\",\n description: \"This toast can be dismissed programmatically\",\n duration: Infinity,\n });\n\n // Dismiss triggers the exit animation\n toast.dismiss(id);\n });\n});\n```\n\n### Promise Pattern\n\nUse toast.promise() for async operation states\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Promise pattern\", () => {\n it(\"shows loading then success states\", async () => {\n const promiseSpy = vi.spyOn(toast, \"promise\");\n\n const asyncOperation = () =>\n new Promise((resolve) => {\n setTimeout(() => resolve(\"Done\"), 10);\n });\n\n const promise = asyncOperation();\n\n toast.promise(promise, {\n loading: {\n title: \"Loading...\",\n description: \"Please wait\",\n closable: false,\n },\n success: {\n title: \"Success!\",\n description: \"Operation completed\",\n type: \"success\",\n },\n error: {\n title: \"Failed\",\n description: \"Something went wrong\",\n type: \"error\",\n },\n });\n\n // Verify toast.promise was called with the correct promise and state titles\n expect(promiseSpy).toHaveBeenCalledWith(\n promise,\n expect.objectContaining({\n loading: expect.objectContaining({ title: \"Loading...\" }),\n success: expect.objectContaining({ title: \"Success!\" }),\n error: expect.objectContaining({ title: \"Failed\" }),\n })\n );\n\n promiseSpy.mockRestore();\n });\n});\n```\n\n### Action Buttons\n\nCreate toasts with action buttons and callbacks\n\n```tsx\nimport { describe, it, expect, vi } from \"vitest\";\nimport { toast } from \"@commercetools/nimbus\";\n\ndescribe(\"Toast - Action buttons\", () => {\n it(\"creates toast with action button\", () => {\n const handleAction = vi.fn();\n\n const id = toast({\n title: \"Action Required\",\n description: \"Please take action\",\n action: {\n label: \"Retry\",\n onPress: handleAction,\n },\n });\n\n expect(id).toBeDefined();\n\n // Clean up\n toast.remove(id);\n });\n});\n```\n\n\n## Resources\n\n{/* TODO: Add Storybook deep-link once the story is published */}\n- [Chakra UI Toast Documentation](https://www.chakra-ui.com/docs/components/toast)\n- [React Aria Live Regions](https://react-spectrum.adobe.com/react-aria/useLiveAnnouncer.html)\n- [ARIA Live Regions](https://www.w3.org/WAI/ARIA/apg/practices/live-regions/)\n",
701
701
  "toc": [
702
702
  {
703
703
  "value": "Getting started",