@recursica/mantine-adapter 0.55.1 → 0.55.2

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 (33) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.d.ts +21 -0
  3. package/dist/mantine-adapter.cjs +2 -2
  4. package/dist/mantine-adapter.cjs.map +1 -1
  5. package/dist/mantine-adapter.css +1 -1
  6. package/dist/mantine-adapter.js +905 -873
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/package.json +2 -2
  9. package/src/components/Accordion/Accordion.stories.tsx +0 -24
  10. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +3 -9
  11. package/src/components/Avatar/Avatar.stories.tsx +0 -15
  12. package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +33 -0
  13. package/src/components/Breadcrumb/Breadcrumb.module.css +20 -0
  14. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +55 -5
  15. package/src/components/Breadcrumb/Breadcrumb.tsx +17 -3
  16. package/src/components/Breadcrumb/markCurrentPageItem.test.ts +49 -0
  17. package/src/components/Button/Button.module.css +16 -0
  18. package/src/components/Button/Button.tsx +14 -1
  19. package/src/components/Button/IMPLEMENTATION_NOTES.md +31 -5
  20. package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +2 -1
  21. package/src/components/DatePicker/DatePicker.tsx +22 -8
  22. package/src/components/Grid/GRID_IMPLEMENTATION_NOTES.md +9 -0
  23. package/src/components/Grid/Grid.stories.tsx +24 -0
  24. package/src/components/Link/IMPLEMENTATION_NOTES.md +1 -1
  25. package/src/components/Link/Link.module.css +20 -0
  26. package/src/components/NumberInput/NUMBER_INPUT_IMPLEMENTATION_NOTES.md +6 -0
  27. package/src/components/NumberInput/NumberInput.module.css +11 -0
  28. package/src/components/Stepper/IMPLEMENTATION_NOTES.md +28 -0
  29. package/src/components/Stepper/Stepper.module.css +6 -6
  30. package/src/components/Tabs/IMPLEMENTATION_NOTES.md +27 -0
  31. package/src/components/Tabs/Tabs.module.css +30 -9
  32. package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +31 -0
  33. package/src/components/TimePicker/TimePicker.module.css +30 -3
@@ -0,0 +1,28 @@
1
+ # Stepper Implementation Notes
2
+
3
+ ## Completed/current label & description colors never applied (source-of-truth audit, 2026-08-30)
4
+
5
+ **Bug:** `.step[data-progress="completed"]`, `.step[data-progress="progress"]`, and
6
+ `.step[data-progress="pending"]` selectors in `Stepper.module.css` checked `data-progress` for
7
+ literal string values (`"completed"`/`"progress"`/`"pending"`) that Mantine's `Stepper.Step` never
8
+ sets. Mantine actually stamps two independent boolean-presence attributes on the step root:
9
+ `data-completed` (present, value `"true"`, when the step index is before `active`) and
10
+ `data-progress` (present, value `"true"`, only on the active step); neither attribute exists at
11
+ all on upcoming steps. Since `[data-progress="completed"]` and `[data-progress="pending"]` never
12
+ match anything real, every step's label and description silently fell through to Mantine's own
13
+ unstyled defaults instead of any Recursica token — for description text specifically, that meant
14
+ Mantine's native muted/dimmed gray (`rgb(134, 142, 150)`, not any `--recursica_*` variable)
15
+ appeared on _every_ step regardless of state, including completed/current steps where the design
16
+ tokens (`..._completed-description-color`/`..._current-description-color`) both resolve to the
17
+ same near-black `layer-0_elements_text_color` as the label. This was visually indistinguishable
18
+ for labels (Mantine's own default text color happens to look close to black too) but clearly
19
+ visible for descriptions, and was mistaken for a mui-adapter bug (mui's class-based `.Mui-active`/
20
+ `.Mui-completed` selectors were already correctly reading these tokens, so mui rendered
21
+ completed/current descriptions in the token's actual dark color while mantine — the presumed
22
+ source of truth — rendered them in this incidental gray).
23
+
24
+ **Fix:** Match the same working pattern already used one selector up for `.stepIcon` (`.stepIcon
25
+ [data-completed]` / `.stepIcon[data-progress]`, which check attribute _presence_, not a value) —
26
+ changed the `.step` label/description rules to `[data-completed]` / `[data-progress]` /
27
+ `:not([data-completed]):not([data-progress])`. No `mui-adapter` change was needed for this: its
28
+ class-based state selectors were already correct against the design tokens.
@@ -298,13 +298,13 @@
298
298
  height: var(--stepper-svg-size);
299
299
  }
300
300
 
301
- .root .step[data-progress="completed"] .stepLabel {
301
+ .root .step[data-completed] .stepLabel {
302
302
  color: var(
303
303
  --recursica_ui-kit_components_stepper_properties_colors_completed-label-color
304
304
  );
305
305
  }
306
306
 
307
- .root .step[data-progress="completed"] .stepDescription {
307
+ .root .step[data-completed] .stepDescription {
308
308
  color: var(
309
309
  --recursica_ui-kit_components_stepper_properties_colors_completed-description-color
310
310
  );
@@ -323,26 +323,26 @@
323
323
  );
324
324
  }
325
325
 
326
- .root .step[data-progress="progress"] .stepLabel {
326
+ .root .step[data-progress] .stepLabel {
327
327
  color: var(
328
328
  --recursica_ui-kit_components_stepper_properties_colors_current-label-color
329
329
  );
330
330
  }
331
331
 
332
- .root .step[data-progress="progress"] .stepDescription {
332
+ .root .step[data-progress] .stepDescription {
333
333
  color: var(
334
334
  --recursica_ui-kit_components_stepper_properties_colors_current-description-color
335
335
  );
336
336
  }
337
337
 
338
338
  /* Upcoming State (Pending Labels) */
339
- .root .step[data-progress="pending"] .stepLabel {
339
+ .root .step:not([data-completed]):not([data-progress]) .stepLabel {
340
340
  color: var(
341
341
  --recursica_ui-kit_components_stepper_properties_colors_upcoming-label-color
342
342
  );
343
343
  }
344
344
 
345
- .root .step[data-progress="pending"] .stepDescription {
345
+ .root .step:not([data-completed]):not([data-progress]) .stepDescription {
346
346
  color: var(
347
347
  --recursica_ui-kit_components_stepper_properties_colors_upcoming-description-color
348
348
  );
@@ -0,0 +1,27 @@
1
+ # Tabs Implementation Notes
2
+
3
+ ## `inverted` never actually moved the tab list below the content (2026-08-30, source-of-truth audit)
4
+
5
+ **Reported symptom:** in the `Inverted` story, content should render above the tab list (tabs
6
+ below), with padding between them — the padding looked missing "when content is on top".
7
+
8
+ **What was actually happening:** `inverted` is a real, native `@mantine/core` `Tabs` prop
9
+ (confirmed by reading `@mantine/core`'s own `Tabs.cjs`/`Tabs.css`) — this adapter doesn't even
10
+ reference it directly, it flows straight through `...rest` to `<MantineTabs>`. But Mantine's own
11
+ `inverted` support only flips the active-indicator line and border-radius direction (top vs
12
+ bottom) via `data-inverted` CSS selectors; it does not reposition `.list` below `.panel`. The
13
+ story always renders `<Tabs.List>` before the `<Tabs.Panel>`s in JSX regardless of `inverted`, so
14
+ "padding missing when content is on top" wasn't reproducible as such: content was never on top to
15
+ begin with, before this fix.
16
+
17
+ **Fix:** Mantine renders `Tabs.List`/`Tabs.Panel` as flat siblings directly under `.root` despite
18
+ the nested JSX (confirmed via live DOM dump) — so making `.root[data-orientation="horizontal"]`
19
+ a flex column and giving `.list` `order: 1` only when `[data-inverted]` reorders them visually
20
+ with zero DOM changes, no compound-component internals to touch. Scoped to horizontal only —
21
+ vertical's "instead of left" flip isn't exercised by any story. Also introduced a shared
22
+ `--tabs-content-gap` custom property per variant (previously each variant inlined its own long
23
+ token reference directly into `.list`'s `margin-bottom`) so the same gap can be redirected:
24
+ `margin-bottom: 0; margin-top: var(--tabs-content-gap);` when inverted, since the gap now needs
25
+ to land above the now-trailing tab list instead of below it. Verified live against mui-adapter's
26
+ identical fix (see its own `Tabs/IMPLEMENTATION_NOTES.md`) — pixel-equivalent stacking order and
27
+ gap; no regression on Default/Outline/Pills/Vertical stories.
@@ -21,6 +21,24 @@
21
21
  .panel (content)
22
22
  */
23
23
 
24
+ /* `inverted` (RecursicaTabsProps): "tabs list at the bottom ... instead of top". Mantine's own
25
+ Tabs only reuses `data-inverted` to flip indicator-line/border-radius direction (see
26
+ @mantine/core's Tabs.css) — it does NOT move `.list` below `.panel`, since both are plain
27
+ block-level children stacking in DOM order. `.list`/`.panel` here are direct siblings under
28
+ this same `.root` (Mantine renders Tabs.List/Tabs.Panel flat, despite the nested JSX), so a
29
+ flex column + `order` on `.list` alone reorders them visually with no DOM change. Scoped to
30
+ horizontal only — vertical's "instead of left" flip isn't exercised by any story yet.
31
+ See IMPLEMENTATION_NOTES.md. */
32
+ .root[data-orientation="horizontal"] {
33
+ display: flex;
34
+ flex-direction: column;
35
+ }
36
+ .root[data-orientation="horizontal"][data-inverted] .list {
37
+ order: 1;
38
+ margin-bottom: 0;
39
+ margin-top: var(--tabs-content-gap);
40
+ }
41
+
24
42
  /* ---------------------------------
25
43
  VARIANTS
26
44
  Sizing, geometry, colors, and typography are all scoped per style (default/outline/pills) in
@@ -72,13 +90,14 @@
72
90
  --tabs-space-between-tabs: var(
73
91
  --recursica_ui-kit_components_tabs_variants_styles_default_variants_orientation_horizontal_properties_space-between-tabs
74
92
  );
93
+ --tabs-content-gap: var(
94
+ --recursica_ui-kit_components_tabs_variants_styles_default_variants_orientation_horizontal_properties_tabs-content-gap
95
+ );
75
96
  }
76
97
  .root[data-variant="default"][data-orientation="horizontal"] .list {
77
98
  gap: var(--tabs-space-between-tabs);
78
99
  width: 100%;
79
- margin-bottom: var(
80
- --recursica_ui-kit_components_tabs_variants_styles_default_variants_orientation_horizontal_properties_tabs-content-gap
81
- );
100
+ margin-bottom: var(--tabs-content-gap);
82
101
  }
83
102
 
84
103
  .root[data-variant="default"][data-orientation="vertical"] {
@@ -141,13 +160,14 @@
141
160
  --tabs-space-between-tabs: var(
142
161
  --recursica_ui-kit_components_tabs_variants_styles_outline_variants_orientation_horizontal_properties_space-between-tabs
143
162
  );
163
+ --tabs-content-gap: var(
164
+ --recursica_ui-kit_components_tabs_variants_styles_outline_variants_orientation_horizontal_properties_tabs-content-gap
165
+ );
144
166
  }
145
167
  .root[data-variant="outline"][data-orientation="horizontal"] .list {
146
168
  gap: var(--tabs-space-between-tabs);
147
169
  width: 100%;
148
- margin-bottom: var(
149
- --recursica_ui-kit_components_tabs_variants_styles_outline_variants_orientation_horizontal_properties_tabs-content-gap
150
- );
170
+ margin-bottom: var(--tabs-content-gap);
151
171
  }
152
172
 
153
173
  .root[data-variant="outline"][data-orientation="vertical"] {
@@ -204,13 +224,14 @@
204
224
  --tabs-space-between-tabs: var(
205
225
  --recursica_ui-kit_components_tabs_variants_styles_pills_variants_orientation_horizontal_properties_space-between-tabs
206
226
  );
227
+ --tabs-content-gap: var(
228
+ --recursica_ui-kit_components_tabs_variants_styles_pills_variants_orientation_horizontal_properties_tabs-content-gap
229
+ );
207
230
  }
208
231
  .root[data-variant="pills"][data-orientation="horizontal"] .list {
209
232
  gap: var(--tabs-space-between-tabs);
210
233
  width: 100%;
211
- margin-bottom: var(
212
- --recursica_ui-kit_components_tabs_variants_styles_pills_variants_orientation_horizontal_properties_tabs-content-gap
213
- );
234
+ margin-bottom: var(--tabs-content-gap);
214
235
  }
215
236
 
216
237
  .root[data-variant="pills"][data-orientation="vertical"] {
@@ -82,6 +82,37 @@ Added `leftSection` (`RecursicaTimePickerProps`), matching `TextField`/`AutoComp
82
82
 
83
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
84
 
85
+ ## Leading icon overlapped the field text (bug fix, Matt Massey, 2026-08-30)
86
+
87
+ Follow-up to the section above, found during a mantine-vs-mui source-of-truth comparison: `.section`
88
+ (the leading icon) is absolutely positioned — Mantine's own default for `Input.Section`, same as
89
+ `Dropdown`/`AutoComplete`'s equivalent — so it never occupies real flex-flow space. `.timeWrapper`'s
90
+ own `gap` (added in the round above, intended to space the icon from the field) has **no effect** on
91
+ it for exactly that reason: `gap` only spaces in-flow flex siblings, and an absolutely-positioned
92
+ child isn't one. Nothing else reserved room for the icon either, so `.fieldsGroup`'s hour/minute/
93
+ second inputs rendered right where `.timeWrapper`'s own base `padding-left` put them — directly
94
+ underneath the icon (visually, the icon and the leading "-- : --" overlapped).
95
+
96
+ Separately, `.section[data-position="left"]` had no explicit `left`/`padding-left` of its own,
97
+ so it fell back to Mantine's un-tokenized default: centered in a fixed-width box flush against the
98
+ border, not inset by `horizontal-padding` the way `Dropdown`/`AutoComplete`'s own left sections are.
99
+
100
+ **Fix** (mirrors `Dropdown.module.css`'s established pattern exactly):
101
+
102
+ - `.section[data-position="left"] { position: absolute; left: 0; padding-left: horizontal-padding; }`
103
+ — explicit instead of relying on Mantine's un-tokenized default.
104
+ - `.timeWrapper[data-with-left-section] .fieldsGroup { padding-left: calc(icon-size + icon-text-gap); }`
105
+ — reserves the room the (now out-of-flow) icon needs, on top of `.timeWrapper`'s own existing
106
+ base `padding-left` (which already covers the initial `horizontal-padding` inset).
107
+
108
+ **Why mui-adapter, once it fixes its own "icon too far right" bug for the same story, should match
109
+ this corrected rendering and not the previous (overlapping) one**: the previous rendering was a
110
+ straightforward layout bug, not an intentional design — an icon and adjacent text overlapping is
111
+ never a valid state for any other component in either adapter (`Dropdown`, `AutoComplete`,
112
+ `TextField`, `DatePicker` all reserve real space for a leading icon before rendering the field's
113
+ own text/placeholder). This fix brings `TimePicker` in line with that same, already-established
114
+ convention.
115
+
85
116
  ## Visual review round 5 (Matt Massey, 2026-08-08)
86
117
 
87
118
  - **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).
@@ -51,8 +51,10 @@
51
51
  .timeWrapper {
52
52
  display: flex;
53
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. */
54
+ /* This `gap` is a no-op: the leading-icon `.section` is absolutely positioned (Mantine's own
55
+ default for Input.Section, same as Dropdown/AutoComplete), so it never participates in flex
56
+ gap spacing as a normal flow sibling — see `.fieldsGroup` below for the fix that actually
57
+ reserves room for it. Left in place only because it's harmless with a single in-flow child. */
56
58
  gap: var(--recursica_ui-kit_components_time-picker_properties_icon-text-gap);
57
59
  position: relative;
58
60
  box-sizing: border-box;
@@ -127,12 +129,24 @@
127
129
  }
128
130
 
129
131
  /* 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. */
132
+ text-field's separate leading/trailing) — this field has no right-side icon slot.
133
+ Positioned explicitly (left:0 + padding-left) rather than left on Mantine's own absolute-
134
+ positioning default alone (bug fix, see `.fieldsGroup` below): Mantine centers it in a
135
+ fixed-width box flush against the border with no Recursica padding token applied at all,
136
+ same technique Dropdown/AutoComplete's own `.section[data-position="left"]` already use. */
131
137
  .section {
132
138
  display: flex;
133
139
  align-items: center;
134
140
  }
135
141
 
142
+ .section[data-position="left"] {
143
+ position: absolute;
144
+ left: 0;
145
+ padding-left: var(
146
+ --recursica_ui-kit_components_time-picker_properties_horizontal-padding
147
+ );
148
+ }
149
+
136
150
  .section :global(svg) {
137
151
  width: var(--recursica_ui-kit_components_time-picker_properties_icon-size);
138
152
  height: var(--recursica_ui-kit_components_time-picker_properties_icon-size);
@@ -141,6 +155,19 @@
141
155
  );
142
156
  }
143
157
 
158
+ /* Reserves room for the (absolutely positioned, see `.section` above) leading icon in the actual
159
+ text flow — without this, `.fieldsGroup`'s hour/minute/second inputs start right where
160
+ `.timeWrapper`'s own base `padding-left` (horizontal-padding) puts them, which is directly
161
+ underneath the icon (bug: the icon and the field's leading ":" visibly overlapped). Only the
162
+ icon-size/icon-text-gap on top of that base padding is needed here, since `.timeWrapper`'s own
163
+ padding-left already reserves the horizontal-padding portion. */
164
+ .timeWrapper[data-with-left-section] .fieldsGroup {
165
+ padding-left: calc(
166
+ var(--recursica_ui-kit_components_time-picker_properties_icon-size) +
167
+ var(--recursica_ui-kit_components_time-picker_properties_icon-text-gap)
168
+ );
169
+ }
170
+
144
171
  /* SpinInput renders a real <input placeholder="--">, one per hour/minute/second segment. */
145
172
  .timeField::placeholder {
146
173
  opacity: var(