@passui/react 0.0.0-stage → 0.1.15

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 (275) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -2
  3. package/package.json +96 -5
  4. package/src/appearance/index.ts +2 -0
  5. package/src/appearance/platform.tsx +123 -0
  6. package/src/appearance/theme.tsx +535 -0
  7. package/src/components/ambient/ambient.docs.tsx +68 -0
  8. package/src/components/ambient/ambient.tsx +43 -0
  9. package/src/components/ambient/examples/ambient-images.tsx +30 -0
  10. package/src/components/ambient/examples/ambient-now-playing.tsx +55 -0
  11. package/src/components/ambient/examples/ambient-showcase.tsx +33 -0
  12. package/src/components/ambient/examples/ambient-usage.tsx +19 -0
  13. package/src/components/ambient/index.ts +1 -0
  14. package/src/components/button/button-styles.ts +60 -0
  15. package/src/components/button/button.docs.tsx +116 -0
  16. package/src/components/button/button.tsx +91 -0
  17. package/src/components/button/examples/button-disabled.tsx +19 -0
  18. package/src/components/button/examples/button-icon.tsx +39 -0
  19. package/src/components/button/examples/button-link.tsx +12 -0
  20. package/src/components/button/examples/button-pending.tsx +27 -0
  21. package/src/components/button/examples/button-showcase.tsx +115 -0
  22. package/src/components/button/examples/button-sizes.tsx +12 -0
  23. package/src/components/button/examples/button-usage.tsx +12 -0
  24. package/src/components/button/examples/button-variants.tsx +15 -0
  25. package/src/components/button/examples/button-with-icon.tsx +32 -0
  26. package/src/components/button/examples/icon-button-player.tsx +38 -0
  27. package/src/components/button/examples/icon-button-showcase.tsx +83 -0
  28. package/src/components/button/examples/icon-button-usage.tsx +11 -0
  29. package/src/components/button/examples/icon-button-variants.tsx +39 -0
  30. package/src/components/button/icon-button.docs.tsx +68 -0
  31. package/src/components/button/icon-button.tsx +73 -0
  32. package/src/components/button/pending-spinner.tsx +14 -0
  33. package/src/components/button/use-pending-indicator.ts +27 -0
  34. package/src/components/chip/chip.docs.tsx +37 -0
  35. package/src/components/chip/chip.tsx +35 -0
  36. package/src/components/chip/examples/chip-in-rows.tsx +19 -0
  37. package/src/components/chip/examples/chip-showcase.tsx +51 -0
  38. package/src/components/chip/examples/chip-tones.tsx +13 -0
  39. package/src/components/chip/examples/chip-usage.tsx +5 -0
  40. package/src/components/compact-date-picker/calendar-month.ts +131 -0
  41. package/src/components/compact-date-picker/compact-date-picker.docs.tsx +81 -0
  42. package/src/components/compact-date-picker/compact-date-picker.tsx +438 -0
  43. package/src/components/compact-date-picker/examples/compact-date-picker-showcase.tsx +35 -0
  44. package/src/components/compact-date-picker/examples/compact-date-picker-usage.tsx +22 -0
  45. package/src/components/compact-time-picker/compact-time-picker.docs.tsx +80 -0
  46. package/src/components/compact-time-picker/compact-time-picker.tsx +432 -0
  47. package/src/components/compact-time-picker/examples/compact-time-picker-showcase.tsx +31 -0
  48. package/src/components/compact-time-picker/examples/compact-time-picker-usage.tsx +21 -0
  49. package/src/components/compact-time-picker/time-value.ts +67 -0
  50. package/src/components/content-view/content-view.docs.tsx +61 -0
  51. package/src/components/content-view/content-view.tsx +197 -0
  52. package/src/components/content-view/examples/content-view-header-action.tsx +33 -0
  53. package/src/components/content-view/examples/content-view-showcase.tsx +65 -0
  54. package/src/components/content-view/examples/content-view-stats.tsx +35 -0
  55. package/src/components/content-view/examples/content-view-usage.tsx +14 -0
  56. package/src/components/content-view/index.ts +1 -0
  57. package/src/components/context-menu/conceal-menu-source.ts +33 -0
  58. package/src/components/context-menu/context-menu-host.tsx +445 -0
  59. package/src/components/context-menu/context-menu-store.ts +165 -0
  60. package/src/components/context-menu/context-menu.docs.tsx +92 -0
  61. package/src/components/context-menu/examples/context-menu-checked.tsx +37 -0
  62. package/src/components/context-menu/examples/context-menu-list.tsx +85 -0
  63. package/src/components/context-menu/examples/context-menu-showcase.tsx +56 -0
  64. package/src/components/context-menu/examples/context-menu-toolbar.tsx +63 -0
  65. package/src/components/context-menu/examples/context-menu-usage.tsx +24 -0
  66. package/src/components/context-menu/menu-position.ts +55 -0
  67. package/src/components/context-menu/menu-reveal.ts +75 -0
  68. package/src/components/context-menu/use-context-menu.ts +90 -0
  69. package/src/components/conversation/conversation.docs.tsx +88 -0
  70. package/src/components/conversation/conversation.tsx +279 -0
  71. package/src/components/conversation/examples/conversation-composer.tsx +36 -0
  72. package/src/components/conversation/examples/conversation-days.tsx +48 -0
  73. package/src/components/conversation/examples/conversation-showcase.tsx +71 -0
  74. package/src/components/conversation/examples/conversation-usage.tsx +28 -0
  75. package/src/components/conversation/index.ts +1 -0
  76. package/src/components/date-picker/date-picker.docs.tsx +88 -0
  77. package/src/components/date-picker/date-picker.tsx +103 -0
  78. package/src/components/date-picker/date-value.ts +37 -0
  79. package/src/components/date-picker/examples/date-picker-locales.tsx +41 -0
  80. package/src/components/date-picker/examples/date-picker-past-only.tsx +29 -0
  81. package/src/components/date-picker/examples/date-picker-showcase.tsx +49 -0
  82. package/src/components/date-picker/examples/date-picker-usage.tsx +22 -0
  83. package/src/components/dialog/dialog-host.tsx +156 -0
  84. package/src/components/dialog/dialog-store.ts +56 -0
  85. package/src/components/dialog/dialog.docs.tsx +114 -0
  86. package/src/components/dialog/dialog.tsx +200 -0
  87. package/src/components/dialog/examples/dialog-confirm.tsx +24 -0
  88. package/src/components/dialog/examples/dialog-form.tsx +46 -0
  89. package/src/components/dialog/examples/dialog-inline.tsx +48 -0
  90. package/src/components/dialog/examples/dialog-showcase.tsx +53 -0
  91. package/src/components/dialog/examples/dialog-usage.tsx +31 -0
  92. package/src/components/dialog/index.ts +1 -0
  93. package/src/components/labeled-content/examples/labeled-content-descriptions.tsx +37 -0
  94. package/src/components/labeled-content/examples/labeled-content-showcase.tsx +54 -0
  95. package/src/components/labeled-content/examples/labeled-content-usage.tsx +22 -0
  96. package/src/components/labeled-content/examples/labeled-content-values.tsx +26 -0
  97. package/src/components/labeled-content/examples/labeled-content-wide.tsx +46 -0
  98. package/src/components/labeled-content/labeled-content.docs.tsx +69 -0
  99. package/src/components/labeled-content/labeled-content.tsx +66 -0
  100. package/src/components/locale-switcher/examples/locale-switcher-native-names.tsx +30 -0
  101. package/src/components/locale-switcher/examples/locale-switcher-showcase.tsx +46 -0
  102. package/src/components/locale-switcher/examples/locale-switcher-translated-page.tsx +35 -0
  103. package/src/components/locale-switcher/examples/locale-switcher-usage.tsx +20 -0
  104. package/src/components/locale-switcher/index.ts +1 -0
  105. package/src/components/locale-switcher/locale-switcher.docs.tsx +91 -0
  106. package/src/components/locale-switcher/locale-switcher.tsx +252 -0
  107. package/src/components/picker/examples/picker-details.tsx +44 -0
  108. package/src/components/picker/examples/picker-searchable.tsx +48 -0
  109. package/src/components/picker/examples/picker-showcase.tsx +66 -0
  110. package/src/components/picker/examples/picker-usage.tsx +20 -0
  111. package/src/components/picker/picker.docs.tsx +68 -0
  112. package/src/components/picker/picker.tsx +493 -0
  113. package/src/components/popover/examples/popover-help.tsx +57 -0
  114. package/src/components/popover/examples/popover-menu.tsx +51 -0
  115. package/src/components/popover/examples/popover-showcase.tsx +61 -0
  116. package/src/components/popover/examples/popover-usage.tsx +31 -0
  117. package/src/components/popover/popover.docs.tsx +84 -0
  118. package/src/components/popover/popover.tsx +138 -0
  119. package/src/components/profile-image/examples/profile-image-fallbacks.tsx +29 -0
  120. package/src/components/profile-image/examples/profile-image-group.tsx +34 -0
  121. package/src/components/profile-image/examples/profile-image-showcase.tsx +30 -0
  122. package/src/components/profile-image/examples/profile-image-usage.tsx +14 -0
  123. package/src/components/profile-image/profile-image.docs.tsx +81 -0
  124. package/src/components/profile-image/profile-image.tsx +80 -0
  125. package/src/components/search-field/examples/search-field-filter.tsx +53 -0
  126. package/src/components/search-field/examples/search-field-showcase.tsx +56 -0
  127. package/src/components/search-field/examples/search-field-toolbar.tsx +27 -0
  128. package/src/components/search-field/examples/search-field-usage.tsx +18 -0
  129. package/src/components/search-field/search-field.docs.tsx +58 -0
  130. package/src/components/search-field/search-field.tsx +29 -0
  131. package/src/components/search-list/examples/search-list-dropdown.tsx +53 -0
  132. package/src/components/search-list/examples/search-list-groups.tsx +64 -0
  133. package/src/components/search-list/examples/search-list-showcase.tsx +67 -0
  134. package/src/components/search-list/examples/search-list-usage.tsx +27 -0
  135. package/src/components/search-list/search-list.docs.tsx +87 -0
  136. package/src/components/search-list/search-list.tsx +265 -0
  137. package/src/components/section/examples/section-actions.tsx +35 -0
  138. package/src/components/section/examples/section-groups.tsx +41 -0
  139. package/src/components/section/examples/section-showcase.tsx +63 -0
  140. package/src/components/section/examples/section-usage.tsx +19 -0
  141. package/src/components/section/section.docs.tsx +51 -0
  142. package/src/components/section/section.tsx +41 -0
  143. package/src/components/secure-field/examples/secure-field-new-password.tsx +56 -0
  144. package/src/components/secure-field/examples/secure-field-reveal.tsx +20 -0
  145. package/src/components/secure-field/examples/secure-field-showcase.tsx +62 -0
  146. package/src/components/secure-field/examples/secure-field-usage.tsx +19 -0
  147. package/src/components/secure-field/secure-field.docs.tsx +72 -0
  148. package/src/components/secure-field/secure-field.tsx +10 -0
  149. package/src/components/segmented-control/examples/segmented-control-overflow.tsx +28 -0
  150. package/src/components/segmented-control/examples/segmented-control-showcase.tsx +59 -0
  151. package/src/components/segmented-control/examples/segmented-control-usage.tsx +20 -0
  152. package/src/components/segmented-control/examples/segmented-control-views.tsx +32 -0
  153. package/src/components/segmented-control/segmented-control.docs.tsx +61 -0
  154. package/src/components/segmented-control/segmented-control.tsx +122 -0
  155. package/src/components/sheet/examples/sheet-choices.tsx +58 -0
  156. package/src/components/sheet/examples/sheet-share.tsx +51 -0
  157. package/src/components/sheet/examples/sheet-showcase.tsx +74 -0
  158. package/src/components/sheet/examples/sheet-usage.tsx +32 -0
  159. package/src/components/sheet/modal-environment.ts +113 -0
  160. package/src/components/sheet/sheet-portal.tsx +89 -0
  161. package/src/components/sheet/sheet.docs.tsx +75 -0
  162. package/src/components/sheet/sheet.tsx +112 -0
  163. package/src/components/sheet/use-sheet-focus.ts +130 -0
  164. package/src/components/sidebar/examples/sidebar-groups.tsx +133 -0
  165. package/src/components/sidebar/examples/sidebar-showcase.tsx +123 -0
  166. package/src/components/sidebar/examples/sidebar-unread.tsx +66 -0
  167. package/src/components/sidebar/examples/sidebar-usage.tsx +52 -0
  168. package/src/components/sidebar/sidebar.docs.tsx +135 -0
  169. package/src/components/sidebar/sidebar.tsx +69 -0
  170. package/src/components/stat-tile/examples/stat-tile-filters.tsx +32 -0
  171. package/src/components/stat-tile/examples/stat-tile-grid.tsx +21 -0
  172. package/src/components/stat-tile/examples/stat-tile-showcase.tsx +52 -0
  173. package/src/components/stat-tile/examples/stat-tile-usage.tsx +13 -0
  174. package/src/components/stat-tile/index.ts +1 -0
  175. package/src/components/stat-tile/stat-tile.docs.tsx +60 -0
  176. package/src/components/stat-tile/stat-tile.tsx +68 -0
  177. package/src/components/status-indicator/examples/status-indicator-live.tsx +34 -0
  178. package/src/components/status-indicator/examples/status-indicator-showcase.tsx +39 -0
  179. package/src/components/status-indicator/examples/status-indicator-states.tsx +17 -0
  180. package/src/components/status-indicator/examples/status-indicator-usage.tsx +9 -0
  181. package/src/components/status-indicator/status-indicator.docs.tsx +43 -0
  182. package/src/components/status-indicator/status-indicator.tsx +35 -0
  183. package/src/components/swatch-picker/examples/swatch-picker-accents.tsx +42 -0
  184. package/src/components/swatch-picker/examples/swatch-picker-labels.tsx +45 -0
  185. package/src/components/swatch-picker/examples/swatch-picker-showcase.tsx +70 -0
  186. package/src/components/swatch-picker/examples/swatch-picker-usage.tsx +39 -0
  187. package/src/components/swatch-picker/swatch-picker.docs.tsx +60 -0
  188. package/src/components/swatch-picker/swatch-picker.tsx +96 -0
  189. package/src/components/tab-bar/examples/tab-bar-five-tabs.tsx +66 -0
  190. package/src/components/tab-bar/examples/tab-bar-minimize.tsx +71 -0
  191. package/src/components/tab-bar/examples/tab-bar-showcase.tsx +82 -0
  192. package/src/components/tab-bar/examples/tab-bar-usage.tsx +54 -0
  193. package/src/components/tab-bar/tab-bar.docs.tsx +136 -0
  194. package/src/components/tab-bar/tab-bar.tsx +148 -0
  195. package/src/components/tab-view/examples/tab-view-keyboard.tsx +39 -0
  196. package/src/components/tab-view/examples/tab-view-settings.tsx +56 -0
  197. package/src/components/tab-view/examples/tab-view-showcase.tsx +53 -0
  198. package/src/components/tab-view/examples/tab-view-usage.tsx +23 -0
  199. package/src/components/tab-view/tab-view.docs.tsx +77 -0
  200. package/src/components/tab-view/tab-view.tsx +130 -0
  201. package/src/components/table/examples/table-empty.tsx +53 -0
  202. package/src/components/table/examples/table-invoices.tsx +102 -0
  203. package/src/components/table/examples/table-selection.tsx +150 -0
  204. package/src/components/table/examples/table-showcase.tsx +115 -0
  205. package/src/components/table/examples/table-usage.tsx +67 -0
  206. package/src/components/table/examples/table-wide.tsx +75 -0
  207. package/src/components/table/table.docs.tsx +131 -0
  208. package/src/components/table/table.tsx +149 -0
  209. package/src/components/text-editor/examples/text-editor-comment.tsx +48 -0
  210. package/src/components/text-editor/examples/text-editor-count.tsx +38 -0
  211. package/src/components/text-editor/examples/text-editor-showcase.tsx +58 -0
  212. package/src/components/text-editor/examples/text-editor-usage.tsx +25 -0
  213. package/src/components/text-editor/text-editor.docs.tsx +61 -0
  214. package/src/components/text-editor/text-editor.tsx +19 -0
  215. package/src/components/text-field/examples/text-field-icons.tsx +72 -0
  216. package/src/components/text-field/examples/text-field-showcase.tsx +50 -0
  217. package/src/components/text-field/examples/text-field-usage.tsx +18 -0
  218. package/src/components/text-field/examples/text-field-validation.tsx +27 -0
  219. package/src/components/text-field/text-field.docs.tsx +118 -0
  220. package/src/components/text-field/text-field.tsx +135 -0
  221. package/src/components/toast/examples/toast-action.tsx +32 -0
  222. package/src/components/toast/examples/toast-promise.tsx +45 -0
  223. package/src/components/toast/examples/toast-showcase.tsx +49 -0
  224. package/src/components/toast/examples/toast-usage.tsx +13 -0
  225. package/src/components/toast/toast-store.ts +166 -0
  226. package/src/components/toast/toast.docs.tsx +79 -0
  227. package/src/components/toast/toaster.tsx +374 -0
  228. package/src/components/toggle/examples/toggle-checkboxes.tsx +43 -0
  229. package/src/components/toggle/examples/toggle-settings.tsx +31 -0
  230. package/src/components/toggle/examples/toggle-showcase.tsx +59 -0
  231. package/src/components/toggle/examples/toggle-usage.tsx +13 -0
  232. package/src/components/toggle/toggle.docs.tsx +69 -0
  233. package/src/components/toggle/toggle.tsx +85 -0
  234. package/src/components/tooltip/examples/tooltip-icon-buttons.tsx +27 -0
  235. package/src/components/tooltip/examples/tooltip-shortcuts.tsx +38 -0
  236. package/src/components/tooltip/examples/tooltip-showcase.tsx +44 -0
  237. package/src/components/tooltip/examples/tooltip-usage.tsx +13 -0
  238. package/src/components/tooltip/tooltip-host.tsx +156 -0
  239. package/src/components/tooltip/tooltip.docs.tsx +56 -0
  240. package/src/components/view-controller/examples/view-controller-action.tsx +27 -0
  241. package/src/components/view-controller/examples/view-controller-settings.tsx +31 -0
  242. package/src/components/view-controller/examples/view-controller-showcase.tsx +45 -0
  243. package/src/components/view-controller/examples/view-controller-usage.tsx +19 -0
  244. package/src/components/view-controller/index.ts +1 -0
  245. package/src/components/view-controller/view-controller.docs.tsx +71 -0
  246. package/src/components/view-controller/view-controller.tsx +81 -0
  247. package/src/docs/index.ts +3 -0
  248. package/src/docs/registry.ts +113 -0
  249. package/src/docs/snippet.ts +17 -0
  250. package/src/docs/types.ts +180 -0
  251. package/src/hooks/scroll/minimize-decision.ts +13 -0
  252. package/src/hooks/use-anchored-placement.ts +43 -0
  253. package/src/hooks/use-dismiss.ts +58 -0
  254. package/src/hooks/use-media-query.ts +106 -0
  255. package/src/hooks/use-minimize-on-scroll.ts +92 -0
  256. package/src/hooks/use-visual-viewport.ts +76 -0
  257. package/src/hooks/viewport/types.ts +19 -0
  258. package/src/hooks/viewport/viewport-metrics.ts +25 -0
  259. package/src/icons/icons.tsx +298 -0
  260. package/src/index.ts +78 -0
  261. package/src/lib/anchored-placement.ts +92 -0
  262. package/src/lib/class-names.ts +21 -0
  263. package/src/lib/date.ts +96 -0
  264. package/src/lib/floating-layers.ts +28 -0
  265. package/src/lib/haptics.ts +25 -0
  266. package/src/lib/language.ts +21 -0
  267. package/src/lib/screen-edges.ts +45 -0
  268. package/src/lib/shapes.ts +70 -0
  269. package/src/motion/transitions.ts +30 -0
  270. package/src/styles/base.css +1 -0
  271. package/src/styles/styles.css +3 -0
  272. package/src/styles/theme.css +1 -0
  273. package/src/styles/typeset.css +1 -0
  274. package/src/styles/utilities.css +1 -0
  275. package/src/tokens.ts +1 -0
@@ -0,0 +1,55 @@
1
+ import {
2
+ PauseIcon,
3
+ NextIcon,
4
+ PreviousIcon,
5
+ } from "@hugeicons/core-free-icons"
6
+ import { HugeiconsIcon } from "@hugeicons/react"
7
+ import { Ambient } from "@passui/react/ambient"
8
+ import { IconButton } from "@passui/react/icon-button"
9
+
10
+ export function AmbientNowPlaying() {
11
+ return (
12
+ <Ambient
13
+ src="/avatars/kenji.svg"
14
+ className="flex w-full max-w-xs flex-col items-center gap-4 py-10 text-center"
15
+ >
16
+ <img
17
+ src="/avatars/kenji.svg"
18
+ alt=""
19
+ className="size-40 rounded-4xl shadow-sm"
20
+ />
21
+ <div className="flex flex-col">
22
+ <span className="text-headline font-semibold text-label">
23
+ Morning Light
24
+ </span>
25
+ <span className="text-footnote text-label-secondary">
26
+ Kenji Mori
27
+ </span>
28
+ </div>
29
+ <div className="flex items-center gap-2">
30
+ <IconButton label="Previous" variant="ghost">
31
+ <HugeiconsIcon
32
+ icon={PreviousIcon}
33
+ size={22}
34
+ />
35
+ </IconButton>
36
+ <IconButton
37
+ label="Pause"
38
+ variant="secondary"
39
+ size="icon-lg"
40
+ >
41
+ <HugeiconsIcon
42
+ icon={PauseIcon}
43
+ size={24}
44
+ />
45
+ </IconButton>
46
+ <IconButton label="Next" variant="ghost">
47
+ <HugeiconsIcon
48
+ icon={NextIcon}
49
+ size={22}
50
+ />
51
+ </IconButton>
52
+ </div>
53
+ </Ambient>
54
+ )
55
+ }
@@ -0,0 +1,33 @@
1
+ import { Ambient } from "@passui/react/ambient"
2
+ import { Button } from "@passui/react/button"
3
+
4
+ export function AmbientShowcase() {
5
+ return (
6
+ <Ambient
7
+ src="/avatars/mira.svg"
8
+ className="flex w-full max-w-sm flex-col items-center gap-4 py-12 text-center"
9
+ >
10
+ <img
11
+ src="/avatars/mira.svg"
12
+ alt=""
13
+ className="size-28 rounded-full shadow-sm"
14
+ />
15
+ <div className="flex flex-col gap-1">
16
+ <h2 className="text-title2 font-bold text-label">
17
+ Mira Patel
18
+ </h2>
19
+ <p className="text-callout text-label-secondary">
20
+ Support lead, Lisbon
21
+ </p>
22
+ </div>
23
+ <div className="flex gap-2">
24
+ <Button variant="accent" size="sm">
25
+ Message
26
+ </Button>
27
+ <Button variant="secondary" size="sm">
28
+ View profile
29
+ </Button>
30
+ </div>
31
+ </Ambient>
32
+ )
33
+ }
@@ -0,0 +1,19 @@
1
+ import { Ambient } from "@passui/react/ambient"
2
+
3
+ export function AmbientUsage() {
4
+ return (
5
+ <Ambient
6
+ src="/avatars/aiko.svg"
7
+ className="flex flex-col items-center gap-3 py-10"
8
+ >
9
+ <img
10
+ src="/avatars/aiko.svg"
11
+ alt=""
12
+ className="size-20 rounded-full"
13
+ />
14
+ <p className="text-headline font-semibold text-label">
15
+ Aiko Kamado
16
+ </p>
17
+ </Ambient>
18
+ )
19
+ }
@@ -0,0 +1 @@
1
+ export * from "./ambient"
@@ -0,0 +1,60 @@
1
+ export type ButtonVariant =
2
+ | "default"
3
+ | "destructive"
4
+ | "outline"
5
+ | "secondary"
6
+ | "ghost"
7
+ | "link"
8
+ | "accent"
9
+
10
+ export type ButtonSize =
11
+ | "default"
12
+ | "xs"
13
+ | "sm"
14
+ | "lg"
15
+ | "icon"
16
+ | "icon-xs"
17
+ | "icon-sm"
18
+ | "icon-lg"
19
+ | "icon-full"
20
+
21
+ export type IconButtonSize =
22
+ | "icon"
23
+ | "icon-xs"
24
+ | "icon-sm"
25
+ | "icon-lg"
26
+
27
+ export const buttonVariants: Record<ButtonVariant, string> = {
28
+ default:
29
+ "bg-inverse text-on-inverse shadow-[inset_0_1px_0_rgb(255_255_255/0.14),0_1px_2px_rgb(0_0_0/0.18),0_6px_16px_-6px_rgb(0_0_0/0.35)] hover:brightness-[1.12]",
30
+ destructive:
31
+ "bg-danger text-background shadow-[inset_0_1px_0_rgb(255_255_255/0.14),0_1px_2px_rgb(0_0_0/0.18),0_6px_16px_-6px_rgb(0_0_0/0.35)] hover:brightness-[1.08]",
32
+ outline:
33
+ "border border-separator bg-transparent text-label hover:bg-fill/60",
34
+ secondary:
35
+ "bg-elevated text-label shadow-[inset_0_0_0_0.5px_color-mix(in_srgb,var(--color-separator)_55%,transparent),0_1px_2px_rgb(40_30_0/0.06)] hover:bg-fill/60",
36
+ ghost: "bg-transparent text-label hover:bg-fill/80",
37
+ link: "bg-transparent text-accent-text underline-offset-4 hover:underline",
38
+ accent: "bg-accent text-on-accent ring-1 ring-accent-text/25 shadow-[inset_0_1px_0_rgb(255_255_255/0.35),0_1px_2px_rgb(60_40_0/0.16),0_6px_16px_-6px_color-mix(in_srgb,var(--color-accent)_70%,transparent)] hover:brightness-[1.04]",
39
+ }
40
+
41
+ export const buttonSizes: Record<ButtonSize, string> = {
42
+ default: "h-11 px-5 text-subheadline rounded-full",
43
+ xs: "h-8 px-3 text-caption rounded-full",
44
+ sm: "h-9 px-4 text-subheadline rounded-full",
45
+ lg: "h-12 px-6 text-body rounded-full",
46
+ icon: "size-11 p-0 rounded-full",
47
+ "icon-xs": "size-8 p-0 rounded-full",
48
+ "icon-sm": "size-9 p-0 rounded-full",
49
+ "icon-lg": "size-12 p-0 rounded-full",
50
+ "icon-full":
51
+ "h-11 w-full px-5 text-subheadline rounded-full",
52
+ }
53
+
54
+ export const iconButtonSizes: Record<IconButtonSize, string> =
55
+ {
56
+ icon: "size-11",
57
+ "icon-xs": "size-8",
58
+ "icon-sm": "size-9",
59
+ "icon-lg": "size-12",
60
+ }
@@ -0,0 +1,116 @@
1
+ import { ArrowRight01Icon } from "@hugeicons/core-free-icons"
2
+ import { HugeiconsIcon } from "@hugeicons/react"
3
+ import { jsxText } from "../../docs/snippet"
4
+ import { defineDocs } from "../../docs/types"
5
+ import { Button, type ButtonProps } from "./button"
6
+ import type { ButtonSize } from "./button-styles"
7
+ import { ButtonDisabled } from "./examples/button-disabled"
8
+ import { ButtonIcon } from "./examples/button-icon"
9
+ import { ButtonLink } from "./examples/button-link"
10
+ import { ButtonPending } from "./examples/button-pending"
11
+ import { ButtonShowcase } from "./examples/button-showcase"
12
+ import { ButtonSizes } from "./examples/button-sizes"
13
+ import { ButtonUsage } from "./examples/button-usage"
14
+ import { ButtonVariants } from "./examples/button-variants"
15
+ import { ButtonWithIcon } from "./examples/button-with-icon"
16
+
17
+ // Sample values only: the site writes all of the docs' prose.
18
+ const sampleLabel = "Continue"
19
+
20
+ const iconSizes = new Set<ButtonSize>([
21
+ "icon",
22
+ "icon-xs",
23
+ "icon-sm",
24
+ "icon-lg",
25
+ "icon-full",
26
+ ])
27
+
28
+ export const buttonDocs = defineDocs<ButtonProps>()({
29
+ slug: "button",
30
+ name: "Button",
31
+ importPath: "@passui/react/button",
32
+ controls: {
33
+ variant: {
34
+ kind: "select",
35
+ options: {
36
+ default: true,
37
+ secondary: true,
38
+ outline: true,
39
+ ghost: true,
40
+ link: true,
41
+ accent: true,
42
+ destructive: true,
43
+ },
44
+ default: "default",
45
+ },
46
+ size: {
47
+ kind: "select",
48
+ options: {
49
+ default: true,
50
+ xs: true,
51
+ sm: true,
52
+ lg: true,
53
+ icon: true,
54
+ "icon-xs": true,
55
+ "icon-sm": true,
56
+ "icon-lg": true,
57
+ "icon-full": true,
58
+ },
59
+ default: "default",
60
+ },
61
+ isPending: { kind: "boolean", default: false },
62
+ disabled: { kind: "boolean", default: false },
63
+ children: { kind: "text", default: sampleLabel },
64
+ },
65
+ render: ({ children, size, ...props }) =>
66
+ iconSizes.has(size) ? (
67
+ <Button
68
+ {...props}
69
+ size={size}
70
+ aria-label={children}
71
+ >
72
+ <HugeiconsIcon
73
+ icon={ArrowRight01Icon}
74
+ size={20}
75
+ />
76
+ </Button>
77
+ ) : (
78
+ <Button {...props} size={size}>
79
+ {children}
80
+ </Button>
81
+ ),
82
+ childrenSnippet: ({ children, size }) =>
83
+ iconSizes.has(size)
84
+ ? {
85
+ imports: [
86
+ 'import { ArrowRight01Icon } from "@hugeicons/core-free-icons"',
87
+ 'import { HugeiconsIcon } from "@hugeicons/react"',
88
+ ],
89
+ children:
90
+ "<HugeiconsIcon icon={ArrowRight01Icon} size={20} />",
91
+ extraProps: { "aria-label": children },
92
+ }
93
+ : { imports: [], children: jsxText(children) },
94
+ usage: ButtonUsage,
95
+ showcase: ButtonShowcase,
96
+ examples: [
97
+ { id: "variants", component: ButtonVariants },
98
+ { id: "sizes", component: ButtonSizes },
99
+ { id: "with-icon", component: ButtonWithIcon },
100
+ { id: "icon", component: ButtonIcon },
101
+ {
102
+ id: "pending",
103
+ component: ButtonPending,
104
+ playground: {
105
+ variant: "accent",
106
+ isPending: true,
107
+ },
108
+ },
109
+ {
110
+ id: "disabled",
111
+ component: ButtonDisabled,
112
+ playground: { disabled: true },
113
+ },
114
+ { id: "link", component: ButtonLink },
115
+ ],
116
+ })
@@ -0,0 +1,91 @@
1
+ import { type ButtonHTMLAttributes, forwardRef } from "react"
2
+ import { cn } from "../../lib/class-names"
3
+ import { PendingSpinner } from "./pending-spinner"
4
+ import { usePendingIndicator } from "./use-pending-indicator"
5
+ import {
6
+ buttonSizes,
7
+ buttonVariants,
8
+ type ButtonSize,
9
+ type ButtonVariant,
10
+ } from "./button-styles"
11
+
12
+ export type { ButtonSize, ButtonVariant }
13
+
14
+ /**
15
+ * Each variant has a little depth: a fine highlight along the top edge and
16
+ * a soft, layered shadow, like a well-made physical key.
17
+ */
18
+ export interface ButtonProps
19
+ extends ButtonHTMLAttributes<HTMLButtonElement> {
20
+ variant?: ButtonVariant
21
+ size?: ButtonSize
22
+ isPending?: boolean
23
+ }
24
+
25
+ /** Shares native button appearance with semantic links without nesting a button. */
26
+ export function buttonStyle({
27
+ variant = "default",
28
+ size = "default",
29
+ className,
30
+ }: Pick<ButtonProps, "variant" | "size" | "className"> = {}) {
31
+ return cn(
32
+ "group relative inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold tracking-[-0.005em]",
33
+ "transition-[background-color,filter,transform,box-shadow] duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] active:scale-[0.98]",
34
+ "motion-reduce:transform-none motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-40",
35
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 focus-visible:ring-offset-1 focus-visible:ring-offset-background",
36
+ buttonSizes[size],
37
+ buttonVariants[variant],
38
+ className
39
+ )
40
+ }
41
+
42
+ export const Button = forwardRef<
43
+ HTMLButtonElement,
44
+ ButtonProps
45
+ >(function Button(
46
+ {
47
+ variant = "default",
48
+ size = "default",
49
+ isPending = false,
50
+ disabled,
51
+ children,
52
+ className,
53
+ type = "button",
54
+ ...props
55
+ },
56
+ ref
57
+ ) {
58
+ const showPendingIndicator =
59
+ usePendingIndicator(isPending)
60
+
61
+ return (
62
+ <button
63
+ ref={ref}
64
+ type={type}
65
+ className={buttonStyle({
66
+ variant,
67
+ size,
68
+ // pending is busy, not unavailable: keep full colour
69
+ className: cn(
70
+ "data-pending:cursor-progress data-pending:disabled:opacity-100",
71
+ className
72
+ ),
73
+ })}
74
+ {...props}
75
+ data-pending={isPending || undefined}
76
+ disabled={disabled || isPending}
77
+ aria-busy={isPending || props["aria-busy"]}
78
+ >
79
+ {/* the label keeps its space, so the width never jumps */}
80
+ <span
81
+ className={cn(
82
+ "inline-flex items-center justify-center gap-2 transition-opacity duration-200",
83
+ showPendingIndicator && "opacity-0"
84
+ )}
85
+ >
86
+ {children}
87
+ </span>
88
+ {showPendingIndicator && <PendingSpinner />}
89
+ </button>
90
+ )
91
+ })
@@ -0,0 +1,19 @@
1
+ import { Toggle } from "@passui/react"
2
+ import { Button } from "@passui/react/button"
3
+ import { useState } from "react"
4
+
5
+ export function ButtonDisabled() {
6
+ const [agreed, setAgreed] = useState(false)
7
+
8
+ return (
9
+ <div className="flex w-full max-w-xs flex-col gap-4">
10
+ <Toggle
11
+ style="checkbox"
12
+ label="I agree to the terms"
13
+ checked={agreed}
14
+ onChange={setAgreed}
15
+ />
16
+ <Button disabled={!agreed}>Create account</Button>
17
+ </div>
18
+ )
19
+ }
@@ -0,0 +1,39 @@
1
+ import {
2
+ TextAlignCenterIcon,
3
+ TextAlignLeftIcon,
4
+ TextBoldIcon,
5
+ TextItalicIcon,
6
+ } from "@hugeicons/core-free-icons"
7
+ import { HugeiconsIcon } from "@hugeicons/react"
8
+ import { Button } from "@passui/react/button"
9
+
10
+ const tools = [
11
+ { label: "Bold", icon: TextBoldIcon },
12
+ { label: "Italic", icon: TextItalicIcon },
13
+ { label: "Align left", icon: TextAlignLeftIcon },
14
+ { label: "Align centre", icon: TextAlignCenterIcon },
15
+ ]
16
+
17
+ export function ButtonIcon() {
18
+ return (
19
+ <div
20
+ role="toolbar"
21
+ aria-label="Formatting"
22
+ className="flex items-center gap-1 rounded-full bg-fill/60 p-1"
23
+ >
24
+ {tools.map((tool) => (
25
+ <Button
26
+ key={tool.label}
27
+ size="icon-sm"
28
+ variant="ghost"
29
+ aria-label={tool.label}
30
+ >
31
+ <HugeiconsIcon
32
+ icon={tool.icon}
33
+ size={18}
34
+ />
35
+ </Button>
36
+ ))}
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,12 @@
1
+ import { buttonStyle } from "@passui/react/button"
2
+
3
+ export function ButtonLink() {
4
+ return (
5
+ <a
6
+ href="#guide"
7
+ className={buttonStyle({ variant: "secondary" })}
8
+ >
9
+ Read the guide
10
+ </a>
11
+ )
12
+ }
@@ -0,0 +1,27 @@
1
+ import { toast } from "@passui/react"
2
+ import { Button } from "@passui/react/button"
3
+ import { useState } from "react"
4
+
5
+ export function ButtonPending() {
6
+ const [saving, setSaving] = useState(false)
7
+
8
+ async function save() {
9
+ setSaving(true)
10
+ // stands in for a real request
11
+ await new Promise((resolve) =>
12
+ setTimeout(resolve, 1600)
13
+ )
14
+ setSaving(false)
15
+ toast.success("Profile saved")
16
+ }
17
+
18
+ return (
19
+ <Button
20
+ variant="accent"
21
+ isPending={saving}
22
+ onClick={save}
23
+ >
24
+ Save profile
25
+ </Button>
26
+ )
27
+ }
@@ -0,0 +1,115 @@
1
+ import {
2
+ Delete02Icon,
3
+ Link01Icon,
4
+ Share08Icon,
5
+ StarIcon,
6
+ } from "@hugeicons/core-free-icons"
7
+ import { HugeiconsIcon } from "@hugeicons/react"
8
+ import { TextField, toast } from "@passui/react"
9
+ import { Button } from "@passui/react/button"
10
+ import { useState } from "react"
11
+
12
+ export function ButtonShowcase() {
13
+ const [name, setName] = useState("Passui launch")
14
+ const [saving, setSaving] = useState(false)
15
+
16
+ async function save() {
17
+ setSaving(true)
18
+ await new Promise((resolve) =>
19
+ setTimeout(resolve, 1400)
20
+ )
21
+ setSaving(false)
22
+ toast.success("Project saved")
23
+ }
24
+
25
+ return (
26
+ <div className="flex w-full max-w-md flex-col gap-4">
27
+ <section className="flex flex-col gap-5 rounded-4xl bg-elevated p-6 shadow-sm ring-1 ring-separator/60">
28
+ <header className="flex items-start justify-between gap-4">
29
+ <div className="flex flex-col gap-1">
30
+ <h2 className="text-headline font-semibold text-label">
31
+ Project settings
32
+ </h2>
33
+ <p className="text-footnote text-label-secondary">
34
+ Changes apply to everyone on the
35
+ team.
36
+ </p>
37
+ </div>
38
+ <div className="flex gap-1">
39
+ <Button
40
+ size="icon-sm"
41
+ variant="ghost"
42
+ aria-label="Star"
43
+ >
44
+ <HugeiconsIcon
45
+ icon={StarIcon}
46
+ size={18}
47
+ />
48
+ </Button>
49
+ <Button
50
+ size="icon-sm"
51
+ variant="ghost"
52
+ aria-label="Copy link"
53
+ >
54
+ <HugeiconsIcon
55
+ icon={Link01Icon}
56
+ size={18}
57
+ />
58
+ </Button>
59
+ <Button
60
+ size="icon-sm"
61
+ variant="ghost"
62
+ aria-label="Share"
63
+ >
64
+ <HugeiconsIcon
65
+ icon={Share08Icon}
66
+ size={18}
67
+ />
68
+ </Button>
69
+ </div>
70
+ </header>
71
+ <TextField
72
+ label="Project name"
73
+ value={name}
74
+ onChange={(event) =>
75
+ setName(event.target.value)
76
+ }
77
+ />
78
+ <footer className="flex justify-end gap-2">
79
+ <Button
80
+ variant="ghost"
81
+ onClick={() =>
82
+ setName("Passui launch")
83
+ }
84
+ >
85
+ Cancel
86
+ </Button>
87
+ <Button
88
+ variant="accent"
89
+ isPending={saving}
90
+ onClick={save}
91
+ >
92
+ Save changes
93
+ </Button>
94
+ </footer>
95
+ </section>
96
+ <section className="flex items-center justify-between gap-4 rounded-4xl bg-elevated p-5 ring-1 ring-danger/25">
97
+ <div className="flex flex-col gap-1">
98
+ <span className="text-subheadline font-semibold text-label">
99
+ Delete project
100
+ </span>
101
+ <span className="text-footnote text-label-secondary">
102
+ This removes every file for good.
103
+ </span>
104
+ </div>
105
+ <Button variant="destructive" size="sm">
106
+ <HugeiconsIcon
107
+ icon={Delete02Icon}
108
+ size={16}
109
+ />
110
+ Delete
111
+ </Button>
112
+ </section>
113
+ </div>
114
+ )
115
+ }
@@ -0,0 +1,12 @@
1
+ import { Button } from "@passui/react/button"
2
+
3
+ export function ButtonSizes() {
4
+ return (
5
+ <div className="flex flex-wrap items-center justify-center gap-3">
6
+ <Button size="xs">Extra small</Button>
7
+ <Button size="sm">Small</Button>
8
+ <Button>Default</Button>
9
+ <Button size="lg">Large</Button>
10
+ </div>
11
+ )
12
+ }
@@ -0,0 +1,12 @@
1
+ import { toast } from "@passui/react"
2
+ import { Button } from "@passui/react/button"
3
+
4
+ export function ButtonUsage() {
5
+ return (
6
+ <Button
7
+ onClick={() => toast.success("Changes saved")}
8
+ >
9
+ Save changes
10
+ </Button>
11
+ )
12
+ }
@@ -0,0 +1,15 @@
1
+ import { Button } from "@passui/react/button"
2
+
3
+ export function ButtonVariants() {
4
+ return (
5
+ <div className="flex flex-wrap items-center justify-center gap-3">
6
+ <Button>Default</Button>
7
+ <Button variant="secondary">Secondary</Button>
8
+ <Button variant="outline">Outline</Button>
9
+ <Button variant="ghost">Ghost</Button>
10
+ <Button variant="link">Link</Button>
11
+ <Button variant="accent">Accent</Button>
12
+ <Button variant="destructive">Destructive</Button>
13
+ </div>
14
+ )
15
+ }
@@ -0,0 +1,32 @@
1
+ import {
2
+ Delete02Icon,
3
+ Download04Icon,
4
+ Mail01Icon,
5
+ } from "@hugeicons/core-free-icons"
6
+ import { HugeiconsIcon } from "@hugeicons/react"
7
+ import { Button } from "@passui/react/button"
8
+
9
+ export function ButtonWithIcon() {
10
+ return (
11
+ <div className="flex flex-wrap items-center justify-center gap-3">
12
+ <Button variant="accent">
13
+ <HugeiconsIcon icon={Mail01Icon} size={18} />
14
+ Email
15
+ </Button>
16
+ <Button variant="secondary">
17
+ <HugeiconsIcon
18
+ icon={Download04Icon}
19
+ size={18}
20
+ />
21
+ Download
22
+ </Button>
23
+ <Button variant="destructive">
24
+ <HugeiconsIcon
25
+ icon={Delete02Icon}
26
+ size={18}
27
+ />
28
+ Delete
29
+ </Button>
30
+ </div>
31
+ )
32
+ }