@recursica/mantine-adapter 0.53.0 → 0.55.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 (42) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/mantine-adapter.cjs +1 -1
  4. package/dist/mantine-adapter.cjs.map +1 -1
  5. package/dist/mantine-adapter.css +1 -1
  6. package/dist/mantine-adapter.js +634 -617
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/package.json +5 -4
  9. package/src/components/Accordion/Accordion.module.css +17 -8
  10. package/src/components/AutoComplete/AutoComplete.module.css +9 -2
  11. package/src/components/Chip/Chip.module.css +18 -0
  12. package/src/components/DatePicker/DatePicker.module.css +9 -2
  13. package/src/components/Dropdown/Dropdown.module.css +9 -2
  14. package/src/components/FileInput/FileInput.module.css +12 -0
  15. package/src/components/FileUpload/FileUpload.module.css +6 -1
  16. package/src/components/FormControlLayout/FormControlLayout.module.css +12 -0
  17. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +36 -0
  18. package/src/components/HoverCard/HoverCard.module.css +6 -1
  19. package/src/components/Label/Label.stories.tsx +63 -54
  20. package/src/components/Link/Link.module.css +5 -0
  21. package/src/components/Loader/Loader.animate.dom.test.tsx +82 -0
  22. package/src/components/Loader/Loader.module.css +10 -0
  23. package/src/components/Loader/Loader.stories.tsx +20 -0
  24. package/src/components/Loader/Loader.tsx +8 -1
  25. package/src/components/Modal/Modal.module.css +6 -2
  26. package/src/components/NumberInput/NumberInput.module.css +9 -2
  27. package/src/components/SegmentedControl/SegmentedControl.module.css +6 -0
  28. package/src/components/Table/TABLE_IMPLEMENTATION_NOTES.md +11 -0
  29. package/src/components/Table/Table.module.css +14 -0
  30. package/src/components/Table/Table.stories.tsx +1 -1
  31. package/src/components/Table/Table.tsx +9 -1
  32. package/src/components/Table/USAGE.md +4 -2
  33. package/src/components/TextArea/TextArea.module.css +9 -2
  34. package/src/components/TextField/TextField.module.css +9 -2
  35. package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +7 -1
  36. package/src/components/TimePicker/TimePicker.module.css +44 -0
  37. package/src/components/TimePicker/TimePicker.stories.tsx +22 -0
  38. package/src/components/TimePicker/TimePicker.tsx +1 -0
  39. package/src/components/Timeline/Timeline.module.css +5 -0
  40. package/src/components/Toast/Toast.module.css +8 -0
  41. package/src/components/Tooltip/Tooltip.module.css +9 -1
  42. package/src/components/TransferList/TransferList.module.css +9 -1
@@ -41,6 +41,11 @@ const meta: Meta<LoaderStoryArgs> = {
41
41
  description:
42
42
  "Applies a wrapping context to observe rendering logic externally",
43
43
  },
44
+ animate: {
45
+ control: "boolean",
46
+ description:
47
+ "Freezes the CSS animation when false — deterministic, for visual regression",
48
+ },
44
49
  },
45
50
  };
46
51
 
@@ -48,6 +53,11 @@ export default meta;
48
53
 
49
54
  type Story = StoryObj<LoaderStoryArgs>;
50
55
 
56
+ /**
57
+ * Animated — excluded from visual regression (`adapter-tester.config.json`),
58
+ * since a moving animation diffs differently every run. See the `Static*`
59
+ * stories below for the deterministic, visual-regression-covered equivalents.
60
+ */
51
61
  export const Default: Story = {
52
62
  args: {
53
63
  variant: "oval",
@@ -62,33 +72,43 @@ export const Default: Story = {
62
72
  ),
63
73
  };
64
74
 
75
+ /** `animate: false` freezes the spin — deterministic for visual regression. */
65
76
  export const StaticOvalDefault: Story = {
66
77
  args: {
67
78
  variant: "oval",
68
79
  size: "default",
80
+ animate: false,
69
81
  },
70
82
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
71
83
  render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
72
84
  };
73
85
 
86
+ /** `animate: false` freezes the bars — deterministic for visual regression. */
74
87
  export const StaticBarsLarge: Story = {
75
88
  args: {
76
89
  variant: "bars",
77
90
  size: "large",
91
+ animate: false,
78
92
  },
79
93
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
80
94
  render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
81
95
  };
82
96
 
97
+ /** `animate: false` freezes the dots — deterministic for visual regression. */
83
98
  export const StaticDotsSmall: Story = {
84
99
  args: {
85
100
  variant: "dots",
86
101
  size: "sm",
102
+ animate: false,
87
103
  },
88
104
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
89
105
  render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
90
106
  };
91
107
 
108
+ /**
109
+ * Animated — excluded from visual regression, same as `Default`; this one
110
+ * additionally demonstrates rendering inside a `layer={2}` context.
111
+ */
92
112
  export const LayerTwoOval: Story = {
93
113
  args: {
94
114
  variant: "oval",
@@ -17,7 +17,13 @@ export type LoaderProps = RecursicaOverStyled<
17
17
  >;
18
18
 
19
19
  export const Loader = forwardRef<HTMLSpanElement, LoaderProps>(function Loader(
20
- { variant = "oval", size = "default", overStyled = false, ...rest },
20
+ {
21
+ variant = "oval",
22
+ size = "default",
23
+ animate = true,
24
+ overStyled = false,
25
+ ...rest
26
+ },
21
27
  ref,
22
28
  ) {
23
29
  const mapSize = {
@@ -53,6 +59,7 @@ export const Loader = forwardRef<HTMLSpanElement, LoaderProps>(function Loader(
53
59
  type={variant}
54
60
  data-variant={variant}
55
61
  data-size={resolvedSize}
62
+ data-animate={animate}
56
63
  className={finalClass}
57
64
  classNames={mergedClassNames}
58
65
  />
@@ -72,7 +72,9 @@
72
72
  var(
73
73
  --recursica_ui-kit_components_modal_properties_header-footer-horizontal-padding
74
74
  );
75
- background-color: transparent; /* Inherit from content */
75
+ background-color: var(
76
+ --recursica_ui-kit_components_modal_properties_colors_header-background-color
77
+ );
76
78
  }
77
79
 
78
80
  .title {
@@ -201,7 +203,9 @@
201
203
  var(
202
204
  --recursica_ui-kit_components_modal_properties_header-footer-horizontal-padding
203
205
  );
204
- background-color: transparent;
206
+ background-color: var(
207
+ --recursica_ui-kit_components_modal_properties_colors_footer-background-color
208
+ );
205
209
  display: flex;
206
210
  justify-content: flex-end;
207
211
  gap: var(--recursica_ui-kit_components_modal_properties_button-gap);
@@ -35,13 +35,18 @@
35
35
  }
36
36
 
37
37
  /* HARDCODED VALUES:
38
- - border-width: 1px. Native geometric boundary for the input box.
39
38
  - border-style: solid. Native structural rendering rule.
40
39
  - outline: none. Bypassing browser focus rings to rely strictly on Recursica focus states natively.
41
40
  - flex/layout: display: flex on the root wrapper safely encapsulating internal section rendering.
42
41
  - Controls: Hardcoded to bypass Mantine's default borders and backgrounds on the increment/decrement arrows.
43
42
  */
44
43
 
44
+ /* EXEMPTIONS:
45
+ - border-size is constant across disabled/error states — only border-color changes; the
46
+ disabled/error border-size tokens are never applied by design. */
47
+ /* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_border-size */
48
+ /* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_border-size */
49
+
45
50
  .root {
46
51
  display: flex;
47
52
  position: relative;
@@ -92,7 +97,9 @@
92
97
  border-radius: var(
93
98
  --recursica_ui-kit_components_number-input_properties_border-radius
94
99
  );
95
- border-width: 1px;
100
+ border-width: var(
101
+ --recursica_ui-kit_components_number-input_properties_border-size
102
+ );
96
103
  border-style: solid;
97
104
 
98
105
  /* Strict Typography Unification */
@@ -4,6 +4,12 @@
4
4
  - Scope prefix .root to enforce Figma tokens over Mantine's inline calculation without using !important
5
5
  */
6
6
 
7
+ /* EXEMPTIONS:
8
+ - The generic, non-state-specific segmented-control-item border-radius is ignored: the
9
+ selected/unselected state-specific border-radius tokens below already cover both states an
10
+ item can be in. */
11
+ /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_item_border-radius */
12
+
7
13
  .root {
8
14
  background-color: var(
9
15
  --recursica_ui-kit_components_segmented-control_properties_colors_background-color
@@ -56,6 +56,17 @@ recipe with a plain `Checkbox`; the only MUI product with built-in selection is
56
56
  selected-row background/token state; wiring an actual checkbox column is a separate,
57
57
  not-yet-scoped ask.
58
58
 
59
+ ## Currency alignment on header/footer reuses the table-cell token
60
+
61
+ `--recursica_ui-kit_components_table-cell_properties_currency-style_text-align` (`right`) is the
62
+ only currency-style text-align token the UI Kit exports — there's no
63
+ `table-header_properties_currency-style_*` block at all, and the
64
+ `table-footer_properties_currency-style_*` block skips `text-align` specifically. `Table.module.css`
65
+ reuses the table-cell token for both `thead th[data-currency="true"]` and
66
+ `tfoot td[data-currency="true"]` so header/footer currency cells stay right-aligned in step with
67
+ the body. `Table.Th` gained a `variant` prop (mirroring `Table.Td`) to set `data-currency` since
68
+ header cells had no way to opt into this before.
69
+
59
70
  ## Deliberately not changed
60
71
 
61
72
  - **No wrapper divs** — Forge wraps the table in two divs to get a scrollable bordered
@@ -218,6 +218,15 @@
218
218
  );
219
219
  }
220
220
 
221
+ /* Currency column header alignment override. No dedicated table-header currency-style token
222
+ exists yet, so this reuses the table-cell currency-style text-align token to match the
223
+ currency value cells below it. */
224
+ .root thead th[data-currency="true"] {
225
+ text-align: var(
226
+ --recursica_ui-kit_components_table-cell_properties_currency-style_text-align
227
+ );
228
+ }
229
+
221
230
  /* Header sort icon & gap spacing helper */
222
231
  .root thead th .sortIcon {
223
232
  width: var(--recursica_ui-kit_components_table-header_properties_icon-size);
@@ -407,6 +416,11 @@
407
416
  line-height: var(
408
417
  --recursica_ui-kit_components_table-footer_properties_currency-style_line-height
409
418
  );
419
+ /* No table-footer currency-style text-align token exists yet; reuse the table-cell one so
420
+ footer currency cells match the body's right alignment. */
421
+ text-align: var(
422
+ --recursica_ui-kit_components_table-cell_properties_currency-style_text-align
423
+ );
410
424
  text-decoration: var(
411
425
  --recursica_ui-kit_components_table-footer_properties_currency-style_text-decoration
412
426
  );
@@ -117,7 +117,7 @@ export const CurrencyColumnWithFooter: Story = {
117
117
  <Table.Thead>
118
118
  <Table.Tr>
119
119
  <Table.Th>Item</Table.Th>
120
- <Table.Th>Price</Table.Th>
120
+ <Table.Th variant="currency">Price</Table.Th>
121
121
  </Table.Tr>
122
122
  </Table.Thead>
123
123
  <Table.Tbody>
@@ -114,7 +114,14 @@ export type TableThProps = RecursicaOverStyled<
114
114
 
115
115
  export const TableTh = forwardRef<HTMLTableCellElement, TableThProps>(
116
116
  function TableTh(
117
- { overStyled = false, sorted = false, disabled = false, children, ...rest },
117
+ {
118
+ overStyled = false,
119
+ sorted = false,
120
+ disabled = false,
121
+ variant = "default",
122
+ children,
123
+ ...rest
124
+ },
118
125
  ref,
119
126
  ) {
120
127
  const sanitizedProps = filterStylingProps(rest, overStyled);
@@ -124,6 +131,7 @@ export const TableTh = forwardRef<HTMLTableCellElement, TableThProps>(
124
131
  {...(sanitizedProps as unknown as MantineTableThProps)}
125
132
  data-sorted={sorted ? "true" : undefined}
126
133
  data-disabled={disabled ? "true" : undefined}
134
+ data-currency={variant === "currency" ? "true" : undefined}
127
135
  aria-sort={
128
136
  sorted === "asc"
129
137
  ? "ascending"
@@ -43,7 +43,7 @@ export default function Demo() {
43
43
  ## 3. Row and Cell States
44
44
 
45
45
  - **`Table.Tr`**: `selected` applies the selected-row background; `disabled` dims the row and applies the disabled cell colors to every cell in it.
46
- - **`Table.Th`**: `sorted="asc" | "desc"` applies the sorted header style and renders the matching chevron icon; omit it (or pass `false`) for the unsorted style. `disabled` dims the header cell.
46
+ - **`Table.Th`**: `sorted="asc" | "desc"` applies the sorted header style and renders the matching chevron icon; omit it (or pass `false`) for the unsorted style. `disabled` dims the header cell. `variant="currency"` right-aligns the header to match a currency column's value cells.
47
47
  - **`Table.Td`**: `variant="currency"` applies the currency text style (for numeric/monetary columns); `disabled` dims the cell.
48
48
 
49
49
  ```tsx
@@ -51,7 +51,9 @@ export default function Demo() {
51
51
  <Table.Thead>
52
52
  <Table.Tr>
53
53
  <Table.Th>Name</Table.Th>
54
- <Table.Th sorted="asc">Balance</Table.Th>
54
+ <Table.Th sorted="asc" variant="currency">
55
+ Balance
56
+ </Table.Th>
55
57
  </Table.Tr>
56
58
  </Table.Thead>
57
59
  <Table.Tbody>
@@ -35,12 +35,17 @@
35
35
  }
36
36
 
37
37
  /* HARDCODED VALUES:
38
- - border-width: 1px. Native geometric boundary for the input box.
39
38
  - border-style: solid. Native structural rendering rule.
40
39
  - outline: none. Bypassing browser focus rings to rely strictly on Recursica focus states natively.
41
40
  - flex/layout: display: flex on the root wrapper safely encapsulating internal section rendering.
42
41
  */
43
42
 
43
+ /* EXEMPTIONS:
44
+ - border-size is constant across disabled/error states — only border-color changes; the
45
+ disabled/error border-size tokens are never applied by design. */
46
+ /* recursica-ignore: --recursica_ui-kit_components_textarea_variants_states_disabled_properties_border-size */
47
+ /* recursica-ignore: --recursica_ui-kit_components_textarea_variants_states_error_properties_border-size */
48
+
44
49
  .root {
45
50
  display: flex;
46
51
  position: relative;
@@ -78,7 +83,9 @@
78
83
  border-radius: var(
79
84
  --recursica_ui-kit_components_textarea_properties_border-radius
80
85
  );
81
- border-width: 1px;
86
+ border-width: var(
87
+ --recursica_ui-kit_components_textarea_properties_border-size
88
+ );
82
89
  border-style: solid;
83
90
 
84
91
  /* Strict Typography Unification */
@@ -35,12 +35,17 @@
35
35
  }
36
36
 
37
37
  /* HARDCODED VALUES:
38
- - border-width: 1px. Native geometric boundary for the input box.
39
38
  - border-style: solid. Native structural rendering rule.
40
39
  - outline: none. Bypassing browser focus rings to rely strictly on Recursica focus states natively.
41
40
  - flex/layout: display: flex on the root wrapper safely encapsulating internal section rendering.
42
41
  */
43
42
 
43
+ /* EXEMPTIONS:
44
+ - border-size is constant across disabled/error states — only border-color changes; the
45
+ disabled/error border-size tokens are never applied by design. */
46
+ /* recursica-ignore: --recursica_ui-kit_components_text-field_variants_states_disabled_properties_border-size */
47
+ /* recursica-ignore: --recursica_ui-kit_components_text-field_variants_states_error_properties_border-size */
48
+
44
49
  .root {
45
50
  display: flex;
46
51
  position: relative;
@@ -91,7 +96,9 @@
91
96
  border-radius: var(
92
97
  --recursica_ui-kit_components_text-field_properties_border-radius
93
98
  );
94
- border-width: 1px;
99
+ border-width: var(
100
+ --recursica_ui-kit_components_text-field_properties_border-size
101
+ );
95
102
  border-style: solid;
96
103
 
97
104
  /* Strict Typography Unification */
@@ -36,7 +36,7 @@ Because the AM/PM behavior is fixed, several of Mantine's `TimePicker` props are
36
36
  ## Design tokens
37
37
 
38
38
  - No dedicated `min-height` token exists for `time-picker` (unlike `text-field`/`date-picker`) — `.fieldsGroup`'s explicit `height` is derived from `text_line-height` instead, which also fixes a real bug: Mantine's own field CSS sets `height: 100%` on every field to fill `.fieldsGroup`, and a percentage height only resolves against a _concrete_ parent height. Before this, `.fieldsGroup` was auto-height (padding only), so `100%` resolved to `0` — this is why the AM/PM control was invisible in early builds (verified via real headless-browser inspection, not just markup checks — a raw server-render test had missed this entirely since it doesn't compute layout).
39
- - `icon-size`/`icon-color`/`icon-text-gap`/`placeholder-opacity` are exempted (`recursica-ignore`) the time field has no icon slot and no native `::placeholder` pseudo-element to target (each `SpinInput`'s `"--"` placeholder is styled internally by Mantine).
39
+ - `icon-size`/`icon-color`/`icon-text-gap`/`placeholder-opacity` (plus the disabled/error `icon-color` variants) are wired via `leftSection` see "Leading icon" below. Not exempted: despite an earlier note here claiming otherwise, `SpinInput` (`@mantine/dates`) renders a real `<input placeholder="--">` per hour/minute/second segment, so `::placeholder` targets it like any other input.
40
40
  - The AM/PM `BareDropdown` draws its own border/background/padding from `Dropdown`'s own tokens via `Dropdown.module.css` — it does not reuse any `time-picker` tokens.
41
41
 
42
42
  ## Read-Only Implementation
@@ -76,6 +76,12 @@ The MUI adapter's equivalent (`mui-adapter/src/components/TimePicker/TimePicker.
76
76
 
77
77
  Note: selecting AM/PM before any hour is typed is still a no-op in both adapters (`handleMeridiemChange` bails out when `hour`/`internalValue` is undefined) — that's pre-existing, shared behavior in both adapters, not something this fix touches.
78
78
 
79
+ ## Leading icon (Matt Massey, 2026-08-30)
80
+
81
+ Added `leftSection` (`RecursicaTimePickerProps`), matching `TextField`/`AutoComplete`'s naming and convention: purely decorative, consumer-supplied, no default. Unlike `DatePicker`'s fixed `CalendarIcon`, there's no single icon that makes sense for every `TimePicker` use, so this is opt-in only.
82
+
83
+ Mantine's `TimePicker` already accepts `leftSection` (inherited from `@mantine/core`'s `__InputProps` via `InputBase`, which it renders through internally with `component: "div"` — confirmed by reading `TimePicker.mjs`/`Input.mjs` directly) — it was already passing through unstyled before this change (not blocked by `filterStylingProps`), just never mapped to a `classNames.section`/CSS, so an icon would render with no size or color. Added `section: styles.section` to the `classNames` map and the token-driven `.section`/`.section svg` rules (icon-size, icon-color, disabled/error icon-color) to actually style it.
84
+
79
85
  ## Visual review round 5 (Matt Massey, 2026-08-08)
80
86
 
81
87
  - **AM/PM error-state border wasn't changing**: `BareDropdown` set `data-error`/`data-disabled` via Mantine's `wrapperProps` — which targets the _outer_ `Input.Wrapper` (the label/description/error stacking element), a different, ancestor element from the "wrapper" styles-api slot that actually carries `styles.root`'s border. `Dropdown.module.css`'s `.root[data-error]`/`[data-disabled]` rules never matched as a result. This is the exact same "two different things both called 'wrapper'" trap as the earlier `style` vs `styles.wrapper` bug. Fixed by using `attributes={{ wrapper: {...} }}` instead — the styles-api hook that actually targets the same slot as `classNames.wrapper`. **This is a shared, pre-existing bug** — the real `Dropdown.tsx` had the identical mistake, so its error/disabled states never applied a border color either; fixed there too (low-risk, purely-additive, same reasoning as the `data-selected` fix above).
@@ -6,6 +6,12 @@
6
6
  * recursica-allow-brand: --recursica_brand_states_focus_margin
7
7
  */
8
8
 
9
+ /* EXEMPTIONS:
10
+ - border-size is constant across disabled/error states — only border-color changes; the
11
+ disabled/error border-size tokens are never applied by design. */
12
+ /* recursica-ignore: --recursica_ui-kit_components_time-picker_variants_states_disabled_properties_border-size */
13
+ /* recursica-ignore: --recursica_ui-kit_components_time-picker_variants_states_error_properties_border-size */
14
+
9
15
  /* LAYOUT SPACING OVERRIDES:
10
16
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
11
17
  .layoutOverride {
@@ -45,6 +51,9 @@
45
51
  .timeWrapper {
46
52
  display: flex;
47
53
  align-items: center;
54
+ /* Gap between the leading-icon section (when leftSection is passed) and the field itself — a
55
+ no-op with no icon, since gap has no effect between fewer than two flex children. */
56
+ gap: var(--recursica_ui-kit_components_time-picker_properties_icon-text-gap);
48
57
  position: relative;
49
58
  box-sizing: border-box;
50
59
  /* min-width, not width: the `width` token was authored for a single plain input (confirmed against
@@ -117,6 +126,29 @@
117
126
  );
118
127
  }
119
128
 
129
+ /* Leading icon (leftSection). Only one icon-color token exists for time-picker (unlike
130
+ text-field's separate leading/trailing) — this field has no right-side icon slot. */
131
+ .section {
132
+ display: flex;
133
+ align-items: center;
134
+ }
135
+
136
+ .section :global(svg) {
137
+ width: var(--recursica_ui-kit_components_time-picker_properties_icon-size);
138
+ height: var(--recursica_ui-kit_components_time-picker_properties_icon-size);
139
+ color: var(
140
+ --recursica_ui-kit_components_time-picker_properties_colors_icon-color
141
+ );
142
+ }
143
+
144
+ /* SpinInput renders a real <input placeholder="--">, one per hour/minute/second segment. */
145
+ .timeField::placeholder {
146
+ opacity: var(
147
+ --recursica_ui-kit_components_time-picker_properties_placeholder-opacity
148
+ );
149
+ color: inherit;
150
+ }
151
+
120
152
  .timeField {
121
153
  font-family: var(
122
154
  --recursica_ui-kit_components_time-picker_properties_text_font-family
@@ -202,6 +234,12 @@
202
234
  ) !important;
203
235
  }
204
236
 
237
+ .root[data-error="true"] .section :global(svg) {
238
+ color: var(
239
+ --recursica_ui-kit_components_time-picker_variants_states_error_properties_colors_icon-color
240
+ ) !important;
241
+ }
242
+
205
243
  /* Disabled State Mapping (Propagated strictly down from the wrapper DOM context) */
206
244
  .root[data-disabled="true"] .timeWrapper {
207
245
  border-color: var(
@@ -218,3 +256,9 @@
218
256
  ) !important;
219
257
  cursor: not-allowed;
220
258
  }
259
+
260
+ .root[data-disabled="true"] .section :global(svg) {
261
+ color: var(
262
+ --recursica_ui-kit_components_time-picker_variants_states_disabled_properties_colors_icon-color
263
+ ) !important;
264
+ }
@@ -101,6 +101,28 @@ export const ErrorState: Story = {
101
101
  },
102
102
  };
103
103
 
104
+ export const WithLeadingIcon: Story = {
105
+ args: {
106
+ label: "Meeting Time",
107
+ assistiveText: "Choose the start time in your local timezone.",
108
+ leftSection: (
109
+ <svg
110
+ width="24"
111
+ height="24"
112
+ viewBox="0 0 24 24"
113
+ fill="none"
114
+ stroke="currentColor"
115
+ strokeWidth="2"
116
+ strokeLinecap="round"
117
+ strokeLinejoin="round"
118
+ >
119
+ <circle cx="12" cy="12" r="10"></circle>
120
+ <polyline points="12 6 12 12 16 14"></polyline>
121
+ </svg>
122
+ ),
123
+ },
124
+ };
125
+
104
126
  export const StaticReadOnly: Story = {
105
127
  args: {
106
128
  label: "Static ReadOnly Review",
@@ -255,6 +255,7 @@ export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(
255
255
  {
256
256
  wrapper: styles.timeWrapper,
257
257
  input: styles.timeInput,
258
+ section: styles.section,
258
259
  fieldsGroup: styles.fieldsGroup,
259
260
  field: styles.timeField,
260
261
  },
@@ -43,6 +43,11 @@
43
43
  --recursica_ui-kit_components_timeline-bullet_variants_types_default_properties_bullet-size
44
44
  ) !important;
45
45
  }
46
+ /* EXEMPTIONS:
47
+ - The avatar variant's own avatar-size token is ignored — sizing is deliberately pinned to
48
+ the default bullet-size above (not the avatar's own size) to keep the connector line
49
+ aligned across bullet variants; the child Avatar component governs its own visual size. */
50
+ /* recursica-ignore: --recursica_ui-kit_components_timeline-bullet_variants_types_avatar_properties_avatar-size */
46
51
 
47
52
  /* Control connector lines via the item ::before pseudo-element */
48
53
  /* Inactive connector */
@@ -2,6 +2,14 @@
2
2
  - border-style: solid. Baseline so the per-variant border-color tokens actually render.
3
3
  */
4
4
 
5
+ /* EXEMPTIONS:
6
+ - elevation_layer-1/2/3 are ignored: a toast's elevation doesn't depend on the layer it's
7
+ summoned from — all four layer variants resolve to the same brand elevation value, so
8
+ layer-0 (already wired below) is the correct, layer-agnostic choice for all of them. */
9
+ /* recursica-ignore: --recursica_ui-kit_components_toast_properties_elevation_layer-1 */
10
+ /* recursica-ignore: --recursica_ui-kit_components_toast_properties_elevation_layer-2 */
11
+ /* recursica-ignore: --recursica_ui-kit_components_toast_properties_elevation_layer-3 */
12
+
5
13
  /* ======================================
6
14
  TOAST / NOTIFICATION ROOT
7
15
  ====================================== */
@@ -3,11 +3,19 @@
3
3
  which may not set border-style natively). Same pattern as Menu / HoverCard.
4
4
  - arrowSize defaults to 16 in Tooltip.tsx. Mantine uses arrowSize for inline width/height
5
5
  and positioning (-arrowSize/2) calculations that cannot be CSS-driven. The default matches
6
- the Recursica beak-size token (16px). See COMPONENT_ISSUES.md for details.
6
+ the Recursica beak-size token (16px).
7
+ - arrow inset/offset is Mantine's own default (no arrowOffset override) — same "must be a JS
8
+ prop, not CSS-driven" constraint as arrowSize.
7
9
  - All structural layout (display, position, overflow) is deferred to Mantine's native
8
10
  behavior. We only override visual design tokens (colors, typography, spacing, borders).
9
11
  */
10
12
 
13
+ /* EXEMPTIONS:
14
+ - beak-size/beak-inset are ignored: the arrow's size and position are Mantine's own
15
+ `arrowSize`/`arrowOffset` JS props, not CSS-driven — see HARDCODED VALUES above. */
16
+ /* recursica-ignore: --recursica_ui-kit_components_tooltip_properties_beak-size */
17
+ /* recursica-ignore: --recursica_ui-kit_components_tooltip_properties_beak-inset */
18
+
11
19
  /* ======================================
12
20
  TOOLTIP CONTAINER
13
21
  ====================================== */
@@ -4,6 +4,12 @@
4
4
  - `properties_title-filter-gap` sits between each pane's header row and its search field.
5
5
  - `properties_filter-items-gap` sits between each pane's search field and its item list. */
6
6
 
7
+ /* EXEMPTIONS:
8
+ - border-size is constant across disabled/error states — only border-color changes; the
9
+ disabled/error border-size tokens are never applied by design. */
10
+ /* recursica-ignore: --recursica_ui-kit_components_transfer-list_variants_states_disabled_properties_border-size */
11
+ /* recursica-ignore: --recursica_ui-kit_components_transfer-list_variants_states_error_properties_border-size */
12
+
7
13
  .layoutOverride {
8
14
  --form-control-margin-bottom: var(
9
15
  --recursica_ui-kit_components_transfer-list_variants_layouts_stacked_properties_top-bottom-margin
@@ -41,7 +47,9 @@
41
47
  var(
42
48
  --recursica_ui-kit_components_transfer-list_properties_horizontal-padding
43
49
  );
44
- border-width: 1px;
50
+ border-width: var(
51
+ --recursica_ui-kit_components_transfer-list_properties_border-size
52
+ );
45
53
  border-style: solid;
46
54
  border-radius: var(
47
55
  --recursica_ui-kit_components_transfer-list_properties_border-radius