@pradip1995/theme-sahsha 3.1.1 → 3.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pradip1995/theme-sahsha",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "Sahsha storefront theme — Impulse-based editorial layout, luxury nav, and brand tokens",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -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 transition-colors disabled:opacity-50"
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 transition-colors disabled:opacity-50"
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
- {isAdding ? (
174
- <span className="product-cta-btn__label">
175
- <Spinner />
176
- </span>
177
- ) : (
178
- <span className="product-cta-btn__label">
179
- {!selectedVariant || !isValidVariant
180
- ? product.options?.every((opt) => options[opt.id])
181
- ? "Unavailable"
182
- : "Select options"
183
- : !inStock
184
- ? inventoryLimit !== null &&
185
- inventoryLimit + quantityInCart > 0 &&
186
- quantity > inventoryLimit
187
- ? inventoryLimit <= 0
188
- ? "Sold out"
189
- : "Max quantity reached"
190
- : "Sold out"
191
- : `Add to Cart${quantity > 1 ? ` (${quantity})` : ""}`}
192
- </span>
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
- <button
199
- onClick={handleBuyNow}
200
- disabled={
201
- !inStock ||
202
- !!disabled ||
203
- isAdding ||
204
- isBuyingNow ||
205
- (!isValidVariant && product.options?.every((opt) => options[opt.id]))
206
- }
207
- className={`${btnBase} w-full product-cta-btn--primary`}
208
- >
209
- {isBuyingNow ? (
210
- <span className="product-cta-btn__label">
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
- <span className="product-cta-btn__label">Buy It Now</span>
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(false)
82
- const [isDescriptionOpen, setIsDescriptionOpen] = useState(false)
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"
@@ -5215,22 +5215,28 @@ body.mobile-menu-open .promo-bar {
5215
5215
  align-items: flex-start;
5216
5216
  }
5217
5217
 
5218
- #product-gallery-container {
5218
+ #product-gallery-container,
5219
+ .product-images-sticky {
5219
5220
  position: sticky !important;
5220
- top: calc(var(--header-height) + var(--promo-bar-height, 0px) + 1rem) !important;
5221
- max-height: calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 2rem);
5222
- overflow: hidden;
5223
- z-index: 1;
5221
+ top: calc(var(--header-height) + var(--promo-bar-height, 0px) + 20px) !important;
5222
+ align-self: flex-start !important;
5223
+ height: fit-content !important;
5224
+ max-height: calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 40px) !important;
5225
+ overflow: visible !important;
5226
+ z-index: 2;
5224
5227
  }
5225
5228
 
5226
- .product-details-column {
5227
- max-height: calc(100vh - var(--header-height) - var(--promo-bar-height, 0px) - 2rem);
5228
- overflow-x: hidden;
5229
- overflow-y: auto;
5230
- overscroll-behavior: contain;
5231
- -ms-overflow-style: none;
5232
- scrollbar-width: none;
5233
- padding-bottom: 1.5rem;
5229
+ .product-details-column,
5230
+ .product-details-scroll {
5231
+ flex: 1;
5232
+ min-width: 0;
5233
+ height: auto !important;
5234
+ max-height: none !important;
5235
+ overflow: visible !important;
5236
+ overflow-x: visible !important;
5237
+ overflow-y: visible !important;
5238
+ overscroll-behavior: auto !important;
5239
+ padding-bottom: 0 !important;
5234
5240
  }
5235
5241
 
5236
5242
  .product-details-column::-webkit-scrollbar {
@@ -5912,6 +5918,45 @@ body.mobile-menu-open .promo-bar {
5912
5918
  color: #1a1a1a;
5913
5919
  }
5914
5920
 
5921
+ /* Single-line CTA row: qty + Add to Cart + Buy It Now (no slide animation) */
5922
+ .product-cta-row,
5923
+ .product-cta-row > .product-cta-btn {
5924
+ flex-wrap: nowrap;
5925
+ }
5926
+
5927
+ .product-cta-btn::before {
5928
+ content: none !important;
5929
+ display: none !important;
5930
+ }
5931
+
5932
+ .product-cta-btn {
5933
+ overflow: visible;
5934
+ }
5935
+
5936
+ .product-cta-btn__label {
5937
+ transition: none;
5938
+ }
5939
+
5940
+ .product-cta-btn--primary:hover:not(:disabled) {
5941
+ background: #4a2236 !important;
5942
+ color: #ffffff !important;
5943
+ border-color: #4a2236 !important;
5944
+ }
5945
+
5946
+ .product-cta-btn--primary:hover:not(:disabled) .product-cta-btn__label {
5947
+ color: #ffffff !important;
5948
+ }
5949
+
5950
+ .product-cta-btn--secondary:hover:not(:disabled) {
5951
+ background: #5a2a43 !important;
5952
+ color: #ffffff !important;
5953
+ border-color: #5a2a43 !important;
5954
+ }
5955
+
5956
+ .product-cta-btn--secondary:hover:not(:disabled) .product-cta-btn__label {
5957
+ color: #ffffff !important;
5958
+ }
5959
+
5915
5960
  .product-details__spec-heading {
5916
5961
  margin: 1.25rem 0 0.85rem;
5917
5962
  font-family: var(--font-sans);