fragment-headless-sdk 1.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { ButtonType } from "../../constants";
3
+ import { mergeSlotAttributes, mergeSlotClasses, mergeSlotStyles, resolveToken, resolveTokenByCategory, } from "../../utils";
3
4
  export default function AnnouncementButton({ content, buttonHref, }) {
4
5
  // Don’t render if no button or explicitly None
5
6
  if (!content?.buttonText || content.buttonType === ButtonType.None)
@@ -11,16 +12,32 @@ export default function AnnouncementButton({ content, buttonHref, }) {
11
12
  // If you already have a boolean like `content.buttonLink` meaning "open in new tab",
12
13
  // keep using it; otherwise you can add one later.
13
14
  const openInNewTab = Boolean(content.buttonLink);
14
- const style = content.buttonType === ButtonType.Text
15
+ const styling = content.styling;
16
+ const baseTextColor = resolveTokenByCategory(styling, "colors", "text") ||
17
+ resolveToken(styling, "textColor");
18
+ const buttonBgColor = resolveTokenByCategory(styling, "colors", "button") ||
19
+ resolveToken(styling, "buttonColor");
20
+ const buttonTextColor = resolveTokenByCategory(styling, "colors", "buttonText") ||
21
+ resolveToken(styling, "buttonTextColor");
22
+ const baseStyle = content.buttonType === ButtonType.Text
15
23
  ? {
16
24
  textDecoration: "underline",
17
- color: content.textColor,
25
+ color: baseTextColor,
18
26
  }
19
27
  : {
20
- backgroundColor: content.buttonColor,
21
- color: content.buttonTextColor,
28
+ backgroundColor: buttonBgColor,
29
+ color: buttonTextColor,
22
30
  };
23
- return (React.createElement("a", { href: buttonHref, className: "whitespace-nowrap rounded-md px-3 py-2 text-sm font-semibold no-underline hover:cursor-pointer hover:opacity-70", style: style, ...(openInNewTab
31
+ const style = mergeSlotStyles(baseStyle, styling, "announcementButton", "button");
32
+ const className = mergeSlotClasses("whitespace-nowrap rounded-md px-3 py-2 text-sm font-semibold no-underline hover:cursor-pointer hover:opacity-70", styling, "announcementButton", "button");
33
+ const attributes = mergeSlotAttributes(styling, "announcementButton", "button");
34
+ const ariaLabel = attributes && "aria-label" in attributes
35
+ ? attributes["aria-label"]
36
+ : content.buttonText;
37
+ if (attributes && "aria-label" in attributes) {
38
+ delete attributes["aria-label"];
39
+ }
40
+ return (React.createElement("a", { href: buttonHref, className: className, style: style, ...(openInNewTab
24
41
  ? { target: "_blank", rel: "noopener noreferrer" }
25
- : {}), "aria-label": content.buttonText }, content.buttonText));
42
+ : {}), ...(attributes ?? {}), "aria-label": ariaLabel }, content.buttonText));
26
43
  }
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useState } from "react";
2
+ import { mergeSlotAttributes, mergeSlotClasses, mergeSlotStyles, resolveToken, } from "../../utils";
2
3
  export default function CountdownTimer({ content, }) {
3
4
  const [timeLeft, setTimeLeft] = useState({
4
5
  days: "00",
@@ -25,18 +26,36 @@ export default function CountdownTimer({ content, }) {
25
26
  const interval = setInterval(updateTimer, 1000);
26
27
  return () => clearInterval(interval);
27
28
  }, [content.counterEndDate]);
28
- const renderBlock = (value, label) => (React.createElement("div", { className: "flex flex-col items-center" },
29
- React.createElement("div", { className: "flex gap-1" }, value.split("").map((digit, i) => (React.createElement("span", { key: i, className: "w-5 h-6 rounded bg-black text-white text-base font-bold flex items-center justify-center", style: {
30
- backgroundColor: content.counterBgColor || "#000000",
31
- color: content.counterDigitColor || "#FFFFFF",
32
- } }, digit)))),
33
- React.createElement("span", { className: "mt-0.5 text-[10px]" }, label)));
34
- return (React.createElement("div", { className: "flex items-center justify-center gap-1 p-2" },
29
+ const styling = content.styling;
30
+ const containerClass = mergeSlotClasses("flex items-center justify-center gap-1 p-2", styling, "countdownContainer");
31
+ const containerStyle = mergeSlotStyles(undefined, styling, "countdownContainer");
32
+ const containerAttributes = mergeSlotAttributes(styling, "countdownContainer");
33
+ const blockClass = mergeSlotClasses("flex flex-col items-center", styling, "countdownBlock");
34
+ const blockStyle = mergeSlotStyles(undefined, styling, "countdownBlock");
35
+ const blockAttributes = mergeSlotAttributes(styling, "countdownBlock");
36
+ const digitClass = mergeSlotClasses("w-5 h-6 rounded bg-black text-white text-base font-bold flex items-center justify-center", styling, "countdownDigit");
37
+ const digitBgColor = resolveToken(styling, "counterBgColor", "#000000") || "#000000";
38
+ const digitTextColor = resolveToken(styling, "counterDigitColor", "#FFFFFF") || "#FFFFFF";
39
+ const digitStyle = mergeSlotStyles({
40
+ backgroundColor: digitBgColor,
41
+ color: digitTextColor,
42
+ }, styling, "countdownDigit");
43
+ const digitAttributes = mergeSlotAttributes(styling, "countdownDigit");
44
+ const labelClass = mergeSlotClasses("mt-0.5 text-[10px]", styling, "countdownLabel");
45
+ const labelStyle = mergeSlotStyles(undefined, styling, "countdownLabel");
46
+ const labelAttributes = mergeSlotAttributes(styling, "countdownLabel");
47
+ const separatorClass = mergeSlotClasses("text-xl font-semibold -mt-4", styling, "countdownSeparator");
48
+ const separatorStyle = mergeSlotStyles(undefined, styling, "countdownSeparator");
49
+ const separatorAttributes = mergeSlotAttributes(styling, "countdownSeparator");
50
+ const renderBlock = (value, label) => (React.createElement("div", { className: blockClass, style: blockStyle, ...(blockAttributes ?? {}) },
51
+ React.createElement("div", { className: "flex gap-1" }, value.split("").map((digit, i) => (React.createElement("span", { key: i, className: digitClass, style: digitStyle, ...(digitAttributes ?? {}) }, digit)))),
52
+ React.createElement("span", { className: labelClass, style: labelStyle, ...(labelAttributes ?? {}) }, label)));
53
+ return (React.createElement("div", { className: containerClass, style: containerStyle, ...(containerAttributes ?? {}) },
35
54
  renderBlock(timeLeft.days, "Days"),
36
- React.createElement("span", { className: "text-xl font-semibold -mt-4" }, ":"),
55
+ React.createElement("span", { className: separatorClass, style: separatorStyle, ...(separatorAttributes ?? {}) }, ":"),
37
56
  renderBlock(timeLeft.hours, "Hours"),
38
- React.createElement("span", { className: "text-xl font-semibold -mt-4" }, ":"),
57
+ React.createElement("span", { className: separatorClass, style: separatorStyle, ...(separatorAttributes ?? {}) }, ":"),
39
58
  renderBlock(timeLeft.minutes, "Minutes"),
40
- React.createElement("span", { className: "text-xl font-semibold -mt-4" }, ":"),
59
+ React.createElement("span", { className: separatorClass, style: separatorStyle, ...(separatorAttributes ?? {}) }, ":"),
41
60
  renderBlock(timeLeft.seconds, "Seconds")));
42
61
  }
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef } from "react";
2
2
  import { AnnouncementType, ButtonType } from "../../constants";
3
- import { buildClickUrl, fireImpressionWhenVisible } from "../../utils";
3
+ import { buildClickUrl, fireImpressionWhenVisible, getThemeClasses, mergeSlotAttributes, mergeSlotClasses, mergeSlotStyles, resolveToken, resolveTokenByCategory, } from "../../utils";
4
4
  import AnnouncementButton from "./AnnouncementButton";
5
5
  import { AnnouncementStyles } from "./AnnouncementStyles";
6
6
  import CountdownTimer from "./CountdownTimer";
@@ -16,26 +16,59 @@ export default function Announcement({ content, type, handleClose, }) {
16
16
  : undefined;
17
17
  if (!content)
18
18
  return null;
19
- return (React.createElement("div", { ref: ref, className: "relative w-full", style: {
20
- backgroundColor: content.bgColor,
21
- color: content.textColor,
22
- } },
19
+ const styling = content.styling;
20
+ const backgroundColor = resolveTokenByCategory(styling, "colors", "background") ||
21
+ resolveToken(styling, "bgColor");
22
+ const textColor = resolveTokenByCategory(styling, "colors", "text") ||
23
+ resolveToken(styling, "textColor");
24
+ const themeClasses = getThemeClasses(styling);
25
+ const rootClass = mergeSlotClasses(`relative w-full ${themeClasses}`, styling, "root");
26
+ const rootStyle = mergeSlotStyles({
27
+ backgroundColor,
28
+ color: textColor,
29
+ }, styling, "root");
30
+ const rootAttributes = mergeSlotAttributes(styling, "root");
31
+ const innerClass = mergeSlotClasses("relative mx-auto flex max-w-screen-xl flex-col md:flex-row items-center justify-center gap-4 px-10 md:px-4 py-2 md:py-0 text-center md:text-left min-h-[50px]", styling, "inner", "wrapper");
32
+ const innerStyle = mergeSlotStyles(undefined, styling, "inner", "wrapper");
33
+ const innerAttributes = mergeSlotAttributes(styling, "inner", "wrapper");
34
+ const marqueeContainerClass = mergeSlotClasses("flex w-full flex-col md:flex-row items-center justify-between gap-2 md:gap-4 overflow-hidden md:pr-8", styling, "marqueeContainer");
35
+ const marqueeContainerStyle = mergeSlotStyles(undefined, styling, "marqueeContainer");
36
+ const marqueeContainerAttributes = mergeSlotAttributes(styling, "marqueeContainer");
37
+ const marqueeTextWrapperClass = mergeSlotClasses("w-full md:flex-1 overflow-hidden", styling, "marqueeTextWrapper");
38
+ const marqueeTextWrapperStyle = mergeSlotStyles(undefined, styling, "marqueeTextWrapper");
39
+ const marqueeTextWrapperAttributes = mergeSlotAttributes(styling, "marqueeTextWrapper");
40
+ const marqueeTextClass = mergeSlotClasses("whitespace-nowrap animate-marquee", styling, "marqueeText");
41
+ const marqueeTextStyle = mergeSlotStyles(undefined, styling, "marqueeText");
42
+ const marqueeTextAttributes = mergeSlotAttributes(styling, "marqueeText");
43
+ const marqueeContentClass = mergeSlotClasses("inline-block max-w-none text-base", styling, "marqueeContent");
44
+ const marqueeContentStyle = mergeSlotStyles(undefined, styling, "marqueeContent");
45
+ const marqueeContentAttributes = mergeSlotAttributes(styling, "marqueeContent");
46
+ const contentRowClass = mergeSlotClasses("flex flex-col md:flex-row items-center justify-center gap-2 md:gap-4 w-full", styling, "contentRow");
47
+ const contentRowStyle = mergeSlotStyles(undefined, styling, "contentRow");
48
+ const contentRowAttributes = mergeSlotAttributes(styling, "contentRow");
49
+ const announcementTextClass = mergeSlotClasses("max-w-none text-base font-semibold", styling, "announcementText");
50
+ const announcementTextStyle = mergeSlotStyles(undefined, styling, "announcementText");
51
+ const announcementTextAttributes = mergeSlotAttributes(styling, "announcementText");
52
+ const closeButtonClass = mergeSlotClasses("absolute right-4 top-1/2 -translate-y-1/2 text-3xl leading-none cursor-pointer", styling, "closeButton");
53
+ const closeButtonColor = resolveTokenByCategory(styling, "colors", "closeButton") ||
54
+ (resolveToken(styling, "closeButtonColor", textColor ?? "#000") ?? "#000");
55
+ const closeButtonStyle = mergeSlotStyles({ color: closeButtonColor }, styling, "closeButton");
56
+ const closeButtonAttributes = mergeSlotAttributes(styling, "closeButton");
57
+ return (React.createElement("div", { ref: ref, className: rootClass, style: rootStyle, ...(rootAttributes ?? {}) },
23
58
  React.createElement(AnnouncementStyles, null),
24
- React.createElement("div", { className: "relative mx-auto flex max-w-screen-xl flex-col md:flex-row items-center justify-center gap-4 px-10 md:px-4 py-2 md:py-0 text-center md:text-left min-h-[50px]" },
25
- type === AnnouncementType.Marquee ? (React.createElement("div", { className: "flex w-full flex-col md:flex-row items-center justify-between gap-2 md:gap-4 overflow-hidden md:pr-8" },
26
- React.createElement("div", { className: "w-full md:flex-1 overflow-hidden" },
27
- React.createElement("div", { className: "whitespace-nowrap animate-marquee" },
28
- React.createElement("div", { className: "inline-block max-w-none text-base", dangerouslySetInnerHTML: {
59
+ React.createElement("div", { className: innerClass, style: innerStyle, ...(innerAttributes ?? {}) },
60
+ type === AnnouncementType.Marquee ? (React.createElement("div", { className: marqueeContainerClass, style: marqueeContainerStyle, ...(marqueeContainerAttributes ?? {}) },
61
+ React.createElement("div", { className: marqueeTextWrapperClass, style: marqueeTextWrapperStyle, ...(marqueeTextWrapperAttributes ?? {}) },
62
+ React.createElement("div", { className: marqueeTextClass, style: marqueeTextStyle, ...(marqueeTextAttributes ?? {}) },
63
+ React.createElement("div", { className: marqueeContentClass, style: marqueeContentStyle, ...(marqueeContentAttributes ?? {}), dangerouslySetInnerHTML: {
29
64
  __html: content.announcementHtml || "",
30
65
  } }))),
31
- content.buttonText && content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: signedButtonHref })))) : (React.createElement("div", { className: "flex flex-col md:flex-row items-center justify-center gap-2 md:gap-4 w-full" },
32
- React.createElement("div", { className: "max-w-none text-base font-semibold" },
66
+ content.buttonText && content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: signedButtonHref })))) : (React.createElement("div", { className: contentRowClass, style: contentRowStyle, ...(contentRowAttributes ?? {}) },
67
+ React.createElement("div", { className: announcementTextClass, style: announcementTextStyle, ...(announcementTextAttributes ?? {}) },
33
68
  React.createElement("div", { dangerouslySetInnerHTML: {
34
69
  __html: content.announcementHtml || "",
35
70
  } })),
36
71
  type === AnnouncementType.Countdown ? (React.createElement(CountdownTimer, { content: content })) : (content.buttonText &&
37
72
  content.buttonType !== ButtonType.None && (React.createElement(AnnouncementButton, { content: content, buttonHref: signedButtonHref || content.buttonLink || "#" }))))),
38
- React.createElement("div", { onClick: handleClose, className: "absolute right-4 top-1/2 -translate-y-1/2 text-3xl leading-none cursor-pointer", style: {
39
- color: content.textColor || "#000",
40
- } }, "\u00D7"))));
73
+ React.createElement("div", { onClick: handleClose, className: closeButtonClass, style: closeButtonStyle, ...(closeButtonAttributes ?? {}) }, "\u00D7"))));
41
74
  }
@@ -1,6 +1,14 @@
1
1
  import React from "react";
2
2
  import { IHeroContent } from "../../types";
3
- export default function DesktopHero({ buttonHref, content, }: {
3
+ import { resolveHeroColors, resolveHeroTypography } from "../../utils/hero-resolvers";
4
+ interface HeroViewProps {
4
5
  buttonHref?: string;
5
6
  content: IHeroContent;
6
- }): React.JSX.Element;
7
+ colors: ReturnType<typeof resolveHeroColors>;
8
+ contentWidthClass: string;
9
+ typography: ReturnType<typeof resolveHeroTypography>;
10
+ position: "left" | "center" | "right";
11
+ height: string;
12
+ }
13
+ export default function DesktopHero({ buttonHref, content, colors, contentWidthClass, typography, position, height, }: HeroViewProps): React.JSX.Element;
14
+ export {};
@@ -1,14 +1,42 @@
1
1
  import React from "react";
2
- export default function DesktopHero({ buttonHref, content, }) {
3
- return (React.createElement("div", { className: "relative h-[400px] gap-4 w-full" },
4
- content?.imageUrl && (React.createElement("img", { src: content.imageUrl, alt: content.title || "Hero", className: "absolute inset-0 z-0 object-cover w-full h-full" })),
5
- React.createElement("div", { className: "relative z-10 mx-auto flex h-full max-w-screen-xl flex-col items-start justify-center px-10 text-left xl:px-4" },
6
- React.createElement("div", { className: "w-2/5" },
7
- content?.title && (React.createElement("h1", { className: "text-5xl font-bold leading-tight drop-shadow-xl", style: { color: content.titleColor || "#ffffff" } }, content.title)),
8
- content?.description && (React.createElement("div", { className: "mt-4 text-2xl drop-shadow-lg prose", style: { color: content.textColor || undefined }, dangerouslySetInnerHTML: { __html: content.description } })),
2
+ import { DEFAULT_CONTENT_WIDTH_CLASS, joinClassNames, renderText, } from "../../utils/hero-resolvers";
3
+ export default function DesktopHero({ buttonHref, content, colors, contentWidthClass, typography, position, height, }) {
4
+ const getPositionClasses = () => {
5
+ switch (position) {
6
+ case "center":
7
+ return "items-center text-center";
8
+ case "right":
9
+ return "items-end text-right";
10
+ case "left":
11
+ default:
12
+ return "items-start text-left";
13
+ }
14
+ };
15
+ return (React.createElement("div", { className: `relative ${height} gap-4 w-full`, style: { backgroundColor: colors.background } },
16
+ content?.videoUrl ? (React.createElement("video", { src: content.videoUrl, autoPlay: true, muted: true, loop: true, playsInline: true, className: "absolute inset-0 z-0 object-cover w-full h-full" })) : (
17
+ /* Image Background */
18
+ content?.imageUrl && (React.createElement("img", { src: content.imageUrl, alt: content.title || "Hero", className: "absolute inset-0 z-0 object-cover w-full h-full" }))),
19
+ React.createElement("div", { className: `relative z-10 mx-auto flex h-full max-w-screen-xl flex-col justify-center px-10 ${getPositionClasses()} xl:px-4` },
20
+ React.createElement("div", { className: joinClassNames("max-w-full", contentWidthClass || DEFAULT_CONTENT_WIDTH_CLASS) },
21
+ renderText({
22
+ fontSize: typography.title.fontSize,
23
+ lineHeight: typography.title.lineHeight,
24
+ text: content?.title,
25
+ className: "mt-4",
26
+ color: colors.title,
27
+ font: typography.title.font,
28
+ }),
29
+ renderText({
30
+ fontSize: typography.description.fontSize,
31
+ lineHeight: typography.description.lineHeight,
32
+ text: content?.description,
33
+ className: "mt-4",
34
+ color: colors.text,
35
+ font: typography.description.font,
36
+ }),
9
37
  content?.buttonLink && content?.buttonText && (React.createElement("a", { href: buttonHref, target: "_blank", rel: "noopener noreferrer", className: "no-underline" },
10
- React.createElement("div", { className: "mt-6 rounded-md px-8 py-2 text-2xl font-semibold drop-shadow-lg transition-all duration-200 hover:bg-gray-800 inline-block", style: {
11
- color: content.buttonTextColor ?? undefined,
12
- backgroundColor: content.buttonColor ?? undefined,
38
+ React.createElement("div", { className: "mt-6 inline-block rounded-md px-8 py-2 text-2xl font-semibold drop-shadow-lg transition-all duration-200 hover:opacity-90", style: {
39
+ color: colors.buttonText,
40
+ backgroundColor: colors.buttonBackground,
13
41
  } }, content.buttonText)))))));
14
42
  }
@@ -1,6 +1,11 @@
1
1
  import React from "react";
2
2
  import { IHeroContent } from "../../types";
3
- export default function MobileHero({ buttonHref, content, }: {
3
+ import { resolveHeroColors, resolveHeroTypography } from "../../utils/hero-resolvers";
4
+ interface MobileHeroProps {
4
5
  buttonHref?: string;
5
6
  content: IHeroContent;
6
- }): React.JSX.Element;
7
+ colors: ReturnType<typeof resolveHeroColors>;
8
+ typography: ReturnType<typeof resolveHeroTypography>;
9
+ }
10
+ export default function MobileHero({ buttonHref, content, colors, typography, }: MobileHeroProps): React.JSX.Element;
11
+ export {};
@@ -1,13 +1,30 @@
1
1
  import React from "react";
2
- export default function MobileHero({ buttonHref, content, }) {
3
- return (React.createElement("div", { className: "relative z-10 mx-auto gap-4 flex h-full max-w-screen-md flex-col items-center justify-center py-6 text-center" },
4
- content?.title && (React.createElement("h1", { className: "text-3xl font-bold drop-shadow-xl px-4", style: { color: content.titleColor || undefined } }, content.title)),
5
- (content?.mobileImageUrl || content?.imageUrl) && (React.createElement("div", { className: "w-full" },
6
- React.createElement("img", { src: content.mobileImageUrl || content.imageUrl || "", alt: content.title || "Hero", className: "h-full w-full object-cover" }))),
7
- content?.description && (React.createElement("div", { className: "px-4 text-2xl drop-shadow-lg prose", style: { color: content.textColor || undefined }, dangerouslySetInnerHTML: { __html: content.description } })),
2
+ import { renderText, } from "../../utils/hero-resolvers";
3
+ export default function MobileHero({ buttonHref, content, colors, typography, }) {
4
+ return (React.createElement("div", { className: "relative z-10 mx-auto gap-4 flex max-w-screen-md flex-col items-center justify-center py-6 text-center", style: { backgroundColor: colors.background } },
5
+ renderText({
6
+ fontSize: typography.title.fontSize,
7
+ lineHeight: typography.title.lineHeight,
8
+ text: content?.title,
9
+ className: "px-4 drop-shadow-xl text-center",
10
+ color: colors.title,
11
+ font: typography.title.font,
12
+ }),
13
+ content?.videoUrl ? (React.createElement("div", { className: "w-full" },
14
+ React.createElement("video", { src: content.videoUrl, autoPlay: true, muted: true, loop: true, playsInline: true, className: "h-full w-full object-cover" }))) : content?.mobileImageUrl ? (React.createElement("div", { className: "w-full" },
15
+ React.createElement("img", { src: content.mobileImageUrl, alt: content.title || "Hero", className: "h-full w-full object-cover" }))) : content?.imageUrl ? (React.createElement("div", { className: "w-full" },
16
+ React.createElement("img", { src: content.imageUrl, alt: content.title || "Hero", className: "h-full w-full object-cover" }))) : null,
17
+ renderText({
18
+ fontSize: typography.description.fontSize,
19
+ lineHeight: typography.description.lineHeight,
20
+ text: content?.description,
21
+ className: "px-4 drop-shadow-lg text-center mt-4",
22
+ color: colors.text,
23
+ font: typography.description.font,
24
+ }),
8
25
  content?.buttonLink && content?.buttonText && (React.createElement("a", { href: buttonHref, target: "_blank", rel: "noopener noreferrer", className: "no-underline" },
9
- React.createElement("div", { className: "mb-2 rounded-md px-6 py-2 text-lg font-semibold drop-shadow-lg transition-all duration-200 hover:bg-gray-800", style: {
10
- color: content.buttonTextColor ?? undefined,
11
- backgroundColor: content.buttonColor ?? undefined,
26
+ React.createElement("div", { className: "mb-2 rounded-md px-6 py-2 text-lg font-semibold drop-shadow-lg transition-all duration-200 hover:opacity-90", style: {
27
+ color: colors.buttonText,
28
+ backgroundColor: colors.buttonBackground,
12
29
  } }, content.buttonText)))));
13
30
  }
@@ -1,5 +1,6 @@
1
1
  import React, { useEffect, useRef } from "react";
2
2
  import { buildClickUrl, fireImpressionWhenVisible } from "../../utils";
3
+ import { resolveContentWidthClass, resolveHeight, resolveHeroColors, resolveHeroTypography, resolvePosition, } from "../../utils/hero-resolvers";
3
4
  import DesktopHero from "./DesktopHero";
4
5
  import MobileHero from "./MobileHero";
5
6
  export default function Hero({ content }) {
@@ -14,9 +15,14 @@ export default function Hero({ content }) {
14
15
  : undefined;
15
16
  if (!content)
16
17
  return null;
17
- return (React.createElement("div", { className: "bg-black", ref: ref },
18
+ const colors = resolveHeroColors(content);
19
+ const contentWidthClass = resolveContentWidthClass(content);
20
+ const typography = resolveHeroTypography(content);
21
+ const position = resolvePosition(content);
22
+ const height = resolveHeight(content);
23
+ return (React.createElement("div", { className: "bg-black", ref: ref, style: { backgroundColor: colors.background } },
18
24
  React.createElement("div", { className: "hidden lg:block" },
19
- React.createElement(DesktopHero, { content: content, buttonHref: signedButtonHref })),
25
+ React.createElement(DesktopHero, { content: content, buttonHref: signedButtonHref, colors: colors, contentWidthClass: contentWidthClass, typography: typography, position: position, height: height })),
20
26
  React.createElement("div", { className: "block lg:hidden" },
21
- React.createElement(MobileHero, { content: content, buttonHref: signedButtonHref }))));
27
+ React.createElement(MobileHero, { content: content, buttonHref: signedButtonHref, colors: colors, typography: typography }))));
22
28
  }
@@ -0,0 +1,31 @@
1
+ @import url("https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=Geist+Mono:wght@100..900&family=Roboto:wght@300;400;500;700&family=Open+Sans:wght@300;400;600;700&family=Lato:wght@300;400;700&family=Montserrat:wght@300;400;500;700&family=Inter:wght@300;400;500;700&family=Poppins:wght@300;400;600;700&family=Raleway:wght@300;400;500;700&family=Playfair+Display:wght@400;700&family=Merriweather:wght@300;400;700&family=Oswald:wght@300;400;500;700&display=swap");
2
+
3
+ /* Custom animations for fragment-headless-sdk */
4
+ @keyframes marquee {
5
+ 0% {
6
+ transform: translateX(0%);
7
+ }
8
+ 100% {
9
+ transform: translateX(-100%);
10
+ }
11
+ }
12
+
13
+ /* Utility classes for marquee animations */
14
+ .animate-marquee {
15
+ animation: marquee 25s linear infinite;
16
+ }
17
+
18
+ /* Make hero/announcement prose links inherit the surrounding text color (v2.1+) */
19
+ .prose {
20
+ --tw-prose-links: currentColor;
21
+ --tw-prose-links-hover: currentColor;
22
+ }
23
+
24
+ .prose :where(a):not(:where([class~="not-prose"] *)) {
25
+ color: currentColor;
26
+ }
27
+
28
+ .prose :where(a):not(:where([class~="not-prose"] *)):hover {
29
+ color: currentColor;
30
+ text-decoration: underline;
31
+ }
@@ -1,4 +1,5 @@
1
1
  import { AnnouncementStatus, AnnouncementType, ButtonType } from "../constants";
2
+ import { IAnnouncementStyling } from "./styling";
2
3
  export declare const buttonTypes: {
3
4
  label: string;
4
5
  value: ButtonType;
@@ -7,16 +8,11 @@ export interface IAnnouncementContent {
7
8
  buttonType: ButtonType;
8
9
  buttonText: string;
9
10
  buttonLink: string;
10
- bgColor: string;
11
11
  announcementHtml: string;
12
- buttonColor: string;
13
- buttonTextColor: string;
14
- textColor: string;
15
12
  counterEndDate?: string;
16
- counterBgColor?: string;
17
- counterDigitColor?: string;
18
13
  impressionUrl: string;
19
14
  clickUrlBase: string;
15
+ styling?: IAnnouncementStyling;
20
16
  }
21
17
  export interface IAnnouncement {
22
18
  id: string;
@@ -1,4 +1,5 @@
1
1
  import { HeroStatus, HeroType } from "../constants";
2
+ import { IHeroStyling } from "./styling";
2
3
  export type ShopPage = {
3
4
  id: string;
4
5
  title: string;
@@ -6,18 +7,15 @@ export type ShopPage = {
6
7
  };
7
8
  export interface IHeroContent {
8
9
  title: string;
9
- titleColor: string;
10
10
  description: string;
11
- textColor: string;
12
11
  buttonText: string;
13
12
  buttonLink: string;
14
- buttonColor: string;
15
- buttonTextColor: string;
16
13
  imageUrl: string;
17
14
  mobileImageUrl: string;
18
15
  videoUrl?: string;
19
16
  impressionUrl: string;
20
17
  clickUrlBase: string;
18
+ styling?: IHeroStyling;
21
19
  }
22
20
  export interface IHero {
23
21
  id: string;
@@ -1,2 +1,3 @@
1
1
  export * from "./announcement";
2
2
  export * from "./hero";
3
+ export * from "./styling";
@@ -1,2 +1,3 @@
1
1
  export * from "./announcement";
2
2
  export * from "./hero";
3
+ export * from "./styling";
@@ -0,0 +1,133 @@
1
+ export type InlineStyleValue = string | number | undefined;
2
+ export type InlineStyleMap = Record<string, InlineStyleValue>;
3
+ export interface IFragmentTokens {
4
+ colors?: Record<string, string>;
5
+ spacing?: Record<string, string>;
6
+ typography?: Record<string, string>;
7
+ transitions?: Record<string, string>;
8
+ shadows?: Record<string, string>;
9
+ borders?: Record<string, string>;
10
+ [key: string]: Record<string, string> | undefined;
11
+ }
12
+ export interface IFragmentStylingSlot {
13
+ /**
14
+ * Additional classes to append to the base slot.
15
+ * Accepts a single string or array of strings.
16
+ */
17
+ className?: string | string[];
18
+ /**
19
+ * Inline CSS properties merged onto the slot.
20
+ */
21
+ style?: InlineStyleMap;
22
+ /**
23
+ * CSS variables that will be applied to the slot.
24
+ * Keys may be provided with or without the leading `--`.
25
+ */
26
+ vars?: InlineStyleMap;
27
+ /**
28
+ * Arbitrary attributes (data-, aria-, etc.) to spread on the slot.
29
+ */
30
+ attributes?: Record<string, string>;
31
+ /**
32
+ * CSS-in-JS support for styled-components, emotion, etc.
33
+ */
34
+ css?: string;
35
+ /**
36
+ * Responsive overrides for different breakpoints.
37
+ */
38
+ responsive?: {
39
+ sm?: Partial<IFragmentStylingSlot>;
40
+ md?: Partial<IFragmentStylingSlot>;
41
+ lg?: Partial<IFragmentStylingSlot>;
42
+ xl?: Partial<IFragmentStylingSlot>;
43
+ "2xl"?: Partial<IFragmentStylingSlot>;
44
+ };
45
+ /**
46
+ * State-based styling (hover, active, focus, disabled, etc.).
47
+ */
48
+ states?: {
49
+ hover?: Partial<IFragmentStylingSlot>;
50
+ active?: Partial<IFragmentStylingSlot>;
51
+ focus?: Partial<IFragmentStylingSlot>;
52
+ disabled?: Partial<IFragmentStylingSlot>;
53
+ loading?: Partial<IFragmentStylingSlot>;
54
+ [state: string]: Partial<IFragmentStylingSlot> | undefined;
55
+ };
56
+ }
57
+ export interface IFragmentStyling {
58
+ /**
59
+ * Slot-level overrides. Slot keys are component-specific (e.g. "root", "button").
60
+ */
61
+ slots?: Record<string, IFragmentStylingSlot>;
62
+ /**
63
+ * Structured design tokens for consistent theming.
64
+ */
65
+ tokens?: IFragmentTokens;
66
+ /**
67
+ * Legacy flat token support for backward compatibility.
68
+ * @deprecated Use structured tokens instead.
69
+ */
70
+ legacyTokens?: Record<string, string>;
71
+ /**
72
+ * Theme variant (light, dark, brand, etc.).
73
+ */
74
+ theme?: "light" | "dark" | "brand" | "minimal" | "bold" | string;
75
+ /**
76
+ * Component variant for different styles of the same component.
77
+ */
78
+ variant?: "default" | "outlined" | "ghost" | "solid" | "gradient" | string;
79
+ /**
80
+ * Global CSS variables to apply to the component root.
81
+ */
82
+ globalVars?: InlineStyleMap;
83
+ }
84
+ export interface IAnnouncementSlots {
85
+ root?: IFragmentStylingSlot;
86
+ inner?: IFragmentStylingSlot;
87
+ wrapper?: IFragmentStylingSlot;
88
+ marqueeContainer?: IFragmentStylingSlot;
89
+ marqueeTextWrapper?: IFragmentStylingSlot;
90
+ marqueeText?: IFragmentStylingSlot;
91
+ marqueeContent?: IFragmentStylingSlot;
92
+ contentRow?: IFragmentStylingSlot;
93
+ announcementText?: IFragmentStylingSlot;
94
+ announcementButton?: IFragmentStylingSlot;
95
+ button?: IFragmentStylingSlot;
96
+ closeButton?: IFragmentStylingSlot;
97
+ countdown?: IFragmentStylingSlot;
98
+ countdownItem?: IFragmentStylingSlot;
99
+ countdownLabel?: IFragmentStylingSlot;
100
+ countdownValue?: IFragmentStylingSlot;
101
+ }
102
+ export interface IHeroSlots {
103
+ root?: IFragmentStylingSlot;
104
+ desktopWrapper?: IFragmentStylingSlot;
105
+ mobileWrapper?: IFragmentStylingSlot;
106
+ container?: IFragmentStylingSlot;
107
+ desktopContainer?: IFragmentStylingSlot;
108
+ mobileContainer?: IFragmentStylingSlot;
109
+ image?: IFragmentStylingSlot;
110
+ desktopImage?: IFragmentStylingSlot;
111
+ mobileImage?: IFragmentStylingSlot;
112
+ contentWrapper?: IFragmentStylingSlot;
113
+ desktopContentWrapper?: IFragmentStylingSlot;
114
+ mobileContentWrapper?: IFragmentStylingSlot;
115
+ content?: IFragmentStylingSlot;
116
+ desktopContent?: IFragmentStylingSlot;
117
+ mobileContent?: IFragmentStylingSlot;
118
+ title?: IFragmentStylingSlot;
119
+ desktopTitle?: IFragmentStylingSlot;
120
+ mobileTitle?: IFragmentStylingSlot;
121
+ description?: IFragmentStylingSlot;
122
+ desktopDescription?: IFragmentStylingSlot;
123
+ mobileDescription?: IFragmentStylingSlot;
124
+ button?: IFragmentStylingSlot;
125
+ desktopButton?: IFragmentStylingSlot;
126
+ mobileButton?: IFragmentStylingSlot;
127
+ }
128
+ export interface IAnnouncementStyling extends Omit<IFragmentStyling, "slots"> {
129
+ slots?: IAnnouncementSlots;
130
+ }
131
+ export interface IHeroStyling extends Omit<IFragmentStyling, "slots"> {
132
+ slots?: IHeroSlots;
133
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import React from "react";
2
+ import { IHeroContent } from "../types";
3
+ export interface HeroResolvedColors {
4
+ title: string;
5
+ text: string;
6
+ buttonBackground: string;
7
+ buttonText: string;
8
+ background: string;
9
+ }
10
+ export declare const DEFAULT_COLORS: HeroResolvedColors;
11
+ export declare const DEFAULT_CONTENT_WIDTH_CLASS = "w-2/5";
12
+ export declare const DEFAULT_HEIGHT_CLASS = "h-[400px]";
13
+ export declare const FONT_FAMILY_MAP: {
14
+ readonly geist: "\"Geist\", -apple-system, BlinkMacSystemFont, sans-serif";
15
+ readonly georgia: "\"Georgia\", \"Times New Roman\", serif";
16
+ readonly geistMono: "\"Geist Mono\", \"SFMono-Regular\", Menlo, monospace";
17
+ readonly roboto: "\"Roboto\", sans-serif";
18
+ readonly openSans: "\"Open Sans\", sans-serif";
19
+ readonly lato: "\"Lato\", sans-serif";
20
+ readonly montserrat: "\"Montserrat\", sans-serif";
21
+ readonly inter: "\"Inter\", sans-serif";
22
+ readonly poppins: "\"Poppins\", sans-serif";
23
+ readonly raleway: "\"Raleway\", sans-serif";
24
+ readonly playfair: "\"Playfair Display\", serif";
25
+ readonly merriweather: "\"Merriweather\", serif";
26
+ readonly oswald: "\"Oswald\", sans-serif";
27
+ };
28
+ export type FontKey = keyof typeof FONT_FAMILY_MAP;
29
+ export declare const DEFAULT_TYPOGRAPHY: {
30
+ titleFontSize: string;
31
+ titleLineHeight: string;
32
+ titleFont: FontKey;
33
+ descriptionFontSize: string;
34
+ descriptionLineHeight: string;
35
+ descriptionFont: FontKey;
36
+ };
37
+ export interface HeroTypographySettings {
38
+ title: {
39
+ fontSize: string;
40
+ lineHeight: string;
41
+ font: FontKey;
42
+ };
43
+ description: {
44
+ fontSize: string;
45
+ lineHeight: string;
46
+ font: FontKey;
47
+ };
48
+ }
49
+ export declare const joinClassNames: (...classes: Array<string | false | null | undefined>) => string;
50
+ export declare function resolveHeroColors(content: IHeroContent): HeroResolvedColors;
51
+ export declare function resolveContentWidthClass(content: IHeroContent): string;
52
+ export declare function resolvePosition(content: IHeroContent): "left" | "center" | "right";
53
+ export declare function resolveHeight(content: IHeroContent): string;
54
+ export declare function resolveHeroTypography(content: IHeroContent): HeroTypographySettings;
55
+ export declare function renderText({ fontSize, lineHeight, text, className, color, font, }: {
56
+ fontSize: string;
57
+ lineHeight: string;
58
+ text: string | null | undefined;
59
+ className?: string;
60
+ color?: string;
61
+ font: FontKey;
62
+ }): React.JSX.Element | null;