@opensite/ui 3.11.0 → 3.11.2

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.
@@ -423,6 +423,36 @@ var Section = React__namespace.default.forwardRef(
423
423
  Section.displayName = "Section";
424
424
  var IG_TILE_WIDTH = 320;
425
425
  var IG_TILE_STYLE = { width: "100%" };
426
+ var IG_TILE_RADIUS = 18;
427
+ var TILE_WRAPPER_STYLE = {
428
+ position: "relative",
429
+ borderRadius: IG_TILE_RADIUS,
430
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.10)"
431
+ };
432
+ var CAPTION_SCRIM_STYLE = {
433
+ position: "absolute",
434
+ left: 0,
435
+ right: 0,
436
+ bottom: 0,
437
+ padding: "28px 14px 13px",
438
+ background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
439
+ borderBottomLeftRadius: IG_TILE_RADIUS,
440
+ borderBottomRightRadius: IG_TILE_RADIUS,
441
+ pointerEvents: "none"
442
+ };
443
+ var CAPTION_TEXT_STYLE = {
444
+ color: "#fff",
445
+ fontSize: 13,
446
+ lineHeight: 1.4,
447
+ fontWeight: 600,
448
+ letterSpacing: "0.005em",
449
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.35)",
450
+ display: "-webkit-box",
451
+ WebkitLineClamp: 2,
452
+ WebkitBoxOrient: "vertical",
453
+ overflow: "hidden",
454
+ height: "2.8em"
455
+ };
426
456
  var TITLE_MAX = 90;
427
457
  function truncate(text, max = TITLE_MAX) {
428
458
  const trimmed = text.trim();
@@ -472,19 +502,23 @@ function likeBadge(likeCount) {
472
502
  style: {
473
503
  display: "inline-flex",
474
504
  alignItems: "center",
475
- gap: 4,
476
- padding: "3px 8px",
505
+ gap: 6,
506
+ // Breathing room from the tile's top-left corner (refinement #1).
507
+ margin: 6,
508
+ padding: "6px 11px",
477
509
  borderRadius: 999,
478
- background: "rgba(8,12,24,0.65)",
479
- backdropFilter: "blur(6px)",
480
- WebkitBackdropFilter: "blur(6px)",
510
+ background: "rgba(8, 12, 24, 0.55)",
511
+ backdropFilter: "blur(8px)",
512
+ WebkitBackdropFilter: "blur(8px)",
513
+ border: "1px solid rgba(255, 255, 255, 0.14)",
481
514
  color: "#fff",
482
- fontSize: 11,
483
- fontWeight: 700,
484
- lineHeight: 1
515
+ fontSize: 13,
516
+ fontWeight: 600,
517
+ lineHeight: 1,
518
+ letterSpacing: "0.01em"
485
519
  },
486
520
  children: [
487
- /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/heart", size: 13, "aria-hidden": "true" }),
521
+ /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/heart", size: 15, "aria-hidden": "true" }),
488
522
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
489
523
  ]
490
524
  }
@@ -666,26 +700,39 @@ function InstagramFeedGrid({
666
700
  "div",
667
701
  {
668
702
  className: cn(
669
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
703
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
670
704
  gridClassName
671
705
  ),
672
706
  children: mediaItems.map((mediaItem) => {
673
707
  const likeCount = mediaItem.meta?.likeCount;
674
- return /* @__PURE__ */ jsxRuntime.jsx(
675
- mediaImmersive.ThumbnailCard,
676
- {
677
- item: mediaItem,
678
- onOpen: open,
679
- size: IG_TILE_WIDTH,
680
- style: IG_TILE_STYLE,
681
- className: itemClassName,
682
- elevated: false,
683
- showDuration: false,
684
- glyphMode: "hover",
685
- posterImgProps,
686
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
687
- },
688
- mediaItem.id
708
+ return (
709
+ // The wrapper owns the tile's outer chrome (subtle boundary shadow,
710
+ // matching radius) and the block's own caption overlay — the card's
711
+ // built-in caption is hidden so 1- and 2-line captions can share a
712
+ // fixed-height, top-aligned text box (annotated refinements #2–#4).
713
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClassName, style: TILE_WRAPPER_STYLE, children: [
714
+ /* @__PURE__ */ jsxRuntime.jsx(
715
+ mediaImmersive.ThumbnailCard,
716
+ {
717
+ item: mediaItem,
718
+ onOpen: open,
719
+ size: IG_TILE_WIDTH,
720
+ style: IG_TILE_STYLE,
721
+ elevated: false,
722
+ hideCaption: true,
723
+ hideProgressHint: true,
724
+ showDuration: false,
725
+ glyphMode: "hover",
726
+ posterImgProps,
727
+ badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
728
+ }
729
+ ),
730
+ mediaItem.title ? (
731
+ // aria-hidden: the card already announces the post title; this
732
+ // overlay is purely visual. pointer-events pass through to the card.
733
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: CAPTION_SCRIM_STYLE, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: CAPTION_TEXT_STYLE, children: mediaItem.title }) })
734
+ ) : null
735
+ ] }, mediaItem.id)
689
736
  );
690
737
  })
691
738
  }
@@ -698,7 +745,8 @@ function InstagramPostGrid({
698
745
  items,
699
746
  itemsSlot,
700
747
  className,
701
- containerClassName,
748
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
749
+ spacing = "py-12 md:py-32",
702
750
  headerClassName,
703
751
  headingClassName,
704
752
  subheadingClassName,
@@ -707,14 +755,21 @@ function InstagramPostGrid({
707
755
  imageClassName,
708
756
  optixFlowConfig,
709
757
  background,
710
- spacing,
711
758
  pattern,
712
759
  patternOpacity,
713
760
  patternClassName
714
761
  }) {
715
762
  const mediaItems = React.useMemo(() => {
716
763
  if (itemsSlot || !items) return [];
717
- return items.filter((item) => Boolean(item.image)).map(toMediaItem);
764
+ const withImage = items.filter((item) => Boolean(item.image));
765
+ if (process.env.NODE_ENV !== "production" && withImage.length < items.length) {
766
+ const skipped = items.filter((item) => !item.image);
767
+ const skippedIds = skipped.map((item) => item.id).join(", ");
768
+ console.warn(
769
+ `[instagram-post-grid] skipped ${skipped.length} item(s) without a resolvable image: ${skippedIds}`
770
+ );
771
+ }
772
+ return withImage.map(toMediaItem);
718
773
  }, [items, itemsSlot]);
719
774
  if (!itemsSlot && (!items || items.length === 0)) {
720
775
  return null;
@@ -757,7 +812,7 @@ function InstagramPostGrid({
757
812
  "div",
758
813
  {
759
814
  className: cn(
760
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
815
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
761
816
  gridClassName
762
817
  ),
763
818
  children: itemsSlot
@@ -192,6 +192,6 @@ interface InstagramPostGridProps {
192
192
  * />
193
193
  * ```
194
194
  */
195
- declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, background, spacing, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
195
+ declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, spacing, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, background, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
196
196
 
197
197
  export { InstagramPostGrid, type InstagramPostGridProps, type InstagramPostItem };
@@ -192,6 +192,6 @@ interface InstagramPostGridProps {
192
192
  * />
193
193
  * ```
194
194
  */
195
- declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, background, spacing, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
195
+ declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, spacing, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, background, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
196
196
 
197
197
  export { InstagramPostGrid, type InstagramPostGridProps, type InstagramPostItem };
@@ -402,6 +402,36 @@ var Section = React__default.forwardRef(
402
402
  Section.displayName = "Section";
403
403
  var IG_TILE_WIDTH = 320;
404
404
  var IG_TILE_STYLE = { width: "100%" };
405
+ var IG_TILE_RADIUS = 18;
406
+ var TILE_WRAPPER_STYLE = {
407
+ position: "relative",
408
+ borderRadius: IG_TILE_RADIUS,
409
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.10)"
410
+ };
411
+ var CAPTION_SCRIM_STYLE = {
412
+ position: "absolute",
413
+ left: 0,
414
+ right: 0,
415
+ bottom: 0,
416
+ padding: "28px 14px 13px",
417
+ background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
418
+ borderBottomLeftRadius: IG_TILE_RADIUS,
419
+ borderBottomRightRadius: IG_TILE_RADIUS,
420
+ pointerEvents: "none"
421
+ };
422
+ var CAPTION_TEXT_STYLE = {
423
+ color: "#fff",
424
+ fontSize: 13,
425
+ lineHeight: 1.4,
426
+ fontWeight: 600,
427
+ letterSpacing: "0.005em",
428
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.35)",
429
+ display: "-webkit-box",
430
+ WebkitLineClamp: 2,
431
+ WebkitBoxOrient: "vertical",
432
+ overflow: "hidden",
433
+ height: "2.8em"
434
+ };
405
435
  var TITLE_MAX = 90;
406
436
  function truncate(text, max = TITLE_MAX) {
407
437
  const trimmed = text.trim();
@@ -451,19 +481,23 @@ function likeBadge(likeCount) {
451
481
  style: {
452
482
  display: "inline-flex",
453
483
  alignItems: "center",
454
- gap: 4,
455
- padding: "3px 8px",
484
+ gap: 6,
485
+ // Breathing room from the tile's top-left corner (refinement #1).
486
+ margin: 6,
487
+ padding: "6px 11px",
456
488
  borderRadius: 999,
457
- background: "rgba(8,12,24,0.65)",
458
- backdropFilter: "blur(6px)",
459
- WebkitBackdropFilter: "blur(6px)",
489
+ background: "rgba(8, 12, 24, 0.55)",
490
+ backdropFilter: "blur(8px)",
491
+ WebkitBackdropFilter: "blur(8px)",
492
+ border: "1px solid rgba(255, 255, 255, 0.14)",
460
493
  color: "#fff",
461
- fontSize: 11,
462
- fontWeight: 700,
463
- lineHeight: 1
494
+ fontSize: 13,
495
+ fontWeight: 600,
496
+ lineHeight: 1,
497
+ letterSpacing: "0.01em"
464
498
  },
465
499
  children: [
466
- /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/heart", size: 13, "aria-hidden": "true" }),
500
+ /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/heart", size: 15, "aria-hidden": "true" }),
467
501
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
468
502
  ]
469
503
  }
@@ -645,26 +679,39 @@ function InstagramFeedGrid({
645
679
  "div",
646
680
  {
647
681
  className: cn(
648
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
682
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
649
683
  gridClassName
650
684
  ),
651
685
  children: mediaItems.map((mediaItem) => {
652
686
  const likeCount = mediaItem.meta?.likeCount;
653
- return /* @__PURE__ */ jsx(
654
- ThumbnailCard,
655
- {
656
- item: mediaItem,
657
- onOpen: open,
658
- size: IG_TILE_WIDTH,
659
- style: IG_TILE_STYLE,
660
- className: itemClassName,
661
- elevated: false,
662
- showDuration: false,
663
- glyphMode: "hover",
664
- posterImgProps,
665
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
666
- },
667
- mediaItem.id
687
+ return (
688
+ // The wrapper owns the tile's outer chrome (subtle boundary shadow,
689
+ // matching radius) and the block's own caption overlay — the card's
690
+ // built-in caption is hidden so 1- and 2-line captions can share a
691
+ // fixed-height, top-aligned text box (annotated refinements #2–#4).
692
+ /* @__PURE__ */ jsxs("div", { className: itemClassName, style: TILE_WRAPPER_STYLE, children: [
693
+ /* @__PURE__ */ jsx(
694
+ ThumbnailCard,
695
+ {
696
+ item: mediaItem,
697
+ onOpen: open,
698
+ size: IG_TILE_WIDTH,
699
+ style: IG_TILE_STYLE,
700
+ elevated: false,
701
+ hideCaption: true,
702
+ hideProgressHint: true,
703
+ showDuration: false,
704
+ glyphMode: "hover",
705
+ posterImgProps,
706
+ badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
707
+ }
708
+ ),
709
+ mediaItem.title ? (
710
+ // aria-hidden: the card already announces the post title; this
711
+ // overlay is purely visual. pointer-events pass through to the card.
712
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: CAPTION_SCRIM_STYLE, children: /* @__PURE__ */ jsx("div", { style: CAPTION_TEXT_STYLE, children: mediaItem.title }) })
713
+ ) : null
714
+ ] }, mediaItem.id)
668
715
  );
669
716
  })
670
717
  }
@@ -677,7 +724,8 @@ function InstagramPostGrid({
677
724
  items,
678
725
  itemsSlot,
679
726
  className,
680
- containerClassName,
727
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
728
+ spacing = "py-12 md:py-32",
681
729
  headerClassName,
682
730
  headingClassName,
683
731
  subheadingClassName,
@@ -686,14 +734,21 @@ function InstagramPostGrid({
686
734
  imageClassName,
687
735
  optixFlowConfig,
688
736
  background,
689
- spacing,
690
737
  pattern,
691
738
  patternOpacity,
692
739
  patternClassName
693
740
  }) {
694
741
  const mediaItems = useMemo(() => {
695
742
  if (itemsSlot || !items) return [];
696
- return items.filter((item) => Boolean(item.image)).map(toMediaItem);
743
+ const withImage = items.filter((item) => Boolean(item.image));
744
+ if (process.env.NODE_ENV !== "production" && withImage.length < items.length) {
745
+ const skipped = items.filter((item) => !item.image);
746
+ const skippedIds = skipped.map((item) => item.id).join(", ");
747
+ console.warn(
748
+ `[instagram-post-grid] skipped ${skipped.length} item(s) without a resolvable image: ${skippedIds}`
749
+ );
750
+ }
751
+ return withImage.map(toMediaItem);
697
752
  }, [items, itemsSlot]);
698
753
  if (!itemsSlot && (!items || items.length === 0)) {
699
754
  return null;
@@ -736,7 +791,7 @@ function InstagramPostGrid({
736
791
  "div",
737
792
  {
738
793
  className: cn(
739
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
794
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
740
795
  gridClassName
741
796
  ),
742
797
  children: itemsSlot
package/dist/registry.cjs CHANGED
@@ -35274,6 +35274,36 @@ function InteriorCarousel({
35274
35274
  }
35275
35275
  var IG_TILE_WIDTH = 320;
35276
35276
  var IG_TILE_STYLE = { width: "100%" };
35277
+ var IG_TILE_RADIUS = 18;
35278
+ var TILE_WRAPPER_STYLE = {
35279
+ position: "relative",
35280
+ borderRadius: IG_TILE_RADIUS,
35281
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.10)"
35282
+ };
35283
+ var CAPTION_SCRIM_STYLE = {
35284
+ position: "absolute",
35285
+ left: 0,
35286
+ right: 0,
35287
+ bottom: 0,
35288
+ padding: "28px 14px 13px",
35289
+ background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
35290
+ borderBottomLeftRadius: IG_TILE_RADIUS,
35291
+ borderBottomRightRadius: IG_TILE_RADIUS,
35292
+ pointerEvents: "none"
35293
+ };
35294
+ var CAPTION_TEXT_STYLE = {
35295
+ color: "#fff",
35296
+ fontSize: 13,
35297
+ lineHeight: 1.4,
35298
+ fontWeight: 600,
35299
+ letterSpacing: "0.005em",
35300
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.35)",
35301
+ display: "-webkit-box",
35302
+ WebkitLineClamp: 2,
35303
+ WebkitBoxOrient: "vertical",
35304
+ overflow: "hidden",
35305
+ height: "2.8em"
35306
+ };
35277
35307
  var TITLE_MAX = 90;
35278
35308
  function truncate(text, max = TITLE_MAX) {
35279
35309
  const trimmed = text.trim();
@@ -35323,19 +35353,23 @@ function likeBadge(likeCount) {
35323
35353
  style: {
35324
35354
  display: "inline-flex",
35325
35355
  alignItems: "center",
35326
- gap: 4,
35327
- padding: "3px 8px",
35356
+ gap: 6,
35357
+ // Breathing room from the tile's top-left corner (refinement #1).
35358
+ margin: 6,
35359
+ padding: "6px 11px",
35328
35360
  borderRadius: 999,
35329
- background: "rgba(8,12,24,0.65)",
35330
- backdropFilter: "blur(6px)",
35331
- WebkitBackdropFilter: "blur(6px)",
35361
+ background: "rgba(8, 12, 24, 0.55)",
35362
+ backdropFilter: "blur(8px)",
35363
+ WebkitBackdropFilter: "blur(8px)",
35364
+ border: "1px solid rgba(255, 255, 255, 0.14)",
35332
35365
  color: "#fff",
35333
- fontSize: 11,
35334
- fontWeight: 700,
35335
- lineHeight: 1
35366
+ fontSize: 13,
35367
+ fontWeight: 600,
35368
+ lineHeight: 1,
35369
+ letterSpacing: "0.01em"
35336
35370
  },
35337
35371
  children: [
35338
- /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/heart", size: 13, "aria-hidden": "true" }),
35372
+ /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/heart", size: 15, "aria-hidden": "true" }),
35339
35373
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
35340
35374
  ]
35341
35375
  }
@@ -35517,26 +35551,39 @@ function InstagramFeedGrid({
35517
35551
  "div",
35518
35552
  {
35519
35553
  className: cn(
35520
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
35554
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
35521
35555
  gridClassName
35522
35556
  ),
35523
35557
  children: mediaItems.map((mediaItem) => {
35524
35558
  const likeCount = mediaItem.meta?.likeCount;
35525
- return /* @__PURE__ */ jsxRuntime.jsx(
35526
- mediaImmersive.ThumbnailCard,
35527
- {
35528
- item: mediaItem,
35529
- onOpen: open,
35530
- size: IG_TILE_WIDTH,
35531
- style: IG_TILE_STYLE,
35532
- className: itemClassName,
35533
- elevated: false,
35534
- showDuration: false,
35535
- glyphMode: "hover",
35536
- posterImgProps,
35537
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
35538
- },
35539
- mediaItem.id
35559
+ return (
35560
+ // The wrapper owns the tile's outer chrome (subtle boundary shadow,
35561
+ // matching radius) and the block's own caption overlay — the card's
35562
+ // built-in caption is hidden so 1- and 2-line captions can share a
35563
+ // fixed-height, top-aligned text box (annotated refinements #2–#4).
35564
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClassName, style: TILE_WRAPPER_STYLE, children: [
35565
+ /* @__PURE__ */ jsxRuntime.jsx(
35566
+ mediaImmersive.ThumbnailCard,
35567
+ {
35568
+ item: mediaItem,
35569
+ onOpen: open,
35570
+ size: IG_TILE_WIDTH,
35571
+ style: IG_TILE_STYLE,
35572
+ elevated: false,
35573
+ hideCaption: true,
35574
+ hideProgressHint: true,
35575
+ showDuration: false,
35576
+ glyphMode: "hover",
35577
+ posterImgProps,
35578
+ badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
35579
+ }
35580
+ ),
35581
+ mediaItem.title ? (
35582
+ // aria-hidden: the card already announces the post title; this
35583
+ // overlay is purely visual. pointer-events pass through to the card.
35584
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: CAPTION_SCRIM_STYLE, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: CAPTION_TEXT_STYLE, children: mediaItem.title }) })
35585
+ ) : null
35586
+ ] }, mediaItem.id)
35540
35587
  );
35541
35588
  })
35542
35589
  }
@@ -35549,7 +35596,8 @@ function InstagramPostGrid({
35549
35596
  items,
35550
35597
  itemsSlot,
35551
35598
  className,
35552
- containerClassName,
35599
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
35600
+ spacing = "py-12 md:py-32",
35553
35601
  headerClassName,
35554
35602
  headingClassName,
35555
35603
  subheadingClassName,
@@ -35558,14 +35606,21 @@ function InstagramPostGrid({
35558
35606
  imageClassName,
35559
35607
  optixFlowConfig,
35560
35608
  background,
35561
- spacing,
35562
35609
  pattern,
35563
35610
  patternOpacity,
35564
35611
  patternClassName
35565
35612
  }) {
35566
35613
  const mediaItems = React30.useMemo(() => {
35567
35614
  if (itemsSlot || !items) return [];
35568
- return items.filter((item) => Boolean(item.image)).map(toMediaItem);
35615
+ const withImage = items.filter((item) => Boolean(item.image));
35616
+ if (process.env.NODE_ENV !== "production" && withImage.length < items.length) {
35617
+ const skipped = items.filter((item) => !item.image);
35618
+ const skippedIds = skipped.map((item) => item.id).join(", ");
35619
+ console.warn(
35620
+ `[instagram-post-grid] skipped ${skipped.length} item(s) without a resolvable image: ${skippedIds}`
35621
+ );
35622
+ }
35623
+ return withImage.map(toMediaItem);
35569
35624
  }, [items, itemsSlot]);
35570
35625
  if (!itemsSlot && (!items || items.length === 0)) {
35571
35626
  return null;
@@ -35608,7 +35663,7 @@ function InstagramPostGrid({
35608
35663
  "div",
35609
35664
  {
35610
35665
  className: cn(
35611
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
35666
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
35612
35667
  gridClassName
35613
35668
  ),
35614
35669
  children: itemsSlot
@@ -111521,9 +111576,12 @@ var instagramPostGrid = {
111521
111576
  "media_library"
111522
111577
  ),
111523
111578
  propConstraints: {
111579
+ heading: { maxLength: 60 },
111524
111580
  items: {
111525
- minItems: 3,
111526
- maxItems: 24
111581
+ required: true,
111582
+ minItems: 4,
111583
+ maxItems: 16,
111584
+ note: "Use multiples of the 4-column desktop grid (8 or 12) so rows fill cleanly. Every item must carry a re-hosted CDN image URL \u2014 imageless items are skipped at render (dev builds warn)."
111527
111585
  }
111528
111586
  },
111529
111587
  mediaSlots: {
@@ -111549,33 +111607,90 @@ var instagramPostGrid = {
111549
111607
  id: "1",
111550
111608
  href: "https://www.instagram.com/p/CxAmpLe001/",
111551
111609
  image: GALLERY_EXAMPLE_IMAGE_URL,
111552
- imageAlt: "Espresso being poured at the counter",
111610
+ imageAlt: "Espresso being pulled at the counter",
111553
111611
  caption: "Morning pours before the rush \u2615\uFE0F",
111554
- date: "Jul 1, 2026",
111555
- likeCount: 128,
111556
- commentCount: 12
111612
+ date: "Jul 6, 2026",
111613
+ likeCount: 428,
111614
+ commentCount: 34
111557
111615
  },
111558
111616
  {
111559
111617
  id: "2",
111560
111618
  href: "https://www.instagram.com/p/CxAmpLe002/",
111561
- image: GALLERY_EXAMPLE_IMAGE_URL,
111562
- imageAlt: "Behind the scenes reel",
111563
- caption: "Behind the scenes of today's bake #freshbread",
111564
- date: "Jun 28, 2026",
111619
+ image: "https://toastability-production.s3.amazonaws.com/z9u4sdrj2oq3eds0qyui0nxsus3j",
111620
+ imageAlt: "Line cook plating on a busy Friday service",
111621
+ caption: "Behind the pass on a Friday night service \u{1F525}",
111622
+ date: "Jul 4, 2026",
111565
111623
  isVideo: true,
111566
- videoUrl: "https://toastability-production.s3.amazonaws.com/4kox2ux0ye1wlqkdwg03s08a67i1",
111567
- likeCount: 342,
111568
- commentCount: 21,
111569
- viewCount: 4820
111624
+ videoUrl: "https://toastability-production.s3.amazonaws.com/b555hwjt7ltr81et05v5254q1ak6",
111625
+ likeCount: 1203,
111626
+ commentCount: 88,
111627
+ viewCount: 12840
111570
111628
  },
111571
111629
  {
111572
111630
  id: "3",
111573
111631
  href: "https://www.instagram.com/p/CxAmpLe003/",
111632
+ image: "https://toastability-production.s3.amazonaws.com/63aotyt2pb4gqpccej2kkw8reson",
111633
+ imageAlt: "House pancakes with maple butter",
111634
+ caption: "Weekend brunch is back \u2014 house pancakes + maple butter",
111635
+ date: "Jul 2, 2026",
111636
+ likeCount: 356,
111637
+ commentCount: 27
111638
+ },
111639
+ {
111640
+ id: "4",
111641
+ href: "https://www.instagram.com/p/CxAmpLe004/",
111642
+ image: "https://toastability-production.s3.amazonaws.com/we9r4e711an6d0bd3dwbl9tb9z7q",
111643
+ imageAlt: "Barista finishing latte art",
111644
+ caption: "60 seconds of latte art, no cuts \u2728",
111645
+ date: "Jun 29, 2026",
111646
+ isVideo: true,
111647
+ videoUrl: "https://toastability-production.s3.amazonaws.com/dvz0441h9fxjhh88lzqbwdoyxv52",
111648
+ likeCount: 942,
111649
+ commentCount: 61,
111650
+ viewCount: 8675
111651
+ },
111652
+ {
111653
+ id: "5",
111654
+ href: "https://www.instagram.com/p/CxAmpLe005/",
111574
111655
  image: GALLERY_EXAMPLE_IMAGE_URL,
111575
- imageAlt: "Weekend brunch spread",
111576
- caption: "Weekend brunch is back on the menu",
111656
+ imageAlt: "Fresh sourdough loaves out of the oven",
111657
+ caption: "Fresh sourdough out of the oven at 6am \u{1F956}",
111658
+ date: "Jun 27, 2026",
111659
+ likeCount: 512,
111660
+ commentCount: 41
111661
+ },
111662
+ {
111663
+ id: "6",
111664
+ href: "https://www.instagram.com/p/CxAmpLe006/",
111665
+ image: "https://toastability-production.s3.amazonaws.com/c4sgsy0g7o2rrjmvm9x7evxems82",
111666
+ imageAlt: "Chef plating a summer heirloom tomato salad",
111667
+ caption: "Plating the summer heirloom tomato salad \u{1F345}",
111577
111668
  date: "Jun 24, 2026",
111578
- likeCount: 96
111669
+ isVideo: true,
111670
+ videoUrl: "https://toastability-production.s3.amazonaws.com/jhjfvkmdzktacyijd9fh6acc7o2c",
111671
+ likeCount: 731,
111672
+ commentCount: 45,
111673
+ viewCount: 6390
111674
+ },
111675
+ {
111676
+ id: "7",
111677
+ href: "https://www.instagram.com/p/CxAmpLe007/",
111678
+ image: "https://toastability-production.s3.amazonaws.com/kh1p8y15v55ctp5ulobm4pd77etm",
111679
+ imageAlt: "Sunny patio seating with fresh flowers",
111680
+ caption: "Patio season is officially open \u2600\uFE0F",
111681
+ date: "Jun 21, 2026",
111682
+ likeCount: 289,
111683
+ commentCount: 19
111684
+ },
111685
+ {
111686
+ id: "8",
111687
+ href: "https://www.instagram.com/p/CxAmpLe008/",
111688
+ image: "https://toastability-production.s3.amazonaws.com/2d4k8d5shwg82276hzj2ztbj7mxq",
111689
+ imageAlt: "Charred corn elote on a plate",
111690
+ caption: "New on the menu: charred corn elote, our way \u{1F33D}",
111691
+ date: "Jun 18, 2026",
111692
+ likeCount: 634,
111693
+ commentCount: 52
111579
111694
  }
111580
111695
  ]
111581
111696
  }
package/dist/registry.js CHANGED
@@ -35234,6 +35234,36 @@ function InteriorCarousel({
35234
35234
  }
35235
35235
  var IG_TILE_WIDTH = 320;
35236
35236
  var IG_TILE_STYLE = { width: "100%" };
35237
+ var IG_TILE_RADIUS = 18;
35238
+ var TILE_WRAPPER_STYLE = {
35239
+ position: "relative",
35240
+ borderRadius: IG_TILE_RADIUS,
35241
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.10)"
35242
+ };
35243
+ var CAPTION_SCRIM_STYLE = {
35244
+ position: "absolute",
35245
+ left: 0,
35246
+ right: 0,
35247
+ bottom: 0,
35248
+ padding: "28px 14px 13px",
35249
+ background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
35250
+ borderBottomLeftRadius: IG_TILE_RADIUS,
35251
+ borderBottomRightRadius: IG_TILE_RADIUS,
35252
+ pointerEvents: "none"
35253
+ };
35254
+ var CAPTION_TEXT_STYLE = {
35255
+ color: "#fff",
35256
+ fontSize: 13,
35257
+ lineHeight: 1.4,
35258
+ fontWeight: 600,
35259
+ letterSpacing: "0.005em",
35260
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.35)",
35261
+ display: "-webkit-box",
35262
+ WebkitLineClamp: 2,
35263
+ WebkitBoxOrient: "vertical",
35264
+ overflow: "hidden",
35265
+ height: "2.8em"
35266
+ };
35237
35267
  var TITLE_MAX = 90;
35238
35268
  function truncate(text, max = TITLE_MAX) {
35239
35269
  const trimmed = text.trim();
@@ -35283,19 +35313,23 @@ function likeBadge(likeCount) {
35283
35313
  style: {
35284
35314
  display: "inline-flex",
35285
35315
  alignItems: "center",
35286
- gap: 4,
35287
- padding: "3px 8px",
35316
+ gap: 6,
35317
+ // Breathing room from the tile's top-left corner (refinement #1).
35318
+ margin: 6,
35319
+ padding: "6px 11px",
35288
35320
  borderRadius: 999,
35289
- background: "rgba(8,12,24,0.65)",
35290
- backdropFilter: "blur(6px)",
35291
- WebkitBackdropFilter: "blur(6px)",
35321
+ background: "rgba(8, 12, 24, 0.55)",
35322
+ backdropFilter: "blur(8px)",
35323
+ WebkitBackdropFilter: "blur(8px)",
35324
+ border: "1px solid rgba(255, 255, 255, 0.14)",
35292
35325
  color: "#fff",
35293
- fontSize: 11,
35294
- fontWeight: 700,
35295
- lineHeight: 1
35326
+ fontSize: 13,
35327
+ fontWeight: 600,
35328
+ lineHeight: 1,
35329
+ letterSpacing: "0.01em"
35296
35330
  },
35297
35331
  children: [
35298
- /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/heart", size: 13, "aria-hidden": "true" }),
35332
+ /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/heart", size: 15, "aria-hidden": "true" }),
35299
35333
  /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
35300
35334
  ]
35301
35335
  }
@@ -35477,26 +35511,39 @@ function InstagramFeedGrid({
35477
35511
  "div",
35478
35512
  {
35479
35513
  className: cn(
35480
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
35514
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
35481
35515
  gridClassName
35482
35516
  ),
35483
35517
  children: mediaItems.map((mediaItem) => {
35484
35518
  const likeCount = mediaItem.meta?.likeCount;
35485
- return /* @__PURE__ */ jsx(
35486
- ThumbnailCard,
35487
- {
35488
- item: mediaItem,
35489
- onOpen: open,
35490
- size: IG_TILE_WIDTH,
35491
- style: IG_TILE_STYLE,
35492
- className: itemClassName,
35493
- elevated: false,
35494
- showDuration: false,
35495
- glyphMode: "hover",
35496
- posterImgProps,
35497
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
35498
- },
35499
- mediaItem.id
35519
+ return (
35520
+ // The wrapper owns the tile's outer chrome (subtle boundary shadow,
35521
+ // matching radius) and the block's own caption overlay — the card's
35522
+ // built-in caption is hidden so 1- and 2-line captions can share a
35523
+ // fixed-height, top-aligned text box (annotated refinements #2–#4).
35524
+ /* @__PURE__ */ jsxs("div", { className: itemClassName, style: TILE_WRAPPER_STYLE, children: [
35525
+ /* @__PURE__ */ jsx(
35526
+ ThumbnailCard,
35527
+ {
35528
+ item: mediaItem,
35529
+ onOpen: open,
35530
+ size: IG_TILE_WIDTH,
35531
+ style: IG_TILE_STYLE,
35532
+ elevated: false,
35533
+ hideCaption: true,
35534
+ hideProgressHint: true,
35535
+ showDuration: false,
35536
+ glyphMode: "hover",
35537
+ posterImgProps,
35538
+ badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
35539
+ }
35540
+ ),
35541
+ mediaItem.title ? (
35542
+ // aria-hidden: the card already announces the post title; this
35543
+ // overlay is purely visual. pointer-events pass through to the card.
35544
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: CAPTION_SCRIM_STYLE, children: /* @__PURE__ */ jsx("div", { style: CAPTION_TEXT_STYLE, children: mediaItem.title }) })
35545
+ ) : null
35546
+ ] }, mediaItem.id)
35500
35547
  );
35501
35548
  })
35502
35549
  }
@@ -35509,7 +35556,8 @@ function InstagramPostGrid({
35509
35556
  items,
35510
35557
  itemsSlot,
35511
35558
  className,
35512
- containerClassName,
35559
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
35560
+ spacing = "py-12 md:py-32",
35513
35561
  headerClassName,
35514
35562
  headingClassName,
35515
35563
  subheadingClassName,
@@ -35518,14 +35566,21 @@ function InstagramPostGrid({
35518
35566
  imageClassName,
35519
35567
  optixFlowConfig,
35520
35568
  background,
35521
- spacing,
35522
35569
  pattern,
35523
35570
  patternOpacity,
35524
35571
  patternClassName
35525
35572
  }) {
35526
35573
  const mediaItems = useMemo(() => {
35527
35574
  if (itemsSlot || !items) return [];
35528
- return items.filter((item) => Boolean(item.image)).map(toMediaItem);
35575
+ const withImage = items.filter((item) => Boolean(item.image));
35576
+ if (process.env.NODE_ENV !== "production" && withImage.length < items.length) {
35577
+ const skipped = items.filter((item) => !item.image);
35578
+ const skippedIds = skipped.map((item) => item.id).join(", ");
35579
+ console.warn(
35580
+ `[instagram-post-grid] skipped ${skipped.length} item(s) without a resolvable image: ${skippedIds}`
35581
+ );
35582
+ }
35583
+ return withImage.map(toMediaItem);
35529
35584
  }, [items, itemsSlot]);
35530
35585
  if (!itemsSlot && (!items || items.length === 0)) {
35531
35586
  return null;
@@ -35568,7 +35623,7 @@ function InstagramPostGrid({
35568
35623
  "div",
35569
35624
  {
35570
35625
  className: cn(
35571
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
35626
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
35572
35627
  gridClassName
35573
35628
  ),
35574
35629
  children: itemsSlot
@@ -111481,9 +111536,12 @@ var instagramPostGrid = {
111481
111536
  "media_library"
111482
111537
  ),
111483
111538
  propConstraints: {
111539
+ heading: { maxLength: 60 },
111484
111540
  items: {
111485
- minItems: 3,
111486
- maxItems: 24
111541
+ required: true,
111542
+ minItems: 4,
111543
+ maxItems: 16,
111544
+ note: "Use multiples of the 4-column desktop grid (8 or 12) so rows fill cleanly. Every item must carry a re-hosted CDN image URL \u2014 imageless items are skipped at render (dev builds warn)."
111487
111545
  }
111488
111546
  },
111489
111547
  mediaSlots: {
@@ -111509,33 +111567,90 @@ var instagramPostGrid = {
111509
111567
  id: "1",
111510
111568
  href: "https://www.instagram.com/p/CxAmpLe001/",
111511
111569
  image: GALLERY_EXAMPLE_IMAGE_URL,
111512
- imageAlt: "Espresso being poured at the counter",
111570
+ imageAlt: "Espresso being pulled at the counter",
111513
111571
  caption: "Morning pours before the rush \u2615\uFE0F",
111514
- date: "Jul 1, 2026",
111515
- likeCount: 128,
111516
- commentCount: 12
111572
+ date: "Jul 6, 2026",
111573
+ likeCount: 428,
111574
+ commentCount: 34
111517
111575
  },
111518
111576
  {
111519
111577
  id: "2",
111520
111578
  href: "https://www.instagram.com/p/CxAmpLe002/",
111521
- image: GALLERY_EXAMPLE_IMAGE_URL,
111522
- imageAlt: "Behind the scenes reel",
111523
- caption: "Behind the scenes of today's bake #freshbread",
111524
- date: "Jun 28, 2026",
111579
+ image: "https://toastability-production.s3.amazonaws.com/z9u4sdrj2oq3eds0qyui0nxsus3j",
111580
+ imageAlt: "Line cook plating on a busy Friday service",
111581
+ caption: "Behind the pass on a Friday night service \u{1F525}",
111582
+ date: "Jul 4, 2026",
111525
111583
  isVideo: true,
111526
- videoUrl: "https://toastability-production.s3.amazonaws.com/4kox2ux0ye1wlqkdwg03s08a67i1",
111527
- likeCount: 342,
111528
- commentCount: 21,
111529
- viewCount: 4820
111584
+ videoUrl: "https://toastability-production.s3.amazonaws.com/b555hwjt7ltr81et05v5254q1ak6",
111585
+ likeCount: 1203,
111586
+ commentCount: 88,
111587
+ viewCount: 12840
111530
111588
  },
111531
111589
  {
111532
111590
  id: "3",
111533
111591
  href: "https://www.instagram.com/p/CxAmpLe003/",
111592
+ image: "https://toastability-production.s3.amazonaws.com/63aotyt2pb4gqpccej2kkw8reson",
111593
+ imageAlt: "House pancakes with maple butter",
111594
+ caption: "Weekend brunch is back \u2014 house pancakes + maple butter",
111595
+ date: "Jul 2, 2026",
111596
+ likeCount: 356,
111597
+ commentCount: 27
111598
+ },
111599
+ {
111600
+ id: "4",
111601
+ href: "https://www.instagram.com/p/CxAmpLe004/",
111602
+ image: "https://toastability-production.s3.amazonaws.com/we9r4e711an6d0bd3dwbl9tb9z7q",
111603
+ imageAlt: "Barista finishing latte art",
111604
+ caption: "60 seconds of latte art, no cuts \u2728",
111605
+ date: "Jun 29, 2026",
111606
+ isVideo: true,
111607
+ videoUrl: "https://toastability-production.s3.amazonaws.com/dvz0441h9fxjhh88lzqbwdoyxv52",
111608
+ likeCount: 942,
111609
+ commentCount: 61,
111610
+ viewCount: 8675
111611
+ },
111612
+ {
111613
+ id: "5",
111614
+ href: "https://www.instagram.com/p/CxAmpLe005/",
111534
111615
  image: GALLERY_EXAMPLE_IMAGE_URL,
111535
- imageAlt: "Weekend brunch spread",
111536
- caption: "Weekend brunch is back on the menu",
111616
+ imageAlt: "Fresh sourdough loaves out of the oven",
111617
+ caption: "Fresh sourdough out of the oven at 6am \u{1F956}",
111618
+ date: "Jun 27, 2026",
111619
+ likeCount: 512,
111620
+ commentCount: 41
111621
+ },
111622
+ {
111623
+ id: "6",
111624
+ href: "https://www.instagram.com/p/CxAmpLe006/",
111625
+ image: "https://toastability-production.s3.amazonaws.com/c4sgsy0g7o2rrjmvm9x7evxems82",
111626
+ imageAlt: "Chef plating a summer heirloom tomato salad",
111627
+ caption: "Plating the summer heirloom tomato salad \u{1F345}",
111537
111628
  date: "Jun 24, 2026",
111538
- likeCount: 96
111629
+ isVideo: true,
111630
+ videoUrl: "https://toastability-production.s3.amazonaws.com/jhjfvkmdzktacyijd9fh6acc7o2c",
111631
+ likeCount: 731,
111632
+ commentCount: 45,
111633
+ viewCount: 6390
111634
+ },
111635
+ {
111636
+ id: "7",
111637
+ href: "https://www.instagram.com/p/CxAmpLe007/",
111638
+ image: "https://toastability-production.s3.amazonaws.com/kh1p8y15v55ctp5ulobm4pd77etm",
111639
+ imageAlt: "Sunny patio seating with fresh flowers",
111640
+ caption: "Patio season is officially open \u2600\uFE0F",
111641
+ date: "Jun 21, 2026",
111642
+ likeCount: 289,
111643
+ commentCount: 19
111644
+ },
111645
+ {
111646
+ id: "8",
111647
+ href: "https://www.instagram.com/p/CxAmpLe008/",
111648
+ image: "https://toastability-production.s3.amazonaws.com/2d4k8d5shwg82276hzj2ztbj7mxq",
111649
+ imageAlt: "Charred corn elote on a plate",
111650
+ caption: "New on the menu: charred corn elote, our way \u{1F33D}",
111651
+ date: "Jun 18, 2026",
111652
+ likeCount: 634,
111653
+ commentCount: 52
111539
111654
  }
111540
111655
  ]
111541
111656
  }
@@ -77,6 +77,6 @@ interface SocialLinkIconProps extends Omit<PressableProps, "children">, SocialLi
77
77
  * />
78
78
  * ```
79
79
  */
80
- declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement | HTMLSpanElement>>;
80
+ declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement | HTMLSpanElement>>;
81
81
 
82
82
  export { SocialLinkIcon, type SocialLinkIconDynamicIconProps, type SocialLinkIconProps };
@@ -77,6 +77,6 @@ interface SocialLinkIconProps extends Omit<PressableProps, "children">, SocialLi
77
77
  * />
78
78
  * ```
79
79
  */
80
- declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement | HTMLSpanElement>>;
80
+ declare const SocialLinkIcon: React.ForwardRefExoticComponent<SocialLinkIconProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement | HTMLSpanElement>>;
81
81
 
82
82
  export { SocialLinkIcon, type SocialLinkIconDynamicIconProps, type SocialLinkIconProps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensite/ui",
3
- "version": "3.11.0",
3
+ "version": "3.11.2",
4
4
  "description": "Foundational UI component library for OpenSite Semantic Site Builder with tree-shakable exports and abstract styling",
5
5
  "keywords": [
6
6
  "react",