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.
Files changed (274) hide show
  1. package/01-tokens/README.md +236 -0
  2. package/01-tokens/behavior/dismissable.js +132 -0
  3. package/01-tokens/behavior/emit.js +76 -0
  4. package/01-tokens/behavior/focus-return.js +81 -0
  5. package/01-tokens/behavior/roving.js +152 -0
  6. package/01-tokens/behavior/typeahead.js +124 -0
  7. package/01-tokens/focus-ring-demo.html +133 -0
  8. package/01-tokens/focus-ring.css +102 -0
  9. package/01-tokens/page.css +244 -0
  10. package/01-tokens/polyfill.js +116 -0
  11. package/01-tokens/responsive-demo.html +171 -0
  12. package/01-tokens/scrollbar-demo.html +194 -0
  13. package/01-tokens/scrollbar.css +179 -0
  14. package/01-tokens/theme-toggle.js +103 -0
  15. package/01-tokens/tokens.css +972 -0
  16. package/01-tokens/typography.css +156 -0
  17. package/02-primitives/badge/README.md +71 -0
  18. package/02-primitives/badge/badge.css +141 -0
  19. package/02-primitives/badge/demo.html +101 -0
  20. package/02-primitives/button/README.md +99 -0
  21. package/02-primitives/button/button.css +353 -0
  22. package/02-primitives/button/demo.html +200 -0
  23. package/02-primitives/card/README.md +145 -0
  24. package/02-primitives/card/card.css +246 -0
  25. package/02-primitives/card/demo.html +170 -0
  26. package/02-primitives/choice/README.md +161 -0
  27. package/02-primitives/choice/choice.css +395 -0
  28. package/02-primitives/choice/demo.html +277 -0
  29. package/02-primitives/combobox/README.md +79 -0
  30. package/02-primitives/combobox/combobox.css +223 -0
  31. package/02-primitives/combobox/combobox.js +485 -0
  32. package/02-primitives/combobox/demo.html +109 -0
  33. package/02-primitives/date-range/README.md +100 -0
  34. package/02-primitives/date-range/date-range.css +206 -0
  35. package/02-primitives/date-range/date-range.js +301 -0
  36. package/02-primitives/date-range/demo.html +104 -0
  37. package/02-primitives/input/README.md +170 -0
  38. package/02-primitives/input/demo.html +246 -0
  39. package/02-primitives/input/input.css +239 -0
  40. package/02-primitives/popover/README.md +80 -0
  41. package/02-primitives/popover/demo.html +150 -0
  42. package/02-primitives/popover/popover.css +187 -0
  43. package/02-primitives/popover/popover.js +331 -0
  44. package/02-primitives/progress/README.md +82 -0
  45. package/02-primitives/progress/demo.html +154 -0
  46. package/02-primitives/progress/progress.css +172 -0
  47. package/02-primitives/select/README.md +95 -0
  48. package/02-primitives/select/demo.html +121 -0
  49. package/02-primitives/select/select.css +151 -0
  50. package/02-primitives/select/select.js +549 -0
  51. package/02-primitives/separator/README.md +63 -0
  52. package/02-primitives/separator/demo.html +84 -0
  53. package/02-primitives/separator/separator.css +73 -0
  54. package/02-primitives/skeleton/README.md +69 -0
  55. package/02-primitives/skeleton/demo.html +110 -0
  56. package/02-primitives/skeleton/skeleton.css +96 -0
  57. package/02-primitives/switch/README.md +65 -0
  58. package/02-primitives/switch/demo.html +115 -0
  59. package/02-primitives/switch/switch.css +370 -0
  60. package/03-patterns/accordion/README.md +121 -0
  61. package/03-patterns/accordion/accordion.css +453 -0
  62. package/03-patterns/accordion/accordion.js +369 -0
  63. package/03-patterns/accordion/demo.html +223 -0
  64. package/03-patterns/content/README.md +103 -0
  65. package/03-patterns/content/content.css +286 -0
  66. package/03-patterns/content/demo.html +88 -0
  67. package/03-patterns/drawer/README.md +79 -0
  68. package/03-patterns/drawer/demo.html +101 -0
  69. package/03-patterns/drawer/drawer.css +129 -0
  70. package/03-patterns/drawer/drawer.js +258 -0
  71. package/03-patterns/dropdown/README.md +78 -0
  72. package/03-patterns/dropdown/demo.html +101 -0
  73. package/03-patterns/dropdown/dropdown.css +135 -0
  74. package/03-patterns/dropdown/dropdown.js +451 -0
  75. package/03-patterns/form-validation/README.md +147 -0
  76. package/03-patterns/form-validation/demo.html +280 -0
  77. package/03-patterns/form-validation/form-validation.css +263 -0
  78. package/03-patterns/list/README.md +150 -0
  79. package/03-patterns/list/demo.html +225 -0
  80. package/03-patterns/list/flip.js +201 -0
  81. package/03-patterns/list/list.css +162 -0
  82. package/03-patterns/nav/README.md +225 -0
  83. package/03-patterns/nav/demo.html +131 -0
  84. package/03-patterns/nav/nav.css +314 -0
  85. package/03-patterns/nav/nav.js +430 -0
  86. package/03-patterns/nav/toc.js +111 -0
  87. package/03-patterns/overlay/README.md +196 -0
  88. package/03-patterns/overlay/demo.html +241 -0
  89. package/03-patterns/overlay/overlay.css +319 -0
  90. package/03-patterns/overlay/overlay.js +563 -0
  91. package/03-patterns/pagination/README.md +114 -0
  92. package/03-patterns/pagination/demo.html +165 -0
  93. package/03-patterns/pagination/pagination.css +195 -0
  94. package/03-patterns/pagination/pagination.js +395 -0
  95. package/03-patterns/reveal/README.md +91 -0
  96. package/03-patterns/reveal/demo.html +106 -0
  97. package/03-patterns/reveal/reveal.css +93 -0
  98. package/03-patterns/reveal/reveal.js +172 -0
  99. package/03-patterns/states/README.md +141 -0
  100. package/03-patterns/states/demo.html +240 -0
  101. package/03-patterns/states/states.css +252 -0
  102. package/03-patterns/tabs/README.md +84 -0
  103. package/03-patterns/tabs/demo.html +139 -0
  104. package/03-patterns/tabs/tabs.css +172 -0
  105. package/03-patterns/tabs/tabs.js +307 -0
  106. package/03-patterns/tooltip/README.md +96 -0
  107. package/03-patterns/tooltip/demo.html +78 -0
  108. package/03-patterns/tooltip/tooltip.css +89 -0
  109. package/03-patterns/tooltip/tooltip.js +288 -0
  110. package/03-patterns/tree/README.md +94 -0
  111. package/03-patterns/tree/demo.html +138 -0
  112. package/03-patterns/tree/tree.css +113 -0
  113. package/03-patterns/tree/tree.js +456 -0
  114. package/04-recipes/analysis-report/README.md +80 -0
  115. package/04-recipes/analysis-report/analysis-report.css +187 -0
  116. package/04-recipes/analysis-report/demo.html +274 -0
  117. package/04-recipes/data-showcase/README.md +45 -0
  118. package/04-recipes/data-showcase/demo.html +247 -0
  119. package/04-recipes/detail-page/README.md +93 -0
  120. package/04-recipes/detail-page/demo.html +55 -0
  121. package/04-recipes/detail-page/detail-page.css +190 -0
  122. package/04-recipes/detail-page/full.html +60 -0
  123. package/04-recipes/detail-page/null.html +60 -0
  124. package/04-recipes/detail-page/partial.html +60 -0
  125. package/04-recipes/detail-page/verify-skeleton.py +19 -0
  126. package/04-recipes/longform/README.md +93 -0
  127. package/04-recipes/longform/agc-memory.svg +4 -0
  128. package/04-recipes/longform/content.html +610 -0
  129. package/04-recipes/longform/content.md +51 -0
  130. package/04-recipes/longform/longform.html +627 -0
  131. package/04-recipes/report/README.md +176 -0
  132. package/04-recipes/report/demo.html +355 -0
  133. package/04-recipes/report/report.css +364 -0
  134. package/04-recipes/table/README.md +103 -0
  135. package/04-recipes/table/demo.html +455 -0
  136. package/04-recipes/table/table.css +372 -0
  137. package/04-recipes/tierA-tokens.css +189 -0
  138. package/09-assets/README.md +74 -0
  139. package/09-assets/bar/README.md +94 -0
  140. package/09-assets/bar/bar.css +166 -0
  141. package/09-assets/bar/bar.js +320 -0
  142. package/09-assets/bar/demo.html +176 -0
  143. package/09-assets/echarts-adapter/demo.html +192 -0
  144. package/09-assets/echarts-adapter/echarts-adapter.js +277 -0
  145. package/09-assets/model-viewer/README.md +155 -0
  146. package/09-assets/model-viewer/demo.html +105 -0
  147. package/09-assets/model-viewer/model-viewer.css +143 -0
  148. package/09-assets/model-viewer/model-viewer.js +423 -0
  149. package/09-assets/scientific-plot/README.md +94 -0
  150. package/09-assets/scientific-plot/demo.html +252 -0
  151. package/09-assets/scientific-plot/scientific-plot.css +141 -0
  152. package/09-assets/scientific-plot/scientific-plot.js +610 -0
  153. package/09-assets/sparkline/README.md +153 -0
  154. package/09-assets/sparkline/demo.html +163 -0
  155. package/09-assets/sparkline/sparkline.css +158 -0
  156. package/09-assets/sparkline/sparkline.js +508 -0
  157. package/API.md +332 -0
  158. package/CHANGELOG.md +3116 -0
  159. package/CODE_OF_CONDUCT.md +64 -0
  160. package/CONTRIBUTING.md +89 -0
  161. package/ERRORS.md +1658 -0
  162. package/LICENSE +21 -0
  163. package/README.md +491 -2
  164. package/SECURITY.md +72 -0
  165. package/START-HERE.md +164 -0
  166. package/SUPPORT.md +109 -0
  167. package/adapters/presentation/README.md +113 -0
  168. package/adapters/presentation/check.js +101 -0
  169. package/adapters/presentation/deck.schema.json +74 -0
  170. package/adapters/presentation/to-doc.js +138 -0
  171. package/ai/START-HERE.md +104 -0
  172. package/ai/behaviors.json +380 -0
  173. package/ai/behaviors.schema.json +61 -0
  174. package/ai/capabilities.json +56 -0
  175. package/ai/cli.js +387 -0
  176. package/ai/components.json +1944 -0
  177. package/ai/components.meta.json +168 -0
  178. package/ai/components.schema.json +220 -0
  179. package/ai/contract.schema.json +147 -0
  180. package/ai/deprecations.json +55 -0
  181. package/ai/deprecations.schema.json +85 -0
  182. package/ai/motion-policy.json +22 -0
  183. package/ai/patch.js +323 -0
  184. package/ai/patch.schema.json +63 -0
  185. package/ai/profiles.json +92 -0
  186. package/ai/tokens.audience.json +55 -0
  187. package/ai/tokens.audience.schema.json +73 -0
  188. package/ai/tokens.json +762 -0
  189. package/ai/tokens.schema.json +58 -0
  190. package/ai/tokens.tree.json +752 -0
  191. package/ai/tokens.tree.schema.json +56 -0
  192. package/ai/validate.js +383 -0
  193. package/dist/01-tokens/behavior/dismissable.js +60 -0
  194. package/dist/01-tokens/behavior/emit.js +21 -0
  195. package/dist/01-tokens/behavior/focus-return.js +31 -0
  196. package/dist/01-tokens/behavior/roving.js +77 -0
  197. package/dist/01-tokens/behavior/typeahead.js +61 -0
  198. package/dist/01-tokens/focus-ring.css +1 -0
  199. package/dist/01-tokens/page.css +1 -0
  200. package/dist/01-tokens/polyfill.js +78 -0
  201. package/dist/01-tokens/scrollbar.css +1 -0
  202. package/dist/01-tokens/theme-toggle.js +61 -0
  203. package/dist/01-tokens/tokens.css +1 -0
  204. package/dist/01-tokens/typography.css +1 -0
  205. package/dist/02-primitives/badge/badge.css +1 -0
  206. package/dist/02-primitives/button/button.css +1 -0
  207. package/dist/02-primitives/card/card.css +1 -0
  208. package/dist/02-primitives/choice/choice.css +1 -0
  209. package/dist/02-primitives/combobox/combobox.css +1 -0
  210. package/dist/02-primitives/combobox/combobox.js +439 -0
  211. package/dist/02-primitives/date-range/date-range.css +1 -0
  212. package/dist/02-primitives/date-range/date-range.js +267 -0
  213. package/dist/02-primitives/input/input.css +1 -0
  214. package/dist/02-primitives/popover/popover.css +1 -0
  215. package/dist/02-primitives/popover/popover.js +257 -0
  216. package/dist/02-primitives/progress/progress.css +1 -0
  217. package/dist/02-primitives/select/select.css +1 -0
  218. package/dist/02-primitives/select/select.js +449 -0
  219. package/dist/02-primitives/separator/separator.css +1 -0
  220. package/dist/02-primitives/skeleton/skeleton.css +1 -0
  221. package/dist/02-primitives/switch/switch.css +1 -0
  222. package/dist/03-patterns/accordion/accordion.css +1 -0
  223. package/dist/03-patterns/accordion/accordion.js +219 -0
  224. package/dist/03-patterns/content/content.css +1 -0
  225. package/dist/03-patterns/drawer/drawer.css +1 -0
  226. package/dist/03-patterns/drawer/drawer.js +229 -0
  227. package/dist/03-patterns/dropdown/dropdown.css +1 -0
  228. package/dist/03-patterns/dropdown/dropdown.js +365 -0
  229. package/dist/03-patterns/form-validation/form-validation.css +1 -0
  230. package/dist/03-patterns/list/flip.js +117 -0
  231. package/dist/03-patterns/list/list.css +1 -0
  232. package/dist/03-patterns/nav/nav.css +1 -0
  233. package/dist/03-patterns/nav/nav.js +322 -0
  234. package/dist/03-patterns/nav/toc.js +76 -0
  235. package/dist/03-patterns/overlay/overlay.css +1 -0
  236. package/dist/03-patterns/overlay/overlay.js +398 -0
  237. package/dist/03-patterns/pagination/pagination.css +1 -0
  238. package/dist/03-patterns/pagination/pagination.js +273 -0
  239. package/dist/03-patterns/reveal/reveal.css +1 -0
  240. package/dist/03-patterns/reveal/reveal.js +138 -0
  241. package/dist/03-patterns/states/states.css +1 -0
  242. package/dist/03-patterns/tabs/tabs.css +1 -0
  243. package/dist/03-patterns/tabs/tabs.js +242 -0
  244. package/dist/03-patterns/tooltip/tooltip.css +1 -0
  245. package/dist/03-patterns/tooltip/tooltip.js +213 -0
  246. package/dist/03-patterns/tree/tree.css +1 -0
  247. package/dist/03-patterns/tree/tree.js +383 -0
  248. package/dist/04-recipes/analysis-report/analysis-report.css +1 -0
  249. package/dist/04-recipes/detail-page/detail-page.css +1 -0
  250. package/dist/04-recipes/report/report.css +1 -0
  251. package/dist/04-recipes/table/table.css +1 -0
  252. package/dist/04-recipes/tierA-tokens.css +1 -0
  253. package/dist/09-assets/bar/bar.css +1 -0
  254. package/dist/09-assets/bar/bar.js +222 -0
  255. package/dist/09-assets/echarts-adapter/echarts-adapter.js +185 -0
  256. package/dist/09-assets/model-viewer/model-viewer.css +1 -0
  257. package/dist/09-assets/model-viewer/model-viewer.js +320 -0
  258. package/dist/09-assets/scientific-plot/scientific-plot.css +1 -0
  259. package/dist/09-assets/scientific-plot/scientific-plot.js +568 -0
  260. package/dist/09-assets/sparkline/sparkline.css +1 -0
  261. package/dist/09-assets/sparkline/sparkline.js +265 -0
  262. package/dist/COSTS.md +56 -0
  263. package/docs/AI-CONTRACT.md +197 -0
  264. package/docs/BACKLOG.md +127 -0
  265. package/docs/BENCHMARK.md +252 -0
  266. package/docs/GUIDANCE.md +154 -0
  267. package/docs/INVARIANT.md +372 -0
  268. package/docs/LAYER-BASELINE.md +158 -0
  269. package/docs/RELEASING.md +275 -0
  270. package/docs/RFC-THEME-SCOPE.md +141 -0
  271. package/docs/ROADMAP.md +182 -0
  272. package/index.html +82 -0
  273. package/package.json +107 -4
  274. package/types/index.d.ts +310 -0
@@ -0,0 +1,319 @@
1
+ /* ============================================================================
2
+ frontend-lib / 03-patterns / overlay / overlay.css
3
+ 反馈层:toast(通知)与 dialog(模态)
4
+
5
+ 依赖:01-tokens/tokens.css + 02-primitives/button/button.css
6
+ 版本:A ✅ / B ✅ / C ✅
7
+
8
+ 🔴 本文件的核心是一条判断,不是样式:
9
+
10
+ toast 和 dialog 是两种东西,用错比做丑严重得多。
11
+
12
+ ┌ toast(通知)
13
+ │ 发生了什么、你不需要做任何事
14
+ │ 不抢焦点、自动消失、role="status"
15
+ │ 错用:把"确认删除"做成 toast
16
+ │
17
+ └ dialog(模态)
18
+ 必须你处理完才能继续
19
+ 困住焦点、Esc 可关、role="dialog" + aria-modal
20
+ 错用:把"已保存"做成 dialog
21
+
22
+ 判据只有一条:**用户不做任何操作,任务能不能继续?**
23
+ 能 → toast;不能 → dialog。
24
+ ========================================================================= */
25
+
26
+ /* ============================================================================
27
+ Toast
28
+ ========================================================================= */
29
+
30
+ .toast-region {
31
+ position: fixed;
32
+ inset-inline-end: var(--sp-5);
33
+ bottom: var(--sp-5);
34
+ z-index: var(--z-toast);
35
+
36
+ display: flex;
37
+ flex-direction: column;
38
+ gap: var(--sp-2);
39
+ max-width: min(380px, calc(100vw - var(--sp-5) * 2));
40
+ pointer-events: none; /* 区域本身不吃点击,只有 toast 吃 */
41
+ }
42
+
43
+ .toast {
44
+ pointer-events: auto; /* 🔴 撤销区域上的 none,否则按钮点不动 */
45
+
46
+ display: flex;
47
+ align-items: flex-start;
48
+ gap: var(--sp-3);
49
+ padding: var(--sp-3) var(--sp-4);
50
+
51
+ background: var(--surface);
52
+ border: 1px solid var(--border-decor);
53
+ border-radius: var(--r-sm);
54
+ box-shadow: var(--shadow-2);
55
+
56
+ /* 🔴 去掉 `border-left: 3px solid`。
57
+ 评审意见左侧竖条"很蠢"——它是纯装饰:3px 竖条在浅底上形成一条硬边,
58
+ 视觉噪声大于信息量。已从全库去掉,**这里漏了**(典型的"只改一半")。
59
+
60
+ WCAG 1.4.1(不能只靠颜色)**不依赖竖条**:图标 + 文字本身就够了
61
+ —— 那两条是可读的形状与文字,而左侧条既不是文字也不是形状,
62
+ 只是把颜色重复了一遍。 */
63
+ font-size: var(--fs-sm);
64
+ line-height: var(--lh-base);
65
+ color: var(--text-primary);
66
+
67
+ animation: toast-in var(--dur-base) var(--ease-out);
68
+ }
69
+
70
+ .toast--success { background: var(--success-bg); }
71
+ .toast--error { background: var(--danger-bg); }
72
+ .toast--warning { background: var(--warning-bg); }
73
+
74
+ .toast__body { flex: 1; min-width: 0; }
75
+
76
+ .toast__title {
77
+ font-weight: 500;
78
+ margin: 0 0 2px;
79
+ }
80
+
81
+ .toast__desc {
82
+ margin: 0;
83
+ color: var(--text-secondary);
84
+ font-size: var(--fs-xs);
85
+ line-height: 1.5;
86
+ }
87
+
88
+ .toast__close {
89
+ flex: none;
90
+ /* 🔴 28 → 44(WCAG 2.5.5,iOS 真机实测反馈后统一)
91
+ 🔴 注意用 min-width 而**不是** width:
92
+ 固定 `width: 28px` 会**赢过** min-width(同名属性里
93
+ min-width 只在 width:auto 时才生效)⇒ 改完必须确认实测值。 */
94
+ min-width: 44px;
95
+ min-height: 44px;;
96
+ margin: -4px -4px 0 0;
97
+ display: grid;
98
+ place-items: center;
99
+ border: none;
100
+ border-radius: var(--r-sm);
101
+ background: transparent;
102
+ color: var(--text-tertiary);
103
+ cursor: pointer;
104
+ font-size: 16px;
105
+ line-height: 1;
106
+ transition: background-color var(--dur-fast) var(--ease-out);
107
+ }
108
+ @media (hover: hover) and (pointer: fine) {
109
+ .toast__close:hover { background: var(--surface-sunken); color: var(--text-primary); }
110
+ }
111
+ .toast__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
112
+
113
+ /* 关闭动画:往右淡出,和进场方向一致 */
114
+ .toast[data-state="leaving"] {
115
+ animation: toast-out var(--dur-fast) var(--ease-in-out) forwards;
116
+ }
117
+
118
+ @keyframes toast-in {
119
+ from { opacity: 0; transform: translateX(var(--lift-lg)); }
120
+ to { opacity: 1; transform: translateX(0); }
121
+ }
122
+ @keyframes toast-out {
123
+ to { opacity: 0; transform: translateX(var(--lift-lg)); }
124
+ }
125
+
126
+ /* 进度条:让用户知道"它自己会消失"以及还剩多久。
127
+ 只在有明确时长的 toast 上用。 */
128
+ .toast__timer {
129
+ position: absolute;
130
+ inset-inline-start: 0; bottom: 0;
131
+ height: 2px;
132
+ background: currentColor;
133
+ opacity: 0.25;
134
+ border-radius: 0 0 0 var(--r-sm);
135
+ animation: toast-timer linear forwards;
136
+ }
137
+ @keyframes toast-timer { from { width: 100%; } to { width: 0%; } }
138
+
139
+ /* ============================================================================
140
+ Dialog(模态)
141
+ ========================================================================= */
142
+
143
+ .dialog-backdrop {
144
+ position: fixed;
145
+ inset: 0;
146
+ z-index: var(--z-modal);
147
+ display: grid;
148
+ place-items: center;
149
+ padding: var(--sp-5);
150
+
151
+ /* 🔴 同 nav:写死的深色遮罩在暗色下会"消失"。改用令牌。 */
152
+ background: var(--scrim);
153
+ /* 🔴 不做模糊:backdrop-filter 在旧 WebView 上要么无效要么掉帧 */
154
+ animation: backdrop-in var(--dur-base) var(--ease-out);
155
+ }
156
+
157
+ .dialog {
158
+ position: relative;
159
+ width: 100%;
160
+ max-width: 460px;
161
+ max-height: calc(100vh - var(--sp-7));
162
+ overflow-y: auto;
163
+
164
+ background: var(--surface);
165
+ border-radius: var(--r-lg);
166
+ box-shadow: var(--shadow-2);
167
+ padding: var(--sp-6);
168
+
169
+ animation: dialog-in var(--dur-base) var(--ease-out);
170
+ }
171
+
172
+ /* 🔴 scale 只到 0.98,位移只有 8px。
173
+ 大幅缩放 + 弹跳 = 廉价感。模态是"出现",不是"跳出来"。 */
174
+ @keyframes dialog-in {
175
+ from { opacity: 0; transform: translateY(var(--lift-lg)) scale(0.98); }
176
+ to { opacity: 1; transform: translateY(0) scale(1); }
177
+ }
178
+ @keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
179
+
180
+ .dialog__title {
181
+ margin: 0 0 var(--sp-2);
182
+ font-size: var(--fs-lg);
183
+ font-weight: 500;
184
+ line-height: var(--lh-tight);
185
+ color: var(--text-primary);
186
+ }
187
+
188
+ .dialog__desc {
189
+ margin: 0 0 var(--sp-5);
190
+ font-size: var(--fs-sm);
191
+ color: var(--text-secondary);
192
+ line-height: var(--lh-base);
193
+ }
194
+
195
+ /* 动作区:按钮**右对齐且相邻**。
196
+ 🔴 去掉 space-between + margin-right:auto。
197
+ 原来让破坏性按钮靠左、取消靠右,中间空一大片 ——
198
+ 评审意见「删除和取消放到中间来」,看着别扭。
199
+ 常规做法(Linear / Vercel / GitHub 都这样):两个按钮挨着,一起靠右。
200
+ 次要的在左(取消),主要的在右(删除)。 */
201
+ .dialog__actions {
202
+ display: flex;
203
+ justify-content: flex-end;
204
+ align-items: center;
205
+ gap: var(--sp-3);
206
+ flex-wrap: wrap;
207
+ }
208
+
209
+ /* ---------------------------------------------------------------------------
210
+ 滚动锁定
211
+ ---------------------------------------------------------------------------
212
+ 🔴 打开模态必须锁背景滚动,否则鼠标滚轮在弹层边缘会连带滚动背后页面。
213
+
214
+ 🔴🔴 实测反馈"弹窗打开时整页明显向右移动" —— 这是锁滚动最典型的症状:
215
+ 滚动条消失 ⇒ 可视区变宽 ⇒ 居中内容整体右移。
216
+
217
+ 为什么**不用** `scrollbar-gutter: stable`(实测推翻)
218
+ ------------------------------------------------------------------------
219
+ 它确实能让内容不跳,但前提是**一直**占位。两种写法都量过
220
+ (`showScrollbars` 打开,滚动条 15px,视口 393):
221
+
222
+ · 无条件 `html { scrollbar-gutter: stable }`
223
+ 三项全稳,但**导入即生效**:一个弹层都没开,宿主的 `100vw`
224
+ 就已经被压到 378。改宿主全局布局,与"组件只影响自己"冲突。
225
+
226
+ · 状态级 `html[data-scroll-locked] { scrollbar-gutter: stable }`
227
+ 流式内容稳了,但 `100vw` 元素从 393 跳到 378 ——
228
+ 锁定前没占位、锁定后占位,抖动只是换了个地方发生。
229
+
230
+ ⇒ 所以这里**只锁滚动**,宽度补偿交给 JS 叠加(overlay.js 的 lockScroll),
231
+ 实测流式内容与 `100vw` 两者都不动。
232
+
233
+ ⚠️ 锁 `body` 就够了,**不要同时锁 `html`** ——
234
+ 两者都锁会让某些浏览器出现双层滚动容器。
235
+ ------------------------------------------------------------------------ */
236
+
237
+ /* ⚠️ 用**独立属性**而不是 `data-state`:锁的是滚动,不是这个元素的状态,
238
+ `data-state` 是留给组件自己的状态枚举的,混进去会污染枚举取值集合。 */
239
+ body[data-scroll-locked] { overflow: hidden; }
240
+
241
+ /* 命中上面这条时,JS 会同时在 `html` 上给出
242
+ `--overlay-scrollbar-width`(滚动条宽度,仅锁定期间存在)。
243
+ body 的 padding 补偿管不到 `position: fixed` 的元素,
244
+ 宿主若有吸底条,自己用这个变量补:
245
+ .my-bar { right: var(--overlay-scrollbar-width, 0px) } */
246
+
247
+ /* 极窄屏:dialog 贴底(移动端习惯,比居中更符合直觉) */
248
+ @media (max-width: 480px) {
249
+ .dialog-backdrop { place-items: end center; padding: 0; }
250
+ .dialog {
251
+ /* 🔴 修一个 iPhone 实测到的 bug:
252
+ 这里原本是 `max-width: none`(意图是"窄屏下撑满"),
253
+ 但**没有上界** —— 一旦内容里有最小宽度的东西
254
+ (<table> / <pre> / 长 code / flex 不换行的行),
255
+ 弹窗会**撑破视口**:iPhone 15 Pro 实测宽 444 > 视口 393,
256
+ **左边缘跑到 -26px**(左侧被切掉一块,且左边距也没了)。
257
+
258
+ ⇒ 改成 `width: 100vw` + 两侧留边:
259
+ ① 上界由视口决定(不可能超出屏幕)
260
+ ② 仍然"贴底弹出"(place-items: end center 没变)
261
+ ③ 安全区照旧加在 padding-bottom
262
+ ⇒ 内容再宽也只能在自己内部滚动,不会把壳撑破。 */
263
+ width: 100vw;
264
+ max-width: 100vw;
265
+ box-sizing: border-box;
266
+ border-radius: var(--r-lg) var(--r-lg) 0 0;
267
+ padding-bottom: calc(var(--sp-6) + env(safe-area-inset-bottom, 0px));
268
+ }
269
+ .dialog__actions .btn { flex: 1; } /* 移动端按钮并排等宽更好点 */
270
+ }
271
+
272
+ /* ============================================================================
273
+ reduced-motion
274
+ =========================================================================
275
+ 进场是 opacity + 8px 位移,属于小幅位移 —— 可以保留但去掉位移。
276
+ 🔴 单纯 opacity 淡入是最稳的:既不违反 reduced-motion,也保留"出现了"的反馈。
277
+ ========================================================================= */
278
+ @media (prefers-reduced-motion: reduce) {
279
+ .toast,
280
+ .toast[data-state="leaving"],
281
+ .dialog,
282
+ .dialog-backdrop { animation: none; }
283
+ .toast__timer { animation: none; width: 100%; } /* 进度条停住,不假装还在倒计时 */
284
+ }
285
+
286
+ /* ============================================================================
287
+ 反模式
288
+ =========================================================================
289
+ 1. toast 抢焦点
290
+ 通知不该打断当前操作。role="status" 会自动播报,不需要 focus。
291
+
292
+ 2. 用 toast 做需要确认的操作
293
+ "确认删除"做成 toast = 用户可能根本没看见就把文件删了。
294
+ 判据:不做任何操作能不能继续?能 → toast。
295
+
296
+ 3. dialog 不困住焦点
297
+ Tab 会跑到背后页面的按钮上,屏幕阅读器读到的是"你在模态框里,
298
+ 但按钮在背景上",完全混乱。
299
+
300
+ 4. dialog 关闭后焦点不回去
301
+ 焦点掉到 <body>,键盘用户要重新 Tab 一遍才能回到原处。
302
+ 必须回到**触发它的那个元素**。
303
+
304
+ 5. 锁滚动不补偿滚动条宽度
305
+ 弹层出现时页面横向"闪一下"。这多半就是"闪"的来源。
306
+
307
+ 6. 错误 toast 3 秒就消失
308
+ 用户还没读完就没了。错误要么不自动消失,要么给足时间 + 可手动关。
309
+
310
+ 7. dialog 做大幅缩放/回弹
311
+ 廉价感。scale 最多 0.98,位移最多 8px。
312
+
313
+ 8. 不用 <dialog> 元素
314
+ 本库用 div + role="dialog" + aria-modal,是为了让动画和样式可控。
315
+ 若不需要自定义动画,原生 <dialog> 的 focus trap 和 Esc 是免费的 —— 能用就用。
316
+
317
+ 9. backdrop 加 backdrop-filter: blur
318
+ 旧 WebView 上要么无效要么掉帧,精简档 尤其要避开。
319
+ ========================================================================= */