@arroyavecommerce/theme-kinetic 0.2.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.
Files changed (99) hide show
  1. package/docs/design/README.md +22 -0
  2. package/docs/design/kinetic-street-concept.html +558 -0
  3. package/package.json +45 -0
  4. package/src/blocks/_shared.tsx +174 -0
  5. package/src/blocks/carrusel.tsx +109 -0
  6. package/src/blocks/category-tiles.test.tsx +59 -0
  7. package/src/blocks/category-tiles.tsx +70 -0
  8. package/src/blocks/editorial-banner.test.tsx +96 -0
  9. package/src/blocks/editorial-banner.tsx +205 -0
  10. package/src/blocks/faq.test.tsx +68 -0
  11. package/src/blocks/faq.tsx +122 -0
  12. package/src/blocks/featured-collection.test.tsx +80 -0
  13. package/src/blocks/featured-collection.tsx +97 -0
  14. package/src/blocks/features.test.tsx +71 -0
  15. package/src/blocks/features.tsx +159 -0
  16. package/src/blocks/hero.test.tsx +75 -0
  17. package/src/blocks/hero.tsx +290 -0
  18. package/src/blocks/index.tsx +35 -0
  19. package/src/blocks/lookbook.test.tsx +80 -0
  20. package/src/blocks/lookbook.tsx +117 -0
  21. package/src/blocks/marquee.test.tsx +54 -0
  22. package/src/blocks/marquee.tsx +114 -0
  23. package/src/blocks/newsletter.test.tsx +60 -0
  24. package/src/blocks/newsletter.tsx +105 -0
  25. package/src/blocks/product-grid.test.tsx +160 -0
  26. package/src/blocks/product-grid.tsx +76 -0
  27. package/src/blocks/promo-countdown.tsx +75 -0
  28. package/src/blocks/promo.test.tsx +71 -0
  29. package/src/blocks/promo.tsx +120 -0
  30. package/src/blocks/rich-text.test.tsx +67 -0
  31. package/src/blocks/rich-text.tsx +75 -0
  32. package/src/blocks/spacer.test.tsx +54 -0
  33. package/src/blocks/spacer.tsx +56 -0
  34. package/src/blocks/testimonials.test.tsx +74 -0
  35. package/src/blocks/testimonials.tsx +104 -0
  36. package/src/blocks/video.test.tsx +159 -0
  37. package/src/blocks/video.tsx +226 -0
  38. package/src/catalog/buy-box.test.tsx +225 -0
  39. package/src/catalog/buy-box.tsx +353 -0
  40. package/src/catalog/config.test.ts +115 -0
  41. package/src/catalog/config.ts +167 -0
  42. package/src/catalog/filter-controls.test.tsx +149 -0
  43. package/src/catalog/filter-controls.tsx +187 -0
  44. package/src/catalog/gallery.test.tsx +201 -0
  45. package/src/catalog/gallery.tsx +212 -0
  46. package/src/catalog/lightbox.test.tsx +95 -0
  47. package/src/catalog/lightbox.tsx +184 -0
  48. package/src/catalog/pdp.test.tsx +309 -0
  49. package/src/catalog/pdp.tsx +354 -0
  50. package/src/catalog/plp-arreglos.test.tsx +235 -0
  51. package/src/catalog/plp-inline.tsx +54 -0
  52. package/src/catalog/plp-overlay.tsx +129 -0
  53. package/src/catalog/plp-parts.test.tsx +146 -0
  54. package/src/catalog/plp-parts.tsx +292 -0
  55. package/src/catalog/plp-pills.tsx +44 -0
  56. package/src/catalog/plp-sidebar.tsx +47 -0
  57. package/src/catalog/plp.test.tsx +319 -0
  58. package/src/catalog/plp.tsx +259 -0
  59. package/src/catalog/product-card.test.tsx +221 -0
  60. package/src/catalog/product-card.tsx +411 -0
  61. package/src/catalog/registry.tsx +16 -0
  62. package/src/catalog/related.test.tsx +52 -0
  63. package/src/catalog/related.tsx +40 -0
  64. package/src/catalog/reviews.test.tsx +56 -0
  65. package/src/catalog/reviews.tsx +116 -0
  66. package/src/catalog/skeletons.test.tsx +61 -0
  67. package/src/catalog/skeletons.tsx +103 -0
  68. package/src/chrome/announcement-bar.test.tsx +86 -0
  69. package/src/chrome/announcement-bar.tsx +173 -0
  70. package/src/chrome/drawer-movil.test.tsx +113 -0
  71. package/src/chrome/drawer-movil.tsx +342 -0
  72. package/src/chrome/footer.test.tsx +133 -0
  73. package/src/chrome/footer.tsx +603 -0
  74. package/src/chrome/header.test.tsx +165 -0
  75. package/src/chrome/header.tsx +592 -0
  76. package/src/chrome/index.ts +26 -0
  77. package/src/chrome/mega-menu.test.tsx +161 -0
  78. package/src/chrome/mega-menu.tsx +317 -0
  79. package/src/chrome/mini-cart.test.tsx +111 -0
  80. package/src/chrome/mini-cart.tsx +320 -0
  81. package/src/content/home.ts +19 -0
  82. package/src/content/seed.test.ts +33 -0
  83. package/src/content/seed.ts +28 -0
  84. package/src/context.ts +104 -0
  85. package/src/icons/icons.test.tsx +118 -0
  86. package/src/icons/index.ts +136 -0
  87. package/src/icons/payment-icons.tsx +241 -0
  88. package/src/icons/social-icons.tsx +108 -0
  89. package/src/icons/svg-base.tsx +68 -0
  90. package/src/index.ts +49 -0
  91. package/src/puck-config.tsx +404 -0
  92. package/src/smoke.test.ts +8 -0
  93. package/src/template.test.tsx +88 -0
  94. package/src/test/mock-ctx.test.tsx +77 -0
  95. package/src/test/mock-ctx.tsx +236 -0
  96. package/src/theme-defaults.test.ts +9 -0
  97. package/src/theme.test.ts +82 -0
  98. package/src/theme.ts +195 -0
  99. package/tsconfig.json +4 -0
@@ -0,0 +1,136 @@
1
+ import type { FC } from "react"
2
+ import type { IconProps } from "./svg-base"
3
+ import {
4
+ AddiIcon,
5
+ AmexIcon,
6
+ ApplepayIcon,
7
+ BancolombiaIcon,
8
+ BitcoinIcon,
9
+ DaviplataIcon,
10
+ DinersIcon,
11
+ DiscoverIcon,
12
+ EfectivoIcon,
13
+ EfectyIcon,
14
+ GooglepayIcon,
15
+ MaestroIcon,
16
+ MastercardIcon,
17
+ MercadopagoIcon,
18
+ NequiIcon,
19
+ PaypalIcon,
20
+ PseIcon,
21
+ VisaIcon,
22
+ } from "./payment-icons"
23
+ import {
24
+ FacebookIcon,
25
+ InstagramIcon,
26
+ LinkedinIcon,
27
+ PinterestIcon,
28
+ ThreadsIcon,
29
+ TiktokIcon,
30
+ WhatsappIcon,
31
+ XIcon,
32
+ YoutubeIcon,
33
+ } from "./social-icons"
34
+
35
+ export type { IconProps }
36
+
37
+ export type PaymentKey =
38
+ | "visa"
39
+ | "mastercard"
40
+ | "amex"
41
+ | "diners"
42
+ | "pse"
43
+ | "nequi"
44
+ | "daviplata"
45
+ | "bancolombia"
46
+ | "efecty"
47
+ | "addi"
48
+ | "mercadopago"
49
+ | "paypal"
50
+ | "efectivo"
51
+ | "applepay"
52
+ | "googlepay"
53
+ | "maestro"
54
+ | "discover"
55
+ | "bitcoin"
56
+
57
+ export type SocialKey =
58
+ | "instagram"
59
+ | "tiktok"
60
+ | "whatsapp"
61
+ | "x"
62
+ | "facebook"
63
+ | "youtube"
64
+ | "threads"
65
+ | "pinterest"
66
+ | "linkedin"
67
+
68
+ export type IconEntry = { label: string; Icon: FC<IconProps> }
69
+
70
+ /** Registro tipado de iconos de medios de pago. */
71
+ export const PAYMENT_ICONS: Record<PaymentKey, IconEntry> = {
72
+ visa: { label: "Visa", Icon: VisaIcon },
73
+ mastercard: { label: "Mastercard", Icon: MastercardIcon },
74
+ amex: { label: "American Express", Icon: AmexIcon },
75
+ diners: { label: "Diners Club", Icon: DinersIcon },
76
+ pse: { label: "PSE", Icon: PseIcon },
77
+ nequi: { label: "Nequi", Icon: NequiIcon },
78
+ daviplata: { label: "DaviPlata", Icon: DaviplataIcon },
79
+ bancolombia: { label: "Bancolombia", Icon: BancolombiaIcon },
80
+ efecty: { label: "Efecty", Icon: EfectyIcon },
81
+ addi: { label: "Addi", Icon: AddiIcon },
82
+ mercadopago: { label: "Mercado Pago", Icon: MercadopagoIcon },
83
+ paypal: { label: "PayPal", Icon: PaypalIcon },
84
+ efectivo: { label: "Efectivo", Icon: EfectivoIcon },
85
+ applepay: { label: "Apple Pay", Icon: ApplepayIcon },
86
+ googlepay: { label: "Google Pay", Icon: GooglepayIcon },
87
+ maestro: { label: "Maestro", Icon: MaestroIcon },
88
+ discover: { label: "Discover", Icon: DiscoverIcon },
89
+ bitcoin: { label: "Bitcoin", Icon: BitcoinIcon },
90
+ }
91
+
92
+ /** Registro tipado de iconos de redes sociales. */
93
+ export const SOCIAL_ICONS: Record<SocialKey, IconEntry> = {
94
+ instagram: { label: "Instagram", Icon: InstagramIcon },
95
+ tiktok: { label: "TikTok", Icon: TiktokIcon },
96
+ whatsapp: { label: "WhatsApp", Icon: WhatsappIcon },
97
+ x: { label: "X", Icon: XIcon },
98
+ facebook: { label: "Facebook", Icon: FacebookIcon },
99
+ youtube: { label: "YouTube", Icon: YoutubeIcon },
100
+ threads: { label: "Threads", Icon: ThreadsIcon },
101
+ pinterest: { label: "Pinterest", Icon: PinterestIcon },
102
+ linkedin: { label: "LinkedIn", Icon: LinkedinIcon },
103
+ }
104
+
105
+ export {
106
+ AddiIcon,
107
+ AmexIcon,
108
+ ApplepayIcon,
109
+ BancolombiaIcon,
110
+ BitcoinIcon,
111
+ DaviplataIcon,
112
+ DinersIcon,
113
+ DiscoverIcon,
114
+ EfectivoIcon,
115
+ EfectyIcon,
116
+ GooglepayIcon,
117
+ MaestroIcon,
118
+ MastercardIcon,
119
+ MercadopagoIcon,
120
+ NequiIcon,
121
+ PaypalIcon,
122
+ PseIcon,
123
+ VisaIcon,
124
+ } from "./payment-icons"
125
+
126
+ export {
127
+ FacebookIcon,
128
+ InstagramIcon,
129
+ LinkedinIcon,
130
+ PinterestIcon,
131
+ ThreadsIcon,
132
+ TiktokIcon,
133
+ WhatsappIcon,
134
+ XIcon,
135
+ YoutubeIcon,
136
+ } from "./social-icons"
@@ -0,0 +1,241 @@
1
+ import { Svg, type IconProps } from "./svg-base"
2
+
3
+ /**
4
+ * Iconos de medios de pago — marcas reconocibles y simplificadas, legibles a
5
+ * ~20–30px de alto. Los colores de marca viven en elementos con `data-brand`
6
+ * para permitir el tintado monocromo por CSS (ver `svg-base.tsx`). Los huecos
7
+ * blancos (negativo del logo) NO llevan `data-brand`.
8
+ *
9
+ * No son logos pixel-perfect: priorizan legibilidad y reconocimiento.
10
+ */
11
+
12
+ /** Pila de fuentes sans para wordmarks; bold para máxima legibilidad en chico. */
13
+ const SANS = "'Helvetica Neue', Arial, sans-serif"
14
+
15
+ /** Wordmark reutilizable centrado en un badge rectangular. */
16
+ function Wordmark({
17
+ texto,
18
+ color,
19
+ props,
20
+ italic,
21
+ weight = 800,
22
+ letterSpacing = 0,
23
+ fontSize = 15,
24
+ }: {
25
+ texto: string
26
+ color: string
27
+ props: IconProps
28
+ italic?: boolean
29
+ weight?: number
30
+ letterSpacing?: number
31
+ fontSize?: number
32
+ }) {
33
+ return (
34
+ <Svg viewBox="0 0 64 24" {...props}>
35
+ <text
36
+ data-brand
37
+ x="32"
38
+ y="17"
39
+ textAnchor="middle"
40
+ fontFamily={SANS}
41
+ fontSize={fontSize}
42
+ fontWeight={weight}
43
+ fontStyle={italic ? "italic" : undefined}
44
+ letterSpacing={letterSpacing}
45
+ fill={color}
46
+ >
47
+ {texto}
48
+ </text>
49
+ </Svg>
50
+ )
51
+ }
52
+
53
+ export const VisaIcon = (props: IconProps) => (
54
+ <Wordmark texto="VISA" color="#1A1F71" italic letterSpacing={1} props={props} />
55
+ )
56
+
57
+ export const MastercardIcon = (props: IconProps) => (
58
+ <Svg viewBox="0 0 40 24" {...props}>
59
+ <circle data-brand cx="16" cy="12" r="9" fill="#EB001B" />
60
+ <circle data-brand cx="24" cy="12" r="9" fill="#F79E1B" fillOpacity="0.9" />
61
+ {/* Solape central: naranja translúcido sobre rojo insinúa el ámbar Mastercard */}
62
+ <path data-brand d="M20 5.2a9 9 0 0 0 0 13.6 9 9 0 0 0 0-13.6z" fill="#FF5F00" />
63
+ </Svg>
64
+ )
65
+
66
+ export const AmexIcon = (props: IconProps) => (
67
+ <Svg viewBox="0 0 40 24" {...props}>
68
+ <rect data-brand x="1" y="1" width="38" height="22" rx="3" fill="#2E77BB" />
69
+ <text
70
+ x="20"
71
+ y="16"
72
+ textAnchor="middle"
73
+ fontFamily={SANS}
74
+ fontSize="10"
75
+ fontWeight="800"
76
+ letterSpacing="0.5"
77
+ fill="#FFFFFF"
78
+ >
79
+ AMEX
80
+ </text>
81
+ </Svg>
82
+ )
83
+
84
+ export const DinersIcon = (props: IconProps) => (
85
+ <Svg viewBox="0 0 40 24" {...props}>
86
+ <circle data-brand cx="20" cy="12" r="10" fill="#0079BE" />
87
+ <circle cx="20" cy="12" r="6.5" fill="#FFFFFF" />
88
+ <circle data-brand cx="20" cy="12" r="4" fill="#0079BE" />
89
+ </Svg>
90
+ )
91
+
92
+ export const PseIcon = (props: IconProps) => (
93
+ <Svg viewBox="0 0 40 24" {...props}>
94
+ <rect data-brand x="1" y="1" width="38" height="22" rx="3" fill="#00579F" />
95
+ <text
96
+ x="20"
97
+ y="16"
98
+ textAnchor="middle"
99
+ fontFamily={SANS}
100
+ fontSize="11"
101
+ fontWeight="800"
102
+ letterSpacing="1"
103
+ fill="#FFFFFF"
104
+ >
105
+ PSE
106
+ </text>
107
+ </Svg>
108
+ )
109
+
110
+ export const NequiIcon = (props: IconProps) => (
111
+ <Wordmark texto="Nequi" color="#DA0081" weight={800} fontSize={14} props={props} />
112
+ )
113
+
114
+ export const DaviplataIcon = (props: IconProps) => (
115
+ <Wordmark texto="DaviPlata" color="#ED1C27" weight={800} fontSize={11} props={props} />
116
+ )
117
+
118
+ export const BancolombiaIcon = (props: IconProps) => (
119
+ <Svg viewBox="0 0 64 24" {...props}>
120
+ <rect data-brand x="4" y="7" width="10" height="10" rx="1.5" fill="#FDDA24" />
121
+ <path data-brand d="M6.5 12h5M9 9.5v5" stroke="#2C2A29" strokeWidth="1.6" />
122
+ <text
123
+ data-brand
124
+ x="18"
125
+ y="16"
126
+ fontFamily={SANS}
127
+ fontSize="9"
128
+ fontWeight="800"
129
+ fill="#2C2A29"
130
+ >
131
+ Bancolombia
132
+ </text>
133
+ </Svg>
134
+ )
135
+
136
+ export const EfectyIcon = (props: IconProps) => (
137
+ <Wordmark texto="Efecty" color="#8DC63F" weight={800} fontSize={13} props={props} />
138
+ )
139
+
140
+ export const AddiIcon = (props: IconProps) => (
141
+ <Svg viewBox="0 0 40 24" {...props}>
142
+ <rect data-brand x="1" y="1" width="38" height="22" rx="6" fill="#161615" />
143
+ <text
144
+ x="20"
145
+ y="16"
146
+ textAnchor="middle"
147
+ fontFamily={SANS}
148
+ fontSize="11"
149
+ fontWeight="800"
150
+ letterSpacing="0.5"
151
+ fill="#C6F24E"
152
+ >
153
+ addi
154
+ </text>
155
+ </Svg>
156
+ )
157
+
158
+ export const MercadopagoIcon = (props: IconProps) => (
159
+ <Svg viewBox="0 0 40 24" {...props}>
160
+ <ellipse data-brand cx="20" cy="12" rx="18" ry="10" fill="#009EE3" />
161
+ {/* Apretón de manos simplificado en blanco (negativo del óvalo celeste) */}
162
+ <path
163
+ d="M9 12.5c2.2-2.2 4-2.4 6-1 1.6-1 3-.9 4.4.2l3.2 2.6c.7.6.2 1.7-.7 1.5l-2-.5 1 1c.7.7-.3 1.8-1.1 1.2l-1-.8.4.7c.5.8-.6 1.6-1.3.9l-1.2-1.1c-1.8 1-3.6.7-5.2-.7L9 15.5a1.8 1.8 0 0 1 0-3z"
164
+ fill="#FFFFFF"
165
+ />
166
+ <circle cx="27" cy="9.5" r="1.6" fill="#FFE600" />
167
+ </Svg>
168
+ )
169
+
170
+ export const PaypalIcon = (props: IconProps) => (
171
+ <Svg viewBox="0 0 64 24" {...props}>
172
+ <text x="6" y="17" fontFamily={SANS} fontSize="15" fontWeight="800" fontStyle="italic">
173
+ <tspan data-brand fill="#003087">Pay</tspan>
174
+ <tspan data-brand fill="#009CDE">Pal</tspan>
175
+ </text>
176
+ </Svg>
177
+ )
178
+
179
+ export const EfectivoIcon = (props: IconProps) => (
180
+ <Svg viewBox="0 0 40 24" {...props}>
181
+ <rect data-brand x="2" y="5" width="36" height="14" rx="2" fill="#2E7D32" />
182
+ <rect x="5" y="8" width="30" height="8" rx="1" fill="none" stroke="#FFFFFF" strokeWidth="1" />
183
+ <circle cx="20" cy="12" r="3.2" fill="none" stroke="#FFFFFF" strokeWidth="1.2" />
184
+ <text x="20" y="14.5" textAnchor="middle" fontFamily={SANS} fontSize="6" fontWeight="800" fill="#FFFFFF">
185
+ $
186
+ </text>
187
+ </Svg>
188
+ )
189
+
190
+ export const ApplepayIcon = (props: IconProps) => (
191
+ <Svg viewBox="0 0 48 24" {...props}>
192
+ {/* Manzana simplificada */}
193
+ <path
194
+ data-brand
195
+ d="M12.6 8.2c-.5.6-1.3 1-2 .95-.1-.8.28-1.65.73-2.18.5-.6 1.35-1 2.05-1.03.08.83-.25 1.64-.78 2.26zm.77.98c-1.12-.07-2.07.63-2.6.63-.55 0-1.36-.6-2.24-.58-1.15.02-2.22.67-2.8 1.7-1.2 2.08-.31 5.15.85 6.84.57.83 1.24 1.76 2.13 1.73.85-.03 1.18-.55 2.2-.55 1.03 0 1.32.55 2.22.53.92-.02 1.5-.84 2.06-1.68.65-.96.92-1.9.93-1.94-.02-.01-1.78-.69-1.8-2.72-.02-1.7 1.39-2.51 1.45-2.55-.79-1.17-2.02-1.3-2.45-1.33z"
196
+ fill="#000000"
197
+ />
198
+ <text data-brand x="20" y="16" fontFamily={SANS} fontSize="11" fontWeight="600" fill="#000000">
199
+ Pay
200
+ </text>
201
+ </Svg>
202
+ )
203
+
204
+ export const GooglepayIcon = (props: IconProps) => (
205
+ <Svg viewBox="0 0 48 24" {...props}>
206
+ {/* G multicolor de Google (4 arcos) sobre barra central */}
207
+ <path data-brand d="M11 12a5 5 0 0 1 5-5c1.3 0 2.4.45 3.3 1.2l-1.4 1.4A2.9 2.9 0 0 0 16 9.1 2.9 2.9 0 0 0 13.1 12" fill="#EA4335" />
208
+ <path data-brand d="M13.1 12A2.9 2.9 0 0 0 16 14.9c.9 0 1.6-.3 2.1-.8.4-.4.65-.95.75-1.6H16v-1.9h4.8c.06.34.1.68.1 1.05 0 1.5-.54 2.77-1.48 3.63A4.9 4.9 0 0 1 16 17 5 5 0 0 1 11 12" fill="#4285F4" />
209
+ <path d="M11 12a5 5 0 0 1 .27-1.63l1.9.66A2.9 2.9 0 0 0 13.1 12z" fill="#FBBC04" />
210
+ <path d="M13.1 12a2.9 2.9 0 0 0 .07.63l-1.9.66A5 5 0 0 1 11 12z" fill="#34A853" />
211
+ <text data-brand x="24" y="16" fontFamily={SANS} fontSize="11" fontWeight="600" fill="#5F6368">
212
+ Pay
213
+ </text>
214
+ </Svg>
215
+ )
216
+
217
+ export const MaestroIcon = (props: IconProps) => (
218
+ <Svg viewBox="0 0 40 24" {...props}>
219
+ <circle data-brand cx="16" cy="12" r="9" fill="#0099DF" />
220
+ <circle data-brand cx="24" cy="12" r="9" fill="#ED0006" fillOpacity="0.9" />
221
+ <path data-brand d="M20 5.2a9 9 0 0 0 0 13.6 9 9 0 0 0 0-13.6z" fill="#6C6BBD" />
222
+ </Svg>
223
+ )
224
+
225
+ export const DiscoverIcon = (props: IconProps) => (
226
+ <Svg viewBox="0 0 64 24" {...props}>
227
+ <text data-brand x="4" y="16" fontFamily={SANS} fontSize="10" fontWeight="800" letterSpacing="0.3" fill="#1A1A1A">
228
+ DISCOVER
229
+ </text>
230
+ <circle data-brand cx="58" cy="12.5" r="4.5" fill="#F76E11" />
231
+ </Svg>
232
+ )
233
+
234
+ export const BitcoinIcon = (props: IconProps) => (
235
+ <Svg viewBox="0 0 24 24" {...props}>
236
+ <circle data-brand cx="12" cy="12" r="11" fill="#F7931A" />
237
+ <text x="12" y="17" textAnchor="middle" fontFamily={SANS} fontSize="15" fontWeight="800" fill="#FFFFFF">
238
+
239
+ </text>
240
+ </Svg>
241
+ )
@@ -0,0 +1,108 @@
1
+ import { Svg, type IconProps } from "./svg-base"
2
+
3
+ /**
4
+ * Iconos de redes sociales — glifos limpios de marca, legibles a ~20–30px.
5
+ * Los colores de marca viven en elementos con `data-brand` para el tintado
6
+ * monocromo por CSS (ver `svg-base.tsx`). Los huecos blancos (negativo del
7
+ * símbolo) NO llevan `data-brand`.
8
+ */
9
+
10
+ const SANS = "'Helvetica Neue', Arial, sans-serif"
11
+
12
+ export const InstagramIcon = (props: IconProps) => (
13
+ <Svg viewBox="0 0 24 24" {...props}>
14
+ <defs>
15
+ <linearGradient id="ig-grad" x1="0" y1="24" x2="24" y2="0" gradientUnits="userSpaceOnUse">
16
+ <stop offset="0" stopColor="#FEDA75" />
17
+ <stop offset="0.35" stopColor="#FA7E1E" />
18
+ <stop offset="0.6" stopColor="#D62976" />
19
+ <stop offset="0.8" stopColor="#962FBF" />
20
+ <stop offset="1" stopColor="#4F5BD5" />
21
+ </linearGradient>
22
+ </defs>
23
+ <rect data-brand x="2" y="2" width="20" height="20" rx="6" fill="none" stroke="url(#ig-grad)" strokeWidth="2" />
24
+ <circle data-brand cx="12" cy="12" r="5" fill="none" stroke="url(#ig-grad)" strokeWidth="2" />
25
+ <circle data-brand cx="17.5" cy="6.5" r="1.4" fill="url(#ig-grad)" />
26
+ </Svg>
27
+ )
28
+
29
+ export const TiktokIcon = (props: IconProps) => (
30
+ <Svg viewBox="0 0 24 24" {...props}>
31
+ <path
32
+ data-brand
33
+ d="M15 3c.3 2.2 1.6 3.8 3.9 4v2.6c-1.4.1-2.7-.3-3.9-1.05V15a5.5 5.5 0 1 1-5.5-5.5c.3 0 .6.02.9.07v2.7a2.8 2.8 0 1 0 1.9 2.65V3H15z"
34
+ fill="#111111"
35
+ />
36
+ </Svg>
37
+ )
38
+
39
+ export const WhatsappIcon = (props: IconProps) => (
40
+ <Svg viewBox="0 0 24 24" {...props}>
41
+ <path
42
+ data-brand
43
+ d="M12 2a10 10 0 0 0-8.6 15.05L2 22l5.1-1.34A10 10 0 1 0 12 2z"
44
+ fill="#25D366"
45
+ />
46
+ <path
47
+ d="M8.4 7.1c-.2-.45-.4-.46-.6-.47h-.5c-.18 0-.47.07-.72.34-.25.27-.95.93-.95 2.27s.98 2.63 1.12 2.81c.14.18 1.9 3.04 4.7 4.14 2.33.92 2.8.73 3.3.69.5-.05 1.63-.67 1.86-1.31.23-.64.23-1.19.16-1.31-.07-.11-.25-.18-.53-.32-.28-.14-1.63-.8-1.88-.9-.25-.09-.43-.13-.62.14-.18.27-.71.9-.87 1.08-.16.18-.32.2-.6.07-.28-.14-1.17-.43-2.22-1.37-.82-.73-1.37-1.63-1.53-1.9-.16-.28-.02-.43.12-.56.13-.13.28-.32.42-.48.14-.16.18-.28.28-.46.09-.18.05-.34-.02-.48-.07-.14-.6-1.5-.84-2.05z"
48
+ fill="#FFFFFF"
49
+ />
50
+ </Svg>
51
+ )
52
+
53
+ export const XIcon = (props: IconProps) => (
54
+ <Svg viewBox="0 0 24 24" {...props}>
55
+ <path
56
+ data-brand
57
+ d="M17.5 3h3l-6.55 7.5L21.7 21h-5.9l-4.6-6-5.3 6H3l7-8-6.6-9h6l4.15 5.5L17.5 3zm-1.05 16h1.65L7.6 4.6H5.85L16.45 19z"
58
+ fill="#000000"
59
+ />
60
+ </Svg>
61
+ )
62
+
63
+ export const FacebookIcon = (props: IconProps) => (
64
+ <Svg viewBox="0 0 24 24" {...props}>
65
+ <circle data-brand cx="12" cy="12" r="11" fill="#1877F2" />
66
+ <path
67
+ d="M15.1 12.5l.4-2.6h-2.5V8.2c0-.72.35-1.42 1.48-1.42h1.13V4.56s-1.03-.18-2-.18c-2.05 0-3.38 1.24-3.38 3.48V9.9H8.4v2.6h2.23V19h2.87v-6.5h2.6z"
68
+ fill="#FFFFFF"
69
+ />
70
+ </Svg>
71
+ )
72
+
73
+ export const YoutubeIcon = (props: IconProps) => (
74
+ <Svg viewBox="0 0 24 24" {...props}>
75
+ <rect data-brand x="1" y="5" width="22" height="14" rx="4" fill="#FF0000" />
76
+ <path d="M10 8.5l6 3.5-6 3.5v-7z" fill="#FFFFFF" />
77
+ </Svg>
78
+ )
79
+
80
+ export const ThreadsIcon = (props: IconProps) => (
81
+ <Svg viewBox="0 0 24 24" {...props}>
82
+ <path
83
+ data-brand
84
+ d="M16.5 11.4c-.1-.05-.2-.1-.3-.14-.18-3.2-1.94-5.03-4.9-5.05h-.04c-1.77 0-3.24.75-4.15 2.13l1.63 1.12c.68-1.02 1.74-1.24 2.52-1.24h.03c.97.01 1.7.29 2.17.84.34.4.57.95.68 1.64-.85-.14-1.77-.19-2.75-.13-2.76.16-4.53 1.77-4.41 4 .06 1.14.63 2.11 1.6 2.74.82.53 1.88.79 2.98.73 1.45-.08 2.59-.63 3.38-1.65.6-.77.98-1.77 1.15-3.03.7.42 1.21.98 1.5 1.65.48 1.14.51 3.01-1.01 4.53-1.34 1.33-2.94 1.91-5.36 1.93-2.68-.02-4.71-.88-6.03-2.56C4.66 17.5 4.02 15.45 4 12.86c.02-2.58.66-4.63 1.9-6.09C7.22 5.09 9.25 4.23 11.93 4.2c2.7.02 4.76.88 6.13 2.57.67.83 1.18 1.87 1.51 3.09l1.9-.51c-.4-1.5-1.04-2.8-1.9-3.87C17.83 3.4 15.25 2.28 11.94 2.26h-.01c-3.3.02-5.85 1.14-7.57 3.33C2.83 7.61 2.03 10.1 2 12.85v.02c.03 2.75.83 5.24 2.36 7.06 1.72 2.19 4.27 3.31 7.57 3.33h.01c2.93-.02 5-.79 6.7-2.49 2.23-2.22 2.16-5.01 1.43-6.72-.53-1.23-1.53-2.23-2.98-2.65z"
85
+ fill="#000000"
86
+ />
87
+ </Svg>
88
+ )
89
+
90
+ export const PinterestIcon = (props: IconProps) => (
91
+ <Svg viewBox="0 0 24 24" {...props}>
92
+ <circle data-brand cx="12" cy="12" r="11" fill="#E60023" />
93
+ <path
94
+ d="M12.2 5.5c-3.6 0-5.6 2.35-5.6 4.9 0 1.18.66 2.65 1.72 3.12.16.07.25.04.28-.11.03-.12.17-.7.24-.97.02-.09.01-.16-.06-.25-.4-.47-.71-1.34-.71-2.15 0-2.08 1.6-4.1 4.31-4.1 2.35 0 3.99 1.55 3.99 3.77 0 2.5-1.29 4.24-2.96 4.24-.92 0-1.62-.75-1.4-1.68.26-1.1.78-2.29.78-3.08 0-.71-.39-1.3-1.19-1.3-.94 0-1.7.96-1.7 2.25 0 .82.28 1.37.28 1.37s-.96 3.98-1.13 4.68c-.28 1.18-.04 2.62-.02 2.77.01.09.13.11.18.04.08-.1 1.09-1.34 1.43-2.58.1-.35.55-2.11.55-2.11.27.51 1.07.95 1.91.95 2.52 0 4.23-2.27 4.23-5.31 0-2.3-1.97-4.46-4.87-4.46z"
95
+ fill="#FFFFFF"
96
+ />
97
+ </Svg>
98
+ )
99
+
100
+ export const LinkedinIcon = (props: IconProps) => (
101
+ <Svg viewBox="0 0 24 24" {...props}>
102
+ <rect data-brand x="1" y="1" width="22" height="22" rx="4" fill="#0A66C2" />
103
+ <path
104
+ d="M7.2 9.5H4.8V18h2.4V9.5zM6 5.6a1.4 1.4 0 1 0 0 2.8 1.4 1.4 0 0 0 0-2.8zM18.2 18h2.4v-4.9c0-2.35-1.26-3.44-2.93-3.44-1.35 0-1.95.74-2.29 1.26V9.5h-2.4V18h2.4v-4.5c0-1.02.5-1.6 1.35-1.6.78 0 1.47.55 1.47 1.6V18z"
105
+ fill="#FFFFFF"
106
+ />
107
+ </Svg>
108
+ )
@@ -0,0 +1,68 @@
1
+ import type { ReactNode } from "react"
2
+
3
+ /**
4
+ * API pública de cada icono. Ninguna dimensión es fija: por defecto el icono
5
+ * mide `1em` de alto (hereda del `font-size` del contenedor) y el ancho se
6
+ * calcula solo a partir del `viewBox`. Pasá `size` (número → px, o cualquier
7
+ * unidad CSS) para forzar un alto concreto.
8
+ */
9
+ export type IconProps = {
10
+ className?: string
11
+ /** Texto accesible. Si se omite, el icono se marca `aria-hidden` (decorativo). */
12
+ title?: string
13
+ /** Alto del icono. Número = px; string = unidad CSS. Default `"1em"`. */
14
+ size?: number | string
15
+ }
16
+
17
+ /**
18
+ * Envoltura común de todos los iconos.
19
+ *
20
+ * ── Tintado por CSS ────────────────────────────────────────────────────────
21
+ * Los colores de marca viven en elementos marcados con `data-brand`. El
22
+ * consumidor (footer/chrome) envuelve la fila de iconos en un contenedor con
23
+ * una clase y elige el modo con CSS. Convención:
24
+ *
25
+ * modo "marca" (default): no hace nada, cada icono muestra sus colores.
26
+ * modo "tinta" (monocromo con el color de texto):
27
+ * .icons--tinta svg [data-brand] { fill: currentColor; stroke: currentColor }
28
+ * modo "acento" (monocromo con el acento de la paleta):
29
+ * .icons--acento svg [data-brand] { fill: var(--cms-acento, #3b2bff);
30
+ * stroke: var(--cms-acento, #3b2bff) }
31
+ *
32
+ * Los elementos "hueco" (blancos, que representan el negativo del logo) NO
33
+ * llevan `data-brand`, así en modo monocromo el fondo se recolorea pero el
34
+ * hueco se mantiene y el símbolo sigue legible.
35
+ *
36
+ * El alto se controla con el atributo intrínseco (del `viewBox`) + `style`:
37
+ * `height` fija el alto y `width:auto` conserva la proporción, igual que un
38
+ * `<img>`. Una clase del consumidor puede sobrescribir el alto sin deformar.
39
+ */
40
+ export function Svg({
41
+ viewBox,
42
+ title,
43
+ className,
44
+ size,
45
+ children,
46
+ }: IconProps & { viewBox: string; children: ReactNode }) {
47
+ const parts = viewBox.split(/\s+/)
48
+ const w = parts[2]
49
+ const h = parts[3]
50
+ const height = size ?? "1em"
51
+ return (
52
+ <svg
53
+ xmlns="http://www.w3.org/2000/svg"
54
+ viewBox={viewBox}
55
+ width={w}
56
+ height={h}
57
+ className={className}
58
+ style={{ height, width: "auto" }}
59
+ role="img"
60
+ aria-label={title}
61
+ aria-hidden={title ? undefined : true}
62
+ focusable="false"
63
+ >
64
+ {title ? <title>{title}</title> : null}
65
+ {children}
66
+ </svg>
67
+ )
68
+ }
package/src/index.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { defineTemplate, type Template } from "@arroyavecommerce/cms-storefront"
2
+ import type { RenderContext } from "./context"
3
+ import { homeBlocks } from "./blocks"
4
+ import { puckConfig } from "./puck-config"
5
+ import { DEFAULT_HOME_PAGE } from "./content/home"
6
+ import { FUENTES, THEME_DEFAULTS, THEME_PRESETS, themeTokens } from "./theme"
7
+ import { chromeRegistry } from "./chrome"
8
+ import { catalogo } from "./catalog/registry"
9
+ import { SkeletonPlp, SkeletonPdp } from "./catalog/skeletons"
10
+ import {
11
+ PAYMENT_ICONS,
12
+ SOCIAL_ICONS,
13
+ type PaymentKey,
14
+ type SocialKey,
15
+ type IconEntry,
16
+ type IconProps,
17
+ } from "./icons"
18
+
19
+ export type { RenderContext }
20
+ export type { PaymentKey, SocialKey, IconEntry, IconProps }
21
+ export { themeTokens, FUENTES, THEME_PRESETS, homeBlocks, puckConfig, DEFAULT_HOME_PAGE, chromeRegistry, catalogo }
22
+ export { PAYMENT_ICONS, SOCIAL_ICONS }
23
+ export { SkeletonPlp, SkeletonPdp }
24
+
25
+ export const themeKinetic: Template<RenderContext> = defineTemplate<RenderContext>()({
26
+ meta: { id: "kinetic", nombre: "Kinetic", industria: "fashion" },
27
+ blocks: homeBlocks,
28
+ chrome: chromeRegistry,
29
+ catalogo,
30
+ puckConfig,
31
+ theme: {
32
+ definition: { tokens: themeTokens, fuentes: FUENTES },
33
+ defaults: THEME_DEFAULTS,
34
+ marca: "Kinetic",
35
+ },
36
+ defaultHomePage: DEFAULT_HOME_PAGE,
37
+ reservados: [
38
+ "store",
39
+ "products",
40
+ "cart",
41
+ "checkout",
42
+ "account",
43
+ "categories",
44
+ "collections",
45
+ "studio",
46
+ "api",
47
+ ],
48
+ basePath: "/p",
49
+ })