liderbektas/payload-theme

GitHub: liderbektas/payload-theme

一款 Payload CMS 管理面板的 shadcn 风格主题插件,仅需两行代码即可实现包含仪表板、命令面板和深色模式的完整 UI 改造。

Stars: 2 | Forks: 0

# payload-theme **只需 2 行代码,让你的 Payload 管理面板看起来像价值 5 万美元的定制项目。** 输入一种强调色,输出完整的 shadcn 风格重新设计:带有迷你图的仪表板、⌘K 命令面板、分组图标侧边栏、分屏登录、实时主题定制器 —— 支持浅色*和*深色模式。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/liderbektas/payload-theme/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/payload-theme?color=0d9488)](https://www.npmjs.com/package/payload-theme) [![npm downloads](https://img.shields.io/npm/dm/payload-theme?color=0d9488)](https://www.npmjs.com/package/payload-theme) [![Payload 3](https://img.shields.io/badge/Payload-3.x-000000)](https://payloadcms.com) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/liderbektas/payload-theme/blob/main/LICENSE) [**快速开始**](#installation) · [**功能导览**](#the-tour) · [**尝试 Demo**](#-try-it-in-60-seconds) · [**配置选项**](#options)
payload-theme dashboard 同一个面板,只需加一个插件。上图中的每一个像素都包含在这个包中。
## 为什么选择它 Payload 是 Node 生态中最好的 headless CMS —— 但它的管理面板看起来就像一个数据库 UI。客户会注意到,编辑人员也会注意到。**payload-theme** 能将原装面板变成让人忍不住截图的漂亮界面,而无需 fork 任何组件: - 🎨 **一个强调色驱动一切** —— 11 级的 OKLCH 色阶会重新调整按钮、焦点环、导航、迷你图和登录光晕的颜色。内置自动 WCAG 对比度。 - 🧱 **无需 fork 组件,无需复杂配置** —— 只需一个插件入口和一次 CSS import。删除这两行代码即可恢复原样。 - 🌗 **精心设计的深色模式,而非简单反转** —— 每个表面都处于 zinc 色阶上;深色模式拥有自己重新映射的色阶。 - ⚡ **零运行时开销** —— 颜色计算在启动时运行一次,并作为 CSS 自定义属性输出。支持 SSR,无 FOUC。 ## 安装说明 ``` pnpm add payload-theme # 或者:npm i payload-theme / yarn add payload-theme ``` **第 1 行** —— 在 `payload.config.ts` 中添加该插件: ``` import { payloadTheme } from 'payload-theme' export default buildConfig({ plugins: [ payloadTheme({ accent: '#0d9488' }), ], }) ``` **第 2 行** —— 在 `src/app/(payload)/custom.scss` 中引入样式表(每个 `create-payload-app` 项目都已包含此文件): ``` @import 'payload-theme/styles.css'; ``` 然后重新生成 import map 并重启: ``` npx payload generate:importmap ``` 打开管理面板。迁移就这么简单。 🎉 ## 功能导览 ### 让人忍不住截图的登录界面 分体式卡片:一个始终为深色的品牌面板,其光晕由**你的强调色**绘制,包含你的 Logo 和文案(`login.heading` / `login.tagline`)—— 表单就在其侧边。 Login ### 真正像仪表板的仪表板 默认仪表板变成了一个小部件网格:每个 collection 对应一个带有动画计数和 **30 天创建数迷你图**的统计卡片,globals 也有对应的卡片,并且 —— 如果你愿意 —— 可以在下方放置**你自定义的 React 小部件**([文档](#dashboard-widgets))。通过 Payload 的 local API 进行服务端渲染:应用访问控制,无加载闪烁。 Dashboard ### 读起来像一款产品的侧边栏 顶部是你的 Logo,一个 ⌘K 搜索胶囊,**带有 lucide 图标的分组 collections**(`admin.group` + `nav.icons`),当前活动项上有强调色胶囊,以及固定在底部的 shadcn 风格**用户区块** —— 头像、姓名、电子邮件,以及一个包含 Account、区域设置切换器和退出的弹窗。当 collections 溢出时,只有菜单会滚动;Logo、搜索和用户区块保持不动。 ### ⌘K 命令面板 在任何地方按下 `⌘K` / `Ctrl+K`:跳转到任何 collection 或 global,**在输入时跨 collections 搜索文档**,切换浅色/深色模式或退出登录。内置于主题中 —— 零额外依赖。 Command palette ### 位于顶部的实时主题定制器 调色板按钮会打开一个面板,任何人都可以在运行时重新设置面板样式 —— 无需重新构建,无需重新部署: - **强调色 (Accent)** —— 10 个精选预设 + 一个自由输入的 hex 字段,通过相同的 OKLCH 引擎实时重新调整整个面板的颜色 - **圆角 (Radius)** —— 从 `'none'` 到 `'full'` 的完整刻度 - **颜色模式** —— 浅色/深色,存储在 Payload 的 Account 页面所使用的相同首选项中 - **内容布局** —— 居中(约 1280px)或全宽 所有设置都会保存在浏览器中;**重置为默认值** 可返回你的配置。 Theme customizer ### 看起来像页面大纲的 Block 结构化内容不再看起来像原生的 Payload。Block 和数组行渲染为**一个统一的列表** —— 带有细分隔线的条目,包含柔和的行号、**每种 block 类型对应的图标**、block 标题以及隐约的幽灵操作按钮。该主题为常见的 slug(`content`、`cta`、`hero`)提供了图标;任何项目 block 都可以通过一个 CSS 自定义属性来启用它: ``` .blocks-field__block-pill-gallery { --pt-block-ico: url("data:image/svg+xml,..."); /* any 24×24 stroke SVG */ } ``` 在任何地方添加行都是一致的操作 —— 一个全宽的虚线条,在悬停时会亮起强调色。Block 选择器抽屉显示 shadcn 风格的卡片。 Blocks list with per-type icons ### Edit 视图:真正的表单布局,而非字段堆砌 带有**双列字段网格**的单一内容卡片:紧凑字段成对出现(*标题 | Slug*),宽界面保留整行,所有内容在低于 1024px 时堆叠显示。输入框遵循 shadcn 语言(细边框、强调色焦点环),复选框渲染为开关,顶级组位于凸起的面板中,而粘性操作栏 —— 每个操作都有图标 —— 会对下方滚动的内容进行模糊处理。 Edit view Tabs、radio groups、JSON、代码编辑器、日期选择器、多选、relationship 字段 —— 全部应用了主题: Tabs and field types ### List 视图和真正的媒体库 表格变成了单行工具栏下方的简洁卡片 —— 搜索、列/过滤器胶囊以及一个实心的 **+ 新建** 位于同一行。状态和布尔值渲染为始终为圆形的中性徽章,空的 collections 拥有带插画的空状态。 List view Media grid ### 免费的深色模式 每个表面、徽章、卡片和光晕都是 token 驱动的。嵌套的表面在堆叠时会变得*更亮*(绝不会出现更暗的“凹陷”),凸起面板内的字段保持扁平,依靠其边框来体现层次,而强调色经过了重新映射,以确保在深色模式下依然鲜艳。 Dark edit view ## 🚀 60 秒内尝试 该仓库提供了一个**完整的 Demo 面板** —— 六个 collections、每种字段类型、由 block 构建的页面以及预置的媒体库: ``` git clone https://github.com/liderbektas/payload-theme cd payload-theme && pnpm install && pnpm build cp dev/.env.example dev/.env pnpm seed && pnpm dev ``` 打开 [http://localhost:3000/admin](http://localhost:3000/admin) 并登录: | 用户 | 密码 | 角色 | | --- | --- | --- | | `dev@local.test` | `test1234` | Admin | | `editor@local.test` | `test1234` | Editor | 体验一下顶部的主题定制器 —— 强调色、圆角、颜色模式和布局都会实时生效。 ## 选项 所有配置都是可选的。这是完整的配置项: ``` payloadTheme({ // The one color that drives everything: buttons, active nav pill, // focus rings, selected rows, sparklines, the login glow... Any hex works. accent: '#e30613', // Corner rounding for the WHOLE panel: 'none' | 'sm' | 'md' | 'lg' | 'full'. radius: 'md', // Your logo — top of the sidebar AND above the login form. // A URL, or { light, dark } to swap artwork per color scheme. logo: { light: '/logo.svg', dark: '/logo-dark.svg' }, // Rendered height of the logo: a number in px, or any CSS length. logoHeight: 28, // Small mark, used as a fallback for the login logo. icon: '/mark.svg', // Copy on the login brand panel. login: { heading: 'Welcome back', tagline: 'Sign in to manage your content.', }, // Sidebar + dashboard + palette icons per collection/global slug — // any icon name from lucide.dev. nav: { icons: { posts: 'newspaper', media: 'image', users: 'users', settings: 'settings', }, }, // Your own React components below the built-in dashboard content. dashboard: { widgets: [ '/components/widgets/StatisticsWidget#StatisticsWidget', { component: '/components/widgets/LastLoginWidget#LastLoginWidget', width: 'third' }, ], }, // Escape hatch: raw --pt-* token overrides, applied last. cssVariables: { '--pt-radius-card': '10px', }, }) ``` | 选项 | 类型 | 默认值 | 作用 | | --- | --- | --- | --- | | `accent` | `string` (hex) | `#4f4ece` | 生成完整的 50–950 OKLCH 色阶,并用它为每个交互元素上色。 | | `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'full'` | `'md'` | 全局圆角设置 —— 按钮、输入框、徽章、卡片、表格、弹窗和菜单项都遵循此设置。 | | `logo` | `string \| { light, dark }` | Payload logo | 显示在侧边栏顶部和登录表单上方的图片 URL。 | | `logoHeight` | `number \| string` | `26` | 渲染的 Logo 高度 —— 数字为 px,字符串为任何 CSS 长度。 | | `icon` | `string \| { light, dark }` | — | 小图标,用作登录 Logo 的后备。 | | `login.heading` | `string` | `'Welcome back'` | 登录品牌面板上的大标题。 | | `login.tagline` | `string` | `'Sign in to manage your content.'` | 标题下方的辅助说明。 | | `nav.icons` | `Record` | 文件夹图标 | 将 collections/globals 映射到 [lucide](https://lucide.dev) 图标 —— 侧边栏、仪表板卡片和命令面板。 | | `dashboard.widgets` | `DashboardWidget[]` | `[]` | 渲染在内置仪表板内容下方的自定义组件。 | | `cssVariables` | `Record` | — | 逃生舱:直接覆盖任何原始的 `--pt-*` token。 | ## Dashboard 小部件 内置仪表板始终保持原样渲染 —— 小部件是位于其下方的*附加*区域。使用 Payload 的标准 import-map 路径约定将每个条目指向一个 React 组件: ``` payloadTheme({ dashboard: { widgets: [ // string form — 'half' width by default '/components/widgets/StatisticsWidget#StatisticsWidget', // object form — 'full' | 'half' | 'third' { component: '/components/widgets/LastLoginWidget#LastLoginWidget', width: 'third' }, ], }, }) ``` **服务端组件** 接收实时的 Payload context 作为 props: ``` import type { DashboardWidgetServerProps } from 'payload-theme' export const StatisticsWidget: React.FC = async ({ payload, user }) => { const drafts = await payload.count({ collection: 'posts', overrideAccess: false, user, where: { _status: { equals: 'draft' } }, }) return
…{drafts.totalDocs}…
} ``` **客户端组件**(`'use client'`)不接收 props —— 请使用 Payload 的 hooks 或 REST API。该插件会在 `admin.dependencies` 中注册每个小部件,因此 `payload generate:importmap` 会自动拾取它们。 如果你希望自己的小部件与内置统计卡片保持一致,可以复用主题的卡片类(`pt-dash__card`、`pt-dash__card-head`、`pt-dash__card-label`、`pt-dash__card-body`、`pt-dash__card-count`、`pt-dash__card-caption`)。 ## 底层机制 - **处处体现同一强调色** —— 你的 hex 值会变成 11 级的 OKLCH 色阶;每个交互元素的颜色都会一致更新。 - **智能深色模式** —— 该色阶针对深色模式进行了重新映射(更亮的强调色级别,绝非简单的反转)。 - **自动对比度** —— 强调色上的文字会根据 WCAG 相对亮度自动选择黑色或白色。 - **开关而非复选框** —— 纯 CSS 实现;表单行为和可访问性保持不变。 - **零运行时颜色计算** —— 在启动时计算一次,并作为 CSS 自定义属性注入。无 FOUC,支持 SSR。 - **非破坏性** —— 所有内容都在 `@layer payload` 中发布,覆盖 Payload 的默认设置,而不会引发特异性冲突或使用 `!important`。 - **Zinc 基础** —— Payload 的中性色阶被重新定向到 shadcn 的 zinc 色阶,同时适用于浅色*和*深色。 - **通过 CSS 遮罩实现图标** —— 字形是 `currentColor` 遮罩;它们会根据各种状态、强调色和配色方案重新着色。 - **运行时重新设置样式** —— 定制器会使用与服务端相同的引擎在客户端重新计算强调色色阶。 ## 通过 CSS 变量进行微调 每个 token 都是一个普通的 CSS 自定义属性: ``` payloadTheme({ accent: '#0ea5e9', cssVariables: { '--pt-accent-subtle': 'oklch(0.95 0.03 240)', }, }) ``` 重要的包括:`--pt-accent-50` … `--pt-accent-950`、`--pt-accent`、`--pt-accent-hover`、`--pt-accent-active`、`--pt-accent-subtle`、`--pt-accent-contrast`、`--pt-accent-ring`,以及圆角 token `--pt-radius-ctl`、`--pt-radius-card`、`--pt-radius-item`,还有针对每个 block 的图标钩子 `--pt-block-ico`。 ## 环境要求 - Payload **3.x**(peer range `^3.0.0`;基于 **3.85** 进行开发和 e2e 测试) - Next.js **15+**,React **19** ## 故障排除 **"Component not found in import map"** —— 安装后运行 `npx payload generate:importmap`,然后重启 dev 服务器。 **样式未生效** —— 确保在 `src/app/(payload)/custom.scss` 中包含 `@import 'payload-theme/styles.css';`**主题更改未在 dev 中显示** —— Turbopack 缓存非常激进;请删除你应用的 `.next` 文件夹并重启。 **`Invalid accent color: '…'`** —— 强调色必须是像 `#7c3aed` 这样的 hex 字符串。 ## 许可证 MIT © [Lider Bektaş](https://github.com/liderbektas) ## 开发说明(此 monorepo) ``` packages/payload-theme the plugin (published to npm) dev a Payload 3 playground app that consumes it docs README screenshots ``` ``` pnpm install pnpm build # build the plugin pnpm seed # seed the playground with demo content pnpm dev # start the playground at http://localhost:3000/admin pnpm test # unit + integration tests pnpm lint # eslint across the repo pnpm --filter dev test:e2e # Playwright against the real admin panel pnpm --filter dev test:visual # screenshot regression suite ``` 完整指南请参阅 [CONTRIBUTING.md](https://github.com/liderbektas/payload-theme/blob/main/CONTRIBUTING.md),历史记录请参阅 [CHANGELOG.md](https://github.com/liderbektas/payload-theme/blob/main/CHANGELOG.md)。
标签:Payload CMS, shadcn, UI主题, 插件, 深色模式, 界面定制, 自动化攻击