@kilivi/payloadcms-theme-management 0.1.9 → 0.1.10
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
CHANGED
|
@@ -1,97 +1,387 @@
|
|
|
1
|
-
# Theme
|
|
2
|
-
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🎨 **Theme
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
pnpm add @payloadcms-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
# Theme Management Plugin for Payload CMS v3
|
|
2
|
+
|
|
3
|
+
A comprehensive theme management plugin for Payload CMS v3 that provides powerful theming capabilities with SSR support, preventing FOUC (Flash of Unstyled Content).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **Multiple Theme Presets** - Cool, Brutal, Neon, Solar, and more
|
|
8
|
+
- 🎨 **Custom Color Palette** - Full HSL color customization
|
|
9
|
+
- 🔤 **Typography Control** - Google Fonts integration for headings, body, and code
|
|
10
|
+
- 📐 **Border Radius Presets** - Sharp, Rounded, or Pill styles
|
|
11
|
+
- 🌓 **Dark Mode Support** - Built-in light/dark/system mode toggle
|
|
12
|
+
- 🚀 **SSR Theme Injection** - Zero FOUC with server-side rendering
|
|
13
|
+
- ⚡ **Performance Optimized** - Critical CSS inlining, preload links
|
|
14
|
+
- 🎯 **Type Safe** - Full TypeScript support
|
|
15
|
+
- 🔧 **Highly Configurable** - Flexible plugin options
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @kilivi/payloadcms-theme-management
|
|
21
|
+
# or
|
|
22
|
+
npm install @kilivi/payloadcms-theme-management
|
|
23
|
+
# or
|
|
24
|
+
yarn add @kilivi/payloadcms-theme-management
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### 1. Add Plugin to Payload Config
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { buildConfig } from 'payload'
|
|
33
|
+
import { themeManagementPlugin } from '@kilivi/payloadcms-theme-management'
|
|
34
|
+
|
|
35
|
+
export default buildConfig({
|
|
36
|
+
// ... other config
|
|
37
|
+
|
|
38
|
+
globals: [
|
|
39
|
+
{
|
|
40
|
+
slug: 'site-settings',
|
|
41
|
+
fields: [
|
|
42
|
+
{
|
|
43
|
+
name: 'siteName',
|
|
44
|
+
type: 'text',
|
|
45
|
+
},
|
|
46
|
+
// Theme configuration will be injected here
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
|
|
51
|
+
plugins: [
|
|
52
|
+
themeManagementPlugin({
|
|
53
|
+
enabled: true,
|
|
54
|
+
targetCollection: 'site-settings',
|
|
55
|
+
defaultTheme: 'cool',
|
|
56
|
+
includeColorModeToggle: true,
|
|
57
|
+
enableLogging: true,
|
|
58
|
+
}),
|
|
59
|
+
],
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Add Server Theme Injection (Next.js)
|
|
64
|
+
|
|
65
|
+
**⚠️ IMPORTANT:** Import from `/server` entry point!
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// app/layout.tsx
|
|
69
|
+
import { ServerThemeInjector } from '@kilivi/payloadcms-theme-management/server'
|
|
70
|
+
import configPromise from '@payload-config'
|
|
71
|
+
import { getPayload } from 'payload'
|
|
72
|
+
|
|
73
|
+
export default async function RootLayout({
|
|
74
|
+
children,
|
|
75
|
+
}: {
|
|
76
|
+
children: React.ReactNode
|
|
77
|
+
}) {
|
|
78
|
+
const payload = await getPayload({ config: configPromise })
|
|
79
|
+
|
|
80
|
+
const { docs } = await payload.find({
|
|
81
|
+
collection: 'site-settings',
|
|
82
|
+
limit: 1,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<html lang="en">
|
|
87
|
+
<head>
|
|
88
|
+
<ServerThemeInjector themeConfiguration={docs[0]?.themeConfiguration} />
|
|
89
|
+
</head>
|
|
90
|
+
<body>{children}</body>
|
|
91
|
+
</html>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 3. Use Theme Variables in Your CSS
|
|
97
|
+
|
|
98
|
+
The plugin injects CSS custom properties you can use:
|
|
99
|
+
|
|
100
|
+
```css
|
|
101
|
+
.my-component {
|
|
102
|
+
background-color: hsl(var(--background));
|
|
103
|
+
color: hsl(var(--foreground));
|
|
104
|
+
border-radius: var(--radius);
|
|
105
|
+
font-family: var(--font-body);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or with Tailwind CSS:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<div className="bg-primary text-primary-foreground rounded-lg">
|
|
113
|
+
Themed Content
|
|
114
|
+
</div>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## Plugin Configuration Options
|
|
119
|
+
|
|
120
|
+
| Option | Type | Default | Description |
|
|
121
|
+
|--------|------|---------|-------------|
|
|
122
|
+
| `enabled` | `boolean` | `true` | Enable/disable the plugin |
|
|
123
|
+
| `targetCollection` | `string` | `'site-settings'` | Global or collection slug to add theme field to |
|
|
124
|
+
| `themePresets` | `ThemePreset[]` | Built-in presets | Custom theme presets |
|
|
125
|
+
| `defaultTheme` | `string` | `'cool'` | Default theme preset name |
|
|
126
|
+
| `includeColorModeToggle` | `boolean` | `true` | Show light/dark mode toggle |
|
|
127
|
+
| `includeCustomCSS` | `boolean` | `true` | Allow custom CSS injection |
|
|
128
|
+
| `includeBrandIdentity` | `boolean` | `false` | Show brand identity fields |
|
|
129
|
+
| `enableAdvancedFeatures` | `boolean` | `true` | Enable advanced customization |
|
|
130
|
+
| `enableLogging` | `boolean` | `false` | Log plugin actions to console |
|
|
131
|
+
|
|
132
|
+
## Available Theme Presets
|
|
133
|
+
|
|
134
|
+
- **Cool** - Professional blue theme
|
|
135
|
+
- **Brutal** - High contrast, bold design
|
|
136
|
+
- **Neon** - Vibrant, energetic colors
|
|
137
|
+
- **Solar** - Warm, golden tones
|
|
138
|
+
- **Dealership** - Automotive-inspired
|
|
139
|
+
- **Real Estate** - Professional property theme (+ Gold, Neutral variants)
|
|
140
|
+
|
|
141
|
+
## API Reference
|
|
142
|
+
|
|
143
|
+
### Main Entry Point
|
|
144
|
+
|
|
145
|
+
Import from `@kilivi/payloadcms-theme-management` for client-safe code:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Plugin
|
|
149
|
+
import { themeManagementPlugin } from '@kilivi/payloadcms-theme-management'
|
|
150
|
+
|
|
151
|
+
// Client Components
|
|
152
|
+
import { ThemeProvider } from '@kilivi/payloadcms-theme-management'
|
|
153
|
+
|
|
154
|
+
// Utilities
|
|
155
|
+
import {
|
|
156
|
+
generateThemeColorsCss,
|
|
157
|
+
generateThemeCSS,
|
|
158
|
+
getThemeStyles,
|
|
159
|
+
resolveThemeConfiguration,
|
|
160
|
+
fetchThemeConfiguration,
|
|
161
|
+
} from '@kilivi/payloadcms-theme-management'
|
|
162
|
+
|
|
163
|
+
// Types
|
|
164
|
+
import type {
|
|
165
|
+
ThemePreset,
|
|
166
|
+
ThemeDefaults,
|
|
167
|
+
ThemeManagementPluginOptions,
|
|
168
|
+
} from '@kilivi/payloadcms-theme-management'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Server Entry Point
|
|
172
|
+
|
|
173
|
+
⚠️ **Import from `/server` for server components only:**
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import {
|
|
177
|
+
ServerThemeInjector,
|
|
178
|
+
getThemeCriticalCSS,
|
|
179
|
+
getThemeCSSPath,
|
|
180
|
+
generateThemePreloadLinks,
|
|
181
|
+
} from '@kilivi/payloadcms-theme-management/server'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Subpath Exports
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// Direct field imports
|
|
188
|
+
import { ThemeColorPickerField } from '@kilivi/payloadcms-theme-management/fields/ThemeColorPickerField'
|
|
189
|
+
import { ThemeTokenSelectField } from '@kilivi/payloadcms-theme-management/fields/ThemeTokenSelectField'
|
|
190
|
+
|
|
191
|
+
// Direct component imports
|
|
192
|
+
import { ThemePreview } from '@kilivi/payloadcms-theme-management/components/ThemePreview'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Integration Examples
|
|
196
|
+
|
|
197
|
+
### With Tailwind CSS
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// tailwind.config.ts
|
|
201
|
+
import type { Config } from 'tailwindcss'
|
|
202
|
+
|
|
203
|
+
const config: Config = {
|
|
204
|
+
theme: {
|
|
205
|
+
extend: {
|
|
206
|
+
colors: {
|
|
207
|
+
background: 'hsl(var(--background))',
|
|
208
|
+
foreground: 'hsl(var(--foreground))',
|
|
209
|
+
primary: {
|
|
210
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
211
|
+
foreground: 'hsl(var(--primary-foreground))',
|
|
212
|
+
},
|
|
213
|
+
// ... other color variables
|
|
214
|
+
},
|
|
215
|
+
borderRadius: {
|
|
216
|
+
lg: 'var(--radius)',
|
|
217
|
+
md: 'calc(var(--radius) - 2px)',
|
|
218
|
+
sm: 'calc(var(--radius) - 4px)',
|
|
219
|
+
},
|
|
220
|
+
fontFamily: {
|
|
221
|
+
heading: ['var(--font-heading)', 'sans-serif'],
|
|
222
|
+
body: ['var(--font-body)', 'sans-serif'],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Fetching Theme Configuration
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { fetchThemeConfiguration } from '@kilivi/payloadcms-theme-management'
|
|
233
|
+
|
|
234
|
+
// Simple usage
|
|
235
|
+
const theme = await fetchThemeConfiguration()
|
|
236
|
+
|
|
237
|
+
// With options
|
|
238
|
+
const theme = await fetchThemeConfiguration({
|
|
239
|
+
collectionSlug: 'site-settings',
|
|
240
|
+
depth: 2,
|
|
241
|
+
locale: 'en',
|
|
242
|
+
})
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Custom Theme Preset
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import type { ThemePreset } from '@kilivi/payloadcms-theme-management'
|
|
249
|
+
|
|
250
|
+
const myTheme: ThemePreset = {
|
|
251
|
+
name: 'my-custom-theme',
|
|
252
|
+
label: 'My Custom Theme',
|
|
253
|
+
colors: {
|
|
254
|
+
primary: { h: 220, s: 70, l: 50 },
|
|
255
|
+
secondary: { h: 180, s: 60, l: 45 },
|
|
256
|
+
// ... other colors
|
|
257
|
+
},
|
|
258
|
+
borderRadius: 'rounded',
|
|
259
|
+
typography: {
|
|
260
|
+
heading: 'Poppins',
|
|
261
|
+
body: 'Inter',
|
|
262
|
+
code: 'Fira Code',
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Use in plugin config
|
|
267
|
+
themeManagementPlugin({
|
|
268
|
+
themePresets: [myTheme],
|
|
269
|
+
defaultTheme: 'my-custom-theme',
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Migrating from Older Versions
|
|
274
|
+
|
|
275
|
+
See [MIGRATION_GUIDE.md](./MIGRATION_GUIDE.md) for detailed migration instructions.
|
|
276
|
+
|
|
277
|
+
### Key Changes in v0.1.9+
|
|
278
|
+
|
|
279
|
+
- **Server components must import from `/server`**
|
|
280
|
+
- Added `server-only` package to prevent client bundling errors
|
|
281
|
+
- Removed `ServerThemeInjector` from main entry point
|
|
282
|
+
|
|
283
|
+
```diff
|
|
284
|
+
- import { ServerThemeInjector } from '@kilivi/payloadcms-theme-management'
|
|
285
|
+
+ import { ServerThemeInjector } from '@kilivi/payloadcms-theme-management/server'
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
## Testing
|
|
290
|
+
|
|
291
|
+
See [TEST_APP_GUIDE.md](./TEST_APP_GUIDE.md) for instructions on creating a test application.
|
|
292
|
+
|
|
293
|
+
## Troubleshooting
|
|
294
|
+
|
|
295
|
+
### `Module not found: Can't resolve 'fs/promises'`
|
|
296
|
+
|
|
297
|
+
**Solution:** Make sure you're using v0.1.9+ and importing server components from `/server`:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { ServerThemeInjector } from '@kilivi/payloadcms-theme-management/server'
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Then clear your cache:
|
|
304
|
+
```bash
|
|
305
|
+
rm -rf .next node_modules/.cache
|
|
306
|
+
pnpm install
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Theme Not Applying
|
|
310
|
+
|
|
311
|
+
1. Verify `ServerThemeInjector` is in your `<head>` tag
|
|
312
|
+
2. Check that site settings exist with theme configuration
|
|
313
|
+
3. Inspect page source - should see `<style>` tag with CSS variables
|
|
314
|
+
4. Ensure Tailwind/CSS is configured to use the CSS variables
|
|
315
|
+
|
|
316
|
+
### TypeScript Errors
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# Regenerate Payload types
|
|
320
|
+
pnpm payload generate:types
|
|
321
|
+
|
|
322
|
+
# Restart TypeScript server
|
|
323
|
+
# VS Code: Ctrl+Shift+P → "TypeScript: Restart TS Server"
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Documentation
|
|
327
|
+
|
|
328
|
+
- [Migration Guide](./MIGRATION_GUIDE.md) - Upgrading from older versions
|
|
329
|
+
- [Test App Guide](./TEST_APP_GUIDE.md) - Create a test application
|
|
330
|
+
- [Server/Client Separation](./SERVER_CLIENT_SEPARATION.md) - Technical details
|
|
331
|
+
- [Build Setup](./BUILD_SETUP.md) - Build configuration details
|
|
332
|
+
|
|
333
|
+
## Development
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# Install dependencies
|
|
337
|
+
pnpm install
|
|
338
|
+
|
|
339
|
+
# Build the plugin
|
|
340
|
+
pnpm build
|
|
341
|
+
|
|
342
|
+
# Watch mode for development
|
|
343
|
+
pnpm dev
|
|
344
|
+
|
|
345
|
+
# Clean build artifacts
|
|
346
|
+
pnpm clean
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Contributing
|
|
350
|
+
|
|
351
|
+
Contributions are welcome! Please:
|
|
352
|
+
|
|
353
|
+
1. Fork the repository
|
|
354
|
+
2. Create a feature branch
|
|
355
|
+
3. Make your changes
|
|
356
|
+
4. Add tests if applicable
|
|
357
|
+
5. Submit a pull request
|
|
358
|
+
|
|
359
|
+
## License
|
|
360
|
+
|
|
361
|
+
Apache-2.0
|
|
362
|
+
|
|
363
|
+
## Author
|
|
364
|
+
|
|
365
|
+
Created for Payload CMS v3 applications.
|
|
366
|
+
|
|
367
|
+
## Links
|
|
368
|
+
|
|
369
|
+
- [NPM Package](https://www.npmjs.com/package/@kilivi/payloadcms-theme-management)
|
|
370
|
+
- [GitHub Repository](https://github.com/vitakili/payload-plugins)
|
|
371
|
+
- [Payload CMS](https://payloadcms.com)
|
|
372
|
+
|
|
373
|
+
## Changelog
|
|
374
|
+
|
|
375
|
+
### v0.1.9 (Latest)
|
|
376
|
+
- ✅ **Fixed:** Server/client component separation
|
|
377
|
+
- ✅ **Added:** `server-only` package to prevent bundling errors
|
|
378
|
+
- ✅ **Changed:** `ServerThemeInjector` now exported from `/server` entry
|
|
379
|
+
- ✅ **Added:** Comprehensive documentation
|
|
380
|
+
|
|
381
|
+
### v0.1.7
|
|
382
|
+
- ✅ **Fixed:** ESM import resolution with `.js` extensions
|
|
383
|
+
- ✅ **Added:** SWC + TypeScript build pipeline
|
|
384
|
+
|
|
385
|
+
### v0.1.5
|
|
386
|
+
- Initial release with theme management features
|
|
387
|
+
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import type { SiteSetting } from '../payload-types.js';
|
|
2
1
|
interface ServerThemeInjectorProps {
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Theme configuration object from your site settings.
|
|
4
|
+
* Pass the themeConfiguration field directly, not the entire site settings object.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const siteSettings = await payload.findGlobal({ slug: 'site-settings' })
|
|
9
|
+
* <ServerThemeInjector themeConfiguration={siteSettings.themeConfiguration} />
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
themeConfiguration?: unknown;
|
|
4
13
|
}
|
|
5
14
|
/**
|
|
6
15
|
* Server-side theme CSS injector to prevent FOUC
|
|
7
16
|
* This component injects critical theme CSS directly into the HTML head during SSR
|
|
8
17
|
*/
|
|
9
|
-
export declare function ServerThemeInjector({
|
|
18
|
+
export declare function ServerThemeInjector({ themeConfiguration }: Readonly<ServerThemeInjectorProps>): Promise<import("react").JSX.Element>;
|
|
10
19
|
export {};
|
|
11
20
|
//# sourceMappingURL=ServerThemeInjector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerThemeInjector.d.ts","sourceRoot":"","sources":["../../src/components/ServerThemeInjector.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ServerThemeInjector.d.ts","sourceRoot":"","sources":["../../src/components/ServerThemeInjector.tsx"],"names":[],"mappings":"AAaA,UAAU,wBAAwB;IAChC;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAMD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,EAAE,QAAQ,CAAC,wBAAwB,CAAC,wCAuFnG"}
|
|
@@ -7,17 +7,13 @@ import { generateThemeCSS } from '../utils/themeUtils.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* Server-side theme CSS injector to prevent FOUC
|
|
9
9
|
* This component injects critical theme CSS directly into the HTML head during SSR
|
|
10
|
-
*/ export async function ServerThemeInjector({
|
|
11
|
-
|
|
10
|
+
*/ export async function ServerThemeInjector({ themeConfiguration }) {
|
|
11
|
+
// Accept unknown type to avoid type conflicts between app's payload-types and plugin's payload-types
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
const resolvedConfiguration = resolveThemeConfiguration(themeConfiguration);
|
|
12
14
|
const { theme, borderRadius, customCSS, lightMode, darkMode, fontScale, spacing, animationLevel, allowColorModeToggle, colorMode, typography } = resolvedConfiguration;
|
|
13
|
-
|
|
14
|
-
...siteSettings?.themeConfiguration ?? {}
|
|
15
|
-
};
|
|
16
|
-
if ('typography' in baseThemeConfiguration) {
|
|
17
|
-
delete baseThemeConfiguration.typography;
|
|
18
|
-
}
|
|
15
|
+
// Use resolved configuration directly
|
|
19
16
|
const normalizedThemeConfiguration = {
|
|
20
|
-
...baseThemeConfiguration,
|
|
21
17
|
theme,
|
|
22
18
|
colorMode,
|
|
23
19
|
allowColorModeToggle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/ServerThemeInjector.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../src/components/ServerThemeInjector.tsx"],"sourcesContent":["import { getBorderRadiusConfig } from '../providers/Theme/themeConfig.js'\r\nimport type { BorderRadiusPreset, ThemeDefaults } from '../providers/Theme/types.js'\r\nimport type { ThemeTypographyPreset } from '../presets.js'\r\nimport { resolveThemeConfiguration } from '../utils/resolveThemeConfiguration.js'\r\nimport type { ResolvedThemeConfiguration } from '../utils/resolveThemeConfiguration.js'\r\nimport {\r\n createFallbackCriticalCSS,\r\n getThemeCriticalCSS,\r\n getThemeCSSPath,\r\n} from '../utils/themeAssets.js'\r\nimport { generateThemeColorsCss } from '../utils/themeColors.js'\r\nimport { generateThemeCSS } from '../utils/themeUtils.js'\r\n\r\ninterface ServerThemeInjectorProps {\r\n /**\r\n * Theme configuration object from your site settings.\r\n * Pass the themeConfiguration field directly, not the entire site settings object.\r\n * \r\n * @example\r\n * ```tsx\r\n * const siteSettings = await payload.findGlobal({ slug: 'site-settings' })\r\n * <ServerThemeInjector themeConfiguration={siteSettings.themeConfiguration} />\r\n * ```\r\n */\r\n themeConfiguration?: unknown\r\n}\r\n\r\ntype RuntimeThemeConfiguration = Omit<ResolvedThemeConfiguration, 'typography'> & {\r\n typography?: ThemeTypographyPreset | null\r\n}\r\n\r\n/**\r\n * Server-side theme CSS injector to prevent FOUC\r\n * This component injects critical theme CSS directly into the HTML head during SSR\r\n */\r\nexport async function ServerThemeInjector({ themeConfiguration }: Readonly<ServerThemeInjectorProps>) {\r\n // Accept unknown type to avoid type conflicts between app's payload-types and plugin's payload-types\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const resolvedConfiguration = resolveThemeConfiguration(themeConfiguration as any)\r\n const {\r\n theme,\r\n borderRadius,\r\n customCSS,\r\n lightMode,\r\n darkMode,\r\n fontScale,\r\n spacing,\r\n animationLevel,\r\n allowColorModeToggle,\r\n colorMode,\r\n typography,\r\n } = resolvedConfiguration\r\n\r\n // Use resolved configuration directly\r\n const normalizedThemeConfiguration: RuntimeThemeConfiguration = {\r\n theme,\r\n colorMode,\r\n allowColorModeToggle,\r\n borderRadius,\r\n fontScale,\r\n spacing,\r\n animationLevel,\r\n customCSS: customCSS ?? undefined,\r\n lightMode,\r\n darkMode,\r\n typography: typography ?? undefined,\r\n }\r\n\r\n let criticalCSS = await getThemeCriticalCSS(theme as ThemeDefaults)\r\n criticalCSS ??= createFallbackCriticalCSS(theme as ThemeDefaults)\r\n\r\n const borderRadiusMap: Record<string, BorderRadiusPreset> = {\r\n none: 'none',\r\n small: 'small',\r\n medium: 'medium',\r\n large: 'large',\r\n xl: 'xl',\r\n full: 'xl',\r\n }\r\n const mappedBorderRadius = borderRadiusMap[borderRadius ?? 'medium'] ?? 'medium'\r\n const borderRadiusConfig = getBorderRadiusConfig(mappedBorderRadius)\r\n const cssRecord = typeof borderRadiusConfig?.css === 'string'\r\n ? { '--radius-default': borderRadiusConfig.css }\r\n : borderRadiusConfig?.css ?? {}\r\n\r\n const borderRadiusCSS = Object.entries(cssRecord)\r\n .map(([property, value]) => ` ${property}: ${value};`)\r\n .join('\\n')\r\n\r\n const borderRadiusBlock = borderRadiusCSS\r\n ? `:root {\r\n${borderRadiusCSS}\r\n}`\r\n : ''\r\n\r\n const themeConfigurationCSS = generateThemeCSS(normalizedThemeConfiguration)\r\n const colorModesCSS = generateThemeColorsCss({ themeName: theme, lightMode, darkMode })\r\n const customCSSBlock = typeof customCSS === 'string' ? customCSS.trim() : ''\r\n\r\n const combinedCSS = [\r\n criticalCSS,\r\n borderRadiusBlock,\r\n themeConfigurationCSS,\r\n colorModesCSS,\r\n customCSSBlock,\r\n ]\r\n .filter((block) => Boolean(block?.trim?.().length))\r\n .join('\\n\\n')\r\n\r\n const themeCSSPath = getThemeCSSPath(theme as ThemeDefaults)\r\n return (\r\n <>\r\n <style\r\n id=\"theme-critical-css\"\r\n dangerouslySetInnerHTML={{\r\n __html: combinedCSS,\r\n }}\r\n />\r\n <link rel=\"preload\" href={themeCSSPath} as=\"style\" />\r\n <link rel=\"stylesheet\" href={themeCSSPath} />\r\n </>\r\n )\r\n}\r\n"],"names":["getBorderRadiusConfig","resolveThemeConfiguration","createFallbackCriticalCSS","getThemeCriticalCSS","getThemeCSSPath","generateThemeColorsCss","generateThemeCSS","ServerThemeInjector","themeConfiguration","resolvedConfiguration","theme","borderRadius","customCSS","lightMode","darkMode","fontScale","spacing","animationLevel","allowColorModeToggle","colorMode","typography","normalizedThemeConfiguration","undefined","criticalCSS","borderRadiusMap","none","small","medium","large","xl","full","mappedBorderRadius","borderRadiusConfig","cssRecord","css","borderRadiusCSS","Object","entries","map","property","value","join","borderRadiusBlock","themeConfigurationCSS","colorModesCSS","themeName","customCSSBlock","trim","combinedCSS","filter","block","Boolean","length","themeCSSPath","style","id","dangerouslySetInnerHTML","__html","link","rel","href","as"],"mappings":";AAAA,SAASA,qBAAqB,QAAQ,oCAAmC;AAGzE,SAASC,yBAAyB,QAAQ,wCAAuC;AAEjF,SACEC,yBAAyB,EACzBC,mBAAmB,EACnBC,eAAe,QACV,0BAAyB;AAChC,SAASC,sBAAsB,QAAQ,0BAAyB;AAChE,SAASC,gBAAgB,QAAQ,yBAAwB;AAoBzD;;;CAGC,GACD,OAAO,eAAeC,oBAAoB,EAAEC,kBAAkB,EAAsC;IAClG,qGAAqG;IACrG,8DAA8D;IAC9D,MAAMC,wBAAwBR,0BAA0BO;IACxD,MAAM,EACJE,KAAK,EACLC,YAAY,EACZC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,OAAO,EACPC,cAAc,EACdC,oBAAoB,EACpBC,SAAS,EACTC,UAAU,EACX,GAAGX;IAEJ,sCAAsC;IACtC,MAAMY,+BAA0D;QAC9DX;QACAS;QACAD;QACAP;QACAI;QACAC;QACAC;QACAL,WAAWA,aAAaU;QACxBT;QACAC;QACAM,YAAYA,cAAcE;IAC5B;IAEA,IAAIC,cAAc,MAAMpB,oBAAoBO;IAC5Ca,gBAAgBrB,0BAA0BQ;IAE1C,MAAMc,kBAAsD;QAC1DC,MAAM;QACNC,OAAO;QACPC,QAAQ;QACRC,OAAO;QACPC,IAAI;QACJC,MAAM;IACR;IACA,MAAMC,qBAAqBP,eAAe,CAACb,gBAAgB,SAAS,IAAI;IACxE,MAAMqB,qBAAqBhC,sBAAsB+B;IACjD,MAAME,YAAY,OAAOD,oBAAoBE,QAAQ,WACjD;QAAE,oBAAoBF,mBAAmBE,GAAG;IAAC,IAC7CF,oBAAoBE,OAAO,CAAC;IAEhC,MAAMC,kBAAkBC,OAAOC,OAAO,CAACJ,WACpCK,GAAG,CAAC,CAAC,CAACC,UAAUC,MAAM,GAAK,CAAC,EAAE,EAAED,SAAS,EAAE,EAAEC,MAAM,CAAC,CAAC,EACrDC,IAAI,CAAC;IAER,MAAMC,oBAAoBP,kBACtB,CAAC;AACP,EAAEA,gBAAgB;CACjB,CAAC,GACI;IAEJ,MAAMQ,wBAAwBrC,iBAAiBe;IAC/C,MAAMuB,gBAAgBvC,uBAAuB;QAAEwC,WAAWnC;QAAOG;QAAWC;IAAS;IACrF,MAAMgC,iBAAiB,OAAOlC,cAAc,WAAWA,UAAUmC,IAAI,KAAK;IAE1E,MAAMC,cAAc;QAClBzB;QACAmB;QACAC;QACAC;QACAE;KACD,CACEG,MAAM,CAAC,CAACC,QAAUC,QAAQD,OAAOH,SAASK,SAC1CX,IAAI,CAAC;IAER,MAAMY,eAAejD,gBAAgBM;IACrC,qBACE;;0BACE,KAAC4C;gBACCC,IAAG;gBACHC,yBAAyB;oBACvBC,QAAQT;gBACV;;0BAEF,KAACU;gBAAKC,KAAI;gBAAUC,MAAMP;gBAAcQ,IAAG;;0BAC3C,KAACH;gBAAKC,KAAI;gBAAaC,MAAMP;;;;AAGnC"}
|