@shipfox/react-ui 0.3.0 → 0.5.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 (314) hide show
  1. package/.storybook/main.ts +20 -10
  2. package/.storybook/preview.tsx +1 -1
  3. package/.storybook/vitest.setup.ts +4 -0
  4. package/.turbo/turbo-build.log +2 -2
  5. package/.turbo/turbo-check.log +2 -2
  6. package/.turbo/turbo-type.log +1 -1
  7. package/CHANGELOG.md +18 -0
  8. package/README.md +40 -1
  9. package/argos.config.ts +33 -0
  10. package/dist/components/alert/alert.d.ts +2 -2
  11. package/dist/components/alert/alert.js +3 -3
  12. package/dist/components/alert/alert.js.map +1 -1
  13. package/dist/components/alert/alert.stories.js +2 -2
  14. package/dist/components/alert/alert.stories.js.map +1 -1
  15. package/dist/components/avatar/avatar-group.js +3 -3
  16. package/dist/components/avatar/avatar-group.js.map +1 -1
  17. package/dist/components/avatar/avatar.d.ts +4 -1
  18. package/dist/components/avatar/avatar.d.ts.map +1 -1
  19. package/dist/components/avatar/avatar.js +7 -8
  20. package/dist/components/avatar/avatar.js.map +1 -1
  21. package/dist/components/avatar/avatar.stories.js +15 -3
  22. package/dist/components/avatar/avatar.stories.js.map +1 -1
  23. package/dist/components/badge/badge.d.ts +48 -0
  24. package/dist/components/badge/badge.d.ts.map +1 -0
  25. package/dist/components/badge/badge.js +72 -0
  26. package/dist/components/badge/badge.js.map +1 -0
  27. package/dist/components/badge/badge.stories.js +802 -0
  28. package/dist/components/badge/badge.stories.js.map +1 -0
  29. package/dist/components/badge/icon-badge.d.ts +9 -0
  30. package/dist/components/badge/icon-badge.d.ts.map +1 -0
  31. package/dist/components/badge/icon-badge.js +32 -0
  32. package/dist/components/badge/icon-badge.js.map +1 -0
  33. package/dist/components/badge/index.d.ts +5 -0
  34. package/dist/components/badge/index.d.ts.map +1 -0
  35. package/dist/components/badge/index.js +6 -0
  36. package/dist/components/badge/index.js.map +1 -0
  37. package/dist/components/badge/status-badge.d.ts +9 -0
  38. package/dist/components/badge/status-badge.d.ts.map +1 -0
  39. package/dist/components/badge/status-badge.js +29 -0
  40. package/dist/components/badge/status-badge.js.map +1 -0
  41. package/dist/components/badge/user-badge.d.ts +8 -0
  42. package/dist/components/badge/user-badge.d.ts.map +1 -0
  43. package/dist/components/badge/user-badge.js +24 -0
  44. package/dist/components/badge/user-badge.js.map +1 -0
  45. package/dist/components/button/button-link.d.ts +14 -0
  46. package/dist/components/button/button-link.d.ts.map +1 -0
  47. package/dist/components/button/button-link.js +63 -0
  48. package/dist/components/button/button-link.js.map +1 -0
  49. package/dist/components/button/button-link.stories.js +127 -0
  50. package/dist/components/button/button-link.stories.js.map +1 -0
  51. package/dist/components/{button.d.ts → button/button.d.ts} +2 -2
  52. package/dist/components/button/button.d.ts.map +1 -0
  53. package/dist/components/{button.js → button/button.js} +9 -8
  54. package/dist/components/button/button.js.map +1 -0
  55. package/dist/components/{button.stories.js → button/button.stories.js} +2 -14
  56. package/dist/components/button/button.stories.js.map +1 -0
  57. package/dist/components/button/icon-button.d.ts +14 -0
  58. package/dist/components/button/icon-button.d.ts.map +1 -0
  59. package/dist/components/button/icon-button.js +53 -0
  60. package/dist/components/button/icon-button.js.map +1 -0
  61. package/dist/components/button/icon-button.stories.js +254 -0
  62. package/dist/components/button/icon-button.stories.js.map +1 -0
  63. package/dist/components/button/index.d.ts +4 -0
  64. package/dist/components/button/index.d.ts.map +1 -0
  65. package/dist/components/button/index.js +5 -0
  66. package/dist/components/button/index.js.map +1 -0
  67. package/dist/components/checkbox/checkbox-label.d.ts +14 -0
  68. package/dist/components/checkbox/checkbox-label.d.ts.map +1 -0
  69. package/dist/components/checkbox/checkbox-label.js +82 -0
  70. package/dist/components/checkbox/checkbox-label.js.map +1 -0
  71. package/dist/components/checkbox/checkbox-links.d.ts +18 -0
  72. package/dist/components/checkbox/checkbox-links.d.ts.map +1 -0
  73. package/dist/components/checkbox/checkbox-links.js +58 -0
  74. package/dist/components/checkbox/checkbox-links.js.map +1 -0
  75. package/dist/components/checkbox/checkbox.d.ts +9 -0
  76. package/dist/components/checkbox/checkbox.d.ts.map +1 -0
  77. package/dist/components/checkbox/checkbox.js +49 -0
  78. package/dist/components/checkbox/checkbox.js.map +1 -0
  79. package/dist/components/checkbox/checkbox.stories.js +566 -0
  80. package/dist/components/checkbox/checkbox.stories.js.map +1 -0
  81. package/dist/components/checkbox/index.d.ts +4 -0
  82. package/dist/components/checkbox/index.d.ts.map +1 -0
  83. package/dist/components/checkbox/index.js +5 -0
  84. package/dist/components/checkbox/index.js.map +1 -0
  85. package/dist/components/code-block/code-block-footer.d.ts +26 -0
  86. package/dist/components/code-block/code-block-footer.d.ts.map +1 -0
  87. package/dist/components/code-block/code-block-footer.js +86 -0
  88. package/dist/components/code-block/code-block-footer.js.map +1 -0
  89. package/dist/components/code-block/code-block.d.ts +50 -0
  90. package/dist/components/code-block/code-block.d.ts.map +1 -0
  91. package/dist/components/code-block/code-block.js +142 -0
  92. package/dist/components/code-block/code-block.js.map +1 -0
  93. package/dist/components/code-block/code-block.stories.js +341 -0
  94. package/dist/components/code-block/code-block.stories.js.map +1 -0
  95. package/dist/components/code-block/code-content.d.ts +11 -0
  96. package/dist/components/code-block/code-content.d.ts.map +1 -0
  97. package/dist/components/code-block/code-content.js +29 -0
  98. package/dist/components/code-block/code-content.js.map +1 -0
  99. package/dist/components/code-block/code-copy-button.d.ts +11 -0
  100. package/dist/components/code-block/code-copy-button.d.ts.map +1 -0
  101. package/dist/components/code-block/code-copy-button.js +49 -0
  102. package/dist/components/code-block/code-copy-button.js.map +1 -0
  103. package/dist/components/code-block/code-tabs.d.ts +16 -0
  104. package/dist/components/code-block/code-tabs.d.ts.map +1 -0
  105. package/dist/components/code-block/code-tabs.js +98 -0
  106. package/dist/components/code-block/code-tabs.js.map +1 -0
  107. package/dist/components/code-block/index.d.ts +4 -0
  108. package/dist/components/code-block/index.d.ts.map +1 -0
  109. package/dist/components/code-block/index.js +5 -0
  110. package/dist/components/code-block/index.js.map +1 -0
  111. package/dist/components/dynamic-item/dynamic-item.d.ts +13 -0
  112. package/dist/components/dynamic-item/dynamic-item.d.ts.map +1 -0
  113. package/dist/components/dynamic-item/dynamic-item.js +43 -0
  114. package/dist/components/dynamic-item/dynamic-item.js.map +1 -0
  115. package/dist/components/dynamic-item/dynamic-item.stories.js +375 -0
  116. package/dist/components/dynamic-item/dynamic-item.stories.js.map +1 -0
  117. package/dist/components/dynamic-item/index.d.ts +2 -0
  118. package/dist/components/dynamic-item/index.d.ts.map +1 -0
  119. package/dist/components/dynamic-item/index.js +3 -0
  120. package/dist/components/dynamic-item/index.js.map +1 -0
  121. package/dist/components/icon/custom/index.d.ts +2 -0
  122. package/dist/components/icon/custom/index.d.ts.map +1 -1
  123. package/dist/components/icon/custom/index.js +2 -0
  124. package/dist/components/icon/custom/index.js.map +1 -1
  125. package/dist/components/icon/custom/slack-logo.d.ts +6 -0
  126. package/dist/components/icon/custom/slack-logo.d.ts.map +1 -0
  127. package/dist/components/icon/custom/slack-logo.js +34 -0
  128. package/dist/components/icon/custom/slack-logo.js.map +1 -0
  129. package/dist/components/icon/custom/stripe-logo.d.ts +8 -0
  130. package/dist/components/icon/custom/stripe-logo.d.ts.map +1 -0
  131. package/dist/components/icon/custom/stripe-logo.js +24 -0
  132. package/dist/components/icon/custom/stripe-logo.js.map +1 -0
  133. package/dist/components/icon/icon.d.ts +13 -2
  134. package/dist/components/icon/icon.d.ts.map +1 -1
  135. package/dist/components/icon/icon.js +14 -3
  136. package/dist/components/icon/icon.js.map +1 -1
  137. package/dist/components/icon/icon.stories.js +6 -3
  138. package/dist/components/icon/icon.stories.js.map +1 -1
  139. package/dist/components/index.d.ts +9 -1
  140. package/dist/components/index.d.ts.map +1 -1
  141. package/dist/components/index.js +10 -2
  142. package/dist/components/index.js.map +1 -1
  143. package/dist/components/inline-tips/inline-tips.d.ts +1 -1
  144. package/dist/components/inline-tips/inline-tips.d.ts.map +1 -1
  145. package/dist/components/inline-tips/inline-tips.js +1 -1
  146. package/dist/components/inline-tips/inline-tips.js.map +1 -1
  147. package/dist/components/inline-tips/inline-tips.stories.js +5 -5
  148. package/dist/components/inline-tips/inline-tips.stories.js.map +1 -1
  149. package/dist/components/input/index.d.ts +2 -0
  150. package/dist/components/input/index.d.ts.map +1 -0
  151. package/dist/components/input/index.js +3 -0
  152. package/dist/components/input/index.js.map +1 -0
  153. package/dist/components/input/input.d.ts.map +1 -0
  154. package/dist/components/{input.js → input/input.js} +2 -2
  155. package/dist/components/input/input.js.map +1 -0
  156. package/dist/components/{input.stories.js → input/input.stories.js} +1 -1
  157. package/dist/components/input/input.stories.js.map +1 -0
  158. package/dist/components/item/index.d.ts +2 -0
  159. package/dist/components/item/index.d.ts.map +1 -0
  160. package/dist/components/item/index.js +3 -0
  161. package/dist/components/item/index.js.map +1 -0
  162. package/dist/components/item/item.d.ts +32 -0
  163. package/dist/components/item/item.d.ts.map +1 -0
  164. package/dist/components/item/item.js +120 -0
  165. package/dist/components/item/item.js.map +1 -0
  166. package/dist/components/item/item.stories.js +232 -0
  167. package/dist/components/item/item.stories.js.map +1 -0
  168. package/dist/components/label/index.d.ts +2 -0
  169. package/dist/components/label/index.d.ts.map +1 -0
  170. package/dist/components/label/index.js +3 -0
  171. package/dist/components/label/index.js.map +1 -0
  172. package/dist/components/label/label.d.ts +7 -0
  173. package/dist/components/label/label.d.ts.map +1 -0
  174. package/dist/components/label/label.js +13 -0
  175. package/dist/components/label/label.js.map +1 -0
  176. package/dist/components/label/label.stories.js +105 -0
  177. package/dist/components/label/label.stories.js.map +1 -0
  178. package/dist/components/moving-border/moving-border.d.ts +9 -0
  179. package/dist/components/moving-border/moving-border.d.ts.map +1 -0
  180. package/dist/components/moving-border/moving-border.js +54 -0
  181. package/dist/components/moving-border/moving-border.js.map +1 -0
  182. package/dist/components/textarea/textarea.js +1 -1
  183. package/dist/components/textarea/textarea.js.map +1 -1
  184. package/dist/components/theme/index.d.ts +2 -0
  185. package/dist/components/theme/index.d.ts.map +1 -0
  186. package/dist/components/theme/index.js +3 -0
  187. package/dist/components/theme/index.js.map +1 -0
  188. package/dist/components/{theme-provider.d.ts → theme/theme-provider.d.ts} +1 -1
  189. package/dist/components/theme/theme-provider.d.ts.map +1 -0
  190. package/dist/components/{theme-provider.js → theme/theme-provider.js} +1 -1
  191. package/dist/components/theme/theme-provider.js.map +1 -0
  192. package/dist/components/toast/index.d.ts +3 -0
  193. package/dist/components/toast/index.d.ts.map +1 -0
  194. package/dist/components/toast/index.js +4 -0
  195. package/dist/components/toast/index.js.map +1 -0
  196. package/dist/components/toast/toast-custom.d.ts +19 -0
  197. package/dist/components/toast/toast-custom.d.ts.map +1 -0
  198. package/dist/components/toast/toast-custom.js +134 -0
  199. package/dist/components/toast/toast-custom.js.map +1 -0
  200. package/dist/components/toast/toast.d.ts +5 -0
  201. package/dist/components/toast/toast.d.ts.map +1 -0
  202. package/dist/components/toast/toast.js +40 -0
  203. package/dist/components/toast/toast.js.map +1 -0
  204. package/dist/components/toast/toast.stories.js +326 -0
  205. package/dist/components/toast/toast.stories.js.map +1 -0
  206. package/dist/components/tooltip/index.d.ts +2 -0
  207. package/dist/components/tooltip/index.d.ts.map +1 -0
  208. package/dist/components/tooltip/index.js +3 -0
  209. package/dist/components/tooltip/index.js.map +1 -0
  210. package/dist/components/tooltip/tooltip.d.ts +18 -5
  211. package/dist/components/tooltip/tooltip.d.ts.map +1 -1
  212. package/dist/components/tooltip/tooltip.js +63 -3
  213. package/dist/components/tooltip/tooltip.js.map +1 -1
  214. package/dist/components/tooltip/tooltip.stories.js +560 -0
  215. package/dist/components/tooltip/tooltip.stories.js.map +1 -0
  216. package/dist/hooks/index.d.ts +3 -0
  217. package/dist/hooks/index.d.ts.map +1 -1
  218. package/dist/hooks/index.js +3 -0
  219. package/dist/hooks/index.js.map +1 -1
  220. package/dist/hooks/useResolvedTheme.d.ts +2 -0
  221. package/dist/hooks/useResolvedTheme.d.ts.map +1 -0
  222. package/dist/hooks/useResolvedTheme.js +24 -0
  223. package/dist/hooks/useResolvedTheme.js.map +1 -0
  224. package/dist/hooks/useShikiHighlight.d.ts +28 -0
  225. package/dist/hooks/useShikiHighlight.d.ts.map +1 -0
  226. package/dist/hooks/useShikiHighlight.js +106 -0
  227. package/dist/hooks/useShikiHighlight.js.map +1 -0
  228. package/dist/hooks/useShikiStyleInjection.d.ts +2 -0
  229. package/dist/hooks/useShikiStyleInjection.d.ts.map +1 -0
  230. package/dist/hooks/useShikiStyleInjection.js +34 -0
  231. package/dist/hooks/useShikiStyleInjection.js.map +1 -0
  232. package/dist/onboarding/sign-in.stories.js +93 -0
  233. package/dist/onboarding/sign-in.stories.js.map +1 -0
  234. package/index.css +130 -12
  235. package/package.json +14 -3
  236. package/src/assets/illustration-1.svg +92 -0
  237. package/src/assets/illustration-2.svg +14 -0
  238. package/src/assets/illustration-gradient.svg +7049 -0
  239. package/src/components/alert/alert.stories.tsx +2 -2
  240. package/src/components/alert/alert.tsx +3 -3
  241. package/src/components/avatar/avatar-group.tsx +3 -3
  242. package/src/components/avatar/avatar.stories.tsx +9 -2
  243. package/src/components/avatar/avatar.tsx +10 -6
  244. package/src/components/badge/badge.stories.tsx +468 -0
  245. package/src/components/badge/badge.tsx +147 -0
  246. package/src/components/badge/icon-badge.tsx +43 -0
  247. package/src/components/badge/index.ts +4 -0
  248. package/src/components/badge/status-badge.tsx +43 -0
  249. package/src/components/badge/user-badge.tsx +34 -0
  250. package/src/components/button/button-link.stories.tsx +86 -0
  251. package/src/components/button/button-link.tsx +76 -0
  252. package/src/components/{button.stories.tsx → button/button.stories.tsx} +1 -7
  253. package/src/components/{button.tsx → button/button.tsx} +9 -7
  254. package/src/components/button/icon-button.stories.tsx +182 -0
  255. package/src/components/button/icon-button.tsx +69 -0
  256. package/src/components/button/index.ts +3 -0
  257. package/src/components/checkbox/checkbox-label.tsx +125 -0
  258. package/src/components/checkbox/checkbox-links.tsx +90 -0
  259. package/src/components/checkbox/checkbox.stories.tsx +375 -0
  260. package/src/components/checkbox/checkbox.tsx +71 -0
  261. package/src/components/checkbox/index.ts +3 -0
  262. package/src/components/code-block/code-block-footer.tsx +173 -0
  263. package/src/components/code-block/code-block.stories.tsx +323 -0
  264. package/src/components/code-block/code-block.tsx +283 -0
  265. package/src/components/code-block/code-content.tsx +63 -0
  266. package/src/components/code-block/code-copy-button.tsx +73 -0
  267. package/src/components/code-block/code-tabs.tsx +170 -0
  268. package/src/components/code-block/index.ts +3 -0
  269. package/src/components/dynamic-item/dynamic-item.stories.tsx +261 -0
  270. package/src/components/dynamic-item/dynamic-item.tsx +68 -0
  271. package/src/components/dynamic-item/index.ts +1 -0
  272. package/src/components/icon/custom/index.ts +2 -0
  273. package/src/components/icon/custom/slack-logo.tsx +35 -0
  274. package/src/components/icon/custom/stripe-logo.tsx +27 -0
  275. package/src/components/icon/icon.stories.tsx +3 -1
  276. package/src/components/icon/icon.tsx +23 -1
  277. package/src/components/index.ts +9 -1
  278. package/src/components/inline-tips/inline-tips.stories.tsx +3 -3
  279. package/src/components/inline-tips/inline-tips.tsx +2 -2
  280. package/src/components/input/index.ts +1 -0
  281. package/src/components/{input.tsx → input/input.tsx} +1 -1
  282. package/src/components/item/index.ts +1 -0
  283. package/src/components/item/item.stories.tsx +150 -0
  284. package/src/components/item/item.tsx +182 -0
  285. package/src/components/label/index.ts +1 -0
  286. package/src/components/label/label.stories.tsx +67 -0
  287. package/src/components/label/label.tsx +15 -0
  288. package/src/components/moving-border/moving-border.tsx +67 -0
  289. package/src/components/textarea/textarea.tsx +1 -1
  290. package/src/components/theme/index.ts +1 -0
  291. package/src/components/toast/index.ts +2 -0
  292. package/src/components/toast/toast-custom.tsx +154 -0
  293. package/src/components/toast/toast.stories.tsx +369 -0
  294. package/src/components/toast/toast.tsx +41 -0
  295. package/src/components/tooltip/index.ts +1 -0
  296. package/src/components/tooltip/tooltip.stories.tsx +284 -0
  297. package/src/components/tooltip/tooltip.tsx +79 -10
  298. package/src/hooks/index.ts +3 -0
  299. package/src/hooks/useResolvedTheme.ts +34 -0
  300. package/src/hooks/useShikiHighlight.ts +140 -0
  301. package/src/hooks/useShikiStyleInjection.ts +34 -0
  302. package/src/onboarding/sign-in.stories.tsx +73 -0
  303. package/vitest.config.ts +30 -3
  304. package/dist/components/button.d.ts.map +0 -1
  305. package/dist/components/button.js.map +0 -1
  306. package/dist/components/button.stories.js.map +0 -1
  307. package/dist/components/input.d.ts.map +0 -1
  308. package/dist/components/input.js.map +0 -1
  309. package/dist/components/input.stories.js.map +0 -1
  310. package/dist/components/theme-provider.d.ts.map +0 -1
  311. package/dist/components/theme-provider.js.map +0 -1
  312. /package/dist/components/{input.d.ts → input/input.d.ts} +0 -0
  313. /package/src/components/{input.stories.tsx → input/input.stories.tsx} +0 -0
  314. /package/src/components/{theme-provider.tsx → theme/theme-provider.tsx} +0 -0
@@ -0,0 +1,147 @@
1
+ import {Slot} from '@radix-ui/react-slot';
2
+ import {cva, type VariantProps} from 'class-variance-authority';
3
+ import {Icon, type IconName} from 'components/icon';
4
+ import type {ComponentProps} from 'react';
5
+ import {cn} from 'utils/cn';
6
+
7
+ export const badgeVariants = cva(
8
+ 'inline-flex select-none items-center justify-center font-medium transition-colors shrink-0 leading-20',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ neutral:
13
+ 'bg-tag-neutral-bg text-tag-neutral-text border border-tag-neutral-border hover:bg-tag-neutral-bg-hover',
14
+ info: 'bg-tag-blue-bg text-tag-blue-text border border-tag-blue-border hover:bg-tag-blue-bg-hover',
15
+ feature:
16
+ 'bg-tag-purple-bg text-tag-purple-text border border-tag-purple-border hover:bg-tag-purple-bg-hover',
17
+ success:
18
+ 'bg-tag-success-bg text-tag-success-text border border-tag-success-border hover:bg-tag-success-bg-hover',
19
+ warning:
20
+ 'bg-tag-warning-bg text-tag-warning-text border border-tag-warning-border hover:bg-tag-warning-bg-hover',
21
+ error:
22
+ 'bg-tag-error-bg text-tag-error-text border border-tag-error-border hover:bg-tag-error-bg-hover',
23
+ },
24
+ size: {
25
+ '2xs': 'h-20 px-6 text-xs gap-4',
26
+ xs: 'h-24 px-8 text-xs gap-6',
27
+ },
28
+ radius: {
29
+ default: 'rounded-6',
30
+ rounded: 'rounded-full',
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: 'neutral',
35
+ size: '2xs',
36
+ radius: 'default',
37
+ },
38
+ },
39
+ );
40
+
41
+ export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
42
+
43
+ type BaseBadgeProps = ComponentProps<'span'> &
44
+ VariantProps<typeof badgeVariants> & {
45
+ asChild?: boolean;
46
+ };
47
+
48
+ type BadgePropsWithLeftClick = BaseBadgeProps & {
49
+ iconLeft: IconName;
50
+ onIconLeftClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
51
+ iconLeftAriaLabel: string;
52
+ iconRight?: IconName;
53
+ onIconRightClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
54
+ iconRightAriaLabel?: string;
55
+ };
56
+
57
+ type BadgePropsWithRightClick = BaseBadgeProps & {
58
+ iconRight: IconName;
59
+ onIconRightClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
60
+ iconRightAriaLabel: string;
61
+ iconLeft?: IconName;
62
+ onIconLeftClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
63
+ iconLeftAriaLabel?: string;
64
+ };
65
+
66
+ type BadgePropsWithBothClicks = BaseBadgeProps & {
67
+ iconLeft: IconName;
68
+ onIconLeftClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
69
+ iconLeftAriaLabel: string;
70
+ iconRight: IconName;
71
+ onIconRightClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
72
+ iconRightAriaLabel: string;
73
+ };
74
+
75
+ type BadgePropsWithoutClicks = BaseBadgeProps & {
76
+ iconLeft?: IconName;
77
+ iconRight?: IconName;
78
+ onIconLeftClick?: never;
79
+ onIconRightClick?: never;
80
+ iconLeftAriaLabel?: never;
81
+ iconRightAriaLabel?: never;
82
+ };
83
+
84
+ export type BadgeProps =
85
+ | BadgePropsWithLeftClick
86
+ | BadgePropsWithRightClick
87
+ | BadgePropsWithBothClicks
88
+ | BadgePropsWithoutClicks;
89
+
90
+ export function Badge({
91
+ className,
92
+ variant,
93
+ size,
94
+ radius,
95
+ asChild = false,
96
+ children,
97
+ iconLeft,
98
+ iconRight,
99
+ onIconLeftClick,
100
+ onIconRightClick,
101
+ iconLeftAriaLabel,
102
+ iconRightAriaLabel,
103
+ ...props
104
+ }: BadgeProps) {
105
+ const Comp = asChild ? Slot : 'span';
106
+
107
+ const renderIcon = (
108
+ iconName: IconName,
109
+ position: 'left' | 'right',
110
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void,
111
+ ariaLabel?: string,
112
+ ) => {
113
+ const isInteractive = Boolean(onClick);
114
+
115
+ if (isInteractive) {
116
+ if (!ariaLabel) {
117
+ // biome-ignore lint/suspicious/noConsole: Development warning for accessibility
118
+ console.warn(
119
+ `Badge: Missing aria-label for interactive ${position} icon. Please provide icon${position === 'left' ? 'Left' : 'Right'}AriaLabel prop.`,
120
+ );
121
+
122
+ return null;
123
+ }
124
+
125
+ return (
126
+ <button
127
+ type="button"
128
+ onClick={onClick}
129
+ className="inline-flex items-center justify-center transition-colors shrink-0 hover:opacity-70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-blue-500"
130
+ aria-label={ariaLabel}
131
+ >
132
+ <Icon name={iconName} className="size-12" />
133
+ </button>
134
+ );
135
+ }
136
+
137
+ return <Icon name={iconName} className="size-12" />;
138
+ };
139
+
140
+ return (
141
+ <Comp className={cn(badgeVariants({variant, size, radius}), className)} {...props}>
142
+ {iconLeft && renderIcon(iconLeft, 'left', onIconLeftClick, iconLeftAriaLabel)}
143
+ {children}
144
+ {iconRight && renderIcon(iconRight, 'right', onIconRightClick, iconRightAriaLabel)}
145
+ </Comp>
146
+ );
147
+ }
@@ -0,0 +1,43 @@
1
+ import {Icon, type IconName} from 'components/icon';
2
+ import type {ComponentProps} from 'react';
3
+ import {cn} from 'utils/cn';
4
+
5
+ export type IconBadgeVariant = 'neutral' | 'info' | 'feature' | 'success' | 'primary' | 'error';
6
+
7
+ export type IconBadgeProps = ComponentProps<'span'> & {
8
+ variant?: IconBadgeVariant;
9
+ name?: IconName;
10
+ };
11
+
12
+ const variantStyles: Record<IconBadgeVariant, string> = {
13
+ neutral: 'bg-tag-neutral-bg border-tag-neutral-border',
14
+ info: 'bg-tag-blue-bg border-tag-blue-border',
15
+ feature: 'bg-tag-purple-bg border-tag-purple-border',
16
+ success: 'bg-tag-success-bg border-tag-success-border',
17
+ primary: 'bg-tag-warning-bg border-tag-warning-border',
18
+ error: 'bg-tag-error-bg border-tag-error-border',
19
+ };
20
+
21
+ const iconColorStyles: Record<IconBadgeVariant, string> = {
22
+ neutral: 'text-tag-neutral-icon',
23
+ info: 'text-tag-blue-icon',
24
+ feature: 'text-tag-purple-icon',
25
+ success: 'text-tag-success-icon',
26
+ primary: 'text-tag-warning-icon',
27
+ error: 'text-tag-error-icon',
28
+ };
29
+
30
+ export function IconBadge({className, variant = 'neutral', name, ...props}: IconBadgeProps) {
31
+ return (
32
+ <span
33
+ className={cn(
34
+ 'inline-flex size-20 items-center justify-center rounded-6 border',
35
+ variantStyles[variant],
36
+ className,
37
+ )}
38
+ {...props}
39
+ >
40
+ {name && <Icon name={name} className={cn('shrink-0', iconColorStyles[variant])} size={12} />}
41
+ </span>
42
+ );
43
+ }
@@ -0,0 +1,4 @@
1
+ export {Badge, type BadgeProps, type BadgeVariant, badgeVariants} from './badge';
2
+ export {IconBadge, type IconBadgeProps, type IconBadgeVariant} from './icon-badge';
3
+ export {StatusBadge, type StatusBadgeProps} from './status-badge';
4
+ export {UserBadge, type UserBadgeProps} from './user-badge';
@@ -0,0 +1,43 @@
1
+ import type {ComponentProps} from 'react';
2
+ import {cn} from 'utils/cn';
3
+ import {badgeVariants} from './badge';
4
+
5
+ export type StatusBadgeProps = ComponentProps<'span'> & {
6
+ variant?: StatusVariant;
7
+ dotClassName?: string;
8
+ };
9
+
10
+ type StatusVariant = 'neutral' | 'info' | 'feature' | 'success' | 'warning' | 'error';
11
+
12
+ const dotVariantStyles: Record<StatusVariant, string> = {
13
+ neutral: 'bg-tag-neutral-icon',
14
+ info: 'bg-tag-blue-icon',
15
+ feature: 'bg-tag-purple-icon',
16
+ success: 'bg-tag-success-icon',
17
+ warning: 'bg-tag-warning-icon',
18
+ error: 'bg-tag-error-icon',
19
+ };
20
+
21
+ export function StatusBadge({
22
+ className,
23
+ variant = 'neutral',
24
+ children,
25
+ dotClassName,
26
+ ...props
27
+ }: StatusBadgeProps) {
28
+ return (
29
+ <span
30
+ className={cn(badgeVariants({variant, size: '2xs', radius: 'default'}), 'gap-6', className)}
31
+ {...props}
32
+ >
33
+ <span
34
+ className={cn(
35
+ 'size-8.5 rounded-2 shrink-0',
36
+ dotVariantStyles[variant as StatusVariant],
37
+ dotClassName,
38
+ )}
39
+ />
40
+ {children}
41
+ </span>
42
+ );
43
+ }
@@ -0,0 +1,34 @@
1
+ import {Avatar} from 'components/avatar';
2
+ import type {ComponentProps} from 'react';
3
+ import {cn} from 'utils/cn';
4
+
5
+ export type UserBadgeProps = ComponentProps<'button'> & {
6
+ name: string;
7
+ avatarSrc?: string;
8
+ avatarFallback?: string;
9
+ };
10
+
11
+ export function UserBadge({className, name, avatarSrc, avatarFallback, ...props}: UserBadgeProps) {
12
+ return (
13
+ <button
14
+ type="button"
15
+ className={cn(
16
+ 'inline-flex items-center gap-6 rounded-full px-6 py-2 text-xs font-medium leading-20',
17
+ 'bg-background-components-base hover:bg-background-components-hover active:bg-background-components-pressed',
18
+ 'text-foreground-neutral-base transition-colors',
19
+ 'border border-border-neutral-base-component',
20
+ 'h-28',
21
+ className,
22
+ )}
23
+ {...props}
24
+ >
25
+ <Avatar
26
+ className="size-16 shrink-0"
27
+ content="image"
28
+ src={avatarSrc}
29
+ fallback={avatarFallback}
30
+ />
31
+ <span className="whitespace-nowrap">{name}</span>
32
+ </button>
33
+ );
34
+ }
@@ -0,0 +1,86 @@
1
+ import type {Meta, StoryObj} from '@storybook/react';
2
+ import {ButtonLink} from './button-link';
3
+
4
+ const meta = {
5
+ title: 'Components/Button/ButtonLink',
6
+ component: ButtonLink,
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ variant: {
10
+ control: 'select',
11
+ options: ['base', 'interactive', 'muted', 'subtle'],
12
+ },
13
+ size: {
14
+ control: 'select',
15
+ options: ['xs', 'sm', 'md', 'xl'],
16
+ },
17
+ underline: {control: 'boolean'},
18
+ asChild: {control: 'boolean'},
19
+ },
20
+ args: {
21
+ children: 'Label',
22
+ variant: 'base',
23
+ size: 'sm',
24
+ underline: false,
25
+ href: '#',
26
+ },
27
+ } satisfies Meta<typeof ButtonLink>;
28
+
29
+ export default meta;
30
+ type Story = StoryObj<typeof meta>;
31
+
32
+ export const Default: Story = {};
33
+
34
+ export const Variants: Story = {
35
+ render: (args) => (
36
+ <div className="flex gap-16 items-center">
37
+ <ButtonLink {...args} variant="base">
38
+ Base
39
+ </ButtonLink>
40
+ <ButtonLink {...args} variant="interactive">
41
+ Interactive
42
+ </ButtonLink>
43
+ <ButtonLink {...args} variant="muted">
44
+ Muted
45
+ </ButtonLink>
46
+ <ButtonLink {...args} variant="subtle">
47
+ Subtle
48
+ </ButtonLink>
49
+ </div>
50
+ ),
51
+ };
52
+
53
+ export const WithUnderline: Story = {
54
+ render: (args) => (
55
+ <div className="flex gap-16 items-center">
56
+ <ButtonLink {...args} variant="base" underline>
57
+ Base
58
+ </ButtonLink>
59
+ <ButtonLink {...args} variant="interactive" underline>
60
+ Interactive
61
+ </ButtonLink>
62
+ <ButtonLink {...args} variant="muted" underline>
63
+ Muted
64
+ </ButtonLink>
65
+ <ButtonLink {...args} variant="subtle" underline>
66
+ Subtle
67
+ </ButtonLink>
68
+ </div>
69
+ ),
70
+ };
71
+
72
+ export const WithIcons: Story = {
73
+ render: (args) => (
74
+ <div className="flex gap-16 items-center">
75
+ <ButtonLink {...args} iconLeft="addLine">
76
+ Icon Left
77
+ </ButtonLink>
78
+ <ButtonLink {...args} iconRight="chevronRight">
79
+ Icon Right
80
+ </ButtonLink>
81
+ <ButtonLink {...args} iconLeft="addLine" iconRight="chevronRight">
82
+ Both Icons
83
+ </ButtonLink>
84
+ </div>
85
+ ),
86
+ };
@@ -0,0 +1,76 @@
1
+ import {Slot} from '@radix-ui/react-slot';
2
+ import {cva, type VariantProps} from 'class-variance-authority';
3
+ import {Icon, type IconName} from 'components/icon';
4
+ import type {ComponentProps} from 'react';
5
+ import {cn} from 'utils/cn';
6
+
7
+ export const buttonLinkVariants = cva(
8
+ 'inline-flex items-center justify-center gap-4 whitespace-nowrap transition-colors disabled:pointer-events-none outline-none font-medium',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ base: 'text-foreground-neutral-base hover:text-foreground-neutral-base focus-visible:text-foreground-neutral-base disabled:text-foreground-neutral-disabled',
13
+ interactive:
14
+ 'text-foreground-highlight-interactive hover:text-foreground-highlight-interactive-hover focus-visible:text-foreground-highlight-interactive disabled:text-foreground-neutral-disabled',
15
+ muted:
16
+ 'text-foreground-neutral-muted hover:text-foreground-neutral-base focus-visible:text-foreground-neutral-base disabled:text-foreground-neutral-disabled',
17
+ subtle:
18
+ 'text-foreground-neutral-subtle hover:text-foreground-neutral-base focus-visible:text-foreground-neutral-base disabled:text-foreground-neutral-disabled',
19
+ },
20
+ size: {
21
+ xs: 'text-xs',
22
+ sm: 'text-sm',
23
+ md: 'text-md',
24
+ xl: 'text-xl',
25
+ },
26
+ underline: {
27
+ true: 'underline decoration-solid [text-underline-position:from-font]',
28
+ false: '',
29
+ },
30
+ },
31
+ defaultVariants: {
32
+ variant: 'base',
33
+ size: 'sm',
34
+ underline: false,
35
+ },
36
+ },
37
+ );
38
+
39
+ const iconSizeMap = {
40
+ xs: 14,
41
+ sm: 14,
42
+ md: 16,
43
+ xl: 20,
44
+ } as const;
45
+
46
+ export function ButtonLink({
47
+ className,
48
+ variant,
49
+ size = 'sm',
50
+ underline,
51
+ asChild = false,
52
+ children,
53
+ iconLeft,
54
+ iconRight,
55
+ ...props
56
+ }: ComponentProps<'a'> &
57
+ VariantProps<typeof buttonLinkVariants> & {
58
+ asChild?: boolean;
59
+ iconLeft?: IconName;
60
+ iconRight?: IconName;
61
+ }) {
62
+ const Comp = asChild ? Slot : 'a';
63
+ const iconSize = iconSizeMap[size as keyof typeof iconSizeMap];
64
+
65
+ return (
66
+ <Comp
67
+ data-slot="button-link"
68
+ className={cn(buttonLinkVariants({variant, size, underline, className}))}
69
+ {...props}
70
+ >
71
+ {iconLeft && <Icon name={iconLeft} size={iconSize} />}
72
+ {children}
73
+ {iconRight && <Icon name={iconRight} size={iconSize} />}
74
+ </Comp>
75
+ );
76
+ }
@@ -6,6 +6,7 @@ const variantOptions = [
6
6
  'primary',
7
7
  'secondary',
8
8
  'danger',
9
+ 'success',
9
10
  'transparent',
10
11
  'transparentMuted',
11
12
  ] as const;
@@ -48,7 +49,6 @@ export const Variants: Story = {
48
49
  <th>{size}</th>
49
50
  <th>Default</th>
50
51
  <th>Hover</th>
51
- <th>Active</th>
52
52
  <th>Focus</th>
53
53
  <th>Disabled</th>
54
54
  </tr>
@@ -71,11 +71,6 @@ export const Variants: Story = {
71
71
  Click me
72
72
  </Button>
73
73
  </td>
74
- <td>
75
- <Button {...args} variant={variant} className="active" size={size}>
76
- Click me
77
- </Button>
78
- </td>
79
74
  <td>
80
75
  <Button {...args} variant={variant} className="focus" size={size}>
81
76
  Click me
@@ -98,7 +93,6 @@ export const Variants: Story = {
98
93
  Variants.parameters = {
99
94
  pseudo: {
100
95
  hover: '.hover',
101
- active: '.active',
102
96
  focusVisible: '.focus',
103
97
  },
104
98
  };
@@ -1,8 +1,8 @@
1
1
  import {Slot} from '@radix-ui/react-slot';
2
2
  import {cva, type VariantProps} from 'class-variance-authority';
3
+ import {Icon, type IconName} from 'components/icon';
3
4
  import type {ComponentProps} from 'react';
4
5
  import {cn} from 'utils/cn';
5
- import {Icon, type IconName} from './icon/icon';
6
6
 
7
7
  export const buttonVariants = cva(
8
8
  'rounded-6 inline-flex items-center justify-center whitespace-nowrap transition-colors disabled:pointer-events-none shrink-0 outline-none',
@@ -15,18 +15,20 @@ export const buttonVariants = cva(
15
15
  'bg-background-button-neutral-default text-foreground-neutral-base shadow-button-neutral hover:bg-background-button-neutral-hover active:bg-background-button-neutral-pressed disabled:bg-background-neutral-disabled focus-visible:shadow-button-neutral-focus disabled:text-foreground-neutral-disabled disabled:shadow-none',
16
16
  danger:
17
17
  'bg-background-button-danger-default text-foreground-neutral-on-color shadow-button-danger hover:bg-background-button-danger-hover active:bg-background-button-danger-pressed focus-visible:shadow-button-danger-focus disabled:bg-background-neutral-disabled disabled:text-foreground-neutral-disabled disabled:shadow-none',
18
+ success:
19
+ 'bg-background-button-success-default text-foreground-neutral-on-color shadow-button-success hover:bg-background-button-success-hover active:bg-background-button-success-pressed focus-visible:shadow-button-success-focus disabled:bg-background-neutral-disabled disabled:text-foreground-neutral-disabled disabled:shadow-none',
18
20
  transparent:
19
21
  'bg-background-button-transparent-default text-foreground-neutral-base hover:bg-background-button-transparent-hover active:bg-background-button-transparent-pressed focus-visible:shadow-button-neutral-focus disabled:text-foreground-neutral-disabled',
20
22
  transparentMuted:
21
23
  'bg-background-button-transparent-default text-foreground-neutral-muted hover:bg-background-button-transparent-hover active:bg-background-button-transparent-pressed focus-visible:shadow-button-neutral-focus disabled:text-foreground-neutral-disabled',
22
24
  },
23
25
  size: {
24
- '2xs': 'px-6 text-xs gap-4',
25
- xs: 'px-6 py-2 text-xs gap-4',
26
- sm: 'px-8 py-4 text-sm gap-6',
27
- md: 'px-10 py-6 text-md gap-8',
28
- lg: 'px-12 py-8 text-lg gap-8',
29
- xl: 'px-12 py-10 text-xl gap-10',
26
+ '2xs': 'h-20 px-6 text-xs gap-4',
27
+ xs: 'h-24 px-6 text-xs gap-4',
28
+ sm: 'h-28 px-8 text-sm gap-6',
29
+ md: 'h-32 px-10 text-md gap-8',
30
+ lg: 'h-36 px-12 text-lg gap-8',
31
+ xl: 'h-40 px-12 text-xl gap-10',
30
32
  },
31
33
  },
32
34
  defaultVariants: {
@@ -0,0 +1,182 @@
1
+ import type {Meta, StoryObj} from '@storybook/react';
2
+ import {Code} from 'components/typography';
3
+ import {IconButton} from './icon-button';
4
+
5
+ const variantOptions = ['primary', 'transparent'] as const;
6
+ const sizeOptions = ['2xs', 'xs', 'sm', 'md', 'lg', 'xl'] as const;
7
+ const radiusOptions = ['rounded', 'full'] as const;
8
+
9
+ const meta = {
10
+ title: 'Components/Button/IconButton',
11
+ component: IconButton,
12
+ tags: ['autodocs'],
13
+ argTypes: {
14
+ variant: {
15
+ control: 'select',
16
+ options: variantOptions,
17
+ },
18
+ size: {
19
+ control: 'select',
20
+ options: sizeOptions,
21
+ },
22
+ radius: {
23
+ control: 'select',
24
+ options: radiusOptions,
25
+ },
26
+ muted: {control: 'boolean'},
27
+ asChild: {control: 'boolean'},
28
+ },
29
+ args: {
30
+ icon: 'addLine',
31
+ variant: 'primary',
32
+ size: 'md',
33
+ radius: 'rounded',
34
+ muted: false,
35
+ },
36
+ } satisfies Meta<typeof IconButton>;
37
+
38
+ export default meta;
39
+ type Story = StoryObj<typeof meta>;
40
+
41
+ export const Default: Story = {};
42
+
43
+ export const Variants: Story = {
44
+ render: (args) => (
45
+ <div className="flex flex-col gap-32">
46
+ {sizeOptions.map((size) => (
47
+ <div key={size} className="flex flex-col gap-16">
48
+ <Code variant="label" className="text-foreground-neutral-subtle">
49
+ Size: {size}
50
+ </Code>
51
+ {radiusOptions.map((radius) => (
52
+ <table
53
+ key={radius}
54
+ className="w-fit border-separate border-spacing-x-32 border-spacing-y-16"
55
+ >
56
+ <thead>
57
+ <tr>
58
+ <th>{radius}</th>
59
+ <th>Default</th>
60
+ <th>Hover</th>
61
+ <th>Focus</th>
62
+ <th>Disabled</th>
63
+ </tr>
64
+ </thead>
65
+ <tbody>
66
+ {variantOptions.map((variant) => (
67
+ <tr key={variant}>
68
+ <td>
69
+ <Code variant="label" className="text-foreground-neutral-subtle">
70
+ {variant}
71
+ </Code>
72
+ </td>
73
+ <td>
74
+ <IconButton
75
+ {...args}
76
+ icon="addLine"
77
+ aria-label="Add"
78
+ variant={variant}
79
+ size={size}
80
+ radius={radius}
81
+ />
82
+ </td>
83
+ <td>
84
+ <IconButton
85
+ {...args}
86
+ icon="addLine"
87
+ aria-label="Add"
88
+ variant={variant}
89
+ className="hover"
90
+ size={size}
91
+ radius={radius}
92
+ />
93
+ </td>
94
+ <td>
95
+ <IconButton
96
+ {...args}
97
+ icon="addLine"
98
+ aria-label="Add"
99
+ variant={variant}
100
+ className="focus"
101
+ size={size}
102
+ radius={radius}
103
+ />
104
+ </td>
105
+ <td>
106
+ <IconButton
107
+ {...args}
108
+ icon="addLine"
109
+ aria-label="Add"
110
+ variant={variant}
111
+ disabled
112
+ size={size}
113
+ radius={radius}
114
+ />
115
+ </td>
116
+ </tr>
117
+ ))}
118
+ </tbody>
119
+ </table>
120
+ ))}
121
+ </div>
122
+ ))}
123
+ </div>
124
+ ),
125
+ };
126
+
127
+ Variants.parameters = {
128
+ pseudo: {
129
+ hover: '.hover',
130
+ focusVisible: '.focus',
131
+ },
132
+ };
133
+
134
+ export const Muted: Story = {
135
+ render: (args) => (
136
+ <div className="flex flex-col gap-16">
137
+ <div className="flex gap-16 items-center">
138
+ <Code variant="label">Normal:</Code>
139
+ <IconButton {...args} icon="addLine" aria-label="Add" />
140
+ <IconButton {...args} icon="addLine" aria-label="Add" variant="transparent" />
141
+ </div>
142
+ <div className="flex gap-16 items-center">
143
+ <Code variant="label">Muted:</Code>
144
+ <IconButton {...args} icon="addLine" aria-label="Add" muted />
145
+ <IconButton {...args} icon="addLine" aria-label="Add" variant="transparent" muted />
146
+ </div>
147
+ </div>
148
+ ),
149
+ };
150
+
151
+ export const Sizes: Story = {
152
+ render: ({children: _children, ...args}) => (
153
+ <div className="flex flex-col gap-16">
154
+ <div className="flex gap-16 items-center">
155
+ <Code variant="label">Rounded:</Code>
156
+ {sizeOptions.map((size) => (
157
+ <IconButton
158
+ {...args}
159
+ key={size}
160
+ icon="addLine"
161
+ aria-label="Add"
162
+ size={size}
163
+ radius="rounded"
164
+ />
165
+ ))}
166
+ </div>
167
+ <div className="flex gap-16 items-center">
168
+ <Code variant="label">Full:</Code>
169
+ {sizeOptions.map((size) => (
170
+ <IconButton
171
+ {...args}
172
+ key={size}
173
+ icon="addLine"
174
+ aria-label="Add"
175
+ size={size}
176
+ radius="full"
177
+ />
178
+ ))}
179
+ </div>
180
+ </div>
181
+ ),
182
+ };