@orion-studios/payload-admin-components 0.1.0 → 0.2.0-beta.1
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/dist/admin.css +68 -57
- package/dist/index.d.mts +119 -1
- package/dist/index.d.ts +119 -1
- package/dist/index.js +1234 -76
- package/dist/index.mjs +1222 -75
- package/dist/styles/admin.css +85 -0
- package/dist/styles/overrides.css +375 -0
- package/dist/styles/themes/brand-dark.css +69 -0
- package/dist/styles/themes/brand-light.css +69 -0
- package/dist/styles/themes/dark.css +68 -0
- package/dist/styles/themes/light.css +68 -0
- package/package.json +3 -3
- package/src/components/BlockPicker.tsx +167 -0
- package/src/components/Dashboard.tsx +415 -0
- package/src/components/EmptyState.tsx +86 -0
- package/src/components/HelpTooltip.tsx +121 -0
- package/src/components/Icon.tsx +16 -0
- package/src/components/Logo.tsx +52 -0
- package/src/components/SectionTabs.tsx +84 -0
- package/src/components/StatusBadge.tsx +49 -0
- package/src/components/ThemeSwitcher.tsx +120 -0
- package/src/components/WelcomeHeader.tsx +54 -0
- package/src/fields/themePreference.ts +22 -0
- package/src/helpers/configureAdmin.ts +122 -0
- package/src/helpers/withTooltips.ts +91 -0
- package/src/hooks/useTheme.ts +128 -0
- package/src/index.ts +27 -0
- package/src/styles/admin.css +68 -57
- package/src/styles/overrides.css +375 -0
- package/src/styles/themes/brand-dark.css +69 -0
- package/src/styles/themes/brand-light.css +69 -0
- package/src/styles/themes/dark.css +68 -0
- package/src/styles/themes/light.css +68 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { CollectionConfig, GlobalConfig } from 'payload'
|
|
2
|
+
import { themePreferenceField } from '../fields/themePreference'
|
|
3
|
+
|
|
4
|
+
export interface AdminConfig {
|
|
5
|
+
brandName: string
|
|
6
|
+
brandPrimary?: string
|
|
7
|
+
brandSecondary?: string
|
|
8
|
+
logoUrl?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generates Payload admin configuration with Orion Studios admin components.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { configureAdmin } from '@orion-studios/payload-admin-components'
|
|
17
|
+
*
|
|
18
|
+
* const adminConfig = configureAdmin({
|
|
19
|
+
* brandName: 'My Site',
|
|
20
|
+
* brandPrimary: '#2d5016',
|
|
21
|
+
* brandSecondary: '#8B4513',
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* export default buildConfig({
|
|
25
|
+
* admin: { ...adminConfig.admin },
|
|
26
|
+
* collections: [adminConfig.wrapUsers(Users), Media, Pages],
|
|
27
|
+
* globals: adminConfig.wrapGlobals([SiteSettings, Header, Footer]),
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function configureAdmin(config: AdminConfig) {
|
|
32
|
+
const { brandName, brandPrimary, brandSecondary } = config
|
|
33
|
+
|
|
34
|
+
// Build CSS string to inject brand colors
|
|
35
|
+
const brandCssVars = [
|
|
36
|
+
brandPrimary ? `--brand-primary: ${brandPrimary};` : '',
|
|
37
|
+
brandSecondary ? `--brand-secondary: ${brandSecondary};` : '',
|
|
38
|
+
]
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.join('\n ')
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
admin: {
|
|
44
|
+
components: {
|
|
45
|
+
graphics: {
|
|
46
|
+
Logo: {
|
|
47
|
+
exportName: 'Logo',
|
|
48
|
+
path: '@orion-studios/payload-admin-components',
|
|
49
|
+
},
|
|
50
|
+
Icon: {
|
|
51
|
+
exportName: 'Icon',
|
|
52
|
+
path: '@orion-studios/payload-admin-components',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
views: {
|
|
56
|
+
dashboard: {
|
|
57
|
+
Component: {
|
|
58
|
+
exportName: 'Dashboard',
|
|
59
|
+
path: '@orion-studios/payload-admin-components',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
providers: [
|
|
64
|
+
{
|
|
65
|
+
exportName: 'ThemeProvider',
|
|
66
|
+
path: '@orion-studios/payload-admin-components',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
afterNavLinks: [
|
|
70
|
+
{
|
|
71
|
+
exportName: 'ThemeSwitcher',
|
|
72
|
+
path: '@orion-studios/payload-admin-components',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
meta: {
|
|
77
|
+
titleSuffix: ` — ${brandName}`,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
brandName,
|
|
82
|
+
brandPrimary: brandPrimary || '#3b82f6',
|
|
83
|
+
brandSecondary: brandSecondary || '#8b5cf6',
|
|
84
|
+
brandCssVars,
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Wraps the Users collection to add theme preference field.
|
|
88
|
+
*/
|
|
89
|
+
wrapUsers(usersCollection: CollectionConfig): CollectionConfig {
|
|
90
|
+
return {
|
|
91
|
+
...usersCollection,
|
|
92
|
+
fields: [...(usersCollection.fields || []), themePreferenceField],
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Wraps globals with intuitive group labels.
|
|
98
|
+
* Maps common global slugs to the "Site Design" group with user-friendly labels.
|
|
99
|
+
*/
|
|
100
|
+
wrapGlobals(globals: GlobalConfig[]): GlobalConfig[] {
|
|
101
|
+
const labelMap: Record<string, { group: string; label: string }> = {
|
|
102
|
+
header: { group: 'Site Design', label: 'Header & Navigation' },
|
|
103
|
+
footer: { group: 'Site Design', label: 'Footer' },
|
|
104
|
+
'site-settings': { group: 'Site Design', label: 'Website Settings' },
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return globals.map((global) => {
|
|
108
|
+
const mapping = labelMap[global.slug]
|
|
109
|
+
if (!mapping) return global
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
...global,
|
|
113
|
+
admin: {
|
|
114
|
+
...global.admin,
|
|
115
|
+
group: mapping.group,
|
|
116
|
+
},
|
|
117
|
+
label: mapping.label,
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Field } from 'payload'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default tooltip descriptions for common Payload CMS field names.
|
|
5
|
+
* These provide helpful context for non-technical users.
|
|
6
|
+
*/
|
|
7
|
+
const defaultTooltips: Record<string, string> = {
|
|
8
|
+
title: 'The main title displayed on this page.',
|
|
9
|
+
slug: 'The URL-friendly name for this page (e.g., "about-us"). This appears in the web address.',
|
|
10
|
+
template: 'Choose a layout template. This controls the overall structure of the page.',
|
|
11
|
+
parent: 'Select a parent page to nest this page under. This affects the URL path.',
|
|
12
|
+
path: 'The full URL path for this page. This is automatically generated from the slug and parent.',
|
|
13
|
+
layout: 'Add and arrange content sections on your page. Each section is a building block.',
|
|
14
|
+
metaTitle: 'The title shown in search engine results and browser tabs. Keep under 60 characters.',
|
|
15
|
+
metaDescription: 'A brief summary shown in search results. Keep under 160 characters for best results.',
|
|
16
|
+
canonicalUrl: 'The preferred URL for this page. Used to prevent duplicate content in search engines.',
|
|
17
|
+
ogImage: 'The image shown when this page is shared on social media (Facebook, LinkedIn, etc.).',
|
|
18
|
+
noIndex: 'When enabled, search engines will not list this page in results.',
|
|
19
|
+
noFollow: 'When enabled, search engines will not follow links on this page.',
|
|
20
|
+
publishedAt: 'The date and time this page was first published.',
|
|
21
|
+
alt: 'Describe this image for screen readers and search engines. Be specific and concise.',
|
|
22
|
+
navItems: 'The links shown in your website\'s navigation menu.',
|
|
23
|
+
copyright: 'The copyright text displayed in your website footer.',
|
|
24
|
+
contactEmail: 'The email address displayed in your footer and contact sections.',
|
|
25
|
+
contactPhone: 'The phone number displayed in your footer and contact sections.',
|
|
26
|
+
siteName: 'Your website\'s name. Appears in browser tabs and search results.',
|
|
27
|
+
tagline: 'A short phrase describing your business. Used in SEO and site metadata.',
|
|
28
|
+
canonicalBaseUrl: 'The base URL of your website (e.g., "https://example.com"). Used for generating canonical URLs.',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Adds tooltip descriptions to Payload fields by matching field names
|
|
33
|
+
* to a tooltip map. This enriches the admin UI with contextual help.
|
|
34
|
+
*
|
|
35
|
+
* Usage:
|
|
36
|
+
* ```ts
|
|
37
|
+
* const fields = withTooltips(myFields, {
|
|
38
|
+
* // Optional: override or add custom tooltips
|
|
39
|
+
* myCustomField: 'Explanation of what this field does',
|
|
40
|
+
* })
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function withTooltips(
|
|
44
|
+
fields: Field[],
|
|
45
|
+
customTooltips?: Record<string, string>,
|
|
46
|
+
): Field[] {
|
|
47
|
+
const tooltips = { ...defaultTooltips, ...customTooltips }
|
|
48
|
+
|
|
49
|
+
return fields.map((field) => addTooltipToField(field, tooltips))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function addTooltipToField(field: Field, tooltips: Record<string, string>): Field {
|
|
53
|
+
// Handle fields with a name property
|
|
54
|
+
if ('name' in field && field.name && tooltips[field.name]) {
|
|
55
|
+
const tooltip = tooltips[field.name]
|
|
56
|
+
const admin = (field as any).admin
|
|
57
|
+
|
|
58
|
+
// Only add description if one doesn't already exist
|
|
59
|
+
if (!admin?.description) {
|
|
60
|
+
return {
|
|
61
|
+
...field,
|
|
62
|
+
admin: {
|
|
63
|
+
...admin,
|
|
64
|
+
description: tooltip,
|
|
65
|
+
},
|
|
66
|
+
} as Field
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Recurse into nested field types
|
|
71
|
+
if ('fields' in field && Array.isArray((field as any).fields)) {
|
|
72
|
+
return {
|
|
73
|
+
...field,
|
|
74
|
+
fields: (field as any).fields.map((f: Field) => addTooltipToField(f, tooltips)),
|
|
75
|
+
} as Field
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if ('tabs' in field && Array.isArray((field as any).tabs)) {
|
|
79
|
+
return {
|
|
80
|
+
...field,
|
|
81
|
+
tabs: (field as any).tabs.map((tab: any) => ({
|
|
82
|
+
...tab,
|
|
83
|
+
fields: tab.fields
|
|
84
|
+
? tab.fields.map((f: Field) => addTooltipToField(f, tooltips))
|
|
85
|
+
: tab.fields,
|
|
86
|
+
})),
|
|
87
|
+
} as Field
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return field
|
|
91
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export type ThemeOption = 'light' | 'dark' | 'brand-light' | 'brand-dark'
|
|
6
|
+
|
|
7
|
+
const STORAGE_KEY = 'orion-admin-theme'
|
|
8
|
+
const DEFAULT_THEME: ThemeOption = 'light'
|
|
9
|
+
|
|
10
|
+
function applyTheme(theme: ThemeOption) {
|
|
11
|
+
document.documentElement.setAttribute('data-theme', theme)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getCachedTheme(): ThemeOption | null {
|
|
15
|
+
try {
|
|
16
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
17
|
+
if (stored && ['light', 'dark', 'brand-light', 'brand-dark'].includes(stored)) {
|
|
18
|
+
return stored as ThemeOption
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
// localStorage not available
|
|
22
|
+
}
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function cacheTheme(theme: ThemeOption) {
|
|
27
|
+
try {
|
|
28
|
+
localStorage.setItem(STORAGE_KEY, theme)
|
|
29
|
+
} catch {
|
|
30
|
+
// localStorage not available
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function useTheme() {
|
|
35
|
+
const [theme, setThemeState] = useState<ThemeOption>(() => {
|
|
36
|
+
if (typeof window === 'undefined') return DEFAULT_THEME
|
|
37
|
+
return getCachedTheme() || DEFAULT_THEME
|
|
38
|
+
})
|
|
39
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
40
|
+
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
41
|
+
const userIdRef = useRef<string | null>(null)
|
|
42
|
+
|
|
43
|
+
// On mount: apply cached theme immediately, then sync from DB
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const cached = getCachedTheme()
|
|
46
|
+
if (cached) {
|
|
47
|
+
applyTheme(cached)
|
|
48
|
+
setThemeState(cached)
|
|
49
|
+
} else {
|
|
50
|
+
applyTheme(DEFAULT_THEME)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fetch user's preference from DB
|
|
54
|
+
fetch('/api/users/me', { credentials: 'include' })
|
|
55
|
+
.then((res) => res.json())
|
|
56
|
+
.then((data) => {
|
|
57
|
+
const user = data?.user || data
|
|
58
|
+
if (user?.id) {
|
|
59
|
+
userIdRef.current = user.id
|
|
60
|
+
}
|
|
61
|
+
if (user?.themePreference) {
|
|
62
|
+
const dbTheme = user.themePreference as ThemeOption
|
|
63
|
+
setThemeState(dbTheme)
|
|
64
|
+
applyTheme(dbTheme)
|
|
65
|
+
cacheTheme(dbTheme)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.catch(() => {
|
|
69
|
+
// Silently fail - use cached/default theme
|
|
70
|
+
})
|
|
71
|
+
.finally(() => {
|
|
72
|
+
setIsLoading(false)
|
|
73
|
+
})
|
|
74
|
+
}, [])
|
|
75
|
+
|
|
76
|
+
const setTheme = useCallback((newTheme: ThemeOption) => {
|
|
77
|
+
setThemeState(newTheme)
|
|
78
|
+
applyTheme(newTheme)
|
|
79
|
+
cacheTheme(newTheme)
|
|
80
|
+
|
|
81
|
+
// Debounced DB write
|
|
82
|
+
if (debounceRef.current) {
|
|
83
|
+
clearTimeout(debounceRef.current)
|
|
84
|
+
}
|
|
85
|
+
debounceRef.current = setTimeout(() => {
|
|
86
|
+
const userId = userIdRef.current
|
|
87
|
+
if (!userId) return
|
|
88
|
+
|
|
89
|
+
fetch(`/api/users/${userId}`, {
|
|
90
|
+
method: 'PATCH',
|
|
91
|
+
credentials: 'include',
|
|
92
|
+
headers: { 'Content-Type': 'application/json' },
|
|
93
|
+
body: JSON.stringify({ themePreference: newTheme }),
|
|
94
|
+
}).catch(() => {
|
|
95
|
+
// Silently fail - local state is already updated
|
|
96
|
+
})
|
|
97
|
+
}, 300)
|
|
98
|
+
}, [])
|
|
99
|
+
|
|
100
|
+
const isDark = theme === 'dark' || theme === 'brand-dark'
|
|
101
|
+
const isBrand = theme === 'brand-light' || theme === 'brand-dark'
|
|
102
|
+
|
|
103
|
+
const toggleDarkMode = useCallback(() => {
|
|
104
|
+
if (isBrand) {
|
|
105
|
+
setTheme(isDark ? 'brand-light' : 'brand-dark')
|
|
106
|
+
} else {
|
|
107
|
+
setTheme(isDark ? 'light' : 'dark')
|
|
108
|
+
}
|
|
109
|
+
}, [isDark, isBrand, setTheme])
|
|
110
|
+
|
|
111
|
+
const toggleBrandMode = useCallback(() => {
|
|
112
|
+
if (isBrand) {
|
|
113
|
+
setTheme(isDark ? 'dark' : 'light')
|
|
114
|
+
} else {
|
|
115
|
+
setTheme(isDark ? 'brand-dark' : 'brand-light')
|
|
116
|
+
}
|
|
117
|
+
}, [isDark, isBrand, setTheme])
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
theme,
|
|
121
|
+
setTheme,
|
|
122
|
+
isDark,
|
|
123
|
+
isBrand,
|
|
124
|
+
isLoading,
|
|
125
|
+
toggleDarkMode,
|
|
126
|
+
toggleBrandMode,
|
|
127
|
+
}
|
|
128
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
export { Logo } from './components/Logo'
|
|
3
|
+
export { Icon } from './components/Icon'
|
|
4
|
+
export { Dashboard } from './components/Dashboard'
|
|
5
|
+
export { ThemeSwitcher, ThemeProvider } from './components/ThemeSwitcher'
|
|
6
|
+
export { HelpTooltip } from './components/HelpTooltip'
|
|
7
|
+
export { StatusBadge } from './components/StatusBadge'
|
|
8
|
+
export { EmptyState } from './components/EmptyState'
|
|
9
|
+
export { BlockPicker } from './components/BlockPicker'
|
|
10
|
+
export { SectionTabs } from './components/SectionTabs'
|
|
11
|
+
export { WelcomeHeader } from './components/WelcomeHeader'
|
|
12
|
+
|
|
13
|
+
// Helpers
|
|
14
|
+
export { configureAdmin } from './helpers/configureAdmin'
|
|
15
|
+
export type { AdminConfig } from './helpers/configureAdmin'
|
|
16
|
+
export { withTooltips } from './helpers/withTooltips'
|
|
17
|
+
|
|
18
|
+
// Hooks
|
|
19
|
+
export { useTheme } from './hooks/useTheme'
|
|
20
|
+
export type { ThemeOption } from './hooks/useTheme'
|
|
21
|
+
|
|
22
|
+
// Fields
|
|
23
|
+
export { themePreferenceField } from './fields/themePreference'
|
|
24
|
+
|
|
25
|
+
// CSS styles are exported via package.json exports map
|
|
26
|
+
// Import in your payload.config.ts:
|
|
27
|
+
// css: require.resolve('@orion-studios/payload-admin-components/dist/admin.css')
|
package/src/styles/admin.css
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Override Payload's default CSS variables to match your brand
|
|
2
|
+
* Orion Studios Admin Theme System
|
|
5
3
|
*
|
|
4
|
+
* Import all theme files and overrides.
|
|
6
5
|
* Usage in payload.config.ts:
|
|
7
6
|
* export default buildConfig({
|
|
8
7
|
* admin: {
|
|
@@ -11,64 +10,76 @@
|
|
|
11
10
|
* })
|
|
12
11
|
*/
|
|
13
12
|
|
|
13
|
+
/* Default brand colors (overridden by configureAdmin) */
|
|
14
14
|
:root {
|
|
15
|
-
/* Brand colors - customize these */
|
|
16
15
|
--brand-primary: #3b82f6;
|
|
17
|
-
--brand-
|
|
18
|
-
--brand-primary-light: #60a5fa;
|
|
19
|
-
|
|
20
|
-
/* Override Payload theme colors */
|
|
21
|
-
--theme-bg: #ffffff;
|
|
22
|
-
--theme-elevation-50: #f9fafb;
|
|
23
|
-
--theme-elevation-100: #f3f4f6;
|
|
24
|
-
--theme-elevation-150: #e5e7eb;
|
|
25
|
-
--theme-elevation-200: #d1d5db;
|
|
26
|
-
--theme-elevation-300: #9ca3af;
|
|
27
|
-
--theme-elevation-400: #6b7280;
|
|
28
|
-
--theme-elevation-500: #4b5563;
|
|
29
|
-
--theme-elevation-600: #374151;
|
|
30
|
-
--theme-elevation-700: #1f2937;
|
|
31
|
-
--theme-elevation-800: #111827;
|
|
32
|
-
--theme-elevation-900: #030712;
|
|
33
|
-
|
|
34
|
-
/* Text colors */
|
|
35
|
-
--theme-text: #111827;
|
|
36
|
-
--theme-text-muted: #6b7280;
|
|
37
|
-
|
|
38
|
-
/* Success/warning/error colors */
|
|
39
|
-
--theme-success-50: #f0fdf4;
|
|
40
|
-
--theme-success-500: #22c55e;
|
|
41
|
-
--theme-success-600: #16a34a;
|
|
42
|
-
|
|
43
|
-
--theme-warning-50: #fffbeb;
|
|
44
|
-
--theme-warning-500: #f59e0b;
|
|
45
|
-
--theme-warning-600: #d97706;
|
|
46
|
-
|
|
47
|
-
--theme-error-50: #fef2f2;
|
|
48
|
-
--theme-error-500: #ef4444;
|
|
49
|
-
--theme-error-600: #dc2626;
|
|
50
|
-
|
|
51
|
-
/* Use brand color for primary actions */
|
|
52
|
-
--theme-input-border-color: var(--brand-primary);
|
|
16
|
+
--brand-secondary: #8b5cf6;
|
|
53
17
|
}
|
|
54
18
|
|
|
55
|
-
/*
|
|
56
|
-
|
|
57
|
-
.
|
|
58
|
-
|
|
59
|
-
|
|
19
|
+
/* Theme files */
|
|
20
|
+
@import './themes/light.css';
|
|
21
|
+
@import './themes/dark.css';
|
|
22
|
+
@import './themes/brand-light.css';
|
|
23
|
+
@import './themes/brand-dark.css';
|
|
60
24
|
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
background-color: var(--brand-primary-dark) !important;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/* Customize the sidebar */
|
|
67
|
-
.nav {
|
|
68
|
-
background-color: var(--theme-elevation-50);
|
|
69
|
-
}
|
|
25
|
+
/* Payload UI overrides */
|
|
26
|
+
@import './overrides.css';
|
|
70
27
|
|
|
71
|
-
/*
|
|
72
|
-
|
|
73
|
-
|
|
28
|
+
/* Default to light theme when no data-theme attribute is set */
|
|
29
|
+
:root:not([data-theme]) {
|
|
30
|
+
--admin-bg: #ffffff;
|
|
31
|
+
--admin-surface: #f9fafb;
|
|
32
|
+
--admin-surface-elevated: #ffffff;
|
|
33
|
+
--admin-border: #e5e7eb;
|
|
34
|
+
--admin-border-subtle: #f3f4f6;
|
|
35
|
+
--admin-text: #111827;
|
|
36
|
+
--admin-text-secondary: #4b5563;
|
|
37
|
+
--admin-text-muted: #9ca3af;
|
|
38
|
+
--admin-text-inverse: #ffffff;
|
|
39
|
+
--admin-accent: #3b82f6;
|
|
40
|
+
--admin-accent-hover: #2563eb;
|
|
41
|
+
--admin-accent-subtle: #eff6ff;
|
|
42
|
+
--admin-accent-secondary: #8b5cf6;
|
|
43
|
+
--admin-accent-secondary-hover: #7c3aed;
|
|
44
|
+
--admin-accent-secondary-subtle: #f5f3ff;
|
|
45
|
+
--admin-nav-bg: #f8fafc;
|
|
46
|
+
--admin-nav-text: #374151;
|
|
47
|
+
--admin-nav-text-active: #111827;
|
|
48
|
+
--admin-nav-item-hover: #f1f5f9;
|
|
49
|
+
--admin-nav-item-active: #e0e7ff;
|
|
50
|
+
--admin-nav-group-text: #6b7280;
|
|
51
|
+
--admin-nav-border: #e2e8f0;
|
|
52
|
+
--admin-input-bg: #ffffff;
|
|
53
|
+
--admin-input-border: #d1d5db;
|
|
54
|
+
--admin-input-border-focus: var(--admin-accent);
|
|
55
|
+
--admin-input-placeholder: #9ca3af;
|
|
56
|
+
--admin-card-bg: #ffffff;
|
|
57
|
+
--admin-card-border: #e5e7eb;
|
|
58
|
+
--admin-card-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
59
|
+
--admin-card-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04);
|
|
60
|
+
--admin-success: #16a34a;
|
|
61
|
+
--admin-success-bg: #f0fdf4;
|
|
62
|
+
--admin-warning: #d97706;
|
|
63
|
+
--admin-warning-bg: #fffbeb;
|
|
64
|
+
--admin-error: #dc2626;
|
|
65
|
+
--admin-error-bg: #fef2f2;
|
|
66
|
+
--admin-info: #0284c7;
|
|
67
|
+
--admin-info-bg: #f0f9ff;
|
|
68
|
+
--admin-tooltip-bg: #1f2937;
|
|
69
|
+
--admin-tooltip-text: #f9fafb;
|
|
70
|
+
--admin-overlay: rgba(0, 0, 0, 0.3);
|
|
71
|
+
--admin-scrollbar-track: #f1f5f9;
|
|
72
|
+
--admin-scrollbar-thumb: #cbd5e1;
|
|
73
|
+
--admin-scrollbar-thumb-hover: #94a3b8;
|
|
74
|
+
--admin-focus-ring: 0 0 0 2px #eff6ff, 0 0 0 4px #3b82f6;
|
|
75
|
+
--admin-radius-sm: 6px;
|
|
76
|
+
--admin-radius-md: 8px;
|
|
77
|
+
--admin-radius-lg: 12px;
|
|
78
|
+
--admin-radius-xl: 16px;
|
|
79
|
+
--admin-badge-draft-bg: #fef3c7;
|
|
80
|
+
--admin-badge-draft-text: #92400e;
|
|
81
|
+
--admin-badge-published-bg: #dcfce7;
|
|
82
|
+
--admin-badge-published-text: #166534;
|
|
83
|
+
--admin-badge-changed-bg: #dbeafe;
|
|
84
|
+
--admin-badge-changed-text: #1e40af;
|
|
74
85
|
}
|