snice 2.5.4 → 3.1.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 (323) hide show
  1. package/README.md +501 -882
  2. package/bin/templates/base/src/components/counter-button.ts +13 -26
  3. package/bin/templates/base/src/controllers/counter-controller.ts +3 -3
  4. package/dist/components/accordion/snice-accordion-item.d.ts +4 -5
  5. package/dist/components/accordion/snice-accordion-item.js +37 -39
  6. package/dist/components/accordion/snice-accordion-item.js.map +1 -1
  7. package/dist/components/accordion/snice-accordion.d.ts +5 -11
  8. package/dist/components/accordion/snice-accordion.js +51 -52
  9. package/dist/components/accordion/snice-accordion.js.map +1 -1
  10. package/dist/components/alert/snice-alert.d.ts +2 -6
  11. package/dist/components/alert/snice-alert.js +41 -56
  12. package/dist/components/alert/snice-alert.js.map +1 -1
  13. package/dist/components/avatar/snice-avatar.d.ts +2 -6
  14. package/dist/components/avatar/snice-avatar.js +64 -71
  15. package/dist/components/avatar/snice-avatar.js.map +1 -1
  16. package/dist/components/badge/snice-badge.d.ts +2 -3
  17. package/dist/components/badge/snice-badge.js +22 -23
  18. package/dist/components/badge/snice-badge.js.map +1 -1
  19. package/dist/components/breadcrumbs/snice-breadcrumbs.d.ts +5 -12
  20. package/dist/components/breadcrumbs/snice-breadcrumbs.js +88 -89
  21. package/dist/components/breadcrumbs/snice-breadcrumbs.js.map +1 -1
  22. package/dist/components/button/snice-button.d.ts +3 -7
  23. package/dist/components/button/snice-button.js +37 -58
  24. package/dist/components/button/snice-button.js.map +1 -1
  25. package/dist/components/card/snice-card.d.ts +5 -8
  26. package/dist/components/card/snice-card.js +71 -56
  27. package/dist/components/card/snice-card.js.map +1 -1
  28. package/dist/components/checkbox/snice-checkbox.d.ts +4 -13
  29. package/dist/components/checkbox/snice-checkbox.js +66 -137
  30. package/dist/components/checkbox/snice-checkbox.js.map +1 -1
  31. package/dist/components/chip/snice-chip.d.ts +5 -11
  32. package/dist/components/chip/snice-chip.js +44 -47
  33. package/dist/components/chip/snice-chip.js.map +1 -1
  34. package/dist/components/date-picker/snice-date-picker.d.ts +11 -11
  35. package/dist/components/date-picker/snice-date-picker.js +134 -133
  36. package/dist/components/date-picker/snice-date-picker.js.map +1 -1
  37. package/dist/components/divider/snice-divider.d.ts +2 -4
  38. package/dist/components/divider/snice-divider.js +14 -22
  39. package/dist/components/divider/snice-divider.js.map +1 -1
  40. package/dist/components/drawer/snice-drawer.d.ts +4 -4
  41. package/dist/components/drawer/snice-drawer.js +25 -19
  42. package/dist/components/drawer/snice-drawer.js.map +1 -1
  43. package/dist/components/input/snice-input.d.ts +8 -6
  44. package/dist/components/input/snice-input.js +122 -105
  45. package/dist/components/input/snice-input.js.map +1 -1
  46. package/dist/components/layout/snice-layout-blog.d.ts +4 -4
  47. package/dist/components/layout/snice-layout-blog.js +21 -19
  48. package/dist/components/layout/snice-layout-blog.js.map +1 -1
  49. package/dist/components/layout/snice-layout-card.d.ts +2 -2
  50. package/dist/components/layout/snice-layout-card.js +16 -9
  51. package/dist/components/layout/snice-layout-card.js.map +1 -1
  52. package/dist/components/layout/snice-layout-centered.d.ts +2 -2
  53. package/dist/components/layout/snice-layout-centered.js +14 -7
  54. package/dist/components/layout/snice-layout-centered.js.map +1 -1
  55. package/dist/components/layout/snice-layout-dashboard.d.ts +5 -5
  56. package/dist/components/layout/snice-layout-dashboard.js +38 -30
  57. package/dist/components/layout/snice-layout-dashboard.js.map +1 -1
  58. package/dist/components/layout/snice-layout-fullscreen.d.ts +2 -2
  59. package/dist/components/layout/snice-layout-fullscreen.js +17 -10
  60. package/dist/components/layout/snice-layout-fullscreen.js.map +1 -1
  61. package/dist/components/layout/snice-layout-landing.d.ts +4 -4
  62. package/dist/components/layout/snice-layout-landing.js +21 -19
  63. package/dist/components/layout/snice-layout-landing.js.map +1 -1
  64. package/dist/components/layout/snice-layout-minimal.d.ts +2 -2
  65. package/dist/components/layout/snice-layout-minimal.js +17 -6
  66. package/dist/components/layout/snice-layout-minimal.js.map +1 -1
  67. package/dist/components/layout/snice-layout-sidebar.d.ts +5 -4
  68. package/dist/components/layout/snice-layout-sidebar.js +42 -20
  69. package/dist/components/layout/snice-layout-sidebar.js.map +1 -1
  70. package/dist/components/layout/snice-layout-split.d.ts +2 -2
  71. package/dist/components/layout/snice-layout-split.js +14 -7
  72. package/dist/components/layout/snice-layout-split.js.map +1 -1
  73. package/dist/components/layout/snice-layout.d.ts +4 -4
  74. package/dist/components/layout/snice-layout.js +16 -10
  75. package/dist/components/layout/snice-layout.js.map +1 -1
  76. package/dist/components/login/snice-login.d.ts +6 -11
  77. package/dist/components/login/snice-login.js +97 -71
  78. package/dist/components/login/snice-login.js.map +1 -1
  79. package/dist/components/modal/snice-modal.d.ts +5 -9
  80. package/dist/components/modal/snice-modal.js +47 -78
  81. package/dist/components/modal/snice-modal.js.map +1 -1
  82. package/dist/components/nav/snice-nav.d.ts +13 -7
  83. package/dist/components/nav/snice-nav.js +191 -100
  84. package/dist/components/nav/snice-nav.js.map +1 -1
  85. package/dist/components/nav/snice-nav.types.d.ts +3 -3
  86. package/dist/components/pagination/snice-pagination.d.ts +6 -7
  87. package/dist/components/pagination/snice-pagination.js +94 -81
  88. package/dist/components/pagination/snice-pagination.js.map +1 -1
  89. package/dist/components/progress/snice-progress.d.ts +2 -7
  90. package/dist/components/progress/snice-progress.js +41 -98
  91. package/dist/components/progress/snice-progress.js.map +1 -1
  92. package/dist/components/radio/snice-radio.d.ts +4 -4
  93. package/dist/components/radio/snice-radio.js +52 -44
  94. package/dist/components/radio/snice-radio.js.map +1 -1
  95. package/dist/components/select/snice-option.d.ts +2 -1
  96. package/dist/components/select/snice-option.js +12 -5
  97. package/dist/components/select/snice-option.js.map +1 -1
  98. package/dist/components/select/snice-select.d.ts +9 -21
  99. package/dist/components/select/snice-select.js +98 -170
  100. package/dist/components/select/snice-select.js.map +1 -1
  101. package/dist/components/skeleton/snice-skeleton.d.ts +2 -6
  102. package/dist/components/skeleton/snice-skeleton.js +18 -49
  103. package/dist/components/skeleton/snice-skeleton.js.map +1 -1
  104. package/dist/components/snice-cell-BLFVdxPp.js +4 -0
  105. package/dist/components/snice-cell-BLFVdxPp.js.map +1 -0
  106. package/dist/components/switch/snice-switch.d.ts +2 -2
  107. package/dist/components/switch/snice-switch.js +38 -26
  108. package/dist/components/switch/snice-switch.js.map +1 -1
  109. package/dist/components/table/snice-cell-actions.d.ts +24 -0
  110. package/dist/components/table/snice-cell-actions.js +149 -0
  111. package/dist/components/table/snice-cell-actions.js.map +1 -0
  112. package/dist/components/table/snice-cell-boolean.d.ts +2 -2
  113. package/dist/components/table/snice-cell-boolean.js +13 -7
  114. package/dist/components/table/snice-cell-boolean.js.map +1 -1
  115. package/dist/components/table/snice-cell-color.d.ts +18 -0
  116. package/dist/components/table/snice-cell-color.js +149 -0
  117. package/dist/components/table/snice-cell-color.js.map +1 -0
  118. package/dist/components/table/snice-cell-currency.d.ts +24 -0
  119. package/dist/components/table/snice-cell-currency.js +235 -0
  120. package/dist/components/table/snice-cell-currency.js.map +1 -0
  121. package/dist/components/table/snice-cell-date.d.ts +2 -2
  122. package/dist/components/table/snice-cell-date.js +14 -8
  123. package/dist/components/table/snice-cell-date.js.map +1 -1
  124. package/dist/components/table/snice-cell-duration.d.ts +2 -2
  125. package/dist/components/table/snice-cell-duration.js +12 -6
  126. package/dist/components/table/snice-cell-duration.js.map +1 -1
  127. package/dist/components/table/snice-cell-email.d.ts +15 -0
  128. package/dist/components/table/snice-cell-email.js +125 -0
  129. package/dist/components/table/snice-cell-email.js.map +1 -0
  130. package/dist/components/table/snice-cell-filesize.d.ts +2 -2
  131. package/dist/components/table/snice-cell-filesize.js +12 -6
  132. package/dist/components/table/snice-cell-filesize.js.map +1 -1
  133. package/dist/components/table/snice-cell-image.d.ts +20 -0
  134. package/dist/components/table/snice-cell-image.js +162 -0
  135. package/dist/components/table/snice-cell-image.js.map +1 -0
  136. package/dist/components/table/snice-cell-json.d.ts +20 -0
  137. package/dist/components/table/snice-cell-json.js +186 -0
  138. package/dist/components/table/snice-cell-json.js.map +1 -0
  139. package/dist/components/table/snice-cell-link.d.ts +17 -0
  140. package/dist/components/table/snice-cell-link.js +142 -0
  141. package/dist/components/table/snice-cell-link.js.map +1 -0
  142. package/dist/components/table/snice-cell-location.d.ts +19 -0
  143. package/dist/components/table/snice-cell-location.js +185 -0
  144. package/dist/components/table/snice-cell-location.js.map +1 -0
  145. package/dist/components/table/snice-cell-number.d.ts +2 -2
  146. package/dist/components/table/snice-cell-number.js +12 -6
  147. package/dist/components/table/snice-cell-number.js.map +1 -1
  148. package/dist/components/table/snice-cell-percentage.d.ts +22 -0
  149. package/dist/components/table/snice-cell-percentage.js +208 -0
  150. package/dist/components/table/snice-cell-percentage.js.map +1 -0
  151. package/dist/components/table/snice-cell-phone.d.ts +18 -0
  152. package/dist/components/table/snice-cell-phone.js +153 -0
  153. package/dist/components/table/snice-cell-phone.js.map +1 -0
  154. package/dist/components/table/snice-cell-progress.d.ts +2 -2
  155. package/dist/components/table/snice-cell-progress.js +12 -6
  156. package/dist/components/table/snice-cell-progress.js.map +1 -1
  157. package/dist/components/table/snice-cell-rating.d.ts +2 -2
  158. package/dist/components/table/snice-cell-rating.js +12 -6
  159. package/dist/components/table/snice-cell-rating.js.map +1 -1
  160. package/dist/components/table/snice-cell-sparkline.d.ts +2 -2
  161. package/dist/components/table/snice-cell-sparkline.js +13 -7
  162. package/dist/components/table/snice-cell-sparkline.js.map +1 -1
  163. package/dist/components/table/snice-cell-status.d.ts +17 -0
  164. package/dist/components/table/snice-cell-status.js +144 -0
  165. package/dist/components/table/snice-cell-status.js.map +1 -0
  166. package/dist/components/table/snice-cell-tag.d.ts +16 -0
  167. package/dist/components/table/snice-cell-tag.js +131 -0
  168. package/dist/components/table/snice-cell-tag.js.map +1 -0
  169. package/dist/components/table/snice-cell-text.d.ts +2 -2
  170. package/dist/components/table/snice-cell-text.js +14 -8
  171. package/dist/components/table/snice-cell-text.js.map +1 -1
  172. package/dist/components/table/snice-cell.d.ts +2 -2
  173. package/dist/components/table/snice-cell.js +12 -6
  174. package/dist/components/table/snice-cell.js.map +1 -1
  175. package/dist/components/table/snice-column.d.ts +1 -1
  176. package/dist/components/table/snice-column.js +6 -3
  177. package/dist/components/table/snice-column.js.map +1 -1
  178. package/dist/components/table/snice-header.d.ts +5 -5
  179. package/dist/components/table/snice-header.js +60 -50
  180. package/dist/components/table/snice-header.js.map +1 -1
  181. package/dist/components/table/snice-progress.d.ts +2 -2
  182. package/dist/components/table/snice-progress.js +18 -11
  183. package/dist/components/table/snice-progress.js.map +1 -1
  184. package/dist/components/table/snice-rating.d.ts +2 -2
  185. package/dist/components/table/snice-rating.js +15 -8
  186. package/dist/components/table/snice-rating.js.map +1 -1
  187. package/dist/components/table/snice-row.d.ts +17 -6
  188. package/dist/components/table/snice-row.js +95 -44
  189. package/dist/components/table/snice-row.js.map +1 -1
  190. package/dist/components/table/snice-table.d.ts +18 -10
  191. package/dist/components/table/snice-table.js +355 -173
  192. package/dist/components/table/snice-table.js.map +1 -1
  193. package/dist/components/table/snice-table.types.d.ts +101 -2
  194. package/dist/components/tabs/snice-tab-panel.d.ts +2 -2
  195. package/dist/components/tabs/snice-tab-panel.js +12 -6
  196. package/dist/components/tabs/snice-tab-panel.js.map +1 -1
  197. package/dist/components/tabs/snice-tab.d.ts +6 -5
  198. package/dist/components/tabs/snice-tab.js +36 -19
  199. package/dist/components/tabs/snice-tab.js.map +1 -1
  200. package/dist/components/tabs/snice-tabs.d.ts +5 -5
  201. package/dist/components/tabs/snice-tabs.js +38 -28
  202. package/dist/components/tabs/snice-tabs.js.map +1 -1
  203. package/dist/components/toast/snice-toast-container.d.ts +7 -7
  204. package/dist/components/toast/snice-toast-container.js +19 -12
  205. package/dist/components/toast/snice-toast-container.js.map +1 -1
  206. package/dist/components/toast/snice-toast.d.ts +3 -15
  207. package/dist/components/toast/snice-toast.js +49 -108
  208. package/dist/components/toast/snice-toast.js.map +1 -1
  209. package/dist/components/tooltip/snice-tooltip.d.ts +2 -2
  210. package/dist/components/tooltip/snice-tooltip.js +14 -7
  211. package/dist/components/tooltip/snice-tooltip.js.map +1 -1
  212. package/dist/context.d.ts +44 -0
  213. package/dist/element-ready.d.ts +40 -0
  214. package/dist/{types/element.d.ts → element.d.ts} +2 -8
  215. package/dist/{types/events.d.ts → events.d.ts} +0 -4
  216. package/dist/index.cjs +2589 -605
  217. package/dist/index.cjs.map +1 -1
  218. package/dist/index.d.ts +21 -0
  219. package/dist/index.esm.js +2568 -604
  220. package/dist/index.esm.js.map +1 -1
  221. package/dist/index.iife.js +2589 -605
  222. package/dist/index.iife.js.map +1 -1
  223. package/dist/method-decorators.d.ts +121 -0
  224. package/dist/on.d.ts +59 -0
  225. package/dist/parts.d.ts +159 -0
  226. package/dist/render-debug.d.ts +27 -0
  227. package/dist/render-tracker.d.ts +14 -0
  228. package/dist/render.d.ts +96 -0
  229. package/dist/symbols.cjs +163 -0
  230. package/dist/symbols.cjs.map +1 -1
  231. package/dist/{types/symbols.d.ts → symbols.d.ts} +22 -0
  232. package/dist/symbols.esm.js +27 -3
  233. package/dist/symbols.esm.js.map +1 -1
  234. package/dist/template.d.ts +100 -0
  235. package/dist/transitions.cjs +219 -0
  236. package/dist/transitions.esm.js +2 -2
  237. package/dist/types/context.d.ts +48 -0
  238. package/dist/types/element-options.d.ts +26 -0
  239. package/dist/types/index.d.ts +25 -9
  240. package/dist/types/nav-context.d.ts +19 -0
  241. package/dist/types/{types/on-options.d.ts → on-options.d.ts} +2 -0
  242. package/dist/types/{types/placard.d.ts → placard.d.ts} +0 -1
  243. package/docs/ai/README.md +17 -0
  244. package/docs/ai/api.md +175 -0
  245. package/docs/ai/architecture.md +160 -0
  246. package/docs/ai/components/accordion.md +174 -0
  247. package/docs/ai/components/alert.md +77 -0
  248. package/docs/ai/components/avatar.md +61 -0
  249. package/docs/ai/components/badge.md +69 -0
  250. package/docs/ai/components/breadcrumbs.md +74 -0
  251. package/docs/ai/components/button.md +75 -0
  252. package/docs/ai/components/card.md +61 -0
  253. package/docs/ai/components/checkbox.md +74 -0
  254. package/docs/ai/components/chip.md +73 -0
  255. package/docs/ai/components/date-picker.md +75 -0
  256. package/docs/ai/components/divider.md +66 -0
  257. package/docs/ai/components/drawer.md +80 -0
  258. package/docs/ai/components/input.md +111 -0
  259. package/docs/ai/components/login.md +109 -0
  260. package/docs/ai/components/modal.md +67 -0
  261. package/docs/ai/components/nav.md +76 -0
  262. package/docs/ai/components/pagination.md +55 -0
  263. package/docs/ai/components/progress.md +72 -0
  264. package/docs/ai/components/radio.md +79 -0
  265. package/docs/ai/components/select.md +92 -0
  266. package/docs/ai/components/skeleton.md +57 -0
  267. package/docs/ai/components/switch.md +53 -0
  268. package/docs/ai/components/table.md +227 -0
  269. package/docs/ai/components/tabs.md +83 -0
  270. package/docs/ai/components/toast.md +140 -0
  271. package/docs/ai/components/tooltip.md +146 -0
  272. package/docs/ai/patterns.md +244 -0
  273. package/docs/components/accordion.md +558 -0
  274. package/docs/components/drawer.md +602 -0
  275. package/docs/components/modal.md +558 -0
  276. package/docs/components/nav.md +239 -0
  277. package/docs/components/pagination.md +289 -0
  278. package/docs/components/select.md +599 -0
  279. package/docs/components/switch.md +354 -0
  280. package/docs/components/tabs.md +546 -0
  281. package/docs/components/toast.md +506 -0
  282. package/docs/components/tooltip.md +523 -0
  283. package/docs/controllers.md +744 -0
  284. package/docs/elements.md +855 -0
  285. package/docs/events.md +807 -0
  286. package/docs/migration-v2-to-v3.md +569 -0
  287. package/docs/observe.md +588 -0
  288. package/docs/placards.md +401 -0
  289. package/docs/request-response.md +852 -0
  290. package/docs/routing.md +1186 -0
  291. package/package.json +10 -11
  292. package/dist/components/snice-cell-C9N6yGxQ.js +0 -4
  293. package/dist/components/snice-cell-C9N6yGxQ.js.map +0 -1
  294. package/dist/types/types/index.d.ts +0 -23
  295. /package/dist/{types/controller.d.ts → controller.d.ts} +0 -0
  296. /package/dist/{types/global.d.ts → global.d.ts} +0 -0
  297. /package/dist/{types/observe.d.ts → observe.d.ts} +0 -0
  298. /package/dist/{types/request-response.d.ts → request-response.d.ts} +0 -0
  299. /package/dist/{types/router.d.ts → router.d.ts} +0 -0
  300. /package/dist/{types/testing.d.ts → testing.d.ts} +0 -0
  301. /package/dist/{types/transitions.d.ts → transitions.d.ts} +0 -0
  302. /package/dist/types/{types/adopted-options.d.ts → adopted-options.d.ts} +0 -0
  303. /package/dist/types/{types/app-context.d.ts → app-context.d.ts} +0 -0
  304. /package/dist/types/{types/dispatch-options.d.ts → dispatch-options.d.ts} +0 -0
  305. /package/dist/types/{types/guard.d.ts → guard.d.ts} +0 -0
  306. /package/dist/types/{types/i-controller.d.ts → i-controller.d.ts} +0 -0
  307. /package/dist/types/{types/moved-options.d.ts → moved-options.d.ts} +0 -0
  308. /package/dist/types/{types/observe-options.d.ts → observe-options.d.ts} +0 -0
  309. /package/dist/types/{types/page-options.d.ts → page-options.d.ts} +0 -0
  310. /package/dist/types/{types/part-options.d.ts → part-options.d.ts} +0 -0
  311. /package/dist/types/{types/property-converter.d.ts → property-converter.d.ts} +0 -0
  312. /package/dist/types/{types/property-options.d.ts → property-options.d.ts} +0 -0
  313. /package/dist/types/{types/query-options.d.ts → query-options.d.ts} +0 -0
  314. /package/dist/types/{types/request-options.d.ts → request-options.d.ts} +0 -0
  315. /package/dist/types/{types/respond-options.d.ts → respond-options.d.ts} +0 -0
  316. /package/dist/types/{types/route-params.d.ts → route-params.d.ts} +0 -0
  317. /package/dist/types/{types/router-instance.d.ts → router-instance.d.ts} +0 -0
  318. /package/dist/types/{types/router-options.d.ts → router-options.d.ts} +0 -0
  319. /package/dist/types/{types/simple-array.d.ts → simple-array.d.ts} +0 -0
  320. /package/dist/types/{types/snice-element.d.ts → snice-element.d.ts} +0 -0
  321. /package/dist/types/{types/snice-global.d.ts → snice-global.d.ts} +0 -0
  322. /package/dist/types/{types/transition.d.ts → transition.d.ts} +0 -0
  323. /package/dist/{types/utils.d.ts → utils.d.ts} +0 -0
@@ -0,0 +1,588 @@
1
+ # Observe API Documentation
2
+
3
+ The `@observe` decorator provides lifecycle-managed observation of external changes like viewport intersection, element resize, media queries, and DOM mutations.
4
+
5
+ ## Table of Contents
6
+ - [Overview](#overview)
7
+ - [Intersection Observer](#intersection-observer)
8
+ - [Resize Observer](#resize-observer)
9
+ - [Media Query Observer](#media-query-observer)
10
+ - [Mutation Observer](#mutation-observer)
11
+ - [Using with Controllers](#using-with-controllers)
12
+ - [Options](#options)
13
+ - [Best Practices](#best-practices)
14
+
15
+ ## Overview
16
+
17
+ The `@observe` decorator automatically manages browser observers with proper cleanup, preventing memory leaks and simplifying complex observation patterns.
18
+
19
+ ```typescript
20
+ import { element, observe, render, html } from 'snice';
21
+
22
+ @element('lazy-image')
23
+ class LazyImage extends HTMLElement {
24
+ @observe('intersection', 'img')
25
+ loadImage(entry: IntersectionObserverEntry) {
26
+ if (entry.isIntersecting) {
27
+ const img = entry.target as HTMLImageElement;
28
+ img.src = img.dataset.src!;
29
+ return false; // Stop observing this element
30
+ }
31
+ }
32
+
33
+ @render()
34
+ renderContent() {
35
+ return html`<img data-src="image.jpg" />`;
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### Array Syntax
41
+
42
+ You can observe multiple types with a single handler using array syntax:
43
+
44
+ ```typescript
45
+ @element('dynamic-content')
46
+ class DynamicContent extends HTMLElement {
47
+ @render()
48
+ renderContent() {
49
+ return html`<div class="content" data-state="initial">Content</div>`;
50
+ }
51
+
52
+ // Watch for both child changes and attribute changes
53
+ @observe(['mutation:childList', 'mutation:attributes'], '.content')
54
+ handleContentChange(mutations: MutationRecord[]) {
55
+ mutations.forEach(m => {
56
+ if (m.type === 'childList') {
57
+ console.log('Children changed');
58
+ } else if (m.type === 'attributes') {
59
+ console.log('Attributes changed');
60
+ }
61
+ });
62
+ }
63
+
64
+ // Multiple media queries with one handler
65
+ @observe(['media:(max-width: 768px)', 'media:(prefers-color-scheme: dark)'])
66
+ handleResponsiveTheme(matches: boolean) {
67
+ // Called for each media query independently
68
+ this.updateLayout();
69
+ }
70
+
71
+ updateLayout() {
72
+ // Update logic
73
+ }
74
+ }
75
+ ```
76
+
77
+ ## Intersection Observer
78
+
79
+ Detect when elements enter or leave the viewport. Perfect for lazy loading, infinite scroll, and animations.
80
+
81
+ ### Basic Usage
82
+
83
+ ```typescript
84
+ @element('scroll-trigger')
85
+ class ScrollTrigger extends HTMLElement {
86
+ @render()
87
+ renderContent() {
88
+ return html`
89
+ <div class="content">Scroll to see me</div>
90
+ <img class="lazy" data-src="image.jpg" />
91
+ `;
92
+ }
93
+
94
+ // Observe when element becomes visible
95
+ @observe('intersection')
96
+ handleVisible(entry: IntersectionObserverEntry) {
97
+ if (entry.isIntersecting) {
98
+ this.classList.add('visible');
99
+ }
100
+ }
101
+
102
+ // Observe specific element with threshold
103
+ @observe('intersection', '.lazy', { threshold: 0.1 })
104
+ loadImage(entry: IntersectionObserverEntry) {
105
+ if (entry.isIntersecting) {
106
+ const img = entry.target as HTMLImageElement;
107
+ img.src = img.dataset.src!;
108
+ return false; // Stop observing after loading
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### Options
115
+
116
+ - `threshold`: Number or array (0-1) defining visibility percentage to trigger
117
+ - `rootMargin`: Margin around root to expand/shrink observation area
118
+ - `root`: Element to use as viewport (defaults to browser viewport)
119
+
120
+ ### Stopping Observation
121
+
122
+ Return `false` from the handler to stop observing that specific element:
123
+
124
+ ```typescript
125
+ @observe('intersection', '.item')
126
+ handleItemVisible(entry: IntersectionObserverEntry) {
127
+ if (entry.isIntersecting) {
128
+ this.animateItem(entry.target);
129
+ return false; // Don't observe this item anymore
130
+ }
131
+ }
132
+
133
+ animateItem(target: Element) {
134
+ // Animation logic
135
+ }
136
+ ```
137
+
138
+ ## Resize Observer
139
+
140
+ Monitor element size changes for responsive components.
141
+
142
+ ### Basic Usage
143
+
144
+ ```typescript
145
+ @element('responsive-chart')
146
+ class ResponsiveChart extends HTMLElement {
147
+ @render()
148
+ renderContent() {
149
+ return html`<canvas class="chart"></canvas>`;
150
+ }
151
+
152
+ // Observe host element resize
153
+ @observe('resize')
154
+ handleResize(entry: ResizeObserverEntry) {
155
+ const { width, height } = entry.contentRect;
156
+ this.redrawChart(width, height);
157
+ }
158
+
159
+ // Observe specific element with throttling
160
+ @observe('resize', '.chart', { throttle: 100 })
161
+ handleChartResize(entry: ResizeObserverEntry) {
162
+ this.updateChartDimensions(entry.contentRect);
163
+ }
164
+
165
+ redrawChart(width: number, height: number) {
166
+ // Chart redraw logic
167
+ }
168
+
169
+ updateChartDimensions(rect: DOMRectReadOnly) {
170
+ // Update logic
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### Options
176
+
177
+ - `box`: `'content-box'` or `'border-box'` (which box model to observe)
178
+ - `throttle`: Milliseconds to throttle resize callbacks
179
+
180
+ ## Media Query Observer
181
+
182
+ Respond to viewport and user preference changes.
183
+
184
+ ### Basic Usage
185
+
186
+ ```typescript
187
+ @element('responsive-layout')
188
+ class ResponsiveLayout extends HTMLElement {
189
+ isDesktop = false;
190
+ isDarkMode = false;
191
+
192
+ @render()
193
+ renderContent() {
194
+ return html`<div class="layout">Content</div>`;
195
+ }
196
+
197
+ // Desktop breakpoint
198
+ @observe('media:(min-width: 768px)')
199
+ handleDesktop(matches: boolean) {
200
+ this.isDesktop = matches;
201
+ this.updateLayout();
202
+ }
203
+
204
+ // Dark mode preference
205
+ @observe('media:(prefers-color-scheme: dark)')
206
+ handleDarkMode(matches: boolean) {
207
+ this.isDarkMode = matches;
208
+ this.updateTheme();
209
+ }
210
+
211
+ // Portrait orientation on mobile
212
+ @observe('media:(orientation: portrait) and (max-width: 768px)')
213
+ handleMobilePortrait(matches: boolean) {
214
+ if (matches) {
215
+ this.adjustForMobilePortrait();
216
+ }
217
+ }
218
+
219
+ updateLayout() {
220
+ // Layout update logic
221
+ }
222
+
223
+ updateTheme() {
224
+ // Theme update logic
225
+ }
226
+
227
+ adjustForMobilePortrait() {
228
+ // Adjustment logic
229
+ }
230
+ }
231
+ ```
232
+
233
+ ### Important Notes
234
+
235
+ - Handler is called immediately with current state when observer is set up
236
+ - Media query strings use standard CSS media query syntax
237
+ - Media queries are cached globally for efficiency
238
+
239
+ ## Mutation Observer
240
+
241
+ Watch for DOM changes like added/removed nodes or attribute modifications.
242
+
243
+ ### Basic Usage
244
+
245
+ ```typescript
246
+ @element('dynamic-list')
247
+ class DynamicList extends HTMLElement {
248
+ @render()
249
+ renderContent() {
250
+ return html`
251
+ <ul class="list"></ul>
252
+ <div class="count">0 items</div>
253
+ `;
254
+ }
255
+
256
+ // Watch for child list changes
257
+ @observe('mutation:childList', '.list')
258
+ handleListChange(mutations: MutationRecord[]) {
259
+ const count = this.querySelector('.list')?.children.length || 0;
260
+ this.updateCount(count);
261
+ }
262
+
263
+ // Watch for specific attribute changes
264
+ @observe('mutation:attributes:data-state', '.item')
265
+ handleStateChange(mutations: MutationRecord[]) {
266
+ const mutation = mutations[0];
267
+ const newState = (mutation.target as Element).getAttribute('data-state');
268
+ this.updateItemDisplay(mutation.target, newState);
269
+ }
270
+
271
+ updateCount(count: number) {
272
+ const countDiv = this.querySelector('.count');
273
+ if (countDiv) countDiv.textContent = `${count} items`;
274
+ }
275
+
276
+ updateItemDisplay(target: Node, newState: string | null) {
277
+ // Update display logic
278
+ }
279
+ }
280
+ ```
281
+
282
+ ### Mutation Types
283
+
284
+ - `mutation:childList` - Observe added/removed child nodes
285
+ - `mutation:attributes` - Observe all attribute changes
286
+ - `mutation:attributes:name` - Observe specific attribute changes
287
+
288
+ ### Options
289
+
290
+ - `subtree`: Also observe descendants (use with caution for performance)
291
+ - `throttle`: Milliseconds to throttle mutation callbacks
292
+
293
+ ### Safety Features
294
+
295
+ - `subtree: true` is not enabled by default to prevent performance issues
296
+ - Character data mutations are not supported (too granular)
297
+ - Always be specific about what you're observing
298
+
299
+ ## Using with Controllers
300
+
301
+ Controllers can also use `@observe` for separation of concerns. When used in controllers, observers operate on the attached element:
302
+
303
+ ```typescript
304
+ @controller('viewport-controller')
305
+ class ViewportController implements IController {
306
+ element: HTMLElement | null = null;
307
+
308
+ @observe('media:(min-width: 1024px)')
309
+ handleLargeScreen(matches: boolean) {
310
+ if (this.element) {
311
+ this.element.classList.toggle('large-screen', matches);
312
+ }
313
+ }
314
+
315
+ @observe('intersection', { threshold: 0.5 })
316
+ handleVisibility(entry: IntersectionObserverEntry) {
317
+ if (entry.isIntersecting) {
318
+ this.trackImpression();
319
+ }
320
+ }
321
+
322
+ async attach(element: HTMLElement) {
323
+ this.element = element;
324
+ }
325
+
326
+ async detach(element: HTMLElement) {
327
+ this.element = null;
328
+ // Observers are automatically cleaned up
329
+ }
330
+
331
+ trackImpression() {
332
+ // Analytics tracking
333
+ }
334
+ }
335
+ ```
336
+
337
+ ## Options
338
+
339
+ ### Common Options
340
+
341
+ All observers support these options:
342
+
343
+ ```typescript
344
+ interface ObserveOptions {
345
+ throttle?: number; // Throttle callbacks by milliseconds
346
+ }
347
+ ```
348
+
349
+ ### Specific Options
350
+
351
+ ```typescript
352
+ // Intersection Observer
353
+ interface IntersectionOptions extends ObserveOptions {
354
+ threshold?: number | number[]; // 0-1 visibility threshold
355
+ rootMargin?: string; // Margin around root
356
+ root?: Element | null; // Viewport element
357
+ }
358
+
359
+ // Resize Observer
360
+ interface ResizeOptions extends ObserveOptions {
361
+ box?: 'content-box' | 'border-box'; // Box model to observe
362
+ }
363
+
364
+ // Mutation Observer
365
+ interface MutationOptions extends ObserveOptions {
366
+ subtree?: boolean; // Observe descendants
367
+ maxDepth?: number; // Limit subtree depth
368
+ }
369
+ ```
370
+
371
+ ## Best Practices
372
+
373
+ ### 1. Be Specific
374
+
375
+ ```typescript
376
+ // Good - specific selector and threshold
377
+ @observe('intersection', '.lazy-image', { threshold: 0.1 })
378
+
379
+ // Bad - observing everything
380
+ @observe('intersection', '*')
381
+ ```
382
+
383
+ ### 2. Use Throttling for High-Frequency Events
384
+
385
+ ```typescript
386
+ // Good - throttled resize observer
387
+ @observe('resize', { throttle: 100 })
388
+ handleResize(entry: ResizeObserverEntry) {
389
+ this.expensiveOperation();
390
+ }
391
+
392
+ // Bad - unthrottled resize can fire many times per second
393
+ @observe('resize')
394
+ handleResize(entry: ResizeObserverEntry) {
395
+ this.expensiveOperation();
396
+ }
397
+
398
+ expensiveOperation() {
399
+ // Expensive logic
400
+ }
401
+ ```
402
+
403
+ ### 3. Stop Observing When Done
404
+
405
+ ```typescript
406
+ @observe('intersection', '.load-more')
407
+ handleLoadMore(entry: IntersectionObserverEntry) {
408
+ if (entry.isIntersecting) {
409
+ this.loadMoreContent();
410
+ return false; // Stop observing after trigger
411
+ }
412
+ }
413
+
414
+ loadMoreContent() {
415
+ // Load more logic
416
+ }
417
+ ```
418
+
419
+ ### 4. Avoid Deep Subtree Observation
420
+
421
+ ```typescript
422
+ // Bad - observing entire subtree
423
+ @observe('mutation:childList', { subtree: true })
424
+
425
+ // Good - observe specific container
426
+ @observe('mutation:childList', '.list-container')
427
+ ```
428
+
429
+ ### 5. Use Media Queries for Responsive Design
430
+
431
+ ```typescript
432
+ // Good - declarative responsive behavior
433
+ @observe('media:(min-width: 768px)')
434
+ handleDesktop(matches: boolean) {
435
+ this.layout = matches ? 'desktop' : 'mobile';
436
+ }
437
+
438
+ // Avoid - manual window resize listening
439
+ // window.addEventListener('resize', () => {
440
+ // if (window.innerWidth >= 768) { /* ... */ }
441
+ // });
442
+ ```
443
+
444
+ ## Lifecycle and Cleanup
445
+
446
+ All observers are automatically:
447
+ - Set up when element connects to DOM
448
+ - Cleaned up when element disconnects
449
+ - Re-established if element is moved in DOM
450
+
451
+ No manual cleanup is required - the framework handles everything:
452
+
453
+ ```typescript
454
+ @element('auto-cleanup')
455
+ class AutoCleanup extends HTMLElement {
456
+ // All observers are automatically managed
457
+ @observe('intersection')
458
+ handleIntersection(entry: IntersectionObserverEntry) { }
459
+
460
+ @observe('resize')
461
+ handleResize(entry: ResizeObserverEntry) { }
462
+
463
+ @observe('media:(min-width: 768px)')
464
+ handleMedia(matches: boolean) { }
465
+
466
+ // No cleanup code needed!
467
+ @render()
468
+ renderContent() {
469
+ return html`<div>Auto-cleanup content</div>`;
470
+ }
471
+ }
472
+ ```
473
+
474
+ ## Performance Considerations
475
+
476
+ 1. **Browser Support**: Observers check for API availability and warn if unsupported
477
+ 2. **Shared Observers**: Media queries are cached globally
478
+ 3. **Automatic Throttling**: Built-in throttle option prevents callback flooding
479
+ 4. **Memory Management**: Proper cleanup prevents memory leaks
480
+ 5. **Error Isolation**: Errors in one observer don't affect others
481
+
482
+ ## Examples
483
+
484
+ ### Virtual Scrolling
485
+
486
+ ```typescript
487
+ @element('virtual-list')
488
+ class VirtualList extends HTMLElement {
489
+ visibleItems = new Set<Element>();
490
+
491
+ @render()
492
+ renderContent() {
493
+ return html`<div class="viewport"></div>`;
494
+ }
495
+
496
+ @observe('intersection', '.item', { rootMargin: '100px' })
497
+ handleItemVisibility(entry: IntersectionObserverEntry) {
498
+ if (entry.isIntersecting) {
499
+ this.visibleItems.add(entry.target);
500
+ this.renderItem(entry.target);
501
+ } else {
502
+ this.visibleItems.delete(entry.target);
503
+ this.unrenderItem(entry.target);
504
+ }
505
+ }
506
+
507
+ renderItem(target: Element) {
508
+ // Render logic
509
+ }
510
+
511
+ unrenderItem(target: Element) {
512
+ // Unrender logic
513
+ }
514
+ }
515
+ ```
516
+
517
+ ### Responsive Dashboard
518
+
519
+ ```typescript
520
+ @element('dashboard')
521
+ class Dashboard extends HTMLElement {
522
+ columns = 1;
523
+
524
+ @observe('media:(min-width: 768px)')
525
+ handleTablet(matches: boolean) {
526
+ this.columns = matches ? 2 : 1;
527
+ }
528
+
529
+ @observe('media:(min-width: 1024px)')
530
+ handleDesktop(matches: boolean) {
531
+ this.columns = matches ? 3 : this.columns;
532
+ }
533
+
534
+ @observe('resize', { throttle: 200 })
535
+ handleResize(entry: ResizeObserverEntry) {
536
+ this.adjustCardSizes(entry.contentRect.width);
537
+ }
538
+
539
+ @render()
540
+ renderContent() {
541
+ return html`<div class="dashboard">Dashboard with ${this.columns} columns</div>`;
542
+ }
543
+
544
+ adjustCardSizes(width: number) {
545
+ // Adjust logic
546
+ }
547
+ }
548
+ ```
549
+
550
+ ### Form Auto-Save
551
+
552
+ ```typescript
553
+ @element('auto-save-form')
554
+ class AutoSaveForm extends HTMLElement {
555
+ @observe('mutation:attributes:value', 'input, textarea', { throttle: 1000 })
556
+ handleInputChange(mutations: MutationRecord[]) {
557
+ this.saveFormData();
558
+ }
559
+
560
+ @observe('mutation:childList', '.dynamic-fields')
561
+ handleFieldsAdded(mutations: MutationRecord[]) {
562
+ mutations.forEach(mutation => {
563
+ mutation.addedNodes.forEach(node => {
564
+ if (node.nodeType === Node.ELEMENT_NODE) {
565
+ this.initializeField(node as Element);
566
+ }
567
+ });
568
+ });
569
+ }
570
+
571
+ @render()
572
+ renderContent() {
573
+ return html`
574
+ <form>
575
+ <div class="dynamic-fields"></div>
576
+ </form>
577
+ `;
578
+ }
579
+
580
+ saveFormData() {
581
+ // Save logic
582
+ }
583
+
584
+ initializeField(field: Element) {
585
+ // Initialize logic
586
+ }
587
+ }
588
+ ```