@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,39 @@
1
+ <!-- Auto-generated from apps/lm-web/.ai/rules/ at publish time. Do not edit here; edit the canonical source. -->
2
+
3
+ > **Applies to:** any consumer of `@scalepad/ui`. Originally authored inside the ScalePad lm-web app; lm-web-specific app architecture rules (architecture, mutations, data-fetching, error-handling, routing, sort-types, testing, feature-conventions) are deliberately not shipped.
4
+
5
+
6
+ # Date Handling
7
+
8
+ Never call `new Date()` directly in feature or shared code. Use helpers from `@scalepad/ui-utils/date`:
9
+
10
+ | Situation | Use | Import |
11
+ |---|---|---|
12
+ | Non-nullable DTO date string (`created_at`, `updated_at`, `enrolled_at`) | `toDate(dto.field)` | `@scalepad/ui-utils/date` |
13
+ | Nullable DTO date string | `parseApiDate(dto.field)` | `@/shared/utils` |
14
+ | Current wall-clock time | `getCurrentDate()` | `@scalepad/ui-utils/date` |
15
+ | Date string → formatted display | `formatDate(toDate(str), DateFormat.X)` | `@scalepad/ui-utils/date` |
16
+
17
+ ```ts
18
+ // ✅ Correct
19
+ import { toDate, getCurrentDate, formatDate, DateFormat } from '@scalepad/ui-utils/date';
20
+ import { parseApiDate } from '@/shared/utils';
21
+
22
+ createdAt: toDate(dto.created_at), // non-nullable DTO field
23
+ lastSeenAt: parseApiDate(dto.last_seen_at), // nullable DTO field
24
+ const now = getCurrentDate(); // wall-clock time
25
+ formatDate(toDate(str), DateFormat.MONTH_SHORT) // formatted display
26
+
27
+ // ❌ Wrong
28
+ createdAt: new Date(dto.created_at),
29
+ const now = new Date(),
30
+ d.toLocaleString('en-US', { month: 'short' })
31
+ ```
32
+
33
+ **Why:** `toDate` and `getCurrentDate` are mockable in tests; bare `new Date()` calls are not. `parseApiDate` centralises the nullable-string guard. `formatDate` + `DateFormat` keeps display formatting consistent with the design system.
34
+
35
+ **Exceptions** — `new Date()` is acceptable in:
36
+ - `src/mocks/` — test fixture and seed data
37
+ - Runtime type-guard coercions for external/unknown inputs (e.g. `x instanceof Date ? x : new Date(x)`)
38
+ - `packages/utils/src/date/` — the helpers themselves
39
+ - Storybook stories and test files
@@ -0,0 +1,372 @@
1
+ <!-- Auto-generated from .cursor/rules/ at publish time. Do not edit here; edit the canonical source. -->
2
+
3
+ > **Path-context note:** Paths in this rule that begin with `src/frontend/ClientApps/packages/ui/src/...` refer to the ScalePad UI source tree as authored. In your consumer project, that source lives at `node_modules/@scalepad/ui/src/...`. Treat the two paths as interchangeable when you're reading examples.
4
+
5
+
6
+ # LM Design System — Figma Mockup Rules
7
+
8
+ These rules are MANDATORY for every Figma-related task: building or updating mockups, screens, modals, drawers, or panels in Figma; reading a Figma frame to implement in code; or writing Code Connect mappings. They override generic Figma-MCP guidance whenever there is conflict. If a rule cannot be satisfied (e.g. the design system genuinely lacks a primitive), STOP and tell the user — do not silently substitute a hardcoded value.
9
+
10
+ ## 1. Canonical Design System
11
+
12
+ **The ONLY design system to draw from is the `LM Design System` library.**
13
+
14
+ | Field | Value |
15
+ |---|---|
16
+ | File name | `LM Design System` |
17
+ | Figma file URL | `https://www.figma.com/design/VCLfybgU3OaUUPrQdBaVmP/LM-Design-System` |
18
+ | `fileKey` | `VCLfybgU3OaUUPrQdBaVmP` |
19
+ | Library key | `lk-5ca1dcd26f40f0d14e872ac95ebdb56292e1c16c64d9157f837dc2ae6441e3d4a025a1dbcaadcb677883d963c16f12bdaefe8caf762f8b9f9c10f6dbf68d2c8e` |
20
+ | Library short name (in MCP) | `LM Design System` |
21
+ | Variable collections | `Tokens` (colors, spacing, radius), `typography` (paragraph spacing), `border radii` (raw absolute) |
22
+
23
+ **IMPORTANT — library precedence (when `search_design_system` returns duplicates):**
24
+
25
+ 1. Always pick `LM Design System` (`lk-5ca1...2c8e`).
26
+ 2. NEVER use `Basic Design System` (`lk-60d6...dafb`) — it is the upstream parent. `LM Design System` overrides and re-exports everything you need.
27
+ 3. NEVER use `ControlMap Library` (`lk-d7e3...f23c`), `Simple Design System`, `Material 3 Design Kit`, `iOS / iPadOS / watchOS / visionOS` libraries — they are different products or stale.
28
+
29
+ When `search_design_system` returns the same asset name under both `LM Design System` and `Basic Design System`, ALWAYS use the `LM Design System` entry — not the first result.
30
+
31
+ ## 2. Sample Mockup For Pattern Reference
32
+
33
+ When you need a reference for "what good looks like" assembled from this library:
34
+
35
+ - File: `LM-Vision`, `fileKey` `AE4ZLZoEgdJList3S4eJ7q`
36
+ - Sample frame node id: `5228:40665`
37
+
38
+ Use it via `get_design_context` to inspect spacing, component variants, and token bindings BEFORE composing a new mockup. Match its conventions. If it disagrees with these rules, these rules win.
39
+
40
+ ## 3. Mandatory Pre-Flight Before Any Figma Write
41
+
42
+ A persistent cache of LM Design System keys lives in [`figma-lm-design-system-keys.mdc`](mdc:.cursor/rules/figma-lm-design-system-keys.mdc) — read it BEFORE running any `search_design_system` query. Most components, variables, text styles, and effect styles you need are already cached there with the canonical role-based recipes from §6 / §7 / §8 below.
43
+
44
+ Before the FIRST `use_figma` mutation in a task that produces a mockup:
45
+
46
+ 1. **Read `figma-lm-design-system-keys.mdc`** and pick keys by role.
47
+ 2. **For anything missing from the cache**, run `search_design_system` (with `fileKey` set to the target file), then **add the resolved key back into `figma-lm-design-system-keys.mdc` in the same change.** When `search_design_system` returns matches from multiple libraries, pick the row whose `libraryKey` starts with `lk-5ca1dcd2...` (LM Design System) per §1 precedence.
48
+ 3. **Open the LM Vision reference frame** (`5787:30996` foundation list-page, `5555:14` UC11 share-modal chrome, `5680:13` UC15 drawer chrome, `5722:13` UC22 public branded chrome) via `get_design_context` to confirm visual conventions for the surface you're building.
49
+ 4. Only AFTER cached keys are loaded, write the wrapper frame, then build sections one `use_figma` call at a time.
50
+
51
+ If a needed token/component/style is genuinely missing from `LM Design System` (verified by both the cache AND a follow-up `search_design_system`), STOP and ask the user — do NOT fall back to `Basic Design System`, `ControlMap Library`, hex codes, or raw pixel values.
52
+
53
+ Always pass `skillNames: "figma-generate-design"` (or the actual skill stack you loaded, e.g. `"figma-use,figma-generate-design"`) to `use_figma` for logging.
54
+
55
+ ## 4. Spacing — Strict Token-Only
56
+
57
+ **Figma `LM Design System` spacing tokens (collection `Tokens`, scope `GAP`, ALL of these and ONLY these):**
58
+
59
+ | Figma variable name | Approx px | When to use |
60
+ |---|---:|---|
61
+ | `Spacing/spacing-none` | 0 | Eliminate gap, flush layout |
62
+ | `Spacing/spacing-xxs` | 4 | Icon ↔ adjacent label, dense badge interior |
63
+ | `Spacing/spacing-xs` | 8 | Tight inline groups, compact button padding |
64
+ | `Spacing/spacing-sm` | 12 | Default field-row gap, card content-to-content |
65
+ | `Spacing/spacing-md` | 16 | Section padding interior, card padding |
66
+ | `Spacing/spacing-lg` | 20 | Section ↔ section in a card or panel |
67
+ | `Spacing/spacing-xl` | 24 | Page section padding |
68
+ | `Spacing/spacing-xxl` | 32 | Major page-level gaps, column gutter |
69
+
70
+ Bind via `figma.variables.importVariableByKeyAsync(key)` then `frame.setBoundVariable("itemSpacing"|"paddingTop"|"paddingBottom"|"paddingLeft"|"paddingRight", spacingVar)`.
71
+
72
+ **FORBIDDEN spacing patterns:**
73
+
74
+ - Setting `itemSpacing`, `paddingLeft`, `paddingRight`, `paddingTop`, `paddingBottom`, `paddingVertical`, or `paddingHorizontal` to a numeric literal (e.g. `frame.itemSpacing = 12`). ALWAYS bind to a variable.
75
+ - Using a variable from `Basic Design System` even when it has the same name.
76
+ - Using the `border radii` or `typography` collections for layout spacing — those scopes are wrong.
77
+ - "Eyeballing" gaps that fall between tokens. Round to the nearest valid token. The token ladder is intentionally coarse to enforce rhythm.
78
+
79
+ **Reading the LM-Vision reference (`5228:40665`):** if you see a gap that does not match one of the eight tokens, you are reading the wrong number — re-inspect with `get_design_context` and use the bound variable, not the resolved px value.
80
+
81
+ **Code-side spacing (when implementing in `apps/lm-web/`):** the `@scalepad/ui` props mostly mirror the Figma names but differ at the extremes:
82
+
83
+ | Figma token | `@scalepad/ui` Mantine prop value |
84
+ |---|---|
85
+ | `Spacing/spacing-none` | `"0"` (or omit) |
86
+ | `Spacing/spacing-xxs` | `"2xs"` (4px) |
87
+ | `Spacing/spacing-xs` | `"xs"` (8px) |
88
+ | `Spacing/spacing-sm` | `"sm"` (12px) |
89
+ | `Spacing/spacing-md` | `"md"` (16px) |
90
+ | `Spacing/spacing-lg` | `"lg"` (20px) |
91
+ | `Spacing/spacing-xl` | `"xl"` (24px) |
92
+ | `Spacing/spacing-xxl` | `"2xl"` (32px) |
93
+
94
+ The codebase exposes additional `3xs` (2 px), `3xl` (40 px), `4xl` (48 px), `5xl` (64 px) values that DO NOT exist in Figma. When porting from a Figma frame, you must NOT use those — if a code mockup needs them, the design must be updated in Figma first. When generating a mockup from code that uses them, round to the nearest Figma token and call out the rounding to the user.
95
+
96
+ ## 5. Radius — Strict Token-Only
97
+
98
+ Use only these variables from collection `Tokens` (scope `CORNER_RADIUS`):
99
+
100
+ | Figma variable name | Approx px | Use |
101
+ |---|---:|---|
102
+ | `Radius/radius-none` | 0 | Hard edges (table cells, full-bleed) |
103
+ | `Radius/radius-xs` | 2 | Tiny tags, small chips |
104
+ | `Radius/radius-sm` | 4 | Inputs, dense controls |
105
+ | `Radius/radius-md` | 6 | Default for buttons, badges |
106
+ | `Radius/radius-lg` | 8 | Cards, popovers, modals interior |
107
+ | `Radius/radius-xl` | 12 | Large surfaces, prominent cards |
108
+ | `Radius/radius-xxl` | 16 | Hero / marketing surfaces |
109
+
110
+ Pill / circular shapes use Mantine prop `radius="full"` in code; in Figma there is no token for that — set `cornerRadius` to half the height for pills, full for circles, and document why a token wasn't bound.
111
+
112
+ The `border radii / absolute/radius-*` collection exists for primitives the system itself is built from. **Do not use it in mockups.**
113
+
114
+ ## 6. Colors — Bind Variables, Never Set Hex
115
+
116
+ **Categories** (collection `Tokens`):
117
+
118
+ - `Background/*` — frame and shape fills (scopes `FRAME_FILL`, `SHAPE_FILL`, `EFFECT_COLOR`)
119
+ - `Text/*` — text fills (scope `TEXT_FILL`)
120
+ - `Stroke/*` — borders and dividers
121
+ - `Icon/*` — icon fills inside `Icon /` components
122
+
123
+ **Sub-categories**: `default` (neutral), `Subdued`, `Primary` (brand green), `Information` (indigo), `Success` (teal), `Warning` (yellow), `Danger` (red), `Inverse`, `disabled`, `transparent`, `body`, `backdrop`, `input`.
124
+
125
+ **Variants**: `default`, `light`, `filled`, `strong`, `disabled`, `inverse`, `default-hover`, `light-hover`, `filled-hover`.
126
+
127
+ **Common token recipe** — pick by *role*, not by hue:
128
+
129
+ | Need | Variable to bind |
130
+ |---|---|
131
+ | Primary CTA fill | `Background/Primary/filled` |
132
+ | Primary CTA hover | `Background/Primary/filled-hover` |
133
+ | Primary CTA label | `Text/inverse` |
134
+ | Soft brand surface (selected pill, info card) | `Background/Primary/light` |
135
+ | Brand-tinted text on soft surface | `Text/Primary/light` (or `Text/link` for links) |
136
+ | Page background | `Background/body` |
137
+ | Card / sheet background | `Background/default` |
138
+ | Subtle striping or subdued chip | `Background/Subdued/light` |
139
+ | Filled neutral chip | `Background/Subdued/filled` |
140
+ | Default body copy | `Text/default` |
141
+ | Subdued body copy / helper text | `Text/Subdued/strong` (NOT `light`) |
142
+ | Lightest helper / placeholder | `Text/Subdued/light` |
143
+ | Disabled label | `Text/disabled/default` |
144
+ | Danger label | `Text/Danger/default` (or `Text/Danger/strong` on light bg) |
145
+ | Success label | `Text/Success/strong` |
146
+ | Warning label | `Text/Warning/strong` |
147
+ | Information label | `Text/Information/strong` |
148
+ | Default border | `Stroke/default` |
149
+ | Hairline border | `Stroke/Subdued/default` |
150
+ | Focus ring (outer) | `Stroke/focus/strong` |
151
+ | Modal backdrop overlay | `Background/backdrop` |
152
+ | Input field background | `Background/input` |
153
+
154
+ Bind colors via:
155
+
156
+ ```js
157
+ const bgVar = await figma.variables.importVariableByKeyAsync(BG_KEY);
158
+ const fill = figma.variables.setBoundVariableForPaint(
159
+ { type: 'SOLID', color: { r: 0, g: 0, b: 0 } }, 'color', bgVar
160
+ );
161
+ node.fills = [fill];
162
+ ```
163
+
164
+ **FORBIDDEN color patterns:**
165
+
166
+ - `node.fills = [{ type: 'SOLID', color: { r: ..., g: ..., b: ... } }]` without a bound variable.
167
+ - Hex codes anywhere in mockup data, including inside `iconBg` / `iconFg` props (this is a real recurring failure — see `apps/lm-web/src/features/global-search/components/QuickCreateRail.tsx`'s pre-rules pattern).
168
+ - Picking a hue family (e.g. `green`, `red`) directly from primitives like `colors.ts` instead of a semantic role (`Primary`, `Danger`).
169
+ - "Per-entity" accent colors invented for a single mockup. If the design needs five distinct accent surfaces (e.g. task / initiative / goal / meeting / deliverable), use the five available semantic tinted backgrounds — `Primary/light`, `Information/light`, `Success/light`, `Warning/light`, `Danger/light` — paired with their matching `Text/<role>/strong`. If you genuinely need more than the five semantic roles, STOP and ask the user — don't invent a sixth.
170
+
171
+ ## 7. Text Styles — Apply, Don't Reinvent
172
+
173
+ Available text styles in `LM Design System`:
174
+
175
+ | Figma style name | Use |
176
+ |---|---|
177
+ | `heading 1` | Page title (rare; reserved for marketing / empty states) |
178
+ | `heading 2` | Major section title |
179
+ | `heading 3` | Card title, drawer title |
180
+ | `heading 4` | Subsection title |
181
+ | `heading 5` | Inline group title, dense panel title |
182
+ | `Body/Body 1` | Default body copy (14 px regular) |
183
+ | `Body/Body 1 Strong` | Body copy with emphasis (14 px medium 500) |
184
+ | `Body/Body 1 Stronger` | Body copy with stronger emphasis (14 px semibold 600) |
185
+
186
+ The library also exposes `caption1`, `caption1.strong`, `caption1.stronger`, `caption2`, `caption2.strong`, `caption2.stronger`, `caption3`, `caption3.strong`, `caption3.stronger`, `smallCaps`, `monospaced`. Search for the exact name with `search_design_system` (`includeStyles: true`) before binding — case and slash placement matter.
187
+
188
+ Apply via:
189
+
190
+ ```js
191
+ const textStyle = await figma.importStyleByKeyAsync(STYLE_KEY);
192
+ textNode.textStyleId = textStyle.id;
193
+ ```
194
+
195
+ **FORBIDDEN typography patterns:**
196
+
197
+ - Setting `fontSize`, `fontName`, `lineHeight`, `letterSpacing`, or `textCase` directly on a text node when a matching style exists. Apply the style.
198
+ - Choosing a heading level by *visual weight* rather than *semantic role* (e.g. using `heading 2` because it "looks the right size" for a card title — use `heading 3` for cards).
199
+ - Applying `smallCaps` to anything outside its intended use (eyebrow labels above sections / cards). It is opinionated and breaks easily.
200
+
201
+ **Code-side**: in `apps/lm-web/`, use `<Text variant="...">` from `@scalepad/ui`. Variant names mirror Figma: `body1`, `body1.strong`, `body1.stronger`, `caption1`, `caption2`, `caption3`, `smallCaps`, `monospaced`. Heading variants live on `<Heading>` (`heading1`–`heading5`).
202
+
203
+ ## 8. Effects (Shadows) — Apply, Don't Build
204
+
205
+ Use these `EFFECT` styles from `LM Design System`:
206
+
207
+ | Figma style name | Use |
208
+ |---|---|
209
+ | `shadow-2xs` | Pressed / inset hint |
210
+ | `shadow-xs` | Subtle separator (1 px stack hint) |
211
+ | `shadow-sm` | Inline cards, popover tip |
212
+ | `shadow-md` | Default elevated card |
213
+ | `shadow-lg` | Drawer, popover floating layer |
214
+ | `shadow-xl` | Modal, command palette |
215
+ | `shadow-2xl` | High-emphasis floating UI (rare) |
216
+
217
+ Apply with `node.effectStyleId = (await figma.importStyleByKeyAsync(SHADOW_KEY)).id`. Never construct a `DROP_SHADOW` effect manually; the colors and offsets in the system are tuned for both light and dark modes.
218
+
219
+ ## 9. Components — Instances Only, From `LM Design System`
220
+
221
+ When the system has the component, use a component INSTANCE — never re-build with primitives. Confirmed components in `LM Design System` (this is not exhaustive — verify with `search_design_system` first):
222
+
223
+ | Need | Component name |
224
+ |---|---|
225
+ | Action / submit button | `Button` (component_set) |
226
+ | Icon-only button | `Icon Button` |
227
+ | Toolbar / segmented buttons | `Button Group`, `Button Group Icon Button` |
228
+ | Text-style link with icon | `Link Button` |
229
+ | Toggle (single state) | `Toggle Button`, `Toggle Icon Button` |
230
+ | Pagination control | `Pagination Button` |
231
+ | Tab strip | `Tabs` (container component_set) wrapping one `Tab` (single tab component_set) per panel |
232
+ | Surface / panel container | `Card` (component_set), `Hover Card` |
233
+ | Image carousel | `Carousel with Image` |
234
+ | **Confirmation / alert modal** (single yes-or-no question — "Are you sure?", "Delete this?", "Discard changes?", validation prompt) | **`Alert Dialog`** (component_set, key `9b2fb3dcc9a991615d1119cb0638f5a01682700f`). Set `Type` = `Desktop` or `Mobile`. Toggle `ShowError` for destructive / failure states. **Sample assembled instance**: node `4141:5746` — open it, copy it, override the title/body/buttons. Do NOT recompose from `Dialog` + `Dialog Header` + `Dialog Footer` + buttons. |
235
+ | Full-content modal (forms, multi-step flows, dense panels) | `Dialog` (component_set) composed with `Dialog Header` + `Dialog Footer`. Use this only when the body content is too rich for `Alert Dialog` (more than a sentence or two of body copy, embedded forms, lists, etc.). |
236
+ | **Labeled form control** (label + optional hint + error + the control itself) | **`Vertical Field`** (default, label above) or **`Horizontal Field`** (label-on-the-side, settings-style). Set the `Type` variant to one of `Text Value` / `Select` / `Radio` / `Textarea` / `Checkbox` / `Slider`. **One component covers all six control shapes** — never compose a labeled form control by hand from primitives. |
237
+ | Bare text input (no label, e.g. inline search, table cell editor, card-embedded value) | `Input` |
238
+ | OTP input | `Input OTP` |
239
+ | File upload | `Input File` |
240
+ | Date input | `Date Picker Input` |
241
+ | Table cell wrapper | `Table Cell` |
242
+ | Filter dropdown | `Filter Menu` |
243
+ | Inline iconography | `Icon / <name>` (e.g. `Icon / circle-check-big`, `Icon / rocket`) |
244
+
245
+ For each instance:
246
+
247
+ 1. Import via `figma.importComponentSetByKeyAsync(KEY)` (or `importComponentByKeyAsync` for non-variant components).
248
+ 2. **Discover keys at runtime — never copy keys from this file.** Read `componentPropertyDefinitions` on the imported component_set/component and inspect every entry: `VARIANT` keys are bare names (`Variant`, `Size`, `State`, `Content`, `Slot No.`); `BOOLEAN`, `INSTANCE_SWAP`, and `TEXT` keys carry an unstable `#nnn:m` suffix (e.g. `Show left icon#11:0`). The suffix changes when the component is restructured upstream.
249
+ 3. Override text via the component's TEXT property keys (e.g. `"Label#2:0": "Save changes"` — but read the actual key from `componentPropertyDefinitions`), not by writing `node.characters`.
250
+ 4. For nested instances that expose their own TEXT properties, call `setProperties()` on the nested instance.
251
+ 5. Never detach an instance to "tweak" it. If a variant is missing, STOP and ask.
252
+
253
+ ### Conditional Icon / Slot Properties — Set BOTH The Gate AND The Swap
254
+
255
+ This is THE most common slip when authoring instances of `LM Design System` components programmatically. Many components hide an icon-instance or content-slot behind a *gate* — either a `BOOLEAN` toggle or a `VARIANT` value. Setting the swap without flipping the gate ⇒ the icon never renders. Flipping the gate without setting the swap ⇒ the placeholder icon (`circle-help`-shaped) renders.
256
+
257
+ There are three gating patterns in `LM Design System`:
258
+
259
+ | Pattern | Gate | Paired swap | Example components |
260
+ |---|---|---|---|
261
+ | **A. Boolean-gated swap** | `BOOLEAN` named `Show <something>` | `INSTANCE_SWAP` with `⮑` prefix on a sibling property | `Button` (`Show left icon` → `⮑ Left Content`, plus `Show right icon` → `⮑ Right Content`); `Badge` (`Show icon left` → `⮑ Icon left`; note the word order differs from `Button`) |
262
+ | **B. Variant-gated swap** | `VARIANT` value reveals the swap | `INSTANCE_SWAP` (no `⮑` prefix) | `Tab` (`Content` = `Icon` or `Icon + Label` reveals `Icon`); `Card` (`Slot No.` = `1 Slot` … `6 Slots` reveals `Main Slot` / `Header Slot` / `Footer Slot`) |
263
+ | **C. Always-visible swap** | none | `INSTANCE_SWAP` always present | `Icon Button` (the whole component IS the icon — just set `⮑ Left icon`) |
264
+
265
+ There is **also** a fourth shape worth knowing: components like `Input` expose `Show decoration left` / `Show decoration right` booleans **without** a sibling instance-swap. The decoration content is a nested instance you reach by traversing `instance.children` and calling `setProperties()` on the inner decoration node. Don't assume a paired swap exists just because a `Show <x>` boolean exists — check the `componentPropertyDefinitions` first.
266
+
267
+ **Recipe — boolean-gated icon (the Button shape):**
268
+
269
+ ```js
270
+ // 1. Discover keys at runtime.
271
+ const buttonSet = await figma.importComponentSetByKeyAsync(BUTTON_KEY);
272
+ const defs = buttonSet.componentPropertyDefinitions;
273
+ const showLeftKey = Object.keys(defs).find(k => k.startsWith('Show left icon#'));
274
+ const leftSwapKey = Object.keys(defs).find(k => k.startsWith('⮑ Left Content#'));
275
+ if (!showLeftKey || !leftSwapKey) throw new Error('Button shape changed — re-discover.');
276
+
277
+ // 2. Resolve the icon to a NODE ID. INSTANCE_SWAP values are node ids, NOT component keys.
278
+ const plusIcon = await figma.importComponentByKeyAsync(ICON_PLUS_KEY);
279
+
280
+ // 3. Set BOTH the gate AND the swap in a single setProperties() call.
281
+ const instance = buttonSet.defaultVariant.createInstance();
282
+ instance.setProperties({
283
+ Variant: 'Primary',
284
+ Size: 'md',
285
+ [showLeftKey]: true,
286
+ [leftSwapKey]: plusIcon.id,
287
+ });
288
+ ```
289
+
290
+ **Recipe — variant-gated icon (the Tab shape):** flip the variant first, then set the swap.
291
+
292
+ ```js
293
+ const tabSet = await figma.importComponentSetByKeyAsync(TAB_KEY);
294
+ const defs = tabSet.componentPropertyDefinitions;
295
+ const iconKey = Object.keys(defs).find(k => k.startsWith('Icon#'));
296
+ const icon = await figma.importComponentByKeyAsync(ICON_KEY);
297
+
298
+ const instance = tabSet.defaultVariant.createInstance();
299
+ instance.setProperties({
300
+ Content: 'Icon + Label', // VARIANT — gates the Icon swap
301
+ Size: 'Regular',
302
+ [iconKey]: icon.id,
303
+ });
304
+ ```
305
+
306
+ **Rules of thumb:**
307
+
308
+ - INSTANCE_SWAP values are **node ids** (e.g. `"123:45"`), not component keys. Pattern: `(await figma.importComponentByKeyAsync(KEY)).id`.
309
+ - VARIANT keys are bare strings; everything else has a `#nnn:m` suffix you must read at runtime.
310
+ - The `⮑` arrow prefix in property names (`⮑ Left Content`, `⮑ Icon right`, `⮑ Left icon`) is part of the actual key — preserve it exactly. It's the literal Unicode character U+2BAD.
311
+ - Property names are **inconsistent across components** by design — `Button` says `"Show left icon"` while `Badge` says `"Show icon left"`. Never copy a key from another component; always re-discover.
312
+
313
+ **Icons specifically:** prefer `Icon / <lucide-name>` components from `LM Design System`. The codebase ships `lucide-react` icons in code, but in Figma mockups always use the design-system icon component so the icon stays linked. If the icon you need does not exist in `Icon / *`, STOP and ask before creating a new vector.
314
+
315
+ **FORBIDDEN component patterns:**
316
+
317
+ - Drawing a button with a `RECTANGLE` + `TEXT`. Use `Button`.
318
+ - Placing a Lucide SVG asset inline instead of `Icon / *`.
319
+ - Picking a button variant by visual size match — match the *role*: `Primary` for the single dominant CTA, `Secondary` for normal actions, `Outline` / `Ghost` for tertiary, `Destructive` for delete-class actions, `Outline Inverse` only on dark surfaces.
320
+ - Composing a labeled form control by hand — `Input` + a separate `Text` for the label + another `Text` for the helper/hint + manual red styling for errors. `Vertical Field` / `Horizontal Field` already wraps all of that and renders the label, hint, error, and control with consistent spacing. Pick `Type` = `Text Value` / `Select` / `Radio` / `Textarea` / `Checkbox` / `Slider` for the inner control. Reserve bare `Input` (and bare `Radio` / `Checkbox` / `Switch` / `Textarea`) for cases where the design intentionally has no label — inline search, value cells in a `Table`, embedded controls in a `Card` body where the label lives elsewhere.
321
+ - Reaching for the bare primitive component (`Radio`, `Checkbox`, `Switch`, `Textarea`, `Input`) when the design shows a label or helper next to it. The label/helper is part of the `Field`, not next to it.
322
+ - Composing a confirmation / alert modal from `Dialog` + `Dialog Header` + `Dialog Footer` + two `Button`s + a custom layout. Use `Alert Dialog` (sample at node `4141:5746`) and override the inner text and buttons. `Dialog` is for content-rich modals — forms, multi-step flows, dense panels — not for "Are you sure?" prompts.
323
+ - Setting an `INSTANCE_SWAP` property (icon slot, content slot) **without** also flipping its boolean gate or setting the variant value that reveals it. The icon will not render.
324
+ - Flipping a `Show <something>` boolean **without** also setting the paired `INSTANCE_SWAP`. The default placeholder icon (`circle-help`-shaped) will render and the mockup will look broken.
325
+ - Hardcoding `#nnn:m` property suffixes from this rule file, a sample script, or another component into a script. Always re-discover from `componentPropertyDefinitions` on the live component.
326
+ - Passing a component **key** as an `INSTANCE_SWAP` value. It must be the node `.id` returned by `importComponentByKeyAsync(...)`.
327
+
328
+ ## 10. Layout Discipline
329
+
330
+ - Every container that has more than one child MUST be auto-layout. No absolute positioning except for floating overlays (tooltips, popovers).
331
+ - `counterAxisAlignItems` and `primaryAxisAlignItems` must be set explicitly — don't rely on defaults.
332
+ - After `appendChild`, set `layoutSizingHorizontal` / `layoutSizingVertical` on the appended node — `FILL` for stretch children, `HUG` for content-sized, `FIXED` only when the parent is `HUG` and a child needs a hard width.
333
+ - One section per `use_figma` call. Build wrapper first, return its id, then build each section into the wrapper.
334
+ - Validate each section with `get_screenshot` (by node id, not the whole frame) before moving on.
335
+
336
+ ## 11. Translating Mockups Back To Code (lm-web)
337
+
338
+ When implementing a Figma frame in `apps/lm-web/src/features/<name>/...`:
339
+
340
+ - UI imports come from `@scalepad/ui`, never `@mantine/core` directly. (See `.ai/rules/styling.md`.)
341
+ - Spacing uses Mantine props `gap` / `p` / `px` / `py` / `mt` / etc., with the token names from §4 above (`"3xs"` is FORBIDDEN unless the corresponding Figma frame is updated to add the missing token first).
342
+ - Colors use `bg="background.<role>"` / `c="text.<role>"` / `style={{ borderColor: toCssVar(tokens.color.stroke.default) }}` etc. — see `packages/ui/src/theme/themeContract.css.ts` for the full contract. NEVER inline hex / rgba.
343
+ - Text uses `<Text variant="...">` / `<Heading variant="...">` from `@scalepad/ui`. Don't set `fz`, `fw`, `lh`, `lts`, or `ff` directly.
344
+ - Icons: use `lucide-react` components in code (this is the only place Lucide is allowed); name should match the Figma icon name `Icon / <kebab-name>` → React `<KebabName />`.
345
+
346
+ When you find an existing file using hardcoded hex / rgba on accent bubbles, badges, or icon tints (the recurring symptom), fix it back to tokens at the same time you ship the mockup. The accent palette for "per-entity" tints is the five semantic roles in §6 — not invented hex codes.
347
+
348
+ ## 12. Forbidden Shortcuts (Hard Stop List)
349
+
350
+ Every one of these is a stop-and-ask, not a substitute-and-continue:
351
+
352
+ - ✗ Hex code or `rgba(...)` literal in a mockup, in CSS-in-TS, in a component prop, or in a variable definition.
353
+ - ✗ Numeric literal as `itemSpacing`, padding, gap, cornerRadius, fontSize, lineHeight.
354
+ - ✗ Component drawn from primitives when the named component exists in `LM Design System`.
355
+ - ✗ Pulling assets from `Basic Design System` or `ControlMap Library` files when the same name exists in `LM Design System`.
356
+ - ✗ Detaching an instance to override style.
357
+ - ✗ Inventing a new accent color outside the five semantic roles.
358
+ - ✗ Setting `node.characters` on a text inside an instance whose containing component exposes a TEXT property — use `setProperties()`.
359
+ - ✗ Setting an `INSTANCE_SWAP` icon/slot property without also flipping its gate (the paired `Show <…>` boolean, or the `Content` / `Slot No.` variant value). See §9 "Conditional Icon / Slot Properties".
360
+ - ✗ Hardcoding `#nnn:m` property suffixes from this file or any prior script. Read from `componentPropertyDefinitions` at runtime, every time.
361
+ - ✗ Composing a labeled form control by hand from `Input` + label `Text` + helper `Text`. Use `Vertical Field` / `Horizontal Field` with the appropriate `Type` variant. See §9 component table.
362
+ - ✗ Composing a confirmation / alert modal from `Dialog` + `Dialog Header` + `Dialog Footer` + buttons. Use `Alert Dialog` (sample assembled instance: node `4141:5746` in `LM Design System`). See §9 component table.
363
+ - ✗ Skipping the §3 pre-flight before the first mutating `use_figma` call.
364
+
365
+ ## 13. Reference Anchors (do not edit by hand)
366
+
367
+ - Code source of truth for tokens: `src/frontend/ClientApps/packages/ui/src/tokens/` and `src/frontend/ClientApps/packages/ui/src/theme/themeContract.css.ts`.
368
+ - Token JSON exports the codebase generates from: `packages/figma-tokens/source/*.json` (Figma-side export of `LM Design System`).
369
+ - Code Connect mappings: `src/frontend/ClientApps/packages/ui/src/components/**/*.figma.tsx`. They point at `LM Design System` (`VCLfybgU3OaUUPrQdBaVmP`). Safe to use as a starting reference for component URLs, but always verify the target node still exists with `search_design_system` before linking from a new mockup.
370
+ - New-frontend styling rules (lm-web): `src/frontend/ClientApps/.ai/rules/styling.md`.
371
+
372
+ If any rule above conflicts with `.ai/rules/styling.md`, the lm-web rule wins for code; this file wins for Figma writes.