@scalepad/ui 0.1.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 (273) hide show
  1. package/.ai/rules/date-handling.md +39 -0
  2. package/.ai/rules/figma-design-system.md +372 -0
  3. package/.ai/rules/figma-lm-design-system-keys.md +680 -0
  4. package/.ai/rules/file-extensions.md +13 -0
  5. package/.ai/rules/modal-confirmation-mutation.md +56 -0
  6. package/.ai/rules/react-hooks.md +29 -0
  7. package/.ai/rules/styling.md +83 -0
  8. package/AGENTS.md +37 -0
  9. package/README.md +125 -0
  10. package/figma.config.json +9 -0
  11. package/package.json +127 -0
  12. package/scripts/install-ai-rules.mjs +136 -0
  13. package/src/ThemeProvider.tsx +57 -0
  14. package/src/charts.ts +32 -0
  15. package/src/components/ActionCard/ActionCard.css.ts +60 -0
  16. package/src/components/ActionCard/ActionCard.tsx +154 -0
  17. package/src/components/ActionCard/index.ts +2 -0
  18. package/src/components/Anchor/Anchor.tsx +47 -0
  19. package/src/components/Anchor/index.ts +2 -0
  20. package/src/components/AppliedFiltersManagerBar/AppliedFiltersManagerBar.tsx +105 -0
  21. package/src/components/AppliedFiltersManagerBar/FilterBadge.css.ts +23 -0
  22. package/src/components/AppliedFiltersManagerBar/FilterBadge.tsx +50 -0
  23. package/src/components/AppliedFiltersManagerBar/index.ts +5 -0
  24. package/src/components/Badge/Badge.css.ts +72 -0
  25. package/src/components/Badge/Badge.figma.tsx +43 -0
  26. package/src/components/Badge/Badge.tsx +159 -0
  27. package/src/components/Badge/index.ts +2 -0
  28. package/src/components/BreadCrumb/BreadCrumb.tsx +62 -0
  29. package/src/components/BreadCrumb/index.ts +2 -0
  30. package/src/components/BulkActionBar/BulkActionBar.css.ts +26 -0
  31. package/src/components/BulkActionBar/BulkActionBar.tsx +164 -0
  32. package/src/components/BulkActionBar/index.ts +2 -0
  33. package/src/components/Button/Button.css.ts +272 -0
  34. package/src/components/Button/Button.figma.tsx +74 -0
  35. package/src/components/Button/Button.tsx +84 -0
  36. package/src/components/Button/index.ts +2 -0
  37. package/src/components/Charts/ChartTooltip.figma.tsx +33 -0
  38. package/src/components/Charts/ChartTooltip.tsx +101 -0
  39. package/src/components/Charts/MiniBarSparkline.tsx +75 -0
  40. package/src/components/Charts/StackedPatternBarChart.tsx +494 -0
  41. package/src/components/Charts/TrendAreaChart.css.ts +23 -0
  42. package/src/components/Charts/TrendAreaChart.tsx +210 -0
  43. package/src/components/Charts/index.ts +12 -0
  44. package/src/components/CodePanel/CodePanel.css.ts +113 -0
  45. package/src/components/CodePanel/CodePanel.tsx +121 -0
  46. package/src/components/CodePanel/index.ts +2 -0
  47. package/src/components/CommentComposer/CommentComposer.css.ts +60 -0
  48. package/src/components/CommentComposer/CommentComposer.tsx +181 -0
  49. package/src/components/CommentComposer/index.ts +2 -0
  50. package/src/components/ConfirmationModal/ConfirmationModal.tsx +149 -0
  51. package/src/components/ConfirmationModal/index.ts +2 -0
  52. package/src/components/ConfirmationTooltip/ConfirmationTooltip.tsx +132 -0
  53. package/src/components/ConfirmationTooltip/index.ts +2 -0
  54. package/src/components/DataDialog.figma.tsx +33 -0
  55. package/src/components/DataDialog.tsx +46 -0
  56. package/src/components/DataTable/DataTable.tsx +1042 -0
  57. package/src/components/DataTable/RowExpandToggle.tsx +105 -0
  58. package/src/components/DataTable/RowGroupHeader.tsx +190 -0
  59. package/src/components/DataTable/createActionsColumn.tsx +86 -0
  60. package/src/components/DataTable/index.ts +25 -0
  61. package/src/components/DatePicker/CustomRangePicker.tsx +59 -0
  62. package/src/components/DatePicker/DateInput.tsx +329 -0
  63. package/src/components/DatePicker/DateNavigator.tsx +486 -0
  64. package/src/components/DatePicker/DatePicker.tsx +242 -0
  65. package/src/components/DatePicker/MonthlyRangePicker.tsx +231 -0
  66. package/src/components/DatePicker/QuarterlyRangePicker.tsx +224 -0
  67. package/src/components/DatePicker/QuickPicksSidebar.tsx +242 -0
  68. package/src/components/DatePicker/YearlyRangePicker.tsx +171 -0
  69. package/src/components/DatePicker/index.ts +7 -0
  70. package/src/components/DatePicker/types.ts +12 -0
  71. package/src/components/DesignSystemPrimitives/FluidGrid.tsx +44 -0
  72. package/src/components/DesignSystemPrimitives/InteractivePrimitives.tsx +177 -0
  73. package/src/components/DesignSystemPrimitives/LayoutPrimitives.tsx +220 -0
  74. package/src/components/DesignSystemPrimitives/LayoutPrimitives.types.tsx +15 -0
  75. package/src/components/DesignSystemPrimitives/SurfacePrimitives.tsx +46 -0
  76. package/src/components/DesignSystemPrimitives/index.ts +55 -0
  77. package/src/components/Details/Details.css.ts +74 -0
  78. package/src/components/Details/Details.tsx +140 -0
  79. package/src/components/Details/index.ts +2 -0
  80. package/src/components/DownloadCard/DownloadCard.css.ts +22 -0
  81. package/src/components/DownloadCard/DownloadCard.tsx +63 -0
  82. package/src/components/DownloadCard/index.ts +2 -0
  83. package/src/components/Drawer/Drawer.css.ts +32 -0
  84. package/src/components/Drawer/Drawer.tsx +236 -0
  85. package/src/components/Drawer/hooks/useDetailDrawer.ts +61 -0
  86. package/src/components/Drawer/hooks/useDetailDrawerNavigation.ts +125 -0
  87. package/src/components/Drawer/hooks/useDetailDrawerNavigationContext.ts +66 -0
  88. package/src/components/EditableRichText/EditableRichText.css.ts +72 -0
  89. package/src/components/EditableRichText/EditableRichText.tsx +324 -0
  90. package/src/components/EditableRichText/index.ts +2 -0
  91. package/src/components/EditableSelect/EditableSelect.css.ts +62 -0
  92. package/src/components/EditableSelect/EditableSelect.tsx +224 -0
  93. package/src/components/EditableSelect/index.ts +2 -0
  94. package/src/components/EditableText/EditableText.tsx +377 -0
  95. package/src/components/EditableText/index.ts +2 -0
  96. package/src/components/EmptyState/EmptyState.figma.tsx +33 -0
  97. package/src/components/EmptyState/EmptyState.tsx +230 -0
  98. package/src/components/EmptyState/index.ts +2 -0
  99. package/src/components/ErrorBoundary.tsx +135 -0
  100. package/src/components/ErrorState/ErrorState.tsx +197 -0
  101. package/src/components/ErrorState/index.ts +2 -0
  102. package/src/components/FeatureCard.tsx +42 -0
  103. package/src/components/FilterMenu/FilterMenu.figma.tsx +30 -0
  104. package/src/components/FilterMenu/FilterMenu.tsx +198 -0
  105. package/src/components/FilterMenu/FilterSubMenuTypes/BooleanFilterSubmenu.tsx +46 -0
  106. package/src/components/FilterMenu/FilterSubMenuTypes/SearchableFilterSubmenu.tsx +239 -0
  107. package/src/components/FilterMenu/FilterSubMenuTypes/index.ts +8 -0
  108. package/src/components/FilterMenu/defaultFilterSchemas.ts +63 -0
  109. package/src/components/FilterMenu/helpers.ts +115 -0
  110. package/src/components/FilterMenu/index.ts +35 -0
  111. package/src/components/FilterMenu/types.ts +101 -0
  112. package/src/components/IconButton/IconButton.css.ts +272 -0
  113. package/src/components/IconButton/IconButton.figma.tsx +47 -0
  114. package/src/components/IconButton/IconButton.tsx +72 -0
  115. package/src/components/IconButton/README.md +230 -0
  116. package/src/components/IconButton/index.ts +2 -0
  117. package/src/components/InfiniteScrollSentinel.tsx +86 -0
  118. package/src/components/InfiniteScrollTrigger.tsx +78 -0
  119. package/src/components/InfoCard.figma.tsx +47 -0
  120. package/src/components/InfoCard.tsx +216 -0
  121. package/src/components/KbdHint/KbdHint.tsx +23 -0
  122. package/src/components/KbdHint/index.ts +2 -0
  123. package/src/components/LabeledField/LabeledField.tsx +21 -0
  124. package/src/components/LabeledField/index.ts +2 -0
  125. package/src/components/LookupSelect/LookupSelect.css.ts +149 -0
  126. package/src/components/LookupSelect/LookupSelect.tsx +325 -0
  127. package/src/components/LookupSelect/index.ts +2 -0
  128. package/src/components/Menu/Menu.css.ts +89 -0
  129. package/src/components/Menu/Menu.tsx +105 -0
  130. package/src/components/Menu/index.ts +2 -0
  131. package/src/components/MessageBox/MessageBox.tsx +168 -0
  132. package/src/components/MessageBox/index.ts +2 -0
  133. package/src/components/MetricDisplay/MetricDisplay.tsx +55 -0
  134. package/src/components/MetricDisplay/index.ts +1 -0
  135. package/src/components/MultiSelect/MultiSelect.tsx +278 -0
  136. package/src/components/MultiSelect/index.ts +2 -0
  137. package/src/components/Notifications/Notifications.tsx +12 -0
  138. package/src/components/Notifications/README.md +93 -0
  139. package/src/components/Notifications/index.ts +4 -0
  140. package/src/components/Notifications/showToast.tsx +100 -0
  141. package/src/components/PropertyRow/PropertyRow.tsx +96 -0
  142. package/src/components/PropertyRow/index.ts +2 -0
  143. package/src/components/RadioTile/RadioTile.tsx +253 -0
  144. package/src/components/RadioTile/index.ts +2 -0
  145. package/src/components/RichText/FormattingToolbar.css.ts +69 -0
  146. package/src/components/RichText/FormattingToolbar.tsx +112 -0
  147. package/src/components/RichText/RichTextInline.css.ts +54 -0
  148. package/src/components/RichText/RichTextInline.tsx +318 -0
  149. package/src/components/RichText/formattingCommands.ts +181 -0
  150. package/src/components/RichText/formattingTypes.ts +34 -0
  151. package/src/components/RichText/index.ts +49 -0
  152. package/src/components/RichText/richTextExtensions.ts +111 -0
  153. package/src/components/RichText/richTextHelpers.ts +65 -0
  154. package/src/components/RichText/richTextImage.ts +253 -0
  155. package/src/components/RichText/richTextImageHandlers.ts +244 -0
  156. package/src/components/RichText/richTextProse.css.ts +261 -0
  157. package/src/components/RichTextEditor/RichTextEditor.css.ts +82 -0
  158. package/src/components/RichTextEditor/RichTextEditor.tsx +204 -0
  159. package/src/components/RichTextEditor/index.ts +2 -0
  160. package/src/components/RichTextView/RichTextView.css.ts +11 -0
  161. package/src/components/RichTextView/RichTextView.tsx +114 -0
  162. package/src/components/RichTextView/index.ts +2 -0
  163. package/src/components/Schedule/Schedule.tsx +35 -0
  164. package/src/components/SchedulePicker/SchedulePicker.css.ts +42 -0
  165. package/src/components/SchedulePicker/SchedulePicker.tsx +130 -0
  166. package/src/components/SchedulePicker/index.ts +2 -0
  167. package/src/components/SearchableList/types.ts +30 -0
  168. package/src/components/SearchableSubMenu/SearchableSubMenu.css.ts +25 -0
  169. package/src/components/SearchableSubMenu/SearchableSubMenu.tsx +139 -0
  170. package/src/components/SearchableSubMenu/index.ts +2 -0
  171. package/src/components/Select/README.md +114 -0
  172. package/src/components/Select/Select.css.ts +110 -0
  173. package/src/components/Select/Select.tsx +133 -0
  174. package/src/components/Select/index.ts +2 -0
  175. package/src/components/SelectCreatable/SelectCreatable.css.ts +16 -0
  176. package/src/components/SelectCreatable/SelectCreatable.tsx +203 -0
  177. package/src/components/SelectCreatable/index.ts +2 -0
  178. package/src/components/SettingsCard/SettingsCard.tsx +98 -0
  179. package/src/components/SettingsCard/index.ts +2 -0
  180. package/src/components/Sidebar/Sidebar.css.ts +91 -0
  181. package/src/components/Sidebar/Sidebar.tsx +129 -0
  182. package/src/components/Sidebar/index.ts +5 -0
  183. package/src/components/SimpleList/SimpleList.css.ts +12 -0
  184. package/src/components/SimpleList/SimpleList.tsx +44 -0
  185. package/src/components/SimpleList/index.ts +2 -0
  186. package/src/components/SimpleTable/SimpleTable.tsx +296 -0
  187. package/src/components/SimpleTable/index.ts +2 -0
  188. package/src/components/SlashRichTextEditor/SelectionBubbleMenu.css.ts +62 -0
  189. package/src/components/SlashRichTextEditor/SelectionBubbleMenu.tsx +85 -0
  190. package/src/components/SlashRichTextEditor/SlashCommandMenu.css.ts +124 -0
  191. package/src/components/SlashRichTextEditor/SlashCommandMenu.tsx +168 -0
  192. package/src/components/SlashRichTextEditor/SlashRichTextEditor.css.ts +81 -0
  193. package/src/components/SlashRichTextEditor/SlashRichTextEditor.tsx +538 -0
  194. package/src/components/SlashRichTextEditor/SlashSuggestionExtension.ts +48 -0
  195. package/src/components/SlashRichTextEditor/index.ts +13 -0
  196. package/src/components/SlashRichTextEditor/types.ts +48 -0
  197. package/src/components/StatCard/StatCard.css.ts +70 -0
  198. package/src/components/StatCard/StatCard.tsx +201 -0
  199. package/src/components/StatCard/index.ts +1 -0
  200. package/src/components/StatusBadge/StatusBadge.tsx +70 -0
  201. package/src/components/StatusBadge/index.ts +2 -0
  202. package/src/components/StatusIndicator/StatusIndicator.tsx +67 -0
  203. package/src/components/StatusIndicator/index.ts +6 -0
  204. package/src/components/SubNavigation/SubNavigation.css.ts +72 -0
  205. package/src/components/SubNavigation/SubNavigation.tsx +104 -0
  206. package/src/components/SubNavigation/index.ts +2 -0
  207. package/src/components/SuspenseLoader.tsx +22 -0
  208. package/src/components/Table/SortableColumnHeader.tsx +99 -0
  209. package/src/components/Table/TableSkeletonRows.figma.tsx +22 -0
  210. package/src/components/Table/TableSkeletonRows.tsx +113 -0
  211. package/src/components/Table/index.ts +9 -0
  212. package/src/components/TableActionsMenu.tsx +58 -0
  213. package/src/components/TableCard.tsx +29 -0
  214. package/src/components/TableContainer/TableContainer.tsx +86 -0
  215. package/src/components/TableContainer/index.ts +2 -0
  216. package/src/components/TableControlBar/TableControlBar.tsx +156 -0
  217. package/src/components/TableControlBar/TableSelectionButton.tsx +57 -0
  218. package/src/components/TableControlBar/index.ts +13 -0
  219. package/src/components/TableControlBar/useTableControlBar.tsx +314 -0
  220. package/src/components/TableSelection/TableSelection.tsx +43 -0
  221. package/src/components/TableSelection/index.ts +5 -0
  222. package/src/components/Tabs/README.md +76 -0
  223. package/src/components/Tabs/Tabs.css.ts +54 -0
  224. package/src/components/Tabs/Tabs.figma.tsx +47 -0
  225. package/src/components/Tabs/Tabs.tsx +96 -0
  226. package/src/components/Tabs/index.ts +8 -0
  227. package/src/components/TextInput/README.md +98 -0
  228. package/src/components/TextInput/SearchTextInput.figma.tsx +22 -0
  229. package/src/components/TextInput/SearchTextInput.tsx +150 -0
  230. package/src/components/TextInput/TextInput.figma.tsx +44 -0
  231. package/src/components/TextInput/TextInput.tsx +42 -0
  232. package/src/components/TextInput/index.ts +4 -0
  233. package/src/components/ThemeSwitcher.figma.tsx +28 -0
  234. package/src/components/ThemeSwitcher.tsx +69 -0
  235. package/src/components/TrendBadge/TrendBadge.tsx +76 -0
  236. package/src/components/TrendBadge/index.ts +2 -0
  237. package/src/components/TruncatedText.tsx +115 -0
  238. package/src/components/Typography/Text.tsx +74 -0
  239. package/src/components/Typography/Title.tsx +100 -0
  240. package/src/components/Typography/index.ts +4 -0
  241. package/src/geist-fonts.ts +48 -0
  242. package/src/hooks/index.ts +31 -0
  243. package/src/hooks/useFilters.ts +152 -0
  244. package/src/hooks/useInfiniteScroll.ts +62 -0
  245. package/src/hooks/usePlatform.ts +33 -0
  246. package/src/hooks/useServerTable.ts +495 -0
  247. package/src/hooks/useTableSelection.ts +102 -0
  248. package/src/hooks/useTableSort.ts +259 -0
  249. package/src/index.ts +483 -0
  250. package/src/mantine.ts +25 -0
  251. package/src/theme/mantineVars.ts +12 -0
  252. package/src/theme/themeContract.css.ts +131 -0
  253. package/src/theme/themeVars.ts +31 -0
  254. package/src/theme.ts +168 -0
  255. package/src/tokens/color-types.ts +107 -0
  256. package/src/tokens/colors.ts +243 -0
  257. package/src/tokens/index.ts +14 -0
  258. package/src/tokens/radius.ts +17 -0
  259. package/src/tokens/semantic-colors.ts +224 -0
  260. package/src/tokens/semantic-tokens-css.ts +53 -0
  261. package/src/tokens/shadows.ts +11 -0
  262. package/src/tokens/spacing.ts +20 -0
  263. package/src/tokens/text-styles.ts +179 -0
  264. package/src/tokens/typography.ts +40 -0
  265. package/src/tokens/zIndex.ts +27 -0
  266. package/src/types/mantine-theme.d.ts +17 -0
  267. package/src/types/tanstack-table.d.ts +22 -0
  268. package/src/utils/avatar.ts +150 -0
  269. package/src/utils/chartHelpers.ts +53 -0
  270. package/src/utils/color-props.ts +77 -0
  271. package/src/utils/createDesignComponent.tsx +104 -0
  272. package/src/utils/nestFlatRows.ts +111 -0
  273. package/src/utils/withStaticComponents.ts +6 -0
@@ -0,0 +1,272 @@
1
+ /**
2
+ * IconButton component styles – vanilla-extract with semantic design tokens
3
+ */
4
+
5
+ import { style } from '@vanilla-extract/css';
6
+
7
+ import { mantineVars } from '../../theme/mantineVars';
8
+ import { tokens } from '../../theme/themeContract.css';
9
+
10
+ const focusRing = {
11
+ outline: `2px solid ${tokens.color.stroke.focusStrong}`,
12
+ outlineOffset: 3,
13
+ };
14
+
15
+ const outlineBase = style({
16
+ backgroundColor: tokens.color.background.default,
17
+ border: `1px solid ${tokens.color.stroke.default}`,
18
+ color: tokens.color.text.default,
19
+ transition: 'all 150ms ease',
20
+ selectors: {
21
+ '&:focus-visible': focusRing,
22
+ '&:hover': {
23
+ backgroundColor: tokens.color.background.primaryLight,
24
+ borderColor: tokens.color.stroke.default,
25
+ color: tokens.color.text.primaryDefault,
26
+ },
27
+ '&:active': {
28
+ backgroundColor: tokens.color.background.primaryLight,
29
+ color: tokens.color.text.primaryDefault,
30
+ },
31
+ '&:disabled': {
32
+ backgroundColor: tokens.color.background.default,
33
+ borderColor: tokens.color.stroke.default,
34
+ color: tokens.color.text.disabledDefault,
35
+ opacity: 0.6,
36
+ cursor: 'not-allowed',
37
+ boxShadow: 'none',
38
+ },
39
+ [`${mantineVars.darkSelector} &`]: {
40
+ backgroundColor: tokens.color.background.default,
41
+ borderColor: tokens.color.stroke.default,
42
+ color: tokens.color.text.default,
43
+ },
44
+ [`${mantineVars.darkSelector} &:hover`]: {
45
+ backgroundColor: tokens.color.background.primaryLight,
46
+ borderColor: tokens.color.stroke.default,
47
+ },
48
+ [`${mantineVars.darkSelector} &:active`]: {
49
+ backgroundColor: tokens.color.background.primaryLight,
50
+ },
51
+ [`${mantineVars.darkSelector} &:disabled`]: {
52
+ backgroundColor: tokens.color.background.default,
53
+ borderColor: tokens.color.stroke.default,
54
+ color: tokens.color.text.disabledDefault,
55
+ opacity: 0.6,
56
+ },
57
+ },
58
+ });
59
+
60
+ export const outlineIconButton = outlineBase;
61
+
62
+ export const outlineInverseIconButton = style({
63
+ backgroundColor: tokens.color.background.transparent,
64
+ borderColor: tokens.color.stroke.inverse,
65
+ color: tokens.color.text.inverse,
66
+ transition: 'all 150ms ease',
67
+ selectors: {
68
+ '&:focus-visible': focusRing,
69
+ '&:hover': {
70
+ backgroundColor: tokens.color.background.primaryLight,
71
+ borderColor: tokens.color.stroke.inverse,
72
+ color: tokens.color.text.primaryDefault,
73
+ },
74
+ '&:disabled': {
75
+ borderColor: tokens.color.stroke.inverse,
76
+ color: tokens.color.text.disabledDefault,
77
+ opacity: 0.6,
78
+ cursor: 'not-allowed',
79
+ },
80
+ },
81
+ });
82
+
83
+ export const secondaryIconButton = style({
84
+ backgroundColor: tokens.color.background.subduedLight,
85
+ color: tokens.color.text.default,
86
+ transition: 'all 150ms ease',
87
+ border: `1px solid ${tokens.color.stroke.default}`,
88
+ selectors: {
89
+ '&:focus-visible': focusRing,
90
+ '&:hover': {
91
+ backgroundColor: tokens.color.background.primaryLight,
92
+ color: tokens.color.text.primaryDefault,
93
+ },
94
+ '&:disabled': {
95
+ backgroundColor: tokens.color.background.subduedLight,
96
+ color: tokens.color.text.disabledDefault,
97
+ borderColor: tokens.color.stroke.default,
98
+ opacity: 0.6,
99
+ cursor: 'not-allowed',
100
+ },
101
+ [`${mantineVars.darkSelector} &`]: {
102
+ backgroundColor: tokens.color.background.subduedLight,
103
+ color: tokens.color.text.default,
104
+ borderColor: tokens.color.stroke.default,
105
+ },
106
+ [`${mantineVars.darkSelector} &:hover`]: {
107
+ backgroundColor: tokens.color.background.primaryLight,
108
+ color: tokens.color.text.primaryDefault,
109
+ },
110
+ [`${mantineVars.darkSelector} &:disabled`]: {
111
+ backgroundColor: tokens.color.background.subduedLight,
112
+ color: tokens.color.text.disabledDefault,
113
+ borderColor: tokens.color.stroke.default,
114
+ opacity: 0.6,
115
+ },
116
+ },
117
+ });
118
+
119
+ export const tertiaryIconButton = style({
120
+ backgroundColor: tokens.color.background.primaryLight,
121
+ color: tokens.color.text.primaryDefault,
122
+ transition: 'all 150ms ease',
123
+ border: 'none',
124
+ selectors: {
125
+ '&:focus-visible': focusRing,
126
+ '&:hover': {
127
+ backgroundColor: tokens.color.background.primaryLightHover,
128
+ },
129
+ '&:disabled': {
130
+ backgroundColor: tokens.color.background.primaryLight,
131
+ color: tokens.color.text.disabledDefault,
132
+ opacity: 0.6,
133
+ cursor: 'not-allowed',
134
+ },
135
+ [`${mantineVars.darkSelector} &`]: {
136
+ backgroundColor: tokens.color.background.primaryLight,
137
+ color: tokens.color.text.primaryDefault,
138
+ },
139
+ [`${mantineVars.darkSelector} &:hover`]: {
140
+ backgroundColor: tokens.color.background.primaryLightHover,
141
+ },
142
+ [`${mantineVars.darkSelector} &:disabled`]: {
143
+ backgroundColor: tokens.color.background.primaryLight,
144
+ color: tokens.color.text.disabledDefault,
145
+ opacity: 0.6,
146
+ },
147
+ },
148
+ });
149
+
150
+ export const destructiveIconButton = style({
151
+ backgroundColor: tokens.color.background.dangerFilled,
152
+ color: tokens.color.text.inverse,
153
+ transition: 'all 150ms ease',
154
+ border: 'none',
155
+ selectors: {
156
+ '&:focus-visible': focusRing,
157
+ '&:hover': {
158
+ backgroundColor: tokens.color.background.dangerFilledHover,
159
+ },
160
+ '&:disabled': {
161
+ backgroundColor: tokens.color.background.disabledDefault,
162
+ color: tokens.color.text.disabledDefault,
163
+ opacity: 0.6,
164
+ cursor: 'not-allowed',
165
+ },
166
+ [`${mantineVars.darkSelector} &`]: {
167
+ backgroundColor: tokens.color.background.dangerFilled,
168
+ color: tokens.color.text.inverse,
169
+ },
170
+ [`${mantineVars.darkSelector} &:hover`]: {
171
+ backgroundColor: tokens.color.background.dangerFilledHover,
172
+ },
173
+ [`${mantineVars.darkSelector} &:disabled`]: {
174
+ backgroundColor: tokens.color.background.disabledDefault,
175
+ color: tokens.color.text.disabledDefault,
176
+ opacity: 0.6,
177
+ },
178
+ },
179
+ });
180
+
181
+ export const ghostIconButton = style({
182
+ backgroundColor: 'transparent',
183
+ color: tokens.color.text.default,
184
+ transition: 'all 150ms ease',
185
+ border: 'none',
186
+ selectors: {
187
+ '&:focus-visible': focusRing,
188
+ '&:hover': {
189
+ backgroundColor: tokens.color.background.primaryLight,
190
+ color: tokens.color.text.primaryDefault,
191
+ },
192
+ '&:disabled': {
193
+ backgroundColor: 'transparent',
194
+ color: tokens.color.text.disabledDefault,
195
+ opacity: 0.6,
196
+ cursor: 'not-allowed',
197
+ },
198
+ [`${mantineVars.darkSelector} &`]: {
199
+ color: tokens.color.text.default,
200
+ },
201
+ [`${mantineVars.darkSelector} &:hover`]: {
202
+ backgroundColor: tokens.color.background.primaryLight,
203
+ color: tokens.color.text.primaryDefault,
204
+ },
205
+ [`${mantineVars.darkSelector} &:disabled`]: {
206
+ color: tokens.color.text.disabledDefault,
207
+ opacity: 0.6,
208
+ },
209
+ },
210
+ });
211
+
212
+ export const filledIconButton = style({
213
+ backgroundColor: tokens.color.background.primaryFilled,
214
+ color: tokens.color.text.inverse,
215
+ transition: 'all 150ms ease',
216
+ border: 'none',
217
+ selectors: {
218
+ '&:focus-visible': focusRing,
219
+ '&:hover': {
220
+ backgroundColor: tokens.color.background.primaryFilledHover,
221
+ },
222
+ '&:disabled': {
223
+ backgroundColor: tokens.color.background.disabledDefault,
224
+ color: tokens.color.text.disabledDefault,
225
+ opacity: 0.6,
226
+ cursor: 'not-allowed',
227
+ },
228
+ [`${mantineVars.darkSelector} &`]: {
229
+ backgroundColor: tokens.color.background.primaryFilled,
230
+ color: tokens.color.text.inverse,
231
+ },
232
+ [`${mantineVars.darkSelector} &:hover`]: {
233
+ backgroundColor: tokens.color.background.primaryFilledHover,
234
+ },
235
+ [`${mantineVars.darkSelector} &:disabled`]: {
236
+ backgroundColor: tokens.color.background.disabledDefault,
237
+ color: tokens.color.text.disabledDefault,
238
+ opacity: 0.6,
239
+ },
240
+ },
241
+ });
242
+
243
+ export const ghostMutedIconButton = style({
244
+ backgroundColor: tokens.color.background.transparent,
245
+ color: tokens.color.text.subduedStrong,
246
+ transition: 'all 150ms ease',
247
+ border: 'none',
248
+ selectors: {
249
+ '&:focus-visible': focusRing,
250
+ '&:hover': {
251
+ backgroundColor: tokens.color.background.primaryLight,
252
+ color: tokens.color.text.primaryDefault,
253
+ },
254
+ '&:disabled': {
255
+ backgroundColor: tokens.color.background.transparent,
256
+ color: tokens.color.text.disabledDefault,
257
+ opacity: 0.4,
258
+ cursor: 'not-allowed',
259
+ },
260
+ [`${mantineVars.darkSelector} &`]: {
261
+ color: tokens.color.text.subduedStrong,
262
+ },
263
+ [`${mantineVars.darkSelector} &:hover`]: {
264
+ backgroundColor: tokens.color.background.primaryLight,
265
+ color: tokens.color.text.primaryDefault,
266
+ },
267
+ [`${mantineVars.darkSelector} &:disabled`]: {
268
+ color: tokens.color.text.disabledDefault,
269
+ opacity: 0.4,
270
+ },
271
+ },
272
+ });
@@ -0,0 +1,47 @@
1
+ import figma from '@figma/code-connect';
2
+
3
+ import { IconButton } from './IconButton';
4
+
5
+ /**
6
+ * -- This file was auto-generated by Code Connect --
7
+ * None of your props could be automatically mapped to Figma properties.
8
+ * You should update the `props` object to include a mapping from your
9
+ * code props to Figma properties, and update the `example` function to
10
+ * return the code example you'd like to see in Figma
11
+ */
12
+
13
+ figma.connect(
14
+ IconButton,
15
+ 'https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System?node-id=9%3A775',
16
+ {
17
+ props: {
18
+ // No matching props could be found for these Figma properties:
19
+ // "leftIcon": figma.instance('⮑ Left icon'),
20
+ // "roundness": figma.enum('Roundness', {
21
+ // "Default": "default",
22
+ // "Round": "round"
23
+ // }),
24
+ // "variant": figma.enum('Variant', {
25
+ // "Primary": "primary",
26
+ // "Secondary": "secondary",
27
+ // "Outline": "outline",
28
+ // "Ghost": "ghost",
29
+ // "Ghost Muted": "ghost-muted",
30
+ // "Destructive": "destructive"
31
+ // }),
32
+ // "size": figma.enum('Size', {
33
+ // "Regular": "regular",
34
+ // "Large": "large",
35
+ // "Small": "small",
36
+ // "Mini": "mini"
37
+ // }),
38
+ // "state": figma.enum('State', {
39
+ // "Default": "default",
40
+ // "Hover & Active": "hover---active",
41
+ // "Focus": "focus",
42
+ // "Disabled": "disabled"
43
+ // })
44
+ },
45
+ example: _props => <IconButton aria-label="Add item">+</IconButton>,
46
+ },
47
+ );
@@ -0,0 +1,72 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ import {
4
+ createPolymorphicComponent,
5
+ ActionIcon as MantineActionIcon,
6
+ type ActionIconProps as MantineActionIconProps,
7
+ } from '@mantine/core';
8
+
9
+ import * as iconButtonStyles from './IconButton.css';
10
+
11
+ import type { ButtonProps } from '../Button';
12
+
13
+ /** IconButton variant: design system (matches Button) or legacy 'outline'/'filled' for backward compat */
14
+ export type IconButtonVariant = ButtonProps['variant'];
15
+
16
+ export interface IconButtonProps extends MantineActionIconProps {
17
+ /** IconButton variant from Figma design system */
18
+ variant?: IconButtonVariant;
19
+ }
20
+
21
+ const IconButtonBase = forwardRef<HTMLButtonElement, IconButtonProps>(
22
+ ({ variant = 'outline', className, ...props }, ref) => {
23
+ let mantineVariant: 'outline' | 'subtle' | 'filled' = 'outline';
24
+ let mantineColor = props.color;
25
+ let customClassName = className;
26
+
27
+ if (variant === 'primary') {
28
+ mantineVariant = 'filled';
29
+ mantineColor = 'dark';
30
+ customClassName = `${iconButtonStyles.filledIconButton}${className ? ` ${className}` : ''}`;
31
+ } else if (variant === 'secondary') {
32
+ mantineVariant = 'filled';
33
+ customClassName = `${iconButtonStyles.secondaryIconButton}${className ? ` ${className}` : ''}`;
34
+ } else if (variant === 'tertiary') {
35
+ mantineVariant = 'filled';
36
+ customClassName = `${iconButtonStyles.tertiaryIconButton}${className ? ` ${className}` : ''}`;
37
+ } else if (variant === 'destructive') {
38
+ mantineVariant = 'filled';
39
+ customClassName = `${iconButtonStyles.destructiveIconButton}${className ? ` ${className}` : ''}`;
40
+ } else if (variant === 'outline') {
41
+ mantineVariant = 'outline';
42
+ customClassName = `${iconButtonStyles.outlineIconButton}${className ? ` ${className}` : ''}`;
43
+ } else if (variant === 'outline-inverse') {
44
+ mantineVariant = 'outline';
45
+ mantineColor = 'white';
46
+ customClassName = `${iconButtonStyles.outlineInverseIconButton}${className ? ` ${className}` : ''}`;
47
+ } else if (variant === 'ghost') {
48
+ mantineVariant = 'subtle';
49
+ customClassName = `${iconButtonStyles.ghostIconButton}${className ? ` ${className}` : ''}`;
50
+ } else if (variant === 'ghost-muted') {
51
+ mantineVariant = 'subtle';
52
+ customClassName = `${iconButtonStyles.ghostMutedIconButton}${className ? ` ${className}` : ''}`;
53
+ }
54
+
55
+ return (
56
+ <MantineActionIcon
57
+ ref={ref}
58
+ variant={mantineVariant}
59
+ color={mantineColor}
60
+ className={customClassName}
61
+ {...props}
62
+ radius={props.radius ?? 'lg'}
63
+ />
64
+ );
65
+ },
66
+ );
67
+
68
+ IconButtonBase.displayName = 'IconButton';
69
+
70
+ export const IconButton = createPolymorphicComponent<'button', IconButtonProps>(
71
+ IconButtonBase,
72
+ );
@@ -0,0 +1,230 @@
1
+ # IconButton Component
2
+
3
+ A reusable icon button component that matches the Figma design system specifications. This component wraps Mantine's `ActionIcon` with custom styling that includes shadows, proper colors, hover states, and focus rings.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Shadow Support**: Includes box-shadow on outline variant (matches Figma design)
8
+ - ✅ **Design System Colors**: Uses semantic color tokens from Figma
9
+ - ✅ **Three Variants**: outline (default), ghost, and filled
10
+ - ✅ **Hover States**: Proper hover and active states
11
+ - ✅ **Focus Visible**: Keyboard navigation support with focus ring
12
+ - ✅ **Disabled State**: Proper disabled styling
13
+ - ✅ **Dark Mode**: Full dark mode support
14
+ - ✅ **Polymorphic**: Can render as different HTML elements
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import { IconButton } from '@acme/ui';
20
+ import { Copy } from 'lucide-react';
21
+
22
+ // Outline variant (default) - has shadow
23
+ <IconButton variant="outline" size="lg" radius="lg" onClick={handleCopy}>
24
+ <Copy size={16} />
25
+ </IconButton>
26
+
27
+ // Ghost variant - transparent background
28
+ <IconButton variant="ghost" size="lg" radius="lg" onClick={handleSettings}>
29
+ <Settings size={16} />
30
+ </IconButton>
31
+
32
+ // Filled variant - solid background
33
+ <IconButton variant="filled" size="lg" radius="lg" onClick={handleDelete}>
34
+ <Trash2 size={16} />
35
+ </IconButton>
36
+
37
+ // Disabled state
38
+ <IconButton variant="outline" size="lg" radius="lg" disabled>
39
+ <Copy size={16} />
40
+ </IconButton>
41
+ ```
42
+
43
+ ## Props
44
+
45
+ Inherits all props from Mantine's `ActionIconProps` plus:
46
+
47
+ | Prop | Type | Default | Description |
48
+ |------|------|---------|-------------|
49
+ | `variant` | `'outline' \| 'ghost' \| 'filled'` | `'outline'` | Visual style variant |
50
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | - | Button size |
51
+ | `radius` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | - | Border radius |
52
+ | `disabled` | `boolean` | `false` | Disabled state |
53
+
54
+ ## Variants
55
+
56
+ ### Outline (Default)
57
+
58
+ The outline variant is designed to match the Figma specifications exactly:
59
+
60
+ - White background (`--color-unofficial-outline`)
61
+ - Border with design system color (`--color-general-border`)
62
+ - **Box shadow** (`var(--mantine-shadow-sm)`) - this is what was missing before!
63
+ - Hover: Dark background with white text
64
+ - Active: Darker background
65
+
66
+ ```tsx
67
+ <IconButton variant="outline" size="lg" radius="lg">
68
+ <Copy size={16} />
69
+ </IconButton>
70
+ ```
71
+
72
+ ### Ghost
73
+
74
+ Transparent background with subtle hover state:
75
+
76
+ - Transparent background
77
+ - No border
78
+ - No shadow
79
+ - Hover: Subtle background color
80
+
81
+ ```tsx
82
+ <IconButton variant="ghost" size="lg" radius="lg">
83
+ <Settings size={16} />
84
+ </IconButton>
85
+ ```
86
+
87
+ ### Filled
88
+
89
+ Solid background for primary actions:
90
+
91
+ - Dark background (`--color-general-primary`)
92
+ - White text
93
+ - No border
94
+ - Hover: Slightly darker background
95
+
96
+ ```tsx
97
+ <IconButton variant="filled" size="lg" radius="lg">
98
+ <Heart size={16} />
99
+ </IconButton>
100
+ ```
101
+
102
+ ## Icon Sizing
103
+
104
+ Icon size should typically match the button size:
105
+
106
+ | Button Size | Icon Size |
107
+ |-------------|-----------|
108
+ | `xs` | `12px` |
109
+ | `sm` | `14px` |
110
+ | `md` | `16px` |
111
+ | `lg` | `18px` |
112
+ | `xl` | `20px` |
113
+
114
+ ```tsx
115
+ <IconButton variant="outline" size="lg" radius="lg">
116
+ <Copy size={18} /> {/* 18px for lg size */}
117
+ </IconButton>
118
+ ```
119
+
120
+ ## Common Use Cases
121
+
122
+ ### Copy to Clipboard
123
+
124
+ ```tsx
125
+ <IconButton
126
+ variant="outline"
127
+ size="lg"
128
+ radius="lg"
129
+ onClick={() => navigator.clipboard.writeText(text)}
130
+ >
131
+ <Copy size={16} />
132
+ </IconButton>
133
+ ```
134
+
135
+ ### Settings Menu
136
+
137
+ ```tsx
138
+ <IconButton
139
+ variant="ghost"
140
+ size="lg"
141
+ radius="lg"
142
+ onClick={openSettings}
143
+ >
144
+ <Settings size={16} />
145
+ </IconButton>
146
+ ```
147
+
148
+ ### Delete Action
149
+
150
+ ```tsx
151
+ <IconButton
152
+ variant="filled"
153
+ size="lg"
154
+ radius="lg"
155
+ onClick={handleDelete}
156
+ >
157
+ <Trash2 size={16} />
158
+ </IconButton>
159
+ ```
160
+
161
+ ## Design System Integration
162
+
163
+ This component uses the following Figma design tokens:
164
+
165
+ ### Colors
166
+
167
+ - `--color-unofficial-outline` - Background color for outline variant
168
+ - `--color-general-border` - Border color
169
+ - `--color-general-foreground` - Text/icon color
170
+ - `--color-unofficial-outline-hover` - Hover background
171
+ - `--color-unofficial-outline-active` - Active background
172
+ - `--color-general-primary` - Filled variant background
173
+ - `--color-general-primary-foreground` - Filled variant text
174
+
175
+ ### Shadows
176
+
177
+ - `var(--mantine-shadow-sm)` - Box shadow for outline variant (from Figma shadow tokens)
178
+
179
+ ### Focus Ring
180
+
181
+ - `--color-focus-ring` - Focus outline color
182
+
183
+ ## Migration Guide
184
+
185
+ If you're currently using `ActionIcon` directly, you should migrate to `IconButton`:
186
+
187
+ ### Before
188
+
189
+ ```tsx
190
+ import { ActionIcon } from '@acme/ui';
191
+
192
+ <ActionIcon variant="outline" size="lg" radius="lg">
193
+ <Copy size={16} />
194
+ </ActionIcon>
195
+ ```
196
+
197
+ ### After
198
+
199
+ ```tsx
200
+ import { IconButton } from '@acme/ui';
201
+
202
+ <IconButton variant="outline" size="lg" radius="lg">
203
+ <Copy size={16} />
204
+ </IconButton>
205
+ ```
206
+
207
+ The main differences:
208
+ 1. ✅ Shadow is now applied automatically to outline variant
209
+ 2. ✅ Colors match the Figma design exactly
210
+ 3. ✅ Hover states are more refined
211
+ 4. ✅ Focus states include proper focus ring
212
+
213
+ ## Accessibility
214
+
215
+ - Supports keyboard navigation with Tab/Shift+Tab
216
+ - Shows focus ring when focused via keyboard (`:focus-visible`)
217
+ - Can be disabled with proper visual feedback
218
+ - Works with screen readers (use `aria-label` for icon-only buttons)
219
+
220
+ ```tsx
221
+ <IconButton
222
+ variant="outline"
223
+ size="lg"
224
+ radius="lg"
225
+ aria-label="Copy to clipboard"
226
+ onClick={handleCopy}
227
+ >
228
+ <Copy size={16} />
229
+ </IconButton>
230
+ ```
@@ -0,0 +1,2 @@
1
+ export { IconButton } from './IconButton';
2
+ export type { IconButtonProps, IconButtonVariant } from './IconButton';