@olympusoss/canvas 3.2.1 → 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 (302) hide show
  1. package/README.md +75 -65
  2. package/package.json +11 -5
  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/styles/canvas.css +127 -74
  234. package/tsconfig.json +4 -2
  235. package/src/cn.ts +0 -3
  236. package/styles/atoms/avatar.css +0 -22
  237. package/styles/atoms/badge.css +0 -83
  238. package/styles/atoms/breadcrumb.css +0 -35
  239. package/styles/atoms/button-group.css +0 -23
  240. package/styles/atoms/button.css +0 -107
  241. package/styles/atoms/checkbox.css +0 -55
  242. package/styles/atoms/combobox.css +0 -76
  243. package/styles/atoms/dropdown.css +0 -54
  244. package/styles/atoms/icon.css +0 -8
  245. package/styles/atoms/input-group.css +0 -45
  246. package/styles/atoms/input.css +0 -56
  247. package/styles/atoms/kbd.css +0 -15
  248. package/styles/atoms/pagination.css +0 -48
  249. package/styles/atoms/popover.css +0 -14
  250. package/styles/atoms/radio.css +0 -28
  251. package/styles/atoms/select.css +0 -57
  252. package/styles/atoms/separator.css +0 -32
  253. package/styles/atoms/skeleton.css +0 -32
  254. package/styles/atoms/spinner.css +0 -26
  255. package/styles/atoms/switch.css +0 -45
  256. package/styles/atoms/textarea.css +0 -31
  257. package/styles/atoms/tooltip.css +0 -53
  258. package/styles/atoms/typography.css +0 -105
  259. package/styles/base.css +0 -17
  260. package/styles/molecules/alert.css +0 -66
  261. package/styles/molecules/card.css +0 -58
  262. package/styles/molecules/code-block.css +0 -18
  263. package/styles/molecules/empty-state.css +0 -17
  264. package/styles/molecules/field.css +0 -27
  265. package/styles/molecules/form.css +0 -27
  266. package/styles/molecules/page-header.css +0 -52
  267. package/styles/molecules/section-card.css +0 -49
  268. package/styles/molecules/stat-card.css +0 -71
  269. package/styles/molecules/toast.css +0 -95
  270. package/styles/organisms/app-shell.css +0 -46
  271. package/styles/organisms/calendar.css +0 -73
  272. package/styles/organisms/command.css +0 -95
  273. package/styles/organisms/data-table.css +0 -142
  274. package/styles/organisms/dialog.css +0 -72
  275. package/styles/organisms/filter-panel.css +0 -58
  276. package/styles/organisms/row-menu.css +0 -69
  277. package/styles/organisms/sheet.css +0 -70
  278. package/styles/organisms/sidebar.css +0 -146
  279. package/styles/organisms/stepper.css +0 -63
  280. package/styles/organisms/tabs.css +0 -40
  281. package/styles/organisms/topbar.css +0 -24
  282. package/styles/patterns/backdrops.css +0 -35
  283. package/styles/patterns/density.css +0 -66
  284. package/styles/patterns/focus.css +0 -22
  285. package/styles/patterns/glass.css +0 -85
  286. package/styles/patterns/high-contrast.css +0 -70
  287. package/styles/patterns/reduced-motion.css +0 -12
  288. package/styles/patterns/scrollbar.css +0 -10
  289. package/styles/reset.css +0 -89
  290. package/styles/tokens/colors.css +0 -108
  291. package/styles/tokens/motion.css +0 -33
  292. package/styles/tokens/radius.css +0 -10
  293. package/styles/tokens/shadows.css +0 -35
  294. package/styles/tokens/spacing.css +0 -19
  295. package/styles/tokens/typography.css +0 -6
  296. package/styles/tokens/z-index.css +0 -12
  297. package/styles/utilities/display.css +0 -66
  298. package/styles/utilities/flexbox.css +0 -240
  299. package/styles/utilities/gap.css +0 -288
  300. package/styles/utilities/grid.css +0 -138
  301. package/styles/utilities/position.css +0 -78
  302. package/styles/utilities/sizing.css +0 -138
package/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # @olympusoss/canvas
2
2
 
3
- CSS-first design system for the Olympus platform.
3
+ Universal React Native UI kit. Canvas runs natively on iOS and Android, and on
4
+ the web through React Native Web, from a single component API. Components are
5
+ styled with semantic boolean props and authored desktop-first, so they adapt
6
+ cleanly from large desktop down to phone.
4
7
 
5
8
  ## Install
6
9
 
@@ -8,90 +11,97 @@ CSS-first design system for the Olympus platform.
8
11
  npm install @olympusoss/canvas
9
12
  ```
10
13
 
11
- ## Quick Start
12
-
13
- Import all styles:
14
-
15
- ```css
16
- @import "@olympusoss/canvas/styles/canvas.css";
17
- ```
14
+ Canvas relies on three peer dependencies that you install alongside it:
18
15
 
19
- Or import selectively:
20
-
21
- ```css
22
- @layer canvas.reset, canvas.tokens, canvas.base, canvas.components, canvas.patterns;
23
-
24
- @import "@olympusoss/canvas/styles/reset.css";
25
- @import "@olympusoss/canvas/styles/tokens/colors.css";
26
- @import "@olympusoss/canvas/styles/tokens/typography.css";
27
- @import "@olympusoss/canvas/styles/base.css";
28
- @import "@olympusoss/canvas/styles/atoms/button.css";
16
+ ```bash
17
+ npm install react react-native react-native-svg
29
18
  ```
30
19
 
31
- Component CSS is organized by atomic-design level: `styles/atoms/`,
32
- `styles/molecules/`, `styles/organisms/`. (Importing the all-in-one
33
- `styles/canvas.css` pulls in everything and is unaffected by this layout.)
20
+ For web rendering, add `react-native-web` to your app and alias `react-native`
21
+ to `react-native-web` in your bundler, the same way any React Native Web project
22
+ does.
34
23
 
35
- Use component classes in your HTML:
24
+ ## Quick Start
36
25
 
37
- ```html
38
- <button class="btn btn-default">Save</button>
39
- <button class="btn btn-outline btn-sm">Cancel</button>
40
- <input class="input" placeholder="Search..." />
26
+ Wrap your app in the `ThemeProvider`, then compose components imported
27
+ from `@olympusoss/canvas`. The provider supplies the active color scheme and
28
+ token map; omit `scheme` to follow the OS appearance, or force it with
29
+ `scheme="light"` / `scheme="dark"`.
30
+
31
+ ```jsx
32
+ import { ThemeProvider, Card, CardHeader, CardTitle, CardContent, Button } from "@olympusoss/canvas";
33
+
34
+ export default function App() {
35
+ return (
36
+ <ThemeProvider>
37
+ <Card padded>
38
+ <CardHeader>
39
+ <CardTitle>Welcome to Canvas</CardTitle>
40
+ </CardHeader>
41
+ <CardContent>
42
+ <Button primary large onPress={() => console.log("saved")}>
43
+ Save
44
+ </Button>
45
+ </CardContent>
46
+ </Card>
47
+ </ThemeProvider>
48
+ );
49
+ }
41
50
  ```
42
51
 
43
- ## Theming
44
-
45
- Toggle themes with HTML attributes:
52
+ The same component tree renders natively on iOS and Android and, through React
53
+ Native Web, in the browser. There is no separate web component set to learn.
46
54
 
47
- ```html
48
- <!-- Dark mode -->
49
- <html class="dark">
55
+ ## Semantic boolean props
50
56
 
51
- <!-- Glass surface -->
52
- <html data-surface="glass">
57
+ Styling is done with flat boolean props. Each style choice is its own prop,
58
+ named for the meaning it carries, and passing the prop turns it on. The prop
59
+ name is the value, so the call site reads like natural language ("a primary,
60
+ large button").
53
61
 
54
- <!-- Compact density -->
55
- <html data-density="compact">
62
+ ```jsx
63
+ <Button primary large>Save</Button>
64
+ <Button destructive>Delete</Button>
65
+ <Button ghost small>Cancel</Button>
66
+ <Card glass>...</Card>
56
67
  ```
57
68
 
58
- Or use the JS utilities:
69
+ Props are grouped into orthogonal axes (intent, size, surface, density, and
70
+ stacking state/layout flags). Props on different axes combine freely; props
71
+ within one axis are mutually exclusive, so you pass at most one and the
72
+ component resolves any conflict by a fixed precedence.
59
73
 
60
- ```js
61
- import { setTheme, toggleTheme, setSurface, setDensity } from "@olympusoss/canvas";
62
-
63
- toggleTheme(); // switches between light/dark
64
- setSurface("glass"); // enables glass surface
65
- setDensity("compact"); // switches to compact density
74
+ ```jsx
75
+ // Four props from four axes, all applied together.
76
+ <Button primary large loading block>Save</Button>
66
77
  ```
67
78
 
68
- ## What's Included
69
-
70
- - **62 CSS files**: tokens, components, and patterns
71
- - **4 JS utilities**: theme switching, token access, class composition
72
- - **5 cascade layers**: reset, tokens, base, components, patterns
73
- - **45 component styles**: buttons, cards, tables, forms, dialogs, and more
74
- - **Light/dark mode**, glass surface, compact/comfy density
75
- - **WCAG AA** color contrast compliance
76
- - **prefers-reduced-motion** and **prefers-contrast** support
79
+ String-valued enum props such as `variant="primary"`, `size="lg"`, or
80
+ `tone="destructive"` are not part of the API and are not accepted. The boolean
81
+ form is the only styling surface.
77
82
 
78
- ## Documentation
83
+ ## Theming
79
84
 
80
- - [Token Reference](docs/tokens.md)
81
- - [Component Catalog](docs/components.md)
82
- - [Theming Guide](docs/theming.md)
83
- - [Migration Guide (v2 to v3)](docs/migration.md)
84
- - [Consumer Integration](docs/integration.md)
85
- - [Browser Support](docs/browser-support.md)
85
+ `ThemeProvider` reads the OS color scheme by default and exposes the resolved
86
+ tokens to every Canvas component through `useTheme`. Force a scheme when you
87
+ need to:
86
88
 
87
- ## Framework Packages
89
+ ```jsx
90
+ <ThemeProvider scheme="dark">
91
+ <App />
92
+ </ThemeProvider>
93
+ ```
88
94
 
89
- Canvas provides the CSS foundation. Framework-specific components live in dedicated packages:
95
+ ## What's Included
90
96
 
91
- - `@olympusoss/canvas-react`: React components for web
92
- - `@olympusoss/canvas-react-native`: React Native components
93
- - `@olympusoss/canvas-vue`: Vue components
94
- - `@olympusoss/canvas-flux`: Flux components
97
+ - A full component kit: buttons, inputs, cards, tables, tabs, dialogs,
98
+ dropdowns, calendars, charts, sidebars, and more, all exported from
99
+ `@olympusoss/canvas`.
100
+ - The style foundation: design tokens, the theme runtime (`ThemeProvider`,
101
+ `useTheme`), the `useResponsive` / `shadow` / `alpha` helpers, and the raw React
102
+ Native `View` / `Text` / `Pressable` / `Image` / `TextInput` / `ScrollView` primitives.
103
+ - Light and dark color schemes resolved through theme tokens.
104
+ - Desktop-first responsiveness built into every component.
95
105
 
96
106
  ## License
97
107
 
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@olympusoss/canvas",
3
- "version": "3.2.1",
3
+ "version": "5.0.0",
4
4
  "type": "module",
5
- "description": "CSS-first design system for the Olympus platform",
5
+ "description": "Universal React Native UI kit styled with Tailwind",
6
6
  "main": "./src/index.ts",
7
7
  "types": "./src/index.ts",
8
8
  "exports": {
@@ -23,13 +23,10 @@
23
23
  ],
24
24
  "scripts": {
25
25
  "typecheck": "tsc --noEmit",
26
- "generate-utilities": "bun scripts/generate-utilities.ts",
27
26
  "validate-tokens": "bun scripts/validate-tokens.ts",
28
27
  "check-size": "bun scripts/check-size.ts",
29
28
  "check-duplicates": "bun scripts/check-duplicates.ts",
30
- "check-contrast": "bun scripts/check-contrast.ts",
31
29
  "screenshots": "bun scripts/capture-screenshots.ts",
32
- "verify-docs": "bun scripts/verify-docs.ts",
33
30
  "changeset": "changeset",
34
31
  "version-packages": "changeset version",
35
32
  "release": "changeset publish",
@@ -40,9 +37,18 @@
40
37
  "registry": "https://registry.npmjs.org",
41
38
  "access": "public"
42
39
  },
40
+ "peerDependencies": {
41
+ "react": ">=18",
42
+ "react-native": ">=0.74",
43
+ "react-native-svg": ">=13"
44
+ },
43
45
  "devDependencies": {
44
46
  "@changesets/cli": "^2.31.0",
45
47
  "@playwright/test": "^1.60.0",
48
+ "@types/react": "^19.2.16",
49
+ "react": "^19.2.7",
50
+ "react-native": "^0.85.3",
51
+ "react-native-svg": "^15",
46
52
  "typescript": "^5.8.3"
47
53
  }
48
54
  }
@@ -0,0 +1,185 @@
1
+ # Avatars
2
+
3
+ A photo when the account has one, falling back to two initials on a brand gradient (seeded admin accounts). Sizes scale font proportionally (40% of diameter).
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ <Avatar name="AO" />
9
+ ```
10
+
11
+ ## Variants
12
+
13
+ ### Variant - stacked
14
+
15
+ ```tsx
16
+ <View style={{ flexDirection: "row", alignItems: "center" }}>
17
+ <Avatar ring src="/rachel-chen.jpg" name="RC" />
18
+ <Avatar ring src="/liang-bao.jpg" name="LB" style={{ marginLeft: -12 }} />
19
+ <Avatar ring src="/marcus-allen.jpg" name="LB" style={{ marginLeft: -12 }} />
20
+ <Avatar ring src="/kira-tanaka.jpg" name="KT" style={{ marginLeft: -12 }} />
21
+ </View>
22
+ ```
23
+
24
+ ### Variant - topbar
25
+
26
+ ```tsx
27
+ <Dropdown items={[
28
+ { label: "Your profile", icon: "👤" },
29
+ { label: "Settings", icon: "⚙" },
30
+ { label: "Sign out", icon: "↩", separatorBefore: true }
31
+ ]}>
32
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8, borderRadius: 9999, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card, paddingVertical: 4, paddingLeft: 4, paddingRight: 10 }}>
33
+ <Avatar small src="/marcus-allen.jpg" name="MA" />
34
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>admin@example.com</Text>
35
+ <Icon chevronDown muted size={12} />
36
+ </View>
37
+ </Dropdown>
38
+ ```
39
+
40
+ ### Variant - identity
41
+
42
+ ```tsx
43
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 16 }}>
44
+ <Avatar src="/rachel-chen.jpg" name="RC" />
45
+ <View>
46
+ <Text style={{ fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens.foreground }}>Rachel Chen</Text>
47
+ <Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>rachel.chen@example.com</Text>
48
+ </View>
49
+ </View>
50
+ ```
51
+
52
+ ### Variant - menu
53
+
54
+ ```tsx
55
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 12, borderBottomWidth: 1, borderColor: tokens.border, paddingBottom: 12 }}>
56
+ <Avatar src="/ada-lovelace.jpg" name="AL" />
57
+ <View>
58
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "600", color: tokens.foreground }}>Ada Lovelace</Text>
59
+ <Text style={{ fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>admin@example.com</Text>
60
+ </View>
61
+ </View>
62
+ ```
63
+
64
+ ### Ring outline
65
+
66
+ ```tsx
67
+ <Avatar ring name="AO" />
68
+ ```
69
+
70
+ ## Do & Don't
71
+
72
+ ### Single
73
+
74
+ **Do** — One or two initials, sized about 40% of the diameter.
75
+
76
+ ```tsx
77
+ <Avatar name="AO" />
78
+ ```
79
+
80
+ **Don't** — Cramming in a full set of initials shrinks the type and crowds the circle.
81
+
82
+ ```tsx
83
+ <View style={{ flexShrink: 0, alignItems: "center", justifyContent: "center", overflow: "hidden", backgroundColor: tokens.muted, width: 40, height: 40, borderRadius: 9999 }}>
84
+ <Text style={{ fontWeight: "500", color: tokens["muted-foreground"], fontSize: 12 }}>ABCD</Text>
85
+ </View>
86
+ ```
87
+
88
+ ### Stacked
89
+
90
+ **Do** — Cap the stack and summarize the rest with a +N count.
91
+
92
+ ```tsx
93
+ <View style={{ flexDirection: "row", alignItems: "center" }}>
94
+ <Avatar small ring name="AO" />
95
+ <Avatar small ring name="RC" style={{ marginLeft: -10 }} />
96
+ <Avatar small ring name="LB" style={{ marginLeft: -10 }} />
97
+ <Avatar small ring name="KT" style={{ marginLeft: -10 }} />
98
+ <Text style={{ marginLeft: 6, fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>+12</Text>
99
+ </View>
100
+ ```
101
+
102
+ **Don't** — An unbounded stack runs off the row and stops being scannable.
103
+
104
+ ```tsx
105
+ <View style={{ flexDirection: "row", alignItems: "center" }}>
106
+ <Avatar small ring name="AO" />
107
+ <Avatar small ring name="RC" style={{ marginLeft: -10 }} />
108
+ <Avatar small ring name="LB" style={{ marginLeft: -10 }} />
109
+ <Avatar small ring name="KT" style={{ marginLeft: -10 }} />
110
+ <Avatar small ring name="JD" style={{ marginLeft: -10 }} />
111
+ <Avatar small ring name="MA" style={{ marginLeft: -10 }} />
112
+ <Avatar small ring name="AL" style={{ marginLeft: -10 }} />
113
+ <Avatar small ring name="SK" style={{ marginLeft: -10 }} />
114
+ </View>
115
+ ```
116
+
117
+ ### Topbar account menu
118
+
119
+ **Do** — Pair it with the account name and a chevron so it reads as a trigger.
120
+
121
+ ```tsx
122
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8, borderRadius: 9999, borderWidth: 1, borderColor: tokens.border, backgroundColor: tokens.card, paddingVertical: 4, paddingLeft: 4, paddingRight: 10 }}>
123
+ <Avatar small src="/marcus-allen.jpg" name="MA" />
124
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "500", color: tokens.foreground }}>admin@example.com</Text>
125
+ <Icon chevronDown muted size={12} />
126
+ </View>
127
+ ```
128
+
129
+ **Don't** — A lone avatar gives no hint that it opens the account menu.
130
+
131
+ ```tsx
132
+ <Avatar small src="/marcus-allen.jpg" name="MA" />
133
+ ```
134
+
135
+ ### Identity
136
+
137
+ **Do** — Name primary; email muted and secondary.
138
+
139
+ ```tsx
140
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 16 }}>
141
+ <Avatar src="/rachel-chen.jpg" name="RC" />
142
+ <View>
143
+ <Text style={{ fontSize: 16, lineHeight: 24, fontWeight: "600", color: tokens.foreground }}>Rachel Chen</Text>
144
+ <Text style={{ fontSize: 14, lineHeight: 20, color: tokens["muted-foreground"] }}>rachel.chen@example.com</Text>
145
+ </View>
146
+ </View>
147
+ ```
148
+
149
+ **Don't** — Equal weight on the name and email flattens the hierarchy.
150
+
151
+ ```tsx
152
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 16 }}>
153
+ <Avatar src="/rachel-chen.jpg" name="RC" />
154
+ <View>
155
+ <Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>Rachel Chen</Text>
156
+ <Text style={{ fontSize: 14, lineHeight: 20, color: tokens.foreground }}>rachel.chen@example.com</Text>
157
+ </View>
158
+ </View>
159
+ ```
160
+
161
+ ### Menu header
162
+
163
+ **Do** — Keep one consistent circular avatar shape across contexts.
164
+
165
+ ```tsx
166
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
167
+ <Avatar src="/ada-lovelace.jpg" name="AL" />
168
+ <View>
169
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "600", color: tokens.foreground }}>Ada Lovelace</Text>
170
+ <Text style={{ fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>admin@example.com</Text>
171
+ </View>
172
+ </View>
173
+ ```
174
+
175
+ **Don't** — Squaring the avatar here clashes with the circular avatars everywhere else.
176
+
177
+ ```tsx
178
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 12 }}>
179
+ <Avatar rounded src="/ada-lovelace.jpg" name="AL" />
180
+ <View>
181
+ <Text style={{ fontSize: 14, lineHeight: 20, fontWeight: "600", color: tokens.foreground }}>Ada Lovelace</Text>
182
+ <Text style={{ fontSize: 12, lineHeight: 16, color: tokens["muted-foreground"] }}>admin@example.com</Text>
183
+ </View>
184
+ </View>
185
+ ```
@@ -0,0 +1,48 @@
1
+ import { type ViewStyle, type TextStyle, type ImageStyle } from "react-native";
2
+ import { type ColorTokens } from "../../style/index.js";
3
+
4
+ // Co-located Avatar styles. Diameter per size, corner radius per shape, and the
5
+ // muted fallback surface, all built from the active tokens.
6
+
7
+ export type Size = "small" | "default" | "large";
8
+ export type Shape = "circle" | "rounded";
9
+
10
+ // Diameter per size: small is the inline topbar/stack size, default the 40px row
11
+ // avatar, large the identity-header size.
12
+ const BOX: Record<Size, number> = { small: 28, default: 40, large: 48 };
13
+
14
+ // Circle by default; the rounded square uses the card/menu radius.
15
+ const RADIUS: Record<Shape, number> = { circle: 9999, rounded: 6 };
16
+
17
+ // Initials type per size, ~40% of the diameter.
18
+ const LABEL_SIZE: Record<Size, { fontSize: number; lineHeight: number }> = {
19
+ small: { fontSize: 12, lineHeight: 16 },
20
+ default: { fontSize: 16, lineHeight: 24 },
21
+ large: { fontSize: 18, lineHeight: 28 },
22
+ };
23
+
24
+ export function container(tokens: ColorTokens, size: Size, shape: Shape, ring: boolean): ViewStyle {
25
+ return {
26
+ flexShrink: 0,
27
+ alignItems: "center",
28
+ justifyContent: "center",
29
+ overflow: "hidden",
30
+ backgroundColor: tokens.muted,
31
+ width: BOX[size],
32
+ height: BOX[size],
33
+ borderRadius: RADIUS[shape],
34
+ // No ring-* equivalent in RN; a 2px background-colored border is the stand-in
35
+ // for ring-2 ring-background, the separator outline used when avatars overlap.
36
+ ...(ring ? { borderWidth: 2, borderColor: tokens.background } : null),
37
+ };
38
+ }
39
+
40
+ // The photo fills the container exactly; the parent's overflow-hidden clips it
41
+ // to the circle (RN clips children to a parent's borderRadius).
42
+ export function image(shape: Shape): ImageStyle {
43
+ return { width: "100%", height: "100%", borderRadius: RADIUS[shape] };
44
+ }
45
+
46
+ export function label(tokens: ColorTokens, size: Size): TextStyle {
47
+ return { fontWeight: "500", color: tokens["muted-foreground"], ...LABEL_SIZE[size] };
48
+ }
@@ -0,0 +1,99 @@
1
+ import { type ReactNode } from "react";
2
+ import { View, Image, Pressable, Text, useTheme, type StyleProp, type ViewStyle } from "../../style/index.js";
3
+ import * as s from "./avatar.styles.js";
4
+ import { type Size, type Shape } from "./avatar.styles.js";
5
+
6
+ // The avatar shows an account's photo when it has one, falling back to one or
7
+ // two initials on a muted surface. It is a circle by default (the consistent
8
+ // shape across topbars, identity rows, and menus), optionally a rounded square.
9
+ //
10
+ // Boolean-prop API: one boolean per option, grouped by axis, first-match
11
+ // precedence within an axis (mirrors Button's intentOf). Size picks the
12
+ // diameter and proportional type (~40% of the diameter); shape switches the
13
+ // corner radius; `ring` draws the separator outline used when avatars overlap
14
+ // in a stack.
15
+
16
+ export interface AvatarProps {
17
+ /** Photo URL. When set, the image fills the circle and the fallback is hidden. */
18
+ src?: string;
19
+ /** Alias for `src`, for callers that think in terms of a native image uri. */
20
+ uri?: string;
21
+ /** Initials fallback shown when there is no photo (e.g. "AO"). */
22
+ name?: string;
23
+ /** Same as `name`; the rendered initials when no photo is supplied. */
24
+ children?: ReactNode;
25
+ // Size (pick one; default is the 40px row avatar).
26
+ small?: boolean;
27
+ large?: boolean;
28
+ // Shape (pick one; default is a circle).
29
+ circle?: boolean;
30
+ rounded?: boolean;
31
+ /** Separator outline, for avatars that overlap in a stack. */
32
+ ring?: boolean;
33
+ /** When set, the avatar becomes pressable (e.g. a topbar account trigger). */
34
+ onPress?: () => void;
35
+ /** Escape hatch for layout/positioning composition (e.g. negative margin to overlap in a stack). */
36
+ style?: StyleProp<ViewStyle>;
37
+ }
38
+
39
+ // Size precedence when more than one is passed: first match wins.
40
+ function sizeOf(p: AvatarProps): Size {
41
+ if (p.small) return "small";
42
+ if (p.large) return "large";
43
+ return "default";
44
+ }
45
+
46
+ // Shape precedence when more than one is passed: first match wins.
47
+ function shapeOf(p: AvatarProps): Shape {
48
+ if (p.circle) return "circle";
49
+ if (p.rounded) return "rounded";
50
+ return "circle";
51
+ }
52
+
53
+ // Reduce a name or label to one or two initials ("Rachel Chen" -> "RC", "AO" ->
54
+ // "AO"), so callers can pass either a full name or ready-made initials.
55
+ function initialsFrom(text: string): string {
56
+ const parts = text.trim().split(/\s+/).filter(Boolean);
57
+ if (parts.length === 0) return "";
58
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
59
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
60
+ }
61
+
62
+ export function Avatar(props: AvatarProps) {
63
+ const { src, uri, name, children, ring, onPress, style } = props;
64
+ const { tokens } = useTheme();
65
+ const size = sizeOf(props);
66
+ const shape = shapeOf(props);
67
+ const photo = src ?? uri;
68
+
69
+ const container: StyleProp<ViewStyle> = [s.container(tokens, size, shape, !!ring), style];
70
+
71
+ let inner: ReactNode;
72
+ if (photo) {
73
+ inner = (
74
+ <Image
75
+ style={s.image(shape)}
76
+ source={{ uri: photo }}
77
+ accessibilityLabel={name}
78
+ resizeMode="cover"
79
+ />
80
+ );
81
+ } else {
82
+ const source = name ?? (typeof children === "string" ? children : "");
83
+ const initials = source ? initialsFrom(source) : "";
84
+ inner = initials ? <Text style={s.label(tokens, size)}>{initials}</Text> : null;
85
+ }
86
+
87
+ if (onPress) {
88
+ return (
89
+ <Pressable
90
+ style={({ pressed }) => [container, pressed ? { opacity: 0.9 } : null]}
91
+ onPress={onPress}
92
+ accessibilityRole="button"
93
+ >
94
+ {inner}
95
+ </Pressable>
96
+ );
97
+ }
98
+ return <View style={container}>{inner}</View>;
99
+ }