@zmzai/theme 0.5.8 → 0.6.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/README.md CHANGED
@@ -9,7 +9,7 @@ zmzai 全品牌设计系统 — Radix UI + framer-motion + Tailwind v4。v0.2.0
9
9
  ```bash
10
10
  pnpm add @zmzai/theme
11
11
  # or via git:
12
- pnpm add @zmzai/theme@github:Ulanxx/zmzai-theme
12
+ pnpm add @zmzai/theme@github:zmzai-cloud/zmzai-theme
13
13
  ```
14
14
 
15
15
  ### Import tokens + fonts
@@ -0,0 +1,50 @@
1
+ # 设计约定(Design Conventions)
2
+
3
+ > 2026-08-27 起。整合:交互取择表(源自 hallmark microinteractions 纪律)、反 AI 味检查清单、hallmark skill 的使用边界。
4
+ > 与 `prefer-theme-components` 原则(UI 一律 @zmzai/theme 组件)配套执行。
5
+
6
+ ## 一、交互取择表(Tool-UI 场景)
7
+
8
+ 写交互时的默认选择——左边被否决时才允许用右边:
9
+
10
+ | 默认采用 ✅ | 而不是 ❌ | 适用 |
11
+ | --- | --- | --- |
12
+ | 静默成功(状态自然变化) | 庆祝型 toast / 弹窗 | 所有操作反馈 |
13
+ | 乐观更新 + Undo | 事前确认对话框 | 可逆操作(删除入回收站、重命名) |
14
+ | 事前确认 | 乐观更新 | **不可逆**操作(真删数据、对外发布)——Undo 救不了的必须问 |
15
+ | 就近内联错误(字段旁/卡片内) | 顶部全局 toast 报错 | 表单与工具卡 |
16
+ | hover 提示延迟 800ms / focus 0ms | 两者都 0ms | Tooltip(防扫过闪烁) |
17
+ | `prefers-reduced-motion: reduce` 全局降级 | 只做动画不管偏好 | 所有动效 |
18
+ | 只动 `transform` / `opacity` | 动 layout 属性(width/top/margin) | 所有动效 |
19
+ | `--ease-out`(入场)/ `--ease-in`(退场) | 浏览器默认 `ease`、bounce/overshoot | 所有过渡 |
20
+ | 动效时长 ≤200ms(状态) / ≤400ms(进出场) | 更长 | 工具 UI 节奏 |
21
+ | 裁剪:删掉不损失信息的动画就删 | 多多益善 | Review 时逐个问「删了会丢信息吗」 |
22
+
23
+ **harness/工作台类界面专项**:消息流自动跟随滚动但不锁底(用户上翻即停,近底再恢复);长任务用 terminal_read 式游标增量而非整段重渲染。
24
+
25
+ ## 二、反 AI 味检查清单(并入 design-review)
26
+
27
+ 每次 UI 批次过 review 时逐项检查,任一命中即退回:
28
+
29
+ 1. **斜体标题**——headings 一律 roman(`font-style: normal`);强调用字重/accent 色/下划线,不用 `<em>`。
30
+ 2. **编造数据**——页面上的任何数字(用户数、百分比、"10×")必须有真实来源;没有就放 `—` 占位并标注"数据待确认",或者干脆换结构。
31
+ 3. **手绘假 chrome**——不画假浏览器栏(URL pill + 红绿灯)、假手机框、假 IDE 窗口包 `<pre>`;要么用真实截图(hairline 边框的 `<figure>`),要么裸内容。
32
+ 4. **模板节奏**——同仓两页不许共享 hero→3 特性→CTA→footer 的同一韵律;工具型界面豁免(一致性优先)。
33
+ 5. **未经 token 的即兴取值**——CSS 里出现内联 hex/oklch/rgb 或绕过 token 块的 `font-family: "某字体"`,一律改为引用命名 token。
34
+ 6. **假占位图冒充成品**——stock 图必须标 placeholder,不得当作最终交付。
35
+
36
+ ## 三、hallmark skill 使用边界(拍板 2026-08-27)
37
+
38
+ | 场景 | 用法 | 约束 |
39
+ | --- | --- | --- |
40
+ | **叙事型新页面**(落地页、官网、产品介绍) | `hallmark redesign` / default Design flow 可用 | **token 只准指向 @zmzai/theme**;禁走 catalog 主题自选(会引入第二套 OKLCH + 字体配对,与 theme 冲突);macrostructure 轮换照常 |
41
+ | **工具型界面**(工作台、后台、harness) | 只用 audit / 反 AI 味清单 | 不进 Design flow(工具 UI 要一致性不要多样性) |
42
+ | **任何 UI 的质检** | `hallmark audit` | 输出进 design-review 流程,与可用性审查并列 |
43
+ | **参考站拆解** | `hallmark study` | 产出的 DNA 回写 theme 时走正常 token 变更(主应用 v0.6.0 荧光绿即 study 产物) |
44
+
45
+ **权威序**:`@zmzai/theme` tokens > 各产品 globals.css 内联 token > 页面级样式。hallmark 在任何环节都不得越过第一级。
46
+
47
+ ## 四、违反后果
48
+
49
+ - push main 前的约定检查(见 `prefer-theme-components` 记忆)把本文件第一、二节纳入检查范围;
50
+ - 新组件 PR 缺 8 态矩阵行(states-discipline.md)不予合并。
@@ -0,0 +1,36 @@
1
+ # 组件状态纪律(States Discipline)
2
+
3
+ > 来源:hallmark skill 的 8-state discipline,经 2026-08-27 评审吸收进 @zmzai/theme。
4
+ > 自 v0.6.1 起执行。适用范围:**所有交互组件**(theme 仓内 + 各产品仓自建控件)。
5
+
6
+ ## 验收标准(8 态清单)
7
+
8
+ 每个交互组件必须对以下状态有**明确的设计决定**——要么有样式,要么有书面豁免理由(如纯装饰元素)。缺态 = 验收不通过。
9
+
10
+ | 状态 | 载体 | 硬性要求 |
11
+ | --- | --- | --- |
12
+ | default | — | 基线样式 |
13
+ | hover | `:hover` | 必须有可感知反馈(边框/背景/阴影任一),纯 `cursor` 不算 |
14
+ | focus-visible | `:focus-visible` | **≥3:1 对比度的 ring,立即可见、禁止动画**。用 `ring-2 ring-ink ring-offset-2 ring-offset-bg`;绝不能用 `:focus` 代替(避免鼠标点击也出 ring) |
15
+ | active | `:active` | 按压反馈统一 `active:scale-[0.98]`(按钮族);不可缩的用背景加深 |
16
+ | disabled | `disabled:` | `pointer-events-none opacity-40`(按钮)/ `cursor-not-allowed bg-surface text-ink-3`(输入族) |
17
+ | loading | `data-state` / 组件内状态 | 由 `StatefulButton` 承担(spinner + 防重复点击) |
18
+ | error | `aria-invalid` | 输入族:边框转 `border-danger`;按钮族:错误不落在按钮上,落在就近的错误文案区 |
19
+ | success | `data-state` | 静默优先——成功不必庆祝,需要确认感的操作用 `StatefulButton` 的 checkmark 过渡(1.5s 自复位) |
20
+
21
+ ## 现状矩阵(v0.6.1 基线)
22
+
23
+ | 组件 | default | hover | focus-visible | active | disabled | loading | error | success |
24
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
25
+ | Button | ✅ | ✅ | ✅ 0.6.1 补 | ✅ 0.6.1 补齐 secondary/ghost | ✅ | via StatefulButton | 豁免(就近文案) | via StatefulButton |
26
+ | Input / Textarea | ✅ | ✅ 0.6.1 补 | ✅ 0.6.1 补 | 豁免(输入不按压) | ✅ 0.6.1 补 | 豁免 | ✅ 0.6.1 补(aria-invalid) | 豁免 |
27
+ | SelectTrigger | ✅ | ✅ | ✅ 0.6.1 补 | 豁免 | ✅ 0.6.1 补 | 豁免 | ✅ 0.6.1 补(aria-invalid) | 豁免 |
28
+ | StatefulButton | ✅ | ✅ | ✅(继承 Button) | ✅ | ✅ | ✅ | ⚠️ **待补**:当前 catch 静默回 idle | ✅ |
29
+
30
+ **待办**:StatefulButton 的 error 呈现(失败态停留 + 重试入口,不自动复位)——下个版本。
31
+
32
+ ## 新组件自查流程
33
+
34
+ 1. 写完组件后对照矩阵逐格打勾,打不了勾的写豁免理由进组件头注释;
35
+ 2. playground 里过一遍键盘 Tab 流(只看 focus-visible 是否全程可见);
36
+ 3. `aria-invalid` / `data-state` 用真实表单校验驱动,不要手动塞 class。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zmzai/theme",
3
- "version": "0.5.8",
3
+ "version": "0.6.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "zmzai 全品牌设计系统 — Radix UI + framer-motion + Tailwind v4 + MiSans",
@@ -56,6 +56,7 @@
56
56
  },
57
57
  "files": [
58
58
  "src",
59
+ "docs",
59
60
  "README.md",
60
61
  "LICENSE"
61
62
  ],
@@ -4,22 +4,33 @@ import { cva } from "class-variance-authority";
4
4
  * Button variant styles.
5
5
  *
6
6
  * Design:
7
- * - 锐角 2px(v0.2 暖纸风),hover 轻微缩放
7
+ * - 锐角 2px(v0.2 暖纸风沿用的锐利语言),hover 阴影 / active 微缩
8
8
  * - primary: black fill, white text, hover scale + shadow
9
9
  * - secondary: transparent, border, hover fill
10
10
  * - ghost: no border, subtle hover bg
11
+ * - danger: red fill
12
+ *
13
+ * States discipline (v0.6.1 起,见 docs/states-discipline.md):
14
+ * - focus-visible: 2px ink ring + offset,立即可见不做动画(键盘可达性硬性要求)
15
+ * - active: 所有可按压 variant 统一 scale 反馈(此前仅 primary/danger 有)
16
+ * - disabled: opacity-40 + pointer-events-none
17
+ * - loading/error/success 语义态由 StatefulButton 承担(error 呈现待补)
11
18
  */
12
19
  export const buttonVariants = cva(
13
- "inline-flex cursor-pointer items-center justify-center gap-2 rounded-sm font-semibold transition-colors transition-transform disabled:pointer-events-none disabled:opacity-40 select-none",
20
+ [
21
+ "inline-flex cursor-pointer items-center justify-center gap-2 rounded-sm font-semibold transition-colors transition-transform select-none",
22
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
23
+ "disabled:pointer-events-none disabled:opacity-40",
24
+ ].join(" "),
14
25
  {
15
26
  variants: {
16
27
  variant: {
17
28
  primary:
18
29
  "bg-ink text-white hover:shadow-md active:scale-[0.98]",
19
30
  secondary:
20
- "border border-line text-ink hover:border-ink hover:bg-surface-2",
31
+ "border border-line text-ink hover:border-ink hover:bg-surface-2 active:scale-[0.98]",
21
32
  ghost:
22
- "text-ink-2 hover:bg-surface-2 hover:text-ink",
33
+ "text-ink-2 hover:bg-surface-2 hover:text-ink active:scale-[0.98]",
23
34
  danger:
24
35
  "bg-danger text-white hover:shadow-md active:scale-[0.98]",
25
36
  },
@@ -4,23 +4,31 @@ import { cva } from "class-variance-authority";
4
4
  * Input variant styles.
5
5
  *
6
6
  * Design:
7
- * - default: light border, focus turns border to ink
8
- * - brutal: 2px ink border + hard offset shadow (neobrutalism), focus shifts
7
+ * - default: light border, hover 提示可交互,focus turns border to ink
8
+ * - brutal: 2px ink border + hard offset shadow (neobrutalism)
9
+ *
10
+ * States discipline (v0.6.1 起,见 docs/states-discipline.md):
11
+ * - disabled: 灰化 + not-allowed 光标
12
+ * - aria-invalid: 边框转 danger(与 Radix/shadcn 惯例一致,业务层无需传额外 prop)
9
13
  */
10
14
  export const inputVariants = cva(
11
- "w-full bg-bg font-sans text-ink placeholder:text-ink-3 outline-none transition-all",
15
+ [
16
+ "w-full bg-bg font-sans text-ink placeholder:text-ink-3 outline-none transition-all",
17
+ "focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-bg disabled:cursor-not-allowed disabled:bg-surface disabled:text-ink-3",
18
+ "aria-invalid:border-danger aria-invalid:focus:border-danger",
19
+ ].join(" "),
12
20
  {
13
21
  variants: {
14
22
  variant: {
15
23
  default:
16
- "border border-line rounded-sm px-4 py-2.5 text-sm focus:border-ink",
24
+ "h-10 border border-line rounded-sm px-4 text-sm hover:border-ink/40 focus:border-ink focus-visible:ring-ink",
17
25
  brutal:
18
- "border-2 border-ink rounded-sm px-4 py-3 text-sm",
26
+ "h-10 border-2 border-ink rounded-sm px-4 text-sm focus-visible:ring-line-strong",
19
27
  },
20
28
  size: {
21
- sm: "text-xs py-2 px-3",
22
- md: "text-sm py-2.5 px-4",
23
- lg: "text-base py-3 px-5",
29
+ sm: "h-8 text-xs px-3",
30
+ md: "h-10 text-sm px-4",
31
+ lg: "h-12 text-base px-5",
24
32
  },
25
33
  },
26
34
  defaultVariants: {
@@ -18,8 +18,10 @@ export function SelectTrigger({
18
18
  return (
19
19
  <SelectPrimitive.Trigger
20
20
  className={cn(
21
- "inline-flex h-9 items-center justify-between gap-2 rounded-sm border border-line bg-bg px-3 py-2 text-sm font-medium text-ink outline-none transition-colors",
21
+ "inline-flex h-10 items-center justify-between gap-2 rounded-sm border border-line bg-bg px-4 text-sm font-medium text-ink outline-none transition-colors",
22
22
  "hover:border-ink/40 focus:border-ink data-[placeholder]:text-ink-3",
23
+ "disabled:cursor-not-allowed disabled:bg-surface disabled:text-ink-3",
24
+ "aria-invalid:border-danger focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-bg focus-visible:ring-ink",
23
25
  className
24
26
  )}
25
27
  >
@@ -6,32 +6,33 @@
6
6
  * @import "@zmzai/theme/tokens";
7
7
  * @import "@zmzai/theme/fonts";
8
8
  *
9
- * v0.2.0 起为暖色杂志风(Warm paper, warm ink, seal-red accent),
10
- * 与 zmzai.cloud 主应用(Hallmark Study)同源。v0.1.x 是黑白
11
- * Monochrome 风,已废弃。
9
+ * v0.6.0 起为纯白 + 荧光绿(Pure white + fluorescent green),
10
+ * 与 zmzai.cloud 主应用(Hallmark Study)同源。
11
+ * v0.2.0~0.5.x 为暖色杂志风(Warm paper + seal-red,已弃用)。
12
+ * v0.1.x 黑白 Monochrome 风,更早废弃。
12
13
  */
13
14
 
14
15
  @theme {
15
16
  /* ===== Color =====
16
- 暖纸张底 + 暖墨字 + 印章红 accent(Hallmark Study 锁定)。
17
- accent 用于强调/链接/审批等,red 系有印章感。 */
18
- --color-bg: oklch(0.95 0.012 85);
19
- --color-surface: oklch(0.97 0.008 85);
20
- --color-surface-2: oklch(0.92 0.014 80);
21
- --color-ink: oklch(0.18 0.011 40);
22
- --color-ink-2: oklch(0.47 0.013 55);
17
+ 纯白底 + 荧光绿 accent(与 zmzai.cloud 主应用同源,Hallmark Study 锁定)。
18
+ accent 为荧光绿(oklch 0.78 0.220 142);accent-ink 为深色(用于绿底上的字)。 */
19
+ --color-bg: oklch(0.995 0.002 90);
20
+ --color-surface: oklch(0.98 0.004 90);
21
+ --color-surface-2: oklch(0.95 0.006 88);
22
+ --color-ink: oklch(0.15 0.010 30);
23
+ --color-ink-2: oklch(0.50 0.012 55);
23
24
  --color-ink-3: oklch(0.55 0.012 55);
24
- --color-line: oklch(0.86 0.012 80);
25
- --color-line-strong: oklch(0.18 0.011 40);
26
- --color-accent: oklch(0.46 0.150 27);
27
- --color-accent-ink: oklch(0.97 0.008 85);
28
- --color-success: oklch(0.52 0.090 160);
29
- --color-danger: oklch(0.42 0.155 27);
30
- --color-warning: oklch(0.65 0.10 80);
25
+ --color-line: oklch(0.90 0.005 88);
26
+ --color-line-strong: oklch(0.15 0.010 30);
27
+ --color-accent: oklch(0.78 0.220 142);
28
+ --color-accent-ink: oklch(0.15 0.010 30);
29
+ --color-success: oklch(0.72 0.180 145);
30
+ --color-danger: oklch(0.55 0.200 25);
31
+ --color-warning: oklch(0.80 0.150 90);
31
32
 
32
33
  /* Dark surface — 仅用于代码块/preview,不是全局 dark mode */
33
34
  --color-dark-bg: #0A0A0A;
34
- --color-dark-surface: #171717;
35
+ --color-dark-surface: #141414;
35
36
  --color-dark-ink: #FAFAFA;
36
37
  --color-dark-line: #27272A;
37
38
 
@@ -62,7 +63,7 @@
62
63
  --color-muted: var(--color-ink-2);
63
64
  --color-rule: var(--color-ink);
64
65
  --color-surface-strong: var(--color-surface-2);
65
- --color-accent-strong: oklch(0.39 0.150 27);
66
+ --color-accent-strong: oklch(0.68 0.220 142);
66
67
  --animate-dur-fast: 150ms;
67
68
  --animate-dur-base: 250ms;
68
69
  --animate-dur-slow: 500ms;
@@ -100,8 +101,8 @@
100
101
  }
101
102
 
102
103
  ::selection {
103
- background: var(--color-ink);
104
- color: #fff;
104
+ background: var(--color-accent);
105
+ color: var(--color-accent-ink);
105
106
  }
106
107
 
107
108
  :focus-visible {