react-motion-gallery 2.0.79 → 2.0.81

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 (191) hide show
  1. package/README.md +211 -267
  2. package/dist/{FullscreenRuntime-YZWI2HL2.css → FullscreenRuntime-JN6YHT3R.css} +1 -1
  3. package/dist/FullscreenRuntime-YWQQ6EE5.mjs +4 -0
  4. package/dist/chunk-36EC6QCM.mjs +1 -0
  5. package/dist/{chunk-RA7HIQFJ.mjs → chunk-3V4NPPY7.mjs} +1 -1
  6. package/dist/{chunk-3YNFTUKE.mjs → chunk-5OWHWSI6.mjs} +2 -2
  7. package/dist/chunk-6H2MRVWI.mjs +1 -0
  8. package/dist/{chunk-LZ53EONL.mjs → chunk-7GCYLW7E.mjs} +1 -1
  9. package/dist/chunk-BORUUP5X.mjs +2 -0
  10. package/dist/chunk-CHFC3K2N.mjs +1 -0
  11. package/dist/{chunk-B73WDXEU.mjs → chunk-EWU5OYTE.mjs} +1 -1
  12. package/dist/chunk-HYXNES5X.mjs +1 -0
  13. package/dist/{chunk-A2YEDYJQ.mjs → chunk-HZLKPCLP.mjs} +1 -1
  14. package/dist/chunk-I2DTE5DH.mjs +1 -0
  15. package/dist/chunk-ITKAYFXD.mjs +2 -0
  16. package/dist/chunk-IWFBL2G3.mjs +1 -0
  17. package/dist/chunk-JUEY27Y6.mjs +4 -0
  18. package/dist/{chunk-HVO7H4XB.mjs → chunk-NJNROEMU.mjs} +3 -3
  19. package/dist/chunk-OEP7JRYH.mjs +1 -0
  20. package/dist/{chunk-ZCVS5WHL.mjs → chunk-PBP3RTCJ.mjs} +1 -1
  21. package/dist/{chunk-3HVF63UG.mjs → chunk-RM77NXVV.mjs} +1 -1
  22. package/dist/{chunk-GKR35A24.mjs → chunk-RMCMQMRC.mjs} +1 -1
  23. package/dist/chunk-SPVRJNXN.mjs +6 -0
  24. package/dist/chunk-UO4HNZIO.mjs +1 -0
  25. package/dist/{chunk-NE64XTCU.mjs → chunk-WLYIORTH.mjs} +1 -1
  26. package/dist/chunk-WNQAUKX5.mjs +1 -0
  27. package/dist/{chunk-GWYW2V72.mjs → chunk-WTYA2CPU.mjs} +1 -1
  28. package/dist/chunk-YIWRZPVX.mjs +4 -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-AEGJQGLQ.mjs +0 -6
  151. package/dist/chunk-AO4U6D24.mjs +0 -1
  152. package/dist/chunk-AZKAPHY5.mjs +0 -1
  153. package/dist/chunk-COVYZ7TI.mjs +0 -1
  154. package/dist/chunk-DCCJH5R3.mjs +0 -1
  155. package/dist/chunk-DECESBPG.mjs +0 -1
  156. package/dist/chunk-G2MIO7SA.mjs +0 -2
  157. package/dist/chunk-GKN6CVVQ.mjs +0 -3
  158. package/dist/chunk-JHKY7OB7.mjs +0 -0
  159. package/dist/chunk-JIVW5KRY.mjs +0 -3
  160. package/dist/chunk-P55ODH2L.mjs +0 -4
  161. package/dist/chunk-PUQSKJHB.mjs +0 -4
  162. package/dist/chunk-Q67WUAF3.mjs +0 -2
  163. package/dist/chunk-ROSKTQWL.mjs +0 -1
  164. package/dist/chunk-VKROQSDU.mjs +0 -1
  165. package/dist/chunk-WJKG7QRD.mjs +0 -1
  166. package/dist/chunk-X5T5MXVQ.mjs +0 -1
  167. package/dist/entries-cache.css +0 -1
  168. package/dist/entries-cache.d.mts +0 -45
  169. package/dist/entries-cache.mjs +0 -1
  170. package/dist/masonry-measured-ready.d.mts +0 -8
  171. package/dist/masonry-measured-ready.mjs +0 -1
  172. package/dist/masonry-measured.css +0 -1
  173. package/dist/masonry-measured.d.mts +0 -31
  174. package/dist/masonry-measured.mjs +0 -1
  175. package/dist/masonry-miqLvs3X.d.mts +0 -52
  176. package/dist/skeleton-cache-base.css +0 -1
  177. package/dist/skeleton-cache-base.d.mts +0 -18
  178. package/dist/skeleton-cache-base.mjs +0 -1
  179. package/dist/skeleton-cache-grid.css +0 -1
  180. package/dist/skeleton-cache-grid.d.mts +0 -20
  181. package/dist/skeleton-cache-grid.mjs +0 -1
  182. package/dist/skeleton-cache-masonry-structured.css +0 -1
  183. package/dist/skeleton-cache-masonry-structured.d.mts +0 -19
  184. package/dist/skeleton-cache-masonry-structured.mjs +0 -1
  185. package/dist/skeleton-cache-masonry.css +0 -1
  186. package/dist/skeleton-cache-masonry.d.mts +0 -14
  187. package/dist/skeleton-cache-masonry.mjs +0 -1
  188. package/dist/skeleton-masonry-structured.css +0 -1
  189. package/dist/skeleton-masonry-structured.d.mts +0 -62
  190. package/dist/skeleton-masonry-structured.mjs +0 -1
  191. 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` | 928.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 |
@@ -202,15 +198,15 @@ Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse
202
198
  | `react-motion-gallery/masonry` | `Masonry`, `Masonry.Item`, masonry types |
203
199
  | `react-motion-gallery/masonry/ready` | `useMasonryReady` |
204
200
  | `react-motion-gallery/masonry/fullscreen` | `masonryFullscreen` for light Masonry + `GalleryCore` |
205
- | `react-motion-gallery/masonry/measured` | Measured `Masonry`, `Masonry.Item`, plugins/reveal/fullscreen types |
206
- | `react-motion-gallery/masonry/measured/ready` | `useMasonryReady` for measured masonry |
207
201
  | `react-motion-gallery/masonry/lazy-load` | `masonryLazyLoad` |
208
202
  | `react-motion-gallery/masonry/pagination` | `masonryPagination`, `useMasonryPagination`, `MasonryPaginationControls`, page range helpers |
209
203
  | `react-motion-gallery/masonry/load-more` | `masonryLoadMore`, `useMasonryLoadMore` |
210
204
  | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll`, `useMasonryInfiniteScroll` |
211
205
  | `react-motion-gallery/masonry/virtualization` | `masonryVirtualization`, `useMasonryVirtualizer` |
212
- | `react-motion-gallery/entries` | `Entries`, `flattenEntries`, entry layout/media container helpers |
213
- | `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` |
214
210
  | `react-motion-gallery/entries/ready` | `useEntriesReady` |
215
211
  | `react-motion-gallery/entries/pagination` | `entriesPagination`, `useEntriesPagination`, `EntriesPaginationControls`, page range helpers |
216
212
  | `react-motion-gallery/entries/load-more` | `entriesLoadMore`, `useEntriesLoadMore` |
@@ -220,14 +216,9 @@ Subpaths give bundlers a smaller graph than the root. Less JS to transfer, parse
220
216
  | `react-motion-gallery/skeleton/slider` | `SliderSkeleton` and slider skeleton authoring types |
221
217
  | `react-motion-gallery/skeleton/grid` | `GridSkeleton` and grid skeleton authoring types |
222
218
  | `react-motion-gallery/skeleton/masonry` | Lightweight `MasonrySkeleton` for dimensioned placeholders |
223
- | `react-motion-gallery/skeleton/masonry/structured` | Structured `MasonrySkeleton` and masonry skeleton authoring types |
224
219
  | `react-motion-gallery/skeleton/cache` | Server-safe skeleton cookie cache helpers and types |
225
220
  | `react-motion-gallery/skeleton/cache/provider` | Client `SkeletonCacheProvider` for SSR snapshots and client cookie refresh |
226
- | `react-motion-gallery/skeleton/cache/base` | `CachedSkeleton` with `cache` |
227
221
  | `react-motion-gallery/skeleton/cache/slider` | `CachedSliderSkeleton` with `cache` |
228
- | `react-motion-gallery/skeleton/cache/grid` | `CachedGridSkeleton` with `cache` |
229
- | `react-motion-gallery/skeleton/cache/masonry` | Lightweight `CachedMasonrySkeleton` |
230
- | `react-motion-gallery/skeleton/cache/masonry/structured` | Structured `CachedMasonrySkeleton` with `cache` |
231
222
  | `react-motion-gallery/skeleton/slider/restore` | `RestoredSliderSkeleton` with `restore` and optional `cache` |
232
223
  | `react-motion-gallery/fullscreen` | `useFullscreenController` and fullscreen types |
233
224
  | `react-motion-gallery/fullscreen/slider` | `fullscreenSlider` |
@@ -508,15 +499,28 @@ export function LoadingShell({ ready, children }: { ready: boolean; children: Re
508
499
 
509
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.
510
501
 
511
- 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`.
512
-
513
- Migration notes for cache and restore:
514
-
515
- - replace `react-motion-gallery/skeleton/base` imports with `react-motion-gallery/skeleton/cache/base` when passing `cache`
516
- - replace `react-motion-gallery/skeleton/slider` imports with `react-motion-gallery/skeleton/cache/slider` when passing only `cache`
517
- - replace `react-motion-gallery/skeleton/slider` imports with `react-motion-gallery/skeleton/slider/restore` when passing `restore`
518
- - replace `react-motion-gallery/skeleton/grid` and `/masonry` imports with their `skeleton/cache/*` counterparts when passing `cache`
519
- - 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. |
520
524
 
521
525
  ### Browser-measured skeleton text authoring
522
526
 
@@ -532,168 +536,43 @@ npm run --silent generate:skeleton-text-module -- \
532
536
 
533
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.
534
538
 
535
- ### Skeleton cookie snapshot cache
536
-
537
- 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.
538
-
539
- 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.
540
-
541
- What the cache stores:
542
-
543
- - cache version, cache key, scope id, route key, timestamp, viewport width, and active width bucket
544
- - measured skeleton text records keyed by `textId`: line count, per-line widths, and optional bar metrics
545
- - masonry-only active geometry: variant key, shell height, and item heights
546
- - slider-only restore state when enabled: active index, measured shell height, slide count, skeleton slot count, route key, scope id, viewport width, and timestamp
547
- - no text strings, no media URLs, and no full CSS text
548
-
549
- Benefits:
550
-
551
- - first visit remains unchanged and uses the full responsive skeleton behavior
552
- - later reloads can parse much less skeleton CSS for the active breakpoint
553
- - text-heavy masonry, grid, slider, entries, and standalone skeletons can keep layout stability while reducing first-paint CSS work
554
- - cache-backed slider restore can render the restored slot order and auto-height before the slider handle is ready
555
- - stale, expired, route-mismatched, scope-mismatched, or malformed cookies silently fall back to the normal responsive path
556
-
557
- 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.
558
-
559
- 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.
560
-
561
- ```typescript
562
- const cardBodySkeleton = {
563
- kind: "text",
564
- textId: "cardBody",
565
- barHeight: 14,
566
- lineHeight: 1.45,
567
- lines: { 0: 4, 900: 3, 1200: 2 },
568
- } as const;
569
-
570
- function CardBody({ children }: { children: React.ReactNode }) {
571
- return <p data-skeleton-text-id="cardBody">{children}</p>;
572
- }
573
- ```
574
-
575
- 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.
576
-
577
- ```tsx
578
- // app/gallery/page.tsx
579
- import { cookies } from "next/headers";
580
- import {
581
- parseSkeletonCacheCookie,
582
- type SkeletonCacheSnapshot,
583
- } from "react-motion-gallery/skeleton/cache";
584
- import { GalleryPageClient } from "./GalleryPageClient";
585
-
586
- function readSkeletonCacheSnapshots(
587
- cookieStore: Awaited<ReturnType<typeof cookies>>,
588
- ) {
589
- const snapshots: Record<string, SkeletonCacheSnapshot> = {};
590
-
591
- for (const cookie of cookieStore.getAll()) {
592
- if (!cookie.name.startsWith("rmg_skel_cache_")) continue;
593
-
594
- const snapshot = parseSkeletonCacheCookie(cookie.value);
595
- if (snapshot) snapshots[snapshot.key] = snapshot;
596
- }
597
-
598
- return snapshots;
599
- }
600
-
601
- export default async function GalleryPage() {
602
- const snapshotMap = readSkeletonCacheSnapshots(await cookies());
603
-
604
- return <GalleryPageClient skeletonCacheSnapshots={snapshotMap} />;
605
- }
606
- ```
607
-
608
- 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.
609
-
610
- ```tsx
611
- // app/gallery/GalleryPageClient.tsx
612
- "use client";
613
-
614
- import type { SkeletonCacheSnapshot } from "react-motion-gallery/skeleton/cache";
615
- import { SkeletonCacheProvider } from "react-motion-gallery/skeleton/cache/provider";
616
- import { CachedMasonrySkeleton as MasonrySkeleton } from "react-motion-gallery/skeleton/cache/masonry/structured";
617
- import { Masonry } from "react-motion-gallery/masonry/measured";
618
- import { useMasonryReady } from "react-motion-gallery/masonry/measured/ready";
619
-
620
- export function GalleryPageClient({
621
- skeletonCacheSnapshots,
622
- }: {
623
- skeletonCacheSnapshots: Record<
624
- string,
625
- SkeletonCacheSnapshot | null | undefined
626
- >;
627
- }) {
628
- const { ref, ready } = useMasonryReady();
629
-
630
- return (
631
- <SkeletonCacheProvider snapshots={skeletonCacheSnapshots}>
632
- <MasonrySkeleton
633
- cache={{
634
- key: "portfolio-masonry",
635
- routeKey: "/gallery",
636
- }}
637
- layout={portfolioSkeleton}
638
- ready={ready}
639
- masonry={{
640
- count: items.length,
641
- columns: { 0: 1, 720: 2, 1140: 4 },
642
- gap: { 0: 12, 1140: 18 },
643
- }}
644
- >
645
- <Masonry ref={ref} columns={{ 0: 1, 720: 2, 1140: 4 }}>
646
- {items.map((item) => (
647
- <Masonry.Item key={item.id}>{/* real card */}</Masonry.Item>
648
- ))}
649
- </Masonry>
650
- </MasonrySkeleton>
651
- </SkeletonCacheProvider>
652
- );
653
- }
654
- ```
655
-
656
- 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`.
657
-
658
- ```tsx
659
- import { CachedEntries as Entries } from "react-motion-gallery/entries/cache";
660
-
661
- <Entries
662
- entries={{
663
- items,
664
- mediaLayout: "grid",
665
- loading: {
666
- cache: {
667
- key: "editorial-entries",
668
- routeKey: "/stories",
669
- },
670
- skeleton: entrySkeleton,
671
- },
672
- }}
673
- />;
674
- ```
675
-
676
- Cookie options can be tuned per skeleton:
677
-
678
- ```tsx
679
- import { CachedGridSkeleton as GridSkeleton } from "react-motion-gallery/skeleton/cache/grid";
680
-
681
- <GridSkeleton
682
- cache={{
683
- key: "product-grid",
684
- routeKey: "/products",
685
- ttlMs: 5 * 60 * 1000,
686
- debounceMs: 150,
687
- cookie: {
688
- path: "/",
689
- sameSite: "lax",
690
- maxCookieBytes: 3000,
691
- maxTotalCookieBytes: 8000,
692
- },
693
- }}
694
- layout={productGridSkeleton}
695
- />;
696
- ```
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.
697
576
 
698
577
  ## Reveal
699
578
 
@@ -1366,6 +1245,7 @@ export function BasicGrid() {
1366
1245
  | ----------- | ------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------ |
1367
1246
  | `children` | `React.ReactNode` | `—` | The grid card content. |
1368
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. |
1369
1249
  | `className` | `string` | `—` | Extra class name merged onto the grid item wrapper. |
1370
1250
  | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the grid item wrapper. |
1371
1251
 
@@ -1381,7 +1261,7 @@ export function BasicGrid() {
1381
1261
  | `itemClassName` | `string` | `—` | Class name added to each wrapped grid item. |
1382
1262
  | `fullscreenTrigger` | `"item" \| "media"` | `"media"` | When `gridFullscreen()` is active, opens fullscreen from the clicked media node or the entire item shell. |
1383
1263
  | `plugins` | `GridPlugin[]` | `[]` | Explicit first-party Grid features such as lazy-load, fullscreen, pagination, load-more, infinite scroll, and virtualization. |
1384
- | `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. |
1385
1265
  | `reveal.staggerMs` | `number` | `60` | Reveal stagger for the fade-in. |
1386
1266
  | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1387
1267
  | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
@@ -1425,7 +1305,7 @@ function ProductGrid({ items, total }) {
1425
1305
 
1426
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.
1427
1307
 
1428
- 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.
1429
1309
 
1430
1310
  Wrap a card in `Grid.Item` when it should span tracks or needs wrapper styling:
1431
1311
 
@@ -1470,6 +1350,47 @@ Grid skeleton slots inherit real item spans by default. Slot overrides in the sk
1470
1350
 
1471
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.
1472
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
+
1473
1394
  ```typescript
1474
1395
  import { Grid } from "react-motion-gallery";
1475
1396
  import type { GridSkeletonSpec } from "react-motion-gallery/skeleton/grid";
@@ -1552,7 +1473,10 @@ export function BasicMasonry() {
1552
1473
  | `children` | `React.ReactNode` | `—` | The masonry card content. |
1553
1474
  | `width` | `number` | `—` | Intrinsic item width used for aspect-ratio layout. |
1554
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. |
1555
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. |
1556
1480
  | `className` | `string` | `—` | Extra class name merged onto the masonry item wrapper. |
1557
1481
  | `style` | `React.CSSProperties` | `—` | Inline styles merged onto the masonry item wrapper. |
1558
1482
 
@@ -1564,19 +1488,23 @@ export function BasicMasonry() {
1564
1488
  | `gap` | `number \| Record<string, number>` | `—` | Responsive gap between columns and items. |
1565
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. |
1566
1490
  | `as` | `React.ElementType` | `"div"` | Root HTML element or custom component. |
1567
- | `rootRef` | `React.Ref<HTMLDivElement>` | `—` | Ref to the masonry root. |
1491
+ | `rootRef` | `React.Ref<HTMLElement>` | `—` | Ref to the masonry root. |
1568
1492
  | `classNames.root` | `string` | `—` | Root class name. |
1569
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. |
1570
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. |
1571
1498
  | `reveal.staggerMs` | `number` | `160` | Reveal stagger for the fade-in. |
1572
1499
  | `reveal.durationMs` | `number` | `600` | Reveal fade duration. |
1573
1500
  | `reveal.easing` | `string` | `"cubic-bezier(.2,.7,.2,1)"` | Reveal fade easing. |
1501
+ | `reveal.staggerLimit` | `number` | `—` | Optional cap on how many items stagger. |
1574
1502
  | `reveal.disabled` | `boolean` | `false` | Disables the built-in reveal classes. |
1575
1503
  | `revealReady` | `boolean` | `true` | Holds the reveal until external loading or viewport orchestration is ready. |
1576
1504
 
1577
1505
  ### Masonry plugins
1578
1506
 
1579
- 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`.
1580
1508
 
1581
1509
  ```typescript
1582
1510
  import { GalleryCore } from "react-motion-gallery/core";
@@ -1609,15 +1537,13 @@ function ProductMasonry({ children, items }) {
1609
1537
  | `react-motion-gallery/masonry/pagination` | `masonryPagination(options)` | Windows child items by `pageIndex` and `pageSize`; `useMasonryPagination()` also returns page state and `MasonryPaginationControls`. |
1610
1538
  | `react-motion-gallery/masonry/load-more` | `masonryLoadMore(options)` | Windows child items by `visibleCount`; `useMasonryLoadMore()` owns and increments the visible count. |
1611
1539
  | `react-motion-gallery/masonry/infinite-scroll` | `masonryInfiniteScroll(options)` | Renders a sentinel after the masonry root so it does not disturb absolute item positioning. |
1612
- | `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. |
1613
1541
 
1614
- `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.
1615
1543
 
1616
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.
1617
1545
 
1618
- Measured Masonry accepts arbitrary React children, including text-containing JSX. The wrapper props are only for styling the built-in masonry item shell.
1619
-
1620
- 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:
1621
1547
 
1622
1548
  ```typescript
1623
1549
  <Masonry
@@ -1640,64 +1566,33 @@ Choose a placement based on what should feel stable:
1640
1566
  - `roundRobin`: best when deterministic column assignment matters more than tight packing.
1641
1567
  - `horizontalOrder`: best when wider cards should still read in a mostly left-to-right order.
1642
1568
 
1643
- 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.
1644
1570
 
1645
- 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`.
1646
1572
 
1647
1573
  Lightweight Masonry skeletons live in `react-motion-gallery/skeleton/masonry` and mirror dimensioned `Masonry.Item` data with `items`, `ratios`, or `heightsPx`.
1648
1574
 
1649
- 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.
1650
1576
 
1651
- 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.
1652
1578
 
1653
- `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.
1654
1580
 
1655
1581
  ```typescript
1656
- import { Masonry } from "react-motion-gallery/masonry/measured";
1657
- 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";
1658
1584
  import {
1659
1585
  MasonrySkeleton,
1660
- type MasonrySkeletonSpec,
1661
- } from "react-motion-gallery/skeleton/masonry/structured";
1586
+ type MasonrySkeletonProps,
1587
+ } from "react-motion-gallery/skeleton/masonry";
1662
1588
 
1663
- const masonrySkeleton: MasonrySkeletonSpec = {
1589
+ const masonrySkeleton: MasonrySkeletonProps = {
1664
1590
  ratios: [118, 126, 102, 146],
1665
- layout: {
1666
- kind: "masonry",
1667
- itemWrapStyle: {
1668
- padding: 14,
1669
- borderRadius: 20,
1670
- boxShadow: "0 18px 36px rgba(15, 23, 42, 0.08)",
1671
- },
1672
- item: {
1673
- kind: "col",
1674
- style: { gap: 12 },
1675
- children: [
1676
- {
1677
- kind: "rect",
1678
- style: { width: "100%", height: 180, borderRadius: 16 },
1679
- },
1680
- {
1681
- kind: "text",
1682
- barHeight: 14,
1683
- lineHeight: 1.55,
1684
- lines: 3,
1685
- lastBarWidth: "74%",
1686
- style: { width: "100%" },
1687
- },
1688
- ],
1689
- },
1690
- slots: [
1691
- {
1692
- ratio: 182,
1693
- span: { 0: 1, 1100: 2 },
1694
- item: {
1695
- kind: "rect",
1696
- style: { width: "100%", aspectRatio: "3 / 5", borderRadius: 16 },
1697
- },
1698
- },
1699
- ],
1700
- },
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,
1701
1596
  };
1702
1597
 
1703
1598
  function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
@@ -1705,15 +1600,10 @@ function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
1705
1600
 
1706
1601
  return (
1707
1602
  <MasonrySkeleton
1708
- layout={masonrySkeleton}
1603
+ {...masonrySkeleton}
1709
1604
  ready={masonryReady}
1710
1605
  timing={{ minVisibleMs: 220, exitMs: 600 }}
1711
- masonry={{
1712
- count: items.length,
1713
- columns: { 0: 1, 700: 2, 1100: 3 },
1714
- gap: { 0: 12, 1100: 20 },
1715
- placement: "balanced",
1716
- }}
1606
+ count={items.length}
1717
1607
  >
1718
1608
  <Masonry
1719
1609
  ref={masonryRef}
@@ -1731,6 +1621,52 @@ function MasonryWithSkeleton({ items }: { items: React.ReactNode[] }) {
1731
1621
  }
1732
1622
  ```
1733
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
+
1734
1670
  ## Entries
1735
1671
 
1736
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.
@@ -1839,9 +1775,9 @@ For common entry media layouts, the exported helper factories can own `renderMed
1839
1775
  ```typescript
1840
1776
  import {
1841
1777
  Entries,
1842
- createEntriesGridMedia,
1843
1778
  type EntriesOptions,
1844
1779
  } from "react-motion-gallery/entries";
1780
+ import { createEntriesGridMedia } from "react-motion-gallery/entries/media/grid";
1845
1781
 
1846
1782
  const renderEntryGridMedia = createEntriesGridMedia({
1847
1783
  gridObject: {
@@ -1863,7 +1799,7 @@ export function EntryGrid({ entries }: { entries: EntriesOptions["items"] }) {
1863
1799
  }
1864
1800
  ```
1865
1801
 
1866
- 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`.
1867
1803
 
1868
1804
  ### Entry loading, decode, and reveal flow
1869
1805
 
@@ -1911,7 +1847,6 @@ Fullscreen close has a matching entry-aware path. If the user closes fullscreen
1911
1847
  | `loading.enabled` | `boolean` | `—` | Enables entry loading and decode gating. |
1912
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`. |
1913
1849
  | `loading.skeleton` | `EntrySkeletonSpec \| ((args) => EntrySkeletonSpec \| null \| undefined)` | `—` | Built-in skeleton spec or resolver. |
1914
- | `loading.cache` | `SkeletonCacheOptions` | `—` | Opts entry skeleton text into the cookie snapshot cache. |
1915
1850
  | `loading.minHeight` | `number \| string` | `"260px"` | Minimum reserved height while loading. |
1916
1851
  | `loading.exitMs` | `number` | `220` | Entry skeleton opacity fade-out duration. |
1917
1852
  | `loading.nearMargin` | `string` | `"700px 0px"` | Preload margin used before entries enter view. |
@@ -1929,6 +1864,15 @@ Fullscreen close has a matching entry-aware path. If the user closes fullscreen
1929
1864
  | `entryList` | `ElementStyle` | `—` | Styles the entry list container. |
1930
1865
  | `entryRow` | `ElementStyle` | `—` | Styles each entry row container. |
1931
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
+
1932
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`.
1933
1877
 
1934
1878
  ### Entries data plugins
@@ -2190,7 +2134,7 @@ Virtualization mounts only the records near the viewport. It runs after paginati
2190
2134
  | `gap` | all | `number` | `24` | Vertical gap included in virtual range and spacer calculations. Values below zero clamp to zero. |
2191
2135
  | `overscan` | all | `number` | `3` | Extra rows or items to mount before and after the visible range. Values below zero clamp to zero. |
2192
2136
 
2193
- 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`.
2194
2138
 
2195
2139
  ## RatingStars
2196
2140