@pradip1995/theme-sahsha 3.1.1 → 3.1.3
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 +1 -1
- package/src/blocks/home/ShopByAge/collections-showcase-client.tsx +22 -3
- package/src/components/product-carousel.tsx +140 -13
- package/src/slots/product/ProductActions/ProductCTASection.tsx +43 -56
- package/src/slots/product/ProductActions/ProductDetailsSection.tsx +2 -2
- package/src/slots/product/ProductCard/index.tsx +3 -2
- package/src/tokens/theme.css +266 -30
package/package.json
CHANGED
|
@@ -32,9 +32,16 @@ const CollectionsShowcaseClient = ({
|
|
|
32
32
|
}: CollectionsShowcaseClientProps) => {
|
|
33
33
|
const viewportRef = useRef<HTMLDivElement>(null)
|
|
34
34
|
const [activeId, setActiveId] = useState(() => getDefaultActiveTabId(tabs))
|
|
35
|
+
const [contentAnimating, setContentAnimating] = useState(false)
|
|
35
36
|
|
|
36
37
|
const activeProducts = productsByCollection[activeId] ?? []
|
|
37
38
|
|
|
39
|
+
const handleTabClick = (tabId: string) => {
|
|
40
|
+
if (tabId === activeId) return
|
|
41
|
+
setActiveId(tabId)
|
|
42
|
+
setContentAnimating(true)
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
useEffect(() => {
|
|
39
46
|
const viewport = viewportRef.current
|
|
40
47
|
if (viewport) {
|
|
@@ -42,6 +49,13 @@ const CollectionsShowcaseClient = ({
|
|
|
42
49
|
}
|
|
43
50
|
}, [activeId])
|
|
44
51
|
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!contentAnimating) return
|
|
54
|
+
|
|
55
|
+
const timer = window.setTimeout(() => setContentAnimating(false), 360)
|
|
56
|
+
return () => window.clearTimeout(timer)
|
|
57
|
+
}, [contentAnimating, activeId])
|
|
58
|
+
|
|
45
59
|
const scrollByPage = (direction: "prev" | "next") => {
|
|
46
60
|
const viewport = viewportRef.current
|
|
47
61
|
if (!viewport) return
|
|
@@ -69,9 +83,9 @@ const CollectionsShowcaseClient = ({
|
|
|
69
83
|
type="button"
|
|
70
84
|
className={`collections-showcase__tab${isActive ? " is-active" : ""}`}
|
|
71
85
|
aria-current={isActive ? "true" : undefined}
|
|
72
|
-
onClick={() =>
|
|
86
|
+
onClick={() => handleTabClick(tab.id)}
|
|
73
87
|
>
|
|
74
|
-
{tab.title}
|
|
88
|
+
<span className="collections-showcase__tab-label">{tab.title}</span>
|
|
75
89
|
</button>
|
|
76
90
|
)
|
|
77
91
|
})}
|
|
@@ -88,7 +102,12 @@ const CollectionsShowcaseClient = ({
|
|
|
88
102
|
</button>
|
|
89
103
|
|
|
90
104
|
<div ref={viewportRef} className="collections-showcase__viewport">
|
|
91
|
-
<div
|
|
105
|
+
<div
|
|
106
|
+
key={activeId}
|
|
107
|
+
className={`collections-showcase__track${
|
|
108
|
+
contentAnimating ? " collections-showcase__track--enter" : ""
|
|
109
|
+
}`}
|
|
110
|
+
>
|
|
92
111
|
{activeProducts.length > 0 ? (
|
|
93
112
|
activeProducts.map((product) => (
|
|
94
113
|
<div key={product.id} className="collections-showcase__slide">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import { useRef } from "react"
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react"
|
|
4
4
|
import { ChevronLeft, ChevronRight } from "lucide-react"
|
|
5
5
|
import { HttpTypes } from "@medusajs/types"
|
|
6
6
|
import type { ProductCardRating } from "@core/types/product-card"
|
|
@@ -12,36 +12,160 @@ type ProductCarouselProps = {
|
|
|
12
12
|
ratings?: ProductCardRating[]
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
const AUTO_SCROLL_MS = 4200
|
|
16
|
+
const AUTO_SCROLL_RESUME_MS = 5000
|
|
17
|
+
|
|
15
18
|
const ProductCarousel = ({ products, region, ratings }: ProductCarouselProps) => {
|
|
16
19
|
const viewportRef = useRef<HTMLDivElement>(null)
|
|
17
20
|
const trackRef = useRef<HTMLDivElement>(null)
|
|
21
|
+
const pauseAutoScrollRef = useRef(false)
|
|
22
|
+
const resumeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
23
|
+
const [canScrollLeft, setCanScrollLeft] = useState(false)
|
|
24
|
+
const [canScrollRight, setCanScrollRight] = useState(true)
|
|
25
|
+
const [reduceMotion, setReduceMotion] = useState(false)
|
|
18
26
|
|
|
19
|
-
const
|
|
27
|
+
const getSlideStep = useCallback(() => {
|
|
20
28
|
const viewport = viewportRef.current
|
|
21
29
|
const track = trackRef.current
|
|
22
|
-
if (!viewport || !track) return
|
|
30
|
+
if (!viewport || !track) return 0
|
|
23
31
|
|
|
24
32
|
const firstSlide = track.querySelector<HTMLElement>(".bestsellers-carousel__slide")
|
|
25
|
-
if (!firstSlide) return
|
|
33
|
+
if (!firstSlide) return viewport.clientWidth * 0.85
|
|
26
34
|
|
|
27
35
|
const trackStyles = getComputedStyle(track)
|
|
28
36
|
const gap =
|
|
29
37
|
Number.parseFloat(trackStyles.columnGap || trackStyles.gap || "0") || 0
|
|
30
|
-
const slideStep = firstSlide.offsetWidth + gap
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
return firstSlide.offsetWidth + gap
|
|
40
|
+
}, [])
|
|
41
|
+
|
|
42
|
+
const updateScrollState = useCallback(() => {
|
|
43
|
+
const viewport = viewportRef.current
|
|
44
|
+
if (!viewport) return
|
|
45
|
+
|
|
46
|
+
const { scrollLeft, scrollWidth, clientWidth } = viewport
|
|
47
|
+
const maxScroll = scrollWidth - clientWidth
|
|
48
|
+
|
|
49
|
+
setCanScrollLeft(scrollLeft > 4)
|
|
50
|
+
setCanScrollRight(scrollLeft < maxScroll - 4)
|
|
51
|
+
}, [])
|
|
52
|
+
|
|
53
|
+
const pauseAutoScroll = useCallback(() => {
|
|
54
|
+
pauseAutoScrollRef.current = true
|
|
55
|
+
|
|
56
|
+
if (resumeTimerRef.current) {
|
|
57
|
+
clearTimeout(resumeTimerRef.current)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
resumeTimerRef.current = setTimeout(() => {
|
|
61
|
+
pauseAutoScrollRef.current = false
|
|
62
|
+
}, AUTO_SCROLL_RESUME_MS)
|
|
63
|
+
}, [])
|
|
64
|
+
|
|
65
|
+
const scrollBySlide = useCallback(
|
|
66
|
+
(direction: "prev" | "next") => {
|
|
67
|
+
const viewport = viewportRef.current
|
|
68
|
+
if (!viewport) return
|
|
69
|
+
|
|
70
|
+
pauseAutoScroll()
|
|
71
|
+
|
|
72
|
+
const slideStep = getSlideStep()
|
|
73
|
+
viewport.scrollBy({
|
|
74
|
+
left: direction === "next" ? slideStep : -slideStep,
|
|
75
|
+
behavior: "smooth",
|
|
76
|
+
})
|
|
77
|
+
},
|
|
78
|
+
[getSlideStep, pauseAutoScroll]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const media = window.matchMedia("(prefers-reduced-motion: reduce)")
|
|
83
|
+
const update = () => setReduceMotion(media.matches)
|
|
84
|
+
update()
|
|
85
|
+
media.addEventListener("change", update)
|
|
86
|
+
return () => media.removeEventListener("change", update)
|
|
87
|
+
}, [])
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const viewport = viewportRef.current
|
|
91
|
+
if (!viewport) return
|
|
92
|
+
|
|
93
|
+
updateScrollState()
|
|
94
|
+
|
|
95
|
+
const onScroll = () => updateScrollState()
|
|
96
|
+
viewport.addEventListener("scroll", onScroll, { passive: true })
|
|
97
|
+
window.addEventListener("resize", updateScrollState)
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
viewport.removeEventListener("scroll", onScroll)
|
|
101
|
+
window.removeEventListener("resize", updateScrollState)
|
|
102
|
+
}
|
|
103
|
+
}, [products, updateScrollState])
|
|
104
|
+
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (reduceMotion || products.length < 3) return
|
|
107
|
+
|
|
108
|
+
const viewport = viewportRef.current
|
|
109
|
+
if (!viewport) return
|
|
110
|
+
|
|
111
|
+
const tick = () => {
|
|
112
|
+
if (pauseAutoScrollRef.current) return
|
|
113
|
+
|
|
114
|
+
const { scrollLeft, scrollWidth, clientWidth } = viewport
|
|
115
|
+
const maxScroll = scrollWidth - clientWidth
|
|
116
|
+
const step = getSlideStep()
|
|
117
|
+
|
|
118
|
+
if (maxScroll <= 0) return
|
|
119
|
+
|
|
120
|
+
if (scrollLeft >= maxScroll - 4) {
|
|
121
|
+
viewport.scrollTo({ left: 0, behavior: "smooth" })
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
viewport.scrollBy({ left: step, behavior: "smooth" })
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const intervalId = window.setInterval(tick, AUTO_SCROLL_MS)
|
|
129
|
+
|
|
130
|
+
return () => window.clearInterval(intervalId)
|
|
131
|
+
}, [reduceMotion, products, getSlideStep])
|
|
132
|
+
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
return () => {
|
|
135
|
+
if (resumeTimerRef.current) {
|
|
136
|
+
clearTimeout(resumeTimerRef.current)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, [])
|
|
37
140
|
|
|
38
141
|
return (
|
|
39
|
-
<div
|
|
142
|
+
<div
|
|
143
|
+
className="bestsellers-carousel"
|
|
144
|
+
onMouseEnter={pauseAutoScroll}
|
|
145
|
+
onTouchStart={pauseAutoScroll}
|
|
146
|
+
onPointerDown={pauseAutoScroll}
|
|
147
|
+
>
|
|
148
|
+
<div
|
|
149
|
+
className={`bestsellers-carousel__fade bestsellers-carousel__fade--left${
|
|
150
|
+
canScrollLeft ? " is-visible" : ""
|
|
151
|
+
}`}
|
|
152
|
+
aria-hidden
|
|
153
|
+
/>
|
|
154
|
+
<div
|
|
155
|
+
className={`bestsellers-carousel__fade bestsellers-carousel__fade--right${
|
|
156
|
+
canScrollRight ? " is-visible" : ""
|
|
157
|
+
}`}
|
|
158
|
+
aria-hidden
|
|
159
|
+
/>
|
|
160
|
+
|
|
40
161
|
<button
|
|
41
162
|
type="button"
|
|
42
|
-
className=
|
|
163
|
+
className={`bestsellers-carousel__nav bestsellers-carousel__nav--prev${
|
|
164
|
+
canScrollLeft ? "" : " is-hidden"
|
|
165
|
+
}`}
|
|
43
166
|
onClick={() => scrollBySlide("prev")}
|
|
44
167
|
aria-label="Previous products"
|
|
168
|
+
tabIndex={canScrollLeft ? 0 : -1}
|
|
45
169
|
>
|
|
46
170
|
<ChevronLeft size={20} strokeWidth={1.75} />
|
|
47
171
|
</button>
|
|
@@ -66,9 +190,12 @@ const ProductCarousel = ({ products, region, ratings }: ProductCarouselProps) =>
|
|
|
66
190
|
|
|
67
191
|
<button
|
|
68
192
|
type="button"
|
|
69
|
-
className=
|
|
193
|
+
className={`bestsellers-carousel__nav bestsellers-carousel__nav--next${
|
|
194
|
+
canScrollRight ? "" : " is-hidden"
|
|
195
|
+
}`}
|
|
70
196
|
onClick={() => scrollBySlide("next")}
|
|
71
197
|
aria-label="Next products"
|
|
198
|
+
tabIndex={canScrollRight ? 0 : -1}
|
|
72
199
|
>
|
|
73
200
|
<ChevronRight size={20} strokeWidth={1.75} />
|
|
74
201
|
</button>
|
|
@@ -99,13 +99,13 @@ export function ProductCTASection(props: ProductCTASectionProps) {
|
|
|
99
99
|
</p>
|
|
100
100
|
)}
|
|
101
101
|
|
|
102
|
-
<div className="flex items-stretch gap-3 w-full">
|
|
102
|
+
<div className="product-cta-row flex items-stretch gap-2 sm:gap-3 w-full flex-nowrap">
|
|
103
103
|
{variantInCart ? (
|
|
104
104
|
<div className="flex h-12 shrink-0 w-[120px] border border-brand-accent rounded-full overflow-hidden">
|
|
105
105
|
<button
|
|
106
106
|
onClick={handleDecreaseQuantity}
|
|
107
107
|
disabled={isAdding}
|
|
108
|
-
className="w-10 flex items-center justify-center hover:bg-surface-muted
|
|
108
|
+
className="w-10 flex items-center justify-center hover:bg-surface-muted disabled:opacity-50"
|
|
109
109
|
aria-label="Decrease quantity"
|
|
110
110
|
>
|
|
111
111
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
@@ -118,7 +118,7 @@ export function ProductCTASection(props: ProductCTASectionProps) {
|
|
|
118
118
|
<button
|
|
119
119
|
onClick={handleIncreaseQuantity}
|
|
120
120
|
disabled={isAdding || !inStock}
|
|
121
|
-
className="w-10 flex items-center justify-center hover:bg-surface-muted
|
|
121
|
+
className="w-10 flex items-center justify-center hover:bg-surface-muted disabled:opacity-50"
|
|
122
122
|
aria-label="Increase quantity"
|
|
123
123
|
>
|
|
124
124
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
@@ -170,63 +170,50 @@ export function ProductCTASection(props: ProductCTASectionProps) {
|
|
|
170
170
|
data-ga-event="add_to_bag_click"
|
|
171
171
|
data-ga-label={product.title || "Product"}
|
|
172
172
|
>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
173
|
+
<span className="product-cta-btn__label whitespace-nowrap">
|
|
174
|
+
{isAdding ? (
|
|
175
|
+
"Adding..."
|
|
176
|
+
) : !selectedVariant || !isValidVariant ? (
|
|
177
|
+
product.options?.every((opt) => options[opt.id]) ? (
|
|
178
|
+
"Unavailable"
|
|
179
|
+
) : (
|
|
180
|
+
"Select options"
|
|
181
|
+
)
|
|
182
|
+
) : !inStock ? (
|
|
183
|
+
inventoryLimit !== null &&
|
|
184
|
+
inventoryLimit + quantityInCart > 0 &&
|
|
185
|
+
quantity > inventoryLimit ? (
|
|
186
|
+
inventoryLimit <= 0 ? (
|
|
187
|
+
"Sold out"
|
|
188
|
+
) : (
|
|
189
|
+
"Max quantity"
|
|
190
|
+
)
|
|
191
|
+
) : (
|
|
192
|
+
"Sold out"
|
|
193
|
+
)
|
|
194
|
+
) : (
|
|
195
|
+
`Add to Cart${quantity > 1 ? ` (${quantity})` : ""}`
|
|
196
|
+
)}
|
|
197
|
+
</span>
|
|
194
198
|
</button>
|
|
195
199
|
)}
|
|
196
|
-
</div>
|
|
197
200
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<Spinner />
|
|
201
|
+
<button
|
|
202
|
+
onClick={handleBuyNow}
|
|
203
|
+
disabled={
|
|
204
|
+
!inStock ||
|
|
205
|
+
!!disabled ||
|
|
206
|
+
isAdding ||
|
|
207
|
+
isBuyingNow ||
|
|
208
|
+
(!isValidVariant && product.options?.every((opt) => options[opt.id]))
|
|
209
|
+
}
|
|
210
|
+
className={`${btnBase} flex-1 min-w-0 product-cta-btn--primary`}
|
|
211
|
+
>
|
|
212
|
+
<span className="product-cta-btn__label whitespace-nowrap">
|
|
213
|
+
{isBuyingNow ? "Processing..." : "Buy It Now"}
|
|
212
214
|
</span>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)}
|
|
216
|
-
</button>
|
|
215
|
+
</button>
|
|
216
|
+
</div>
|
|
217
217
|
</div>
|
|
218
218
|
)
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
function Spinner() {
|
|
222
|
-
return (
|
|
223
|
-
<svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
224
|
-
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
225
|
-
<path
|
|
226
|
-
className="opacity-75"
|
|
227
|
-
fill="currentColor"
|
|
228
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
229
|
-
/>
|
|
230
|
-
</svg>
|
|
231
|
-
)
|
|
232
|
-
}
|
|
@@ -78,8 +78,8 @@ export function ProductDetailsSection({ product }: { product: HttpTypes.StorePro
|
|
|
78
78
|
careGuide !== undefined &&
|
|
79
79
|
String(careGuide).trim() !== ""
|
|
80
80
|
const showProductDetails = hasProductDetailsContent(product)
|
|
81
|
-
const [isDetailsOpen, setIsDetailsOpen] = useState(
|
|
82
|
-
const [isDescriptionOpen, setIsDescriptionOpen] = useState(
|
|
81
|
+
const [isDetailsOpen, setIsDetailsOpen] = useState(true)
|
|
82
|
+
const [isDescriptionOpen, setIsDescriptionOpen] = useState(true)
|
|
83
83
|
const [isCareGuideOpen, setIsCareGuideOpen] = useState(false)
|
|
84
84
|
|
|
85
85
|
return (
|
|
@@ -6,6 +6,7 @@ import LocalizedClientLink from "@modules/common/components/localized-client-lin
|
|
|
6
6
|
import { getProductPrice } from "@core/util/get-product-price"
|
|
7
7
|
import { convertToLocale } from "@core/util/money"
|
|
8
8
|
import PlaceholderImage from "@modules/common/icons/placeholder-image"
|
|
9
|
+
import { productCardImageProps } from "@lib/product-image"
|
|
9
10
|
import ProductCardRatingDisplay from "@modules/common/components/product/product-card-rating"
|
|
10
11
|
import WishlistIcon from "@modules/common/components/product/wishlist-icon"
|
|
11
12
|
import {
|
|
@@ -128,10 +129,10 @@ export default function ProductCard({
|
|
|
128
129
|
{thumbnail ? (
|
|
129
130
|
<>
|
|
130
131
|
<Image
|
|
132
|
+
{...productCardImageProps}
|
|
131
133
|
src={thumbnail}
|
|
132
134
|
alt={product.title || "Product"}
|
|
133
135
|
fill
|
|
134
|
-
sizes="(max-width:640px) 50vw, (max-width:1024px) 33vw, 25vw"
|
|
135
136
|
className={clx(
|
|
136
137
|
"object-cover transition-opacity duration-500",
|
|
137
138
|
imageHover && secondImage ? "opacity-0" : "opacity-100"
|
|
@@ -139,10 +140,10 @@ export default function ProductCard({
|
|
|
139
140
|
/>
|
|
140
141
|
{secondImage && (
|
|
141
142
|
<Image
|
|
143
|
+
{...productCardImageProps}
|
|
142
144
|
src={secondImage}
|
|
143
145
|
alt=""
|
|
144
146
|
fill
|
|
145
|
-
sizes="(max-width:640px) 50vw, (max-width:1024px) 33vw, 25vw"
|
|
146
147
|
className={clx(
|
|
147
148
|
"object-cover transition-opacity duration-500",
|
|
148
149
|
imageHover ? "opacity-100" : "opacity-0"
|
package/src/tokens/theme.css
CHANGED
|
@@ -2583,9 +2583,21 @@ body.mobile-menu-open .promo-bar {
|
|
|
2583
2583
|
}
|
|
2584
2584
|
}
|
|
2585
2585
|
|
|
2586
|
+
@keyframes collections-showcase-tab-enter {
|
|
2587
|
+
from {
|
|
2588
|
+
opacity: 0;
|
|
2589
|
+
transform: translateY(10px);
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
to {
|
|
2593
|
+
opacity: 1;
|
|
2594
|
+
transform: translateY(0);
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2586
2598
|
.collections-showcase__tab {
|
|
2587
2599
|
margin: 0;
|
|
2588
|
-
padding: 0;
|
|
2600
|
+
padding: 0 0 0.4rem;
|
|
2589
2601
|
border: none;
|
|
2590
2602
|
background: none;
|
|
2591
2603
|
cursor: pointer;
|
|
@@ -2597,22 +2609,83 @@ body.mobile-menu-open .promo-bar {
|
|
|
2597
2609
|
line-height: 1.1;
|
|
2598
2610
|
letter-spacing: 0.01em;
|
|
2599
2611
|
color: #c8c8c8;
|
|
2600
|
-
|
|
2612
|
+
position: relative;
|
|
2613
|
+
transition:
|
|
2614
|
+
color 0.28s ease,
|
|
2615
|
+
transform 0.2s cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
2601
2616
|
-webkit-tap-highlight-color: transparent;
|
|
2602
2617
|
outline: none;
|
|
2603
2618
|
box-shadow: none;
|
|
2604
2619
|
}
|
|
2605
2620
|
|
|
2621
|
+
.collections-showcase__tab-label {
|
|
2622
|
+
display: inline-block;
|
|
2623
|
+
transition: transform 0.2s cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
.collections-showcase__tab::after {
|
|
2627
|
+
content: "";
|
|
2628
|
+
position: absolute;
|
|
2629
|
+
left: 0;
|
|
2630
|
+
right: 0;
|
|
2631
|
+
bottom: 0;
|
|
2632
|
+
height: 2px;
|
|
2633
|
+
border-radius: 999px;
|
|
2634
|
+
background: currentColor;
|
|
2635
|
+
transform: scaleX(0);
|
|
2636
|
+
transform-origin: center;
|
|
2637
|
+
transition: transform 0.32s cubic-bezier(0.34, 1.2, 0.64, 1);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2606
2640
|
.collections-showcase__tab.is-active {
|
|
2607
2641
|
color: #1a1a1a;
|
|
2608
2642
|
font-weight: 500;
|
|
2609
2643
|
}
|
|
2610
2644
|
|
|
2645
|
+
.collections-showcase__tab.is-active::after {
|
|
2646
|
+
transform: scaleX(1);
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2611
2649
|
.collections-showcase__tab:hover:not(.is-active),
|
|
2612
2650
|
.collections-showcase__tab:focus-visible:not(.is-active) {
|
|
2613
2651
|
color: #9a9a9a;
|
|
2614
2652
|
}
|
|
2615
2653
|
|
|
2654
|
+
.collections-showcase__tab:hover .collections-showcase__tab-label,
|
|
2655
|
+
.collections-showcase__tab:focus-visible .collections-showcase__tab-label {
|
|
2656
|
+
transform: translateY(-1px);
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
.collections-showcase__tab:active .collections-showcase__tab-label {
|
|
2660
|
+
transform: scale(0.96);
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
.collections-showcase__tab.is-active:active .collections-showcase__tab-label {
|
|
2664
|
+
transform: scale(0.98);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
.collections-showcase__track--enter {
|
|
2668
|
+
animation: collections-showcase-tab-enter 0.36s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2672
|
+
.collections-showcase__tab,
|
|
2673
|
+
.collections-showcase__tab-label,
|
|
2674
|
+
.collections-showcase__tab::after {
|
|
2675
|
+
transition: none;
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
.collections-showcase__tab:hover .collections-showcase__tab-label,
|
|
2679
|
+
.collections-showcase__tab:focus-visible .collections-showcase__tab-label,
|
|
2680
|
+
.collections-showcase__tab:active .collections-showcase__tab-label {
|
|
2681
|
+
transform: none;
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
.collections-showcase__track--enter {
|
|
2685
|
+
animation: none;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2616
2689
|
.collections-showcase__tab:focus,
|
|
2617
2690
|
.collections-showcase__tab:focus-visible {
|
|
2618
2691
|
outline: none;
|
|
@@ -3038,7 +3111,10 @@ body.mobile-menu-open .promo-bar {
|
|
|
3038
3111
|
overflow: hidden;
|
|
3039
3112
|
background: var(--color-brand-cream);
|
|
3040
3113
|
box-shadow: 0 4px 20px rgba(90, 42, 67, 0.07);
|
|
3041
|
-
|
|
3114
|
+
transform-origin: center;
|
|
3115
|
+
transition:
|
|
3116
|
+
box-shadow 0.35s ease,
|
|
3117
|
+
transform 0.28s cubic-bezier(0.34, 1.2, 0.64, 1);
|
|
3042
3118
|
}
|
|
3043
3119
|
|
|
3044
3120
|
.shop-by-category__card--top .shop-by-category__frame,
|
|
@@ -3048,7 +3124,13 @@ body.mobile-menu-open .promo-bar {
|
|
|
3048
3124
|
|
|
3049
3125
|
.shop-by-category__card:hover .shop-by-category__frame {
|
|
3050
3126
|
box-shadow: 0 12px 36px rgba(90, 42, 67, 0.15);
|
|
3051
|
-
transform: translateY(-3px);
|
|
3127
|
+
transform: translateY(-3px) scale(1.02);
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
.shop-by-category__card:active .shop-by-category__frame {
|
|
3131
|
+
box-shadow: 0 6px 24px rgba(90, 42, 67, 0.12);
|
|
3132
|
+
transform: translateY(-1px) scale(0.98);
|
|
3133
|
+
transition-duration: 0.15s;
|
|
3052
3134
|
}
|
|
3053
3135
|
|
|
3054
3136
|
.shop-by-category__image {
|
|
@@ -3219,6 +3301,25 @@ body.mobile-menu-open .promo-bar {
|
|
|
3219
3301
|
}
|
|
3220
3302
|
}
|
|
3221
3303
|
|
|
3304
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3305
|
+
.shop-by-category__frame,
|
|
3306
|
+
.shop-by-category__image,
|
|
3307
|
+
.shop-by-category__overlay,
|
|
3308
|
+
.shop-by-category__count,
|
|
3309
|
+
.shop-by-category__cta {
|
|
3310
|
+
transition: none;
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
.shop-by-category__card:hover .shop-by-category__frame,
|
|
3314
|
+
.shop-by-category__card:active .shop-by-category__frame {
|
|
3315
|
+
transform: none;
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
.shop-by-category__card:hover .shop-by-category__image {
|
|
3319
|
+
transform: none;
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3222
3323
|
/* ── Account login / register (Sahsha split layout) ──────── */
|
|
3223
3324
|
.account-login {
|
|
3224
3325
|
display: flex;
|
|
@@ -5215,22 +5316,31 @@ body.mobile-menu-open .promo-bar {
|
|
|
5215
5316
|
align-items: flex-start;
|
|
5216
5317
|
}
|
|
5217
5318
|
|
|
5218
|
-
#product-gallery-container
|
|
5319
|
+
#product-gallery-container,
|
|
5320
|
+
.product-images-sticky {
|
|
5219
5321
|
position: sticky !important;
|
|
5220
|
-
top: calc(var(--header-height) + var(--promo-bar-height, 0px) +
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5322
|
+
top: calc(var(--header-height) + var(--promo-bar-height, 0px) + 20px) !important;
|
|
5323
|
+
align-self: flex-start !important;
|
|
5324
|
+
width: 100% !important;
|
|
5325
|
+
min-width: 0 !important;
|
|
5326
|
+
height: fit-content !important;
|
|
5327
|
+
max-height: calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 40px) !important;
|
|
5328
|
+
overflow: visible !important;
|
|
5329
|
+
z-index: 2;
|
|
5224
5330
|
}
|
|
5225
5331
|
|
|
5226
|
-
.product-details-column
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5332
|
+
.product-details-column,
|
|
5333
|
+
.product-details-scroll {
|
|
5334
|
+
flex: 1;
|
|
5335
|
+
min-width: 0;
|
|
5336
|
+
width: 100% !important;
|
|
5337
|
+
height: auto !important;
|
|
5338
|
+
max-height: none !important;
|
|
5339
|
+
overflow: visible !important;
|
|
5340
|
+
overflow-x: visible !important;
|
|
5341
|
+
overflow-y: visible !important;
|
|
5342
|
+
overscroll-behavior: auto !important;
|
|
5343
|
+
padding-bottom: 0 !important;
|
|
5234
5344
|
}
|
|
5235
5345
|
|
|
5236
5346
|
.product-details-column::-webkit-scrollbar {
|
|
@@ -5238,12 +5348,21 @@ body.mobile-menu-open .promo-bar {
|
|
|
5238
5348
|
}
|
|
5239
5349
|
|
|
5240
5350
|
.product-gallery__stage-img {
|
|
5241
|
-
max-height:
|
|
5351
|
+
max-height: min(
|
|
5352
|
+
calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 5rem),
|
|
5353
|
+
680px
|
|
5354
|
+
);
|
|
5242
5355
|
}
|
|
5243
5356
|
|
|
5244
5357
|
.product-gallery__thumbs--scrollable {
|
|
5245
|
-
height:
|
|
5246
|
-
|
|
5358
|
+
height: min(
|
|
5359
|
+
calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 5rem),
|
|
5360
|
+
680px
|
|
5361
|
+
);
|
|
5362
|
+
max-height: min(
|
|
5363
|
+
calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 5rem),
|
|
5364
|
+
680px
|
|
5365
|
+
);
|
|
5247
5366
|
}
|
|
5248
5367
|
}
|
|
5249
5368
|
|
|
@@ -5275,15 +5394,18 @@ body.mobile-menu-open .promo-bar {
|
|
|
5275
5394
|
@media (min-width: 1024px) {
|
|
5276
5395
|
.product-gallery {
|
|
5277
5396
|
display: grid;
|
|
5278
|
-
grid-template-columns: 5.25rem
|
|
5397
|
+
grid-template-columns: 5.25rem auto;
|
|
5279
5398
|
gap: 0.375rem;
|
|
5280
5399
|
align-items: start;
|
|
5400
|
+
width: fit-content;
|
|
5401
|
+
max-width: 100%;
|
|
5281
5402
|
background: #ffffff;
|
|
5282
5403
|
}
|
|
5283
5404
|
|
|
5284
5405
|
.product-gallery--single {
|
|
5285
5406
|
display: block;
|
|
5286
|
-
|
|
5407
|
+
width: fit-content;
|
|
5408
|
+
max-width: 100%;
|
|
5287
5409
|
}
|
|
5288
5410
|
|
|
5289
5411
|
.product-gallery__stage {
|
|
@@ -5308,8 +5430,14 @@ body.mobile-menu-open .promo-bar {
|
|
|
5308
5430
|
}
|
|
5309
5431
|
|
|
5310
5432
|
.product-gallery__thumbs--scrollable {
|
|
5311
|
-
height: min(
|
|
5312
|
-
|
|
5433
|
+
height: min(
|
|
5434
|
+
calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 5rem),
|
|
5435
|
+
680px
|
|
5436
|
+
);
|
|
5437
|
+
max-height: min(
|
|
5438
|
+
calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 5rem),
|
|
5439
|
+
680px
|
|
5440
|
+
);
|
|
5313
5441
|
}
|
|
5314
5442
|
|
|
5315
5443
|
.product-gallery__thumbs--static {
|
|
@@ -5359,16 +5487,18 @@ body.mobile-menu-open .promo-bar {
|
|
|
5359
5487
|
display: flex;
|
|
5360
5488
|
align-items: flex-start;
|
|
5361
5489
|
justify-content: center;
|
|
5362
|
-
width:
|
|
5490
|
+
width: fit-content;
|
|
5491
|
+
max-width: 100%;
|
|
5363
5492
|
min-height: 0;
|
|
5364
5493
|
overflow: visible;
|
|
5365
5494
|
}
|
|
5366
5495
|
|
|
5367
5496
|
.product-gallery__stage-img {
|
|
5368
5497
|
display: block;
|
|
5369
|
-
width:
|
|
5498
|
+
width: auto;
|
|
5499
|
+
max-width: 100%;
|
|
5370
5500
|
height: auto !important;
|
|
5371
|
-
max-height: min(
|
|
5501
|
+
max-height: min(70vh, 640px);
|
|
5372
5502
|
object-fit: contain !important;
|
|
5373
5503
|
object-position: center top;
|
|
5374
5504
|
}
|
|
@@ -5912,6 +6042,45 @@ body.mobile-menu-open .promo-bar {
|
|
|
5912
6042
|
color: #1a1a1a;
|
|
5913
6043
|
}
|
|
5914
6044
|
|
|
6045
|
+
/* Single-line CTA row: qty + Add to Cart + Buy It Now (no slide animation) */
|
|
6046
|
+
.product-cta-row,
|
|
6047
|
+
.product-cta-row > .product-cta-btn {
|
|
6048
|
+
flex-wrap: nowrap;
|
|
6049
|
+
}
|
|
6050
|
+
|
|
6051
|
+
.product-cta-btn::before {
|
|
6052
|
+
content: none !important;
|
|
6053
|
+
display: none !important;
|
|
6054
|
+
}
|
|
6055
|
+
|
|
6056
|
+
.product-cta-btn {
|
|
6057
|
+
overflow: visible;
|
|
6058
|
+
}
|
|
6059
|
+
|
|
6060
|
+
.product-cta-btn__label {
|
|
6061
|
+
transition: none;
|
|
6062
|
+
}
|
|
6063
|
+
|
|
6064
|
+
.product-cta-btn--primary:hover:not(:disabled) {
|
|
6065
|
+
background: #4a2236 !important;
|
|
6066
|
+
color: #ffffff !important;
|
|
6067
|
+
border-color: #4a2236 !important;
|
|
6068
|
+
}
|
|
6069
|
+
|
|
6070
|
+
.product-cta-btn--primary:hover:not(:disabled) .product-cta-btn__label {
|
|
6071
|
+
color: #ffffff !important;
|
|
6072
|
+
}
|
|
6073
|
+
|
|
6074
|
+
.product-cta-btn--secondary:hover:not(:disabled) {
|
|
6075
|
+
background: #5a2a43 !important;
|
|
6076
|
+
color: #ffffff !important;
|
|
6077
|
+
border-color: #5a2a43 !important;
|
|
6078
|
+
}
|
|
6079
|
+
|
|
6080
|
+
.product-cta-btn--secondary:hover:not(:disabled) .product-cta-btn__label {
|
|
6081
|
+
color: #ffffff !important;
|
|
6082
|
+
}
|
|
6083
|
+
|
|
5915
6084
|
.product-details__spec-heading {
|
|
5916
6085
|
margin: 1.25rem 0 0.85rem;
|
|
5917
6086
|
font-family: var(--font-sans);
|
|
@@ -10977,17 +11146,77 @@ body.checkout-modal-open nav {
|
|
|
10977
11146
|
.bestsellers-carousel {
|
|
10978
11147
|
position: relative;
|
|
10979
11148
|
container-type: inline-size;
|
|
10980
|
-
|
|
11149
|
+
}
|
|
11150
|
+
|
|
11151
|
+
.new-arrivals .bestsellers-carousel,
|
|
11152
|
+
.bestsellers .bestsellers-carousel {
|
|
11153
|
+
margin-inline: -0.5rem;
|
|
11154
|
+
padding-inline: 0.5rem;
|
|
11155
|
+
}
|
|
11156
|
+
|
|
11157
|
+
@media (min-width: 640px) {
|
|
11158
|
+
.new-arrivals .bestsellers-carousel,
|
|
11159
|
+
.bestsellers .bestsellers-carousel {
|
|
11160
|
+
margin-inline: -0.75rem;
|
|
11161
|
+
padding-inline: 0.75rem;
|
|
11162
|
+
}
|
|
11163
|
+
}
|
|
11164
|
+
|
|
11165
|
+
@media (min-width: 1024px) {
|
|
11166
|
+
.new-arrivals .bestsellers-carousel,
|
|
11167
|
+
.bestsellers .bestsellers-carousel {
|
|
11168
|
+
margin-inline: -1rem;
|
|
11169
|
+
padding-inline: 1rem;
|
|
11170
|
+
}
|
|
11171
|
+
}
|
|
11172
|
+
|
|
11173
|
+
.bestsellers-carousel__fade {
|
|
11174
|
+
position: absolute;
|
|
11175
|
+
top: 0;
|
|
11176
|
+
bottom: 0.25rem;
|
|
11177
|
+
z-index: 1;
|
|
11178
|
+
width: 2.5rem;
|
|
11179
|
+
pointer-events: none;
|
|
11180
|
+
opacity: 0;
|
|
11181
|
+
transition: opacity 0.3s ease;
|
|
11182
|
+
}
|
|
11183
|
+
|
|
11184
|
+
.bestsellers-carousel__fade.is-visible {
|
|
11185
|
+
opacity: 1;
|
|
11186
|
+
}
|
|
11187
|
+
|
|
11188
|
+
.bestsellers-carousel__fade--left {
|
|
11189
|
+
left: 0;
|
|
11190
|
+
background: linear-gradient(
|
|
11191
|
+
to right,
|
|
11192
|
+
var(--color-page-bg, #faf9f7) 0%,
|
|
11193
|
+
transparent 100%
|
|
11194
|
+
);
|
|
11195
|
+
}
|
|
11196
|
+
|
|
11197
|
+
.bestsellers-carousel__fade--right {
|
|
11198
|
+
right: 0;
|
|
11199
|
+
background: linear-gradient(
|
|
11200
|
+
to left,
|
|
11201
|
+
var(--color-page-bg, #faf9f7) 0%,
|
|
11202
|
+
transparent 100%
|
|
11203
|
+
);
|
|
10981
11204
|
}
|
|
10982
11205
|
|
|
10983
11206
|
.bestsellers-carousel__viewport {
|
|
10984
11207
|
overflow-x: auto;
|
|
10985
11208
|
overflow-y: hidden;
|
|
10986
|
-
scroll-snap-type: x
|
|
11209
|
+
scroll-snap-type: x proximity;
|
|
10987
11210
|
scroll-behavior: smooth;
|
|
11211
|
+
scroll-padding-inline: 0.5rem;
|
|
10988
11212
|
-webkit-overflow-scrolling: touch;
|
|
10989
11213
|
scrollbar-width: none;
|
|
10990
11214
|
overscroll-behavior-x: contain;
|
|
11215
|
+
cursor: grab;
|
|
11216
|
+
}
|
|
11217
|
+
|
|
11218
|
+
.bestsellers-carousel__viewport:active {
|
|
11219
|
+
cursor: grabbing;
|
|
10991
11220
|
}
|
|
10992
11221
|
|
|
10993
11222
|
.bestsellers-carousel__viewport::-webkit-scrollbar {
|
|
@@ -10999,11 +11228,13 @@ body.checkout-modal-open nav {
|
|
|
10999
11228
|
gap: 0.75rem;
|
|
11000
11229
|
width: max-content;
|
|
11001
11230
|
padding-bottom: 0.25rem;
|
|
11231
|
+
padding-inline-end: 0.5rem;
|
|
11002
11232
|
}
|
|
11003
11233
|
|
|
11004
11234
|
@media (min-width: 768px) {
|
|
11005
11235
|
.bestsellers-carousel__track {
|
|
11006
11236
|
gap: 1rem;
|
|
11237
|
+
padding-inline-end: 1rem;
|
|
11007
11238
|
}
|
|
11008
11239
|
}
|
|
11009
11240
|
|
|
@@ -11014,7 +11245,6 @@ body.checkout-modal-open nav {
|
|
|
11014
11245
|
min-width: 0;
|
|
11015
11246
|
overflow: hidden;
|
|
11016
11247
|
scroll-snap-align: start;
|
|
11017
|
-
scroll-snap-stop: always;
|
|
11018
11248
|
}
|
|
11019
11249
|
|
|
11020
11250
|
.bestsellers-carousel__slide .product-card {
|
|
@@ -11083,6 +11313,12 @@ body.checkout-modal-open nav {
|
|
|
11083
11313
|
transform: translateY(-1px);
|
|
11084
11314
|
}
|
|
11085
11315
|
|
|
11316
|
+
.bestsellers-carousel__nav.is-hidden {
|
|
11317
|
+
opacity: 0;
|
|
11318
|
+
visibility: hidden;
|
|
11319
|
+
pointer-events: none;
|
|
11320
|
+
}
|
|
11321
|
+
|
|
11086
11322
|
/* ── Promotional Banners ─────────────────────────────────── */
|
|
11087
11323
|
.promo-banners {
|
|
11088
11324
|
width: 100%;
|