medusa-ui-home 3.0.3 → 3.0.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "medusa-ui-home",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "description": "Homepage sections.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,11 +21,11 @@
21
21
  "lodash": ">=4",
22
22
  "lucide-react": "*",
23
23
  "medusa-plugin-dynamic-config": ">=0.0.31",
24
- "medusa-ui-home-config": "^1.1.0",
24
+ "medusa-ui-home-config": "^1.0.0",
25
25
  "medusa-contact-logic-plugin": "^2.0.0",
26
26
  "medusa-reviews-logic": "^2.0.0",
27
27
  "medusa-storefront-analytics": "^1.0.0",
28
- "medusa-storefront-data": "^2.5.3",
28
+ "medusa-storefront-data": "^2.0.0",
29
29
  "medusa-storefront-hooks": "^1.0.0",
30
30
  "medusa-wishlist-logic": "^2.0.0",
31
31
  "medusa-storefront-theme-base": "^3.0.0",
@@ -122,6 +122,11 @@
122
122
  "import": "./src/home/sections/testimonials/index.ts",
123
123
  "default": "./src/home/sections/testimonials/index.ts"
124
124
  },
125
+ "./sections/customerStories": {
126
+ "types": "./src/home/sections/customerStories/index.ts",
127
+ "import": "./src/home/sections/customerStories/index.ts",
128
+ "default": "./src/home/sections/customerStories/index.ts"
129
+ },
125
130
  "./sections/brandPillars": {
126
131
  "types": "./src/home/sections/brandPillars/index.ts",
127
132
  "import": "./src/home/sections/brandPillars/index.ts",
@@ -161,16 +166,6 @@
161
166
  "types": "./src/home/sections/blogPosts/index.ts",
162
167
  "import": "./src/home/sections/blogPosts/index.ts",
163
168
  "default": "./src/home/sections/blogPosts/index.ts"
164
- },
165
- "./catalog/page": {
166
- "types": "./src/home/catalog/page.tsx",
167
- "import": "./src/home/catalog/page.tsx",
168
- "default": "./src/home/catalog/page.tsx"
169
- },
170
- "./components/home-page-json-ld": {
171
- "types": "./src/home/components/home-page-json-ld.tsx",
172
- "import": "./src/home/components/home-page-json-ld.tsx",
173
- "default": "./src/home/components/home-page-json-ld.tsx"
174
169
  }
175
170
  },
176
171
  "typesVersions": {
@@ -192,11 +187,11 @@
192
187
  "@types/react-dom": "^18.3.5",
193
188
  "next": "^15.0.0",
194
189
  "typescript": "^5.6.0",
195
- "medusa-storefront-analytics": "workspace:*",
196
- "medusa-storefront-data": "workspace:*",
197
- "medusa-storefront-theme-base": "workspace:*",
198
- "medusa-ui-common": "workspace:*",
199
- "medusa-ui-product": "workspace:*",
200
- "medusa-ui-home-config": "workspace:*"
190
+ "medusa-storefront-analytics": "1.5.2",
191
+ "medusa-storefront-data": "2.5.10",
192
+ "medusa-storefront-theme-base": "3.0.2",
193
+ "medusa-ui-common": "3.0.3",
194
+ "medusa-ui-product": "3.0.4",
195
+ "medusa-ui-home-config": "1.1.4"
201
196
  }
202
197
  }
@@ -0,0 +1,286 @@
1
+ "use client"
2
+
3
+ import { useCallback, useEffect, useRef, useState } from "react"
4
+ import Image from "next/image"
5
+ import type { HomeThemeClassNames } from "medusa-storefront-theme-base"
6
+ import { useThemeSection } from "medusa-ui-common/providers"
7
+ import { DEFAULT_CUSTOMER_STORIES_COPY } from "../copy-defaults"
8
+ import CustomerStoriesSectionHeader from "./section-header"
9
+ import type { CustomerStoryItem } from "./types"
10
+
11
+ const GAP_PX = 20
12
+
13
+ export type CustomerStoriesProps = {
14
+ title?: string
15
+ eyebrow?: string
16
+ items?: CustomerStoryItem[]
17
+ classNames?: Partial<HomeThemeClassNames>
18
+ }
19
+
20
+ function Stars({
21
+ rating,
22
+ classNames,
23
+ }: {
24
+ rating: number
25
+ classNames: { stars: string; starFilled: string; starMuted: string }
26
+ }) {
27
+ const clamped = Math.max(0, Math.min(5, Math.round(rating)))
28
+ return (
29
+ <div className={classNames.stars} aria-label={`${clamped} out of 5 stars`}>
30
+ {Array.from({ length: 5 }, (_, i) => (
31
+ <span
32
+ key={i}
33
+ className={i < clamped ? classNames.starFilled : classNames.starMuted}
34
+ >
35
+
36
+ </span>
37
+ ))}
38
+ </div>
39
+ )
40
+ }
41
+
42
+ function Chevron({ dir }: { dir: "left" | "right" }) {
43
+ return (
44
+ <svg
45
+ width="18"
46
+ height="18"
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ strokeWidth="1.75"
51
+ strokeLinecap="round"
52
+ strokeLinejoin="round"
53
+ aria-hidden
54
+ >
55
+ {dir === "left" ? (
56
+ <path d="M15 18l-6-6 6-6" />
57
+ ) : (
58
+ <path d="M9 18l6-6-6-6" />
59
+ )}
60
+ </svg>
61
+ )
62
+ }
63
+
64
+ function useCardsPerView() {
65
+ const [perView, setPerView] = useState(1)
66
+
67
+ useEffect(() => {
68
+ const mq = window.matchMedia("(min-width: 768px)")
69
+ const update = () => setPerView(mq.matches ? 2 : 1)
70
+ update()
71
+ mq.addEventListener("change", update)
72
+ return () => mq.removeEventListener("change", update)
73
+ }, [])
74
+
75
+ return perView
76
+ }
77
+
78
+ function StoryCard({
79
+ story,
80
+ classNames,
81
+ }: {
82
+ story: CustomerStoryItem
83
+ classNames: {
84
+ card: string
85
+ imageWrap: string
86
+ image: string
87
+ body: string
88
+ quote: string
89
+ stars: string
90
+ starFilled: string
91
+ starMuted: string
92
+ name: string
93
+ }
94
+ }) {
95
+ const rating = Number(story.rating ?? 5)
96
+ return (
97
+ <article className={classNames.card}>
98
+ <div className={classNames.imageWrap}>
99
+ <Image
100
+ src={story.image}
101
+ alt={`${story.name} — customer story`}
102
+ width={320}
103
+ height={400}
104
+ className={classNames.image}
105
+ loading="lazy"
106
+ unoptimized
107
+ />
108
+ </div>
109
+ <div className={classNames.body}>
110
+ <p className={classNames.quote}>&ldquo;{story.text}&rdquo;</p>
111
+ <Stars
112
+ rating={Number.isFinite(rating) ? rating : 5}
113
+ classNames={{
114
+ stars: classNames.stars,
115
+ starFilled: classNames.starFilled,
116
+ starMuted: classNames.starMuted,
117
+ }}
118
+ />
119
+ <p className={classNames.name}>{story.name}</p>
120
+ </div>
121
+ </article>
122
+ )
123
+ }
124
+
125
+ export default function CustomerStories({
126
+ title: titleProp,
127
+ eyebrow: eyebrowProp,
128
+ items: itemsProp,
129
+ classNames: classNamesProp,
130
+ }: CustomerStoriesProps) {
131
+ const cn = useThemeSection("home", classNamesProp)
132
+ const title = titleProp?.trim() || DEFAULT_CUSTOMER_STORIES_COPY.title
133
+ const eyebrow = eyebrowProp?.trim() || DEFAULT_CUSTOMER_STORIES_COPY.eyebrow
134
+ /** Use CMS/server items only; do not fall back to package copy when server sends an empty list. */
135
+ const items = Array.isArray(itemsProp)
136
+ ? itemsProp
137
+ : DEFAULT_CUSTOMER_STORIES_COPY.items
138
+
139
+ const perView = useCardsPerView()
140
+ const [activeIndex, setActiveIndex] = useState(0)
141
+ const [translateX, setTranslateX] = useState(0)
142
+ const viewportRef = useRef<HTMLDivElement>(null)
143
+ const trackRef = useRef<HTMLDivElement>(null)
144
+
145
+ const maxIndex = Math.max(0, items.length - perView)
146
+ const dotCount = maxIndex + 1
147
+
148
+ const measureOffset = useCallback(() => {
149
+ const slide = trackRef.current?.querySelector<HTMLElement>("[data-story-slide]")
150
+ if (!slide) return 0
151
+ return activeIndex * (slide.offsetWidth + GAP_PX)
152
+ }, [activeIndex])
153
+
154
+ useEffect(() => {
155
+ setTranslateX(measureOffset())
156
+ }, [measureOffset, perView])
157
+
158
+ useEffect(() => {
159
+ const viewport = viewportRef.current
160
+ if (!viewport) return
161
+ const ro = new ResizeObserver(() => setTranslateX(measureOffset()))
162
+ ro.observe(viewport)
163
+ return () => ro.disconnect()
164
+ }, [measureOffset])
165
+
166
+ useEffect(() => {
167
+ setActiveIndex((i) => Math.min(i, maxIndex))
168
+ }, [maxIndex, perView])
169
+
170
+ const goTo = useCallback(
171
+ (index: number) => {
172
+ setActiveIndex(Math.min(Math.max(index, 0), maxIndex))
173
+ },
174
+ [maxIndex]
175
+ )
176
+
177
+ if (items.length === 0) return null
178
+
179
+ return (
180
+ <section
181
+ className={`${cn.customerStoriesSection ?? ""} ${cn.section ?? ""}`}
182
+ aria-labelledby="customer-stories-heading"
183
+ >
184
+ <CustomerStoriesSectionHeader
185
+ eyebrow={eyebrow}
186
+ title={title}
187
+ id="customer-stories-heading"
188
+ classNames={{
189
+ wrap: cn.customerStoriesHeaderWrap ?? "",
190
+ inner: cn.customerStoriesHeaderInner ?? "",
191
+ header: cn.customerStoriesHeader ?? "",
192
+ eyebrow: cn.customerStoriesEyebrow ?? "",
193
+ heading: cn.customerStoriesTitle ?? "",
194
+ accent: cn.customerStoriesAccent ?? "",
195
+ accentLine: cn.customerStoriesAccentLine ?? "",
196
+ accentGem: cn.customerStoriesAccentGem ?? "",
197
+ }}
198
+ />
199
+
200
+ <div className={cn.customerStoriesInner ?? ""}>
201
+ <div className={cn.customerStoriesCarouselWrap ?? ""}>
202
+ <div className={cn.customerStoriesSlidesRow ?? ""}>
203
+ {dotCount > 1 ? (
204
+ <>
205
+ <button
206
+ type="button"
207
+ className={`${cn.customerStoriesNavButton ?? ""} ${cn.customerStoriesNavPrev ?? ""}`}
208
+ onClick={() => goTo(activeIndex - 1)}
209
+ disabled={activeIndex === 0}
210
+ aria-label="Previous customer story"
211
+ >
212
+ <Chevron dir="left" />
213
+ </button>
214
+ <button
215
+ type="button"
216
+ className={`${cn.customerStoriesNavButton ?? ""} ${cn.customerStoriesNavNext ?? ""}`}
217
+ onClick={() => goTo(activeIndex + 1)}
218
+ disabled={activeIndex >= maxIndex}
219
+ aria-label="Next customer story"
220
+ >
221
+ <Chevron dir="right" />
222
+ </button>
223
+ </>
224
+ ) : null}
225
+
226
+ <div ref={viewportRef} className={cn.customerStoriesViewport ?? ""}>
227
+ <div
228
+ ref={trackRef}
229
+ className={cn.customerStoriesTrack ?? ""}
230
+ style={{ transform: `translate3d(-${translateX}px, 0, 0)` }}
231
+ >
232
+ {items.map((story, index) => (
233
+ <div
234
+ key={story.id}
235
+ data-story-slide
236
+ className={cn.customerStoriesSlide ?? ""}
237
+ aria-hidden={
238
+ index < activeIndex || index >= activeIndex + perView
239
+ }
240
+ >
241
+ <StoryCard
242
+ story={story}
243
+ classNames={{
244
+ card: cn.customerStoriesCard ?? "",
245
+ imageWrap: cn.customerStoriesImageWrap ?? "",
246
+ image: cn.customerStoriesImage ?? "",
247
+ body: cn.customerStoriesBody ?? "",
248
+ quote: cn.customerStoriesQuote ?? "",
249
+ stars: cn.customerStoriesStars ?? "",
250
+ starFilled: cn.customerStoriesStarFilled ?? "",
251
+ starMuted: cn.customerStoriesStarMuted ?? "",
252
+ name: cn.customerStoriesName ?? "",
253
+ }}
254
+ />
255
+ </div>
256
+ ))}
257
+ </div>
258
+ </div>
259
+ </div>
260
+
261
+ {dotCount > 1 ? (
262
+ <div
263
+ className={cn.customerStoriesDots ?? ""}
264
+ role="tablist"
265
+ aria-label="Story slides"
266
+ >
267
+ {Array.from({ length: dotCount }).map((_, i) => (
268
+ <button
269
+ key={i}
270
+ type="button"
271
+ role="tab"
272
+ aria-selected={i === activeIndex}
273
+ aria-label={`Go to story ${i + 1}`}
274
+ className={`${cn.customerStoriesDot ?? ""} ${
275
+ i === activeIndex ? cn.customerStoriesDotActive ?? "" : ""
276
+ }`}
277
+ onClick={() => goTo(i)}
278
+ />
279
+ ))}
280
+ </div>
281
+ ) : null}
282
+ </div>
283
+ </div>
284
+ </section>
285
+ )
286
+ }
@@ -0,0 +1,43 @@
1
+ type CustomerStoriesSectionHeaderProps = {
2
+ eyebrow?: string | null
3
+ title: string
4
+ id?: string
5
+ classNames: {
6
+ wrap: string
7
+ inner: string
8
+ header: string
9
+ eyebrow: string
10
+ heading: string
11
+ accent: string
12
+ accentLine: string
13
+ accentGem: string
14
+ }
15
+ }
16
+
17
+ export default function CustomerStoriesSectionHeader({
18
+ eyebrow,
19
+ title,
20
+ id,
21
+ classNames: cn,
22
+ }: CustomerStoriesSectionHeaderProps) {
23
+ const trimmed = title.trim()
24
+ if (!trimmed) return null
25
+
26
+ return (
27
+ <div className={cn.wrap}>
28
+ <div className={cn.inner}>
29
+ <header className={cn.header}>
30
+ {eyebrow?.trim() ? <p className={cn.eyebrow}>{eyebrow.trim()}</p> : null}
31
+ <h2 id={id} className={cn.heading}>
32
+ {trimmed}
33
+ </h2>
34
+ <div className={cn.accent} aria-hidden>
35
+ <span className={cn.accentLine} />
36
+ <span className={cn.accentGem} />
37
+ <span className={`${cn.accentLine} rotate-180`} />
38
+ </div>
39
+ </header>
40
+ </div>
41
+ </div>
42
+ )
43
+ }
@@ -0,0 +1,14 @@
1
+ export type CustomerStoryItem = {
2
+ id: string
3
+ image: string
4
+ text: string
5
+ name: string
6
+ rating?: number
7
+ location?: string
8
+ }
9
+
10
+ export type CustomerStoriesContent = {
11
+ title?: string
12
+ eyebrow?: string
13
+ items: CustomerStoryItem[]
14
+ }
@@ -0,0 +1,15 @@
1
+ import { customerStoriesSectionDefaults } from "medusa-ui-home-config/sections/customerStories/defaults"
2
+ import type { CustomerStoryItem } from "./component/types"
3
+
4
+ export type CustomerStoriesCopy = {
5
+ eyebrow: string
6
+ title: string
7
+ items: CustomerStoryItem[]
8
+ }
9
+
10
+ /** UI fallback copy — sourced from medusa-ui-home-config (single canonical data.json). */
11
+ export const DEFAULT_CUSTOMER_STORIES_COPY: CustomerStoriesCopy = {
12
+ eyebrow: customerStoriesSectionDefaults.eyebrow,
13
+ title: customerStoriesSectionDefaults.title,
14
+ items: customerStoriesSectionDefaults.items as CustomerStoryItem[],
15
+ }
@@ -0,0 +1,17 @@
1
+ export { default } from "./section"
2
+ export * from "./section"
3
+ export {
4
+ defaultTheme,
5
+ mergeSectionTheme,
6
+ type ThemeOverrides,
7
+ } from "./theme"
8
+ export { sectionTheme } from "./theme"
9
+ export {
10
+ DEFAULT_CUSTOMER_STORIES_COPY,
11
+ type CustomerStoriesCopy,
12
+ } from "./copy-defaults"
13
+ export type {
14
+ CustomerStoryItem,
15
+ CustomerStoriesContent,
16
+ } from "./component/types"
17
+ export * from "medusa-ui-home-config/sections/customerStories"
@@ -0,0 +1,29 @@
1
+ import type { ThemeOverrides } from "./theme"
2
+ import type { CustomerStoriesSectionData } from "medusa-storefront-data/home/sections/customerStories"
3
+ import CustomerStories from "./component"
4
+ import {
5
+ copyEyebrow,
6
+ copyTitle,
7
+ } from "medusa-ui-home/home/lib/section-copy"
8
+
9
+ export type CustomerStoriesSectionProps = CustomerStoriesSectionData & {
10
+ /** Override class names for slots this section uses. */
11
+ theme?: ThemeOverrides
12
+ }
13
+
14
+ export function CustomerStoriesSection({
15
+ customerStories,
16
+ copy,
17
+ theme,
18
+ }: CustomerStoriesSectionProps) {
19
+ return (
20
+ <CustomerStories
21
+ classNames={theme}
22
+ title={copyTitle(copy) || customerStories.title}
23
+ eyebrow={copyEyebrow(copy) || customerStories.eyebrow}
24
+ items={customerStories.items}
25
+ />
26
+ )
27
+ }
28
+
29
+ export default CustomerStoriesSection
@@ -0,0 +1,67 @@
1
+ import { mergeThemeSlots } from "medusa-storefront-theme-base"
2
+ import { pickThemeKeys } from "../../theme/pick-theme"
3
+ import { sharedHomeTheme } from "../../theme/shared"
4
+
5
+ /** Slots unique to this section (merged into the global home theme). */
6
+ export const sectionTheme = {
7
+ customerStoriesSection:
8
+ "w-full bg-[color-mix(in_srgb,var(--sf-color-secondary)_8%,var(--sf-color-background))] py-10 md:py-12 lg:py-14",
9
+ customerStoriesInner: "mx-auto w-full max-w-[1440px] px-4 sm:px-6 lg:px-8",
10
+ customerStoriesHeaderWrap: "w-full px-4 sm:px-6 md:px-8 lg:px-12",
11
+ customerStoriesHeaderInner: "mx-auto w-full max-w-[1440px]",
12
+ customerStoriesHeader:
13
+ "mb-5 flex flex-col items-center text-center sm:mb-6 md:mb-7",
14
+ customerStoriesEyebrow:
15
+ "mb-1.5 text-[0.6rem] font-medium uppercase tracking-[0.24em] text-[var(--sf-color-text-muted)] sm:text-[0.65rem] [font-family:var(--sf-font-family)]",
16
+ customerStoriesTitle:
17
+ "text-2xl font-semibold leading-tight tracking-tight text-[var(--sf-color-primary)] sm:text-[1.75rem] md:text-3xl [font-family:var(--sf-font-family-heading)]",
18
+ customerStoriesAccent: "mx-auto mt-3 flex items-center justify-center gap-2",
19
+ customerStoriesAccentLine:
20
+ "h-px w-8 bg-gradient-to-r from-transparent to-[var(--sf-color-secondary)] sm:w-10",
21
+ customerStoriesAccentGem: "h-1 w-1 rotate-45 bg-[var(--sf-color-secondary)]",
22
+ customerStoriesCarouselWrap: "relative",
23
+ customerStoriesSlidesRow: "relative px-8 sm:px-10 md:px-12",
24
+ customerStoriesViewport: "w-full overflow-hidden",
25
+ customerStoriesTrack:
26
+ "flex gap-5 motion-safe:transition-transform motion-safe:duration-[550ms] motion-safe:ease-[cubic-bezier(0.4,0,0.2,1)]",
27
+ customerStoriesSlide: "w-full shrink-0 md:w-[calc((100%-20px)/2)]",
28
+ customerStoriesCard:
29
+ "flex h-[250px] w-full flex-row overflow-hidden rounded-[8px] border border-[var(--sf-color-border)] bg-[var(--sf-color-surface)] shadow-sm md:h-[272px]",
30
+ customerStoriesImageWrap:
31
+ "relative h-full w-[40%] shrink-0 overflow-hidden bg-[color-mix(in_srgb,var(--sf-color-text-muted)_12%,var(--sf-color-background-alt))]",
32
+ customerStoriesImage: "block h-full w-full object-cover object-top",
33
+ customerStoriesBody:
34
+ "flex min-w-0 flex-1 flex-col items-center justify-center px-6 py-5 text-center md:px-8 md:py-6",
35
+ customerStoriesQuote:
36
+ "max-w-[15.5rem] text-[13px] font-normal italic leading-[1.7] text-[var(--sf-color-text-muted)] sm:max-w-[16.5rem] sm:text-[14px] md:max-w-[17.5rem] [font-family:var(--sf-font-family-heading)]",
37
+ customerStoriesStars: "mt-3 flex items-center gap-1",
38
+ customerStoriesStarFilled: "text-sm leading-none text-[var(--sf-color-secondary)]",
39
+ customerStoriesStarMuted:
40
+ "text-sm leading-none text-[color-mix(in_srgb,var(--sf-color-text-muted)_35%,transparent)]",
41
+ customerStoriesName:
42
+ "mt-[1.125rem] text-[14px] font-bold leading-tight text-[var(--sf-color-text)] md:mt-5 md:text-[15px] [font-family:var(--sf-font-family)]",
43
+ customerStoriesNavButton:
44
+ "absolute top-1/2 z-20 flex h-8 w-8 -translate-y-1/2 items-center justify-center border-0 bg-transparent p-0 text-[var(--sf-color-text-muted)] transition-colors hover:text-[var(--sf-color-text)] disabled:pointer-events-none disabled:opacity-25",
45
+ customerStoriesNavPrev: "left-0",
46
+ customerStoriesNavNext: "right-0",
47
+ customerStoriesDots: "mt-7 flex items-center justify-center gap-2 md:mt-8",
48
+ customerStoriesDot:
49
+ "h-[7px] w-[7px] rounded-full bg-[color-mix(in_srgb,var(--sf-color-text-muted)_35%,transparent)] p-0 transition-colors duration-300",
50
+ customerStoriesDotActive: "!bg-[var(--sf-color-text)]",
51
+ } as const
52
+
53
+ export const themeKeys = ["section"] as const
54
+
55
+ const sharedDefaults = pickThemeKeys(sharedHomeTheme, themeKeys)
56
+
57
+ export const defaultTheme = {
58
+ ...sharedDefaults,
59
+ ...sectionTheme,
60
+ } as const
61
+
62
+ export type ThemeOverrides = Partial<typeof defaultTheme>
63
+
64
+ /** Merge site or page-level overrides onto this section's default theme slots. */
65
+ export function mergeSectionTheme(overrides?: ThemeOverrides) {
66
+ return mergeThemeSlots(defaultTheme, overrides)
67
+ }
@@ -8,6 +8,7 @@ import { sectionTheme as baptismPicksSectionTheme } from "../home/sections/bapti
8
8
  import { sectionTheme as testimonialsSectionTheme } from "../home/sections/testimonials/theme"
9
9
  import { sectionTheme as instagramPostsSectionTheme } from "../home/sections/instagramPosts/theme"
10
10
  import { sectionTheme as videoStoriesSectionTheme } from "../home/sections/videoStories/theme"
11
+ import { sectionTheme as customerStoriesSectionTheme } from "../home/sections/customerStories/theme"
11
12
 
12
13
  export const defaultHomeThemeClassNames = {
13
14
  ...sharedHomeTheme,
@@ -19,6 +20,7 @@ export const defaultHomeThemeClassNames = {
19
20
  ...testimonialsSectionTheme,
20
21
  ...instagramPostsSectionTheme,
21
22
  ...videoStoriesSectionTheme,
23
+ ...customerStoriesSectionTheme,
22
24
  } as const
23
25
 
24
26
  export type HomeThemeSlots = {