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,116 @@
1
+ /**
2
+ * polyfill.js — 旧环境所需的两处补丁
3
+ * ============================================================================
4
+ * 为什么需要这个文件
5
+ * ---------------------------------------------------------------------------
6
+ * 本库的**语法**目标是 ES5,但「ES5 语法」不等于「ES5 运行时 API」。
7
+ * 下面两个方法是 DOM/JS 标准里的后期新增项,在老 WebView 上**不存在**:
8
+ *
9
+ * Element.closest() —— 2015 年起 widely available
10
+ * Array.from() —— ES2015
11
+ *
12
+ * 混用「ES5 语法 + 现代 API」是很常见的误解:代码能通过 ES5 语法检查,
13
+ * 却在老环境里运行时抛 `is not a function`。
14
+ *
15
+ * 用法:放在行为脚本**之前**引入一次。
16
+ * <script src="polyfill.js"></script>
17
+ * <script src="patterns/overlay/overlay.js"></script>
18
+ *
19
+ * 代价:约 0.4 KB,不引入任何依赖。
20
+ * ============================================================================
21
+ */
22
+ (function (global) {
23
+ 'use strict';
24
+
25
+ /* ------------------------------------------------------------------------
26
+ * Element.closest
27
+ * ---------------------------------------------------------------------
28
+ * 逐级向上比对选择器,用 getAttribute 代替 matches(后者同样是后期新增)。
29
+ * 只支持标签、类、属性与 #id —— 覆盖本库用到的全部场景。
30
+ * --------------------------------------------------------------------- */
31
+ if (!Element.prototype.closest) {
32
+ Element.prototype.closest = function (selector) {
33
+ var el = this;
34
+ while (el && el.nodeType === 1) {
35
+ if (matchesSimple(el, selector)) return el;
36
+ el = el.parentElement;
37
+ }
38
+ return null;
39
+ };
40
+ }
41
+
42
+ /**
43
+ * 极简选择器匹配:支持 `tag`、`.class`、`#id`、`[attr]`、`[attr="v"]`。
44
+ * 逗号分隔的多个选择器取"任一命中"。
45
+ */
46
+ function matchesSimple(el, selector) {
47
+ var parts = String(selector).split(',');
48
+ for (var i = 0; i < parts.length; i++) {
49
+ if (matchOne(el, parts[i].trim())) return true;
50
+ }
51
+ return false;
52
+ }
53
+
54
+ function matchOne(el, sel) {
55
+ if (!sel) return false;
56
+ var rest = sel;
57
+
58
+ /* 标签名 */
59
+ var m = rest.match(/^([a-zA-Z][\w-]*)/);
60
+ if (m) {
61
+ if (el.tagName.toLowerCase() !== m[1].toLowerCase()) return false;
62
+ rest = rest.slice(m[1].length);
63
+ }
64
+ /* 类 */
65
+ var cls = rest.match(/\.([\w-]+)/g);
66
+ if (cls) {
67
+ for (var i = 0; i < cls.length; i++) {
68
+ var name = cls[i].slice(1);
69
+ if (!(' ' + el.className + ' ').indexOf(' ' + name + ' ') > -1) return false;
70
+ }
71
+ }
72
+ /* id */
73
+ var id = rest.match(/#([\w-]+)/);
74
+ if (id && el.id !== id[1]) return false;
75
+ /* 属性 */
76
+ var attr = rest.match(/\[([\w-]+)(?:=["']?([^\]"']*)["']?)?\]/);
77
+ if (attr) {
78
+ var v = el.getAttribute(attr[1]);
79
+ if (v === null) return false;
80
+ if (attr[2] !== undefined && v !== attr[2]) return false;
81
+ }
82
+ return true;
83
+ }
84
+
85
+ /* ------------------------------------------------------------------------
86
+ * Array.from
87
+ * --------------------------------------------------------------------- */
88
+ if (!Array.from) {
89
+ Array.from = function (obj) {
90
+ var out = [];
91
+ if (obj === null || obj === undefined) return out;
92
+ if (typeof obj.length === 'number') {
93
+ for (var i = 0; i < obj.length; i++) out.push(obj[i]);
94
+ } else {
95
+ for (var k in obj) {
96
+ if (Object.prototype.hasOwnProperty.call(obj, k)) out.push(obj[k]);
97
+ }
98
+ }
99
+ return out;
100
+ };
101
+ }
102
+
103
+ /* ------------------------------------------------------------------------
104
+ * requestAnimationFrame(IE9 与极老 WebView 可能没有)
105
+ * --------------------------------------------------------------------- */
106
+ if (!global.requestAnimationFrame) {
107
+ global.requestAnimationFrame = function (cb) {
108
+ return global.setTimeout(function () {
109
+ cb(Date.now ? Date.now() : new Date().getTime());
110
+ }, 16);
111
+ };
112
+ global.cancelAnimationFrame = function (id) {
113
+ global.clearTimeout(id);
114
+ };
115
+ }
116
+ })(window);
@@ -0,0 +1,171 @@
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
+ <title>响应式 · 流体与断点</title>
7
+ <link rel="stylesheet" href="tokens.css">
8
+ <link rel="stylesheet" href="typography.css">
9
+ <link rel="stylesheet" href="../02-primitives/button/button.css">
10
+ <link rel="stylesheet" href="../02-primitives/input/input.css">
11
+ <link rel="stylesheet" href="../02-primitives/card/card.css">
12
+ <link rel="stylesheet" href="../01-tokens/page.css">
13
+ <style>
14
+ p { margin: 0 0 var(--sp-4); }
15
+ /* 实时读数条 */
16
+ #meter { position: sticky; top: 0; z-index: var(--z-header);
17
+ background: var(--surface); border-bottom: 1px solid var(--border-decor);
18
+ padding: var(--sp-3) var(--sp-5); display: flex; gap: var(--sp-5);
19
+ flex-wrap: wrap; font-family: var(--mono); /* 原写死 ui-monospace,统一走令牌 */ font-size: 12px; }
20
+ #meter b { color: var(--accent); }
21
+ .ruler { height: 14px; background: repeating-linear-gradient(90deg,
22
+ var(--text-tertiary) 0 1px, transparent 1px 8px); opacity: .45; }
23
+ /* 断点演示:容器随宽度自动改变列数 —— 这才是断点该干的活 */
24
+ .auto-grid { display: grid; gap: var(--sp-4);
25
+ grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
26
+ .fixed-demo { display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap;
27
+ padding: var(--sp-4); background: var(--surface);
28
+ border: 1px solid var(--border-decor); border-radius: var(--r-md); }
29
+ .swatch { height: var(--sp-4); background: var(--accent); border-radius: 3px; }
30
+ </style>
31
+ </head>
32
+ <body class="page page">
33
+
34
+ <div id="meter">
35
+ <span>视口 <b id="m-w">—</b></span>
36
+ <span>断点 <b id="m-bp">—</b></span>
37
+ <span>正文 <b id="m-base">—</b></span>
38
+ <span>h2 <b id="m-lg">—</b></span>
39
+ <span>区块间距 <b id="m-sp">—</b></span>
40
+ <span>按钮高 <b id="m-btn">—</b>(固定)</span>
41
+ </div>
42
+
43
+ <div class="wrap wrap--prose">
44
+
45
+ <h1>响应式:流体管尺寸,断点管结构</h1>
46
+
47
+ <div class="note note--warn">
48
+ <strong>把浏览器窗口拖窄看左边那根读数条。</strong>
49
+ 注意最后一项:<strong>按钮高度不变</strong> —— UI 尺寸不参与流体,
50
+ 变了会让控件在不同屏幕下大小不一、布局抖动。
51
+ </div>
52
+
53
+ <h2>一、该流体的:字号与间距</h2>
54
+ <p>
55
+ 这段正文的字号、上下间距、容器内边距都在随视口连续变化,
56
+ <strong>中间没有任何断点跳变</strong>。这就是 <code>clamp()</code> 的意义 ——
57
+ 尺寸问题用流体一次解决,不需要在每个断点里重复写一遍。
58
+ </p>
59
+ <p>
60
+ 第二段用来说明另一件事:<code>clamp()</code> 的上下限用的是 <strong>rem</strong> 不是 px。
61
+ 所以当你把浏览器默认字号调大(或页面缩放到 200%)时,
62
+ 文字会跟着一起变大 —— 如果用 px 锁死上下限,用户放到 200% 也涨不上去,
63
+ 这在 WCAG 1.4.4 里是不通过的。
64
+ </p>
65
+
66
+ <h2>二、不该流体的:控件尺寸</h2>
67
+ <div class="fixed-demo">
68
+ <button class="btn btn--md btn--primary">按钮</button>
69
+ <div class="field" style="width:180px">
70
+ <label class="field__label" for="fixed-1" style="font-size:var(--fs-xs)">输入框</label>
71
+ <input class="field__input" id="fixed-1" placeholder="拖窄看看">
72
+ </div>
73
+ <span style="font-size:var(--fs-xs); color:var(--text-tertiary)">
74
+ 拖窄窗口——它们<strong>不会</strong>跟着变小
75
+ </span>
76
+ </div>
77
+ <p style="margin-top:var(--sp-4)">
78
+ 判据很简单:<strong>这个值变了会不会导致"控件大小跟着变"?会 → 不要流体。</strong>
79
+ `--fs-xs` / `--fs-sm`(UI 文本)和 `--sp-1`~`--sp-3`(小间距)都属这一类。
80
+ </p>
81
+
82
+ <h2>三、该用断点的:布局结构</h2>
83
+ <p>
84
+ 下面三张卡片的列数随宽度改变。注意这里用的是
85
+ <code>repeat(auto-fit, minmax(...))</code> —— 它<strong>不需要任何断点</strong>,
86
+ 浏览器按容器实际能放几张卡就放几张。
87
+ </p>
88
+ <div class="auto-grid">
89
+ <div class="card card--flat"><div class="card__body">
90
+ <h3 class="card__title">单列</h3>
91
+ <p class="card__desc">窄屏时一张卡占满整行。</p></div></div>
92
+ <div class="card card--flat"><div class="card__body">
93
+ <h3 class="card__title">自动两列</h3>
94
+ <p class="card__desc">宽一点就自动放两张。</p></div></div>
95
+ <div class="card card--flat"><div class="card__body">
96
+ <h3 class="card__title">自动三列</h3>
97
+ <p class="card__desc">再宽一点放三张。<strong>中间没有断点跳变。</strong></p></div></div>
98
+ </div>
99
+
100
+ <h2>四、流体间距的直观效果</h2>
101
+ <p>下面这几条是 <code>--sp-4</code>(会流体)到 <code>--sp-8</code>:</p>
102
+ <div style="display:flex; flex-direction:column; gap:var(--sp-2)">
103
+ <div class="swatch" style="width:var(--sp-4)"></div>
104
+ <div class="swatch" style="width:var(--sp-5)"></div>
105
+ <div class="swatch" style="width:var(--sp-6)"></div>
106
+ <div class="swatch" style="width:var(--sp-7)"></div>
107
+ <div class="swatch" style="width:var(--sp-8)"></div>
108
+ </div>
109
+ <p style="margin-top:var(--sp-4)">
110
+ 而 <code>--sp-1</code>~<code>--sp-3</code>(4/8/12px)<strong>是固定的</strong> ——
111
+ 它们是元素内部的间距,参与缩放会让控件尺寸忽大忽小。
112
+ </p>
113
+
114
+ <h2>五、断点在这里</h2>
115
+ <p>
116
+ 断点只留给"必须换结构"的地方。本页的断点只有一个用途:
117
+ 读数条在窄屏换行。
118
+ </p>
119
+ <p style="font-size:var(--fs-xs); color:var(--text-secondary)">
120
+ 断点定义在 <code>tokens.css</code>:<code>30rem / 48rem / 64rem / 80rem</code>
121
+ (480 / 768 / 1024 / 1280px)。
122
+ <strong>用 rem 不用 px</strong> —— 用户调大字号时断点跟着变,这才是对的。<br><br>
123
+ 🔴 但 <code>clamp()</code> 和 CSS 变量<strong>都不能</strong>写进 media query 条件,
124
+ 所以 <code>@media (min-width: 48rem)</code> 里的数值仍然是字面值 ——
125
+ 令牌只是"文档与计算的单一出处"。
126
+ </p>
127
+
128
+ </div>
129
+
130
+ <script>
131
+ var probe = {
132
+ w: document.getElementById('m-w'),
133
+ bp: document.getElementById('m-bp'),
134
+ base: document.getElementById('m-base'),
135
+ lg: document.getElementById('m-lg'),
136
+ sp: document.getElementById('m-sp'),
137
+ btn: document.getElementById('m-btn')
138
+ };
139
+ var sample = document.createElement('div');
140
+ sample.style.cssText = 'position:absolute;visibility:hidden;pointer-events:none';
141
+ document.body.appendChild(sample);
142
+
143
+ function px(el, prop) { return Math.round(parseFloat(getComputedStyle(el)[prop])); }
144
+
145
+ function read() {
146
+ probe.w.textContent = window.innerWidth + 'px';
147
+ probe.bp.textContent =
148
+ window.innerWidth >= 1280 ? 'xl (80rem)' :
149
+ window.innerWidth >= 1024 ? 'lg (64rem)' :
150
+ window.innerWidth >= 768 ? 'md (48rem)' :
151
+ window.innerWidth >= 480 ? 'sm (30rem)' : '—';
152
+
153
+ var r = document.querySelector('.wrap');
154
+ sample.style.fontSize = 'var(--fs-base)';
155
+ probe.base.textContent = px(r, 'fontSize') + 'px';
156
+
157
+ var h2 = document.querySelectorAll('h2')[0];
158
+ probe.lg.textContent = px(h2, 'fontSize') + 'px';
159
+
160
+ var sp = document.querySelector('.swatch');
161
+ probe.sp.textContent = px(sp, 'width') + 'px';
162
+
163
+ var btn = document.querySelector('.btn');
164
+ probe.btn.textContent = px(btn, 'height') + 'px';
165
+ }
166
+
167
+ window.addEventListener('resize', read);
168
+ read();
169
+ </script>
170
+ </body>
171
+ </html>
@@ -0,0 +1,194 @@
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, viewport-fit=cover">
6
+ <meta name="color-scheme" content="light dark">
7
+ <title>滚动条 ·· frontend-lib</title>
8
+ <link rel="icon" href="data:,">
9
+ <link rel="stylesheet" href="tokens.css">
10
+ <link rel="stylesheet" href="typography.css">
11
+ <link rel="stylesheet" href="scrollbar.css">
12
+ <link rel="stylesheet" href="../01-tokens/page.css">
13
+ <style>
14
+ h3 { font-size: var(--fs-base); margin: var(--sp-4) 0 var(--sp-2); }
15
+ table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
16
+ th, td { text-align: left; padding: var(--sp-2) var(--sp-3);
17
+ border-bottom: 1px solid var(--border-decor); }
18
+ th { font-size: var(--fs-xs); color: var(--text-secondary); font-weight: 500; }
19
+ td.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
20
+ .ok { color: var(--success); }
21
+ .no { color: var(--danger); }
22
+ /* 🔴 表头是等宽字体标识符(scrollbar-width 等)不可断行,
23
+ 三列在 320px 视口下最小内容宽 325px > 可用 287px ⇒ 整页横向溢出。
24
+ 给表格一个横向滚动容器(而不是让字断行,标识符断行不可读)。 */
25
+ .table-wrap { overflow-x: auto; margin: 0 0 var(--sp-4); }
26
+
27
+ /* 演示面板:给滚动区一个固定高度 */
28
+ .panel {
29
+ height: 220px; /* 关键:不设高就看不出滚动条 */
30
+ border: 1px solid var(--border-decor);
31
+ border-radius: var(--r-md);
32
+ background: var(--surface);
33
+ }
34
+ .row { display: flex; align-items: center; gap: var(--sp-3);
35
+ padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border-decor);
36
+ font-size: var(--fs-sm); }
37
+ .row:last-child { border-bottom: 0; }
38
+ .row .n { font-family: var(--mono); color: var(--text-tertiary); width: 3em; }
39
+ .btn {
40
+ min-height: 44px; padding: var(--sp-2) var(--sp-4); font: inherit;
41
+ font-size: var(--fs-sm); color: var(--text-on-accent);
42
+ background: var(--accent); border: 0; border-radius: var(--r-sm);
43
+ cursor: pointer; -webkit-tap-highlight-color: transparent;
44
+ }
45
+ .btn--ghost { background: var(--surface); color: var(--text-primary);
46
+ border: 1px solid var(--border-control); }
47
+ </style>
48
+ </head>
49
+ <body class="page page">
50
+ <div class="wrap">
51
+
52
+ <h1>滚动条</h1>
53
+ <p class="lede">
54
+ <code></code>。判据见 WCAG 2.5.5 Target Size。
55
+ </p>
56
+
57
+ <h2 class="rule-top">1 · 基本用法</h2>
58
+ <p class="note">
59
+ 页面级滚动条<b>自动生效</b>(<code>scrollbar.css</code> 已经作用于 <code>html</code>)。
60
+ 局部滚动容器挂工具类 <code>.scroll-y</code>(纵向)/ <code>.scroll-x</code>(横向)/
61
+ <code>.scroll-thin</code>(次要场景,更细)。
62
+ </p>
63
+
64
+ <div class="panel scroll-y">
65
+ <div class="row"><span class="n">01</span>拖动这个面板右侧的滑块</div>
66
+ <div class="row"><span class="n">02</span>thumb 是<b>冷灰色胶囊</b></div>
67
+ <div class="row"><span class="n">03</span>track 是<b>透明</b>的</div>
68
+ <div class="row"><span class="n">04</span>hover 会变深一档</div>
69
+ <div class="row"><span class="n">05</span>没有默认的箭头按钮</div>
70
+ <div class="row"><span class="n">06</span>·</div>
71
+ <div class="row"><span class="n">07</span>·</div>
72
+ <div class="row"><span class="n">08</span>·</div>
73
+ <div class="row"><span class="n">09</span>·</div>
74
+ <div class="row"><span class="n">10</span>·</div>
75
+ <div class="row"><span class="n">11</span>·</div>
76
+ <div class="row"><span class="n">12</span>·</div>
77
+ <div class="row"><span class="n">13</span>到底了</div>
78
+ </div>
79
+
80
+ <h2 class="rule-top">2 · 关键:<code>scrollbar-gutter: stable</code></h2>
81
+ <p class="note">
82
+ <b>这是 某项目 / 某项目 踩过的坑</b>:内容动态增加时,滚动条出现 ⇒
83
+ 容器宽度被挤窄 ⇒ 整行内容<b>横向跳一下</b>。
84
+ <code>scrollbar-gutter: stable</code> 会<b>预先占位</b>,从根上避免。
85
+ <br><b>怎么验证</b>:看下面两列的<b>右边缘是否对齐</b>。
86
+ 点「加 5 条」后,右列不该移动。
87
+ </p>
88
+
89
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)">
90
+ <div>
91
+ <h3 style="margin-top:0">有 gutter(正确)</h3>
92
+ <div class="panel scroll-y" id="p1" style="height:140px">
93
+ <div class="row"><span class="n">1</span>甲</div>
94
+ <div class="row"><span class="n">2</span>乙</div>
95
+ </div>
96
+ <p class="note" style="font-size:var(--fs-xs)">
97
+ 右边缘:<span id="e1">—</span>
98
+ </p>
99
+ </div>
100
+ <div>
101
+ <h3 style="margin-top:0">无 gutter(会跳)</h3>
102
+ <div class="panel" id="p2" style="height:140px;overflow-y:auto">
103
+ <div class="row"><span class="n">1</span>甲</div>
104
+ <div class="row"><span class="n">2</span>乙</div>
105
+ </div>
106
+ <p class="note" style="font-size:var(--fs-xs)">
107
+ 右边缘:<span id="e2">—</span>
108
+ </p>
109
+ </div>
110
+ </div>
111
+
112
+ <p class="note">
113
+ <button class="btn" id="add" type="button">加 5 条</button>
114
+ <button class="btn btn--ghost" id="reset" type="button" style="margin-left:var(--sp-2)">重置</button>
115
+ </p>
116
+
117
+ <h2 class="rule-top">3 · 为什么两套 API 都要写</h2>
118
+ <div class="table-wrap">
119
+ <table>
120
+ <tr><th>API</th><th>支持</th><th>能给圆角 / hover</th></tr>
121
+ <tr><td><code>scrollbar-width</code><br><code>scrollbar-color</code></td>
122
+ <td>Firefox 64+ · Chrome 121+ · Safari 18.2+</td>
123
+ <td class="no">❌ 不能</td></tr>
124
+ <tr><td><code>::-webkit-scrollbar</code> 族</td>
125
+ <td>Chromium · WebKit(<b>Firefox 完全不支持</b>)</td>
126
+ <td class="ok">✅ 唯一方式</td></tr>
127
+ </table>
128
+ </div>
129
+ <p class="note">
130
+ ⇒ <b>标准属性打底、厂商伪元素增强</b>。只写一套 ⇒ 跨浏览器不一致。
131
+ </p>
132
+
133
+ <h2 class="rule-top">4 · 颜色是脚本算的,不是挑的</h2>
134
+ <div class="table-wrap">
135
+ <table>
136
+ <tr><th>令牌</th><th class="num">浅色</th><th class="num">暗色</th><th>WCAG 1.4.11</th></tr>
137
+ <tr><td><code>--scrollbar-thumb</code></td>
138
+ <td class="num">#7A808A</td><td class="num">#6E747E</td>
139
+ <td class="num ok">3.71:1 ~ 3.98:1 ✅</td></tr>
140
+ <tr><td><code>--scrollbar-thumb-hov</code></td>
141
+ <td class="num">#6E747E</td><td class="num">#7D828A</td>
142
+ <td class="num ok">4.24:1 ~ 4.71:1 ✅</td></tr>
143
+ </table>
144
+ </div>
145
+ <p class="note">
146
+ 🔴 第一版我随手挑了 <code>#B4BAC4</code>,门禁一算只有 <b>1.82:1</b> ⇒ 直接不合格。
147
+ 后来<b>用脚本反解</b>出满足 3:1 的值,同时保持<b>冷灰(蓝调)</b>
148
+ —— 本库的"高级感"来自冷灰,不能为了达标变成中性灰。
149
+ </p>
150
+
151
+ <h2 class="rule-top">5 · 移动端为什么不做定制</h2>
152
+ <p class="note">
153
+ iOS Safari <b>忽略</b> <code>::-webkit-scrollbar</code> 的外观声明,
154
+ 且滚动条只在滑动时短暂出现 ⇒ 写了也是白写。
155
+ 移动端真正需要的是<b>惯性滚动</b>(<code>-webkit-overflow-scrolling: touch</code>,已给)。
156
+ <br>⇒ <b>克制</b>本身也是通行标准的一部分。
157
+ </p>
158
+
159
+ </div>
160
+
161
+ <script>
162
+ // 演示 gutter:记录两列的右边缘,加内容后看是否移动
163
+ function edge() {
164
+ var a = document.getElementById('p1').getBoundingClientRect().right;
165
+ var b = document.getElementById('p2').getBoundingClientRect().right;
166
+ return { a: Math.round(a), b: Math.round(b) };
167
+ }
168
+ var base = edge();
169
+ document.getElementById('e1').textContent = base.a + 'px';
170
+ document.getElementById('e2').textContent = base.b + 'px';
171
+
172
+ var n = 2;
173
+ document.getElementById('add').addEventListener('click', function () {
174
+ ['p1', 'p2'].forEach(function (id) {
175
+ var p = document.getElementById(id);
176
+ for (var i = 0; i < 5; i++) {
177
+ n++;
178
+ var d = document.createElement('div');
179
+ d.className = 'row';
180
+ d.innerHTML = '<span class="n">' + (n < 10 ? '0' + n : n) + '</span>第 ' + n + ' 条';
181
+ p.appendChild(d);
182
+ }
183
+ });
184
+ var now = edge();
185
+ document.getElementById('e1').textContent = now.a + 'px' +
186
+ (now.a === base.a ? '(没动 ✅)' : '(移动了 ' + (now.a - base.a) + 'px ❌)');
187
+ document.getElementById('e2').textContent = now.b + 'px' +
188
+ (now.b === base.b ? '(没动 ✅)' : '(移动了 ' + (now.b - base.b) + 'px ❌)');
189
+ });
190
+
191
+ document.getElementById('reset').addEventListener('click', function () { location.reload(); });
192
+ </script>
193
+ </body>
194
+ </html>
@@ -0,0 +1,179 @@
1
+ /* ============================================================================
2
+ scrollbar.css — 滚动条(全局基础设施 ·
3
+ ----------------------------------------------------------------------------
4
+ 🔴 判据:WCAG 2.5.5 Target Size。
5
+ ============================================================================
6
+ ⭐ 依据(5 个权威来源口径一致)
7
+ ----------------------------------------------------------------------------
8
+ · **CSS Scrollbars Level 1(W3C,2018)**
9
+ scrollbar-width / scrollbar-color / scrollbar-gutter
10
+ 兼容性:Chrome 121+ · Firefox 64+ · Safari 18.2+(2024-12 转正)
11
+ · **`::-webkit-scrollbar` 伪元素族**(Chromium / WebKit 专属)
12
+ **唯一能给 thumb 加圆角与 hover 的方式**
13
+ ⚠️ Firefox **完全不支持** ⇒ 两套必须都写
14
+ · **WCAG 1.4.11 非文本对比**:thumb 对 track **≥ 3:1**
15
+ · **WCAG 2.5.5 目标大小**:滚动条拇指可拖动区不宜过小
16
+
17
+ ⚠️ **移动端为什么不写 iOS 专用样式**
18
+ iOS Safari 会**忽略**自定义滚动条样式,且滚动条只在滑动时短暂出现
19
+ ⇒ 写了也是白写,还会多几百字节。
20
+ ⇒ **在移动端克制**,把功夫花在桌面与平板上。
21
+
22
+ ⚠️ **为什么不全局 `*` 强制**
23
+ 代码块、横向卡片、下拉内部都有自己的滚动语境,
24
+ 全局一刀切会把它们也改掉 ⇒ 反而更乱。
25
+ ⇒ 做法:**令牌 + 工具类**,由使用者显式挂。
26
+ ========================================================================== */
27
+
28
+ /* ============================================================================
29
+ 一、标准属性(打底)
30
+ Firefox 与新版 Chrome / Safari 都认这三行。
31
+ ⚠️ 放在最前面 —— 后面被厂商伪元素**增强**(Chromium 里伪元素优先级更高)。
32
+ ========================================================================== */
33
+
34
+ /* 页面的纵向滚动条 */
35
+ html {
36
+ scrollbar-width: thin; /* auto | thin | none */
37
+ scrollbar-color: var(--scrollbar-thumb) transparent; /* thumb track */
38
+ }
39
+
40
+ /* 🔴 工具类:需要稳定滚动容器的区域挂这个
41
+ —— `gutter: stable` **预先占位**,滚动条出现时内容不会横向跳。
42
+ ⚠️ 这正是动态列表(某项目 / 某项目 踩过的坑)最需要的一条。 */
43
+ .scroll-y {
44
+ overflow-y: auto;
45
+ -webkit-overflow-scrolling: touch; /* iOS 惯性滚动(旧版 WebView 需要)*/
46
+ scrollbar-width: thin;
47
+ scrollbar-color: var(--scrollbar-thumb) transparent;
48
+ scrollbar-gutter: stable; /* 🔴 防横向跳变 */
49
+ }
50
+
51
+ .scroll-x {
52
+ overflow-x: auto;
53
+ scrollbar-width: thin;
54
+ scrollbar-color: var(--scrollbar-thumb) transparent;
55
+ scrollbar-gutter: stable;
56
+ }
57
+
58
+ .scroll-thin {
59
+ scrollbar-width: thin;
60
+ scrollbar-color: var(--scrollbar-thumb) transparent;
61
+ }
62
+
63
+ /* ============================================================================
64
+ 二、厂商伪元素(增强:圆角 + hover)
65
+ Chromium / WebKit 专属。Firefox 会整段忽略 —— 这正是我们要标准打底的原因。
66
+ ========================================================================== */
67
+
68
+ html::-webkit-scrollbar {
69
+ width: var(--scrollbar-size);
70
+ height: var(--scrollbar-size);
71
+ }
72
+
73
+ html::-webkit-scrollbar-track {
74
+ /* 🔴 track 透明:轨道代表"没有内容的区域",画出来等于视觉噪声 */
75
+ background: transparent;
76
+ }
77
+
78
+ /* 🔴 关键技巧:用 `border` 而不是 `margin` 给 thumb 让出内边距
79
+ —— border 的颜色与 track 相同 ⇒ 视觉上就是"缩进",而实际可拖动区没变小。 */
80
+ html::-webkit-scrollbar-thumb {
81
+ background: var(--scrollbar-thumb);
82
+ border: 3px solid transparent; /* = size − 滑块厚度 */
83
+ background-clip: content-box; /* ← 关键:让 border 真正"陷进去" */
84
+ border-radius: var(--scrollbar-radius);
85
+ }
86
+
87
+ html::-webkit-scrollbar-thumb:hover {
88
+ background: var(--scrollbar-thumb-hov);
89
+ background-clip: content-box;
90
+ }
91
+
92
+ /* 去掉默认箭头按钮(现代设计都不要)*/
93
+ html::-webkit-scrollbar-button {
94
+ display: none;
95
+ }
96
+
97
+ /* 角落:两轴交汇处。透明才不出现一小块方形色块 */
98
+ html::-webkit-scrollbar-corner {
99
+ background: transparent;
100
+ }
101
+
102
+ .scroll-y::-webkit-scrollbar,
103
+ .scroll-x::-webkit-scrollbar,
104
+ .scroll-thin::-webkit-scrollbar {
105
+ width: var(--scrollbar-size);
106
+ height: var(--scrollbar-size);
107
+ }
108
+
109
+ .scroll-y::-webkit-scrollbar-track,
110
+ .scroll-x::-webkit-scrollbar-track,
111
+ .scroll-thin::-webkit-scrollbar-track {
112
+ background: transparent;
113
+ }
114
+
115
+ .scroll-y::-webkit-scrollbar-thumb,
116
+ .scroll-x::-webkit-scrollbar-thumb,
117
+ .scroll-thin::-webkit-scrollbar-thumb {
118
+ background: var(--scrollbar-thumb);
119
+ border: 3px solid transparent;
120
+ background-clip: content-box;
121
+ border-radius: var(--scrollbar-radius);
122
+ }
123
+
124
+ .scroll-y::-webkit-scrollbar-thumb:hover,
125
+ .scroll-x::-webkit-scrollbar-thumb:hover,
126
+ .scroll-thin::-webkit-scrollbar-thumb:hover {
127
+ background: var(--scrollbar-thumb-hov);
128
+ background-clip: content-box;
129
+ }
130
+
131
+ .scroll-y::-webkit-scrollbar-button,
132
+ .scroll-x::-webkit-scrollbar-button,
133
+ .scroll-thin::-webkit-scrollbar-button {
134
+ display: none;
135
+ }
136
+
137
+ .scroll-y::-webkit-scrollbar-corner,
138
+ .scroll-x::-webkit-scrollbar-corner,
139
+ .scroll-thin::-webkit-scrollbar-corner {
140
+ background: transparent;
141
+ }
142
+
143
+ /* --- 次要场景:代码块等用更细的 --- */
144
+ .scroll-thin::-webkit-scrollbar { width: var(--scrollbar-size-thin); }
145
+ /* 🔴 原写 `border-width: 2px solid transparent` ⇒ 整条被丢弃。
146
+ 只补 border 不够:底色默认画到边框底下 ⇒ 必须配 background-clip。 */
147
+ .scroll-thin::-webkit-scrollbar-thumb {
148
+ border: 2px solid transparent;
149
+ background-clip: content-box;
150
+ }
151
+
152
+ /* ============================================================================
153
+ 三、移动端:只保留惯性滚动,不做外观定制
154
+ ----------------------------------------------------------------------------
155
+ ⚠️ iOS 会忽略 ::-webkit-scrollbar 的全部外观声明。
156
+ ⚠️ 真正需要的是**惯性滚动**(已在上面的 .scroll-y 里给了)。
157
+ ⚠️ 这里**刻意不写媒体查询** —— iOS 会忽略外观声明,
158
+ 写了既是白写,也让人以为"漏做了"。真正需要的惯性滚动已在上面给了。
159
+ ========================================================================== */
160
+
161
+ /* ============================================================================
162
+ 🔴 触屏 hover 门控(hover-gate 门禁要求)
163
+ --------------------------------------------------------------------------
164
+ 为什么必须做:`:hover` 在**触屏设备上会"粘住"** ——
165
+ 点一下按钮,hover 状态一直保持,要点别处才消失
166
+ (移动端根本没有"移开鼠标"这个动作)。
167
+
168
+ 正确做法:只对**真能悬停的设备**(鼠标 / 触控板)生效。
169
+ 依据:Emil Kowalski 的设计规则(ui-benchmark-redesign 技能收录)。
170
+ ========================================================================== */
171
+ @media (hover: hover) and (pointer: fine) {
172
+ html::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hov);
173
+ background-clip: content-box; }
174
+
175
+ .scroll-y::-webkit-scrollbar-thumb:hover,
176
+ .scroll-x::-webkit-scrollbar-thumb:hover,
177
+ .scroll-thin::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hov);
178
+ background-clip: content-box; }
179
+ }