@recursica/mantine-adapter 0.47.0 → 0.48.1

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 (36) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/index.d.ts +9 -4
  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 +1633 -1559
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/package.json +2 -2
  9. package/src/components/Accordion/Accordion.module.css +11 -0
  10. package/src/components/AutoComplete/AutoComplete.module.css +10 -0
  11. package/src/components/Button/Button.module.css +10 -0
  12. package/src/components/Card/Card.stories.tsx +2 -4
  13. package/src/components/Chip/Chip.module.css +9 -0
  14. package/src/components/DatePicker/DatePicker.module.css +16 -0
  15. package/src/components/Dropdown/Dropdown.module.css +10 -0
  16. package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +4 -1
  17. package/src/components/FileInput/FileInput.module.css +17 -0
  18. package/src/components/FileUpload/FileUpload.module.css +6 -0
  19. package/src/components/Link/Link.module.css +5 -0
  20. package/src/components/Menu/Menu.module.css +6 -0
  21. package/src/components/NumberInput/NumberInput.module.css +8 -0
  22. package/src/components/Pagination/Pagination.module.css +7 -0
  23. package/src/components/Slider/IMPLEMENTATION_NOTES.md +22 -2
  24. package/src/components/Slider/Slider.module.css +36 -2
  25. package/src/components/Slider/Slider.stories.tsx +65 -0
  26. package/src/components/Slider/Slider.tsx +149 -42
  27. package/src/components/Slider/USAGE.md +15 -1
  28. package/src/components/Switch/Switch.module.css +8 -0
  29. package/src/components/Table/Table.module.css +6 -0
  30. package/src/components/Tabs/Tabs.module.css +7 -0
  31. package/src/components/TextArea/TextArea.module.css +8 -0
  32. package/src/components/TextField/TextField.module.css +8 -0
  33. package/src/components/TimePicker/TimePicker.module.css +8 -0
  34. package/src/components/Tree/IMPLEMENTATION_NOTES.md +3 -0
  35. package/src/components/Tree/Tree.module.css +11 -0
  36. package/src/components/Tree/Tree.tsx +11 -6
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "url": "git+https://github.com/borderux/recursica.git",
14
14
  "directory": "packages/mantine-adapter"
15
15
  },
16
- "version": "0.47.0",
16
+ "version": "0.48.1",
17
17
  "type": "module",
18
18
  "main": "./dist/mantine-adapter.cjs",
19
19
  "module": "./dist/mantine-adapter.js",
@@ -94,7 +94,7 @@
94
94
  "vitest": "^3.2.4"
95
95
  },
96
96
  "dependencies": {
97
- "@recursica/adapter-common": "^0.22.0",
97
+ "@recursica/adapter-common": "^0.23.0",
98
98
  "@recursica/official-release": "^2.8.0"
99
99
  },
100
100
  "peerDependencies": {
@@ -1,3 +1,14 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_disabled
4
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
5
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
6
+ * recursica-allow-brand: --recursica_brand_states_focus_color
7
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
8
+ * recursica-allow-brand: --recursica_brand_states_hover_color
9
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
10
+ */
11
+
1
12
  /* ==== ACCORDION CONTAINER ==== */
2
13
  .root {
3
14
  box-sizing: border-box;
@@ -1,3 +1,13 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
4
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
5
+ * recursica-allow-brand: --recursica_brand_states_focus_color
6
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
7
+ * recursica-allow-brand: --recursica_brand_states_hover_color
8
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
9
+ */
10
+
1
11
  /* LAYOUT SPACING OVERRIDES:
2
12
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
3
13
  - Also sets the --autocomplete-control-{max,min}-width hooks consumed inline in AutoComplete.tsx,
@@ -1,3 +1,13 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
4
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
5
+ * recursica-allow-brand: --recursica_brand_states_focus_color
6
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
7
+ * recursica-allow-brand: --recursica_brand_states_hover_color
8
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
9
+ */
10
+
1
11
  /**
2
12
  * IMPLEMENTATION NOTES:
3
13
  *
@@ -37,8 +37,7 @@ export const Default: Story = {
37
37
  <div
38
38
  style={{
39
39
  padding: "48px",
40
- backgroundColor:
41
- "var(--recursica_brand_palettes_neutral_050_color_tone)",
40
+ backgroundColor: "#e9ecef",
42
41
  }}
43
42
  >
44
43
  <Layer layer={0}>
@@ -73,8 +72,7 @@ export const HeaderlessAndFooterless: Story = {
73
72
  <div
74
73
  style={{
75
74
  padding: "48px",
76
- backgroundColor:
77
- "var(--recursica_brand_palettes_neutral_050_color_tone)",
75
+ backgroundColor: "#e9ecef",
78
76
  }}
79
77
  >
80
78
  <Layer layer={0}>
@@ -1,3 +1,12 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_disabled
4
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
5
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
6
+ * recursica-allow-brand: --recursica_brand_states_focus_color
7
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
8
+ */
9
+
1
10
  /* HARDCODED VALUES
2
11
  border-style: solid; (Baseline reset)
3
12
  box-sizing: border-box; (Baseline reset)
@@ -1,3 +1,19 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * No ui-kit_components_date-picker radius token exists for the calendar day cells; reuses the brand default border-radius.
3
+ * recursica-allow-brand: --recursica_brand_dimensions_border-radii_default
4
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
5
+ * recursica-allow-brand: --recursica_brand_states_disabled
6
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
7
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
8
+ * recursica-allow-brand: --recursica_brand_states_focus_color
9
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
10
+ * recursica-allow-brand: --recursica_brand_states_hover_color
11
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
12
+ * No ui-kit_components_date-picker in-range token exists; the generic overlay tint fills the range-selection band (see inline comment above .day[data-in-range]).
13
+ * recursica-allow-brand: --recursica_brand_states_overlay_color
14
+ * recursica-allow-brand: --recursica_brand_states_overlay_opacity
15
+ */
16
+
1
17
  /* LAYOUT SPACING OVERRIDES:
2
18
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
3
19
  .layoutOverride {
@@ -1,3 +1,13 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
4
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
5
+ * recursica-allow-brand: --recursica_brand_states_focus_color
6
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
7
+ * recursica-allow-brand: --recursica_brand_states_hover_color
8
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
9
+ */
10
+
1
11
  /* LAYOUT SPACING OVERRIDES:
2
12
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
3
13
  - Also sets the --dropdown-control-{max,min}-width hooks consumed inline in Dropdown.tsx, since
@@ -136,7 +136,10 @@ roving group, _then_ the trailing clear button as its own stop.
136
136
  wraps onto multiple lines below the dropzone — `FileInput` is a fixed single-line, `min-height`d
137
137
  control, so wrapping would grow it vertically. Enough chips to overflow the control's own width
138
138
  scroll horizontally within it instead (mouse wheel/trackpad or a native scrollbar), same tradeoff
139
- already made for `.value`'s ellipsis truncation.
139
+ already made for `.value`'s ellipsis truncation. The native scrollbar itself is hidden
140
+ (`scrollbar-width: none`, `-ms-overflow-style: none`, `::-webkit-scrollbar { display: none }`) —
141
+ the control's `min-height` isn't tall enough to host a visible horizontal scrollbar without
142
+ clipping the chips, and scrolling (wheel/trackpad/drag) still works with it hidden.
140
143
 
141
144
  ## Read-only vs disabled
142
145
 
@@ -1,3 +1,13 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
4
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
5
+ * recursica-allow-brand: --recursica_brand_states_focus_color
6
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
7
+ * recursica-allow-brand: --recursica_brand_states_hover_color
8
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
9
+ */
10
+
1
11
  /* LAYOUT SPACING OVERRIDES:
2
12
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
3
13
  - Also sets the --file-input-control-{max,min}-width hooks consumed inline in FileInput.tsx,
@@ -178,6 +188,13 @@
178
188
  overflow-x: auto;
179
189
  overflow-y: hidden;
180
190
  gap: var(--recursica_ui-kit_components_file-input_properties_icon-text-gap);
191
+ /* HARDCODE: scrollbar stays functional (still scrolls via wheel/trackpad/drag) but hidden —
192
+ the control isn't tall enough to host a visible scrollbar without clipping the chips. */
193
+ scrollbar-width: none; /* Firefox */
194
+ -ms-overflow-style: none; /* legacy Edge */
195
+ }
196
+ .chipRow::-webkit-scrollbar {
197
+ display: none; /* Chrome, Safari */
181
198
  }
182
199
 
183
200
  .chipWrapper {
@@ -1,3 +1,9 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_hover_color
4
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
5
+ */
6
+
1
7
  /* HARDCODED VALUES:
2
8
  - .root: display: flex; flex-direction: column; width: 100% — structural layout, not a design
3
9
  token concern (matches how other block-level components like Layer are laid out).
@@ -1,3 +1,8 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Only source for link hover-underline; no per-component token exists (see IMPLEMENTATION_NOTES.md).
3
+ * recursica-allow-brand: --recursica_brand_states_link_decoration
4
+ */
5
+
1
6
  /*
2
7
  HARDCODED VALUES:
3
8
  - display: inline-flex; (Base layout to align icon and text correctly)
@@ -1,3 +1,9 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_hover_color
4
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
5
+ */
6
+
1
7
  /* HARDCODED VALUES:
2
8
  - border-style: solid. Structural rendering rule for the dropdown border (Mantine uses Paper which may not set border natively).
3
9
  - NOTE: overflow is intentionally NOT set on .dropdown. Mantine renders sub-menu dropdowns
@@ -1,3 +1,11 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
4
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
5
+ * recursica-allow-brand: --recursica_brand_states_focus_color
6
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
7
+ */
8
+
1
9
  /* LAYOUT SPACING OVERRIDES:
2
10
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
3
11
  - Also sets the --number-input-control-{max,min}-width hooks consumed inline in NumberInput.tsx,
@@ -1,3 +1,10 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_disabled
4
+ * recursica-allow-brand: --recursica_brand_states_hover_color
5
+ * recursica-allow-brand: --recursica_brand_states_hover_opacity
6
+ */
7
+
1
8
  .root {
2
9
  display: flex;
3
10
  align-items: center;
@@ -67,6 +67,26 @@ This document contains specific design decisions, architectural constraints, and
67
67
  - `minLabel`/`maxLabel` (new `adapter-common` props) override the `.minMaxGuide` text at either end of the track, falling back to the numeric `min`/`max`.
68
68
  - `trailingIcon` (new `adapter-common` prop) renders a second icon on the opposite side of the track from the existing `icon`, reusing the same `.iconWrapper` styling (disabled/error/focus states already target `.iconWrapper` generically, so no new CSS was needed).
69
69
 
70
- ## 8. No Dual-Thumb / Range Support
70
+ ## 8. Dual-Thumb / Range Support
71
71
 
72
- **Decision:** Requested (a caller passing `[number, number]` for `value`/`onChange`, backed by Mantine's separate `RangeSlider` component), declined — no current use case needs it. `Slider` stays single-thumb only; `value`/`onChange` remain typed as `number`.
72
+ **Decision:** Previously requested and declined (see git history for this section) as no use case needed it; reopened with a full spec and implemented. `value`/`defaultValue`/`onChange`/`onChangeEnd` are now typed `number | [number, number]` in `@recursica/adapter-common`'s `RecursicaSliderProps`.
73
+ **Implementation:**
74
+
75
+ - `isRange = Array.isArray(resolvedValue)` picks which underlying Mantine component to render: single-thumb `Slider` (unchanged) or two-thumb `RangeSlider` — Mantine ships these as two separate components (unlike raw MUI, which renders two thumbs off a single `Slider` given a tuple `value`), so this is a real component swap, not just a type/prop change. Both share the same `SliderStylesNames` shape, so `mergedClassNames` (root/track/bar/thumb/mark/markLabel) applies unchanged to either.
76
+ - Internal state (`internalValue`, `resolvedValue`, `inputValue`) generalized from `number` to `number | [number, number]`; range-mode input handlers (`handleLowerInputChange`/`handleUpperInputChange`) clamp each thumb against the other's current value (not the shared `min`/`max`) so the two inputs can't cross.
77
+ - `SliderReadOnlyValue` and the floating `.currentValue` display both render `"lower – upper"` for a range value, running each side through `tooltipLabel` independently when it's a formatter.
78
+ - DOM order in range mode (input → leading icon → min label → track → max label → trailing icon → input) mirrors Forge's own Material/Carbon range layouts — see §9 below.
79
+ - Not carried over: Mantine `RangeSlider`'s own `minRange`/`maxRange`/`pushOnOverlap`/`thumbFromLabel`/`thumbToLabel` pass through via the existing unsanitized `...rest` spread if a caller sets them directly; no adapter-level default is applied (e.g. Mantine's own `minRange` default of `10` is left as-is).
80
+
81
+ ## 9. Trailing Icon Order Relative to the Input Field
82
+
83
+ **Decision:** `trailingIcon` previously rendered after the numeric input (`... max label, input, trailing icon`), reversed from Forge's Material/Carbon kits, which always render the trailing icon directly after the max label and before the input.
84
+ **Implementation:** Moved `{trailingIconEl}` before the `showInput` input block in the single-value layout; the range layout was built with this order from the start (input → icon → min label → track → max label → trailing icon → input).
85
+
86
+ ## 10. Marks Rendering Off Mantine Defaults Instead of Recursica Tokens
87
+
88
+ **Decision:** §3 claimed marks were "fully styles-mapped back to our scoped variables" — not quite true. Mantine's `markWrapper` style slot (the zero-height, top-anchored positioning wrapper around each mark dot + label) was never mapped in `mergedClassNames`, and `markLabel`'s vertical position was never overridden, so both stayed on raw Mantine defaults.
89
+ **Implementation:**
90
+
91
+ - `markWrapper` is anchored to Mantine's own `top: 0` (the track's top edge), with the mark dot inside it unpositioned. Mantine's own dot has no size override so it happens to fill the track height exactly by default; our `step-indicator-width` token is independent of track height, so the dot rendered small and flush to the top instead of centered. Added `markWrapper: styles.sliderMarkWrapper` to `mergedClassNames`, with `.sliderMarkWrapper { top: 50%; }`, and changed `.sliderMark`'s transform from `translateX(-50%)` to `translate(-50%, -50%)` to center the dot on that midpoint.
92
+ - `.sliderMarkLabel` never overrode `transform`, so its vertical offset stayed Mantine's own `calc(var(--mantine-spacing-xs) / 2)` — an unbound theme default, not a recursica token. Replaced the vertical term with `--recursica_ui-kit_globals_form_properties_label-field-gap-vertical` (the same token the mui-adapter's markLabel already uses), keeping the horizontal term (`-50% + var(--slider-size) / 2`, Mantine's own mark-offset compensation) unchanged.
@@ -1,8 +1,20 @@
1
+ /* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
2
+ * Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
3
+ * recursica-allow-brand: --recursica_brand_states_focus_blur
4
+ * recursica-allow-brand: --recursica_brand_states_focus_border-size
5
+ * recursica-allow-brand: --recursica_brand_states_focus_color
6
+ * recursica-allow-brand: --recursica_brand_states_focus_margin
7
+ */
8
+
1
9
  /*
2
10
  * HARDCODED VALUES:
3
11
  * - display: flex; align-items: center; width: 100%; (Standard CSS flexbox layouts for bidirectional components)
4
12
  * - flex-grow: 1; flex-shrink: 0; (Layout control structures)
5
- * - transform: translateX(-50%); (Mantine's standard step marks positioning alignment offset)
13
+ * - transform: translate(-50%, -50%); (custom step-mark centering offset — see .sliderMarkWrapper
14
+ * / .sliderMark below; not inherited from Mantine, which centers marks via its own
15
+ * --slider-size variable that our step-indicator-width token doesn't participate in)
16
+ * - top: 50%; on .sliderMarkWrapper (re-anchors Mantine's zero-height mark wrapper to the
17
+ * track's vertical midpoint instead of its top edge)
6
18
  * - outline: none; border-style: solid; (Standard focus reset and border outlines)
7
19
  * - Focus ring on thumb/icon/input: the token schema no longer provides per-component focus
8
20
  * colors for these (only `active` covers track/step-indicator-color). We apply the generic
@@ -109,6 +121,18 @@
109
121
  var(--recursica_brand_states_focus_color);
110
122
  }
111
123
 
124
+ /*
125
+ * Mantine positions the mark dot inside a zero-height `markWrapper` anchored to the track's
126
+ * top edge, relying on the dot's height matching Mantine's own `--slider-size` to land centered.
127
+ * Our step-indicator-width token is independent of that, so without an explicit vertical
128
+ * transform the dot sits flush to the track's top instead of centered. `sliderMarkWrapper`
129
+ * moves the anchor to the track's vertical midpoint; `translate(-50%, -50%)` below centers the
130
+ * dot on that midpoint (in addition to the pre-existing horizontal centering).
131
+ */
132
+ .sliderMarkWrapper {
133
+ top: 50%;
134
+ }
135
+
112
136
  .sliderMark {
113
137
  box-sizing: border-box;
114
138
  width: var(
@@ -123,7 +147,7 @@
123
147
  background-color: var(
124
148
  --recursica_ui-kit_components_slider_properties_colors_step-indicator-color
125
149
  );
126
- transform: translateX(-50%);
150
+ transform: translate(-50%, -50%);
127
151
  border: none;
128
152
  }
129
153
 
@@ -139,8 +163,18 @@
139
163
  * Mantine's default markLabel has no dedicated recursica token; it renders with Mantine's own
140
164
  * theme grey instead of inheriting the container text color. Reusing the min-max-label
141
165
  * typography tokens and inheriting color keeps this visually consistent with the min/max guides.
166
+ *
167
+ * The vertical gap between mark and label is also Mantine's own default (half of
168
+ * --mantine-spacing-xs, unbound to any recursica token) unless overridden here. The transform's
169
+ * horizontal term (`-50% + var(--slider-size) / 2`) is Mantine's own mark-offset compensation
170
+ * and is left untouched; only the vertical term is replaced with the same label-field gap token
171
+ * the mui-adapter's markLabel already uses, for cross-adapter consistency.
142
172
  */
143
173
  .sliderMarkLabel {
174
+ transform: translate(
175
+ calc(-50% + var(--slider-size) / 2),
176
+ var(--recursica_ui-kit_globals_form_properties_label-field-gap-vertical)
177
+ );
144
178
  color: inherit;
145
179
 
146
180
  font-family: var(
@@ -190,6 +190,71 @@ export const WithIconsAndLabels: Story = {
190
190
  },
191
191
  };
192
192
 
193
+ export const RangeMode: Story = {
194
+ args: {
195
+ label: "Price Range",
196
+ assistiveText: "Pass a [number, number] tuple to render two thumbs.",
197
+ defaultValue: [20, 80],
198
+ min: 0,
199
+ max: 100,
200
+ showMinMaxLabels: true,
201
+ },
202
+ };
203
+
204
+ export const RangeModeWithInputs: Story = {
205
+ args: {
206
+ ...RangeMode.args,
207
+ showInput: true,
208
+ },
209
+ };
210
+
211
+ export const RangeModeWithIconsAndInputs: Story = {
212
+ args: {
213
+ label: "Price Range",
214
+ assistiveText:
215
+ "Full range usage: leading/trailing icons, min/max label overrides, and both bound inputs.",
216
+ defaultValue: [20, 80],
217
+ min: 0,
218
+ max: 100,
219
+ showInput: true,
220
+ minLabel: "$0",
221
+ maxLabel: "$100",
222
+ tooltipLabel: (value: number) => `$${value}`,
223
+ icon: (
224
+ <svg
225
+ xmlns="http://www.w3.org/2000/svg"
226
+ width="16"
227
+ height="16"
228
+ viewBox="0 0 24 24"
229
+ fill="none"
230
+ stroke="currentColor"
231
+ strokeWidth="2"
232
+ strokeLinecap="round"
233
+ strokeLinejoin="round"
234
+ >
235
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
236
+ </svg>
237
+ ),
238
+ trailingIcon: (
239
+ <svg
240
+ xmlns="http://www.w3.org/2000/svg"
241
+ width="16"
242
+ height="16"
243
+ viewBox="0 0 24 24"
244
+ fill="none"
245
+ stroke="currentColor"
246
+ strokeWidth="2"
247
+ strokeLinecap="round"
248
+ strokeLinejoin="round"
249
+ >
250
+ <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>
251
+ <path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>
252
+ <path d="M19.07 4.93a10 10 0 0 1 0 14.14"></path>
253
+ </svg>
254
+ ),
255
+ },
256
+ };
257
+
193
258
  export const FormLayouts: Story = {
194
259
  render: () => (
195
260
  <div