@olympusoss/canvas 4.0.0 → 5.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 (297) hide show
  1. package/README.md +108 -0
  2. package/package.json +14 -3
  3. package/src/atoms/avatar/avatar.md +185 -0
  4. package/src/atoms/avatar/avatar.styles.ts +48 -0
  5. package/src/atoms/avatar/avatar.tsx +99 -0
  6. package/src/atoms/badge/badge.md +237 -0
  7. package/src/atoms/badge/badge.styles.ts +79 -0
  8. package/src/atoms/badge/badge.tsx +86 -0
  9. package/src/atoms/breadcrumb/breadcrumb.md +233 -0
  10. package/src/atoms/breadcrumb/breadcrumb.styles.ts +40 -0
  11. package/src/atoms/breadcrumb/breadcrumb.tsx +130 -0
  12. package/src/atoms/button/button.android.tsx +6 -0
  13. package/src/atoms/button/button.ios.tsx +6 -0
  14. package/src/atoms/button/button.md +184 -0
  15. package/src/atoms/button/button.shared.tsx +79 -0
  16. package/src/atoms/button/button.styles.ts +152 -0
  17. package/src/atoms/button/button.tsx +6 -0
  18. package/src/atoms/button-group/button-group.android.tsx +6 -0
  19. package/src/atoms/button-group/button-group.ios.tsx +6 -0
  20. package/src/atoms/button-group/button-group.md +120 -0
  21. package/src/atoms/button-group/button-group.shared.tsx +398 -0
  22. package/src/atoms/button-group/button-group.styles.ts +483 -0
  23. package/src/atoms/button-group/button-group.tsx +6 -0
  24. package/src/atoms/checkbox/checkbox.android.tsx +6 -0
  25. package/src/atoms/checkbox/checkbox.ios.tsx +6 -0
  26. package/src/atoms/checkbox/checkbox.md +150 -0
  27. package/src/atoms/checkbox/checkbox.shared.tsx +103 -0
  28. package/src/atoms/checkbox/checkbox.styles.ts +106 -0
  29. package/src/atoms/checkbox/checkbox.tsx +6 -0
  30. package/src/atoms/combobox/combobox.android.tsx +6 -0
  31. package/src/atoms/combobox/combobox.ios.tsx +6 -0
  32. package/src/atoms/combobox/combobox.md +213 -0
  33. package/src/atoms/combobox/combobox.shared.tsx +160 -0
  34. package/src/atoms/combobox/combobox.styles.ts +270 -0
  35. package/src/atoms/combobox/combobox.tsx +6 -0
  36. package/src/atoms/divider/divider.md +140 -0
  37. package/src/atoms/divider/divider.styles.ts +35 -0
  38. package/src/atoms/divider/divider.tsx +67 -0
  39. package/src/atoms/dropdown/dropdown.android.tsx +6 -0
  40. package/src/atoms/dropdown/dropdown.ios.tsx +6 -0
  41. package/src/atoms/dropdown/dropdown.md +221 -0
  42. package/src/atoms/dropdown/dropdown.shared.tsx +190 -0
  43. package/src/atoms/dropdown/dropdown.styles.ts +233 -0
  44. package/src/atoms/dropdown/dropdown.tsx +6 -0
  45. package/src/atoms/icon/icon.md +131 -0
  46. package/src/atoms/icon/icon.styles.ts +30 -0
  47. package/src/atoms/icon/icon.tsx +328 -0
  48. package/src/atoms/index.ts +24 -0
  49. package/src/atoms/input/input.android.tsx +6 -0
  50. package/src/atoms/input/input.ios.tsx +6 -0
  51. package/src/atoms/input/input.md +118 -0
  52. package/src/atoms/input/input.shared.tsx +203 -0
  53. package/src/atoms/input/input.styles.ts +286 -0
  54. package/src/atoms/input/input.tsx +6 -0
  55. package/src/atoms/kbd/kbd.md +91 -0
  56. package/src/atoms/kbd/kbd.styles.ts +33 -0
  57. package/src/atoms/kbd/kbd.tsx +27 -0
  58. package/src/atoms/listbox/listbox.md +177 -0
  59. package/src/atoms/listbox/listbox.styles.ts +60 -0
  60. package/src/atoms/listbox/listbox.tsx +113 -0
  61. package/src/atoms/pagination/pagination.android.tsx +6 -0
  62. package/src/atoms/pagination/pagination.ios.tsx +6 -0
  63. package/src/atoms/pagination/pagination.md +133 -0
  64. package/src/atoms/pagination/pagination.shared.tsx +289 -0
  65. package/src/atoms/pagination/pagination.styles.ts +245 -0
  66. package/src/atoms/pagination/pagination.tsx +6 -0
  67. package/src/atoms/popover/popover.android.tsx +8 -0
  68. package/src/atoms/popover/popover.ios.tsx +6 -0
  69. package/src/atoms/popover/popover.md +87 -0
  70. package/src/atoms/popover/popover.shared.tsx +124 -0
  71. package/src/atoms/popover/popover.styles.ts +144 -0
  72. package/src/atoms/popover/popover.tsx +6 -0
  73. package/src/atoms/radio/radio.android.tsx +6 -0
  74. package/src/atoms/radio/radio.ios.tsx +6 -0
  75. package/src/atoms/radio/radio.md +173 -0
  76. package/src/atoms/radio/radio.shared.tsx +98 -0
  77. package/src/atoms/radio/radio.styles.ts +109 -0
  78. package/src/atoms/radio/radio.tsx +6 -0
  79. package/src/atoms/select/select.android.tsx +6 -0
  80. package/src/atoms/select/select.ios.tsx +6 -0
  81. package/src/atoms/select/select.md +156 -0
  82. package/src/atoms/select/select.shared.tsx +143 -0
  83. package/src/atoms/select/select.styles.ts +310 -0
  84. package/src/atoms/select/select.tsx +6 -0
  85. package/src/atoms/skeleton/skeleton.md +135 -0
  86. package/src/atoms/skeleton/skeleton.styles.ts +117 -0
  87. package/src/atoms/skeleton/skeleton.tsx +145 -0
  88. package/src/atoms/spinner/spinner.android.tsx +7 -0
  89. package/src/atoms/spinner/spinner.ios.tsx +7 -0
  90. package/src/atoms/spinner/spinner.md +94 -0
  91. package/src/atoms/spinner/spinner.shared.tsx +92 -0
  92. package/src/atoms/spinner/spinner.styles.tsx +115 -0
  93. package/src/atoms/spinner/spinner.tsx +7 -0
  94. package/src/atoms/switch/switch.android.tsx +6 -0
  95. package/src/atoms/switch/switch.ios.tsx +6 -0
  96. package/src/atoms/switch/switch.md +91 -0
  97. package/src/atoms/switch/switch.shared.tsx +97 -0
  98. package/src/atoms/switch/switch.styles.ts +79 -0
  99. package/src/atoms/switch/switch.tsx +6 -0
  100. package/src/atoms/textarea/textarea.android.tsx +6 -0
  101. package/src/atoms/textarea/textarea.ios.tsx +6 -0
  102. package/src/atoms/textarea/textarea.md +140 -0
  103. package/src/atoms/textarea/textarea.shared.tsx +74 -0
  104. package/src/atoms/textarea/textarea.styles.ts +116 -0
  105. package/src/atoms/textarea/textarea.tsx +6 -0
  106. package/src/atoms/tooltip/tooltip.android.tsx +6 -0
  107. package/src/atoms/tooltip/tooltip.ios.tsx +7 -0
  108. package/src/atoms/tooltip/tooltip.md +122 -0
  109. package/src/atoms/tooltip/tooltip.shared.tsx +113 -0
  110. package/src/atoms/tooltip/tooltip.styles.ts +113 -0
  111. package/src/atoms/tooltip/tooltip.tsx +6 -0
  112. package/src/atoms/typography/typography.md +330 -0
  113. package/src/atoms/typography/typography.styles.ts +95 -0
  114. package/src/atoms/typography/typography.tsx +76 -0
  115. package/src/index.ts +12 -2
  116. package/src/molecules/action-panels/action-panels.md +133 -0
  117. package/src/molecules/action-panels/action-panels.styles.ts +39 -0
  118. package/src/molecules/action-panels/action-panels.tsx +113 -0
  119. package/src/molecules/alert/alert.md +119 -0
  120. package/src/molecules/alert/alert.styles.ts +88 -0
  121. package/src/molecules/alert/alert.tsx +74 -0
  122. package/src/molecules/alert-dialog/alert-dialog.android.tsx +6 -0
  123. package/src/molecules/alert-dialog/alert-dialog.ios.tsx +6 -0
  124. package/src/molecules/alert-dialog/alert-dialog.md +177 -0
  125. package/src/molecules/alert-dialog/alert-dialog.shared.tsx +187 -0
  126. package/src/molecules/alert-dialog/alert-dialog.styles.ts +248 -0
  127. package/src/molecules/alert-dialog/alert-dialog.tsx +6 -0
  128. package/src/molecules/card/card.md +190 -0
  129. package/src/molecules/card/card.styles.ts +67 -0
  130. package/src/molecules/card/card.tsx +176 -0
  131. package/src/molecules/code-block/code-block.md +159 -0
  132. package/src/molecules/code-block/code-block.styles.ts +167 -0
  133. package/src/molecules/code-block/code-block.tsx +176 -0
  134. package/src/molecules/description-lists/description-lists.md +129 -0
  135. package/src/molecules/description-lists/description-lists.styles.ts +102 -0
  136. package/src/molecules/description-lists/description-lists.tsx +133 -0
  137. package/src/molecules/empty-state/empty-state.md +218 -0
  138. package/src/molecules/empty-state/empty-state.styles.ts +63 -0
  139. package/src/molecules/empty-state/empty-state.tsx +77 -0
  140. package/src/molecules/feeds/feeds.md +102 -0
  141. package/src/molecules/feeds/feeds.styles.ts +120 -0
  142. package/src/molecules/feeds/feeds.tsx +167 -0
  143. package/src/molecules/field/field.md +117 -0
  144. package/src/molecules/field/field.styles.ts +85 -0
  145. package/src/molecules/field/field.tsx +175 -0
  146. package/src/molecules/fieldset/fieldset.md +141 -0
  147. package/src/molecules/fieldset/fieldset.styles.ts +79 -0
  148. package/src/molecules/fieldset/fieldset.tsx +182 -0
  149. package/src/molecules/form/form.md +137 -0
  150. package/src/molecules/form/form.styles.ts +39 -0
  151. package/src/molecules/form/form.tsx +246 -0
  152. package/src/molecules/grid-lists/grid-lists.md +114 -0
  153. package/src/molecules/grid-lists/grid-lists.styles.ts +79 -0
  154. package/src/molecules/grid-lists/grid-lists.tsx +157 -0
  155. package/src/molecules/index.ts +16 -0
  156. package/src/molecules/media-objects/media-objects.md +87 -0
  157. package/src/molecules/media-objects/media-objects.styles.ts +94 -0
  158. package/src/molecules/media-objects/media-objects.tsx +128 -0
  159. package/src/molecules/stacked-lists/stacked-lists.md +116 -0
  160. package/src/molecules/stacked-lists/stacked-lists.styles.ts +111 -0
  161. package/src/molecules/stacked-lists/stacked-lists.tsx +195 -0
  162. package/src/molecules/stats/stats.md +166 -0
  163. package/src/molecules/stats/stats.styles.ts +91 -0
  164. package/src/molecules/stats/stats.tsx +88 -0
  165. package/src/organisms/calendar/calendar.android.tsx +6 -0
  166. package/src/organisms/calendar/calendar.ios.tsx +6 -0
  167. package/src/organisms/calendar/calendar.md +114 -0
  168. package/src/organisms/calendar/calendar.shared.tsx +146 -0
  169. package/src/organisms/calendar/calendar.styles.ts +315 -0
  170. package/src/organisms/calendar/calendar.tsx +6 -0
  171. package/src/organisms/charts/charts.md +326 -0
  172. package/src/organisms/charts/charts.styles.ts +135 -0
  173. package/src/organisms/charts/charts.tsx +124 -0
  174. package/src/organisms/command/command.md +117 -0
  175. package/src/organisms/command/command.styles.ts +179 -0
  176. package/src/organisms/command/command.tsx +164 -0
  177. package/src/organisms/data-table/data-table.md +182 -0
  178. package/src/organisms/data-table/data-table.styles.ts +103 -0
  179. package/src/organisms/data-table/data-table.tsx +105 -0
  180. package/src/organisms/dialog/dialog.android.tsx +6 -0
  181. package/src/organisms/dialog/dialog.ios.tsx +6 -0
  182. package/src/organisms/dialog/dialog.md +271 -0
  183. package/src/organisms/dialog/dialog.shared.tsx +230 -0
  184. package/src/organisms/dialog/dialog.styles.ts +272 -0
  185. package/src/organisms/dialog/dialog.tsx +6 -0
  186. package/src/organisms/filter-panel/filter-panel.md +116 -0
  187. package/src/organisms/filter-panel/filter-panel.styles.ts +83 -0
  188. package/src/organisms/filter-panel/filter-panel.tsx +91 -0
  189. package/src/organisms/index.ts +13 -0
  190. package/src/organisms/navbars/navbars.android.tsx +6 -0
  191. package/src/organisms/navbars/navbars.ios.tsx +6 -0
  192. package/src/organisms/navbars/navbars.md +144 -0
  193. package/src/organisms/navbars/navbars.shared.tsx +137 -0
  194. package/src/organisms/navbars/navbars.styles.ts +251 -0
  195. package/src/organisms/navbars/navbars.tsx +6 -0
  196. package/src/organisms/overlays/overlays.android.tsx +6 -0
  197. package/src/organisms/overlays/overlays.ios.tsx +6 -0
  198. package/src/organisms/overlays/overlays.md +123 -0
  199. package/src/organisms/overlays/overlays.shared.tsx +175 -0
  200. package/src/organisms/overlays/overlays.styles.ts +309 -0
  201. package/src/organisms/overlays/overlays.tsx +6 -0
  202. package/src/organisms/row-menu/row-menu.android.tsx +6 -0
  203. package/src/organisms/row-menu/row-menu.ios.tsx +6 -0
  204. package/src/organisms/row-menu/row-menu.md +102 -0
  205. package/src/organisms/row-menu/row-menu.shared.tsx +105 -0
  206. package/src/organisms/row-menu/row-menu.styles.ts +262 -0
  207. package/src/organisms/row-menu/row-menu.tsx +6 -0
  208. package/src/organisms/sidebar/sidebar.android.tsx +6 -0
  209. package/src/organisms/sidebar/sidebar.ios.tsx +6 -0
  210. package/src/organisms/sidebar/sidebar.md +188 -0
  211. package/src/organisms/sidebar/sidebar.shared.tsx +167 -0
  212. package/src/organisms/sidebar/sidebar.styles.ts +262 -0
  213. package/src/organisms/sidebar/sidebar.tsx +6 -0
  214. package/src/organisms/stepper/stepper.android.tsx +6 -0
  215. package/src/organisms/stepper/stepper.ios.tsx +6 -0
  216. package/src/organisms/stepper/stepper.md +150 -0
  217. package/src/organisms/stepper/stepper.shared.tsx +158 -0
  218. package/src/organisms/stepper/stepper.styles.ts +280 -0
  219. package/src/organisms/stepper/stepper.tsx +6 -0
  220. package/src/organisms/tabs/tabs.android.tsx +6 -0
  221. package/src/organisms/tabs/tabs.ios.tsx +6 -0
  222. package/src/organisms/tabs/tabs.md +127 -0
  223. package/src/organisms/tabs/tabs.shared.tsx +281 -0
  224. package/src/organisms/tabs/tabs.styles.ts +398 -0
  225. package/src/organisms/tabs/tabs.tsx +6 -0
  226. package/src/style/color.ts +17 -0
  227. package/src/style/index.ts +14 -0
  228. package/src/style/primitives.ts +26 -0
  229. package/src/style/responsive.ts +45 -0
  230. package/src/style/shadow.ts +21 -0
  231. package/src/style/theme.tsx +56 -0
  232. package/src/style/tokens.ts +487 -0
  233. package/src/theme.ts +21 -0
  234. package/styles/canvas.css +128 -67
  235. package/tsconfig.json +4 -2
  236. package/src/cn.ts +0 -3
  237. package/styles/base.css +0 -17
  238. package/styles/components/alert.css +0 -66
  239. package/styles/components/app-shell.css +0 -46
  240. package/styles/components/avatar.css +0 -15
  241. package/styles/components/badge.css +0 -83
  242. package/styles/components/breadcrumb.css +0 -35
  243. package/styles/components/button-group.css +0 -23
  244. package/styles/components/button.css +0 -107
  245. package/styles/components/calendar.css +0 -73
  246. package/styles/components/card.css +0 -58
  247. package/styles/components/checkbox.css +0 -55
  248. package/styles/components/code-block.css +0 -18
  249. package/styles/components/combobox.css +0 -75
  250. package/styles/components/command.css +0 -94
  251. package/styles/components/data-table.css +0 -142
  252. package/styles/components/dialog.css +0 -72
  253. package/styles/components/dropdown.css +0 -54
  254. package/styles/components/empty-state.css +0 -17
  255. package/styles/components/field.css +0 -27
  256. package/styles/components/filter-panel.css +0 -58
  257. package/styles/components/form.css +0 -27
  258. package/styles/components/icon.css +0 -8
  259. package/styles/components/input-group.css +0 -45
  260. package/styles/components/input.css +0 -56
  261. package/styles/components/kbd.css +0 -15
  262. package/styles/components/page-header.css +0 -52
  263. package/styles/components/pagination.css +0 -48
  264. package/styles/components/popover.css +0 -14
  265. package/styles/components/radio.css +0 -28
  266. package/styles/components/row-menu.css +0 -69
  267. package/styles/components/section-card.css +0 -49
  268. package/styles/components/select.css +0 -57
  269. package/styles/components/separator.css +0 -32
  270. package/styles/components/sheet.css +0 -70
  271. package/styles/components/sidebar.css +0 -146
  272. package/styles/components/skeleton.css +0 -32
  273. package/styles/components/spinner.css +0 -26
  274. package/styles/components/stat-card.css +0 -71
  275. package/styles/components/stepper.css +0 -63
  276. package/styles/components/switch.css +0 -45
  277. package/styles/components/tabs.css +0 -40
  278. package/styles/components/textarea.css +0 -31
  279. package/styles/components/toast.css +0 -95
  280. package/styles/components/tooltip.css +0 -53
  281. package/styles/components/topbar.css +0 -24
  282. package/styles/components/typography.css +0 -105
  283. package/styles/patterns/backdrops.css +0 -35
  284. package/styles/patterns/density.css +0 -66
  285. package/styles/patterns/focus.css +0 -38
  286. package/styles/patterns/glass.css +0 -85
  287. package/styles/patterns/high-contrast.css +0 -70
  288. package/styles/patterns/reduced-motion.css +0 -12
  289. package/styles/patterns/scrollbar.css +0 -10
  290. package/styles/reset.css +0 -89
  291. package/styles/tokens/colors.css +0 -106
  292. package/styles/tokens/motion.css +0 -33
  293. package/styles/tokens/radius.css +0 -10
  294. package/styles/tokens/shadows.css +0 -35
  295. package/styles/tokens/spacing.css +0 -19
  296. package/styles/tokens/typography.css +0 -6
  297. package/styles/tokens/z-index.css +0 -12
@@ -0,0 +1,113 @@
1
+ import { View, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
2
+ import { Card } from "../card/card.js";
3
+ import { Button } from "../../atoms/button/button.js";
4
+ import { Switch } from "../../atoms/switch/switch.js";
5
+ import * as s from "./action-panels.styles.js";
6
+ import { type Tone, type Layout } from "./action-panels.styles.js";
7
+
8
+ // An action panel is a settings card: a headline and a line of consequence copy
9
+ // on one side, and a single action (a Button) that acts on it. It surfaces one
10
+ // decision at a time, the safe-default version always pairing the action with the
11
+ // copy that explains the stakes.
12
+ //
13
+ // Boolean-prop API, grouped by axis, first-match precedence within an axis
14
+ // (mirrors Button's intentOf):
15
+ //
16
+ // - Tone: `destructive` paints the headline red and renders a destructive
17
+ // (red) Button; omit for the neutral tone, where the action is a primary
18
+ // Button. This is the "danger zone" switch.
19
+ // - Layout (pick one): `inline` floats the action to the right of the copy, the
20
+ // two reading as one side-by-side row; omit for the default, where the action
21
+ // sits on its own line below the copy.
22
+ // - Affordance: `toggle` makes the action an on/off Switch (its state is
23
+ // `checked`) instead of a Button; the panel reads as a setting row, always
24
+ // laid out inline with the Switch pinned to the right. Omit for the default
25
+ // Button action.
26
+ //
27
+ // The axes are orthogonal: `<ActionPanel destructive inline />` is a red
28
+ // action sitting to the right of its danger copy, and `<ActionPanel toggle
29
+ // checked title="..." description="..." />` is a settings row whose switch is on.
30
+
31
+ export interface ActionPanelProps {
32
+ /** The headline: what the action acts on. */
33
+ title?: string;
34
+ /** The consequence copy beneath the title: what happens when the action fires. */
35
+ description?: string;
36
+ /** The action button label. */
37
+ actionLabel?: string;
38
+ /** Fired when the action button is pressed. */
39
+ onAction?: () => void;
40
+ // Tone (omit for the neutral, primary-action default).
41
+ destructive?: boolean;
42
+ // Layout (pick one; default stacks the action below the copy).
43
+ inline?: boolean;
44
+ // Affordance: render the action as an on/off Switch instead of a Button. The
45
+ // panel always lays out inline in this mode.
46
+ toggle?: boolean;
47
+ /** The Switch on/off state when `toggle` is set. */
48
+ checked?: boolean;
49
+ /** Fired with the next checked value when the toggle Switch is flipped. */
50
+ onToggle?: (next: boolean) => void;
51
+ /** Escape hatch for layout/positioning composition (mainly width). */
52
+ style?: StyleProp<ViewStyle>;
53
+ }
54
+
55
+ // Tone precedence when more than one flag is passed: first match wins.
56
+ function toneOf(p: ActionPanelProps): Tone {
57
+ if (p.destructive) return "destructive";
58
+ return "neutral";
59
+ }
60
+
61
+ // Layout precedence when more than one flag is passed: first match wins.
62
+ function layoutOf(p: ActionPanelProps): Layout {
63
+ if (p.inline) return "inline";
64
+ return "stacked";
65
+ }
66
+
67
+ export function ActionPanel(props: ActionPanelProps) {
68
+ const { title, description, actionLabel, onAction, toggle, checked, onToggle, style } = props;
69
+ const { tokens, dark } = useTheme();
70
+ const tone = toneOf(props);
71
+ // The toggle affordance always reads as an inline settings row.
72
+ const layout = toggle ? "inline" : layoutOf(props);
73
+
74
+ // The copy block: title above its consequence line. In the inline layout it
75
+ // grows to push the action to the right; stacked, it sits above the action.
76
+ const copy = (
77
+ <View style={[s.copyBlock, layout === "inline" ? s.copyGrow : null]}>
78
+ {title != null ? <Text style={s.titleText(tokens, dark, tone)}>{title}</Text> : null}
79
+ {description != null ? <Text style={s.descriptionText(tokens)}>{description}</Text> : null}
80
+ </View>
81
+ );
82
+
83
+ // The action. In toggle mode it is an on/off Switch pinned to the right;
84
+ // otherwise a destructive (red) Button in the danger zone or a primary Button
85
+ // otherwise, small to sit comfortably inside the panel.
86
+ const action = toggle ? (
87
+ <View style={s.actionPinned}>
88
+ <Switch checked={checked} onValueChange={onToggle} />
89
+ </View>
90
+ ) : actionLabel != null ? (
91
+ <View style={layout === "inline" ? s.actionPinned : s.actionStacked}>
92
+ <Button small destructive={tone === "destructive"} primary={tone !== "destructive"} onPress={onAction}>
93
+ {actionLabel}
94
+ </Button>
95
+ </View>
96
+ ) : null;
97
+
98
+ return (
99
+ <Card padded style={[s.cardWidth, style]}>
100
+ {layout === "inline" ? (
101
+ <View style={s.inlineBody}>
102
+ {copy}
103
+ {action}
104
+ </View>
105
+ ) : (
106
+ <View style={s.stackedBody}>
107
+ {copy}
108
+ {action}
109
+ </View>
110
+ )}
111
+ </Card>
112
+ );
113
+ }
@@ -0,0 +1,119 @@
1
+ # Alerts
2
+
3
+ Inline notification banners: info, success, warning, and error, plus a full-width announcement bar. For a blocking confirmation prompt, see Alert Dialog.
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ <Alert
9
+ info
10
+ icon="ℹ"
11
+ title="Heads up"
12
+ description="Maintenance window scheduled for Sunday 2:00 UTC."
13
+ dismissible
14
+ />
15
+ ```
16
+
17
+ ## Variants
18
+
19
+ ### Variant - success
20
+
21
+ ```tsx
22
+ <Alert
23
+ success
24
+ icon="✓"
25
+ title="All set"
26
+ description="Your changes have been saved successfully."
27
+ dismissible
28
+ />
29
+ ```
30
+
31
+ ### Variant - warning
32
+
33
+ ```tsx
34
+ <Alert
35
+ warning
36
+ icon="⚠"
37
+ title="Action required"
38
+ description="Your trial expires in 3 days."
39
+ dismissible
40
+ />
41
+ ```
42
+
43
+ ### Variant - destructive
44
+
45
+ ```tsx
46
+ <Alert
47
+ error
48
+ icon="✕"
49
+ title="Something went wrong"
50
+ description="Could not save your changes. Please try again."
51
+ dismissible
52
+ />
53
+ ```
54
+
55
+ ## Do & Don't
56
+
57
+ ### info
58
+
59
+ **Do** — Reserve info for passive, non-urgent context (notices, tips); escalate to warning or destructive when action is required.
60
+
61
+ ```tsx
62
+ <Alert info icon="ℹ" title="Heads up" description="Maintenance window scheduled for Sunday 2:00 UTC." />
63
+ ```
64
+
65
+ **Don't** — Dressing an act-now message in the neutral info tone hides the urgency; users skim past it like an FYI.
66
+
67
+ ```tsx
68
+ <Alert info icon="ℹ" title="Trial expires today" description="Upgrade now or you'll lose access to your projects." />
69
+ ```
70
+
71
+ ### success
72
+
73
+ **Do** — Make confirmations transient: auto-dismiss or give a Dismiss control so the success state clears once acknowledged.
74
+
75
+ ```tsx
76
+ <Alert success icon="✓" title="Saved" description="Your changes have been saved successfully.">
77
+ <View style={{ marginTop: 12, flexDirection: "row", gap: 8 }}>
78
+ <Button ghost small>Dismiss</Button>
79
+ </View>
80
+ </Alert>
81
+ ```
82
+
83
+ **Don't** — A success banner pinned with no way to dismiss it lingers as visual noise long after the action is done.
84
+
85
+ ```tsx
86
+ <Alert success icon="✓" title="Saved" description="Your changes have been saved successfully." />
87
+ ```
88
+
89
+ ### warning
90
+
91
+ **Do** — State the consequence, the deadline, and the action: name what's wrong and give a button to resolve it.
92
+
93
+ ```tsx
94
+ <Alert warning icon="⚠" title="Action required" description="Your trial expires in 3 days. Upgrade to keep your projects.">
95
+ <View style={{ marginTop: 12, flexDirection: "row", gap: 8 }}>
96
+ <Button primary small>Upgrade plan</Button>
97
+ </View>
98
+ </Alert>
99
+ ```
100
+
101
+ **Don't** — A warning with no specifics or next step leaves the user guessing what to fix and by when.
102
+
103
+ ```tsx
104
+ <Alert warning icon="⚠" title="Action required" description="Something needs your attention." />
105
+ ```
106
+
107
+ ### destructive
108
+
109
+ **Do** — Match the variant to the severity: reserve destructive for genuine failures, success for confirmations.
110
+
111
+ ```tsx
112
+ <Alert error icon="✕" title="Something went wrong" description="Could not save your changes. Please try again." />
113
+ ```
114
+
115
+ **Don't** — Using the destructive variant for non-errors cries wolf; users learn to tune out red and miss real failures.
116
+
117
+ ```tsx
118
+ <Alert error icon="✕" title="Saved" description="Your changes have been saved successfully." />
119
+ ```
@@ -0,0 +1,88 @@
1
+ import { type ViewStyle, type TextStyle } from "react-native";
2
+ import { type ColorTokens, palette } from "../../style/index.js";
3
+
4
+ // Co-located Alert styles. The toned alerts (info / success / warning / error)
5
+ // draw from the Tailwind palette: a soft 50/200 surface with a 600/700/800 type
6
+ // ramp in light mode, flipping to a 950/800 surface with a 200/300/400 ramp in
7
+ // dark mode (the old dark: variant, resolved by branching on `dark`). The neutral
8
+ // default rides the semantic card / border / foreground tokens so it follows the
9
+ // theme (including glass) on its own.
10
+
11
+ export type Tone = "info" | "success" | "warning" | "error" | "neutral";
12
+
13
+ // flex-row items-start gap-3 rounded-lg border px-4 py-3
14
+ export const alertBase: ViewStyle = {
15
+ flexDirection: "row",
16
+ alignItems: "flex-start",
17
+ gap: 12,
18
+ borderRadius: 8,
19
+ borderWidth: 1,
20
+ paddingHorizontal: 16,
21
+ paddingVertical: 12,
22
+ };
23
+
24
+ // flex-1 gap-1 — the content column beside the icon.
25
+ export const content: ViewStyle = { flexGrow: 1, flexShrink: 1, flexBasis: "0%", gap: 4 };
26
+
27
+ // text-base leading-5 — the leading glyph.
28
+ export const iconType: TextStyle = { fontSize: 16, lineHeight: 20 };
29
+
30
+ // text-sm font-semibold — the title.
31
+ export const titleType: TextStyle = { fontSize: 14, lineHeight: 20, fontWeight: "600" };
32
+
33
+ // text-sm — the description / body.
34
+ export const bodyType: TextStyle = { fontSize: 14, lineHeight: 20 };
35
+
36
+ // -mr-1 -mt-0.5 h-6 w-6 items-center justify-center rounded-md
37
+ // (active:opacity-70 is applied by the component's Pressable).
38
+ export const dismissButton: ViewStyle = {
39
+ marginRight: -4,
40
+ marginTop: -2,
41
+ height: 24,
42
+ width: 24,
43
+ alignItems: "center",
44
+ justifyContent: "center",
45
+ borderRadius: 6,
46
+ };
47
+
48
+ // text-base leading-none — the dismiss glyph.
49
+ export const dismissType: TextStyle = { fontSize: 16, lineHeight: 16 };
50
+
51
+ // The palette hue per toned alert (neutral rides the semantic tokens).
52
+ const TONE_HUE: Record<Exclude<Tone, "neutral">, string> = {
53
+ info: "blue",
54
+ success: "green",
55
+ warning: "amber",
56
+ error: "red",
57
+ };
58
+
59
+ // Container border + fill. Light: 200 border / 50 surface; dark: 800 / 950.
60
+ // Neutral: semantic card surface with the border token.
61
+ export function container(tokens: ColorTokens, dark: boolean, tone: Tone): ViewStyle {
62
+ if (tone === "neutral") return { borderColor: tokens.border, backgroundColor: tokens.card };
63
+ const hue = TONE_HUE[tone];
64
+ return dark
65
+ ? { borderColor: palette[`${hue}-800`], backgroundColor: palette[`${hue}-950`] }
66
+ : { borderColor: palette[`${hue}-200`], backgroundColor: palette[`${hue}-50`] };
67
+ }
68
+
69
+ // Icon color. Light: 600; dark: 400. Neutral: muted-foreground.
70
+ export function iconColor(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
71
+ if (tone === "neutral") return { color: tokens["muted-foreground"] };
72
+ const hue = TONE_HUE[tone];
73
+ return { color: dark ? palette[`${hue}-400`] : palette[`${hue}-600`] };
74
+ }
75
+
76
+ // Title color. Light: 800; dark: 200. Neutral: foreground.
77
+ export function titleColor(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
78
+ if (tone === "neutral") return { color: tokens.foreground };
79
+ const hue = TONE_HUE[tone];
80
+ return { color: dark ? palette[`${hue}-200`] : palette[`${hue}-800`] };
81
+ }
82
+
83
+ // Body color. Light: 700; dark: 300. Neutral: muted-foreground.
84
+ export function bodyColor(tokens: ColorTokens, dark: boolean, tone: Tone): TextStyle {
85
+ if (tone === "neutral") return { color: tokens["muted-foreground"] };
86
+ const hue = TONE_HUE[tone];
87
+ return { color: dark ? palette[`${hue}-300`] : palette[`${hue}-700`] };
88
+ }
@@ -0,0 +1,74 @@
1
+ import { type ReactNode } from "react";
2
+ import { View, Pressable, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
3
+ import * as s from "./alert.styles.js";
4
+ import { type Tone } from "./alert.styles.js";
5
+
6
+ // A bordered banner that surfaces an inline notification: a leading icon glyph,
7
+ // a bold title, and a description. Configured by a tone axis (info / success /
8
+ // warning / error, plus a neutral default), and an optional `icon` glyph.
9
+ //
10
+ // Boolean-prop API: one boolean per tone, first-match precedence within the
11
+ // axis (mirrors Button's intentOf / Badge's statusOf). Each tone is theme-aware:
12
+ // a soft 50/200 surface with a 600/700/800 type ramp in light mode, and a
13
+ // 950/800 surface with a 200/300/400 ramp in dark mode (branching on the active
14
+ // scheme). The neutral default uses the semantic card / border / foreground tokens.
15
+
16
+ export interface AlertProps {
17
+ // Content.
18
+ title?: string;
19
+ description?: string;
20
+ // A leading glyph (a single Text character; the foundation has no icon set).
21
+ icon?: ReactNode;
22
+ // Tone (pick one; omit for the neutral default).
23
+ info?: boolean;
24
+ success?: boolean;
25
+ warning?: boolean;
26
+ error?: boolean;
27
+ /** Shows a trailing dismiss control. */
28
+ dismissible?: boolean;
29
+ /** Fired when the dismiss control is pressed. */
30
+ onDismiss?: () => void;
31
+ children?: ReactNode;
32
+ /** Escape hatch for layout/positioning composition (width, margins). */
33
+ style?: StyleProp<ViewStyle>;
34
+ }
35
+
36
+ // Tone precedence when more than one is passed: first match wins.
37
+ function toneOf(p: AlertProps): Tone {
38
+ if (p.error) return "error";
39
+ if (p.warning) return "warning";
40
+ if (p.success) return "success";
41
+ if (p.info) return "info";
42
+ return "neutral";
43
+ }
44
+
45
+ export function Alert(props: AlertProps) {
46
+ const { title, description, icon, children, dismissible, onDismiss, style } = props;
47
+ const { tokens, dark } = useTheme();
48
+ const tone = toneOf(props);
49
+
50
+ return (
51
+ <View style={[s.alertBase, s.container(tokens, dark, tone), style]}>
52
+ {icon != null ? <Text style={[s.iconType, s.iconColor(tokens, dark, tone)]}>{icon}</Text> : null}
53
+ <View style={s.content}>
54
+ {title != null && title !== "" ? (
55
+ <Text style={[s.titleType, s.titleColor(tokens, dark, tone)]}>{title}</Text>
56
+ ) : null}
57
+ {description != null && description !== "" ? (
58
+ <Text style={[s.bodyType, s.bodyColor(tokens, dark, tone)]}>{description}</Text>
59
+ ) : null}
60
+ {children}
61
+ </View>
62
+ {dismissible ? (
63
+ <Pressable
64
+ onPress={onDismiss}
65
+ accessibilityRole="button"
66
+ accessibilityLabel="Dismiss"
67
+ style={({ pressed }) => [s.dismissButton, pressed ? { opacity: 0.7 } : null]}
68
+ >
69
+ <Text style={[s.dismissType, s.iconColor(tokens, dark, tone)]}>×</Text>
70
+ </Pressable>
71
+ ) : null}
72
+ </View>
73
+ );
74
+ }
@@ -0,0 +1,6 @@
1
+ import { createAlertDialog } from "./alert-dialog.shared.js";
2
+ import { androidSkin } from "./alert-dialog.styles.js";
3
+
4
+ // Material 3 AlertDialog. Metro resolves this file on Android; the docs import it for preview.
5
+ export const AlertDialog = createAlertDialog(androidSkin);
6
+ export type { AlertDialogProps } from "./alert-dialog.shared.js";
@@ -0,0 +1,6 @@
1
+ import { createAlertDialog } from "./alert-dialog.shared.js";
2
+ import { iosSkin } from "./alert-dialog.styles.js";
3
+
4
+ // iOS (iOS 27 / Liquid Glass alert) AlertDialog. Metro resolves this file on iOS; the docs import it for preview.
5
+ export const AlertDialog = createAlertDialog(iosSkin);
6
+ export type { AlertDialogProps } from "./alert-dialog.shared.js";
@@ -0,0 +1,177 @@
1
+ # Alert Dialog
2
+
3
+ Catalyst-style confirmation dialog: a centered panel over a dimmed, blurred backdrop, with a title, description, optional body, and action buttons. Reserve it for decisions that must block the rest of the app.
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ <AlertDialog
9
+ title="Delete this identity?"
10
+ description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
11
+ confirmLabel="Delete"
12
+ destructive
13
+ trigger="Delete identity…"
14
+ />
15
+ ```
16
+
17
+ ## Variants
18
+
19
+ ### Size - xs
20
+
21
+ ```tsx
22
+ <AlertDialog
23
+ title="Delete this identity?"
24
+ description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
25
+ confirmLabel="Delete"
26
+ destructive
27
+ narrow
28
+ trigger="Delete identity…"
29
+ />
30
+ ```
31
+
32
+ ### Size - sm
33
+
34
+ ```tsx
35
+ <AlertDialog
36
+ title="Delete this identity?"
37
+ description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
38
+ confirmLabel="Delete"
39
+ destructive
40
+ small
41
+ trigger="Delete identity…"
42
+ />
43
+ ```
44
+
45
+ ### Size - lg
46
+
47
+ ```tsx
48
+ <AlertDialog
49
+ title="Delete this identity?"
50
+ description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
51
+ confirmLabel="Delete"
52
+ destructive
53
+ large
54
+ trigger="Delete identity…"
55
+ />
56
+ ```
57
+
58
+ ### Body field
59
+
60
+ ```tsx
61
+ <AlertDialog
62
+ title="Delete this identity?"
63
+ description="This permanently removes the identity and revokes any active sessions. This action cannot be undone."
64
+ confirmLabel="Delete"
65
+ destructive
66
+ withInput
67
+ trigger="Delete identity…"
68
+ />
69
+ ```
70
+
71
+ ## Do & Don't
72
+
73
+ ### Reserve the dialog for blocking decisions
74
+
75
+ **Do** — Use an inline banner or toast for passive feedback; reserve the dialog for decisions that must be confirmed.
76
+
77
+ ```tsx
78
+ <Alert success title="Saved" description="Your changes have been saved." />
79
+ ```
80
+
81
+ **Don't** — A blocking alert dialog for passive confirmation interrupts the user for no reason.
82
+
83
+ ```tsx
84
+ <AlertDialog open small title="Saved" description="Your changes have been saved." confirmLabel="OK" />
85
+ ```
86
+
87
+ ### Name the action on the confirm button
88
+
89
+ **Do** — Label the confirm button with the verb it performs (Delete, Archive, Sign out).
90
+
91
+ ```tsx
92
+ <AlertDialog open small destructive title="Delete this identity?" cancelLabel="Cancel" confirmLabel="Delete" />
93
+ ```
94
+
95
+ **Don't** — Generic Yes / No forces the user to re-read the title to know what they are confirming.
96
+
97
+ ```tsx
98
+ <AlertDialog open small destructive title="Delete this identity?" cancelLabel="No" confirmLabel="Yes" />
99
+ ```
100
+
101
+ ### xs
102
+
103
+ **Do** — Reserve xs for a terse one-line question with short button labels and no body content.
104
+
105
+ ```tsx
106
+ <AlertDialog open narrow destructive title="Remove device?" cancelLabel="Cancel" confirmLabel="Remove" />
107
+ ```
108
+
109
+ **Don't** — A long title, multi-line description, and wordy buttons get cramped in the xs width and wrap awkwardly.
110
+
111
+ ```tsx
112
+ <AlertDialog open narrow destructive title="Remove this trusted device?" description="It will need to re-authenticate, and any pending background syncs from it will be cancelled the next time it connects." cancelLabel="Cancel" confirmLabel="Remove device" />
113
+ ```
114
+
115
+ ### sm
116
+
117
+ **Do** — Use sm for a single short confirmation with a one-line description; move real forms to a full dialog.
118
+
119
+ ```tsx
120
+ <AlertDialog open small title="Transfer ownership?" description="You will lose admin access to this workspace." cancelLabel="Cancel" confirmLabel="Transfer" />
121
+ ```
122
+
123
+ **Don't** — Packing a multi-field form into sm makes it feel like a form crammed into a confirmation popup.
124
+
125
+ ```tsx
126
+ <View style={{ alignItems: "center", justifyContent: "center", borderRadius: 8, backgroundColor: alpha("#000000", 0.5), padding: 32, minHeight: 200 }}>
127
+ <View style={{ width: "100%", maxWidth: 384, borderRadius: 8, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.popover, padding: 24, ...shadow("xl") }}>
128
+ <Text style={{ fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens["popover-foreground"] }}>Transfer ownership</Text>
129
+ <View style={{ marginTop: 16, gap: 16 }}>
130
+ <View>
131
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground, marginBottom: 6 }}>New owner email</Text>
132
+ <Input placeholder="owner@example.com" />
133
+ </View>
134
+ <View>
135
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground, marginBottom: 6 }}>Reason</Text>
136
+ <Input placeholder="Optional note" />
137
+ </View>
138
+ <View>
139
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground, marginBottom: 6 }}>Type TRANSFER to confirm</Text>
140
+ <Input placeholder="TRANSFER" />
141
+ </View>
142
+ </View>
143
+ <View style={{ flexDirection: "row", justifyContent: "flex-end", gap: 8, marginTop: 24 }}>
144
+ <Button outline small>Cancel</Button>
145
+ <Button primary small>Transfer</Button>
146
+ </View>
147
+ </View>
148
+ </View>
149
+ ```
150
+
151
+ ### md
152
+
153
+ **Do** — md is the default home for a typical destructive confirm with a sentence or two of description.
154
+
155
+ ```tsx
156
+ <AlertDialog open destructive title="Delete this identity?" description="This permanently removes the identity and revokes any active sessions. This action cannot be undone." cancelLabel="Cancel" confirmLabel="Delete" />
157
+ ```
158
+
159
+ **Don't** — Squeezing a description-carrying confirm into a smaller width crowds the copy against the edges.
160
+
161
+ ```tsx
162
+ <AlertDialog open narrow destructive title="Delete this identity?" description="This permanently removes the identity and revokes any active sessions. This action cannot be undone." cancelLabel="Cancel" confirmLabel="Delete" />
163
+ ```
164
+
165
+ ### lg
166
+
167
+ **Do** — Reserve lg for dialogs that earn the width: a body field or a longer explanation to read.
168
+
169
+ ```tsx
170
+ <AlertDialog open large destructive withInput title="Delete this identity?" description="This permanently removes the identity and revokes any active sessions. This action cannot be undone." cancelLabel="Cancel" confirmLabel="Delete" />
171
+ ```
172
+
173
+ **Don't** — A bare yes/no in lg leaves a wide, empty panel that reads as heavier than the trivial decision it asks for.
174
+
175
+ ```tsx
176
+ <AlertDialog open large title="Sign out?" cancelLabel="Cancel" confirmLabel="Sign out" />
177
+ ```