Skip to content

Commit 2360c3d

Browse files
committed
feat(ui): add pill radius tier to surface
1 parent 93763ae commit 2360c3d

5 files changed

Lines changed: 35 additions & 11 deletions

File tree

‎app/src/ui/primitives/Surface.css‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,24 @@
3838
.ed-surface--surface { background: var(--ed-bg-surface); } /* 卡片 / 列 / 阅读面(= 基类,显式写出让四档对称) */
3939
.ed-surface--raised { background: var(--ed-bg-raised); box-shadow: var(--ed-shadow-1); } /* 弹层 / 菜单 / 浮窗 */
4040

41-
/* 四档圆角:token 真源是 `SCALE_TOKENS.radiusScale`(3/5/8/10),类名带 `r-` 前缀以免与
42-
同名的 level 档(如 `--surface`)撞在一起 */
41+
/* 五档圆角:前四档的 token 真源是 `SCALE_TOKENS.radiusScale`(3/5/8/10),类名带 `r-` 前缀以免与
42+
同名的 level 档(如 `--surface`)撞在一起。
43+
44+
⚠️ 第五档 `pill`(批 4 T17 · B17 第 2 条的药丸裁决)**不在 token 真源里** —— `SCALE_TOKENS.radiusScale`
45+
今日仍只有 4 档,而 `tokens.drift.test.ts` 钉着 `[3, 5, 8, 10]`、`app/scripts/gen-tokens.mjs` 是它的
46+
生成器 ⇒ 补这一档要动 token 层四处(生成器 / `tokens.gen.ts` / `tokens.css` / drift 测试),**不属
47+
本单元范围**。故本档今日走 `var()` 兜底(与 `Text.css` 的 `var(--ed-type-1-size, 25px)` 同范式):
48+
值可用、名字已占位;token 真源补齐是**批 5/6 的 token 层工作**(已登记为 follow-up)。
49+
50+
Why 加这一档而不是让调用点写行内 `style`:规格 §4.2 只给四档,而切片内 `borderRadius: 999` 实测
51+
**3 处**(≥ B6 阈值)⇒ 按 B17 回来改原语;在调用点用行内 `style` 补圆角被 ADR-033 §4 禁止。
52+
⚠️ 这与规格 §4.2 的「圆角 | 3 印章 · 5 控件与卡 · 8 面板 · 10 浮层」**不一致**(新增第五档)
53+
⇒ 规范回写属 **T18**,本单元不改规范文档。 */
4354
.ed-surface--r-stamp { border-radius: var(--ed-radius-stamp, 3px); }
4455
.ed-surface--r-control { border-radius: var(--ed-radius-control, 5px); }
4556
.ed-surface--r-panel { border-radius: var(--ed-radius-panel, 8px); }
4657
.ed-surface--r-overlay { border-radius: var(--ed-radius-overlay, 10px); }
58+
.ed-surface--r-pill { border-radius: var(--ed-radius-pill, 999px); }
4759

4860
.ed-surface--padded { padding: var(--ed-space-12, 12px); }
4961

‎app/src/ui/primitives/Surface.test.tsx‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type { SurfaceLevel, SurfaceRadius, SurfaceTag } from "./index";
2424

2525
/** 契约名单:与 `Surface.css` 的规则一一对应(改名单必须同时改 CSS 与测试) */
2626
const LEVELS: readonly SurfaceLevel[] = ["sunken", "canvas", "surface", "raised"];
27-
const RADIUS: readonly SurfaceRadius[] = ["stamp", "control", "panel", "overlay"];
27+
const RADIUS: readonly SurfaceRadius[] = ["stamp", "control", "panel", "overlay", "pill"];
2828
const TAGS: ReadonlyArray<readonly [SurfaceTag, string]> = [
2929
["div", "DIV"], ["section", "SECTION"], ["article", "ARTICLE"], ["aside", "ASIDE"], ["li", "LI"],
3030
];
@@ -60,21 +60,28 @@ describe("Surface 默认契约", () => {
6060
});
6161
});
6262

63-
describe("Surface 四档底 / 四档圆角映射到类", () => {
63+
describe("Surface 四档底 / 五档圆角映射到类", () => {
6464
it("4 档 level 各有对应类(sunken 输入槽 · canvas 纸 · surface 卡面 · raised 弹层)", () => {
6565
for (const level of LEVELS) {
6666
const cls = classesOf(render(<Surface level={level}>x</Surface>).container);
6767
expect(cls, `档位 ${level}`).toContain(`ed-surface--${level}`);
6868
}
6969
});
7070

71-
it("4 档 radius 各有对应类(3/5/8/10px 由 token 决定,原语只出档名)", () => {
71+
it("5 档 radius 各有对应类(3/5/8/10px 由 token 决定,原语只出档名)", () => {
7272
for (const radius of RADIUS) {
7373
const cls = classesOf(render(<Surface radius={radius}>x</Surface>).container);
7474
expect(cls, `圆角 ${radius}`).toContain(`ed-surface--r-${radius}`);
7575
}
7676
});
7777

78+
it("第五档 `pill`(批 4 T17 的药丸裁决):类名与档名一致,且**不**落内联 style(值由 CSS 的 var 兜底给)", () => {
79+
const { container } = render(<Surface radius="pill">x</Surface>);
80+
expect(classesOf(container)).toContain("ed-surface--r-pill");
81+
// 原语只出档名、值只在 CSS:这条守住「token 真源补 pill 之前也不许把 999px 内联进来」
82+
expect(root(container).style.borderRadius).toBe("");
83+
});
84+
7885
it("结构不变量:level 类与 radius 类各至多一个(不产生两档叠加态)", () => {
7986
const cls = classesOf(render(<Surface level="raised" radius="overlay">x</Surface>).container);
8087
expect(cls.filter((c) => LEVELS.some((l) => c === `ed-surface--${l}`))).toHaveLength(1);

‎app/src/ui/primitives/Surface.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import "./Surface.css";
3030
/** 四档底,与 `COLOR_TOKENS` 的 `bg-*` 四个颜色 token 一一对应(值只在 `ui/tokens.css`) */
3131
export type SurfaceLevel = "sunken" | "canvas" | "surface" | "raised";
3232

33-
/** 四档圆角,与 `SCALE_TOKENS.radiusScale` 同序(3 / 5 / 8 / 10px) */
34-
export type SurfaceRadius = "stamp" | "control" | "panel" | "overlay";
33+
/** 五档圆角:四档与 `SCALE_TOKENS.radiusScale` 同序(3 / 5 / 8 / 10px),第五档 `pill` 是批 4 T17 加的**药丸**形态
34+
* (`radiusScale` 里还没有它 ⇒ `Surface.css` 的该档走 `var(--ed-radius-pill, 999px)` 兜底;补 token 见彼处注释) */
35+
export type SurfaceRadius = "stamp" | "control" | "panel" | "overlay" | "pill";
3536

3637
/** 可渲染的语义标签(不含 `button` / `a` —— 可交互元素属 `Button` 与调用点) */
3738
export type SurfaceTag = "div" | "section" | "article" | "aside" | "li";

‎app/src/ui/primitives/style-contract.test.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ const RADIUS_CLASS: Record<SurfaceRadius, string> = {
110110
control: "ed-surface--r-control",
111111
panel: "ed-surface--r-panel",
112112
overlay: "ed-surface--r-overlay",
113+
// 批 4 T17(B17 第 2 条):切片内 `borderRadius: 999` 实测 3 处 ⇒ ≥3 ⇒ 加**药丸**档。
114+
// ⚠️ 这与规格 §4.2 的「四档圆角」不一致(新增第五档)⇒ 规范回写属 T18。
115+
pill: "ed-surface--r-pill",
113116
};
114117
const VARIANT_CLASS: Record<ButtonVariant, string> = {
115118
primary: "ed-btn--primary",
@@ -161,7 +164,7 @@ const CONTRACTS: readonly UnionContract[] = [
161164
{ union: "TextSize", css: "Text.css", members: SIZE_CLASS, expected: 6 },
162165
{ union: "TextFont", css: "Text.css", members: FONT_CLASS, expected: 3 },
163166
{ union: "SurfaceLevel", css: "Surface.css", members: LEVEL_CLASS, expected: 4 },
164-
{ union: "SurfaceRadius", css: "Surface.css", members: RADIUS_CLASS, expected: 4 },
167+
{ union: "SurfaceRadius", css: "Surface.css", members: RADIUS_CLASS, expected: 5 },
165168
{ union: "ButtonVariant", css: "Button.css", members: VARIANT_CLASS, expected: 3 },
166169
{ union: "ButtonSize", css: "Button.css", members: BTN_SIZE_CLASS, expected: 3 },
167170
{ union: "StatusKind", css: "StatusLine.css", members: STATUS_CLASS, expected: 4 },

‎app/src/ui/primitives/style-seams.test.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,12 @@ describe("Text 类名 ↔ CSS 规则一致(类名拼错 = 静默无样式)",
8585
});
8686
});
8787

88-
/** `Surface.tsx` 产出的 12 个类名(与 `Surface.test.tsx` 的名单同源) */
88+
/** `Surface.tsx` 产出的 13 个类名(与 `Surface.test.tsx` 的名单同源) */
8989
const SURFACE_CLASSES: readonly string[] = [
9090
"ed-surface",
9191
...["sunken", "canvas", "surface", "raised"].map((l) => `ed-surface--${l}`),
92-
...["stamp", "control", "panel", "overlay"].map((r) => `ed-surface--r-${r}`),
92+
// 批 4 T17:`pill` 是 B17 第 2 条加的药丸档(切片内 `borderRadius: 999` 实测 3 处 ⇒ ≥3)⇒ 12 → 13
93+
...["stamp", "control", "panel", "overlay", "pill"].map((r) => `ed-surface--r-${r}`),
9394
"ed-surface--bordered",
9495
"ed-surface--interactive",
9596
"ed-surface--padded",
@@ -114,7 +115,7 @@ describe("Surface 盒子契约(控制方 2026-09-11 裁决 · 类名 ↔ CSS
114115

115116
it("Surface.tsx 产出的每个类在 Surface.css 里都有规则;且零颜色字面量、无 z-index", () => {
116117
const clean = stripComments(SURFACE_CSS);
117-
expect(SURFACE_CLASSES).toHaveLength(12);
118+
expect(SURFACE_CLASSES).toHaveLength(13);
118119
for (const cls of SURFACE_CLASSES) {
119120
expect(clean, `Surface.css 缺少 .${cls} 的规则`).toContain(`.${cls} {`);
120121
}

0 commit comments

Comments
 (0)