@recursica/mantine-adapter 0.36.0 → 0.38.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 (63) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +3 -3
  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 +2352 -2099
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/dist/src/components/Dropdown/BareDropdown.d.ts +19 -0
  9. package/dist/src/components/TimePicker/TimePicker.d.ts +9 -3
  10. package/dist/src/components/Tree/Tree.d.ts +5 -0
  11. package/dist/src/index.d.ts +1 -1
  12. package/docs/PHILOSOPHY.md +39 -0
  13. package/package.json +3 -2
  14. package/src/components/Accordion/USAGE.md +2 -41
  15. package/src/components/AutoComplete/USAGE.md +2 -18
  16. package/src/components/Avatar/USAGE.md +0 -27
  17. package/src/components/Badge/USAGE.md +3 -6
  18. package/src/components/Breadcrumb/USAGE.md +1 -5
  19. package/src/components/Button/Button.tsx +5 -0
  20. package/src/components/Button/IMPLEMENTATION_NOTES.md +8 -0
  21. package/src/components/Button/USAGE.md +5 -25
  22. package/src/components/Card/USAGE.md +4 -12
  23. package/src/components/Checkbox/USAGE.md +4 -20
  24. package/src/components/Chip/USAGE.md +3 -32
  25. package/src/components/DatePicker/USAGE.md +2 -12
  26. package/src/components/Dropdown/BareDropdown.tsx +85 -0
  27. package/src/components/Dropdown/Dropdown.tsx +12 -3
  28. package/src/components/Dropdown/USAGE.md +2 -6
  29. package/src/components/Flex/USAGE.md +1 -1
  30. package/src/components/FormControlWrapper/USAGE.md +3 -31
  31. package/src/components/Grid/USAGE.md +1 -1
  32. package/src/components/Group/USAGE.md +1 -1
  33. package/src/components/HoverCard/USAGE.md +3 -72
  34. package/src/components/Label/USAGE.md +8 -48
  35. package/src/components/Link/USAGE.md +4 -10
  36. package/src/components/Loader/USAGE.md +4 -23
  37. package/src/components/Menu/USAGE.md +3 -73
  38. package/src/components/Modal/USAGE.md +3 -3
  39. package/src/components/NumberInput/USAGE.md +5 -8
  40. package/src/components/Pagination/USAGE.md +0 -19
  41. package/src/components/Panel/USAGE.md +6 -95
  42. package/src/components/Popover/USAGE.md +6 -66
  43. package/src/components/ReadOnlyField/USAGE.md +2 -10
  44. package/src/components/SegmentedControl/USAGE.md +1 -15
  45. package/src/components/Slider/USAGE.md +1 -45
  46. package/src/components/Stack/USAGE.md +1 -1
  47. package/src/components/Switch/USAGE.md +1 -24
  48. package/src/components/TextArea/USAGE.md +2 -2
  49. package/src/components/TextField/USAGE.md +1 -17
  50. package/src/components/TimePicker/TIMEPICKER_IMPLEMENTATION_NOTES.md +72 -0
  51. package/src/components/TimePicker/TimePicker.module.css +225 -41
  52. package/src/components/TimePicker/TimePicker.stories.tsx +105 -4
  53. package/src/components/TimePicker/TimePicker.tsx +287 -7
  54. package/src/components/TimePicker/USAGE.md +23 -2
  55. package/src/components/Timeline/USAGE.md +2 -10
  56. package/src/components/Toast/USAGE.md +3 -33
  57. package/src/components/Tooltip/USAGE.md +7 -51
  58. package/src/components/Tree/IMPLEMENTATION_NOTES.md +31 -3
  59. package/src/components/Tree/Tree.module.css +84 -40
  60. package/src/components/Tree/Tree.stories.tsx +13 -0
  61. package/src/components/Tree/Tree.tsx +116 -27
  62. package/src/components/Tree/USAGE.md +18 -1
  63. package/src/index.ts +1 -0
@@ -41,35 +41,7 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
41
41
 
42
42
  ---
43
43
 
44
- ## 4. Key Integration Features & Constraints
44
+ ## 4. Notes
45
45
 
46
- ## Architectural Philosophy
47
-
48
- The `FormControlWrapper` is the ultimate structural replacement for Mantine's built-in `Input.Wrapper`. By abandoning Mantine's opinionated wrappers entirely across the design system, we centralize all label tracking, error rendering, ARIA generation, and grid layouts natively inside this single component.
49
-
50
- ### 1. Bypassing `Input.Wrapper`
51
-
52
- Under the hood of Mantine, elements like `TextInput` heavily rely on `Input.Wrapper`. We actively discourage their use. The central tenet of Recursica Forms is to strip the UI primitive back to its "naked" form (e.g. `<Input />`, `<Checkbox />`) and encapsulate it manually inside `<FormControlWrapper>`.
53
-
54
- - **Why?** It enforces complete layout mastery. It natively enables `formLayout="side-by-side"` and completely disables Mantine's margin collisions without requiring messy CSS hacks.
55
-
56
- ### 2. The `cloneElement` ARIA Map
57
-
58
- Because we tore out Mantine's `InputContext` provider (which natively glued error strings to `<input>` tags using React Context), we explicitly utilize `React.cloneElement` on the nested children inside this wrapper.
59
-
60
- - `aria-describedby` and `aria-errormessage` are dynamically generated using `React.useId()` and physically injected back onto the provided child node. Screen readers rely strictly on this mapping to announce the assistive fields correctly.
61
-
62
- ### 3. Strict `AssistiveElement` Coupling
63
-
64
- We completely abandoned generic `<Input.Description>` tags. The `FormControlWrapper` directly renders `<AssistiveElement>` primitives, parsing them seamlessly mapping them to `"error"` or `"help"` variants automatically depending on the component's internal state machine.
65
-
66
- ### 4. Dynamic Geometric Variable Payloads
67
-
68
- Because `FormControlWrapper` acts as an agnostic grid box encompassing raw primitives (like `TextField`, `Select`), it initially stretches `100%` across horizontal bounds.
69
-
70
- - **The Bug:** If a child `TextField` carries its own hardcoded `max-width` token, it stops expanding early, but the wrapper and `<Label>` keep expanding, causing right-aligned labels to aggressively float past the field to the screen's edge dynamically.
71
- - **The Variable Payload Resolution:** Instead of destroying grids with `width: fit-content` arrays, primitive components are required to pass their local `max-width` tokens UP to the wrapper explicitly via React `style`:
72
- ```tsx
73
- <FormControlWrapper style={{ "--form-control-max-width": "var(--...)" }}>
74
- ```
75
- The wrapper natively respects `max-width: var(--form-control-max-width, 100%)`. This structurally unifies the bounding caps so right-aligned labels flawlessly snap tightly to the explicit boundary edge of the specific primitive it is wrapping.
46
+ - `FormControlWrapper` renders the label, error, and help text around the child input; screen-reader associations between the input and its assistive text are handled automatically.
47
+ - The wrapper stretches to fill its container by default. If the wrapped input has its own maximum width, pass a matching `--form-control-max-width` CSS variable via the `style` prop so the label and assistive text align to the same width as the input.
@@ -36,7 +36,7 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
36
36
 
37
37
  > [!IMPORTANT]
38
38
  >
39
- > - **Anti-override protection**: `Grid` is a primitive layout component (see [OVERSTYLING.md](../../../OVERSTYLING.md)) and is exempt from the `RecursicaOverStyled` gatekeeper, so standard Mantine layout props pass through freely.
39
+ > - **Anti-override protection**: `Grid` is a primitive layout component (see [OVERSTYLING.md](../../../OVERSTYLING.md)), so standard Mantine layout props pass through freely without needing `overStyled`.
40
40
  > - **No Direct Layers**: Do not pass a `layer` prop to this component. To place it on a specific visual layer, wrap it in a `<Layer layer={0|1|2|3}>` component natively.
41
41
  > - **Variables and Theming**: Spacing is entirely determined by the `rec-*` token scale, mapped transparently to standard Mantine gutter values.
42
42
 
@@ -45,4 +45,4 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
45
45
  ## 4. Key Integration Features & Constraints
46
46
 
47
47
  The `Group` component is a generic flex layout wrapper mapped directly to Mantine's `Group`.
48
- It currently does not require any custom logical layouts or CSS workarounds since it serves only to organize layout structure, and doesn't enforce any strict design-system token styling itself. All gap, align, wrap, and justify properties pass safely through via the `filterStylingProps` layout-property allowance.
48
+ `gap`, `align`, `wrap`, and `justify` all pass through as normal.
@@ -46,76 +46,7 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
46
46
 
47
47
  ---
48
48
 
49
- ## 4. Key Integration Features & Constraints
49
+ ## 4. Notes
50
50
 
51
- ## 1. Composable API Preservation (1:1 Mapping)
52
-
53
- **Decision:** We maintain the exact library composition API structure (`<HoverCard>`, `<HoverCard.Target>`, `<HoverCard.Dropdown>`) as a 1:1 React component mapping.
54
-
55
- **Implementation:** Mantine's HoverCard internally manages hover detection, open/close delays, Floating UI positioning, and portal rendering. By preserving the exact sub-component tree, Recursica safely inherits all of these behaviors without reimplementation.
56
-
57
- ---
58
-
59
- ## 2. HoverCard.Target Pass-Through
60
-
61
- **Decision:** `HoverCard.Target` is a transparent pass-through with no styling applied.
62
-
63
- **Implementation:** The target wrapper exists solely to manage Mantine's ref forwarding and hover event binding for the trigger element. No `filterStylingProps` or CSS module classes are applied — the trigger's appearance is entirely controlled by whatever component the integrator places inside it (e.g., `<Button>`). This is identical to the `Menu.Target` pattern.
64
-
65
- ---
66
-
67
- ## 3. Token Namespace: `hover-card-popover`
68
-
69
- **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_hover-card-popover_*` namespace.
70
-
71
- **Implementation:** The Recursica token system defines a single shared namespace (`hover-card-popover`) for this component covering geometry (border-radius, border-size, padding, min/max-width), typography (content-text\_\*), elevation, and layer-aware colors (background, border-color, content). No tokens from other component namespaces are referenced.
72
-
73
- ---
74
-
75
- ## 4. Hardcoded Values
76
-
77
- **Decision:** Two hardcoded values exist in the component.
78
-
79
- ### `border-style: solid` (CSS module)
80
-
81
- Mantine renders the dropdown using its `Paper` component, which does not set `border-style` natively. Without this hardcoded value, the border-width and border-color tokens would have no visible effect. This is the same pattern used in the Menu component's dropdown.
82
-
83
- ### `arrowSize` defaulted to `16` (HoverCard.tsx)
84
-
85
- Mantine's `arrowSize` prop is a JavaScript number used for inline style calculations: it sets `width`, `height`, and a positioning offset (`-arrowSize/2`) directly on the arrow `<div>` element. These inline styles **cannot** be overridden via CSS without `!important`, and the positioning offset has no CSS equivalent. This means the beak size cannot be fully CSS-driven — it is one of the rare cases where a design token value must be mirrored as a JS prop.
86
-
87
- The default value `16` matches the Recursica `beak-size` token (`--recursica_ui-kit_components_hover-card-popover_properties_beak-size: 16px`). Developers can override `arrowSize` if needed, but should be aware this is a design system concern. If the token value changes, the default in `HoverCard.tsx` must also be updated.
88
-
89
- This is documented as an open issue in `docs/COMPONENT_ISSUES.md`.
90
-
91
- **Note:** Mantine calls this the "arrow"; Recursica calls it the "beak". The Recursica prop `withBeak` (defaulting to `true`) maps to Mantine's `withArrow`. Both `withBeak` and `withArrow` are accepted; `withBeak` takes precedence.
92
-
93
- ---
94
-
95
- ## 5. Minimal CSS Override Philosophy
96
-
97
- **Decision:** The CSS module only overrides visual design tokens (colors, typography, spacing, borders, shadows). All structural layout properties are deferred to Mantine's native behavior.
98
-
99
- **Implementation:** HoverCard is an overlay component where Mantine's native Floating UI positioning and Paper layout are already correct. We avoid setting:
100
-
101
- - `overflow` on the dropdown (Mantine handles scroll behavior natively)
102
- - `display`, `position`, `z-index` (Floating UI controls these)
103
- - `pointer-events` (Mantine manages hover detection across target and dropdown)
104
-
105
- **Rationale:** The Menu implementation demonstrated that aggressive structural resets (overflow, box-sizing) on overlay components cause layout breakage. Default to Mantine's native behavior and only override what the Recursica token system explicitly needs to control.
106
-
107
- ---
108
-
109
- ## 6. ClassNames Merging on Root
110
-
111
- **Decision:** The root `HoverCard` component binds CSS module classes via `classNames` on the Mantine root, identical to the Menu pattern.
112
-
113
- **Implementation:** The root component receives `classNames={{ dropdown: styles.dropdown }}` and merges any consumer-provided `classNames` when `overStyled` is true. This ensures our token-driven styles are applied to the dropdown panel without wrapper divs, and the consumer's classes are additive.
114
-
115
- ---
116
-
117
- ## 7. Default Position Override
118
-
119
- **Decision:** Recursica defaults `position` to `"top"`. Mantine defaults to `"bottom"`.
120
-
121
- **Implementation:** The `position="top"` default is set on the Mantine root element before the prop spread, so developer-provided `position` values still take precedence. This aligns with Recursica's design intent for overlay components to appear above their trigger by default.
51
+ - Mantine calls the pointer indicator the "arrow"; Recursica calls it the "beak". Use the `withBeak` prop (default `true`) to show or hide it; `withArrow` is also accepted for compatibility, with `withBeak` taking precedence.
52
+ - The default `position` is `"top"` (Mantine's default is `"bottom"`); pass your own `position` prop to override it.
@@ -37,51 +37,11 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
37
37
 
38
38
  ---
39
39
 
40
- ## 4. Key Integration Features & Constraints
41
-
42
- ## Architecture Overview
43
-
44
- The `Label` component is fundamentally built as a strict, localized wrapper around Mantine's native `Input.Label`. The core goal is to preserve context, ref-forwarding, and native accessibility links, while completely overriding visual behaviors via Recursica design variables in scoped CSS.
45
-
46
- ## Key Design Decisions
47
-
48
- ### **Layout Architecture**
49
-
50
- - Because Recursica dictates complex alignments between primary label text, asterisks, edit icons, and optional strings, Mantine's rigid form label flow could not be used out-of-the-box.
51
- - Implemented `display: flex; flex-wrap: wrap;` directly on `.root`.
52
- - Injected strict integer-based flex `order` properties (e.g. `order: 1` for text, `order: 2` for `required`, `order: 5` for `optionalText`) to physically decouple DOM rendering from markup flow.
53
- - Because `optionalText` often acts as secondary contextual detail, it forces `flex-basis: 100%`, securely wrapping to a secondary line underneath the primary label properties and transforming the horizontal gap spacing seamlessly into a `margin-top` vector.
54
-
55
- ### **Optional Text & Required Mutually Exclusive Parsing**
56
-
57
- - By design standards, a component cannot logically be both "Required" and mapped as "Optional".
58
- - To bulletproof implementations natively, the adapter forcibly suppresses rendering of the `resolvedOptionalText` strings if the parent wrapper invokes `required={true}`.
59
- - `optionalText` can operate as dynamic data or specifically as a boolean `true`, which forces the adapter to natively render the formal `(Optional)` string map.
60
-
61
- ### **The "Edit Icon" Replacer Logic**
62
-
63
- - Passing `withEditIcon={true}` evaluates it as fundamentally mutually exclusive to the standard required indicator asterisk.
64
- - `required={required && !withEditIcon}` is passed to the underlying Mantine structure so the native `*` is entirely suppressed.
65
- - Added localized styling via `data-replaces-asterisk={required ? "true" : undefined}` onto the `.editIconWrapper` element. If an editable instance is simultaneously designated as required, the edit icon functionally assumes the indicator role natively, overriding its default icon metrics explicitly to match `--recursica_ui-kit_components_label_properties_colors_asterisk`.
66
-
67
- ### **Bypassing `Input.Wrapper` Integrations**
68
-
69
- - By decoupling from Mantine's standard `Input.Wrapper`, the `Label` component is exclusively mapped manually through `FormControlWrapper`. This grants us exact visual sync regarding where the label renders based on `formLayout="stacked"` or `formLayout="side-by-side"`, without fighting internal Mantine positional hooks that assume vertical stacking by default.
70
-
71
- ### **Label Size Container Constraints**
72
-
73
- - The `labelSize` parameter (mapping values like `"small"`) internally **does not scale typographic font metrics**. Instead, it dynamically defines the explicit horizontal bounding width limit of the block container itself.
74
- - **Architectural Rule:** `labelSize` modifications are strictly designed to execute exclusively when `formLayout="side-by-side"` is active. It acts as an optical grid threshold ensuring left-aligned string wrappers constrain correctly uniformly down a column without bleeding into the physical input arrays alongside them.
75
-
76
- ## Outstanding Technical Debt / Issues
77
-
78
- ### **Description Property Omission**
79
-
80
- - **Issue:** Currently, the adapter `Label` does not natively parse or support a structured `description` node directly attached beneath it.
81
- - **Context:** Standard forms often attach subtext beneath inputs, but mapping a description purely within the `<Label>` (distinct from an overall form-control description) is omitted structurally until subsequent UI Kit requirements mandate dedicated `description` styles locally on the label component itself.
82
-
83
- ### **Global Interactive Hover Mappings**
84
-
85
- - **Issue:** Currently, the `editIconWrapper` lacks a dedicated hover background layer.
86
- - **Context:** While the global theme abstracts interactive hover environments deeply using layered variable syntax (e.g., `--recursica_brand_layer_1_elements_interactive_hover-color`), Recursica's token generation framework natively fails to expose a universally generic placeholder token (e.g., `--recursica_elements_interactive_hover-color`) which downstream wrappers can map their `<Layer>` injections directly into safely.
87
- - **Resolution Path:** We are explicitly ignoring the `.editIconWrapper:hover` background color assignment until the raw Figma token exports directly scaffold a generic token placeholder that we can interface cleanly with across arbitrary `<Layer>` contexts, avoiding manually mapping statically hardcoded depths (`layer-0`, `layer-1`).
40
+ ## 4. Behavior Notes
41
+
42
+ - A label cannot be both required and optional: if `required` is `true`, `optionalText` is not rendered.
43
+ - Set `optionalText` to `true` to render the default "(Optional)" text, or pass a custom string for different wording.
44
+ - `withEditIcon` and `required` are mutually exclusive: when both are set, the edit icon is shown in place of the required asterisk.
45
+ - `labelSize` only affects layout when the surrounding form uses `formLayout="side-by-side"`, where it constrains the label's width.
46
+ - `Label` does not currently support a `description` prop.
47
+ - The edit icon does not currently have a distinct hover background.
@@ -47,16 +47,10 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
47
47
 
48
48
  The design tokens (`recursica_variables_scoped.css`) provide the base link styling directly on `--recursica_ui-kit_components_link_properties_*` (no distinct `default` state), plus a `visited` variant that overrides only `colors_text-color`/`colors_icon-color`. There is no per-state token for `hover` anymore (a prior schema version had one); the component currently applies no distinct hover treatment beyond the browser's native `cursor: pointer`. There are no tokens for `active` or `focus` either; the component relies on the browser's default focus outline for accessibility unless overridden by a global reset.
49
49
 
50
- ## Overriding Mantine's underline Prop
50
+ ## Underline Behavior
51
51
 
52
- Mantine's `Anchor` component uses `underline="hover"` by default. Because our design system specifies exact `text-decoration` styles via CSS tokens, we explicitly pass `underline="never"` to the underlying Mantine component. This prevents Mantine from injecting its own text-decoration inline or via generic classnames, ensuring our scoped CSS remains the single source of truth.
52
+ `Link` does not use Mantine's automatic hover-underline behavior; text decoration is fully controlled by the design system's tokens.
53
53
 
54
- ## Base Layout
54
+ ## Icon Support
55
55
 
56
- Mantine's `Anchor` renders an inline element by default and does not natively support `leftSection` like the `Button` component. To support an optional `icon` alongside the text, we enforce a baseline layout of `display: inline-flex` and `align-items: center` in `Link.module.css`.
57
-
58
- When an icon is present, the component conditionally passes a `data-has-icon` attribute to the root element. The CSS module uses this attribute to apply the `icon-text-gap` token via the CSS `gap` property.
59
-
60
- ## Inner Wrappers
61
-
62
- The icon and children are wrapped in internal `<span>` tags (`.iconWrapper` and `.labelText` respectively). This follows the component development guide for structural robustness, allowing us to enforce specific sizing on the icon and intrinsic text truncation behavior if the link is placed in a bounded container.
56
+ When an `icon` is provided, it renders alongside the link text with spacing between them applied automatically via design tokens.
@@ -37,27 +37,8 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
37
37
 
38
38
  ---
39
39
 
40
- ## 4. Key Integration Features & Constraints
40
+ ## 4. Notes
41
41
 
42
- ## Architecture & Integration
43
-
44
- The `Loader` component acts as a strictly tokenized wrapper bridging the Recursica UI-Kit `loader` variables to the generic Mantine `@mantine/core` `Loader` primitive.
45
-
46
- ### Key Decisions:
47
-
48
- - **Variant Mapping:** Recursica's `variant` directly proxies to Mantine's `type` prop for `"oval" | "bars" | "dots"`.
49
- - **Property Overrides Disabled:** Natively overriding specific structural variants directly on the JSX interface like `thickness` and `borderRadius` has been intentionally omitted. Component rendering relies entirely on variables exposed by the underlying UI Kit mappings tied to the size prop.
50
-
51
- ### Token Mapping:
52
-
53
- Sizes are bound through `data-size` attributes (`sm`, `md`, `lg` parsing to target `<div data-size="small">`, etc.).
54
-
55
- Mantine natively sets sizing dynamically at the component root and parses thickness/variants differently natively (e.g., `oval` styles its geometry strictly via CSS `border`, while `bars` and `dots` utilize specific DOM inner spans `span.dot` / `span.bar`).
56
-
57
- #### Specific CSS Targeting Hacks Used
58
-
59
- - `border-width` and `border-radius` structurally style the `thickness` and `border-radius` configuration of the `oval` variant on the `::after` pseudo-element by resolving `--recursica_ui-kit_components_loader_variants_sizes_..._properties_thickness` and `_border-radius`.
60
-
61
- ### Unsupported Properties
62
-
63
- - **xs and xl Sizing:** These sizes are explicitly unsupported in the Recursica standard logic (as surfaced in `filterStylingProps` and UI Kit mappings). Attempting to use them will safely default back or fallthrough statically unless defined later. See `COMPONENT_ISSUES.md`.
42
+ - `variant` maps to Mantine's loader types: `"oval"`, `"bars"`, or `"dots"`.
43
+ - `thickness` and `border-radius` are not exposed as props; sizing is controlled entirely by the `size` prop.
44
+ - The `xs` and `xl` sizes are not currently supported.
@@ -48,77 +48,7 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
48
48
 
49
49
  ---
50
50
 
51
- ## 4. Key Integration Features & Constraints
51
+ ## 4. Notes
52
52
 
53
- ## 1. Composable API Preservation (1:1 Mapping)
54
-
55
- **Decision:** We maintain the exact library composition API structure (`<Menu>`, `<Menu.Target>`, `<Menu.Dropdown>`, `<Menu.Item>`, `<Menu.Divider>`, `<Menu.Label>`, `<Menu.Sub>`, `<Menu.Sub.Target>`, `<Menu.Sub.Item>`, `<Menu.Sub.Dropdown>`) as a 1:1 React component mapping.
56
-
57
- **Implementation:** Mantine's Menu internally manages WAI-ARIA role assignments (`role="menu"`, `role="menuitem"`, `aria-haspopup`, `aria-expanded`, `aria-controls`), keyboard navigation (arrow keys, Enter, Escape), and focus trapping natively across its composable hierarchy. By preserving the exact sub-component tree, Recursica safely adopts all of these accessibility behaviors without reimplementation.
58
-
59
- ---
60
-
61
- ## 2. Hover State Overlay Technique
62
-
63
- **Decision:** We nullify Mantine's native hover background on `Menu.Item` and exclusively utilize Recursica's hover structure via a `::after` pseudo-element overlay.
64
-
65
- **Implementation:** The `.item::after` pseudo-object dynamically pulls `hover-color` & `hover-opacity` bindings from the Recursica token system. Mantine natively changes `background-color` on item hover which would override our token-driven colors. We enforce `background-color` to remain the unselected-item background on hover, then layer our pseudo-overlay on top. This is the same technique used in the Accordion component.
66
-
67
- ---
68
-
69
- ## 3. Selected vs Unselected Item States
70
-
71
- **Decision:** Menu items map to Recursica's `selected-item` and `unselected-item` color token groups.
72
-
73
- **Implementation:** The CSS module defaults all items to `unselected-item_*` tokens (background, text, opacity). Items with `[data-selected]` switch to `selected-item_*` tokens. This mapping is natively driven by Mantine's internal state tracking without React state hooks.
74
-
75
- ---
76
-
77
- ## 4. `color` Prop Stripping
78
-
79
- **Decision:** Mantine's `color` prop on `Menu.Item` (used for semantics like "danger/red") is explicitly stripped in strict mode.
80
-
81
- **Implementation:** The `color` prop is deleted from the sanitized props when `overStyled` is `false`. This enforces strict design token adherence — all item colors come from the CSS module referencing Recursica variables. Developers requiring custom color semantics must either:
82
-
83
- 1. Use `overStyled={true}` as an explicit escape hatch
84
- 2. Contribute a proper Recursica variant to the token system
85
-
86
- **Rationale:** The Recursica token set does not currently include danger-specific or semantic-color variants for menu items. Allowing arbitrary `color` values would break design system consistency.
87
-
88
- ---
89
-
90
- ## 5. Menu.Target Pass-Through
91
-
92
- **Decision:** `Menu.Target` is a transparent pass-through with no styling applied.
93
-
94
- **Implementation:** The target wrapper exists solely to manage Mantine's ref forwarding and event binding for the trigger element. No `filterStylingProps` or CSS module classes are applied — the trigger's appearance is entirely controlled by whatever component the integrator places inside it (e.g., `<Button>`).
95
-
96
- ---
97
-
98
- ## 6. Sub-Menu Support
99
-
100
- **Decision:** We wrap the full Mantine sub-menu hierarchy (`Menu.Sub`, `Menu.Sub.Target`, `Menu.Sub.Item`, `Menu.Sub.Dropdown`).
101
-
102
- **Implementation:** Sub-menu components inherit the same CSS module classes as their top-level counterparts since the Recursica token set does not distinguish sub-menu-specific styling. `Menu.Sub.Item` also strips the `color` prop in strict mode, consistent with `Menu.Item`. The sub-menu dropdown portal inherits the same `classNames` from the root Menu's `classNames` mapping.
103
-
104
- ---
105
-
106
- ## 7. Dropdown Container Padding
107
-
108
- **Decision:** The dropdown container uses `divider-item-gap` as its internal padding.
109
-
110
- **Implementation:** This ensures consistent spacing between the dropdown border and its content items. The gap token (`4px` by default) creates a subtle inset that visually separates items from the container edge, matching the Figma design specifications.
111
-
112
- ---
113
-
114
- ## 8. Minimal CSS Override Philosophy
115
-
116
- **Decision:** The CSS module only overrides visual design tokens (colors, typography, spacing, borders, shadows). All structural layout properties (flex, cursor, pointer-events, box-sizing, etc.) are deferred to Mantine's native behavior.
117
-
118
- **Implementation:** Unlike form-field components (e.g., Dropdown/TextField) which require deep structural overrides to strip Mantine's macro wrappers, the Menu is an overlay component where Mantine's native layout is already correct. We avoid setting:
119
-
120
- - `overflow` on the dropdown (Mantine renders sub-menu dropdowns inside the parent DOM tree using Floating UI absolute positioning — setting overflow clips them)
121
- - `display`, `align-items`, `width`, `cursor` on items (Mantine's button-based items already handle this)
122
- - `pointer-events` on disabled items (Mantine handles disabled natively)
123
-
124
- **Rationale:** Early iterations included aggressive structural resets (like `overflow: hidden`, `box-sizing: border-box`, `margin: 0`) cargo-culted from the Dropdown component. These caused sub-menus to render clipped inside the parent dropdown with scrollbars. The lesson: default to Mantine's native behavior and only override what the Recursica token system explicitly needs to control.
53
+ - The full Mantine composition API is supported, including sub-menus: `Menu`, `Menu.Target`, `Menu.Dropdown`, `Menu.Item`, `Menu.Divider`, `Menu.Label`, `Menu.Sub`, `Menu.Sub.Target`, `Menu.Sub.Item`, and `Menu.Sub.Dropdown`.
54
+ - The `color` prop on `Menu.Item` (and `Menu.Sub.Item`) — used by Mantine for semantics like a "danger" item — is ignored unless `overStyled={true}` is set.
@@ -45,7 +45,7 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
45
45
 
46
46
  ## Architecture
47
47
 
48
- The `Modal` component strictly wraps Mantine's `<Modal>` primitive. We strip Mantine's abstract native styling props (`size`, `radius`, `shadow`) via the `overStyled` interface and strictly inject CSS variable definitions onto the internal node abstractions (`.content`, `.header`, `.body`, `.title`).
48
+ `Modal` wraps Mantine's `<Modal>` component. Native Mantine styling props (`size`, `radius`, `shadow`) are not used — appearance and sizing are fully controlled by the design system tokens instead.
49
49
 
50
50
  ## Limitations & Structural Decisions
51
51
 
@@ -53,6 +53,6 @@ The `Modal` component strictly wraps Mantine's `<Modal>` primitive. We strip Man
53
53
 
54
54
  Mantine natively exposes an abstract `size` prop (`"sm" | "md" | "lg" | "xl"`) that scales the Modal geometry. The Recursica UI Kit explicitly dictates strict geometric bounding boxes: `max-width: 960px` and `min-width: 304px`. To enforce absolute parity with the design system, the `size` prop has been intentionally omitted from the component's interface. The width of the Modal will scale fluidly strictly between these Figma-driven pixel limits.
55
55
 
56
- ### 2. Scroll Dividers behavior
56
+ ### 2. Scroll Dividers
57
57
 
58
- Mantine internally handles scroll state natively, dynamically showing/hiding a divider line when content overflows in `.body`. This logic is tightly coupled to React DOM measurements internally. Our component inherits this dynamic behavior rather than statically rendering a permanent divider, matching Mantine's robust overflow UX. However, we aggressively override the generated `border-bottom` via CSS modules to ensure that when it _does_ appear, it correctly utilizes the `--recursica_ui-kit_components_modal_colors_scroll-divider` variable and `--recursica_ui-kit_components_modal_properties_scroll-divider-thickness` token.
58
+ When the modal's content overflows and becomes scrollable, a divider line automatically appears, styled using the design system's scroll-divider color and thickness tokens.
@@ -39,17 +39,14 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
39
39
 
40
40
  ## 4. Key Integration Features & Constraints
41
41
 
42
- ## 1. Native Macro Wrapper Bypass
42
+ ## 1. Label, Description, and Error Rendering
43
43
 
44
- **Decision:** The `<NumberInput>` component explicitly bypasses Mantine's native `Input.Wrapper` DOM injections.
45
- **Implementation:** We pass `label={undefined}`, `description={undefined}`, and `error={undefined}` directly into the primitive `<MantineNumberInput>`. All visual form control geometry is delegated exclusively to our unified `<FormControlWrapper>`, ensuring 100% token adherence for label spacing and assistive text styling without duplicate DOM rendering.
44
+ Label, description, and error text are rendered using Recursica's standard form-control layout rather than Mantine's native label/description/error rendering, so spacing and styling stay consistent with other form fields.
46
45
 
47
- ## 2. Right Section & Controls Override
46
+ ## 2. Right Section & Controls
48
47
 
49
- **Decision:** Passing a `rightSection` element will natively remove the increment/decrement arrow controls.
50
- **Implementation:** Mantine inherently renders its stepper controls inside the `rightSection` DOM slot. Providing a custom right-aligned icon or text element intentionally overwrites this slot. If a layout strictly requires both a custom right-aligned element and the stepper controls simultaneously, the integrator must manually rebuild the arrows using Mantine's `handlersRef` within a custom right-section wrapper.
48
+ Providing a custom `rightSection` element replaces the built-in increment/decrement controls. To use both together, rebuild the controls manually using Mantine's `handlersRef` API within your custom right section.
51
49
 
52
50
  ## 3. Controls Styling
53
51
 
54
- **Decision:** The increment/decrement control arrows rely partially on native Mantine CSS inheritance.
55
- **Implementation:** The current Recursica design system tokens do not provide explicit UI styling parameters (`background`, `border`, `hover` states) for the inner number-input arrows. We have explicitly removed Mantine's default borders to cleanly nest them inside the unified input box, and mapped the icon colors to the generic `trailing-icon` token variable, but further visual configurations currently fall back to Mantine defaults.
52
+ The increment/decrement control icons currently fall back to Mantine's default styling beyond their color, as design tokens for their background, border, and hover states are not yet defined.
@@ -34,22 +34,3 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
34
34
  > - **Anti-override protection**: Rogues style injections (like inline `style` or arbitrary `className`) are automatically blocked by our prop layer unless `overStyled={true}` is explicitly provided.
35
35
  > - **No Direct Layers**: Do not pass a `layer` prop to this component. To place it on a specific visual layer, wrap it in a `<Layer layer={0|1|2|3}>` component natively.
36
36
  > - **Variables and Theming**: Styling is entirely determined by local CSS variables defined in `recursica_variables_scoped.css` and mapped in the component's CSS module.
37
-
38
- ---
39
-
40
- ## 4. Key Integration Features & Constraints
41
-
42
- ## Architecture Decision: CSS Inheritance vs. Composition
43
-
44
- The Figma design tokens for the `Pagination` component dictate that pagination pages perfectly mimic `Button` variants (e.g. `active-pages_style: "solid"`, `inactive-pages_style: "outline"`, `navigation-controls_style: "text"`).
45
-
46
- As of the `1.2.0` scoped CSS update, the Figma exporter automatically handles these variants. It flattens and aliases the referenced Button properties directly into the `Pagination` component's variable namespace.
47
-
48
- We opted to use Mantine's native `PaginationControl` components to ensure all DOM structure, focus management, and accessibility attributes are preserved natively without us needing to carefully rebuild `Pagination.Items` mappings.
49
-
50
- To honor the Figma design intents while using Mantine's raw `<button>` elements:
51
-
52
- 1. We inherit and map all natively scoped `Pagination` variant styles (small typography, outline/solid/text colors, and hover overlays) directly into `.control` and `.control[data-active]` within `Pagination.module.css`. We do not need to manually reference `Button` variables cross-component.
53
- 2. We inject `data-variant="text"` via `getControlProps` onto the navigation buttons so that they inherit the explicitly aliased text style variables (`navigation-controls`) defined by the UI Kit for pagination.
54
-
55
- This achieves exact optical alignment with the tokens while maintaining Mantine's robust internal event handling for pagination.
@@ -43,103 +43,14 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
43
43
 
44
44
  ## 4. Key Integration Features & Constraints
45
45
 
46
- ## 1. Mapping to Mantine Drawer
46
+ ### Placement
47
47
 
48
- **Decision:** Panel maps to Mantine's `Drawer` component, not `Paper` or `Card`.
48
+ Use the `placement` prop (not `position`) to choose which edge the panel slides in from: `"left"`, `"right"`, `"top"`, or `"bottom"`. It defaults to `"right"`.
49
49
 
50
- **Implementation:** Per the Recursica design system specification, "Panels slide in or expand from the edge of the screen to reveal additional content or functionality." This is the exact behavior of Mantine's `Drawer` component, which provides:
50
+ ### Panel.Footer
51
51
 
52
- - Slide-in animation from any screen edge
53
- - Overlay/backdrop
54
- - Portal rendering
55
- - Focus trapping
56
- - Scroll locking
57
- - Close button and title in header
52
+ A `Panel.Footer` sub-component is available for footer content and action buttons.
58
53
 
59
- Paper and Card are static containers; Drawer is an overlay that matches Panel's defined behavior.
54
+ ### Content Overflow
60
55
 
61
- ---
62
-
63
- ## 2. Token Namespace: `panel`
64
-
65
- **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_panel_*` namespace.
66
-
67
- **Implementation:** The Recursica token system defines the `panel` namespace covering:
68
-
69
- - Geometry: border-radius, border-size, min-width (200px), max-width (960px)
70
- - Content padding: content-horizontal-padding (xl), content-vertical-padding (lg)
71
- - Header/Footer padding: header-footer-horizontal-padding (xl), header-footer-vertical-padding (md)
72
- - Spacing: header-close-gap (md), footer-button-gap (md)
73
- - Divider: divider-size (1px), divider-color
74
- - Elevation: elevation-3
75
- - Colors (layer-aware): background, border-color, content, divider-color, header-footer-background, title
76
- - Non-CSS: header-style ("h3") — see §7
77
-
78
- No tokens from other component namespaces are referenced.
79
-
80
- ---
81
-
82
- ## 3. ClassNames Mapping to Drawer
83
-
84
- **Decision:** Panel maps CSS module classes to Mantine Drawer's stylesNames.
85
-
86
- **Implementation:** The Drawer stylesNames used are:
87
-
88
- - `content` — Outer container (background, border, elevation)
89
- - `header` — Title bar with close button (padding, divider, gap)
90
- - `title` — Title text color
91
- - `body` — Scrollable content area (padding)
92
-
93
- Other stylesNames (`overlay`, `root`, `inner`, `close`) are left to Mantine defaults.
94
-
95
- ---
96
-
97
- ## 4. Default Placement Override
98
-
99
- **Decision:** Use `placement` instead of `position` for configuring slide-out direction, and default it to `"right"`.
100
-
101
- **Implementation:** The prop was renamed from `position` to `placement` to prevent collision with the CSS `position` keyword, which is strictly blocked by the styling gatekeeper (`BLOCKED_STYLING_KEYS`). This allows configuring the drawer direction natively while maintaining strict design-system boundaries. The `placement="right"` default is mapped internally to Mantine Drawer's `position` prop before any other sanitized props are applied. Right-side panels are the most common pattern for supplementary content, settings, and detail views.
102
-
103
- ---
104
-
105
- ## 5. Custom Panel.Footer
106
-
107
- **Decision:** A custom `Panel.Footer` sub-component is provided. Mantine's Drawer does not have a native footer.
108
-
109
- **Implementation:** `Panel.Footer` is a `<div>` with inline styles referencing Recursica CSS variables for:
110
-
111
- - `header-footer-background` and `header-footer-padding` tokens
112
- - Top divider using `divider-size` and `divider-color`
113
- - `footer-button-gap` for action button spacing
114
- - `margin-top: auto` to push the footer to the bottom
115
-
116
- Inline styles are used instead of a CSS module class because the footer is rendered inside the Drawer's `<body>` element, and the CSS variables are applied to the body's parent container. The inline styles ensure the footer correctly references the panel tokens regardless of DOM position.
117
-
118
- ---
119
-
120
- ## 6. Hardcoded Values
121
-
122
- ### `border-style: solid` (CSS module, `.content`)
123
-
124
- Mantine's Drawer content does not set `border-style` natively. Without this, the border-width and border-color tokens have no visible effect. Same pattern as Card, Menu, HoverCard, Tooltip.
125
-
126
- ---
127
-
128
- ## 7. `header-style` Typography Utility Class
129
-
130
- **Decision:** The `header-style` token exports as the string `"h3"`, so we use the generated global utility class `.recursica_brand_typography_h3`.
131
-
132
- **Implementation:** The PostCSS compiler generates `.recursica_brand_typography_<typeName>` classes at the bottom of the scoped variables file to apply full typography definitions without assigning variables inline. We apply this to the `.title` class via `composes: recursica_brand_typography_h3 from global;`.
133
-
134
- ---
135
-
136
- ## 8. Panel Types: Standard vs Scrollable
137
-
138
- **Decision:** Both standard and scrollable types are supported natively.
139
-
140
- **Implementation:** Per the Recursica specification:
141
-
142
- - **Standard** — All content visible without scrolling. The default behavior when content fits.
143
- - **Scrollable** — Internal scrollbar enabled when content exceeds the panel height. Header and footer CTAs remain pinned.
144
-
145
- Mantine's Drawer handles this automatically — the `body` section scrolls when content overflows, while the `header` remains fixed. The `Panel.Footer` uses `margin-top: auto` to stay at the bottom.
56
+ When content exceeds the panel's available height, the body scrolls internally while the header and footer stay fixed in place.
@@ -48,74 +48,14 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
48
48
 
49
49
  ## 4. Key Integration Features & Constraints
50
50
 
51
- ## 1. Composable API Preservation (1:1 Mapping)
51
+ ### Composition
52
52
 
53
- **Decision:** We maintain the exact library composition API structure (`<Popover>`, `<Popover.Target>`, `<Popover.Dropdown>`) as a 1:1 React component mapping.
53
+ `Popover`, `Popover.Target`, and `Popover.Dropdown` are used together the same way as in Mantine: `Popover.Target` wraps the trigger element and applies no styling of its own, while `Popover.Dropdown` renders the styled panel content.
54
54
 
55
- **Implementation:** Mantine's Popover internally manages click detection, open/close state, Floating UI positioning, and portal rendering. By preserving the exact sub-component tree, Recursica safely inherits all of these behaviors without reimplementation.
55
+ ### Beak (Arrow)
56
56
 
57
- ---
58
-
59
- ## 2. Popover.Target Pass-Through
60
-
61
- **Decision:** `Popover.Target` is a transparent pass-through with no styling applied.
62
-
63
- **Implementation:** The target wrapper exists solely to manage Mantine's ref forwarding and event binding for the trigger element. No `filterStylingProps` or CSS module classes are applied — the trigger's appearance is entirely controlled by whatever component the integrator places inside it (e.g., `<Button>`). This is identical to the `Menu.Target` and `HoverCard.Target` pattern.
64
-
65
- ---
66
-
67
- ## 3. Token Namespace: `hover-card-popover`
68
-
69
- **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_hover-card-popover_*` namespace.
70
-
71
- **Implementation:** The Recursica token system defines a single shared namespace (`hover-card-popover`) for this component and HoverCard. It covers geometry (border-radius, border-size, padding, min/max-width), typography (content-text\_\*), elevation, and layer-aware colors (background, border-color, content).
72
-
73
- ---
74
-
75
- ## 4. Hardcoded Values
76
-
77
- **Decision:** Two hardcoded values exist in the component.
78
-
79
- ### `border-style: solid` (CSS module)
80
-
81
- Mantine renders the dropdown using its `Paper` component, which does not set `border-style` natively. Without this hardcoded value, the border-width and border-color tokens would have no visible effect. This is the same pattern used in the Menu component's dropdown.
82
-
83
- ### `arrowSize` defaulted to `16` (Popover.tsx)
84
-
85
- Mantine's `arrowSize` prop is a JavaScript number used for inline style calculations: it sets `width`, `height`, and a positioning offset (`-arrowSize/2`) directly on the arrow `<div>` element. These inline styles **cannot** be overridden via CSS without `!important`, and the positioning offset has no CSS equivalent. This means the beak size cannot be fully CSS-driven — it is one of the rare cases where a design token value must be mirrored as a JS prop.
86
-
87
- The default value `16` matches the Recursica `beak-size` token (`--recursica_ui-kit_components_hover-card-popover_properties_beak-size: 16px`). Developers can override `arrowSize` if needed, but should be aware this is a design system concern. If the token value changes, the default in `Popover.tsx` must also be updated.
88
-
89
- This is documented as an open issue in `docs/COMPONENT_ISSUES.md`.
90
-
91
- **Note:** Mantine calls this the "arrow"; Recursica calls it the "beak". The Recursica prop `withBeak` (defaulting to `true`) maps to Mantine's `withArrow`. Both `withBeak` and `withArrow` are accepted; `withBeak` takes precedence.
92
-
93
- ---
94
-
95
- ## 5. Minimal CSS Override Philosophy
96
-
97
- **Decision:** The CSS module only overrides visual design tokens (colors, typography, spacing, borders, shadows). All structural layout properties are deferred to Mantine's native behavior.
98
-
99
- **Implementation:** Popover is an overlay component where Mantine's native Floating UI positioning and Paper layout are already correct. We avoid setting:
100
-
101
- - `overflow` on the dropdown (Mantine handles scroll behavior natively)
102
- - `display`, `position`, `z-index` (Floating UI controls these)
103
- - `pointer-events` (Mantine manages hover detection across target and dropdown)
104
-
105
- **Rationale:** Default to Mantine's native behavior and only override what the Recursica token system explicitly needs to control.
106
-
107
- ---
108
-
109
- ## 6. ClassNames Merging on Root
110
-
111
- **Decision:** The root `Popover` component binds CSS module classes via `classNames` on the Mantine root.
112
-
113
- **Implementation:** The root component receives `classNames={{ dropdown: styles.dropdown, arrow: styles.arrow }}` and merges any consumer-provided `classNames` when `overStyled` is true. This ensures our token-driven styles are applied to the dropdown panel without wrapper divs, and the consumer's classes are additive.
114
-
115
- ---
116
-
117
- ## 7. Default Position Override
57
+ The Recursica prop `withBeak` (defaulting to `true`) controls whether the pointer beak is shown, and maps to Mantine's `withArrow`. Both `withBeak` and `withArrow` are accepted; `withBeak` takes precedence. An `arrowSize` prop is also available (default `16`) to size the beak.
118
58
 
119
- **Decision:** Recursica defaults `position` to `"top"`. Mantine defaults to `"bottom"`.
59
+ ### Default Position
120
60
 
121
- **Implementation:** The `position="top"` default is set on the Mantine root element before the prop spread, so developer-provided `position` values still take precedence. This aligns with Recursica's design intent for overlay components to appear above their trigger by default.
61
+ Recursica defaults `position` to `"top"` rather than Mantine's `"bottom"`. Pass your own `position` value to override it.