@mar7th/march7th-theme
v0.1.1
Published
Framework-agnostic March7th design tokens, theme controller, and Web Component.
Maintainers
Readme
@mar7th/march7th-theme
March7th 的框架无关主题包。它提供动态 OKLCH 设计 token、浅色/深色/系统主题控制器、首屏防闪烁脚本以及一个可直接用于 Astro、Vue、React、Svelte 和原生页面的 Web Component。
- 样式层零运行时;可选的状态色生成器使用 Culori 做色域映射和 WCAG 对比度计算。
- 主色相和辅助色相可以独立调整。
- 成功、信息、警告、错误可分别调整色相、色度和明度,并生成 50–950 色阶。
- 支持全局主题和局部主题作用域。
- 保留现有 March7th UI Vue 与 shadcn 语义变量。
- 可选兼容通用的
--primary-50、--bg-primary等变量。 - 支持持久化、系统主题变化、跨标签页同步、强制色和减少动效模式。
安装
pnpm add @mar7th/march7th-themeCSS 入口
| 入口 | 内容 |
| --- | --- |
| tokens.css | 仅带 --m7-* 前缀的基础和语义 token |
| theme.css | token + March7th 旧变量 + shadcn 变量,推荐默认使用 |
| full.css | theme.css + 通用变量 + selection/accent 基础样式 |
| base.css | 可选的 selection 和表单 accent 样式 |
| compat/march7th.css | 现有 March7th UI Vue 变量映射 |
| compat/shadcn.css | --background、--primary、--border 等映射 |
| compat/generic.css | --primary-50、--bg-primary 等无前缀映射 |
这些样式不包含 CSS reset,也不会修改 body 布局。
shadcn 兼容层把 --destructive-foreground 作为可直接显示在页面上的危险文本色。实色危险背景上的高对比度文字应使用 --m7-color-on-danger。
import "@mar7th/march7th-theme/theme.css"控制主题
import {
initializeTheme,
setThemeHue,
setThemeMode,
setThemeToken,
} from "@mar7th/march7th-theme"
initializeTheme({ defaultMode: "system", defaultHue: 215 })
setThemeMode("dark")
setThemeHue(285)
setThemeToken("--m7-secondary-hue", "185")默认使用以下持久化键:
m7-theme:modem7-theme:hue
控制器会维护根节点的 dark class、data-m7-theme、data-m7-resolved-theme、data-m7-hue 和 --m7-hue。
监听状态变化:
import { getThemeController } from "@mar7th/march7th-theme"
const unsubscribe = getThemeController().subscribe((state, detail) => {
console.log(state.mode, state.resolvedMode, state.hue, detail.source)
})
unsubscribe()DOM 上同时会触发 march7th-theme-ready 和 march7th-theme-change。事件的 detail 包含完整主题状态。
Web Component
导入 elements 会安全注册 <march7th-theme-control>:
import "@mar7th/march7th-theme/elements"<march7th-theme-control locale="zh-CN"></march7th-theme-control>它使用 Shadow DOM,不依赖任何框架,可以同时修改 light/dark/system 模式和 --m7-hue。
<march7th-theme-control
mode="system"
hue="215"
default-hue="215"
min="0"
max="360"
step="5"
locale="zh-CN"
></march7th-theme-control>可用属性:
| 属性 | 默认值 | 说明 |
| --- | --- | --- |
| mode | 持久化值或 system | 初始主题模式 |
| hue | 持久化值或 default-hue | 显式初始主色相 |
| default-hue | 215 | 重置目标 |
| min / max | 0 / 360 | 色相选择范围 |
| step | 5 | 色相滑块步长 |
| locale | 文档语言或 en | zh-CN 与英文标签 |
| hide-mode | - | 只显示色相选择器 |
| hide-hue | - | 只显示模式切换器 |
| hue-label | 本地化默认值 | 自定义色相标签 |
组件会发送冒泡且可穿过 Shadow DOM 的 change 事件。也可以直接调用 setMode()、setHue() 和 resetHue()。
状态色色板
状态色适合开放品牌级配置,但不建议让业务代码逐个覆盖色阶。包内把 success、info、warning、danger 作为四个独立角色;error 是 danger 的兼容别名。每个角色只暴露三个输入轴,由生成器统一产生 50(最浅)到 950(最深)的 11 档色阶。
import {
generateStatusPalette,
initializeStatusPalettes,
setStatusColor,
} from "@mar7th/march7th-theme"
initializeStatusPalettes({ gamut: "rgb" })
setStatusColor("success", { hue: 145, chroma: 0.17, lightness: 0.62 })
setStatusColor("error", { hue: 350 }) // 等价于 danger
const preview = generateStatusPalette("warning", {
hue: 82,
chroma: 0.18,
lightness: 0.72,
gamut: "p3",
})
console.log(preview.scale[500], preview.contrast, preview.warnings)生成器会把超出目标 rgb 或 p3 色域的颜色压缩回可显示范围,并检查浅色/深色下的实色按钮文字及状态面板文字是否达到默认 4.5:1。控制器默认把三轴配置保存到 m7-theme:status-palettes,并发送 march7th-status-colors-ready 与 march7th-status-colors-change。
需要可视化编辑时,导入同一个 elements 入口即可使用独立高级组件:
<march7th-status-colors locale="zh-CN"></march7th-status-colors>它提供四个角色的色相、色度、明度滑块、11 档预览、AA 状态和分项重置。可用属性如下:
| 属性 | 默认值 | 说明 |
| --- | --- | --- |
| locale | 文档语言或 en | zh-CN 与英文标签 |
| gamut | rgb | 可设为 p3;应在组件初始化前指定 |
| storage-key | m7-theme:status-palettes | 自定义持久化键 |
| no-persist | - | 禁用持久化 |
组件会发送冒泡且可穿过 Shadow DOM 的 change 事件,并提供 reset()、exportCss() 与 exportJson() 方法。只需要 CSS 定制时,也可以直接覆盖 --m7-success-hue、--m7-success-chroma、--m7-success-lightness 等三轴变量;现代浏览器会用内置 color-mix() 色阶作为无 JavaScript 回退。
Astro
在全局 Layout 引入主题,在浏览器脚本中注册 Web Component:
---
import "@mar7th/march7th-theme/theme.css"
import { createThemeInitScript } from "@mar7th/march7th-theme"
const themeInitScript = createThemeInitScript({ defaultHue: 215 })
---
<script is:inline set:html={themeInitScript} />
<script>
import "@mar7th/march7th-theme/elements"
</script>
<march7th-theme-control locale="zh-CN" />
<march7th-status-colors locale="zh-CN" />
<slot />Web Component 不需要 Astro 的 client:* 指令。Vue、React 或 Svelte island 会继承同一个根节点 token;因此 Astro 页面与其中的 Vue island 会自动保持状态色和主题一致。
首屏防闪烁
SSR/SSG 项目可以把初始化脚本放在主题 CSS 之后、页面内容之前:
import { createThemeInitScript } from "@mar7th/march7th-theme"
const inlineScript = createThemeInitScript({
defaultMode: "system",
defaultHue: 215,
})该脚本只读取主题的 localStorage 键并同步根节点,不加载包运行时。启用严格 CSP 时应为内联脚本配置 nonce 或 hash。
局部主题
import { createThemeController } from "@mar7th/march7th-theme"
const panelTheme = createThemeController({
target: () => document.querySelector<HTMLElement>("#preview-panel"),
defaultMode: "dark",
defaultHue: 305,
persist: false,
})
panelTheme.initialize()tokens.css 会识别局部元素上的 data-m7-theme、data-m7-resolved-theme 和 dark class。
Token 结构
--m7-hue / --m7-secondary-hue 主题轴
--m7-success|info|warning|danger-* 状态色相、色度、明度轴
--m7-color-primary-50 ... 950 主色色阶
--m7-color-secondary-50 ... 950 辅助色色阶
--m7-color-neutral-50 ... 950 中性色阶
--m7-color-success|info|warning|danger-50 ... 950
--m7-color-bg-* / --m7-color-fg-* 背景和文字语义
--m7-color-action-* / --m7-color-border-* 操作和边框语义
--m7-color-*-bg|border|fg / --m7-color-on-* 状态语义
--m7-color-chart-1 ... 5 图表色
--m7-radius-* / --m7-shadow-* 形状和阴影
--m7-duration-* / --m7-ease-* 动效JavaScript 中可以使用带类型的 token 引用,避免手写变量名:
import { setThemeToken, themeTokens } from "@mar7th/march7th-theme"
setThemeToken(themeTokens.radius.md, "0.75rem")
setThemeToken(themeTokens.axes.secondaryHue, "185")
setThemeToken(themeTokens.axes.successChroma, "0.17")
console.log(themeTokens.palette.success[600])与现有 Vue 组件库共用
当前包保留了 March7th UI Vue 使用的全部主题变量以及 shadcn 变量。迁移时在组件库样式之后引入本包,或后续让组件库直接依赖本包即可;本仓库不会修改 Vue 组件源码。
import "@mar7th/march7th-ui-vue/style.css"
import "@mar7th/march7th-theme/theme.css"开发
pnpm install
pnpm dev
pnpm check
pnpm test
pnpm build
pnpm pack:check本地演示地址为 http://127.0.0.1:5173/demo/。
