@pradip1995/theme-impulse 1.1.4
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/README.md +31 -0
- package/assets/hero-desktop.svg +10 -0
- package/assets/hero-mobile.svg +9 -0
- package/assets/logo.svg +3 -0
- package/package.json +60 -0
- package/src/blocks/home/Features/index.tsx +37 -0
- package/src/blocks/home/Hero/index.tsx +102 -0
- package/src/blocks/home/LovedByMoms/index.tsx +59 -0
- package/src/blocks/home/NewArrivals/index.tsx +57 -0
- package/src/blocks/home/ShopByAge/index.tsx +82 -0
- package/src/blocks/home/ShopByCategory/index.tsx +92 -0
- package/src/blocks/home/Testimonials/index.tsx +130 -0
- package/src/blocks/home/WhyChooseUs/index.tsx +46 -0
- package/src/layouts/MainLayoutShell.tsx +14 -0
- package/src/primitives/Button.tsx +31 -0
- package/src/primitives/Card.tsx +32 -0
- package/src/primitives/index.ts +2 -0
- package/src/slots/account/ForgotPassword/index.tsx +1 -0
- package/src/slots/account/Login/index.tsx +1 -0
- package/src/slots/account/LoginTemplate/index.tsx +44 -0
- package/src/slots/account/Register/index.tsx +1 -0
- package/src/slots/cart/CartItem/index.tsx +11 -0
- package/src/slots/cart/CartSummary/index.tsx +13 -0
- package/src/slots/checkout/CheckoutForm/index.tsx +1 -0
- package/src/slots/checkout/CheckoutSummary/index.tsx +1 -0
- package/src/slots/layout/Footer/index.tsx +103 -0
- package/src/slots/layout/Nav/index.tsx +97 -0
- package/src/slots/layout/PromoBar/index.tsx +19 -0
- package/src/slots/layout/PromoBar/promo-bar-content.tsx +174 -0
- package/src/slots/order/OrderDetails/index.tsx +12 -0
- package/src/slots/product/ProductActions/ProductCTASection.tsx +191 -0
- package/src/slots/product/ProductActions/ProductDetailsSection.tsx +137 -0
- package/src/slots/product/ProductActions/ProductFeaturePanel.tsx +245 -0
- package/src/slots/product/ProductActions/ProductHighlightsSection.tsx +99 -0
- package/src/slots/product/ProductActions/ProductOptionsSection.tsx +234 -0
- package/src/slots/product/ProductActions/ProductPriceSection.tsx +53 -0
- package/src/slots/product/ProductActions/ProductTrustSection.tsx +84 -0
- package/src/slots/product/ProductActions/index.tsx +161 -0
- package/src/slots/product/ProductCard/index.tsx +132 -0
- package/src/slots/product/ProductInfo/index.tsx +40 -0
- package/src/templates/StorePage/index.tsx +154 -0
- package/src/tokens/colors.js +16 -0
- package/src/tokens/colors.ts +21 -0
- package/src/tokens/fonts.ts +13 -0
- package/src/tokens/index.ts +3 -0
- package/src/tokens/spacing.ts +9 -0
- package/src/tokens/theme.css +89 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { HttpTypes } from "@medusajs/types"
|
|
2
|
+
import ProductRating from "@modules/common/components/product/product-rating"
|
|
3
|
+
import ShareButton from "@modules/common/components/product/share-button"
|
|
4
|
+
import WishlistIcon from "@modules/common/components/product/wishlist-icon"
|
|
5
|
+
|
|
6
|
+
type ProductInfoProps = {
|
|
7
|
+
product: HttpTypes.StoreProduct
|
|
8
|
+
region: HttpTypes.StoreRegion
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function ProductInfo({ product }: ProductInfoProps) {
|
|
12
|
+
const metadataBrand = product.metadata?.brand as string | undefined
|
|
13
|
+
const brand = metadataBrand || product.collection?.title || "Valero"
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div id="product-info" className="flex flex-col gap-4 w-full">
|
|
17
|
+
<p className="text-[11px] font-semibold uppercase tracking-[var(--letter-spacing-nav)] text-[var(--color-text-muted)]">
|
|
18
|
+
{brand}
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<div className="flex items-start justify-between gap-4">
|
|
22
|
+
<h1
|
|
23
|
+
className="font-display text-2xl sm:text-3xl lg:text-4xl text-heading leading-heading flex-1"
|
|
24
|
+
data-testid="product-title"
|
|
25
|
+
>
|
|
26
|
+
{product.title}
|
|
27
|
+
</h1>
|
|
28
|
+
<div className="flex items-center gap-2 flex-shrink-0 pt-1">
|
|
29
|
+
{product.id && <WishlistIcon productId={product.id} />}
|
|
30
|
+
<ShareButton
|
|
31
|
+
productTitle={product.title ?? ""}
|
|
32
|
+
productHandle={product.handle ?? ""}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<ProductRating product={product} showButton={false} />
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Suspense } from "react"
|
|
2
|
+
import SkeletonProductGrid from "@modules/skeletons/templates/skeleton-product-grid"
|
|
3
|
+
import RefinementList from "@modules/store/components/refinement-list"
|
|
4
|
+
import { SortOptions } from "@modules/store/components/refinement-list/sort-products"
|
|
5
|
+
import MobileFilters from "@modules/store/components/mobile-filters"
|
|
6
|
+
import PaginatedProducts from "@modules/store/templates/paginated-products"
|
|
7
|
+
import { HttpTypes } from "@medusajs/types"
|
|
8
|
+
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
|
9
|
+
|
|
10
|
+
type StorePageProps = {
|
|
11
|
+
sortBy?: SortOptions
|
|
12
|
+
page?: string
|
|
13
|
+
countryCode: string
|
|
14
|
+
collections?: HttpTypes.StoreCollection[]
|
|
15
|
+
collection?: string | string[]
|
|
16
|
+
category?: string | string[]
|
|
17
|
+
gender?: string | string[]
|
|
18
|
+
type?: string | string[]
|
|
19
|
+
material?: string | string[]
|
|
20
|
+
color?: string | string[]
|
|
21
|
+
minPrice?: string
|
|
22
|
+
maxPrice?: string
|
|
23
|
+
genderOptions?: { value: string; label: string }[]
|
|
24
|
+
typeOptions?: { value: string; label: string }[]
|
|
25
|
+
materialOptions?: { value: string; label: string }[]
|
|
26
|
+
colorOptions?: { value: string; label: string }[]
|
|
27
|
+
categoryOptions?: { value: string; label: string }[]
|
|
28
|
+
q?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function StorePage({
|
|
32
|
+
sortBy,
|
|
33
|
+
page,
|
|
34
|
+
countryCode,
|
|
35
|
+
collections,
|
|
36
|
+
collection,
|
|
37
|
+
category,
|
|
38
|
+
gender,
|
|
39
|
+
type,
|
|
40
|
+
material,
|
|
41
|
+
color,
|
|
42
|
+
minPrice,
|
|
43
|
+
maxPrice,
|
|
44
|
+
genderOptions,
|
|
45
|
+
typeOptions,
|
|
46
|
+
materialOptions,
|
|
47
|
+
colorOptions,
|
|
48
|
+
categoryOptions,
|
|
49
|
+
q,
|
|
50
|
+
}: StorePageProps) {
|
|
51
|
+
const pageNumber = page ? parseInt(page, 10) : 1
|
|
52
|
+
const sort = sortBy || "created_at_desc"
|
|
53
|
+
|
|
54
|
+
const filterProps = {
|
|
55
|
+
sortBy: sort,
|
|
56
|
+
collections,
|
|
57
|
+
collectionValue: collection,
|
|
58
|
+
categoryValue: category,
|
|
59
|
+
categoryOptions,
|
|
60
|
+
genderValue: gender,
|
|
61
|
+
typeValue: type,
|
|
62
|
+
materialValue: material,
|
|
63
|
+
colorValue: color,
|
|
64
|
+
minPrice,
|
|
65
|
+
maxPrice,
|
|
66
|
+
genderOptions,
|
|
67
|
+
typeOptions,
|
|
68
|
+
materialOptions,
|
|
69
|
+
colorOptions,
|
|
70
|
+
q,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div className="bg-page-bg min-h-screen">
|
|
75
|
+
{/* Collection banner — Impulse style */}
|
|
76
|
+
<div className="bg-surface-alt border-b border-[var(--color-header-border)]">
|
|
77
|
+
<div
|
|
78
|
+
className="mx-auto px-4 sm:px-6 py-10 md:py-14 text-center"
|
|
79
|
+
style={{ maxWidth: "var(--container-max)" }}
|
|
80
|
+
>
|
|
81
|
+
<nav className="text-xs uppercase tracking-[var(--letter-spacing-nav)] text-[var(--color-text-muted)] mb-4">
|
|
82
|
+
<LocalizedClientLink href="/" className="hover:text-heading">
|
|
83
|
+
Home
|
|
84
|
+
</LocalizedClientLink>
|
|
85
|
+
<span className="mx-2">/</span>
|
|
86
|
+
<span className="text-heading">Shop</span>
|
|
87
|
+
</nav>
|
|
88
|
+
<h1 className="font-display text-3xl md:text-4xl lg:text-5xl text-heading">
|
|
89
|
+
{q ? `Results for “${q}”` : "All products"}
|
|
90
|
+
</h1>
|
|
91
|
+
<p className="mt-3 text-sm text-[var(--color-text-muted)] max-w-lg mx-auto">
|
|
92
|
+
Browse our full collection. Filter by category, price, and more.
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div
|
|
98
|
+
className="mx-auto px-4 sm:px-6 py-8 lg:py-12"
|
|
99
|
+
style={{ maxWidth: "var(--container-max)" }}
|
|
100
|
+
>
|
|
101
|
+
<div className="flex flex-col lg:flex-row gap-8 lg:gap-12">
|
|
102
|
+
<aside className="hidden lg:block w-64 flex-shrink-0 lg:sticky lg:top-28 lg:self-start lg:max-h-[calc(100vh-8rem)] lg:overflow-y-auto">
|
|
103
|
+
<h2 className="text-xs font-semibold uppercase tracking-[var(--letter-spacing-nav)] text-heading mb-6 pb-2 border-b border-[var(--color-header-border)]">
|
|
104
|
+
Filter
|
|
105
|
+
</h2>
|
|
106
|
+
<RefinementList {...filterProps} />
|
|
107
|
+
</aside>
|
|
108
|
+
|
|
109
|
+
<div className="flex-1 min-w-0">
|
|
110
|
+
<div className="flex items-center justify-between gap-4 mb-6 lg:hidden">
|
|
111
|
+
<MobileFilters {...filterProps} />
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{q && (
|
|
115
|
+
<div className="mb-6 flex items-center justify-between gap-4 p-4 bg-surface-muted border border-[var(--color-header-border)]">
|
|
116
|
+
<span className="text-sm text-[var(--color-text-muted)]">
|
|
117
|
+
Showing results for <strong className="text-heading">"{q}"</strong>
|
|
118
|
+
</span>
|
|
119
|
+
<LocalizedClientLink
|
|
120
|
+
href="/store"
|
|
121
|
+
className="text-xs uppercase tracking-[var(--letter-spacing-nav)] font-semibold text-heading hover:underline"
|
|
122
|
+
>
|
|
123
|
+
Clear
|
|
124
|
+
</LocalizedClientLink>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
<Suspense fallback={<SkeletonProductGrid />}>
|
|
129
|
+
<PaginatedProducts
|
|
130
|
+
sortBy={sort}
|
|
131
|
+
page={pageNumber}
|
|
132
|
+
countryCode={countryCode}
|
|
133
|
+
collectionId={
|
|
134
|
+
Array.isArray(collection) ? collection.join(",") : collection
|
|
135
|
+
}
|
|
136
|
+
categoryId={
|
|
137
|
+
Array.isArray(category) ? category.join(",") : category
|
|
138
|
+
}
|
|
139
|
+
gender={Array.isArray(gender) ? gender.join(",") : gender}
|
|
140
|
+
type={Array.isArray(type) ? type.join(",") : type}
|
|
141
|
+
material={Array.isArray(material) ? material.join(",") : material}
|
|
142
|
+
color={Array.isArray(color) ? color.join(",") : color}
|
|
143
|
+
min_price={minPrice}
|
|
144
|
+
max_price={maxPrice}
|
|
145
|
+
q={q}
|
|
146
|
+
filterProps={filterProps}
|
|
147
|
+
/>
|
|
148
|
+
</Suspense>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Use theme.css CSS variables as the source of truth.
|
|
3
|
+
* Kept for tooling that reads JS token files during migration.
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
colors: {
|
|
7
|
+
pageBg: "var(--color-page-bg)",
|
|
8
|
+
brand: {
|
|
9
|
+
accent: "var(--color-brand-accent)",
|
|
10
|
+
accentHover: "var(--color-brand-accent-hover)",
|
|
11
|
+
accentLight: "var(--color-brand-accent-light)",
|
|
12
|
+
pink: "var(--color-brand-pink)",
|
|
13
|
+
primary: "var(--color-brand-primary)",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Tailwind classes mapped to CSS variables in theme.css.
|
|
3
|
+
* Do not hardcode hex values here — edit theme.css instead.
|
|
4
|
+
*/
|
|
5
|
+
export const colorClasses = {
|
|
6
|
+
pageBg: "bg-page-bg",
|
|
7
|
+
surface: "bg-surface",
|
|
8
|
+
surfaceMuted: "bg-surface-muted",
|
|
9
|
+
brandAccent: "bg-brand-accent text-inverse",
|
|
10
|
+
brandAccentText: "text-brand-accent",
|
|
11
|
+
brandAccentBorder: "border-brand-accent",
|
|
12
|
+
brandAccentMuted: "bg-brand-accent-muted",
|
|
13
|
+
heading: "text-heading",
|
|
14
|
+
headingSub: "text-heading-sub",
|
|
15
|
+
body: "text-body",
|
|
16
|
+
muted: "text-muted",
|
|
17
|
+
inverse: "text-inverse",
|
|
18
|
+
footerBg: "bg-brand-footer",
|
|
19
|
+
promoGradient: "bg-promo-gradient",
|
|
20
|
+
cartBorder: "bg-cart-border",
|
|
21
|
+
} as const
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Tailwind font classes mapped to CSS variables in theme.css.
|
|
3
|
+
* Do not hardcode font families here — edit theme.css instead.
|
|
4
|
+
*/
|
|
5
|
+
export const fontClasses = {
|
|
6
|
+
sans: "font-sans",
|
|
7
|
+
heading: "font-heading",
|
|
8
|
+
display: "font-display",
|
|
9
|
+
quote: "font-quote",
|
|
10
|
+
mono: "font-mono",
|
|
11
|
+
body: "font-sans leading-body tracking-body",
|
|
12
|
+
pageTitle: "font-heading font-bold leading-heading tracking-heading",
|
|
13
|
+
} as const
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Impulse theme — Shopify Impulse preset design tokens.
|
|
3
|
+
* Reference: https://themes.shopify.com/themes/impulse/presets/impulse
|
|
4
|
+
*/
|
|
5
|
+
@import url("https://fonts.googleapis.com/css2?family=Karla:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap");
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* Typography — Karla body, Playfair display (Impulse editorial feel) */
|
|
9
|
+
--font-sans: "Karla", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
10
|
+
sans-serif;
|
|
11
|
+
--font-heading: "Karla", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
12
|
+
sans-serif;
|
|
13
|
+
--font-display: "Playfair Display", Georgia, "Times New Roman", serif;
|
|
14
|
+
--font-quote: "Playfair Display", Georgia, serif;
|
|
15
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
16
|
+
|
|
17
|
+
--font-weight-normal: 400;
|
|
18
|
+
--font-weight-medium: 500;
|
|
19
|
+
--font-weight-semibold: 600;
|
|
20
|
+
--font-weight-bold: 700;
|
|
21
|
+
--font-weight-extrabold: 700;
|
|
22
|
+
|
|
23
|
+
--line-height-body: 1.65;
|
|
24
|
+
--line-height-heading: 1.2;
|
|
25
|
+
--letter-spacing-heading: 0.02em;
|
|
26
|
+
--letter-spacing-body: 0;
|
|
27
|
+
--letter-spacing-nav: 0.14em;
|
|
28
|
+
|
|
29
|
+
/* Surfaces */
|
|
30
|
+
--color-page-bg: #ffffff;
|
|
31
|
+
--color-surface: #ffffff;
|
|
32
|
+
--color-surface-muted: #f6f6f6;
|
|
33
|
+
--color-surface-alt: #efefef;
|
|
34
|
+
--color-hero-fallback: #e5e5e5;
|
|
35
|
+
--color-footer-bg: #1a1a1a;
|
|
36
|
+
--color-header-bg: #ffffff;
|
|
37
|
+
--color-header-border: #e5e5e5;
|
|
38
|
+
|
|
39
|
+
/* Brand — Impulse preset: monochrome + sale red */
|
|
40
|
+
--color-brand-accent: #1a1a1a;
|
|
41
|
+
--color-brand-accent-hover: #404040;
|
|
42
|
+
--color-brand-accent-light: #525252;
|
|
43
|
+
--color-brand-accent-border: #d4d4d4;
|
|
44
|
+
--color-brand-accent-muted: #f5f5f5;
|
|
45
|
+
--color-brand-accent-rgb: 26, 26, 26;
|
|
46
|
+
|
|
47
|
+
--color-brand-pink: #c1272d;
|
|
48
|
+
--color-brand-primary: #1a1a1a;
|
|
49
|
+
--color-brand-sale: #c1272d;
|
|
50
|
+
--color-brand-success: #2d6a4f;
|
|
51
|
+
|
|
52
|
+
/* Text */
|
|
53
|
+
--color-text-heading: #1a1a1a;
|
|
54
|
+
--color-text-subheading: #333333;
|
|
55
|
+
--color-text-body: #4d4d4d;
|
|
56
|
+
--color-text-muted: #737373;
|
|
57
|
+
--color-text-inverse: #ffffff;
|
|
58
|
+
|
|
59
|
+
/* Navigation & panels */
|
|
60
|
+
--color-nav-active: var(--color-brand-accent);
|
|
61
|
+
--color-nav-active-bg: var(--color-brand-accent-muted);
|
|
62
|
+
--color-cart-panel-bg: var(--color-page-bg);
|
|
63
|
+
--color-border: var(--color-header-border);
|
|
64
|
+
|
|
65
|
+
/* Layout */
|
|
66
|
+
--header-height: 4rem;
|
|
67
|
+
--container-max: 1440px;
|
|
68
|
+
|
|
69
|
+
/* Effects */
|
|
70
|
+
--shadow-brand: 0 16px 40px rgba(0, 0, 0, 0.1);
|
|
71
|
+
--shadow-brand-sm: 0 4px 12px rgba(0, 0, 0, 0.06);
|
|
72
|
+
--shadow-card: 0 1px 4px rgba(0, 0, 0, 0.04);
|
|
73
|
+
--gradient-promo: linear-gradient(
|
|
74
|
+
135deg,
|
|
75
|
+
var(--color-brand-accent),
|
|
76
|
+
var(--color-brand-accent-light)
|
|
77
|
+
);
|
|
78
|
+
--gradient-cart-border: linear-gradient(
|
|
79
|
+
to bottom right,
|
|
80
|
+
var(--color-brand-accent),
|
|
81
|
+
var(--color-brand-accent-light)
|
|
82
|
+
);
|
|
83
|
+
--gradient-hero: linear-gradient(
|
|
84
|
+
to top,
|
|
85
|
+
rgba(0, 0, 0, 0.5) 0%,
|
|
86
|
+
rgba(0, 0, 0, 0.12) 50%,
|
|
87
|
+
rgba(0, 0, 0, 0) 100%
|
|
88
|
+
);
|
|
89
|
+
}
|