react-motion-gallery 2.0.78 → 2.0.80

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 (190) hide show
  1. package/README.md +215 -267
  2. package/dist/FullscreenRuntime-55I74D6Z.mjs +4 -0
  3. package/dist/{FullscreenRuntime-YZWI2HL2.css → FullscreenRuntime-TOJOJ3CB.css} +1 -1
  4. package/dist/chunk-25UYR7AK.mjs +1 -0
  5. package/dist/{chunk-HVO7H4XB.mjs → chunk-35GYJ57E.mjs} +3 -3
  6. package/dist/{chunk-GWYW2V72.mjs → chunk-3M5A7QO3.mjs} +1 -1
  7. package/dist/{chunk-RA7HIQFJ.mjs → chunk-3V4NPPY7.mjs} +1 -1
  8. package/dist/chunk-4HJVZ352.mjs +1 -0
  9. package/dist/{chunk-A2YEDYJQ.mjs → chunk-52Y5TS67.mjs} +1 -1
  10. package/dist/{chunk-GKR35A24.mjs → chunk-5KMBTPP7.mjs} +1 -1
  11. package/dist/chunk-6H2MRVWI.mjs +1 -0
  12. package/dist/{chunk-3YNFTUKE.mjs → chunk-AQ4TBMBW.mjs} +2 -2
  13. package/dist/chunk-BNL3ZZSS.mjs +1 -0
  14. package/dist/chunk-CBUWCUBQ.mjs +2 -0
  15. package/dist/chunk-DZKLZ4W2.mjs +1 -0
  16. package/dist/{chunk-NE64XTCU.mjs → chunk-FQYIUZ26.mjs} +1 -1
  17. package/dist/{chunk-LZ53EONL.mjs → chunk-GVFJ7NV6.mjs} +1 -1
  18. package/dist/chunk-IWFBL2G3.mjs +1 -0
  19. package/dist/{chunk-ZCVS5WHL.mjs → chunk-JNBSBGV6.mjs} +1 -1
  20. package/dist/{chunk-AEGJQGLQ.mjs → chunk-OGMYWDWR.mjs} +3 -3
  21. package/dist/{chunk-3HVF63UG.mjs → chunk-Q3BOHEVQ.mjs} +1 -1
  22. package/dist/chunk-SYABTARL.mjs +4 -0
  23. package/dist/chunk-VJK7JOIV.mjs +1 -0
  24. package/dist/chunk-WNAGZVDW.mjs +4 -0
  25. package/dist/chunk-YJ7RDT3C.mjs +1 -0
  26. package/dist/chunk-Z3JUCPSU.mjs +2 -0
  27. package/dist/{chunk-B73WDXEU.mjs → chunk-Z5U5O4B6.mjs} +1 -1
  28. package/dist/chunk-ZDX6GV52.mjs +1 -0
  29. package/dist/core.d.mts +1 -1
  30. package/dist/{dataPlugins-DzaWlM6f.d.mts → dataPlugins-C91mlyu6.d.mts} +1 -1
  31. package/dist/entries-infinite-scroll.d.mts +1 -1
  32. package/dist/entries-load-more.d.mts +1 -1
  33. package/dist/entries-media-grid.css +1 -0
  34. package/dist/entries-media-grid.d.mts +32 -0
  35. package/dist/entries-media-grid.mjs +1 -0
  36. package/dist/entries-media-masonry.css +1 -0
  37. package/dist/entries-media-masonry.d.mts +65 -0
  38. package/dist/entries-media-masonry.mjs +1 -0
  39. package/dist/entries-media-slider.css +1 -0
  40. package/dist/entries-media-slider.d.mts +27 -0
  41. package/dist/entries-media-slider.mjs +1 -0
  42. package/dist/entries-pagination.d.mts +2 -2
  43. package/dist/entries-pagination.mjs +1 -1
  44. package/dist/entries-ready.d.mts +1 -1
  45. package/dist/entries-virtualization.d.mts +1 -1
  46. package/dist/entries.css +1 -1
  47. package/dist/entries.d.mts +6 -15
  48. package/dist/entries.mjs +1 -1
  49. package/dist/fullscreen-captions.css +1 -1
  50. package/dist/fullscreen-captions.d.mts +1 -1
  51. package/dist/fullscreen-captions.mjs +1 -1
  52. package/dist/fullscreen-controls.d.mts +1 -1
  53. package/dist/fullscreen-crossfade.d.mts +1 -1
  54. package/dist/fullscreen-lazy-load.css +1 -1
  55. package/dist/fullscreen-lazy-load.d.mts +1 -1
  56. package/dist/fullscreen-lazy-load.mjs +1 -1
  57. package/dist/fullscreen-slider.css +1 -1
  58. package/dist/fullscreen-slider.d.mts +1 -1
  59. package/dist/fullscreen-slider.mjs +1 -1
  60. package/dist/fullscreen-thumbnails.d.mts +1 -1
  61. package/dist/fullscreen-video.css +1 -1
  62. package/dist/fullscreen-video.d.mts +1 -1
  63. package/dist/fullscreen-video.mjs +1 -1
  64. package/dist/fullscreen-zoom-pan.d.mts +1 -1
  65. package/dist/fullscreen.css +1 -1
  66. package/dist/fullscreen.d.mts +2 -2
  67. package/dist/fullscreen.mjs +1 -1
  68. package/dist/fullscreenThumbnails.css +1 -1
  69. package/dist/fullscreenThumbnails.mjs +1 -1
  70. package/dist/grid-fullscreen.d.mts +2 -3
  71. package/dist/grid-infinite-scroll.d.mts +3 -4
  72. package/dist/grid-lazy-load.css +1 -1
  73. package/dist/grid-lazy-load.d.mts +2 -3
  74. package/dist/grid-lazy-load.mjs +1 -1
  75. package/dist/grid-load-more.d.mts +3 -4
  76. package/dist/grid-pagination.d.mts +3 -4
  77. package/dist/grid-ready.d.mts +2 -3
  78. package/dist/grid-virtualization.d.mts +3 -4
  79. package/dist/grid.css +1 -1
  80. package/dist/grid.d.mts +4 -5
  81. package/dist/grid.mjs +1 -1
  82. package/dist/{index-CCmwUMAS.d.mts → index-DUT57ncN.d.mts} +2 -7
  83. package/dist/index.css +1 -1
  84. package/dist/index.d.mts +8 -8
  85. package/dist/index.mjs +1 -1
  86. package/dist/{layout-BSjd7pwQ.d.mts → layout-BOy4geKv.d.mts} +1 -1
  87. package/dist/{masonry-BOnLW8R5.d.mts → masonry-BlNqj9Vn.d.mts} +3 -5
  88. package/dist/masonry-fullscreen.d.mts +1 -1
  89. package/dist/masonry-fullscreen.mjs +1 -1
  90. package/dist/masonry-infinite-scroll.d.mts +2 -7
  91. package/dist/masonry-lazy-load.css +1 -1
  92. package/dist/masonry-lazy-load.d.mts +1 -6
  93. package/dist/masonry-lazy-load.mjs +1 -1
  94. package/dist/masonry-load-more.d.mts +2 -7
  95. package/dist/masonry-pagination.d.mts +2 -7
  96. package/dist/masonry-ready.d.mts +1 -6
  97. package/dist/masonry-text-wrap.d.mts +55 -0
  98. package/dist/masonry-text-wrap.mjs +2 -0
  99. package/dist/masonry-virtualization.d.mts +2 -7
  100. package/dist/masonry.css +1 -1
  101. package/dist/masonry.d.mts +8 -9
  102. package/dist/masonry.mjs +1 -1
  103. package/dist/metafile-esm.json +1 -1
  104. package/dist/placement-DyqA2MX3.d.mts +16 -0
  105. package/dist/{responsive-Bw0ub6Hv.d.mts → responsive-BgOmwHgG.d.mts} +1 -1
  106. package/dist/responsive.d.mts +1 -1
  107. package/dist/skeleton-base.css +1 -1
  108. package/dist/skeleton-base.d.mts +2 -2
  109. package/dist/skeleton-base.mjs +1 -1
  110. package/dist/skeleton-cache-slider.css +1 -1
  111. package/dist/skeleton-cache-slider.d.mts +1 -1
  112. package/dist/skeleton-cache-slider.mjs +1 -1
  113. package/dist/skeleton-grid.css +1 -1
  114. package/dist/skeleton-grid.d.mts +4 -5
  115. package/dist/skeleton-grid.mjs +1 -1
  116. package/dist/skeleton-masonry.css +1 -1
  117. package/dist/skeleton-masonry.d.mts +3 -2
  118. package/dist/skeleton-masonry.mjs +1 -1
  119. package/dist/skeleton-slider-restore.css +1 -1
  120. package/dist/skeleton-slider-restore.d.mts +1 -1
  121. package/dist/skeleton-slider-restore.mjs +3 -3
  122. package/dist/skeleton-slider.css +1 -1
  123. package/dist/skeleton-slider.d.mts +1 -1
  124. package/dist/skeleton-slider.mjs +1 -1
  125. package/dist/slider-dots.css +1 -1
  126. package/dist/slider-dots.mjs +1 -1
  127. package/dist/slider-lazy-load.css +1 -1
  128. package/dist/slider-lazy-load.mjs +1 -1
  129. package/dist/slider-loading.css +1 -1
  130. package/dist/slider-loading.mjs +1 -1
  131. package/dist/slider-scrollbar.css +1 -1
  132. package/dist/slider-scrollbar.mjs +1 -1
  133. package/dist/slider.css +1 -1
  134. package/dist/slider.mjs +1 -1
  135. package/dist/styles.css +3 -7
  136. package/dist/thumbnails.css +1 -1
  137. package/dist/thumbnails.mjs +1 -1
  138. package/dist/types-Bg0qLhxl.d.mts +47 -0
  139. package/dist/{types-DcUQOXvS.d.mts → types-BmnPcuoM.d.mts} +2 -4
  140. package/dist/video.css +1 -1
  141. package/dist/video.mjs +1 -1
  142. package/dist/zoomPan.d.mts +1 -1
  143. package/dist/zoomPan.mjs +1 -1
  144. package/package.json +16 -32
  145. package/dist/FullscreenRuntime-N5ZHMS3K.mjs +0 -4
  146. package/dist/chunk-2AHQQNSJ.mjs +0 -1
  147. package/dist/chunk-2AM3S2QN.mjs +0 -1
  148. package/dist/chunk-3XLFID7Y.mjs +0 -1
  149. package/dist/chunk-6POQO2IQ.mjs +0 -1
  150. package/dist/chunk-AO4U6D24.mjs +0 -1
  151. package/dist/chunk-AZKAPHY5.mjs +0 -1
  152. package/dist/chunk-COVYZ7TI.mjs +0 -1
  153. package/dist/chunk-DCCJH5R3.mjs +0 -1
  154. package/dist/chunk-DECESBPG.mjs +0 -1
  155. package/dist/chunk-G2MIO7SA.mjs +0 -2
  156. package/dist/chunk-GKN6CVVQ.mjs +0 -3
  157. package/dist/chunk-JHKY7OB7.mjs +0 -0
  158. package/dist/chunk-JIVW5KRY.mjs +0 -3
  159. package/dist/chunk-P55ODH2L.mjs +0 -4
  160. package/dist/chunk-PUQSKJHB.mjs +0 -4
  161. package/dist/chunk-Q67WUAF3.mjs +0 -2
  162. package/dist/chunk-ROSKTQWL.mjs +0 -1
  163. package/dist/chunk-VKROQSDU.mjs +0 -1
  164. package/dist/chunk-WJKG7QRD.mjs +0 -1
  165. package/dist/chunk-X5T5MXVQ.mjs +0 -1
  166. package/dist/entries-cache.css +0 -1
  167. package/dist/entries-cache.d.mts +0 -45
  168. package/dist/entries-cache.mjs +0 -1
  169. package/dist/masonry-measured-ready.d.mts +0 -8
  170. package/dist/masonry-measured-ready.mjs +0 -1
  171. package/dist/masonry-measured.css +0 -1
  172. package/dist/masonry-measured.d.mts +0 -31
  173. package/dist/masonry-measured.mjs +0 -1
  174. package/dist/masonry-miqLvs3X.d.mts +0 -52
  175. package/dist/skeleton-cache-base.css +0 -1
  176. package/dist/skeleton-cache-base.d.mts +0 -18
  177. package/dist/skeleton-cache-base.mjs +0 -1
  178. package/dist/skeleton-cache-grid.css +0 -1
  179. package/dist/skeleton-cache-grid.d.mts +0 -20
  180. package/dist/skeleton-cache-grid.mjs +0 -1
  181. package/dist/skeleton-cache-masonry-structured.css +0 -1
  182. package/dist/skeleton-cache-masonry-structured.d.mts +0 -19
  183. package/dist/skeleton-cache-masonry-structured.mjs +0 -1
  184. package/dist/skeleton-cache-masonry.css +0 -1
  185. package/dist/skeleton-cache-masonry.d.mts +0 -14
  186. package/dist/skeleton-cache-masonry.mjs +0 -1
  187. package/dist/skeleton-masonry-structured.css +0 -1
  188. package/dist/skeleton-masonry-structured.d.mts +0 -62
  189. package/dist/skeleton-masonry-structured.mjs +0 -1
  190. package/dist/types-L2pRy8k4.d.mts +0 -136
package/README.md CHANGED
@@ -12,16 +12,18 @@ This table reports local gzip measurements for selected runtime surfaces. Type-o
12
12
  | Surface | JS gzip |
13
13
  | --- | --- |
14
14
  | `Entries` | 16.2kB |
15
- | `entries/cache` | 21.0kB |
15
+ | `entries/media/slider` | 20.7kB |
16
+ | `entries/media/grid` | 20.6kB |
17
+ | `entries/media/masonry` | 18.7kB |
16
18
  | `entries/ready` | 360.0B |
17
- | `entries/pagination` | 240.0B |
19
+ | `entries/pagination` | 242.0B |
18
20
  | `entries/load-more` | 198.0B |
19
21
  | `entries/infinite-scroll` | 208.0B |
20
22
  | `entries/virtualization` | 236.0B |
21
23
  | `rating-stars` | 1.3kB |
22
24
  | `FullscreenThumbnailSlider` | 20.4kB |
23
25
  | `GalleryCore` | 2.6kB |
24
- | `Grid` | 22.2kB |
26
+ | `Grid` | 19.9kB |
25
27
  | `grid/ready` | 323.0B |
26
28
  | `grid/lazy-load` | 3.7kB |
27
29
  | `grid/fullscreen` | 1.6kB |
@@ -29,31 +31,25 @@ This table reports local gzip measurements for selected runtime surfaces. Type-o
29
31
  | `grid/load-more` | 236.0B |
30
32
  | `grid/infinite-scroll` | 667.0B |
31
33
  | `grid/virtualization` | 256.0B |
32
- | `Masonry` | 11.2kB |
34
+ | `Masonry` | 12.1kB |
33
35
  | `masonry/ready` | 323.0B |
34
36
  | `masonry/fullscreen` | 1.1kB |
35
- | `masonry/measured` | 35.2kB |
36
- | `masonry/measured/ready` | 323.0B |
37
- | `masonry/lazy-load` | 3.6kB |
37
+ | `masonry/lazy-load` | 3.7kB |
38
+ | `masonry/text-wrap` | 1.8kB |
38
39
  | `masonry/pagination` | 239.0B |
39
40
  | `masonry/load-more` | 227.0B |
40
41
  | `masonry/infinite-scroll` | 630.0B |
41
42
  | `masonry/virtualization` | 252.0B |
42
43
  | `Skeleton base` | 9.1kB |
43
- | `skeleton/cache/base` | 13.7kB |
44
44
  | `skeleton/slider` | 14.7kB |
45
45
  | `skeleton/cache/slider` | 19.5kB |
46
- | `skeleton/slider/restore` | 25.4kB |
47
- | `skeleton/grid` | 11.4kB |
48
- | `skeleton/cache/grid` | 16.0kB |
49
- | `skeleton/masonry` | 4.4kB |
50
- | `skeleton/cache/masonry` | 4.4kB |
51
- | `skeleton/masonry/structured` | 20.9kB |
52
- | `skeleton/cache/masonry/structured` | 25.9kB |
53
- | `Slider core` | 19.1kB |
46
+ | `skeleton/slider/restore` | 25.5kB |
47
+ | `skeleton/grid` | 11.3kB |
48
+ | `skeleton/masonry` | 4.8kB |
49
+ | `Slider core` | 19.0kB |
54
50
  | `slider/ready` | 894.0B |
55
51
  | `slider/arrows` | 1.2kB |
56
- | `slider/dots` | 934.0B |
52
+ | `slider/dots` | 929.0B |
57
53
  | `slider/progress` | 892.0B |
58
54
  | `slider/scrollbar` | 1.2kB |
59
55
  | `slider/auto-height` | 1.3kB |
@@ -67,13 +63,13 @@ This table reports local gzip measurements for selected runtime surfaces. Type-o
67
63
  | `useFullscreenController` | 5.0kB |
68
64
  | `fullscreen/slider` | 39.7kB |
69
65
  | `fullscreen/controls` | 173.0B |
70
- | `fullscreen/captions` | 13.5kB |
66
+ | `fullscreen/captions` | 13.6kB |
71
67
  | `fullscreen/zoom-pan` | 12.4kB |
72
- | `fullscreen/video` | 16.7kB |
73
- | `fullscreen/lazy-load` | 13.5kB |
68
+ | `fullscreen/video` | 16.8kB |
69
+ | `fullscreen/lazy-load` | 13.6kB |
74
70
  | `fullscreen/crossfade` | 181.0B |
75
71
  | `fullscreen/thumbnails` | 160.0B |
76
- | `Video` | 13.0kB |
72
+ | `Video` | 13.1kB |
77
73
  | `ZoomPanImage` | 11.0kB |
78
74
  | `zoomPan/hover` | 124.0B |
79
75
  | `media / toMediaItems` | 260.0B |
@@ -166,6 +162,8 @@ Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse
166
162
 
167
163
  | Entry point | Main surface |
168
164
  | -------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
165
+ | `react-motion-gallery` | Aggregate root for primary components, helpers, and companion public types |
166
+ | `react-motion-gallery/styles.css` | Compiled stylesheet required by gallery primitives and controls |
169
167
  | `react-motion-gallery/media` | `toMediaItems`, `MediaItem`, `MediaInput` |
170
168
  | `react-motion-gallery/media/ready` | `useImageDecodeReady` |
171
169
  | `react-motion-gallery/responsive` | `BREAKPOINT_MAP` and responsive value types |
@@ -200,15 +198,15 @@ Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse
200
198
  | `react-motion-gallery/masonry` | `Masonry`, `Masonry.Item`, masonry types |
201
199
  | `react-motion-gallery/masonry/ready` | `useMasonryReady` |
202
200
  | `react-motion-gallery/masonry/fullscreen` | `masonryFullscreen` for light Masonry + `GalleryCore` |
203
- | `react-motion-gallery/masonry/measured` | Measured `Masonry`, `Masonry.Item`, plugins/reveal/fullscreen types |
204
- | `react-motion-gallery/masonry/measured/ready` | `useMasonryReady` for measured masonry |
205
201
  | `react-motion-gallery/masonry/lazy-load` | `masonryLazyLoad` |
206
202
  | `react-motion-gallery/masonry/pagination` | `masonryPagination`, `useMasonryPagination`, `MasonryPaginationControls`, page range helpers |
207
203
  | `react-motion-gallery/masonry/load-more` | `masonryLoadMore`, `useMasonryLoadMore` |
208
204
  | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll`, `useMasonryInfiniteScroll` |
209
205
  | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization`, `useMasonryVirtualizer` |
210
- | `react-motion-gallery/entries` | `Entries`, `flattenEntries`, entry layout/media container helpers |
211
- | `react-motion-gallery/entries/cache` | `CachedEntries` with `entries.loading.cache` |
206
+ | `react-motion-gallery/entries` | `Entries`, `flattenEntries`, entry data plugins, hooks, and types |
207
+ | `react-motion-gallery/entries/media/slider` | `createEntriesSliderMedia` |
208
+ | `react-motion-gallery/entries/media/grid` | `createEntriesGridMedia` |
209
+ | `react-motion-gallery/entries/media/masonry` | `createEntriesMasonryMedia` |
212
210
  | `react-motion-gallery/entries/ready` | `useEntriesReady` |
213
211
  | `react-motion-gallery/entries/pagination` | `entriesPagination`, `useEntriesPagination`, `EntriesPaginationControls`, page range helpers |
214
212
  | `react-motion-gallery/entries/load-more` | `entriesLoadMore`, `useEntriesLoadMore` |
@@ -218,14 +216,9 @@ Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse
218
216
  | `react-motion-gallery/skeleton/slider` | `SliderSkeleton` and slider skeleton authoring types |
219
217
  | `react-motion-gallery/skeleton/grid` | `GridSkeleton` and grid skeleton authoring types |
220
218
  | `react-motion-gallery/skeleton/masonry` | Lightweight `MasonrySkeleton` for dimensioned placeholders |
221
- | `react-motion-gallery/skeleton/masonry/structured` | Structured `MasonrySkeleton` and masonry skeleton authoring types |
222
219
  | `react-motion-gallery/skeleton/cache` | Server-safe skeleton cookie cache helpers and types |
223
220
  | `react-motion-gallery/skeleton/cache/provider` | Client `SkeletonCacheProvider` for SSR snapshots and client cookie refresh |
224
- | `react-motion-gallery/skeleton/cache/base` | `CachedSkeleton` with `cache` |
225
221
  | `react-motion-gallery/skeleton/cache/slider` | `CachedSliderSkeleton` with `cache` |
226
- | `react-motion-gallery/skeleton/cache/grid` | `CachedGridSkeleton` with `cache` |
227
- | `react-motion-gallery/skeleton/cache/masonry` | Lightweight `CachedMasonrySkeleton` |
228
- | `react-motion-gallery/skeleton/cache/masonry/structured` | Structured `CachedMasonrySkeleton` with `cache` |
229
222
  | `react-motion-gallery/skeleton/slider/restore` | `RestoredSliderSkeleton` with `restore` and optional `cache` |
230
223
  | `react-motion-gallery/fullscreen` | `useFullscreenController` and fullscreen types |
231
224
  | `react-motion-gallery/fullscreen/slider` | `fullscreenSlider` |
@@ -242,6 +235,8 @@ Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse
242
235
  | `react-motion-gallery/zoomPan` | `ZoomPanImage` and zoom/pan types |
243
236
  | `react-motion-gallery/zoomPan/hover` | `zoomPanHover` |
244
237
 
238
+ For a named-export inventory that covers every public subpath, including type-only exports and lower-level helpers, see [`docs/public-api-inventory.md`](./docs/public-api-inventory.md).
239
+
245
240
  Data plugin imports are intentionally split by surface and behavior:
246
241
 
247
242
  | Surface | Pagination | Load more | Infinite scroll | Virtualization |
@@ -504,15 +499,28 @@ export function LoadingShell({ ready, children }: { ready: boolean; children: Re
504
499
 
505
500
  The wrapper timing model matches the gallery loading layers: content begins fading in as soon as the skeleton exit starts; it does not wait for the skeleton to unmount.
506
501
 
507
- Default skeleton imports are cache-free. Use the opt-in cache subpaths when a surface should read and write skeleton snapshot cookies: `CachedSkeleton`, `CachedSliderSkeleton`, `CachedGridSkeleton`, `CachedMasonrySkeleton`, or `CachedEntries`.
508
-
509
- Migration notes for cache and restore:
510
-
511
- - replace `react-motion-gallery/skeleton/base` imports with `react-motion-gallery/skeleton/cache/base` when passing `cache`
512
- - replace `react-motion-gallery/skeleton/slider` imports with `react-motion-gallery/skeleton/cache/slider` when passing only `cache`
513
- - replace `react-motion-gallery/skeleton/slider` imports with `react-motion-gallery/skeleton/slider/restore` when passing `restore`
514
- - replace `react-motion-gallery/skeleton/grid` and `/masonry` imports with their `skeleton/cache/*` counterparts when passing `cache`
515
- - replace `react-motion-gallery/entries` with `react-motion-gallery/entries/cache` when using `entries.loading.cache`
502
+ Default skeleton imports are cache-free. The cache-backed public skeleton surface retained in this release is `CachedSliderSkeleton` from `react-motion-gallery/skeleton/cache/slider`; use `RestoredSliderSkeleton` from `react-motion-gallery/skeleton/slider/restore` when slider reload/back-forward restore is needed. Non-slider gallery loading surfaces no longer accept `loading.cache`.
503
+
504
+ `SkeletonFrame` is also exported from `react-motion-gallery/skeleton/base` for lower-level composition when you already have a rendered skeleton node and want the shared wrapper timing/layering behavior.
505
+
506
+ | `SkeletonFrame` prop | Type | Default | Notes |
507
+ | ------------------------------ | ----------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
508
+ | `skeletonNode` | `React.ReactNode` | required | Loading layer content. |
509
+ | `children` | `React.ReactNode` | `—` | Real content. Without children, `SkeletonFrame` returns `skeletonNode` directly. |
510
+ | `ready` | `boolean` | `false` | Reveals content when true. |
511
+ | `enabled` | `boolean` | `true` | Bypasses loading behavior when false. |
512
+ | `force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Keeps or compares the loading layer. |
513
+ | `timing` | `{ enterMs?, exitMs?, minVisibleMs? }` | shared defaults | Loading-layer timing. |
514
+ | `shellClassName` / `shellStyle` | `string` / `React.CSSProperties` | `—` | Outer shell class and style. |
515
+ | `loadingShellStyle` | `React.CSSProperties \| null` | `—` | Style applied to the shell only while the loading layer is showing. |
516
+ | `contentClassName` / `contentStyle` | `string` / `React.CSSProperties` | `—` | Content layer class and style. |
517
+ | `contentOwnsWrapperLayout` | `boolean` | `false` | Keeps the wrapper sized by content when the content layer is not locked. |
518
+ | `lockContentLayoutWhileLoading` | `boolean` | `false` | Temporarily makes the loading layer the normal-flow owner and absolutely layers content over it. |
519
+ | `loadingLayerFirst` | `boolean` | `false` | Renders the loading layer before the content layer. |
520
+ | `contentWrapper` | `(children: ReactNode) => ReactNode` | `—` | Wraps content inside the shared skeleton reveal-gate provider. |
521
+ | `shellDataAttributes` | `Record<string, string \| boolean \| undefined>` | `—` | Extra data attributes for the shell. |
522
+ | `loadingShellDataAttributes` | `Record<string, string \| boolean \| undefined>` | `—` | Extra shell data attributes while loading is showing. |
523
+ | `shellRef` | `React.Ref<HTMLDivElement>` | `—` | Ref for the outer shell. |
516
524
 
517
525
  ### Browser-measured skeleton text authoring
518
526
 
@@ -528,168 +536,43 @@ npm run --silent generate:skeleton-text-module -- \
528
536
 
529
537
  Use `responsiveBy: "container"` when text wrapping follows the card or cell width more closely than the viewport. For equal-height card sliders, the browser analyzer can also measure all canonical slider items and emit `rowHeightCompensation` so unseen cards cannot surprise the skeleton row height. See [`docs/skeleton-text-authoring.md`](./docs/skeleton-text-authoring.md) for manifest fields, command options, and the Codex-friendly workflow.
530
538
 
531
- ### Skeleton cookie snapshot cache
532
-
533
- The skeleton cookie snapshot cache is an opt-in SSR acceleration path for skeletons with expensive responsive text or layout geometry. The first visit uses the normal responsive skeleton CSS. After hydration, and again after debounced resizes, the client measures the active rendered content/skeleton geometry and writes a compact cookie. On a later server render, a valid cookie lets the skeleton render only the active snapshot values instead of the full responsive text CSS table.
534
-
535
- This exists because very accurate skeletons can require a lot of responsive text CSS, especially when text wrapping affects masonry packing or card heights. Client-only storage such as `sessionStorage` cannot help SSR because the server cannot read it and the browser only gets it after the document starts executing. A cookie is available during SSR, so the server can reserve the correct first-paint geometry before hydration.
536
-
537
- What the cache stores:
538
-
539
- - cache version, cache key, scope id, route key, timestamp, viewport width, and active width bucket
540
- - measured skeleton text records keyed by `textId`: line count, per-line widths, and optional bar metrics
541
- - masonry-only active geometry: variant key, shell height, and item heights
542
- - slider-only restore state when enabled: active index, measured shell height, slide count, skeleton slot count, route key, scope id, viewport width, and timestamp
543
- - no text strings, no media URLs, and no full CSS text
544
-
545
- Benefits:
546
-
547
- - first visit remains unchanged and uses the full responsive skeleton behavior
548
- - later reloads can parse much less skeleton CSS for the active breakpoint
549
- - text-heavy masonry, grid, slider, entries, and standalone skeletons can keep layout stability while reducing first-paint CSS work
550
- - cache-backed slider restore can render the restored slot order and auto-height before the slider handle is ready
551
- - stale, expired, route-mismatched, scope-mismatched, or malformed cookies silently fall back to the normal responsive path
552
-
553
- Defaults: `ttlMs` is `10 * 60 * 1000`, `debounceMs` is `250`, cookie `path` is `/`, `sameSite` is `lax`, `maxCookieBytes` is `3000`, and `maxTotalCookieBytes` is `8000`. Oversized snapshots are skipped, and when the combined React Motion Gallery skeleton cache cookies would exceed the total budget, older cache cookies are pruned before writing the new snapshot. Use a stable `key` per skeleton surface and a stable `routeKey` when a skeleton only applies to one route.
554
-
555
- For skeleton text to be measurable, the skeleton `text` node needs a `textId`, and the matching real content text needs `data-skeleton-text-id`. Browser-generated skeleton text modules include `textId` automatically, so existing spreads such as `...skeletonText.body` are cache-ready.
556
-
557
- ```typescript
558
- const cardBodySkeleton = {
559
- kind: "text",
560
- textId: "cardBody",
561
- barHeight: 14,
562
- lineHeight: 1.45,
563
- lines: { 0: 4, 900: 3, 1200: 2 },
564
- } as const;
565
-
566
- function CardBody({ children }: { children: React.ReactNode }) {
567
- return <p data-skeleton-text-id="cardBody">{children}</p>;
568
- }
569
- ```
570
-
571
- In SSR frameworks, read cache cookies on the server with the server-safe helper entry, then pass valid snapshots into a client provider. This example parses all React Motion Gallery skeleton cache cookies for the route.
572
-
573
- ```tsx
574
- // app/gallery/page.tsx
575
- import { cookies } from "next/headers";
576
- import {
577
- parseSkeletonCacheCookie,
578
- type SkeletonCacheSnapshot,
579
- } from "react-motion-gallery/skeleton/cache";
580
- import { GalleryPageClient } from "./GalleryPageClient";
581
-
582
- function readSkeletonCacheSnapshots(
583
- cookieStore: Awaited<ReturnType<typeof cookies>>,
584
- ) {
585
- const snapshots: Record<string, SkeletonCacheSnapshot> = {};
586
-
587
- for (const cookie of cookieStore.getAll()) {
588
- if (!cookie.name.startsWith("rmg_skel_cache_")) continue;
589
-
590
- const snapshot = parseSkeletonCacheCookie(cookie.value);
591
- if (snapshot) snapshots[snapshot.key] = snapshot;
592
- }
593
-
594
- return snapshots;
595
- }
596
-
597
- export default async function GalleryPage() {
598
- const snapshotMap = readSkeletonCacheSnapshots(await cookies());
599
-
600
- return <GalleryPageClient skeletonCacheSnapshots={snapshotMap} />;
601
- }
602
- ```
603
-
604
- Wrap the client tree in `SkeletonCacheProvider`, then opt individual skeletons in with `cache={{ key, routeKey }}`. Per-skeleton `cache.snapshot` takes precedence over provider snapshots when you need to pass one directly. The provider uses server snapshots for hydration, then refreshes readable cache cookies on later client mounts and history restores so back/forward navigation can pick up snapshots written after the original server render.
605
-
606
- ```tsx
607
- // app/gallery/GalleryPageClient.tsx
608
- "use client";
609
-
610
- import type { SkeletonCacheSnapshot } from "react-motion-gallery/skeleton/cache";
611
- import { SkeletonCacheProvider } from "react-motion-gallery/skeleton/cache/provider";
612
- import { CachedMasonrySkeleton as MasonrySkeleton } from "react-motion-gallery/skeleton/cache/masonry/structured";
613
- import { Masonry } from "react-motion-gallery/masonry/measured";
614
- import { useMasonryReady } from "react-motion-gallery/masonry/measured/ready";
615
-
616
- export function GalleryPageClient({
617
- skeletonCacheSnapshots,
618
- }: {
619
- skeletonCacheSnapshots: Record<
620
- string,
621
- SkeletonCacheSnapshot | null | undefined
622
- >;
623
- }) {
624
- const { ref, ready } = useMasonryReady();
625
-
626
- return (
627
- <SkeletonCacheProvider snapshots={skeletonCacheSnapshots}>
628
- <MasonrySkeleton
629
- cache={{
630
- key: "portfolio-masonry",
631
- routeKey: "/gallery",
632
- }}
633
- layout={portfolioSkeleton}
634
- ready={ready}
635
- masonry={{
636
- count: items.length,
637
- columns: { 0: 1, 720: 2, 1140: 4 },
638
- gap: { 0: 12, 1140: 18 },
639
- }}
640
- >
641
- <Masonry ref={ref} columns={{ 0: 1, 720: 2, 1140: 4 }}>
642
- {items.map((item) => (
643
- <Masonry.Item key={item.id}>{/* real card */}</Masonry.Item>
644
- ))}
645
- </Masonry>
646
- </MasonrySkeleton>
647
- </SkeletonCacheProvider>
648
- );
649
- }
650
- ```
651
-
652
- Use the same `cache` object on the opt-in cache components: `CachedSliderSkeleton`, `CachedGridSkeleton`, and standalone `CachedSkeleton`. For `Entries`, import `CachedEntries` from `react-motion-gallery/entries/cache` and put the cache object under `entries.loading.cache`.
653
-
654
- ```tsx
655
- import { CachedEntries as Entries } from "react-motion-gallery/entries/cache";
656
-
657
- <Entries
658
- entries={{
659
- items,
660
- mediaLayout: "grid",
661
- loading: {
662
- cache: {
663
- key: "editorial-entries",
664
- routeKey: "/stories",
665
- },
666
- skeleton: entrySkeleton,
667
- },
668
- }}
669
- />;
670
- ```
671
-
672
- Cookie options can be tuned per skeleton:
673
-
674
- ```tsx
675
- import { CachedGridSkeleton as GridSkeleton } from "react-motion-gallery/skeleton/cache/grid";
676
-
677
- <GridSkeleton
678
- cache={{
679
- key: "product-grid",
680
- routeKey: "/products",
681
- ttlMs: 5 * 60 * 1000,
682
- debounceMs: 150,
683
- cookie: {
684
- path: "/",
685
- sameSite: "lax",
686
- maxCookieBytes: 3000,
687
- maxTotalCookieBytes: 8000,
688
- },
689
- }}
690
- layout={productGridSkeleton}
691
- />;
692
- ```
539
+ ### Slider skeleton cookie snapshot cache
540
+
541
+ The skeleton cookie snapshot cache remains available for slider skeletons. Use `CachedSliderSkeleton` from `react-motion-gallery/skeleton/cache/slider` when a slider skeleton should read and write snapshot cookies, or `RestoredSliderSkeleton` from `react-motion-gallery/skeleton/slider/restore` when reload/back-forward restore is needed. Non-slider gallery loading surfaces no longer accept `loading.cache`, and the base/grid/masonry/entries cache wrapper subpaths have been removed.
542
+
543
+ In SSR frameworks, read cache cookies on the server with `react-motion-gallery/skeleton/cache`, pass snapshots through `SkeletonCacheProvider`, and opt slider skeletons in with a stable `cache={{ key, routeKey }}` object.
544
+
545
+ | Cache export | Notes |
546
+ | ------------ | ----- |
547
+ | `getSkeletonCacheCookieName(key)` | Returns the deterministic cookie name for a cache key. |
548
+ | `getSkeletonCacheRouteKey(location?)` | Builds the default route key from `pathname + search`, or `""` without a location. |
549
+ | `parseSkeletonCacheCookie(raw, options?)` | Parses and validates a cookie value into `SkeletonCacheSnapshot \| null`. |
550
+ | `serializeSkeletonCacheSnapshot(snapshot)` | Serializes a snapshot into the compact cookie payload string. |
551
+ | `validateSkeletonCacheSnapshot(snapshot, options?)` | Re-validates an already parsed snapshot against key, route, kind, viewport, TTL, text ids, and item metadata. |
552
+ | `SKELETON_CACHE_VERSION` and default constants | Exported for diagnostics and custom integrations: TTL, debounce, per-cookie byte budget, and total cookie byte budget. |
553
+
554
+ | `SkeletonCacheOptions` field | Type | Notes |
555
+ | ---------------------------- | ---- | ----- |
556
+ | `key` | `string` | Required stable cache identity. |
557
+ | `snapshot` | `SkeletonCacheSnapshot \| null` | Server-provided snapshot override. |
558
+ | `ttlMs` | `number` | Snapshot freshness window. Defaults to `DEFAULT_SKELETON_CACHE_TTL_MS`. |
559
+ | `debounceMs` | `number` | Client write debounce. Defaults to `DEFAULT_SKELETON_CACHE_DEBOUNCE_MS`. |
560
+ | `routeKey` | `string` | Optional route guard for page-specific skeleton geometry. |
561
+ | `cookie.path` | `string` | Cookie path. Defaults to `/`. |
562
+ | `cookie.sameSite` | `"lax" \| "strict" \| "none"` | SameSite policy. Defaults to `lax`. |
563
+ | `cookie.secure` | `boolean` | Adds the Secure cookie attribute when true. |
564
+ | `cookie.maxCookieBytes` | `number` | Per-cookie write budget. Defaults to `DEFAULT_SKELETON_CACHE_COOKIE_MAX_BYTES`. |
565
+ | `cookie.maxTotalCookieBytes` | `number` | Combined React Motion Gallery cache-cookie budget. Defaults to `DEFAULT_SKELETON_CACHE_COOKIE_MAX_TOTAL_BYTES`. |
566
+
567
+ | Parse/validate option | Notes |
568
+ | --------------------- | ----- |
569
+ | `key`, `scopeId`, `kind`, `routeKey` | Reject snapshots that do not match the expected identity, cache kind, or route. |
570
+ | `ttlMs`, `now` | Override freshness validation and the timestamp used for age checks. |
571
+ | `viewportWidth`, `viewportTolerancePx`, `widthBucketMin` | Require compatible viewport or bucket metadata. |
572
+ | `textIds` | Require text measurements for specific skeleton text ids. |
573
+ | `itemCount`, `variantKeys` | Require compatible masonry snapshot metadata when validating older/custom snapshots. |
574
+
575
+ `SkeletonCacheProvider` accepts `children`, shared `options`, one `snapshot`, or a keyed `snapshots` map. Provider snapshots are used during hydration, then the client refreshes readable cache cookies after mounted slider skeletons measure.
693
576
 
694
577
  ## Reveal
695
578
 
@@ -1362,6 +1245,7 @@ export function BasicGrid() {
1362
1245
  | ----------- | ------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------ |
1363
1246
  | `children` | `React.ReactNode` | `—` | The grid card content. |
1364
1247
  | `span` | `number \| "full" \| Record<string, number \| "full">` | `1` | Per-item track span. `"full"` renders `grid-column: 1 / -1`; numeric values render `grid-column: span n / span n`. |
1248
+ | `revealKey` | `React.Key` | `—` | Stable reveal identity for a slot whose backing data can change. |
1365
1249
  | `className` | `string` | `—` | Extra class name merged onto the grid item wrapper. |
1366
1250
  | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the grid item wrapper. |
1367
1251
 
@@ -1377,7 +1261,7 @@ export function BasicGrid() {
1377
1261
  | `itemClassName` | `string` | `—` | Class name added to each wrapped grid item. |
1378
1262
  | `fullscreenTrigger` | `"item" \| "media"` | `"media"` | When `gridFullscreen()` is active, opens fullscreen from the clicked media node or the entire item shell. |
1379
1263
  | `plugins` | `GridPlugin[]` | `[]` | Explicit first-party Grid features such as lazy-load, fullscreen, pagination, load-more, infinite scroll, and virtualization. |
1380
- | `loading` | `GridLoadingOptions` | `—` | Core per-item skeleton/reveal lifecycle. Supports `skeleton`, `cache`, `active`, `count`, media decode waiting, forced compare mode, skeleton timing, and reveal identity memory. |
1264
+ | `loading` | `GridLoadingOptions` | `—` | Core per-item skeleton/reveal lifecycle. Supports structured or callback skeletons, `active`, `count`, media decode waiting, forced compare mode, skeleton timing, and reveal identity memory. |
1381
1265
  | `reveal.staggerMs` | `number` | `60` | Reveal stagger for the fade-in. |
1382
1266
  | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1383
1267
  | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
@@ -1421,7 +1305,7 @@ function ProductGrid({ items, total }) {
1421
1305
 
1422
1306
  Grid pagination, load-more, infinite-scroll, and virtualization APIs are documented in the shared data-plugin sections after Entries. Pagination and load-more window the child list before CSS grid layout, so hidden items do not reserve tracks. In `"server"` mode the supplied children and `fullscreenItems` are treated as the current server window.
1423
1307
 
1424
- Grid fullscreen behavior is provided by `GalleryCore`, `useFullscreenController`, and the opt-in `gridFullscreen()` plugin. Grid itself does not expose a ref-based imperative API.
1308
+ Grid fullscreen behavior is provided by `GalleryCore`, `useFullscreenController`, and the opt-in `gridFullscreen()` plugin. The Grid ref handle is for readiness and DOM access; fullscreen opens through `GalleryCore` rather than a Grid imperative method.
1425
1309
 
1426
1310
  Wrap a card in `Grid.Item` when it should span tracks or needs wrapper styling:
1427
1311
 
@@ -1466,6 +1350,47 @@ Grid skeleton slots inherit real item spans by default. Slot overrides in the sk
1466
1350
 
1467
1351
  Use `loading.active` to hold reveals while async data is pending, `loading.timing.exitMs` to control the per-item skeleton opacity fade-out duration, and `loading.keepSkeletonMounted` when a settled skeleton layer should stay mounted at opacity 0 so a later loading transition can fade it back in smoothly. Set `rememberRevealed: false` for server-paged slots that should animate again when an item leaves and later re-enters the current data window. If an item should keep the same grid slot while its backing data changes, pass `revealKey` on `Grid.Item` or `data-rmg-grid-reveal-key` on a raw child.
1468
1352
 
1353
+ #### `GridLoadingOptions`
1354
+
1355
+ | Option | Type | Default | Notes |
1356
+ | ---------------------------- | ----------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1357
+ | `enabled` | `boolean` | `true` when `loading` is provided | Disables the Grid loading/reveal lifecycle when false. |
1358
+ | `active` | `boolean` | `false` | Holds the grid busy, blocks reveal unless forced, and creates placeholder cells when there are no children. |
1359
+ | `count` | `number` | `0` | Placeholder count used with `active` and empty children. Falls back to `GridSkeletonSpec.layout.count` when available. |
1360
+ | `skeleton` | `GridSkeletonSpec \| ((args: GridLoadingSkeletonArgs) => ReactNode)` | `—` | Structured skeleton spec or custom per-item skeleton renderer. Structured specs also render an internal normal-flow grid skeleton during SSR for layout stability. |
1361
+ | `force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Keeps skeletons visible. `showContent: true` enables compare mode with mounted content under the skeleton. |
1362
+ | `timing.enterMs` | `number` | `exitMs` | Skeleton opacity enter duration when forced loading returns. |
1363
+ | `timing.minVisibleMs` | `number` | `120` | Minimum time an item skeleton remains visible before reveal can start. |
1364
+ | `timing.exitMs` | `number` | `220` | Item skeleton opacity fade-out duration. |
1365
+ | `animate` | `boolean` | `true` | Set false to collapse enter/exit durations to zero. Reduced-motion preferences also disable these durations. |
1366
+ | `waitForMedia` | `boolean` | `true` | Waits for trackable descendant media to load/decode before reveal. |
1367
+ | `decodeTimeoutMs` | `number` | `8000` | Media readiness timeout fallback. |
1368
+ | `rootMargin` | `string` | `"0px"` | IntersectionObserver root margin for item reveal. |
1369
+ | `threshold` | `number` | `0.01` | IntersectionObserver threshold for item reveal. |
1370
+ | `keepSkeletonMounted` | `boolean` | `false` | Keeps callback skeletons mounted after reveal. Structured `GridSkeletonSpec` skeletons stay mounted as inner layout anchors by default. |
1371
+ | `rememberRevealed` | `boolean` | `true` | Keeps known item identities revealed across data-window changes. |
1372
+
1373
+ #### `GridLoadingSkeletonArgs`
1374
+
1375
+ | Field | Type | Notes |
1376
+ | ------------- | ----------- | ---------------------------------------------------------------- |
1377
+ | `index` | `number` | Source item index. |
1378
+ | `key` | `React.Key` | Grid item key. |
1379
+ | `revealKey` | `React.Key` | Stable reveal identity when provided. |
1380
+ | `placeholder` | `boolean` | True for empty-grid placeholder cells created from loading count. |
1381
+ | `ready` | `boolean` | Whether the item content is considered ready for the skeleton. |
1382
+
1383
+ ### `GridHandle` methods
1384
+
1385
+ Forward a ref to `Grid` when parent code needs readiness or DOM access.
1386
+
1387
+ | Method | Signature | Notes |
1388
+ | -------------- | ------------------------------------------------------- | ------------------------------------------ |
1389
+ | `getRootNode` | `() => HTMLElement \| null` | Returns the live grid root. |
1390
+ | `getItemNodes` | `() => HTMLElement[]` | Returns current rendered grid item nodes. |
1391
+ | `isReady` | `() => boolean` | True when client, loading, and plugin gates are ready. |
1392
+ | `onReady` | `(callback: (nodes: HTMLElement[]) => void) => () => void` | Subscribes to the ready signal. |
1393
+
1469
1394
  ```typescript
1470
1395
  import { Grid } from "react-motion-gallery";
1471
1396
  import type { GridSkeletonSpec } from "react-motion-gallery/skeleton/grid";
@@ -1548,7 +1473,10 @@ export function BasicMasonry() {
1548
1473
  | `children` | `React.ReactNode` | `—` | The masonry card content. |
1549
1474
  | `width` | `number` | `—` | Intrinsic item width used for aspect-ratio layout. |
1550
1475
  | `height` | `number` | `—` | Intrinsic item height used for aspect-ratio layout. |
1476
+ | `heightOffsetPx` | `number \| Record<string, number> \| { rules, fallback? }` | `0` | Pixel height added after aspect-ratio scaling, useful for ratio-based media plus fixed or responsive chrome. |
1551
1477
  | `span` | `number \| "full" \| Record<string, number \| "full">` | `1` | Per-item track span. `"full"` resolves to the active column count and numeric values clamp to the current track count. |
1478
+ | `revealKey` | `React.Key` | `—` | Stable reveal identity for a slot whose backing data can change. |
1479
+ | `placeholder` | `boolean` | `false` | Marks a manually supplied item placeholder as hidden from assistive tech. |
1552
1480
  | `className` | `string` | `—` | Extra class name merged onto the masonry item wrapper. |
1553
1481
  | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the masonry item wrapper. |
1554
1482
 
@@ -1560,19 +1488,23 @@ export function BasicMasonry() {
1560
1488
  | `gap` | `number \| Record<string, number>` | `—` | Responsive gap between columns and items. |
1561
1489
  | `placement` | `"balanced" \| "roundRobin" \| "horizontalOrder"` | `"balanced"` | `balanced` packs into the shortest fitting column group, `roundRobin` cycles start columns deterministically, and `horizontalOrder` preserves a stronger left-to-right scan when spans are involved. |
1562
1490
  | `as` | `React.ElementType` | `"div"` | Root HTML element or custom component. |
1563
- | `rootRef` | `React.Ref<HTMLDivElement>` | `—` | Ref to the masonry root. |
1491
+ | `rootRef` | `React.Ref<HTMLElement>` | `—` | Ref to the masonry root. |
1564
1492
  | `classNames.root` | `string` | `—` | Root class name. |
1565
1493
  | `classNames.item` | `string` | `—` | Item class name. |
1494
+ | `className` | `string` | `—` | Class name merged onto the masonry root. |
1495
+ | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the masonry root. |
1566
1496
  | `plugins` | `MasonryPlugin[]` | `[]` | Opt-in Masonry plugins for fullscreen, lazy-load, pagination, load-more, infinite scroll, and virtualization. |
1497
+ | `loading` | `MasonryLoadingOptions` | `—` | Core dimensioned Masonry skeleton/reveal lifecycle. |
1567
1498
  | `reveal.staggerMs` | `number` | `160` | Reveal stagger for the fade-in. |
1568
1499
  | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1569
1500
  | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1501
+ | `reveal.staggerLimit` | `number` | `—` | Optional cap on how many items stagger. |
1570
1502
  | `reveal.disabled` | `boolean` | `false` | Disables the built-in reveal classes. |
1571
1503
  | `revealReady` | `boolean` | `true` | Holds the reveal until external loading or viewport orchestration is ready. |
1572
1504
 
1573
1505
  ### Masonry plugins
1574
1506
 
1575
- Import Masonry plugins from their own subpaths and pass them to `plugins`. The data plugin factories work across both the default dimensioned Masonry and measured Masonry.
1507
+ Import Masonry plugins from their own subpaths and pass them to `plugins`.
1576
1508
 
1577
1509
  ```typescript
1578
1510
  import { GalleryCore } from "react-motion-gallery/core";
@@ -1605,15 +1537,13 @@ function ProductMasonry({ children, items }) {
1605
1537
  | `react-motion-gallery/masonry/pagination` | `masonryPagination(options)` | Windows child items by `pageIndex` and `pageSize`; `useMasonryPagination()` also returns page state and `MasonryPaginationControls`. |
1606
1538
  | `react-motion-gallery/masonry/load-more` | `masonryLoadMore(options)` | Windows child items by `visibleCount`; `useMasonryLoadMore()` owns and increments the visible count. |
1607
1539
  | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll(options)` | Renders a sentinel after the masonry root so it does not disturb absolute item positioning. |
1608
- | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization(options)` | Runs after pagination/load-more. Default Masonry uses known item positions; measured Masonry starts from estimated heights and refines with `ResizeObserver`. |
1540
+ | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization(options)` | Runs after pagination/load-more and windows known dimensioned item positions. |
1609
1541
 
1610
- `masonryLazyLoad()` is for measured Masonry and enables lazy loading by default. Pass `{ enabled: false }` to make the plugin inert.
1542
+ `masonryLazyLoad()` enables lazy loading by default. Pass `{ enabled: false }` to make the plugin inert.
1611
1543
 
1612
1544
  Masonry pagination, load-more, infinite-scroll, and virtualization APIs are documented in the shared data-plugin sections after Entries. Pagination and load-more apply before layout so hidden items do not reserve columns or masonry positions. In `"server"` mode the supplied children and `fullscreenItems` are treated as the current server window.
1613
1545
 
1614
- Measured Masonry accepts arbitrary React children, including text-containing JSX. The wrapper props are only for styling the built-in masonry item shell.
1615
-
1616
- In the measured subpath, wrap a card in `Masonry.Item` when it needs its own span, wrapper `className`, or wrapper `style`:
1546
+ Wrap a card in `Masonry.Item` to provide its dimensions. Use `span`, `heightOffsetPx`, `className`, and `style` when it needs custom placement or wrapper styling:
1617
1547
 
1618
1548
  ```typescript
1619
1549
  <Masonry
@@ -1636,64 +1566,33 @@ Choose a placement based on what should feel stable:
1636
1566
  - `roundRobin`: best when deterministic column assignment matters more than tight packing.
1637
1567
  - `horizontalOrder`: best when wider cards should still read in a mostly left-to-right order.
1638
1568
 
1639
- Masonry no longer owns loading UI. Use `useMasonryReady` and wrap Masonry with `MasonrySkeleton`, the same composition pattern used by Slider and Grid.
1569
+ Masonry no longer owns cache-backed loading UI. Use `useMasonryReady` and wrap Masonry with `MasonrySkeleton` when a separate full-layout skeleton wrapper should own loading layout.
1640
1570
 
1641
- The default Masonry import is dimensioned and lightweight, with built-in reveal timing and opt-in fullscreen through `react-motion-gallery/masonry/fullscreen`. For arbitrary measured card heights, measured lazy-load plugins, and structured skeleton text, import from `react-motion-gallery/masonry/measured` and `react-motion-gallery/skeleton/masonry/structured`.
1571
+ The Masonry import is dimensioned and lightweight, with built-in reveal timing, `loading.skeleton` support, and opt-in fullscreen through `react-motion-gallery/masonry/fullscreen`.
1642
1572
 
1643
1573
  Lightweight Masonry skeletons live in `react-motion-gallery/skeleton/masonry` and mirror dimensioned `Masonry.Item` data with `items`, `ratios`, or `heightsPx`.
1644
1574
 
1645
- Structured Masonry skeletons live in `react-motion-gallery/skeleton/masonry/structured` and can use a `layout` spec with the same inner node vocabulary as Grid skeletons, including `text` nodes and `itemWrapStyle`.
1575
+ For dimensioned cards with text that wraps at different viewport or container widths, import `useMasonryTextWrapLayout` and `createMasonryTextWrapSkeletonLayout` from `react-motion-gallery/masonry/text-wrap`. The hook returns a `rootRef` for Masonry plus `getItemGeometry()`, which computes the ratio dimensions and responsive `heightOffsetPx` rules from measured skeleton text metrics.
1646
1576
 
1647
- Live Masonry content mounts invisibly until the current item set has completed an initial measurement pass. The Skeleton wrapper stays visible during that handoff, so the first revealed layout is based on measured DOM geometry rather than approximate height hints.
1577
+ For text-heavy cards, `heightOffsetPx` lets the ratio-based media height and fixed or responsive card chrome participate in the same deterministic placement model.
1648
1578
 
1649
- `layout.slots` gives Masonry the same per-card override escape hatch that slider skeletons have. Use a slot when one card needs a different placeholder tree, wrapper styling, span, or outer height. `slot.span` can override the corresponding `Masonry.Item` span for the placeholder, `slot.ratio` maps to Masonry's card-height rhythm, and `slot.heightPx` lets you pin a specific shell height when you need an exact placeholder.
1579
+ Use `items` when one card needs a different placeholder height or span. For simpler shells, `ratios`, `heightsPx`, and `spans` can describe the same rhythm without a full item list.
1650
1580
 
1651
1581
  ```typescript
1652
- import { Masonry } from "react-motion-gallery/masonry/measured";
1653
- import { useMasonryReady } from "react-motion-gallery/masonry/measured/ready";
1582
+ import { Masonry } from "react-motion-gallery/masonry";
1583
+ import { useMasonryReady } from "react-motion-gallery/masonry/ready";
1654
1584
  import {
1655
1585
  MasonrySkeleton,
1656
- type MasonrySkeletonSpec,
1657
- } from "react-motion-gallery/skeleton/masonry/structured";
1586
+ type MasonrySkeletonProps,
1587
+ } from "react-motion-gallery/skeleton/masonry";
1658
1588
 
1659
- const masonrySkeleton: MasonrySkeletonSpec = {
1589
+ const masonrySkeleton: MasonrySkeletonProps = {
1660
1590
  ratios: [118, 126, 102, 146],
1661
- layout: {
1662
- kind: "masonry",
1663
- itemWrapStyle: {
1664
- padding: 14,
1665
- borderRadius: 20,
1666
- boxShadow: "0 18px 36px rgba(15, 23, 42, 0.08)",
1667
- },
1668
- item: {
1669
- kind: "col",
1670
- style: { gap: 12 },
1671
- children: [
1672
- {
1673
- kind: "rect",
1674
- style: { width: "100%", height: 180, borderRadius: 16 },
1675
- },
1676
- {
1677
- kind: "text",
1678
- barHeight: 14,
1679
- lineHeight: 1.55,
1680
- lines: 3,
1681
- lastBarWidth: "74%",
1682
- style: { width: "100%" },
1683
- },
1684
- ],
1685
- },
1686
- slots: [
1687
- {
1688
- ratio: 182,
1689
- span: { 0: 1, 1100: 2 },
1690
- item: {
1691
- kind: "rect",
1692
- style: { width: "100%", aspectRatio: "3 / 5", borderRadius: 16 },
1693
- },
1694
- },
1695
- ],
1696
- },
1591
+ spans: [undefined, { 0: 1, 1100: 2 }, undefined, undefined],
1592
+ columns: { 0: 1, 700: 2, 1100: 3 },
1593
+ gap: { 0: 12, 1100: 20 },
1594
+ placement: "balanced",
1595
+ radius: 18,
1697
1596
  };
1698
1597
 
1699
1598
  function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
@@ -1701,15 +1600,10 @@ function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
1701
1600
 
1702
1601
  return (
1703
1602
  <MasonrySkeleton
1704
- layout={masonrySkeleton}
1603
+ {...masonrySkeleton}
1705
1604
  ready={masonryReady}
1706
1605
  timing={{ minVisibleMs: 220, exitMs: 600 }}
1707
- masonry={{
1708
- count: items.length,
1709
- columns: { 0: 1, 700: 2, 1100: 3 },
1710
- gap: { 0: 12, 1100: 20 },
1711
- placement: "balanced",
1712
- }}
1606
+ count={items.length}
1713
1607
  >
1714
1608
  <Masonry
1715
1609
  ref={masonryRef}
@@ -1727,6 +1621,52 @@ function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
1727
1621
  }
1728
1622
  ```
1729
1623
 
1624
+ #### `MasonryLoadingOptions`
1625
+
1626
+ | Option | Type | Default | Notes |
1627
+ | ---------------------------- | -------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
1628
+ | `enabled` | `boolean` | `true` when `loading` is provided | Disables the Masonry loading/reveal lifecycle when false. |
1629
+ | `active` | `boolean` | `false` | Holds the masonry busy, blocks reveal unless forced, and creates placeholder items when there are no children. |
1630
+ | `count` | `number` | `0` | Placeholder count used with `active` and empty children. |
1631
+ | `skeleton` | `MasonrySkeletonProps \| ((args: MasonryLoadingSkeletonArgs) => ReactNode)` | `—` | Standalone masonry skeleton props or custom per-item skeleton renderer. |
1632
+ | `force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `false` | Keeps skeletons visible. `showContent: true` enables compare mode with mounted content under the skeleton. |
1633
+ | `timing.enterMs` | `number` | `exitMs` | Skeleton opacity enter duration when forced loading returns. |
1634
+ | `timing.minVisibleMs` | `number` | `120` | Minimum time an item skeleton remains visible before reveal can start. |
1635
+ | `timing.exitMs` | `number` | `220` | Item skeleton opacity fade-out duration. |
1636
+ | `animate` | `boolean` | `true` | Set false to collapse enter/exit durations to zero. Reduced-motion preferences also disable these durations. |
1637
+ | `waitForMedia` | `boolean` | `true` | Waits for trackable descendant media to load/decode before reveal. |
1638
+ | `decodeTimeoutMs` | `number` | `8000` | Media readiness timeout fallback. |
1639
+ | `rootMargin` | `string` | `"0px"` | IntersectionObserver root margin for item reveal. |
1640
+ | `threshold` | `number` | `0.01` | IntersectionObserver threshold for item reveal. |
1641
+ | `keepSkeletonMounted` | `boolean` | `false` | Keeps settled skeleton layers mounted at opacity 0 for later loading transitions. |
1642
+ | `rememberRevealed` | `boolean` | `true` | Keeps known item identities revealed across data-window changes. |
1643
+
1644
+ #### `MasonryLoadingSkeletonArgs`
1645
+
1646
+ | Field | Type | Notes |
1647
+ | ---------------- | ------------------------ | ----------------------------------------------------------------- |
1648
+ | `index` | `number` | Source item index. |
1649
+ | `itemIndex` | `number` | Original item index when a plugin window is active. |
1650
+ | `key` | `React.Key` | Masonry item key. |
1651
+ | `revealKey` | `React.Key` | Stable reveal identity when provided. |
1652
+ | `placeholder` | `boolean` | True for empty-masonry placeholder items created from loading count. |
1653
+ | `ready` | `boolean` | Whether the item content is considered ready for the skeleton. |
1654
+ | `span` | `ResponsiveMasonrySpan` | Active item span metadata. |
1655
+ | `width` | `number` | Intrinsic item width. |
1656
+ | `height` | `number` | Intrinsic item height. |
1657
+ | `heightOffsetPx` | `MasonryHeightOffsetPx` | Extra fixed or responsive item chrome height. |
1658
+
1659
+ ### `MasonryHandle` methods
1660
+
1661
+ Forward a ref to `Masonry` when parent code needs readiness or DOM access.
1662
+
1663
+ | Method | Signature | Notes |
1664
+ | -------------- | ---------------------------------------------------------- | --------------------------------------------- |
1665
+ | `getRootNode` | `() => HTMLElement \| null` | Returns the masonry root. |
1666
+ | `getItemNodes` | `() => HTMLElement[]` | Returns current rendered masonry item nodes. |
1667
+ | `isReady` | `() => boolean` | True when client, loading, and plugin gates are ready. |
1668
+ | `onReady` | `(callback: (nodes: HTMLElement[]) => void) => () => void` | Subscribes to the ready signal. |
1669
+
1730
1670
  ## Entries
1731
1671
 
1732
1672
  `Entries` is the structured-data surface. You pass entry objects, choose whether the entry rows themselves render as a vertical list or a card grid, render each media item however you want, and provide a `renderMediaContainer` function that decides whether an entry's media should be laid out as a slider, grid, or masonry block.
@@ -1835,9 +1775,9 @@ For common entry media layouts, the exported helper factories can own `renderMed
1835
1775
  ```typescript
1836
1776
  import {
1837
1777
  Entries,
1838
- createEntriesGridMedia,
1839
1778
  type EntriesOptions,
1840
1779
  } from "react-motion-gallery/entries";
1780
+ import { createEntriesGridMedia } from "react-motion-gallery/entries/media/grid";
1841
1781
 
1842
1782
  const renderEntryGridMedia = createEntriesGridMedia({
1843
1783
  gridObject: {
@@ -1859,7 +1799,7 @@ export function EntryGrid({ entries }: { entries: EntriesOptions["items"] }) {
1859
1799
  }
1860
1800
  ```
1861
1801
 
1862
- The same pattern works with `createEntriesSliderMedia()` and `createEntriesMasonryMedia()`. Import from `react-motion-gallery/entries/cache` instead when the entry loading skeleton also uses `loading.cache`.
1802
+ The same pattern works with `createEntriesSliderMedia()` from `react-motion-gallery/entries/media/slider` and `createEntriesMasonryMedia()` from `react-motion-gallery/entries/media/masonry`.
1863
1803
 
1864
1804
  ### Entry loading, decode, and reveal flow
1865
1805
 
@@ -1907,7 +1847,6 @@ Fullscreen close has a matching entry-aware path. If the user closes fullscreen
1907
1847
  | `loading.enabled` | `boolean` | `—` | Enables entry loading and decode gating. |
1908
1848
  | `loading.force` | `boolean \| { enabled?: boolean; showContent?: boolean; skeletonOpacity?: number }` | `—` | Forces entry skeletons to remain visible. Set `showContent: true` to preview mounted, ready entry content under the skeleton, and tune the loading overlay with `skeletonOpacity`. |
1909
1849
  | `loading.skeleton` | `EntrySkeletonSpec \| ((args) => EntrySkeletonSpec \| null \| undefined)` | `—` | Built-in skeleton spec or resolver. |
1910
- | `loading.cache` | `SkeletonCacheOptions` | `—` | Opts entry skeleton text into the cookie snapshot cache. |
1911
1850
  | `loading.minHeight` | `number \| string` | `"260px"` | Minimum reserved height while loading. |
1912
1851
  | `loading.exitMs` | `number` | `220` | Entry skeleton opacity fade-out duration. |
1913
1852
  | `loading.nearMargin` | `string` | `"700px 0px"` | Preload margin used before entries enter view. |
@@ -1925,6 +1864,15 @@ Fullscreen close has a matching entry-aware path. If the user closes fullscreen
1925
1864
  | `entryList` | `ElementStyle` | `—` | Styles the entry list container. |
1926
1865
  | `entryRow` | `ElementStyle` | `—` | Styles each entry row container. |
1927
1866
 
1867
+ ### `EntriesHandle` methods
1868
+
1869
+ | Method | Type | Notes |
1870
+ | --------------- | ------------------------------------------------------- | ------------------------------------------ |
1871
+ | `getRootNode` | `() => HTMLDivElement \| null` | Returns the live entries root. |
1872
+ | `getEntryNodes` | `() => HTMLElement[]` | Returns the rendered entry row elements. |
1873
+ | `isReady` | `() => boolean` | True when loading and plugin gates are ready. |
1874
+ | `onReady` | `(callback: (nodes: HTMLElement[]) => void) => () => void` | Subscribes to the ready signal. |
1875
+
1928
1876
  Entry skeleton `text` nodes also render wrapped line bars via `lines`, matching the slider and grid skeleton behavior, including responsive `barHeight` and line counts plus configurable trailing `lastBarWidth`.
1929
1877
 
1930
1878
  ### Entries data plugins
@@ -2186,7 +2134,7 @@ Virtualization mounts only the records near the viewport. It runs after paginati
2186
2134
  | `gap` | all | `number` | `24` | Vertical gap included in virtual range and spacer calculations. Values below zero clamp to zero. |
2187
2135
  | `overscan` | all | `number` | `3` | Extra rows or items to mount before and after the visible range. Values below zero clamp to zero. |
2188
2136
 
2189
- The hook form memoizes the factory call and returns the plugin. Grid virtualizes by rows and inserts top and bottom spacers that span every grid column. Entries virtualizes list rows by default and grid rows when `entries.layout: "grid"` or `entriesVirtualization({ layout: "grid" })` is used. Default Masonry uses known item positions; measured Masonry starts from estimated or seeded heights and refines its range as items are measured with `ResizeObserver`.
2137
+ The hook form memoizes the factory call and returns the plugin. Grid virtualizes by rows and inserts top and bottom spacers that span every grid column. Entries virtualizes list rows by default and grid rows when `entries.layout: "grid"` or `entriesVirtualization({ layout: "grid" })` is used. Masonry virtualizes known dimensioned item positions from `Masonry.Item` geometry, including spans and `heightOffsetPx`.
2190
2138
 
2191
2139
  ## RatingStars
2192
2140