@pnx-mixtape/mxds 0.1.0-rc.4 → 0.1.0-rc.6

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 (236) hide show
  1. package/.claude/skills/create-mxds-component/SKILL.md +77 -0
  2. package/DESIGN.md +595 -0
  3. package/README.md +2 -3
  4. package/dist/build/accordion.entry.js +2 -1
  5. package/dist/build/autocomplete.entry.js +1 -1
  6. package/dist/build/back-to-top.css +1 -0
  7. package/dist/build/back-to-top.entry.js +1 -0
  8. package/dist/build/base.css +1 -1
  9. package/dist/build/button.css +1 -1
  10. package/dist/build/card.css +1 -1
  11. package/dist/build/chunks/Accordion-Bqe8vTNR.js +44 -0
  12. package/dist/build/chunks/AccordionGroup-BYtoAFxg.js +47 -0
  13. package/dist/build/chunks/AccordionMobile-D517oXYZ.js +26 -0
  14. package/dist/build/chunks/Autocomplete-kRo8RNbC.js +135 -0
  15. package/dist/build/chunks/BackToTop-4M-Wl7z3.js +64 -0
  16. package/dist/build/chunks/ClosableAlert-CZ114JsL.js +55 -0
  17. package/dist/build/chunks/CookieConsent-wd8gQ2Oe.js +18 -0
  18. package/dist/build/chunks/Dialog-DoYcGqhW.js +48 -0
  19. package/dist/build/chunks/DropMenu-CSTZq0Zt.js +68 -0
  20. package/dist/build/chunks/FeedbackForm-BsL7fv8s.js +64 -0
  21. package/dist/build/chunks/Gallery-DqJPujX5.js +165 -0
  22. package/dist/build/chunks/GlobalToggle-7H3F1Wvc.js +82 -0
  23. package/dist/build/chunks/InPageNavigation-D7m3IN89.js +86 -0
  24. package/dist/build/chunks/Navigation-CRModT40.js +142 -0
  25. package/dist/build/chunks/Popover-CiTAftNU.js +30 -0
  26. package/dist/build/chunks/Sticky-C1H8MNqK.js +51 -0
  27. package/dist/build/chunks/Tabs-BJD3kABL.js +160 -0
  28. package/dist/build/chunks/Translate-yAgySwet.js +46 -0
  29. package/dist/build/chunks/cookie-compliance-CG6x-TcV.js +119 -0
  30. package/dist/build/chunks/enums-BrHxPZZ_.js +36 -0
  31. package/dist/build/chunks/polyfills-D-qh1a_2.js +33 -0
  32. package/dist/build/chunks/utilities-B9mNRs7V.js +255 -0
  33. package/dist/build/content-block.css +1 -1
  34. package/dist/build/cookie-consent.entry.js +1 -1
  35. package/dist/build/dialog.entry.js +1 -1
  36. package/dist/build/drop-menu.entry.js +1 -1
  37. package/dist/build/elements.js +147 -1
  38. package/dist/build/feedback-form.css +1 -0
  39. package/dist/build/feedback-form.entry.js +1 -0
  40. package/dist/build/filters.entry.js +145 -1
  41. package/dist/build/form.css +1 -1
  42. package/dist/build/gallery.entry.js +1 -1
  43. package/dist/build/global-alert.entry.js +1 -1
  44. package/dist/build/header.css +1 -1
  45. package/dist/build/header.entry.js +1 -1
  46. package/dist/build/icon.css +1 -1
  47. package/dist/build/in-page-navigation.entry.js +1 -1
  48. package/dist/build/navigation.css +1 -1
  49. package/dist/build/navigation.entry.js +1 -1
  50. package/dist/build/pagination.css +1 -1
  51. package/dist/build/popover.entry.js +1 -1
  52. package/dist/build/react.js +2291 -1
  53. package/dist/build/side-navigation.css +1 -1
  54. package/dist/build/sidebar.css +1 -1
  55. package/dist/build/sticky.entry.js +1 -1
  56. package/dist/build/tabs.entry.js +1 -1
  57. package/dist/build/translate.css +1 -0
  58. package/dist/build/translate.entry.js +1 -0
  59. package/dist/build/utility-list.entry.js +53 -1
  60. package/dist/types/Component/BackToTop/BackToTop.d.ts +20 -0
  61. package/dist/types/Component/BackToTop/BackToTop.d.ts.map +1 -0
  62. package/dist/types/Component/BackToTop/Elements/BackToTop.d.ts +27 -0
  63. package/dist/types/Component/BackToTop/Elements/BackToTop.d.ts.map +1 -0
  64. package/dist/types/Component/BackToTop/back-to-top.entry.d.ts +2 -0
  65. package/dist/types/Component/BackToTop/back-to-top.entry.d.ts.map +1 -0
  66. package/dist/types/Component/DropMenu/DropMenu.d.ts +2 -1
  67. package/dist/types/Component/DropMenu/DropMenu.d.ts.map +1 -1
  68. package/dist/types/Component/Navigation/Elements/Navigation.d.ts +2 -3
  69. package/dist/types/Component/Navigation/Elements/Navigation.d.ts.map +1 -1
  70. package/dist/types/Component/SideNavigation/Components/SideNavigationTree.d.ts +3 -3
  71. package/dist/types/Component/SideNavigation/Components/SideNavigationTree.d.ts.map +1 -1
  72. package/dist/types/Component/SideNavigation/SideNavigation.d.ts +3 -1
  73. package/dist/types/Component/SideNavigation/SideNavigation.d.ts.map +1 -1
  74. package/dist/types/Component/Tabs/Components/TabDropMenu.d.ts.map +1 -1
  75. package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
  76. package/dist/types/Component/Translate/Elements/Translate.d.ts +23 -0
  77. package/dist/types/Component/Translate/Elements/Translate.d.ts.map +1 -0
  78. package/dist/types/Component/Translate/Translate.d.ts +37 -0
  79. package/dist/types/Component/Translate/Translate.d.ts.map +1 -0
  80. package/dist/types/Component/Translate/languages.d.ts +11 -0
  81. package/dist/types/Component/Translate/languages.d.ts.map +1 -0
  82. package/dist/types/Component/Translate/translate.entry.d.ts +2 -0
  83. package/dist/types/Component/Translate/translate.entry.d.ts.map +1 -0
  84. package/dist/types/Form/FeedbackForm/Elements/FeedbackForm.d.ts +37 -0
  85. package/dist/types/Form/FeedbackForm/Elements/FeedbackForm.d.ts.map +1 -0
  86. package/dist/types/Form/FeedbackForm/FeedbackForm.d.ts +36 -0
  87. package/dist/types/Form/FeedbackForm/FeedbackForm.d.ts.map +1 -0
  88. package/dist/types/Form/FeedbackForm/feedback-form.entry.d.ts +2 -0
  89. package/dist/types/Form/FeedbackForm/feedback-form.entry.d.ts.map +1 -0
  90. package/dist/types/Utility/Elements/google-translate-widget.d.ts +91 -0
  91. package/dist/types/Utility/Elements/google-translate-widget.d.ts.map +1 -0
  92. package/dist/types/Utility/utilities.d.ts +2 -0
  93. package/dist/types/Utility/utilities.d.ts.map +1 -1
  94. package/dist/types/elements.d.ts +3 -0
  95. package/dist/types/elements.d.ts.map +1 -1
  96. package/dist/types/enums.d.ts +3 -1
  97. package/dist/types/enums.d.ts.map +1 -1
  98. package/dist/types/react.d.ts +3 -0
  99. package/dist/types/react.d.ts.map +1 -1
  100. package/dist/types/tokens.d.ts +1 -0
  101. package/dist/types/tokens.d.ts.map +1 -1
  102. package/docs/API.md +1215 -0
  103. package/docs/CSSGuidelines.md +893 -0
  104. package/docs/CodeQuality.md +940 -0
  105. package/docs/ComponentArchitecture.md +714 -0
  106. package/docs/Contributing.md +626 -0
  107. package/docs/MemoryManagement.md +598 -0
  108. package/docs/Migrations.md +6 -0
  109. package/docs/README.md +100 -0
  110. package/docs/Security.md +444 -0
  111. package/docs/Testing.md +739 -0
  112. package/docs/Troubleshooting.md +674 -0
  113. package/package.json +9 -6
  114. package/src/Atom/Button/Button.tsx +1 -1
  115. package/src/Atom/Button/__snapshots__/Button.stories.tsx.snap +2 -2
  116. package/src/Atom/Button/_buttons.css +1 -0
  117. package/src/Atom/Icon/README.md +1 -1
  118. package/src/Atom/Icon/_icon.css +1 -0
  119. package/src/Atom/Icon/images/thumb.svg +1 -0
  120. package/src/Atom/Link/_links.css +1 -0
  121. package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +4 -9
  122. package/src/Atom/Media/media.twig +4 -5
  123. package/src/Component/BackToTop/BackToTop.stories.ts +127 -0
  124. package/src/Component/BackToTop/BackToTop.stories.tsx +53 -0
  125. package/src/Component/BackToTop/BackToTop.tsx +120 -0
  126. package/src/Component/BackToTop/Elements/BackToTop.ts +119 -0
  127. package/src/Component/BackToTop/README.md +164 -0
  128. package/src/Component/BackToTop/__snapshots__/BackToTop.stories.ts.snap +24 -0
  129. package/src/Component/BackToTop/__snapshots__/BackToTop.stories.tsx.snap +3 -0
  130. package/src/Component/BackToTop/back-to-top.css +70 -0
  131. package/src/Component/BackToTop/back-to-top.entry.js +1 -0
  132. package/src/Component/BackToTop/back-to-top.twig +26 -0
  133. package/src/Component/Card/Components/CardContent.tsx +1 -1
  134. package/src/Component/Card/Components/CardMedia.tsx +1 -1
  135. package/src/Component/Card/__snapshots__/Card.stories.tsx.snap +3 -3
  136. package/src/Component/Card/card.css +1 -1
  137. package/src/Component/Carousel/Carousel.stories.ts +66 -36
  138. package/src/Component/Carousel/__snapshots__/Carousel.stories.tsx.snap +5 -5
  139. package/src/Component/ContentBlock/Components/ContentBlockMedia.tsx +1 -1
  140. package/src/Component/ContentBlock/content-block.css +1 -1
  141. package/src/Component/DropMenu/DropMenu.stories.ts +21 -2
  142. package/src/Component/DropMenu/DropMenu.tsx +14 -4
  143. package/src/Component/DropMenu/__snapshots__/DropMenu.stories.ts.snap +3 -3
  144. package/src/Component/DropMenu/__snapshots__/DropMenu.stories.tsx.snap +2 -2
  145. package/src/Component/DropMenu/drop-menu.twig +7 -1
  146. package/src/Component/HeroBanner/__snapshots__/HeroBanner.stories.tsx.snap +1 -1
  147. package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +48 -13
  148. package/src/Component/InPageNavigation/in-page-navigation.twig +4 -6
  149. package/src/Component/ListItem/Components/ListItemMedia.tsx +1 -1
  150. package/src/Component/Navigation/Elements/Navigation.ts +25 -29
  151. package/src/Component/Navigation/README.md +11 -10
  152. package/src/Component/Navigation/__snapshots__/Navigation.stories.ts.snap +8 -8
  153. package/src/Component/Navigation/_navigation-dropdown.css +9 -11
  154. package/src/Component/Navigation/_navigation-mega.css +5 -11
  155. package/src/Component/Navigation/_navigation.css +5 -3
  156. package/src/Component/Navigation/menu.twig +1 -1
  157. package/src/Component/Navigation/navigation.twig +0 -1
  158. package/src/Component/Pagination/Pagination.stories.ts +57 -27
  159. package/src/Component/Pagination/pagination.css +16 -7
  160. package/src/Component/Popover/__snapshots__/Popover.stories.tsx.snap +3 -3
  161. package/src/Component/SideNavigation/Components/SideNavigationTree.tsx +68 -24
  162. package/src/Component/SideNavigation/README.md +78 -32
  163. package/src/Component/SideNavigation/SideNavigation.stories.ts +41 -2
  164. package/src/Component/SideNavigation/SideNavigation.stories.tsx +31 -0
  165. package/src/Component/SideNavigation/SideNavigation.tsx +13 -3
  166. package/src/Component/SideNavigation/__snapshots__/SideNavigation.stories.ts.snap +87 -3
  167. package/src/Component/SideNavigation/__snapshots__/SideNavigation.stories.tsx.snap +2 -0
  168. package/src/Component/SideNavigation/side-navigation.css +3 -3
  169. package/src/Component/SideNavigation/side-navigation.twig +10 -3
  170. package/src/Component/Tabs/Components/TabDropMenu.tsx +7 -1
  171. package/src/Component/Tabs/Elements/Tabs.ts +13 -3
  172. package/src/Component/Tabs/Tabs.stories.ts +33 -12
  173. package/src/Component/Tabs/Tabs.stories.tsx +28 -14
  174. package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +3 -3
  175. package/src/Component/Tabs/__snapshots__/Tabs.stories.tsx.snap +2 -2
  176. package/src/Component/Translate/Elements/Translate.ts +76 -0
  177. package/src/Component/Translate/README.md +285 -0
  178. package/src/Component/Translate/Translate.stories.ts +87 -0
  179. package/src/Component/Translate/Translate.stories.tsx +77 -0
  180. package/src/Component/Translate/Translate.tsx +213 -0
  181. package/src/Component/Translate/__snapshots__/Translate.stories.ts.snap +18 -0
  182. package/src/Component/Translate/__snapshots__/Translate.stories.tsx.snap +3 -0
  183. package/src/Component/Translate/languages.ts +39 -0
  184. package/src/Component/Translate/translate.css +62 -0
  185. package/src/Component/Translate/translate.entry.js +1 -0
  186. package/src/Component/Translate/translate.twig +44 -0
  187. package/src/Form/Autocomplete/Autocomplete.stories.tsx +2 -1
  188. package/src/Form/FeedbackForm/Elements/FeedbackForm.ts +122 -0
  189. package/src/Form/FeedbackForm/FeedbackForm.stories.ts +99 -0
  190. package/src/Form/FeedbackForm/FeedbackForm.stories.tsx +73 -0
  191. package/src/Form/FeedbackForm/FeedbackForm.tsx +196 -0
  192. package/src/Form/FeedbackForm/README.md +233 -0
  193. package/src/Form/FeedbackForm/__snapshots__/FeedbackForm.stories.ts.snap +125 -0
  194. package/src/Form/FeedbackForm/__snapshots__/FeedbackForm.stories.tsx.snap +5 -0
  195. package/src/Form/FeedbackForm/feedback-form.css +74 -0
  196. package/src/Form/FeedbackForm/feedback-form.entry.js +1 -0
  197. package/src/Form/FeedbackForm/feedback-form.twig +80 -0
  198. package/src/Form/form.css +17 -19
  199. package/src/Introduction.mdx +1 -1
  200. package/src/Layout/Footer/__snapshots__/Footer.stories.ts.snap +3 -3
  201. package/src/Layout/Header/Components/GlobalToggle.tsx +1 -1
  202. package/src/Layout/Header/Header.stories.ts +53 -48
  203. package/src/Layout/Header/Header.stories.tsx +35 -22
  204. package/src/Layout/Header/Header.tsx +2 -2
  205. package/src/Layout/Header/__snapshots__/Header.stories.ts.snap +18 -14
  206. package/src/Layout/Header/__snapshots__/Header.stories.tsx.snap +2 -2
  207. package/src/Layout/Header/_toggles.css +7 -7
  208. package/src/Layout/Page/__snapshots__/Page.stories.tsx.snap +1 -1
  209. package/src/Layout/Sidebar/sidebar.css +10 -3
  210. package/src/Utility/Elements/google-translate-widget.ts +293 -0
  211. package/src/Utility/global.d.ts +4 -0
  212. package/src/Utility/utilities.ts +2 -0
  213. package/src/_custom-media.css +2 -0
  214. package/src/elements.ts +3 -0
  215. package/src/enums.ts +2 -0
  216. package/src/react.ts +3 -0
  217. package/src/tokens.js +1 -0
  218. package/dist/build/chunks/Accordion-GN0sME4W.js +0 -1
  219. package/dist/build/chunks/AccordionGroup-Bzqp5__q.js +0 -1
  220. package/dist/build/chunks/AccordionMobile-DG5jSe8Q.js +0 -1
  221. package/dist/build/chunks/Autocomplete-B4RtkAyh.js +0 -1
  222. package/dist/build/chunks/ClosableAlert-DEhj8Nxm.js +0 -1
  223. package/dist/build/chunks/CookieConsent-BkCMRVST.js +0 -1
  224. package/dist/build/chunks/Dialog-Haahn-Yn.js +0 -1
  225. package/dist/build/chunks/DropMenu-1baQc8Yc.js +0 -1
  226. package/dist/build/chunks/Gallery-D8NGXy2Q.js +0 -1
  227. package/dist/build/chunks/GlobalToggle-B52fdBQu.js +0 -1
  228. package/dist/build/chunks/InPageNavigation-Dyn2TR-N.js +0 -1
  229. package/dist/build/chunks/Navigation-hOrfLq7h.js +0 -1
  230. package/dist/build/chunks/Popover-Cxg-s5Jn.js +0 -1
  231. package/dist/build/chunks/Sticky-BGCuuEJK.js +0 -1
  232. package/dist/build/chunks/Tabs-DYy1YTdU.js +0 -15
  233. package/dist/build/chunks/cookie-compliance-Bbm-0vpj.js +0 -1
  234. package/dist/build/chunks/enums-DLV_1NAf.js +0 -1
  235. package/dist/build/chunks/polyfills-C7pRt54M.js +0 -1
  236. package/dist/build/chunks/utilities-_Bq3TxAQ.js +0 -1
@@ -0,0 +1,598 @@
1
+ # Memory Management Guide
2
+
3
+ Prevent memory leaks in Web Components and React applications.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Why Memory Leaks Matter](#why-memory-leaks-matter)
8
+ - [Event Listeners](#event-listeners)
9
+ - [Observers](#observers)
10
+ - [Timers](#timers)
11
+ - [React Hooks](#react-hooks)
12
+ - [Testing for Leaks](#testing-for-leaks)
13
+ - [Common Pitfalls](#common-pitfalls)
14
+
15
+ ---
16
+
17
+ ## Why Memory Leaks Matter
18
+
19
+ Memory leaks occur when resources aren't cleaned up properly, causing:
20
+
21
+ - **Increased memory usage** over time
22
+ - **Degraded performance** as garbage collection struggles
23
+ - **Browser crashes** in long-running applications
24
+ - **Wasted CPU cycles** from zombie event listeners
25
+
26
+ **In a design system**, memory leaks are especially critical because:
27
+
28
+ - Components may be created/destroyed frequently
29
+ - A leak in one component multiplies across all instances
30
+ - Consumer applications may run for hours/days
31
+
32
+ ---
33
+
34
+ ## Event Listeners
35
+
36
+ ### The Problem
37
+
38
+ ```typescript
39
+ // ❌ BAD - Memory leak
40
+ class MyComponent extends HTMLElement {
41
+ connectedCallback() {
42
+ this.addEventListener("click", this.handleClick)
43
+ document.addEventListener("keydown", this.handleKeyDown)
44
+ }
45
+
46
+ // No disconnectedCallback = listeners never removed!
47
+ }
48
+ ```
49
+
50
+ **Result:** Every time a component is created and destroyed, listeners accumulate.
51
+
52
+ ### The Solution: AbortController
53
+
54
+ ```typescript
55
+ // ✅ GOOD - Proper cleanup
56
+ class MyComponent extends HTMLElement {
57
+ controller: AbortController
58
+
59
+ constructor() {
60
+ super()
61
+ this.controller = new AbortController()
62
+ }
63
+
64
+ connectedCallback() {
65
+ const { signal } = this.controller
66
+
67
+ // All listeners use the signal
68
+ this.addEventListener("click", this.handleClick, { signal })
69
+ document.addEventListener("keydown", this.handleKeyDown, { signal })
70
+ window.addEventListener("resize", this.handleResize, { signal })
71
+ }
72
+
73
+ disconnectedCallback() {
74
+ // One line removes ALL listeners
75
+ this.controller.abort()
76
+ }
77
+
78
+ handleClick = () => {
79
+ /* ... */
80
+ }
81
+ handleKeyDown = () => {
82
+ /* ... */
83
+ }
84
+ handleResize = () => {
85
+ /* ... */
86
+ }
87
+ }
88
+ ```
89
+
90
+ ### Benefits of AbortController
91
+
92
+ - ✅ **One line cleanup** - `controller.abort()` removes all listeners
93
+ - ✅ **No manual tracking** - Don't need to remember each listener
94
+ - ✅ **Type-safe** - TypeScript knows about the signal option
95
+ - ✅ **Browser standard** - Supported in all modern browsers
96
+ - ✅ **Impossible to forget** - One controller for all listeners
97
+
98
+ ### Pattern for React
99
+
100
+ ```typescript
101
+ useEffect(() => {
102
+ const controller = new AbortController()
103
+ const { signal } = controller
104
+
105
+ element.addEventListener("click", handler, { signal })
106
+ element.addEventListener("focus", otherHandler, { signal })
107
+
108
+ return () => {
109
+ controller.abort() // Cleanup on unmount
110
+ }
111
+ }, [dependencies])
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Observers
117
+
118
+ ### The Problem
119
+
120
+ ```typescript
121
+ // ❌ BAD - Observer never disconnected
122
+ class MyComponent extends HTMLElement {
123
+ connectedCallback() {
124
+ // Lost reference - can't disconnect later
125
+ const observer = new IntersectionObserver(entries => {
126
+ // ...
127
+ })
128
+ observer.observe(this)
129
+ }
130
+ }
131
+ ```
132
+
133
+ **Result:** Observer keeps running even after component is removed.
134
+
135
+ ### The Solution: Store as Properties
136
+
137
+ ```typescript
138
+ // ✅ GOOD - Store observers as properties
139
+ class MyComponent extends HTMLElement {
140
+ iO: IntersectionObserver
141
+ rO: ResizeObserver
142
+ mO: MutationObserver
143
+
144
+ connectedCallback() {
145
+ // Store observers
146
+ this.iO = new IntersectionObserver(entries => {
147
+ entries.forEach(({ isIntersecting, target }) => {
148
+ if (isIntersecting) {
149
+ // Handle intersection
150
+ }
151
+ })
152
+ })
153
+ this.iO.observe(this)
154
+
155
+ this.rO = new ResizeObserver(entries => {
156
+ entries.forEach(({ contentBoxSize }) => {
157
+ // Handle resize
158
+ })
159
+ })
160
+ this.rO.observe(this)
161
+
162
+ this.mO = new MutationObserver(mutations => {
163
+ mutations.forEach(({ type }) => {
164
+ // Handle mutation
165
+ })
166
+ })
167
+ this.mO.observe(this, { childList: true })
168
+ }
169
+
170
+ disconnectedCallback() {
171
+ // Disconnect all observers
172
+ this.iO.disconnect()
173
+ this.rO.disconnect()
174
+ this.mO.disconnect()
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Optional Observers
180
+
181
+ If an observer might not exist, use optional chaining:
182
+
183
+ ```typescript
184
+ connectedCallback() {
185
+ // Only create if needed
186
+ if (this.hasAttribute('observe-resize')) {
187
+ this.rO = new ResizeObserver(...)
188
+ this.rO.observe(this)
189
+ }
190
+ }
191
+
192
+ disconnectedCallback() {
193
+ // Safe cleanup even if not created
194
+ this.iO?.disconnect()
195
+ this.rO?.disconnect()
196
+ this.mO?.disconnect()
197
+ }
198
+ ```
199
+
200
+ ### Pattern for React
201
+
202
+ ```typescript
203
+ useEffect(() => {
204
+ const observer = new IntersectionObserver(entries => {
205
+ // Handle entries
206
+ })
207
+
208
+ observer.observe(elementRef.current)
209
+
210
+ return () => {
211
+ observer.disconnect() // Cleanup on unmount
212
+ }
213
+ }, [])
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Timers
219
+
220
+ ### The Problem
221
+
222
+ ```typescript
223
+ // ❌ BAD - Timeout never cleared
224
+ class MyComponent extends HTMLElement {
225
+ connectedCallback() {
226
+ // Lost reference - can't clear later
227
+ setTimeout(() => {
228
+ this.doSomething()
229
+ }, 1000)
230
+
231
+ setInterval(() => {
232
+ this.poll()
233
+ }, 5000) // Runs forever!
234
+ }
235
+ }
236
+ ```
237
+
238
+ **Result:** Timers keep firing even after component is destroyed.
239
+
240
+ ### The Solution: Store Timer IDs
241
+
242
+ ```typescript
243
+ // ✅ GOOD - Store timer IDs
244
+ class MyComponent extends HTMLElement {
245
+ timeout: ReturnType<typeof setTimeout>
246
+ interval: ReturnType<typeof setInterval>
247
+
248
+ connectedCallback() {
249
+ // Store timer IDs
250
+ this.timeout = setTimeout(() => {
251
+ this.doSomething()
252
+ }, 1000)
253
+
254
+ this.interval = setInterval(() => {
255
+ this.poll()
256
+ }, 5000)
257
+ }
258
+
259
+ disconnectedCallback() {
260
+ // Clear timers
261
+ clearTimeout(this.timeout)
262
+ clearInterval(this.interval)
263
+ }
264
+ }
265
+ ```
266
+
267
+ ### Add Timeout Safety to setInterval
268
+
269
+ Prevent infinite loops if a condition is never met:
270
+
271
+ ```typescript
272
+ // ✅ GOOD - Has timeout safety
273
+ interval: ReturnType<typeof setInterval>
274
+ timeout: ReturnType<typeof setTimeout>
275
+
276
+ connectedCallback() {
277
+ // Safety timeout: give up after 2 seconds
278
+ this.timeout = setTimeout(() => {
279
+ clearInterval(this.interval)
280
+ this.interval = null
281
+ console.warn('Timeout: condition never met')
282
+ }, 2000)
283
+
284
+ // Check condition every 5ms
285
+ this.interval = setInterval(() => {
286
+ if (this.checkCondition()) {
287
+ clearTimeout(this.timeout)
288
+ clearInterval(this.interval)
289
+ this.timeout = null
290
+ this.interval = null
291
+ // Success!
292
+ }
293
+ }, 5)
294
+ }
295
+
296
+ disconnectedCallback() {
297
+ clearTimeout(this.timeout)
298
+ clearInterval(this.interval)
299
+ }
300
+ ```
301
+
302
+ ### Pattern for React
303
+
304
+ ```typescript
305
+ useEffect(() => {
306
+ const timeout = setTimeout(() => {
307
+ doSomething()
308
+ }, 1000)
309
+
310
+ const interval = setInterval(() => {
311
+ poll()
312
+ }, 5000)
313
+
314
+ return () => {
315
+ clearTimeout(timeout)
316
+ clearInterval(interval)
317
+ }
318
+ }, [])
319
+ ```
320
+
321
+ ---
322
+
323
+ ## React Hooks
324
+
325
+ ### The Problem
326
+
327
+ ```typescript
328
+ // ❌ BAD - No cleanup
329
+ useEffect(() => {
330
+ element.addEventListener('click', handler)
331
+ const observer = new ResizeObserver(...)
332
+ observer.observe(element)
333
+ }, [])
334
+ ```
335
+
336
+ **Result:** Listeners and observers persist after component unmounts.
337
+
338
+ ### The Solution: Return Cleanup Functions
339
+
340
+ ```typescript
341
+ // ✅ GOOD - Proper cleanup
342
+ useEffect(() => {
343
+ const controller = new AbortController()
344
+ element.addEventListener("click", handler, {
345
+ signal: controller.signal,
346
+ })
347
+
348
+ const observer = new ResizeObserver(entries => {
349
+ // Handle resize
350
+ })
351
+ observer.observe(element)
352
+
353
+ return () => {
354
+ controller.abort() // Remove listeners
355
+ observer.disconnect() // Disconnect observer
356
+ }
357
+ }, [element])
358
+ ```
359
+
360
+ ### Separate Controllers per Effect
361
+
362
+ Don't share AbortControllers across multiple effects:
363
+
364
+ ```typescript
365
+ // ❌ BAD - Shared controller causes conflicts
366
+ const Component = () => {
367
+ const controller = new AbortController() // Shared!
368
+
369
+ useEffect(() => {
370
+ element.addEventListener("click", handler, { signal: controller.signal })
371
+ return () => controller.abort() // Aborts controller
372
+ }, [])
373
+
374
+ useEffect(() => {
375
+ // Uses same signal - will be aborted by first effect!
376
+ element.addEventListener("focus", handler, { signal: controller.signal })
377
+ return () => controller.abort() // Tries to abort again
378
+ }, [])
379
+ }
380
+
381
+ // ✅ GOOD - Separate controller per effect
382
+ const Component = () => {
383
+ useEffect(() => {
384
+ const controller = new AbortController() // Isolated
385
+ element.addEventListener("click", handler, { signal: controller.signal })
386
+ return () => controller.abort()
387
+ }, [])
388
+
389
+ useEffect(() => {
390
+ const controller = new AbortController() // Another isolated
391
+ element.addEventListener("focus", handler, { signal: controller.signal })
392
+ return () => controller.abort()
393
+ }, [])
394
+ }
395
+ ```
396
+
397
+ ---
398
+
399
+ ## Testing for Leaks
400
+
401
+ ### Manual Testing
402
+
403
+ ```javascript
404
+ // Test 1: Rapid create/destroy
405
+ for (let i = 0; i < 100; i++) {
406
+ const component = document.createElement("mx-component")
407
+ document.body.append(component)
408
+ component.remove()
409
+ }
410
+
411
+ // Expected: No observers, listeners, or timers remaining
412
+ ```
413
+
414
+ ### Chrome DevTools
415
+
416
+ 1. **Performance Monitor**
417
+ - Show: Listeners, Nodes, JS Heap
418
+ - Create/destroy components
419
+ - Numbers should return to baseline
420
+
421
+ 2. **Memory Profiler**
422
+ - Take heap snapshot before
423
+ - Create 100 components
424
+ - Destroy all components
425
+ - Take heap snapshot after
426
+ - Compare: Detached DOM nodes should be 0
427
+
428
+ 3. **Performance Tab**
429
+ - Record timeline
430
+ - Create/destroy components
431
+ - Look for: Continuous timer fires, listener calls
432
+
433
+ ### Automated Testing
434
+
435
+ ```typescript
436
+ describe("Component Memory Leaks", () => {
437
+ it("should clean up event listeners", () => {
438
+ const component = document.createElement("mx-component")
439
+ document.body.append(component)
440
+
441
+ // Mock addEventListener to track calls
442
+ const addSpy = jest.spyOn(component, "addEventListener")
443
+ const removeSpy = jest.spyOn(component, "removeEventListener")
444
+
445
+ // Trigger connectedCallback
446
+ component.connectedCallback()
447
+
448
+ const listenerCount = addSpy.mock.calls.length
449
+
450
+ // Trigger disconnectedCallback
451
+ component.disconnectedCallback()
452
+ component.remove()
453
+
454
+ // With AbortController, no removeEventListener calls needed
455
+ // Just verify controller.abort() was called
456
+ expect(component.controller).toBeDefined()
457
+ })
458
+ })
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Common Pitfalls
464
+
465
+ ### 1. Forgetting disconnectedCallback
466
+
467
+ ```typescript
468
+ // ❌ BAD
469
+ class MyComponent extends HTMLElement {
470
+ connectedCallback() {
471
+ // Setup...
472
+ }
473
+ // Missing disconnectedCallback!
474
+ }
475
+
476
+ // ✅ GOOD
477
+ class MyComponent extends HTMLElement {
478
+ connectedCallback() {
479
+ // Setup...
480
+ }
481
+
482
+ disconnectedCallback() {
483
+ // Cleanup...
484
+ }
485
+ }
486
+ ```
487
+
488
+ ### 2. Anonymous Functions
489
+
490
+ ```typescript
491
+ // ❌ BAD - Can't remove later
492
+ element.addEventListener("click", () => {
493
+ // Can't removeEventListener - anonymous function!
494
+ })
495
+
496
+ // ✅ GOOD - Use AbortController instead
497
+ const controller = new AbortController()
498
+ element.addEventListener(
499
+ "click",
500
+ () => {
501
+ // Can abort all listeners at once
502
+ },
503
+ { signal: controller.signal },
504
+ )
505
+ ```
506
+
507
+ ### 3. Conditional Cleanup
508
+
509
+ ```typescript
510
+ // ❌ BAD - Conditional cleanup can miss things
511
+ disconnectedCallback() {
512
+ if (this.hasObserver) {
513
+ this.observer.disconnect()
514
+ }
515
+ // What if hasObserver is wrong?
516
+ }
517
+
518
+ // ✅ GOOD - Optional chaining handles it
519
+ disconnectedCallback() {
520
+ this.observer?.disconnect() // Safe even if undefined
521
+ }
522
+ ```
523
+
524
+ ### 4. Nested Components
525
+
526
+ ```typescript
527
+ // ❌ BAD - Parent doesn't clean up children
528
+ class Parent extends HTMLElement {
529
+ connectedCallback() {
530
+ this.child = document.createElement("mx-child")
531
+ this.append(this.child)
532
+ }
533
+ // Child never removed!
534
+ }
535
+
536
+ // ✅ GOOD - Clean up children
537
+ class Parent extends HTMLElement {
538
+ child: HTMLElement
539
+
540
+ connectedCallback() {
541
+ this.child = document.createElement("mx-child")
542
+ this.append(this.child)
543
+ }
544
+
545
+ disconnectedCallback() {
546
+ this.child?.remove() // Remove child
547
+ }
548
+ }
549
+ ```
550
+
551
+ ### 5. Global References
552
+
553
+ ```typescript
554
+ // ❌ BAD - Global reference never cleared
555
+ class MyComponent extends HTMLElement {
556
+ connectedCallback() {
557
+ window.MyComponent = this // Leaked!
558
+ }
559
+ }
560
+
561
+ // ✅ GOOD - Clear global references
562
+ class MyComponent extends HTMLElement {
563
+ connectedCallback() {
564
+ window.MyComponent = this
565
+ }
566
+
567
+ disconnectedCallback() {
568
+ if (window.MyComponent === this) {
569
+ window.MyComponent = undefined
570
+ }
571
+ }
572
+ }
573
+ ```
574
+
575
+ ---
576
+
577
+ ## Checklist
578
+
579
+ Before shipping a component, verify:
580
+
581
+ - [ ] **AbortController** used for all event listeners
582
+ - [ ] **disconnectedCallback** implemented
583
+ - [ ] **Observers** stored as properties and disconnected
584
+ - [ ] **Timers** stored and cleared
585
+ - [ ] **Global references** cleaned up
586
+ - [ ] **React effects** return cleanup functions
587
+ - [ ] **Optional chaining** used for nullable cleanup
588
+ - [ ] **Tested** with rapid create/destroy
589
+ - [ ] **DevTools** confirms no leaks
590
+ - [ ] **Documentation** includes cleanup notes
591
+
592
+ ---
593
+
594
+ ## Related Documentation
595
+
596
+ - [Code Quality Guidelines](./CodeQuality.md)
597
+ - [Component Architecture](./ComponentArchitecture.md)
598
+ - [Security Guidelines](./Security.md)
@@ -0,0 +1,6 @@
1
+ # Migration Guideline
2
+
3
+ This document provides guides for migrating between major versions of Mixtape. Each guide includes a checklist of steps to follow, as well as links to relevant documentation and resources.
4
+
5
+ > [!NOTE]
6
+ > This document is a work in progress. We will be adding migration guides for each major version as they are released. If you have any questions or need help with a migration, please reach out to the Mixtape team.
package/docs/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Documentation
2
+
3
+ Welcome to the Mixtape Design System documentation!
4
+
5
+ ## 📚 Documentation Structure
6
+
7
+ ### Getting Started
8
+
9
+ - [Main README](../README.md) - Installation and basic usage
10
+ - [Introduction](../src/Introduction.mdx) - Design system overview
11
+
12
+ ### Developer Guides
13
+
14
+ - **[Code Quality Guidelines](./CodeQuality.md)** - Comprehensive coding standards
15
+ - **[Component Architecture](./ComponentArchitecture.md)** - How to build components
16
+ - **[Memory Management](./MemoryManagement.md)** - Prevent memory leaks
17
+ - **[Security Guidelines](./Security.md)** - Security best practices
18
+ - **[CSS Guidelines](./CSSGuidelines.md)** - Styling conventions
19
+ - **[Testing Guide](./Testing.md)** - Testing strategies
20
+ - **[Contributing Guide](./Contributing.md)** - How to contribute
21
+
22
+ ### Reference
23
+
24
+ - **[API Documentation](./API.md)** - Utilities and helpers
25
+ - **[Migration Guides](./Migrations.md)** - Version upgrade guides
26
+ - **[Troubleshooting](./Troubleshooting.md)** - Common issues and solutions
27
+
28
+ ### Component Documentation
29
+
30
+ - Component-specific docs are in each component's `README.md`
31
+ - See [src/Atom/README.md](../src/Atom/README.md) for atoms
32
+ - See individual component folders for detailed usage (work in progress)
33
+
34
+ ## 🎯 Quick Links
35
+
36
+ ### For New Developers
37
+
38
+ 1. Read the [Main README](../README.md)
39
+ 2. Review [Component Architecture](./ComponentArchitecture.md)
40
+ 3. Check [Code Quality Guidelines](./CodeQuality.md)
41
+ 4. Browse [Storybook](https://mxds.pnx.io)
42
+
43
+ ### For Contributors
44
+
45
+ 1. Read [Contributing Guide](./Contributing.md)
46
+ 2. Follow [Code Quality Guidelines](./CodeQuality.md)
47
+ 3. Run tests: `pnpm test`
48
+ 4. Submit PR with conventional commits
49
+
50
+ ### For Designers
51
+
52
+ 1. Browse [Storybook](https://mxds.pnx.io)
53
+ 2. Check [Design Tokens](../src/tokens.js)
54
+ 3. Review component READMEs for usage
55
+
56
+ ## 📖 External Resources
57
+
58
+ - [Storybook](https://mxds.pnx.io) - Interactive component library
59
+ - [GitHub Repository](https://github.com/previousnext/mxds)
60
+ - [npm Package](https://www.npmjs.com/package/@pnx-mixtape/mxds)
61
+ - [PreviousNext Website](https://www.previousnext.com.au/)
62
+
63
+ ## 🔄 Document Status
64
+
65
+ | Document | Status | Last Updated |
66
+ | ------------------------ | ----------- | ------------ |
67
+ | CodeQuality.md | ✅ Complete | 2026-03-05 |
68
+ | ComponentArchitecture.md | ✅ Complete | 2026-03-05 |
69
+ | MemoryManagement.md | ✅ Complete | 2026-02-26 |
70
+ | Security.md | ✅ Complete | 2026-02-26 |
71
+ | CSSGuidelines.md | ✅ Complete | 2026-03-05 |
72
+ | Testing.md | ✅ Complete | 2026-02-26 |
73
+ | Contributing.md | ✅ Complete | 2026-02-26 |
74
+ | API.md | ✅ Complete | 2026-03-05 |
75
+ | Troubleshooting.md | ✅ Complete | 2026-02-26 |
76
+
77
+ ## 💡 Contributing to Documentation
78
+
79
+ Found a typo or want to improve our docs? Please submit a PR!
80
+
81
+ ```bash
82
+ # Clone the repo
83
+ git clone https://github.com/previousnext/mxds.git
84
+
85
+ # Create a branch
86
+ git checkout -b docs/improve-something
87
+
88
+ # Make your changes
89
+ # ...
90
+
91
+ # Commit with conventional commits
92
+ pnpm commit
93
+
94
+ # Push and create PR
95
+ git push origin docs/improve-something
96
+ ```
97
+
98
+ ---
99
+
100
+ **Need help?** Open an issue on [GitHub](https://github.com/previousnext/mxds/issues)