agentcss 0.0.0-stage → 0.11.2
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/01-tokens/README.md +236 -0
- package/01-tokens/behavior/dismissable.js +132 -0
- package/01-tokens/behavior/emit.js +76 -0
- package/01-tokens/behavior/focus-return.js +81 -0
- package/01-tokens/behavior/roving.js +152 -0
- package/01-tokens/behavior/typeahead.js +124 -0
- package/01-tokens/focus-ring-demo.html +133 -0
- package/01-tokens/focus-ring.css +102 -0
- package/01-tokens/page.css +244 -0
- package/01-tokens/polyfill.js +116 -0
- package/01-tokens/responsive-demo.html +171 -0
- package/01-tokens/scrollbar-demo.html +194 -0
- package/01-tokens/scrollbar.css +179 -0
- package/01-tokens/theme-toggle.js +103 -0
- package/01-tokens/tokens.css +972 -0
- package/01-tokens/typography.css +156 -0
- package/02-primitives/badge/README.md +71 -0
- package/02-primitives/badge/badge.css +141 -0
- package/02-primitives/badge/demo.html +101 -0
- package/02-primitives/button/README.md +99 -0
- package/02-primitives/button/button.css +353 -0
- package/02-primitives/button/demo.html +200 -0
- package/02-primitives/card/README.md +145 -0
- package/02-primitives/card/card.css +246 -0
- package/02-primitives/card/demo.html +170 -0
- package/02-primitives/choice/README.md +161 -0
- package/02-primitives/choice/choice.css +395 -0
- package/02-primitives/choice/demo.html +277 -0
- package/02-primitives/combobox/README.md +79 -0
- package/02-primitives/combobox/combobox.css +223 -0
- package/02-primitives/combobox/combobox.js +485 -0
- package/02-primitives/combobox/demo.html +109 -0
- package/02-primitives/date-range/README.md +100 -0
- package/02-primitives/date-range/date-range.css +206 -0
- package/02-primitives/date-range/date-range.js +301 -0
- package/02-primitives/date-range/demo.html +104 -0
- package/02-primitives/input/README.md +170 -0
- package/02-primitives/input/demo.html +246 -0
- package/02-primitives/input/input.css +239 -0
- package/02-primitives/popover/README.md +80 -0
- package/02-primitives/popover/demo.html +150 -0
- package/02-primitives/popover/popover.css +187 -0
- package/02-primitives/popover/popover.js +331 -0
- package/02-primitives/progress/README.md +82 -0
- package/02-primitives/progress/demo.html +154 -0
- package/02-primitives/progress/progress.css +172 -0
- package/02-primitives/select/README.md +95 -0
- package/02-primitives/select/demo.html +121 -0
- package/02-primitives/select/select.css +151 -0
- package/02-primitives/select/select.js +549 -0
- package/02-primitives/separator/README.md +63 -0
- package/02-primitives/separator/demo.html +84 -0
- package/02-primitives/separator/separator.css +73 -0
- package/02-primitives/skeleton/README.md +69 -0
- package/02-primitives/skeleton/demo.html +110 -0
- package/02-primitives/skeleton/skeleton.css +96 -0
- package/02-primitives/switch/README.md +65 -0
- package/02-primitives/switch/demo.html +115 -0
- package/02-primitives/switch/switch.css +370 -0
- package/03-patterns/accordion/README.md +121 -0
- package/03-patterns/accordion/accordion.css +453 -0
- package/03-patterns/accordion/accordion.js +369 -0
- package/03-patterns/accordion/demo.html +223 -0
- package/03-patterns/content/README.md +103 -0
- package/03-patterns/content/content.css +286 -0
- package/03-patterns/content/demo.html +88 -0
- package/03-patterns/drawer/README.md +79 -0
- package/03-patterns/drawer/demo.html +101 -0
- package/03-patterns/drawer/drawer.css +129 -0
- package/03-patterns/drawer/drawer.js +258 -0
- package/03-patterns/dropdown/README.md +78 -0
- package/03-patterns/dropdown/demo.html +101 -0
- package/03-patterns/dropdown/dropdown.css +135 -0
- package/03-patterns/dropdown/dropdown.js +451 -0
- package/03-patterns/form-validation/README.md +147 -0
- package/03-patterns/form-validation/demo.html +280 -0
- package/03-patterns/form-validation/form-validation.css +263 -0
- package/03-patterns/list/README.md +150 -0
- package/03-patterns/list/demo.html +225 -0
- package/03-patterns/list/flip.js +201 -0
- package/03-patterns/list/list.css +162 -0
- package/03-patterns/nav/README.md +225 -0
- package/03-patterns/nav/demo.html +131 -0
- package/03-patterns/nav/nav.css +314 -0
- package/03-patterns/nav/nav.js +430 -0
- package/03-patterns/nav/toc.js +111 -0
- package/03-patterns/overlay/README.md +196 -0
- package/03-patterns/overlay/demo.html +241 -0
- package/03-patterns/overlay/overlay.css +319 -0
- package/03-patterns/overlay/overlay.js +563 -0
- package/03-patterns/pagination/README.md +114 -0
- package/03-patterns/pagination/demo.html +165 -0
- package/03-patterns/pagination/pagination.css +195 -0
- package/03-patterns/pagination/pagination.js +395 -0
- package/03-patterns/reveal/README.md +91 -0
- package/03-patterns/reveal/demo.html +106 -0
- package/03-patterns/reveal/reveal.css +93 -0
- package/03-patterns/reveal/reveal.js +172 -0
- package/03-patterns/states/README.md +141 -0
- package/03-patterns/states/demo.html +240 -0
- package/03-patterns/states/states.css +252 -0
- package/03-patterns/tabs/README.md +84 -0
- package/03-patterns/tabs/demo.html +139 -0
- package/03-patterns/tabs/tabs.css +172 -0
- package/03-patterns/tabs/tabs.js +307 -0
- package/03-patterns/tooltip/README.md +96 -0
- package/03-patterns/tooltip/demo.html +78 -0
- package/03-patterns/tooltip/tooltip.css +89 -0
- package/03-patterns/tooltip/tooltip.js +288 -0
- package/03-patterns/tree/README.md +94 -0
- package/03-patterns/tree/demo.html +138 -0
- package/03-patterns/tree/tree.css +113 -0
- package/03-patterns/tree/tree.js +456 -0
- package/04-recipes/analysis-report/README.md +80 -0
- package/04-recipes/analysis-report/analysis-report.css +187 -0
- package/04-recipes/analysis-report/demo.html +274 -0
- package/04-recipes/data-showcase/README.md +45 -0
- package/04-recipes/data-showcase/demo.html +247 -0
- package/04-recipes/detail-page/README.md +93 -0
- package/04-recipes/detail-page/demo.html +55 -0
- package/04-recipes/detail-page/detail-page.css +190 -0
- package/04-recipes/detail-page/full.html +60 -0
- package/04-recipes/detail-page/null.html +60 -0
- package/04-recipes/detail-page/partial.html +60 -0
- package/04-recipes/detail-page/verify-skeleton.py +19 -0
- package/04-recipes/longform/README.md +93 -0
- package/04-recipes/longform/agc-memory.svg +4 -0
- package/04-recipes/longform/content.html +610 -0
- package/04-recipes/longform/content.md +51 -0
- package/04-recipes/longform/longform.html +627 -0
- package/04-recipes/report/README.md +176 -0
- package/04-recipes/report/demo.html +355 -0
- package/04-recipes/report/report.css +364 -0
- package/04-recipes/table/README.md +103 -0
- package/04-recipes/table/demo.html +455 -0
- package/04-recipes/table/table.css +372 -0
- package/04-recipes/tierA-tokens.css +189 -0
- package/09-assets/README.md +74 -0
- package/09-assets/bar/README.md +94 -0
- package/09-assets/bar/bar.css +166 -0
- package/09-assets/bar/bar.js +320 -0
- package/09-assets/bar/demo.html +176 -0
- package/09-assets/echarts-adapter/demo.html +192 -0
- package/09-assets/echarts-adapter/echarts-adapter.js +277 -0
- package/09-assets/model-viewer/README.md +155 -0
- package/09-assets/model-viewer/demo.html +105 -0
- package/09-assets/model-viewer/model-viewer.css +143 -0
- package/09-assets/model-viewer/model-viewer.js +423 -0
- package/09-assets/scientific-plot/README.md +94 -0
- package/09-assets/scientific-plot/demo.html +252 -0
- package/09-assets/scientific-plot/scientific-plot.css +141 -0
- package/09-assets/scientific-plot/scientific-plot.js +610 -0
- package/09-assets/sparkline/README.md +153 -0
- package/09-assets/sparkline/demo.html +163 -0
- package/09-assets/sparkline/sparkline.css +158 -0
- package/09-assets/sparkline/sparkline.js +508 -0
- package/API.md +332 -0
- package/CHANGELOG.md +3116 -0
- package/CODE_OF_CONDUCT.md +64 -0
- package/CONTRIBUTING.md +89 -0
- package/ERRORS.md +1658 -0
- package/LICENSE +21 -0
- package/README.md +491 -2
- package/SECURITY.md +72 -0
- package/START-HERE.md +164 -0
- package/SUPPORT.md +109 -0
- package/adapters/presentation/README.md +113 -0
- package/adapters/presentation/check.js +101 -0
- package/adapters/presentation/deck.schema.json +74 -0
- package/adapters/presentation/to-doc.js +138 -0
- package/ai/START-HERE.md +104 -0
- package/ai/behaviors.json +380 -0
- package/ai/behaviors.schema.json +61 -0
- package/ai/capabilities.json +56 -0
- package/ai/cli.js +387 -0
- package/ai/components.json +1944 -0
- package/ai/components.meta.json +168 -0
- package/ai/components.schema.json +220 -0
- package/ai/contract.schema.json +147 -0
- package/ai/deprecations.json +55 -0
- package/ai/deprecations.schema.json +85 -0
- package/ai/motion-policy.json +22 -0
- package/ai/patch.js +323 -0
- package/ai/patch.schema.json +63 -0
- package/ai/profiles.json +92 -0
- package/ai/tokens.audience.json +55 -0
- package/ai/tokens.audience.schema.json +73 -0
- package/ai/tokens.json +762 -0
- package/ai/tokens.schema.json +58 -0
- package/ai/tokens.tree.json +752 -0
- package/ai/tokens.tree.schema.json +56 -0
- package/ai/validate.js +383 -0
- package/dist/01-tokens/behavior/dismissable.js +60 -0
- package/dist/01-tokens/behavior/emit.js +21 -0
- package/dist/01-tokens/behavior/focus-return.js +31 -0
- package/dist/01-tokens/behavior/roving.js +77 -0
- package/dist/01-tokens/behavior/typeahead.js +61 -0
- package/dist/01-tokens/focus-ring.css +1 -0
- package/dist/01-tokens/page.css +1 -0
- package/dist/01-tokens/polyfill.js +78 -0
- package/dist/01-tokens/scrollbar.css +1 -0
- package/dist/01-tokens/theme-toggle.js +61 -0
- package/dist/01-tokens/tokens.css +1 -0
- package/dist/01-tokens/typography.css +1 -0
- package/dist/02-primitives/badge/badge.css +1 -0
- package/dist/02-primitives/button/button.css +1 -0
- package/dist/02-primitives/card/card.css +1 -0
- package/dist/02-primitives/choice/choice.css +1 -0
- package/dist/02-primitives/combobox/combobox.css +1 -0
- package/dist/02-primitives/combobox/combobox.js +439 -0
- package/dist/02-primitives/date-range/date-range.css +1 -0
- package/dist/02-primitives/date-range/date-range.js +267 -0
- package/dist/02-primitives/input/input.css +1 -0
- package/dist/02-primitives/popover/popover.css +1 -0
- package/dist/02-primitives/popover/popover.js +257 -0
- package/dist/02-primitives/progress/progress.css +1 -0
- package/dist/02-primitives/select/select.css +1 -0
- package/dist/02-primitives/select/select.js +449 -0
- package/dist/02-primitives/separator/separator.css +1 -0
- package/dist/02-primitives/skeleton/skeleton.css +1 -0
- package/dist/02-primitives/switch/switch.css +1 -0
- package/dist/03-patterns/accordion/accordion.css +1 -0
- package/dist/03-patterns/accordion/accordion.js +219 -0
- package/dist/03-patterns/content/content.css +1 -0
- package/dist/03-patterns/drawer/drawer.css +1 -0
- package/dist/03-patterns/drawer/drawer.js +229 -0
- package/dist/03-patterns/dropdown/dropdown.css +1 -0
- package/dist/03-patterns/dropdown/dropdown.js +365 -0
- package/dist/03-patterns/form-validation/form-validation.css +1 -0
- package/dist/03-patterns/list/flip.js +117 -0
- package/dist/03-patterns/list/list.css +1 -0
- package/dist/03-patterns/nav/nav.css +1 -0
- package/dist/03-patterns/nav/nav.js +322 -0
- package/dist/03-patterns/nav/toc.js +76 -0
- package/dist/03-patterns/overlay/overlay.css +1 -0
- package/dist/03-patterns/overlay/overlay.js +398 -0
- package/dist/03-patterns/pagination/pagination.css +1 -0
- package/dist/03-patterns/pagination/pagination.js +273 -0
- package/dist/03-patterns/reveal/reveal.css +1 -0
- package/dist/03-patterns/reveal/reveal.js +138 -0
- package/dist/03-patterns/states/states.css +1 -0
- package/dist/03-patterns/tabs/tabs.css +1 -0
- package/dist/03-patterns/tabs/tabs.js +242 -0
- package/dist/03-patterns/tooltip/tooltip.css +1 -0
- package/dist/03-patterns/tooltip/tooltip.js +213 -0
- package/dist/03-patterns/tree/tree.css +1 -0
- package/dist/03-patterns/tree/tree.js +383 -0
- package/dist/04-recipes/analysis-report/analysis-report.css +1 -0
- package/dist/04-recipes/detail-page/detail-page.css +1 -0
- package/dist/04-recipes/report/report.css +1 -0
- package/dist/04-recipes/table/table.css +1 -0
- package/dist/04-recipes/tierA-tokens.css +1 -0
- package/dist/09-assets/bar/bar.css +1 -0
- package/dist/09-assets/bar/bar.js +222 -0
- package/dist/09-assets/echarts-adapter/echarts-adapter.js +185 -0
- package/dist/09-assets/model-viewer/model-viewer.css +1 -0
- package/dist/09-assets/model-viewer/model-viewer.js +320 -0
- package/dist/09-assets/scientific-plot/scientific-plot.css +1 -0
- package/dist/09-assets/scientific-plot/scientific-plot.js +568 -0
- package/dist/09-assets/sparkline/sparkline.css +1 -0
- package/dist/09-assets/sparkline/sparkline.js +265 -0
- package/dist/COSTS.md +56 -0
- package/docs/AI-CONTRACT.md +197 -0
- package/docs/BACKLOG.md +127 -0
- package/docs/BENCHMARK.md +252 -0
- package/docs/GUIDANCE.md +154 -0
- package/docs/INVARIANT.md +372 -0
- package/docs/LAYER-BASELINE.md +158 -0
- package/docs/RELEASING.md +275 -0
- package/docs/RFC-THEME-SCOPE.md +141 -0
- package/docs/ROADMAP.md +182 -0
- package/index.html +82 -0
- package/package.json +107 -4
- package/types/index.d.ts +310 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# content —— 内容渲染样式
|
|
2
|
+
|
|
3
|
+
| 字段 | 内容 |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **来源** | 依据 CommonMark 0.31.2 与 GFM 扩展,**不发明任何结构** |
|
|
6
|
+
| **实测** | 由 `05-audit/md-render.py` 渲染 `04-recipes/longform/content.md` 验证 |
|
|
7
|
+
| **适用** | 精简档 / B / C,纯 CSS |
|
|
8
|
+
| **依赖** | `01-tokens/tokens.css` + `01-tokens/page.css`(页面骨架:`.wrap` / `.lede` / `.note`) + `01-tokens/typography.css` |
|
|
9
|
+
| **规范** | 见本文件「内容渲染约定」一节|
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 一句话原则
|
|
14
|
+
|
|
15
|
+
> **结构用标准,样式用 token。**
|
|
16
|
+
|
|
17
|
+
元素和类名由 CommonMark / GFM 决定;颜色、字号、间距由 token 决定。
|
|
18
|
+
**两者之间不需要发明任何东西。**
|
|
19
|
+
|
|
20
|
+
所以 `content.css` 按**元素选择器**接管,**不给每个元素加类**:
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
.prose > h2 { ... }
|
|
24
|
+
.prose > pre > code { ... }
|
|
25
|
+
.prose > table { ... }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
结果:**从任何 CommonMark 兼容渲染器拿到的 HTML,不需要任何后处理就能正确显示。**
|
|
29
|
+
|
|
30
|
+
唯一的自定义类名是 `.prose`——它不改变语义,只是"这块是正文"的标记。
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 三层能力,不能混为一谈
|
|
35
|
+
|
|
36
|
+
| 层 | 表格 | 删除线 | 任务列表 |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| **CommonMark** | ❌ 不支持 | ❌ | ❌ |
|
|
39
|
+
| **GFM**(生态事实标准)| ✅ | ✅ | ✅ |
|
|
40
|
+
| 本库自定义 | 证据等级标记(`<sup class="evi">`)|
|
|
41
|
+
|
|
42
|
+
🔴 **纯 CommonMark 项目里表格会直接失效**(渲染成 `<p>`)。
|
|
43
|
+
要表格就必须用 GFM,这是硬依赖,不能假装有。
|
|
44
|
+
|
|
45
|
+
本库的 CSS **两层都支持**,用不用由渲染器决定——**样式不挑食**。
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🔴 两个实测踩到的坑
|
|
50
|
+
|
|
51
|
+
### 1. 不同渲染器的输出**不完全一致**
|
|
52
|
+
|
|
53
|
+
| 语法 | GitHub/GFM | markdown-it 实测 |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `~~删除线~~` | `<del>` | **`<s>`** |
|
|
56
|
+
|
|
57
|
+
两者都合法但语义不同(`<s>`=不再准确,`<del>`=已删除),
|
|
58
|
+
所以 CSS **必须同时支持**,否则换渲染器就掉样式。
|
|
59
|
+
|
|
60
|
+
> 这证明了一件事:**不存在"唯一正确的输出",只有"规范允许的输出范围"。**
|
|
61
|
+
> CSS 要覆盖那个范围,而不是假设某一种。
|
|
62
|
+
|
|
63
|
+
### 2. `data:` URI 被渲染器拒绝 —— **不要"解决"它**
|
|
64
|
+
|
|
65
|
+
想把图片内联进单文件(精简档 硬要求),自然会想到 data URI。
|
|
66
|
+
但 markdown-it 的默认 `validateLink` 把 `data:` 列入黑名单(防 XSS),
|
|
67
|
+
于是 `` **原样输出成文本**。
|
|
68
|
+
|
|
69
|
+
🔴 **关掉 `validateLink` 就是降安全性**,正是本规范反对的"自己发挥"。
|
|
70
|
+
|
|
71
|
+
替代方案:图片用真实相对路径(`04-recipes/longform/agc-memory.svg` 就是例子),
|
|
72
|
+
或在构建阶段把图片转 base64 注入 HTML(渲染后处理,不在 Markdown 里写 data URI)。
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 怎么用
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Markdown → 自包含单文件 HTML(精简档)
|
|
80
|
+
python 05-audit/md-render.py 内容.md -o out.html --title "标题"
|
|
81
|
+
|
|
82
|
+
# 只跑检查,不输出
|
|
83
|
+
python 05-audit/md-render.py 内容.md --check
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`md-render.py` 做三件事:
|
|
87
|
+
1. 用 markdown-it 渲染(commonmark preset + table + strikethrough)
|
|
88
|
+
2. 检查标题层级 / `img` alt / 表格语义
|
|
89
|
+
3. 内联 `tokens.css` + `content.css`,输出精简档 单文件
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 反模式
|
|
94
|
+
|
|
95
|
+
- ❌ **给每个元素加类**(`.prose h2`、`.prose p2`…)—— 渲染器输出没有这些类
|
|
96
|
+
- ❌ **`<div class="code-block">` 装代码** —— 用 `<pre><code class="language-x">`
|
|
97
|
+
- 🔴 **`<div class="table">` 模拟表格** —— **最严重**:辅助技术会当普通 div 念,
|
|
98
|
+
屏幕阅读器用户拿不到行列关系。必须用 `<table>`
|
|
99
|
+
- ❌ **`<div class="heading">` 代替标题** —— 文档大纲全废,"跳到标题"失效
|
|
100
|
+
- ❌ **缩进代码块硬加 `class="language-"`** —— CommonMark 规定它没有 class
|
|
101
|
+
- ❌ **`<b>` / `<i>` 代替 `<strong>` / `<em>`** —— `<b>` 只是视觉加粗,无语义
|
|
102
|
+
- ❌ **hr 不清 `height` 和 `border-top`** —— 默认有 inset 边框,会出双线
|
|
103
|
+
- ❌ **不排除代码块内的 `<code>`** —— 代码块每行都会带上行内代码的边框和底色
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
frontend-lib / 03-patterns / content / content.css
|
|
3
|
+
内容渲染样式 —— 接管 CommonMark / GFM 的标准输出
|
|
4
|
+
|
|
5
|
+
依赖:01-tokens/tokens.css
|
|
6
|
+
版本:A ✅ / B ✅ / C ✅
|
|
7
|
+
|
|
8
|
+
🔴 本文件最重要的设计决定:**不发明任何类名**。
|
|
9
|
+
|
|
10
|
+
结构由渲染器(CommonMark / GFM 规范)决定,
|
|
11
|
+
样式由本文件按**元素选择器**接管。
|
|
12
|
+
所以从任何 CommonMark 兼容渲染器拿到的 HTML,
|
|
13
|
+
**不需要任何后处理**就能正确显示。
|
|
14
|
+
|
|
15
|
+
只有一个自定义类名:`.prose` —— 用于限定作用域。
|
|
16
|
+
它不改变任何语义,只是"这块是正文"的标记。
|
|
17
|
+
|
|
18
|
+
依据:本文件顶部列出的渲染约定
|
|
19
|
+
========================================================================= */
|
|
20
|
+
|
|
21
|
+
.prose {
|
|
22
|
+
/* 正文行长:走语言感知令牌。
|
|
23
|
+
🔴 0.8.9 修(外部审计 P0-1b 发现):原来这里硬写 `max-width: 68ch`,
|
|
24
|
+
而 68ch 是 tokens.css 里**拉丁语系**那一档的值
|
|
25
|
+
(中文下 --measure-prose 是 34em,拉丁下才是 68ch)
|
|
26
|
+
⇒ 中文读者拿到的是拉丁的行宽,正文一行太长,扫视吃力。
|
|
27
|
+
⇒ measure-gate 把扫描范围从 *.html 扩到 *.css 之后
|
|
28
|
+
才第一次看见这处硬写。 */
|
|
29
|
+
max-width: var(--measure-prose);
|
|
30
|
+
color: var(--text-primary);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ---------------------------------------------------------------- 元素间距
|
|
34
|
+
用兄弟选择器统一控制,不给每个元素写 margin。
|
|
35
|
+
这是"接管"的正确姿势 —— 渲染器不排版,排版归我们。 */
|
|
36
|
+
.prose > * + * { margin-top: var(--sp-4); }
|
|
37
|
+
|
|
38
|
+
/* ---------------------------------------------------------------- 标题
|
|
39
|
+
结构必须是 <h1>-<h6>,不能是 <div class="heading">。
|
|
40
|
+
层级不跳级由 a11y.py 门禁检查。 */
|
|
41
|
+
.prose > h1,
|
|
42
|
+
.prose > h2,
|
|
43
|
+
.prose > h3,
|
|
44
|
+
.prose > h4,
|
|
45
|
+
.prose > h5,
|
|
46
|
+
.prose > h6 {
|
|
47
|
+
font-family: var(--sans);
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
line-height: var(--lh-tight);
|
|
50
|
+
letter-spacing: -0.008em;
|
|
51
|
+
margin: var(--sp-7) 0 0;
|
|
52
|
+
}
|
|
53
|
+
.prose > h1 { font-size: var(--fs-2xl); margin-top: 0; }
|
|
54
|
+
.prose > h2 {
|
|
55
|
+
font-size: var(--fs-lg);
|
|
56
|
+
padding-top: var(--sp-5);
|
|
57
|
+
border-top: 2px solid var(--accent);
|
|
58
|
+
/* 短线段 + 通栏细线:既有分隔又不像卡片那样"框住" */
|
|
59
|
+
max-width: 92%;
|
|
60
|
+
}
|
|
61
|
+
.prose > h3 { font-size: var(--fs-md); }
|
|
62
|
+
.prose > h4, .prose > h5, .prose > h6 {
|
|
63
|
+
font-size: var(--fs-base);
|
|
64
|
+
color: var(--text-secondary);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ---------------------------------------------------------------- 段落与行内 */
|
|
68
|
+
.prose > p { margin: 0; line-height: 1.75; }
|
|
69
|
+
|
|
70
|
+
/* 行内代码:衬线底 + 细边
|
|
71
|
+
⚠️ 中文正文里的 `code` 通常是函数名/参数名,用等宽更清楚。 */
|
|
72
|
+
.prose > code,
|
|
73
|
+
.prose > li > code,
|
|
74
|
+
.prose > td > code,
|
|
75
|
+
.prose > th > code {
|
|
76
|
+
font-family: var(--mono);
|
|
77
|
+
font-size: 0.88em;
|
|
78
|
+
background: var(--surface-sunken);
|
|
79
|
+
border: 1px solid var(--border-decor);
|
|
80
|
+
border-radius: 3px;
|
|
81
|
+
padding: 0.05em 0.3em;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* 🔴 代码块内的 <code> 必须排除行内样式。
|
|
85
|
+
CommonMark 两种代码块都输出 <pre><code>,不排除的话
|
|
86
|
+
代码块里的每行都会带上行内代码的边框和底色。 */
|
|
87
|
+
.prose > pre > code,
|
|
88
|
+
.prose > pre {
|
|
89
|
+
font-family: inherit;
|
|
90
|
+
font-size: inherit;
|
|
91
|
+
background: none;
|
|
92
|
+
border: none;
|
|
93
|
+
padding: 0;
|
|
94
|
+
border-radius: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ---------------------------------------------------------------- 列表
|
|
98
|
+
CommonMark 的嵌套列表是 <li> 内直接嵌 <ul>,靠缩进表达层级。 */
|
|
99
|
+
.prose > ul,
|
|
100
|
+
.prose > ol { padding-inline-start: 1.5em; margin: 0; }
|
|
101
|
+
.prose > ul { list-style: disc; }
|
|
102
|
+
.prose > ol { list-style: decimal; }
|
|
103
|
+
.prose li + li { margin-top: var(--sp-2); }
|
|
104
|
+
.prose li > ul,
|
|
105
|
+
.prose li > ol { margin-top: var(--sp-2); }
|
|
106
|
+
.prose li > ul { list-style: circle; } /* 二级用空心圆,三级会自动变方块 */
|
|
107
|
+
|
|
108
|
+
/* ---------------------------------------------------------------- 引用
|
|
109
|
+
CommonMark 输出 <blockquote><p>…</p></blockquote> */
|
|
110
|
+
/* 🔴 为什么这里**保留**竖条(0.8.4 · 全库竖条政策)
|
|
111
|
+
本库的整体立场是「去掉纯装饰的竖条」(见 typography.css / form-validation.css
|
|
112
|
+
各自的删除记录):竖条在页面上拉出一条与内容无关的硬边。
|
|
113
|
+
⇒ 但**引语与代码块是例外**,因为它们的竖条是**内容属性**、不是装饰:
|
|
114
|
+
· 引语 = "这是别人说的话" → 竖条是引用边界的视觉标记,
|
|
115
|
+
与学术排版的惯例一致,读者已经建立这个预期
|
|
116
|
+
· 代码块 = "这是原文"→ 同理,与 Markdown/Pandoc 的渲染结果一致
|
|
117
|
+
去掉它们,用户反而要靠缩进去猜"这段是引的还是正文"。*/
|
|
118
|
+
.prose > blockquote {
|
|
119
|
+
margin: var(--sp-5) 0;
|
|
120
|
+
padding: 0 0 0 var(--sp-4);
|
|
121
|
+
border-inline-start: 3px solid var(--accent);
|
|
122
|
+
}
|
|
123
|
+
.prose > blockquote > p {
|
|
124
|
+
color: var(--text-secondary);
|
|
125
|
+
font-size: var(--fs-md);
|
|
126
|
+
line-height: 1.6;
|
|
127
|
+
}
|
|
128
|
+
/* 引用里再套引用 */
|
|
129
|
+
.prose > blockquote > blockquote { margin: var(--sp-3) 0; }
|
|
130
|
+
|
|
131
|
+
/* ---------------------------------------------------------------- 代码块
|
|
132
|
+
两种都输出 <pre><code>;围栏的带 class="language-xxx",缩进的不带。
|
|
133
|
+
🔴 不给缩进代码块硬加 class —— 规范说它没有。 */
|
|
134
|
+
/* 🔴 同上:代码块保留竖条的理由见上方 .prose > blockquote 的注释
|
|
135
|
+
(竖条在这里表示"这是原文",属内容标记而非装饰)。*/
|
|
136
|
+
.prose > pre {
|
|
137
|
+
margin: 0;
|
|
138
|
+
padding: var(--sp-4);
|
|
139
|
+
background: var(--surface-sunken);
|
|
140
|
+
border-inline-start: 3px solid var(--border-decor-str);
|
|
141
|
+
border-radius: 0 var(--r-sm) var(--r-sm) 0;
|
|
142
|
+
overflow-x: auto;
|
|
143
|
+
font-family: var(--mono);
|
|
144
|
+
font-size: var(--fs-xs);
|
|
145
|
+
line-height: 1.6;
|
|
146
|
+
tab-size: 4;
|
|
147
|
+
-webkit-overflow-scrolling: touch;
|
|
148
|
+
}
|
|
149
|
+
/* 缩进代码块没有左边框语言标记,给个更淡的,暗示"这是次要代码" */
|
|
150
|
+
/* 🔴 逻辑属性:与上面 .prose > pre 的 border-inline-start 同侧。 */
|
|
151
|
+
.prose > pre:not([class]) { border-inline-start-color: var(--border-decor); }
|
|
152
|
+
|
|
153
|
+
/* ---------------------------------------------------------------- 表格(GFM)
|
|
154
|
+
🔴 必须是 <table>,不能用 div 模拟 —— div 会让屏幕阅读器读不出行列关系。 */
|
|
155
|
+
.prose > table {
|
|
156
|
+
width: 100%;
|
|
157
|
+
border-collapse: collapse;
|
|
158
|
+
table-layout: auto; /* ⚠️ 有 <colgroup> 时改用 fixed */
|
|
159
|
+
font-size: var(--fs-sm);
|
|
160
|
+
}
|
|
161
|
+
.prose > table th,
|
|
162
|
+
.prose > table td {
|
|
163
|
+
text-align: start;
|
|
164
|
+
padding: var(--sp-2) var(--sp-4) var(--sp-2) 0;
|
|
165
|
+
border-bottom: 1px solid var(--border-decor);
|
|
166
|
+
vertical-align: top;
|
|
167
|
+
}
|
|
168
|
+
.prose > table th {
|
|
169
|
+
font-weight: 500;
|
|
170
|
+
font-size: var(--fs-xs);
|
|
171
|
+
color: var(--text-secondary);
|
|
172
|
+
}
|
|
173
|
+
.prose > table tbody tr:last-child td { border-bottom: 1px solid var(--border-decor-str); }
|
|
174
|
+
|
|
175
|
+
/* ---------------------------------------------------------------- 其他元素 */
|
|
176
|
+
.prose > hr {
|
|
177
|
+
margin: var(--sp-7) 0;
|
|
178
|
+
border: 0;
|
|
179
|
+
border-top: 1px solid var(--border-decor);
|
|
180
|
+
height: 0; /* 🔴 hr 默认有 inset 边框,必须清掉 */
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.prose > img {
|
|
184
|
+
max-width: 100%;
|
|
185
|
+
height: auto;
|
|
186
|
+
display: block;
|
|
187
|
+
/* 🔴 每个 img 必须有显式宽高(CLS)——
|
|
188
|
+
这条由 viewport.py 门禁检查,本 CSS 不兜底 */
|
|
189
|
+
border-radius: var(--r-sm);
|
|
190
|
+
}
|
|
191
|
+
/* 🔴 必须是**后代**选择器,不是 `> a`:正文链接几乎都在 `<p>` 里,
|
|
192
|
+
`> a` 只覆盖直接子链接 ⇒ 段落里的链接落到浏览器默认蓝,
|
|
193
|
+
暗色下对比度 1.91:1(`dark-contrast` 实测抓到)。 */
|
|
194
|
+
.prose a {
|
|
195
|
+
color: var(--accent);
|
|
196
|
+
text-decoration: underline;
|
|
197
|
+
text-underline-offset: 2px;
|
|
198
|
+
text-decoration-thickness: 1px;
|
|
199
|
+
}
|
|
200
|
+
@media (hover: hover) and (pointer: fine) {
|
|
201
|
+
.prose a:hover { color: var(--text-primary); }
|
|
202
|
+
}
|
|
203
|
+
.prose a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
204
|
+
|
|
205
|
+
/* 外链可访问性:新窗口打开必须告知 */
|
|
206
|
+
.prose > a[target="_blank"]::after {
|
|
207
|
+
content: " ↗";
|
|
208
|
+
font-size: 0.8em;
|
|
209
|
+
vertical-align: 0.1em;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* GFM 删除线
|
|
213
|
+
🔴 实测:markdown-it 输出 `<s>`,GitHub/GFM 规范规定 `<del>`。
|
|
214
|
+
两者都是合法 HTML(`<s>`=不再准确,`<del>`=已删除)但语义不同。
|
|
215
|
+
⇒ 样式必须同时支持,否则换渲染器就掉样式。
|
|
216
|
+
查证所用渲染器:markdown-it-py(commonmark preset + strikethrough)。 */
|
|
217
|
+
.prose > del,
|
|
218
|
+
.prose > s,
|
|
219
|
+
.prose > li > del,
|
|
220
|
+
.prose > li > s { color: var(--text-tertiary); }
|
|
221
|
+
|
|
222
|
+
/* GFM 任务列表
|
|
223
|
+
CommonMark 核心不产出这个,GFM 扩展才有:
|
|
224
|
+
<li class="task-list-item"><input type="checkbox" disabled> … */
|
|
225
|
+
.prose .task-list-item { list-style: none; margin-inline-start: -1.5em; }
|
|
226
|
+
.prose .task-list-item input[type="checkbox"] {
|
|
227
|
+
margin-inline-end: var(--sp-2);
|
|
228
|
+
vertical-align: middle;
|
|
229
|
+
}
|
|
230
|
+
.prose .task-list-item input[disabled] {
|
|
231
|
+
/* disabled 的复选框不可改,但必须仍能看出状态 —— 不用 opacity */
|
|
232
|
+
accent-color: var(--accent);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* ---------------------------------------------------------------- 无障碍兜底 */
|
|
236
|
+
.prose :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
237
|
+
|
|
238
|
+
/* ============================================================================
|
|
239
|
+
反模式
|
|
240
|
+
=========================================================================
|
|
241
|
+
1. 给每个元素加类(.prose h2、.prose p2 …)
|
|
242
|
+
渲染器输出没有这些类,样式接不上。**按元素选择器接管**。
|
|
243
|
+
|
|
244
|
+
2. <div class="code-block"> 装代码
|
|
245
|
+
渲染器不认。用 <pre><code class="language-x">。
|
|
246
|
+
|
|
247
|
+
3. <div class="table"> 模拟表格
|
|
248
|
+
🔴 最严重:辅助技术会当普通 div 念,屏幕阅读器用户拿不到行列关系。
|
|
249
|
+
|
|
250
|
+
4. <div class="heading"> 代替标题
|
|
251
|
+
文档大纲全废,屏幕阅读器的"跳到标题"功能失效。
|
|
252
|
+
|
|
253
|
+
5. 缩进代码块硬加 class="language-"
|
|
254
|
+
CommonMark 规定它没有 class。
|
|
255
|
+
|
|
256
|
+
6. <b> / <i> 代替 <strong> / <em>
|
|
257
|
+
<b> 只是"视觉加粗",无语义。
|
|
258
|
+
|
|
259
|
+
7. hr 不清 height 和 border-top
|
|
260
|
+
默认 hr 有 inset 边框,会出现双线。
|
|
261
|
+
|
|
262
|
+
8. 依赖行内 <code> 样式而不排除代码块内的
|
|
263
|
+
代码块每行都会带上边框和底色。
|
|
264
|
+
========================================================================= */
|
|
265
|
+
|
|
266
|
+
/* ============================================================================
|
|
267
|
+
链接
|
|
268
|
+
============================================================================
|
|
269
|
+
暗色下浏览器默认链接蓝只有 1.91:1,几乎看不见 ⇒ 需要设 color。
|
|
270
|
+
|
|
271
|
+
🔴🔴 但**必须限定作用域**,不能写裸 `a {}`
|
|
272
|
+
裸标签选择器会命中**宿主页面的每一个链接** ——
|
|
273
|
+
也就是说:使用者引入本库后,自己站点所有 <a> 的颜色都会被改掉。
|
|
274
|
+
这正是「改一处、全库变样」的污染源之一。
|
|
275
|
+
|
|
276
|
+
正解:挂到本组件自己的根选择器上。作用域与组件一致,组件外完全不受影响。
|
|
277
|
+
|
|
278
|
+
🔴 修:这里原写 `.content a`,而本组件根类是 **`.prose`** ——
|
|
279
|
+
`.content` 全库 0 处出现 ⇒ 整段是死规则,而注释还写着它是根类。
|
|
280
|
+
文档说一套、代码是另一套 ⇒ "链接已处理"这个判断一直是假的。
|
|
281
|
+
============================================================================ */
|
|
282
|
+
.prose a:visited {
|
|
283
|
+
color: var(--accent);
|
|
284
|
+
text-decoration: underline;
|
|
285
|
+
text-underline-offset: 2px;
|
|
286
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<link rel="icon" href="data:,">
|
|
7
|
+
<title>content · 正文渲染 · frontend-lib</title>
|
|
8
|
+
<link rel="stylesheet" href="../../01-tokens/tokens.css">
|
|
9
|
+
<link rel="stylesheet" href="../../01-tokens/typography.css">
|
|
10
|
+
<link rel="stylesheet" href="../../01-tokens/page.css">
|
|
11
|
+
<link rel="stylesheet" href="content.css">
|
|
12
|
+
<style>
|
|
13
|
+
.demo-note {
|
|
14
|
+
max-width: 42rem; margin: 0 auto var(--sp-6);
|
|
15
|
+
padding: var(--sp-3) var(--sp-4);
|
|
16
|
+
background: var(--surface-sunken);
|
|
17
|
+
border-radius: var(--r-sm);
|
|
18
|
+
font-size: var(--fs-sm); color: var(--text-secondary);
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body class="page page">
|
|
23
|
+
|
|
24
|
+
<main>
|
|
25
|
+
<p class="demo-note">
|
|
26
|
+
<strong>content</strong> 接管 Markdown 渲染出来的元素:<b>结构用标准</b>(CommonMark/GFF 决定),
|
|
27
|
+
<b>样式用令牌</b>(颜色/字号/间距全部来自 tokens.css)。
|
|
28
|
+
所以它按<strong>元素选择器</strong>工作,不给每个元素加类。
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<!-- 🔴 新建:此前本组件只有 content.css + README.md,**没有 demo.html**,
|
|
32
|
+
⇒ 契约脚本拿不到页面、量不到任何东西(等于契约在空跑)。
|
|
33
|
+
这份 demo 同时作为「标题层级不跳级」的活样本。 -->
|
|
34
|
+
<div class="prose">
|
|
35
|
+
<h1>季度经营分析</h1>
|
|
36
|
+
<p class="lede">
|
|
37
|
+
本节演示正文容器接管 <code><h1></code>~<code><h3></code>、
|
|
38
|
+
段落、列表、表格、引用、代码块的样子。层级刻意按 h1→h2→h3 顺序排,
|
|
39
|
+
供 <code>content-check.js</code> 检查「不跳级」。
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<h2>1 · 关键指标</h2>
|
|
43
|
+
<p>下面是三个核心指标。<strong>加粗</strong>与<em>斜体</em>都走令牌,不写死样式。</p>
|
|
44
|
+
<ul>
|
|
45
|
+
<li>营业收入:同比 +12.4%</li>
|
|
46
|
+
<li>毛利率:<code>62.1%</code>(用等宽字体,数字对齐)</li>
|
|
47
|
+
<li>经营现金流:转正</li>
|
|
48
|
+
</ul>
|
|
49
|
+
|
|
50
|
+
<h3>1.1 · 分季度明细</h3>
|
|
51
|
+
<table>
|
|
52
|
+
<caption>2025–2026 分季度经营数据(单位:万元)</caption>
|
|
53
|
+
<thead>
|
|
54
|
+
<tr><th scope="col">季度</th><th scope="col">收入</th><th scope="col">毛利率</th></tr>
|
|
55
|
+
</thead>
|
|
56
|
+
<tbody>
|
|
57
|
+
<tr><th scope="row">2025 Q3</th><td class="num">4,120</td><td class="num">60.8%</td></tr>
|
|
58
|
+
<tr><th scope="row">2025 Q4</th><td class="num">4,586</td><td class="num">61.5%</td></tr>
|
|
59
|
+
<tr><th scope="row">2026 Q1</th><td class="num">4,903</td><td class="num">62.1%</td></tr>
|
|
60
|
+
</tbody>
|
|
61
|
+
</table>
|
|
62
|
+
|
|
63
|
+
<h2>2 · 风险提示</h2>
|
|
64
|
+
<blockquote>
|
|
65
|
+
<p>应收账款周转天数环比恶化 6 天,需在下季度收紧信用政策。</p>
|
|
66
|
+
</blockquote>
|
|
67
|
+
<ol>
|
|
68
|
+
<li>对账期 > 60 天的新客户改为预付</li>
|
|
69
|
+
<li>逾期 > 30 天暂停发货</li>
|
|
70
|
+
</ol>
|
|
71
|
+
|
|
72
|
+
<h2>3 · 术语</h2>
|
|
73
|
+
<dl>
|
|
74
|
+
<dt>EBITDA</dt>
|
|
75
|
+
<dd>息税折旧摊销前利润</dd>
|
|
76
|
+
<dt>CAC</dt>
|
|
77
|
+
<dd>客户获取成本</dd>
|
|
78
|
+
</dl>
|
|
79
|
+
|
|
80
|
+
<h2>4 · 代码与链接</h2>
|
|
81
|
+
<pre><code>const f = (x) => x * 2; // 纯 ES5 时代写法</code></pre>
|
|
82
|
+
<p>详细口径见 <a href="#main">内部数据字典</a>。</p>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
</main>
|
|
86
|
+
|
|
87
|
+
</body>
|
|
88
|
+
</html>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# drawer · 抽屉
|
|
2
|
+
|
|
3
|
+
依据 **APG Dialog 模式** —— 抽屉就是"从侧边滑进来的 Dialog"。
|
|
4
|
+
|
|
5
|
+
**与 dialog 的差别只有"从哪进来",三条硬要求完全一样。**
|
|
6
|
+
|
|
7
|
+
## 🔴 三条硬要求(一条都不能少)
|
|
8
|
+
|
|
9
|
+
| # | 要求 | 做错的后果 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| ① | **焦点陷阱**:Tab 在抽屉内循环 | Tab 跑到背景上去,抽屉等于没锁 |
|
|
12
|
+
| ② | `Esc` 关闭 | 键盘用户出不来 |
|
|
13
|
+
| ③ | **关闭后焦点归位**(回到触发它的那个按钮)| 键盘用户被"扔"回页面顶部,得从头 Tab 一遍 |
|
|
14
|
+
|
|
15
|
+
③ 最容易漏、**影响最大**。所以本组件的焦点归位走共享微行为核
|
|
16
|
+
`01-tokens/behavior/focus-return.js` 注入,不是各组件各写一遍。
|
|
17
|
+
|
|
18
|
+
## ⚠️ 抽屉是模态:背景必须 inert
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// 只把 body 的直接子元素(除抽屉容器)设 inert
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
不设 `inert` / `aria-hidden` 的后果:**读屏会同时读到抽屉与背景**
|
|
25
|
+
(业内叫"双重朗读")—— 用户听到一半不知道自己在听哪。
|
|
26
|
+
|
|
27
|
+
## 结构
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<aside class="drawer drawer--right" role="dialog" aria-modal="true"
|
|
31
|
+
aria-label="标题">
|
|
32
|
+
<header class="drawer__head">
|
|
33
|
+
<h2 class="drawer__title">…</h2>
|
|
34
|
+
<button class="drawer__close" type="button">✕</button>
|
|
35
|
+
</header>
|
|
36
|
+
<div class="drawer__body">…</div>
|
|
37
|
+
<footer class="drawer__foot">…</footer>
|
|
38
|
+
</aside>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| 部件 | 说明 |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `--right`(默认)/ `--left` / `--top` / `--bottom` | 四个进入方向 |
|
|
44
|
+
| `.drawer__head` / `__title` / `__close` | 头部与关闭按钮 |
|
|
45
|
+
| `.drawer__body` | 内容 |
|
|
46
|
+
| `.drawer__foot` | 底部操作区 |
|
|
47
|
+
|
|
48
|
+
## 启用
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
Drawer.open({ place: 'right' }); // 默认就是 right
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
全局只导出 `open`(不是 `create`)—— DOM 由 JS 建。
|
|
55
|
+
|
|
56
|
+
### 🔴 焦点必须在插入 DOM **之前**记
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
// ⭐ 记住"打开那一刻的焦点" —— 必须在插入 DOM 之前,
|
|
60
|
+
// 否则会记到刚建出来的新节点上(那就等于没记)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
抽屉没有 fallback:打开它的元素若被删了,就**维持现状不动**,
|
|
64
|
+
不假装"还回去了"(那样会把焦点送到一个已经不存在的地方)。
|
|
65
|
+
|
|
66
|
+
## 嵌套用锁计数
|
|
67
|
+
|
|
68
|
+
背景 `inert` 用 `lockCount` 计数管理 ⇒ 抽屉里再开抽屉时,
|
|
69
|
+
外层的锁不会因为内层关闭而被提前解掉。
|
|
70
|
+
|
|
71
|
+
## 成熟度:beta(两条行为债,如实列出)
|
|
72
|
+
|
|
73
|
+
| 债 | 现在的后果 |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `dismissable:manual` | 点遮罩关闭是手写的,不是统一机制 |
|
|
76
|
+
|
|
77
|
+
## 门禁
|
|
78
|
+
|
|
79
|
+
`05-audit/drawer-check.js`
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<link rel="icon" href="data:,">
|
|
7
|
+
<title>drawer · 抽屉 · frontend-lib</title>
|
|
8
|
+
<link rel="stylesheet" href="../../01-tokens/tokens.css">
|
|
9
|
+
<link rel="stylesheet" href="../../01-tokens/typography.css">
|
|
10
|
+
<link rel="stylesheet" href="../../01-tokens/focus-ring.css">
|
|
11
|
+
<link rel="stylesheet" href="../../01-tokens/page.css">
|
|
12
|
+
<link rel="stylesheet" href="../../02-primitives/button/button.css">
|
|
13
|
+
<link rel="stylesheet" href="drawer.css">
|
|
14
|
+
<style>
|
|
15
|
+
.row { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
|
|
16
|
+
.log {
|
|
17
|
+
margin-top: var(--sp-4); padding: var(--sp-3);
|
|
18
|
+
background: var(--surface-sunken); border-radius: var(--r-sm);
|
|
19
|
+
font-family: var(--mono); font-size: var(--fs-xs);
|
|
20
|
+
color: var(--text-secondary); min-height: 3.4em;
|
|
21
|
+
}
|
|
22
|
+
ul.demo-list { padding-left: var(--sp-5); color: var(--text-secondary); }
|
|
23
|
+
ul.demo-list li { margin: var(--sp-1) 0; }
|
|
24
|
+
</style>
|
|
25
|
+
</head>
|
|
26
|
+
<body class="page page">
|
|
27
|
+
<main class="wrap">
|
|
28
|
+
|
|
29
|
+
<h1>drawer · 抽屉</h1>
|
|
30
|
+
<p class="note">
|
|
31
|
+
依据 <strong>APG Dialog</strong>。与对话框的差别只是「从哪进来」,
|
|
32
|
+
三条硬要求完全一样:<strong>焦点陷阱</strong>(Tab 不跑出抽屉)·
|
|
33
|
+
<code>Esc</code> 关闭 · <strong>关闭后焦点归位</strong>(回到触发按钮)。
|
|
34
|
+
<br>⚠️ 抽屉是<strong>模态</strong>:背景设 <code>inert</code>,
|
|
35
|
+
否则读屏会同时读到抽屉与背景("双重朗读")。
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
<h2>1 · 四个方向</h2>
|
|
39
|
+
<div class="row">
|
|
40
|
+
<button class="btn" data-place="right">右侧</button>
|
|
41
|
+
<button class="btn" data-place="left">左侧</button>
|
|
42
|
+
<button class="btn" data-place="top">顶部</button>
|
|
43
|
+
<button class="btn" data-place="bottom">底部</button>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="log" id="log">(关闭后会显示焦点是否归位)</div>
|
|
46
|
+
|
|
47
|
+
<h2>2 · 初始焦点可指定</h2>
|
|
48
|
+
<div class="row">
|
|
49
|
+
<button class="btn btn--primary" id="b2">打开(焦点落在第二个按钮)</button>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
</main>
|
|
53
|
+
|
|
54
|
+
<script src="drawer.js"></script>
|
|
55
|
+
<script>
|
|
56
|
+
(function () {
|
|
57
|
+
var log = document.getElementById('log');
|
|
58
|
+
|
|
59
|
+
var BODY = '<ul class="demo-list">' +
|
|
60
|
+
'<li>可以在这里放任意内容</li>' +
|
|
61
|
+
'<li>背景已被设成 inert(读屏不会重复读)</li>' +
|
|
62
|
+
'<li>连按 Tab 试试:焦点在抽屉里循环</li>' +
|
|
63
|
+
'<li>按 Esc 关闭,焦点会回到刚才那个按钮</li>' +
|
|
64
|
+
'</ul>';
|
|
65
|
+
|
|
66
|
+
document.querySelectorAll('[data-place]').forEach(function (b) {
|
|
67
|
+
b.addEventListener('click', function () {
|
|
68
|
+
var place = b.getAttribute('data-place');
|
|
69
|
+
var dr = Drawer.open({
|
|
70
|
+
place: place,
|
|
71
|
+
title: '从' + place + '进来的抽屉',
|
|
72
|
+
content: BODY,
|
|
73
|
+
actions: [
|
|
74
|
+
{ label: '取消' },
|
|
75
|
+
{ label: '确定', variant: 'primary' },
|
|
76
|
+
],
|
|
77
|
+
onClose: function () {
|
|
78
|
+
log.textContent = '已关闭 · 焦点现在在「' +
|
|
79
|
+
(document.activeElement ? document.activeElement.textContent.trim().slice(0, 12) : '?') +
|
|
80
|
+
'」' + (document.activeElement === b ? ' ✅ 已归位' : ' 🔴 没归位');
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
document.getElementById('b2').addEventListener('click', function () {
|
|
87
|
+
Drawer.open({
|
|
88
|
+
place: 'right',
|
|
89
|
+
title: '初始焦点在「取消」',
|
|
90
|
+
content: BODY,
|
|
91
|
+
initialFocus: '.drawer__foot .btn:not(.btn--primary)',
|
|
92
|
+
actions: [
|
|
93
|
+
{ label: '取消' },
|
|
94
|
+
{ label: '确定', variant: 'primary' },
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
})();
|
|
99
|
+
</script>
|
|
100
|
+
</body>
|
|
101
|
+
</html>
|