medusa-storefront-theme-base 1.1.0 → 2.0.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.
- package/package.json +45 -3
- package/src/button-css-vars.ts +52 -0
- package/src/button-theme.ts +74 -0
- package/src/button-themes/minimal.ts +19 -0
- package/src/create-site-theme-classes.ts +189 -0
- package/src/create-site-theme.ts +299 -0
- package/src/define-brand.ts +53 -0
- package/src/form-theme.ts +55 -0
- package/src/index.ts +90 -65
- package/src/page-theme-merge.ts +1 -0
- package/src/presets/index.ts +10 -0
- package/src/presets/retail-warm.ts +28 -0
- package/src/storefront-theme.ts +281 -0
- package/src/theme-class-names.ts +293 -0
- package/src/theme-layout.ts +58 -0
- package/src/theme-resolve.ts +17 -0
- package/src/theme-slot-merge.ts +34 -0
- package/src/types/deep-partial.ts +4 -0
- package/styles/storefront-globals.css +165 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { mergeButtonTheme, pillButtonTheme, type ButtonTheme } from "./button-theme";
|
|
2
|
+
import {
|
|
3
|
+
createSiteAccountTheme,
|
|
4
|
+
createSiteCartTheme,
|
|
5
|
+
createSiteCheckoutUiTheme,
|
|
6
|
+
createSiteContactTheme,
|
|
7
|
+
createSiteHelpTheme,
|
|
8
|
+
createSiteHomeTheme,
|
|
9
|
+
createSiteOrderTheme,
|
|
10
|
+
createSiteProductCardTheme,
|
|
11
|
+
createSiteProductTheme,
|
|
12
|
+
createSiteStoreTheme,
|
|
13
|
+
} from "./create-site-theme-classes";
|
|
14
|
+
export { createSiteHomeTheme } from "./create-site-theme-classes";
|
|
15
|
+
export type { StorefrontThemeClassOverrides } from "./theme-class-names";
|
|
16
|
+
import { mergeThemeSlots } from "./theme-slot-merge";
|
|
17
|
+
import {
|
|
18
|
+
themeClassNames,
|
|
19
|
+
type HomeThemeClassNames,
|
|
20
|
+
type LayoutThemeClassNames,
|
|
21
|
+
type NotFoundThemeClassNames,
|
|
22
|
+
type SitePageThemeClassNames,
|
|
23
|
+
type StorefrontThemeClassOverrides,
|
|
24
|
+
type StorefrontThemeClasses,
|
|
25
|
+
} from "./theme-class-names";
|
|
26
|
+
import {
|
|
27
|
+
defaultFormThemeClassNames,
|
|
28
|
+
mergeFormTheme,
|
|
29
|
+
type FormThemeClassNames,
|
|
30
|
+
type SiteThemeInputTokens,
|
|
31
|
+
} from "./form-theme";
|
|
32
|
+
import {
|
|
33
|
+
resolveInputColors,
|
|
34
|
+
themeCssClasses,
|
|
35
|
+
type StorefrontTheme,
|
|
36
|
+
type StorefrontThemeColors,
|
|
37
|
+
} from "./storefront-theme";
|
|
38
|
+
|
|
39
|
+
/** Minimal palette — edit only these in compositions/theme/tokens.ts */
|
|
40
|
+
export type SiteThemePalette = {
|
|
41
|
+
primary: string;
|
|
42
|
+
primaryHover: string;
|
|
43
|
+
secondary: string;
|
|
44
|
+
secondaryHover?: string;
|
|
45
|
+
background: string;
|
|
46
|
+
backgroundAlt: string;
|
|
47
|
+
surface?: string;
|
|
48
|
+
text: string;
|
|
49
|
+
textMuted: string;
|
|
50
|
+
textOnPrimary?: string;
|
|
51
|
+
footer?: string;
|
|
52
|
+
header?: string;
|
|
53
|
+
/** Header chrome text — defaults to `text` */
|
|
54
|
+
headerText?: string;
|
|
55
|
+
/** Footer body/link text — defaults to `textOnPrimary` */
|
|
56
|
+
footerText?: string;
|
|
57
|
+
/** Footer muted copy — defaults to `secondary` */
|
|
58
|
+
footerTextMuted?: string;
|
|
59
|
+
/** Footer section titles — defaults to `secondary` */
|
|
60
|
+
footerHeading?: string;
|
|
61
|
+
border?: string;
|
|
62
|
+
/** Optional — defaults derived from palette (text, border, primary focus) */
|
|
63
|
+
input?: SiteThemeInputTokens;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type SiteThemeFonts = {
|
|
67
|
+
body: string;
|
|
68
|
+
heading?: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export type DefineSiteThemeInput = {
|
|
72
|
+
palette: SiteThemePalette;
|
|
73
|
+
fonts: SiteThemeFonts;
|
|
74
|
+
/** Per-site form field class overrides */
|
|
75
|
+
form?: Partial<FormThemeClassNames>;
|
|
76
|
+
/** Per-area className slot overrides (account, cart, product, …) */
|
|
77
|
+
classOverrides?: StorefrontThemeClassOverrides;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Shared Tailwind fragments (CSS vars) — used by buttons and pages */
|
|
81
|
+
export const themeUi = {
|
|
82
|
+
btn: {
|
|
83
|
+
primary:
|
|
84
|
+
"bg-[var(--sf-color-primary)] text-[var(--sf-color-text-on-primary)] hover:bg-[var(--sf-color-primary-hover)]",
|
|
85
|
+
secondary:
|
|
86
|
+
"bg-[var(--sf-color-secondary)] text-[var(--sf-color-text)] hover:bg-[var(--sf-color-secondary-hover)]",
|
|
87
|
+
outline:
|
|
88
|
+
"bg-transparent text-[var(--sf-color-text)] border-2 border-[var(--sf-color-text)] hover:bg-[var(--sf-color-text)] hover:text-[var(--sf-color-text-on-primary)]",
|
|
89
|
+
ghost:
|
|
90
|
+
"bg-transparent text-[var(--sf-color-text)] hover:bg-[color-mix(in_srgb,var(--sf-color-text-muted)_8%,transparent)]",
|
|
91
|
+
danger:
|
|
92
|
+
"bg-[var(--sf-color-text)] text-[var(--sf-color-text-on-primary)] hover:bg-black",
|
|
93
|
+
link: "text-[var(--sf-color-link)] hover:text-[var(--sf-color-link-hover)] underline-offset-4 hover:underline h-auto p-0",
|
|
94
|
+
},
|
|
95
|
+
page: themeCssClasses,
|
|
96
|
+
input: themeCssClasses.input,
|
|
97
|
+
layout: themeClassNames.layout,
|
|
98
|
+
} as const;
|
|
99
|
+
|
|
100
|
+
export function paletteToThemeColors(palette: SiteThemePalette): StorefrontThemeColors {
|
|
101
|
+
return {
|
|
102
|
+
primary: palette.primary,
|
|
103
|
+
primaryHover: palette.primaryHover,
|
|
104
|
+
secondary: palette.secondary,
|
|
105
|
+
secondaryHover: palette.secondaryHover ?? palette.secondary,
|
|
106
|
+
background: palette.background,
|
|
107
|
+
backgroundAlt: palette.backgroundAlt,
|
|
108
|
+
surface: palette.surface ?? "#FFFFFF",
|
|
109
|
+
text: palette.text,
|
|
110
|
+
textMuted: palette.textMuted,
|
|
111
|
+
textOnPrimary: palette.textOnPrimary ?? "#FFFFFF",
|
|
112
|
+
link: palette.primary,
|
|
113
|
+
linkHover: palette.secondary,
|
|
114
|
+
footerBg: palette.footer ?? "#04122C",
|
|
115
|
+
headerBg: palette.header ?? "#FFFFFF",
|
|
116
|
+
navText: palette.textMuted,
|
|
117
|
+
navActive: palette.primary,
|
|
118
|
+
headerText: palette.headerText ?? palette.text,
|
|
119
|
+
footerText: palette.footerText ?? palette.textOnPrimary ?? "#FFFFFF",
|
|
120
|
+
footerTextMuted: palette.footerTextMuted ?? palette.textMuted,
|
|
121
|
+
footerHeading:
|
|
122
|
+
palette.footerHeading ?? palette.footerText ?? palette.textOnPrimary ?? "#FFFFFF",
|
|
123
|
+
border: palette.border ?? "#E5E7EB",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function createSiteTheme({
|
|
128
|
+
palette,
|
|
129
|
+
fonts,
|
|
130
|
+
}: DefineSiteThemeInput): StorefrontTheme {
|
|
131
|
+
const colors = paletteToThemeColors(palette);
|
|
132
|
+
return {
|
|
133
|
+
colors,
|
|
134
|
+
fonts: {
|
|
135
|
+
body: fonts.body,
|
|
136
|
+
heading: fonts.heading ?? fonts.body,
|
|
137
|
+
},
|
|
138
|
+
input: palette.input
|
|
139
|
+
? resolveInputColors(colors, palette.input)
|
|
140
|
+
: undefined,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function createSiteFormTheme(
|
|
145
|
+
overrides?: Partial<FormThemeClassNames>,
|
|
146
|
+
): FormThemeClassNames {
|
|
147
|
+
return mergeFormTheme(defaultFormThemeClassNames, overrides);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function createSiteButtonTheme(): ButtonTheme {
|
|
151
|
+
return mergeButtonTheme(pillButtonTheme, {
|
|
152
|
+
variants: {
|
|
153
|
+
primary: `${themeUi.btn.primary} shadow-md hover:opacity-95`,
|
|
154
|
+
secondary: `${themeUi.btn.secondary} shadow-sm border border-[var(--sf-color-secondary-hover)]/40`,
|
|
155
|
+
outline: themeUi.btn.outline,
|
|
156
|
+
ghost: themeUi.btn.ghost,
|
|
157
|
+
danger: `${themeUi.btn.danger} shadow-sm`,
|
|
158
|
+
link: themeUi.btn.link,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function createSiteLayoutTheme(): LayoutThemeClassNames {
|
|
164
|
+
const p = themeUi.page;
|
|
165
|
+
const base = themeUi.layout;
|
|
166
|
+
return {
|
|
167
|
+
...base,
|
|
168
|
+
body: `${base.body} ${p.pageBackground} ${p.bodyFont} ${p.text}`.trim(),
|
|
169
|
+
main: `${base.main} ${p.pageBackground}`.trim(),
|
|
170
|
+
checkoutShell: `${base.checkoutShell} ${p.pageBackground}`.trim(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function createSiteNotFoundTheme(): NotFoundThemeClassNames {
|
|
175
|
+
return {
|
|
176
|
+
...themeClassNames.notFound,
|
|
177
|
+
root: `${themeClassNames.notFound.root} ${themeUi.page.pageBackground}`.trim(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function createSitePageTheme(): SitePageThemeClassNames {
|
|
182
|
+
const p = themeUi.page;
|
|
183
|
+
const pages = themeClassNames.pages;
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
checkout: { ...pages.checkout },
|
|
187
|
+
returnIneligible: { ...pages.returnIneligible },
|
|
188
|
+
pageWrappers: {
|
|
189
|
+
legal: `min-h-screen ${p.pageBackground} pb-16 sm:pb-24`,
|
|
190
|
+
resetPassword: `min-h-screen flex items-center justify-center ${p.pageBackgroundAlt} px-4`,
|
|
191
|
+
returnRequest: `${p.pageBackground} min-h-screen`,
|
|
192
|
+
exchangeRequest: `${p.pageBackground} min-h-screen`,
|
|
193
|
+
},
|
|
194
|
+
legal: {
|
|
195
|
+
pageRoot: `min-h-screen ${p.pageBackground} pb-16 sm:pb-24`,
|
|
196
|
+
header: `bg-[var(--sf-color-footer-bg)] text-white py-16 sm:py-24 mb-12 relative overflow-hidden`,
|
|
197
|
+
headerBlurPrimary:
|
|
198
|
+
"absolute top-0 left-0 w-96 h-96 bg-[var(--sf-color-primary)] opacity-10 rounded-full -ml-48 -mt-48 blur-3xl",
|
|
199
|
+
headerBlurSecondary:
|
|
200
|
+
"absolute bottom-0 right-0 w-64 h-64 bg-blue-500 opacity-10 rounded-full -mr-32 -mb-32 blur-3xl",
|
|
201
|
+
headerTitle: `text-3xl sm:text-5xl font-extrabold mb-4 tracking-tight ${p.heading}`,
|
|
202
|
+
headerSubtitle:
|
|
203
|
+
"text-gray-300 text-base sm:text-xl max-w-2xl mx-auto leading-relaxed font-light",
|
|
204
|
+
contentCard: `${p.surface} rounded-3xl shadow-xl shadow-gray-200/50 border ${p.border} overflow-hidden`,
|
|
205
|
+
contentCardFlat: `${p.surface} rounded-3xl shadow-xl shadow-gray-200/50 border ${p.border} p-5 sm:p-10 md:p-16 space-y-10 sm:space-y-12`,
|
|
206
|
+
sectionNumberPrimary:
|
|
207
|
+
"flex-shrink-0 w-10 h-10 sm:w-14 sm:h-14 bg-blue-50 text-[var(--sf-color-footer-bg)] rounded-2xl flex items-center justify-center font-bold text-xl sm:text-2xl shadow-sm",
|
|
208
|
+
sectionNumberAccent:
|
|
209
|
+
"flex-shrink-0 w-10 h-10 sm:w-14 sm:h-14 bg-red-50 text-[var(--sf-color-primary)] rounded-2xl flex items-center justify-center font-bold text-xl sm:text-2xl shadow-sm",
|
|
210
|
+
sectionNumberPink:
|
|
211
|
+
"flex-shrink-0 w-10 h-10 sm:w-14 sm:h-14 bg-pink-50 text-pink-500 rounded-2xl flex items-center justify-center font-bold text-xl sm:text-2xl shadow-sm",
|
|
212
|
+
sectionNumberYellow:
|
|
213
|
+
"flex-shrink-0 w-10 h-10 sm:w-14 sm:h-14 bg-yellow-50 text-yellow-600 rounded-2xl flex items-center justify-center font-bold text-xl sm:text-2xl shadow-sm",
|
|
214
|
+
sectionHeading: `text-2xl font-bold mb-4 ${p.heading}`,
|
|
215
|
+
sectionBody: `leading-relaxed ${p.textMuted} text-lg`,
|
|
216
|
+
highlightBox: "bg-gray-50 rounded-2xl p-6 border border-gray-100",
|
|
217
|
+
highlightLabel:
|
|
218
|
+
"text-sm font-semibold text-[var(--sf-color-footer-bg)] uppercase tracking-wider mb-2",
|
|
219
|
+
footerCard:
|
|
220
|
+
"bg-[var(--sf-color-footer-bg)] rounded-3xl p-6 sm:p-10 text-white relative overflow-hidden",
|
|
221
|
+
contactBlock: "bg-red-50/30 p-6 sm:p-8 rounded-3xl border border-red-100/50",
|
|
222
|
+
contactLink: `${p.link} font-semibold hover:underline`,
|
|
223
|
+
badgeOfficial:
|
|
224
|
+
"px-5 py-2 rounded-full bg-gradient-to-r from-[var(--sf-color-footer-bg)] to-blue-900 text-white text-[10px] font-bold shadow-lg shadow-blue-900/20 uppercase tracking-widest flex items-center gap-1.5 transition-transform hover:scale-105",
|
|
225
|
+
badgeVerified:
|
|
226
|
+
"px-5 py-2 rounded-full bg-gradient-to-r from-green-600 to-emerald-400 text-white text-[10px] font-bold shadow-lg shadow-green-900/20 uppercase tracking-widest flex items-center gap-1.5 transition-transform hover:scale-105",
|
|
227
|
+
badgeSecure:
|
|
228
|
+
"px-5 py-2 rounded-full bg-gradient-to-r from-[var(--sf-color-primary)] to-[var(--sf-color-secondary)] text-white text-[10px] font-bold shadow-lg shadow-red-900/20 flex items-center gap-1.5 uppercase tracking-wider transition-transform hover:scale-105",
|
|
229
|
+
},
|
|
230
|
+
resetPassword: {
|
|
231
|
+
pageRoot: `min-h-screen flex items-center justify-center ${p.pageBackgroundAlt} px-4`,
|
|
232
|
+
card: "w-full max-w-md bg-white rounded-2xl shadow-lg p-8",
|
|
233
|
+
button: `w-full ${themeUi.btn.primary} font-bold rounded-full shadow-md`,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export type SiteThemes = {
|
|
239
|
+
siteTheme: StorefrontTheme;
|
|
240
|
+
siteButtonTheme: ButtonTheme;
|
|
241
|
+
siteFormTheme: FormThemeClassNames;
|
|
242
|
+
siteLayoutTheme: LayoutThemeClassNames;
|
|
243
|
+
siteHomeTheme: HomeThemeClassNames;
|
|
244
|
+
siteNotFoundTheme: NotFoundThemeClassNames;
|
|
245
|
+
sitePageTheme: SitePageThemeClassNames;
|
|
246
|
+
/** All UI slot classNames — pass to StorefrontThemeClassesProvider */
|
|
247
|
+
themeClasses: StorefrontThemeClasses;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
function buildSiteThemeClasses(
|
|
251
|
+
overrides?: StorefrontThemeClassOverrides,
|
|
252
|
+
): StorefrontThemeClasses {
|
|
253
|
+
const base: StorefrontThemeClasses = {
|
|
254
|
+
layout: createSiteLayoutTheme(),
|
|
255
|
+
home: createSiteHomeTheme(),
|
|
256
|
+
notFound: createSiteNotFoundTheme(),
|
|
257
|
+
productCard: createSiteProductCardTheme(),
|
|
258
|
+
account: createSiteAccountTheme(),
|
|
259
|
+
order: createSiteOrderTheme(),
|
|
260
|
+
cart: createSiteCartTheme(),
|
|
261
|
+
product: createSiteProductTheme(),
|
|
262
|
+
store: createSiteStoreTheme(),
|
|
263
|
+
checkout: createSiteCheckoutUiTheme(),
|
|
264
|
+
contact: createSiteContactTheme(),
|
|
265
|
+
help: createSiteHelpTheme(),
|
|
266
|
+
pages: createSitePageTheme(),
|
|
267
|
+
};
|
|
268
|
+
if (!overrides) return base;
|
|
269
|
+
return {
|
|
270
|
+
layout: mergeThemeSlots(base.layout, overrides.layout),
|
|
271
|
+
home: mergeThemeSlots(base.home, overrides.home),
|
|
272
|
+
notFound: mergeThemeSlots(base.notFound, overrides.notFound),
|
|
273
|
+
productCard: mergeThemeSlots(base.productCard, overrides.productCard),
|
|
274
|
+
account: mergeThemeSlots(base.account, overrides.account),
|
|
275
|
+
order: mergeThemeSlots(base.order, overrides.order),
|
|
276
|
+
cart: mergeThemeSlots(base.cart, overrides.cart),
|
|
277
|
+
product: mergeThemeSlots(base.product, overrides.product),
|
|
278
|
+
store: mergeThemeSlots(base.store, overrides.store),
|
|
279
|
+
checkout: mergeThemeSlots(base.checkout, overrides.checkout),
|
|
280
|
+
contact: mergeThemeSlots(base.contact, overrides.contact),
|
|
281
|
+
help: mergeThemeSlots(base.help, overrides.help),
|
|
282
|
+
pages: mergeThemeSlots(base.pages, overrides.pages),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** One call — palette + fonts in, all theme exports out. */
|
|
287
|
+
export function defineSiteTheme(input: DefineSiteThemeInput): SiteThemes {
|
|
288
|
+
const themeClasses = buildSiteThemeClasses(input.classOverrides);
|
|
289
|
+
return {
|
|
290
|
+
siteTheme: createSiteTheme(input),
|
|
291
|
+
siteButtonTheme: createSiteButtonTheme(),
|
|
292
|
+
siteFormTheme: createSiteFormTheme(input.form),
|
|
293
|
+
siteLayoutTheme: themeClasses.layout,
|
|
294
|
+
siteHomeTheme: themeClasses.home,
|
|
295
|
+
siteNotFoundTheme: themeClasses.notFound,
|
|
296
|
+
sitePageTheme: createSitePageTheme(),
|
|
297
|
+
themeClasses,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineSiteTheme,
|
|
3
|
+
type DefineSiteThemeInput,
|
|
4
|
+
type SiteThemeFonts,
|
|
5
|
+
type SiteThemePalette,
|
|
6
|
+
type SiteThemes,
|
|
7
|
+
} from "./create-site-theme";
|
|
8
|
+
import type { FormThemeClassNames } from "./form-theme";
|
|
9
|
+
import { themePresets, type ThemePresetId } from "./presets";
|
|
10
|
+
import { mergeThemeSlots } from "./theme-slot-merge";
|
|
11
|
+
import type { DeepPartial } from "./types/deep-partial";
|
|
12
|
+
import type { StorefrontThemeClassOverrides } from "./theme-class-names";
|
|
13
|
+
|
|
14
|
+
export type DefineBrandInput = {
|
|
15
|
+
/** L3 preset id — merges default palette + class decisions */
|
|
16
|
+
preset?: ThemePresetId;
|
|
17
|
+
/** L4 brand palette (overrides preset palette) */
|
|
18
|
+
palette?: Partial<SiteThemePalette>;
|
|
19
|
+
fonts: SiteThemeFonts;
|
|
20
|
+
form?: Partial<FormThemeClassNames>;
|
|
21
|
+
/** L4 structural overrides (merged after preset) */
|
|
22
|
+
classOverrides?: DeepPartial<StorefrontThemeClassOverrides>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** L4 entry — pick a preset, set palette/fonts, optional overrides. */
|
|
26
|
+
export function defineBrand(input: DefineBrandInput): SiteThemes {
|
|
27
|
+
const preset = input.preset ? themePresets[input.preset] : undefined;
|
|
28
|
+
const palette = {
|
|
29
|
+
...(preset?.palette ?? {}),
|
|
30
|
+
...input.palette,
|
|
31
|
+
} as SiteThemePalette;
|
|
32
|
+
|
|
33
|
+
let classOverrides: StorefrontThemeClassOverrides | undefined;
|
|
34
|
+
if (preset?.classOverrides && input.classOverrides) {
|
|
35
|
+
classOverrides = mergeThemeSlots(
|
|
36
|
+
preset.classOverrides as Record<string, unknown>,
|
|
37
|
+
input.classOverrides,
|
|
38
|
+
) as StorefrontThemeClassOverrides;
|
|
39
|
+
} else {
|
|
40
|
+
classOverrides =
|
|
41
|
+
(input.classOverrides as StorefrontThemeClassOverrides | undefined) ??
|
|
42
|
+
preset?.classOverrides;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const siteInput: DefineSiteThemeInput = {
|
|
46
|
+
palette,
|
|
47
|
+
fonts: input.fonts,
|
|
48
|
+
form: input.form,
|
|
49
|
+
classOverrides,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return defineSiteTheme(siteInput);
|
|
53
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { themeCssClasses } from "./storefront-theme";
|
|
2
|
+
|
|
3
|
+
/** Optional input color overrides — defaults derive from site palette in create-site-theme. */
|
|
4
|
+
export type SiteThemeInputTokens = {
|
|
5
|
+
text?: string;
|
|
6
|
+
border?: string;
|
|
7
|
+
focus?: string;
|
|
8
|
+
background?: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
labelFocus?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** ClassName slots for form fields — library defaults; override per site or per page. */
|
|
15
|
+
export type FormThemeClassNames = {
|
|
16
|
+
field: string;
|
|
17
|
+
label: string;
|
|
18
|
+
labelFloating: string;
|
|
19
|
+
topLabel: string;
|
|
20
|
+
error: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const defaultFormThemeClassNames: FormThemeClassNames = {
|
|
24
|
+
field: [
|
|
25
|
+
"peer pt-6 pb-2 block w-full h-12 px-4 mt-0 rounded-md appearance-none",
|
|
26
|
+
"bg-[var(--sf-input-bg)] text-[var(--sf-input-text)]",
|
|
27
|
+
"border border-[var(--sf-input-border)]",
|
|
28
|
+
"focus:outline-none focus:border-[var(--sf-input-focus)] focus:ring-2 focus:ring-[var(--sf-input-focus)]/20",
|
|
29
|
+
"hover:bg-[var(--sf-input-bg)] transition-colors duration-200 antialiased",
|
|
30
|
+
"[font-family:var(--sf-font-family)]",
|
|
31
|
+
].join(" "),
|
|
32
|
+
label: "text-[var(--sf-input-label)]",
|
|
33
|
+
labelFloating: [
|
|
34
|
+
"peer-focus:top-2 peer-focus:translate-y-0 peer-focus:text-[11px]",
|
|
35
|
+
"peer-focus:text-[var(--sf-input-label-focus)] peer-focus:z-10",
|
|
36
|
+
"peer-[:not(:placeholder-shown)]:top-2 peer-[:not(:placeholder-shown)]:translate-y-0",
|
|
37
|
+
"peer-[:not(:placeholder-shown)]:text-[11px] peer-[:not(:placeholder-shown)]:z-10",
|
|
38
|
+
].join(" "),
|
|
39
|
+
topLabel: `mb-2 txt-compact-medium-plus ${themeCssClasses.text}`,
|
|
40
|
+
error: "text-rose-500 text-xs mt-1",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export function mergeFormTheme(
|
|
44
|
+
base: FormThemeClassNames,
|
|
45
|
+
overrides?: Partial<FormThemeClassNames>,
|
|
46
|
+
): FormThemeClassNames {
|
|
47
|
+
if (!overrides) return base;
|
|
48
|
+
return {
|
|
49
|
+
field: overrides.field ?? base.field,
|
|
50
|
+
label: overrides.label ?? base.label,
|
|
51
|
+
labelFloating: overrides.labelFloating ?? base.labelFloating,
|
|
52
|
+
topLabel: overrides.topLabel ?? base.topLabel,
|
|
53
|
+
error: overrides.error ?? base.error,
|
|
54
|
+
};
|
|
55
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,65 +1,90 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export type
|
|
54
|
-
|
|
55
|
-
export type
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
1
|
+
export type { ButtonTheme, ButtonVariant, ButtonSize } from "./button-theme";
|
|
2
|
+
export {
|
|
3
|
+
defaultButtonTheme,
|
|
4
|
+
pillButtonTheme,
|
|
5
|
+
mergeButtonTheme,
|
|
6
|
+
} from "./button-theme";
|
|
7
|
+
export {
|
|
8
|
+
buttonCssVarNames,
|
|
9
|
+
buttonCssClasses,
|
|
10
|
+
buildButtonCssVars,
|
|
11
|
+
type ButtonCssVarValues,
|
|
12
|
+
} from "./button-css-vars";
|
|
13
|
+
export { minimalButtonTheme } from "./button-themes/minimal";
|
|
14
|
+
export type {
|
|
15
|
+
StorefrontTheme,
|
|
16
|
+
StorefrontThemeColors,
|
|
17
|
+
StorefrontThemeFonts,
|
|
18
|
+
StorefrontThemeInputColors,
|
|
19
|
+
} from "./storefront-theme";
|
|
20
|
+
export {
|
|
21
|
+
themeCssVarNames,
|
|
22
|
+
themeCssClasses,
|
|
23
|
+
defaultStorefrontTheme,
|
|
24
|
+
buildStorefrontThemeCssVars,
|
|
25
|
+
buildStorefrontRootCssVars,
|
|
26
|
+
buildStorefrontRootCss,
|
|
27
|
+
themeColorsToButtonCssVars,
|
|
28
|
+
mergeStorefrontTheme,
|
|
29
|
+
resolveInputColors,
|
|
30
|
+
} from "./storefront-theme";
|
|
31
|
+
export {
|
|
32
|
+
defaultFormThemeClassNames,
|
|
33
|
+
mergeFormTheme,
|
|
34
|
+
type FormThemeClassNames,
|
|
35
|
+
type SiteThemeInputTokens,
|
|
36
|
+
} from "./form-theme";
|
|
37
|
+
export { mergePageTheme, mergeThemeSlots } from "./page-theme-merge";
|
|
38
|
+
export { joinClasses, layoutSlots, responsivePageX, responsivePageY } from "./theme-layout";
|
|
39
|
+
export { resolveThemeSection } from "./theme-resolve";
|
|
40
|
+
export {
|
|
41
|
+
createDefaultStorefrontThemeClasses,
|
|
42
|
+
createSiteThemeClasses,
|
|
43
|
+
createSiteAccountTheme,
|
|
44
|
+
createSiteOrderTheme,
|
|
45
|
+
createSiteCartTheme,
|
|
46
|
+
createSiteProductTheme,
|
|
47
|
+
createSiteStoreTheme,
|
|
48
|
+
createSiteCheckoutUiTheme,
|
|
49
|
+
createSiteContactTheme,
|
|
50
|
+
createSiteHelpTheme,
|
|
51
|
+
createSiteHomeTheme,
|
|
52
|
+
} from "./create-site-theme-classes";
|
|
53
|
+
export { defineBrand, type DefineBrandInput } from "./define-brand";
|
|
54
|
+
export { themePresets, retailWarmPreset, type ThemePresetId } from "./presets";
|
|
55
|
+
export type { DeepPartial } from "./types/deep-partial";
|
|
56
|
+
export {
|
|
57
|
+
defineSiteTheme,
|
|
58
|
+
createSiteTheme,
|
|
59
|
+
createSiteButtonTheme,
|
|
60
|
+
createSiteLayoutTheme,
|
|
61
|
+
createSiteNotFoundTheme,
|
|
62
|
+
createSitePageTheme,
|
|
63
|
+
paletteToThemeColors,
|
|
64
|
+
themeUi,
|
|
65
|
+
type SiteThemePalette,
|
|
66
|
+
type SiteThemeFonts,
|
|
67
|
+
type DefineSiteThemeInput,
|
|
68
|
+
type SiteThemes,
|
|
69
|
+
createSiteFormTheme,
|
|
70
|
+
} from "./create-site-theme";
|
|
71
|
+
export {
|
|
72
|
+
themeClassNames,
|
|
73
|
+
type ThemeClassNames,
|
|
74
|
+
type NotFoundThemeClassNames,
|
|
75
|
+
type LayoutThemeClassNames,
|
|
76
|
+
type HomeThemeClassNames,
|
|
77
|
+
type BasePagesThemeClassNames,
|
|
78
|
+
type SitePageThemeClassNames,
|
|
79
|
+
type AccountThemeClassNames,
|
|
80
|
+
type OrderThemeClassNames,
|
|
81
|
+
type CartThemeClassNames,
|
|
82
|
+
type ProductCardThemeClassNames,
|
|
83
|
+
type ProductThemeClassNames,
|
|
84
|
+
type StoreThemeClassNames,
|
|
85
|
+
type CheckoutUiThemeClassNames,
|
|
86
|
+
type ContactThemeClassNames,
|
|
87
|
+
type HelpThemeClassNames,
|
|
88
|
+
type StorefrontThemeClasses,
|
|
89
|
+
type StorefrontThemeClassOverrides,
|
|
90
|
+
} from "./theme-class-names";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { mergeThemeSlots, mergeThemeSlots as mergePageTheme } from "./theme-slot-merge";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { retailWarmPreset } from "./retail-warm";
|
|
2
|
+
|
|
3
|
+
export { retailWarmPreset } from "./retail-warm";
|
|
4
|
+
export type { RetailWarmPresetId } from "./retail-warm";
|
|
5
|
+
|
|
6
|
+
export const themePresets = {
|
|
7
|
+
"retail-warm": retailWarmPreset,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export type ThemePresetId = keyof typeof themePresets;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SiteThemePalette } from "../create-site-theme";
|
|
2
|
+
import type { StorefrontThemeClassOverrides } from "../theme-class-names";
|
|
3
|
+
|
|
4
|
+
/** L3 — retail-warm preset: Chocomelon-style defaults (palette + slot decisions). */
|
|
5
|
+
export const retailWarmPreset = {
|
|
6
|
+
id: "retail-warm" as const,
|
|
7
|
+
palette: {
|
|
8
|
+
primary: "#C41E3A",
|
|
9
|
+
primaryHover: "#9E1830",
|
|
10
|
+
secondary: "#D4AF37",
|
|
11
|
+
secondaryHover: "#B8942F",
|
|
12
|
+
background: "#FFFFFF",
|
|
13
|
+
backgroundAlt: "#FEF2F2",
|
|
14
|
+
surface: "#FFFFFF",
|
|
15
|
+
text: "#111111",
|
|
16
|
+
textMuted: "#6B7280",
|
|
17
|
+
textOnPrimary: "#FFFFFF",
|
|
18
|
+
footer: "#04122C",
|
|
19
|
+
header: "#FFFFFF",
|
|
20
|
+
footerText: "#F3F4F6",
|
|
21
|
+
footerHeading: "#FFFFFF",
|
|
22
|
+
footerTextMuted: "#94A3B8",
|
|
23
|
+
border: "#E5E7EB",
|
|
24
|
+
} satisfies SiteThemePalette,
|
|
25
|
+
classOverrides: {} satisfies StorefrontThemeClassOverrides,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type RetailWarmPresetId = typeof retailWarmPreset.id;
|