weifuwu 0.70.0 → 0.71.0

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/dist/index.js CHANGED
@@ -4787,11 +4787,18 @@ function extractParams(path, match) {
4787
4787
  const m = path.match(match.re);
4788
4788
  if (m) {
4789
4789
  for (let i = 0; i < match.keys.length; i++) {
4790
- params[match.keys[i]] = decodeURIComponent(m[i + 1]);
4790
+ params[match.keys[i]] = safeDecode(m[i + 1]);
4791
4791
  }
4792
4792
  }
4793
4793
  return params;
4794
4794
  }
4795
+ function safeDecode(s) {
4796
+ try {
4797
+ return decodeURIComponent(s);
4798
+ } catch {
4799
+ return s;
4800
+ }
4801
+ }
4795
4802
 
4796
4803
  // src/ui/ssr-page.ts
4797
4804
  var DEFAULT_TEMPLATE = (p) => `<!DOCTYPE html>
@@ -46,6 +46,7 @@
46
46
  --wf-dark-bg-hover: rgba(255, 255, 255, 0.06); /* 暗色 hover 反白 */
47
47
  --wf-dark-surface-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
48
48
  --wf-dark-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
49
+ --wf-dark-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.3);
49
50
  --wf-dark-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
50
51
  --wf-dark-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
51
52
  --wf-dark-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4);
@@ -82,6 +83,7 @@
82
83
 
83
84
  /* ── 阴影 ── */
84
85
  --wf-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
86
+ --wf-shadow-inset: inset 0 1px 2px rgba(0,0,0,0.04); /* 内阴影:凹槽/轨道深度(Switch/ThemeSwitch) */
85
87
  --wf-shadow: 0 1px 3px rgba(0,0,0,0.08);
86
88
  --wf-shadow-md: 0 4px 12px rgba(0,0,0,0.1);
87
89
  --wf-shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
@@ -222,6 +224,7 @@
222
224
 
223
225
  /* ── 浮层遮罩(Modal/Drawer overlay,暗色经 --wf-dark-overlay 映射) ── */
224
226
  --wf-overlay: rgba(0, 0, 0, 0.4);
227
+ --wf-overlay-blur: 4px; /* 遮罩毛玻璃(弹层现代质感,不支持 backdrop-filter 的环境自动降级为纯色) */
225
228
 
226
229
  /* ── 表单控件 ── */
227
230
  --wf-accent-color: var(--wf-color-primary);
@@ -307,6 +310,7 @@
307
310
  --wf-shadow: var(--wf-dark-shadow);
308
311
  --wf-shadow-md: var(--wf-dark-shadow-md);
309
312
  --wf-shadow-lg: var(--wf-dark-shadow-lg);
313
+ --wf-shadow-inset: var(--wf-dark-shadow-inset);
310
314
 
311
315
  --wf-color-primary-text: var(--wf-dark-brand-500);
312
316
  --wf-color-success-text: var(--wf-dark-green-500);
@@ -357,6 +361,7 @@
357
361
  --wf-shadow: var(--wf-dark-shadow);
358
362
  --wf-shadow-md: var(--wf-dark-shadow-md);
359
363
  --wf-shadow-lg: var(--wf-dark-shadow-lg);
364
+ --wf-shadow-inset: var(--wf-dark-shadow-inset);
360
365
 
361
366
  --wf-color-primary-text: var(--wf-dark-brand-500);
362
367
  --wf-color-success-text: var(--wf-dark-green-500);
@@ -377,6 +382,36 @@
377
382
  box-sizing: border-box;
378
383
  }
379
384
 
385
+ /* 选区色:primary 调(文字反白,与品牌一致) */
386
+ ::selection {
387
+ background: var(--wf-color-primary-bg);
388
+ color: var(--wf-color-primary-text);
389
+ }
390
+
391
+ /* 主题滚动条(全局——所有滚动区一致;Firefox thin + WebKit 样式) */
392
+ * {
393
+ scrollbar-width: thin;
394
+ scrollbar-color: var(--wf-color-border-dark) transparent;
395
+ }
396
+ ::-webkit-scrollbar {
397
+ width: 8px;
398
+ height: 8px;
399
+ }
400
+ ::-webkit-scrollbar-track {
401
+ background: transparent;
402
+ }
403
+ ::-webkit-scrollbar-thumb {
404
+ background: var(--wf-color-border-dark);
405
+ border-radius: var(--wf-radius-sm);
406
+ border: 2px solid transparent;
407
+ background-clip: padding-box;
408
+ }
409
+ ::-webkit-scrollbar-thumb:hover {
410
+ background: var(--wf-color-text-secondary);
411
+ background-clip: padding-box;
412
+ border: 2px solid transparent;
413
+ }
414
+
380
415
  :root {
381
416
  /* 原生控件/滚动条跟随主题;暗色激活时在 _dark.css 中切为 dark */
382
417
  color-scheme: light;
@@ -447,11 +482,20 @@ button {
447
482
  padding: var(--wf-control-pad-y) var(--wf-space-md);
448
483
  min-height: var(--wf-control-height);
449
484
  cursor: pointer;
485
+ transition: border-color var(--wf-dur-fast) var(--wf-ease-out),
486
+ color var(--wf-dur-fast) var(--wf-ease-out),
487
+ box-shadow var(--wf-dur-fast) var(--wf-ease-out),
488
+ background-color var(--wf-dur-fast) var(--wf-ease-out),
489
+ filter var(--wf-dur-fast) var(--wf-ease-out);
450
490
  }
451
491
  button:hover {
452
492
  border-color: var(--wf-color-primary);
453
493
  color: var(--wf-color-primary-text);
454
494
  }
495
+ button:active:not(:disabled),
496
+ [role="button"]:active {
497
+ filter: brightness(0.95); /* 按压反馈:不偏移布局,适用任意宽度(block/inline) */
498
+ }
455
499
 
456
500
  /* 键盘焦点:button 与 [tabindex] 统一 focus ring(仅键盘触发,鼠标点击不闪) */
457
501
  button:focus-visible,
@@ -460,6 +504,15 @@ button:focus-visible,
460
504
  box-shadow: var(--wf-focus-ring);
461
505
  }
462
506
 
507
+ [role="button"] {
508
+ cursor: pointer;
509
+ transition: background-color var(--wf-dur-fast) var(--wf-ease-out),
510
+ border-color var(--wf-dur-fast) var(--wf-ease-out),
511
+ box-shadow var(--wf-dur-fast) var(--wf-ease-out),
512
+ color var(--wf-dur-fast) var(--wf-ease-out),
513
+ filter var(--wf-dur-fast) var(--wf-ease-out);
514
+ }
515
+
463
516
  /* 触屏设备(coarse pointer):交互目标提升到 44px(Apple HIG / WCAG 2.5.8) */
464
517
  @media (pointer: coarse) {
465
518
  button,
@@ -571,10 +624,13 @@ body.wf-dragging {
571
624
  }
572
625
 
573
626
  @layer layout {
574
- /* stack — 纵向堆叠 */
627
+ /* stack — 纵向堆叠(L3:gap 默认值 :where() 零优先级——wf-gap-* 等覆盖工具
628
+ 靠优先级获胜,不再依赖 import 顺序) */
575
629
  .wf-stack {
576
630
  display: flex;
577
631
  flex-direction: column;
632
+ }
633
+ :where(.wf-stack) {
578
634
  gap: var(--wf-gap, var(--wf-gap-md));
579
635
  }
580
636
  @media (min-width: 640px) { .wf-stack\@sm { flex-direction: row; align-items: var(--wf-align, center); } }
@@ -585,6 +641,8 @@ body.wf-dragging {
585
641
  .wf-stack-reverse {
586
642
  display: flex;
587
643
  flex-direction: column-reverse;
644
+ }
645
+ :where(.wf-stack-reverse) {
588
646
  gap: var(--wf-gap, var(--wf-gap-md));
589
647
  }
590
648
  @media (min-width: 640px) { .wf-stack-reverse\@sm { flex-direction: row-reverse; align-items: var(--wf-align, center); } }
@@ -593,10 +651,13 @@ body.wf-dragging {
593
651
  }
594
652
 
595
653
  @layer layout {
596
- /* row — 横向排列 */
654
+ /* row — 横向排列(L3:gap/align 默认值 :where() 零优先级——wf-gap-*、wf-top 等
655
+ 覆盖工具靠优先级获胜,不再依赖 import 顺序) */
597
656
  .wf-row {
598
657
  display: flex;
599
658
  flex-wrap: wrap;
659
+ }
660
+ :where(.wf-row) {
600
661
  gap: var(--wf-gap, var(--wf-gap-md));
601
662
  align-items: var(--wf-align, center);
602
663
  }
@@ -609,6 +670,8 @@ body.wf-dragging {
609
670
  display: flex;
610
671
  flex-direction: row-reverse;
611
672
  flex-wrap: wrap;
673
+ }
674
+ :where(.wf-row-reverse) {
612
675
  gap: var(--wf-gap, var(--wf-gap-md));
613
676
  align-items: var(--wf-align, center);
614
677
  }
@@ -618,10 +681,12 @@ body.wf-dragging {
618
681
  }
619
682
 
620
683
  @layer layout {
621
- /* split — 两端展开 */
684
+ /* split — 两端展开(L3:align/gap 默认值 :where() 零优先级) */
622
685
  .wf-split {
623
686
  display: flex;
624
687
  justify-content: space-between;
688
+ }
689
+ :where(.wf-split) {
625
690
  align-items: var(--wf-align, center);
626
691
  gap: var(--wf-gap, var(--wf-gap-md));
627
692
  }
@@ -641,10 +706,12 @@ body.wf-dragging {
641
706
  }
642
707
 
643
708
  @layer layout {
644
- /* between — 两端对齐(行内垂直居中) */
709
+ /* between — 两端对齐(行内垂直居中;L3:align/gap 默认值 :where() 零优先级) */
645
710
  .wf-between {
646
711
  display: flex;
647
712
  justify-content: space-between;
713
+ }
714
+ :where(.wf-between) {
648
715
  align-items: var(--wf-align, center);
649
716
  gap: var(--wf-gap, var(--wf-gap-md));
650
717
  }
@@ -654,57 +721,69 @@ body.wf-dragging {
654
721
  }
655
722
 
656
723
  @layer layout {
657
- /* right — 靠右对齐 */
724
+ /* right — 靠右对齐(L3:align/gap 默认值 :where() 零优先级) */
658
725
  .wf-right {
659
726
  display: flex;
660
727
  justify-content: flex-end;
728
+ }
729
+ :where(.wf-right) {
661
730
  align-items: var(--wf-align, center);
662
731
  gap: var(--wf-gap, var(--wf-gap-md));
663
732
  }
664
733
  }
665
734
 
666
735
  @layer layout {
667
- /* top — 顶部对齐 */
736
+ /* top — 顶部对齐(L3:gap 默认值 :where() 零优先级;align-items 是身份属性) */
668
737
  .wf-top {
669
738
  display: flex;
670
739
  align-items: flex-start;
740
+ }
741
+ :where(.wf-top) {
671
742
  gap: var(--wf-gap, var(--wf-gap-md));
672
743
  }
673
744
  }
674
745
 
675
746
  @layer layout {
676
- /* bottom — 底部对齐 */
747
+ /* bottom — 底部对齐(L3:gap 默认值 :where() 零优先级;align-items 是身份属性) */
677
748
  .wf-bottom {
678
749
  display: flex;
679
750
  align-items: flex-end;
751
+ }
752
+ :where(.wf-bottom) {
680
753
  gap: var(--wf-gap, var(--wf-gap-md));
681
754
  }
682
755
  }
683
756
 
684
757
  @layer layout {
685
- /* stretch — 拉伸对齐 */
758
+ /* stretch — 拉伸对齐(L3:gap 默认值 :where() 零优先级;align-items 是身份属性) */
686
759
  .wf-stretch {
687
760
  display: flex;
688
761
  align-items: stretch;
762
+ }
763
+ :where(.wf-stretch) {
689
764
  gap: var(--wf-gap, var(--wf-gap-md));
690
765
  }
691
766
  }
692
767
 
693
768
  @layer layout {
694
- /* around — 均匀环绕分布 */
769
+ /* around — 均匀环绕分布(L3:align/gap 默认值 :where() 零优先级) */
695
770
  .wf-around {
696
771
  display: flex;
697
772
  justify-content: space-around;
773
+ }
774
+ :where(.wf-around) {
698
775
  align-items: var(--wf-align, center);
699
776
  gap: var(--wf-gap, var(--wf-gap-md));
700
777
  }
701
778
  }
702
779
 
703
780
  @layer layout {
704
- /* evenly — 均匀分布 */
781
+ /* evenly — 均匀分布(L3:align/gap 默认值 :where() 零优先级) */
705
782
  .wf-evenly {
706
783
  display: flex;
707
784
  justify-content: space-evenly;
785
+ }
786
+ :where(.wf-evenly) {
708
787
  align-items: var(--wf-align, center);
709
788
  gap: var(--wf-gap, var(--wf-gap-md));
710
789
  }
@@ -720,7 +799,9 @@ body.wf-dragging {
720
799
  }
721
800
 
722
801
  @layer layout {
723
- /* fixed — 固定不伸缩 */
802
+ /* flex-none — 固定不伸缩(推荐名;wf-fixed 为兼容别名——易与 position:fixed 混淆,
803
+ position:fixed 请用 wf-pin) */
804
+ .wf-flex-none,
724
805
  .wf-fixed {
725
806
  flex: none;
726
807
  }
@@ -881,19 +962,24 @@ body.wf-dragging {
881
962
  }
882
963
 
883
964
  @layer layout {
884
- /* grid — 二维网格 */
965
+ /* grid — 二维网格(L3:gap 默认值 :where() 零优先级) */
885
966
  .wf-grid {
886
967
  display: grid;
887
968
  grid-template-columns: var(--wf-cols, repeat(auto-fill, minmax(280px, 1fr)));
969
+ }
970
+ :where(.wf-grid) {
888
971
  gap: var(--wf-gap, var(--wf-gap-lg));
889
972
  }
890
973
  }
891
974
 
892
975
  @layer layout {
893
- /* cluster — 换行簇 */
976
+ /* cluster — 换行簇(L3:justify/align/gap 默认值 :where() 零优先级——
977
+ wf-right 等分布工具叠加时靠优先级获胜) */
894
978
  .wf-cluster {
895
979
  display: flex;
896
980
  flex-wrap: wrap;
981
+ }
982
+ :where(.wf-cluster) {
897
983
  gap: var(--wf-gap, var(--wf-gap-sm));
898
984
  justify-content: var(--wf-justify, center);
899
985
  align-items: var(--wf-align, center);
@@ -901,7 +987,7 @@ body.wf-dragging {
901
987
  }
902
988
 
903
989
  @layer layout {
904
- /* scroll — 可滚动容器 */
990
+ /* scroll — 可滚动容器(全局滚动条样式见 _base.css;此类仅标记语义 + overflow) */
905
991
  .wf-scroll {
906
992
  overflow: auto;
907
993
  }
@@ -963,6 +1049,19 @@ body.wf-dragging {
963
1049
  }
964
1050
  }
965
1051
 
1052
+ @layer utilities {
1053
+ /* flex — 弹性盒 display 工具(补全 display 族:block/inline/inline-block/contents/hidden/flex)
1054
+ * 核心场景:响应式显隐的 flex 容器——`wf-hidden wf-flex@lg`(窄隐宽显且保持 flex 布局)。
1055
+ * 不可用 wf-block@lg 替代:block 会覆盖 wf-stack 的 display:flex,
1056
+ * 致 flex-direction/gap/mt-auto 全部静默失效(layouts-demo 侧栏 mt-auto 实测踩坑)。 */
1057
+ .wf-flex {
1058
+ display: flex;
1059
+ }
1060
+ @media (min-width: 640px) { .wf-flex\@sm { display: flex; } }
1061
+ @media (min-width: 768px) { .wf-flex\@md { display: flex; } }
1062
+ @media (min-width: 1024px) { .wf-flex\@lg { display: flex; } }
1063
+ }
1064
+
966
1065
  @layer layout {
967
1066
  /* container — 内容宽度约束居中 */
968
1067
  /* width: 100% 必须:margin: 0 auto 在 flex 父中阻止 stretch(宽度=内容)
@@ -992,10 +1091,13 @@ body.wf-dragging {
992
1091
 
993
1092
  @layer layout {
994
1093
  /* nowrap — 不换行(只管 flex-wrap——不定义 align-items,避免与
995
- wf-stretch 等对齐工具叠加冲突:双份 CSS 下 nowrap 后置曾覆盖 stretch) */
1094
+ wf-stretch 等对齐工具叠加冲突:双份 CSS 下 nowrap 后置曾覆盖 stretch;
1095
+ L3:gap 默认值 :where() 零优先级) */
996
1096
  .wf-nowrap {
997
1097
  display: flex;
998
1098
  flex-wrap: nowrap;
1099
+ }
1100
+ :where(.wf-nowrap) {
999
1101
  gap: var(--wf-gap, var(--wf-gap-md));
1000
1102
  }
1001
1103
  }
@@ -1038,6 +1140,20 @@ body.wf-dragging {
1038
1140
  .wf-py-xl { padding-top: var(--wf-space-xl); padding-bottom: var(--wf-space-xl); }
1039
1141
  .wf-py-2xl { padding-top: var(--wf-space-2xl); padding-bottom: var(--wf-space-2xl); }
1040
1142
 
1143
+ /* ── padding: 单侧顶/底(幽灵类防线抓出 wf-pt-md/wf-pb-md 误用——补全家族) ── */
1144
+ .wf-pt-xs { padding-top: var(--wf-space-xs); }
1145
+ .wf-pt-sm { padding-top: var(--wf-space-sm); }
1146
+ .wf-pt-md { padding-top: var(--wf-space-md); }
1147
+ .wf-pt-lg { padding-top: var(--wf-space-lg); }
1148
+ .wf-pt-xl { padding-top: var(--wf-space-xl); }
1149
+ .wf-pt-2xl { padding-top: var(--wf-space-2xl); }
1150
+ .wf-pb-xs { padding-bottom: var(--wf-space-xs); }
1151
+ .wf-pb-sm { padding-bottom: var(--wf-space-sm); }
1152
+ .wf-pb-md { padding-bottom: var(--wf-space-md); }
1153
+ .wf-pb-lg { padding-bottom: var(--wf-space-lg); }
1154
+ .wf-pb-xl { padding-bottom: var(--wf-space-xl); }
1155
+ .wf-pb-2xl { padding-bottom: var(--wf-space-2xl); }
1156
+
1041
1157
  /* ── margin:重置 ── */
1042
1158
  .wf-m-0 { margin: 0; }
1043
1159
  .wf-p-0 { padding: 0; }
@@ -1067,6 +1183,8 @@ body.wf-dragging {
1067
1183
  /* ── margin: auto(居中) ── */
1068
1184
  .wf-mx-auto { margin-left: auto; margin-right: auto; }
1069
1185
  .wf-my-auto { margin-top: auto; margin-bottom: auto; }
1186
+ .wf-mt-auto { margin-top: auto; } /* flex 列推底(侧栏 footer——曾写不存在类静默失效) */
1187
+ .wf-mb-auto { margin-bottom: auto; }
1070
1188
 
1071
1189
  /* ── gap:直接设置 gap 属性(不设 --wf-gap 变量——变量会继承污染子孙:
1072
1190
  壳 root wf-gap-none 曾让 KPI grid 的 gap: var(--wf-gap) = 0 卡无间距) ── */
@@ -2,14 +2,14 @@
2
2
 
3
3
  > 本页为 weifuwu 官方文档拆分页 · [返回 README](../README.md)
4
4
 
5
- 92 个 HTML 原语组件。每个是 `(_init, ctx) => (props) => VNode`(两阶段组件,与前端框架同一模型),引用 `--wf-*` CSS 变量做主题。另含 `confirm()` / `toast()` 命令式中间件。
5
+ 109 个 HTML 原语组件。每个是 `(_init, ctx) => (props) => VNode`(两阶段组件,与前端框架同一模型),引用 `--wf-*` CSS 变量做主题。另含 `confirm()` / `toast()` 命令式中间件。
6
6
 
7
7
  > **组件速查(weifuwu 组件 ↔ antd / Element Plus / shadcn-ui 对应 + 迁移示例)**:见 [`docs/components-map.md`](components-map.md)——从其他组件库迁来的开发者按功能直接找对应组件。
8
8
  > **自定义组件开发**:见 [docs/custom-components.md](custom-components.md)——usePopup/useControlled/对话框/AI 组件/类型纪律逐步指南。
9
9
 
10
10
  ```ts
11
11
  import { Button, Input, Table, Modal, Toast } from 'weifuwu/components'
12
- import 'weifuwu/components/style.css' // 包含 Token + 67 布局原语 + 组件样式,一次性引入
12
+ import 'weifuwu/components/style.css' // 包含 Token + 57 布局原语 + 136 工具类 + 组件样式,一次性引入
13
13
  ```
14
14
 
15
15
  ### 使用示例
package/docs/layout.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 本页为 weifuwu 官方文档拆分页 · [返回 README](../README.md)
4
4
 
5
- 纯 CSS 布局原语 + 工具类 + 143 个主题 Token。不绑定任何 JS 框架。
5
+ 纯 CSS 57 个布局原语 + 136 个工具类 + 167 个主题 Token。不绑定任何 JS 框架。
6
6
 
7
7
  > **学习路径与命名规范**:见 [`design/style-guide.md`](../design/style-guide.md)——统一语法 `wf-<域>-<名>`、三档学习(组件 → 10 核心原语 → 完整速查)、场景速查、变量定制。
8
8
 
@@ -25,7 +25,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
25
25
 
26
26
  也支持相对路径:`ctx.ui.css('./src/style.css')`。
27
27
 
28
- ## 70 个布局原语
28
+ ## 57 个布局原语
29
29
 
30
30
  | 类别 | 原语 | 效果 |
31
31
  |------|------|------|
@@ -44,11 +44,12 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
44
44
  | | `wf-bottom` | align-items: flex-end |
45
45
  | | `wf-stretch` | align-items: stretch |
46
46
  | **弹性** | `wf-fill` | flex: 1 + min-width: 0 |
47
- | | `wf-fixed` | flex: none |
47
+ | | `wf-flex-none` | flex: none(`wf-fixed` 为兼容别名——易与 position:fixed 混淆,推荐新名) |
48
48
  | | `wf-auto` | flex: auto |
49
49
  | | `wf-shrink` | min-width/height: 0 |
50
50
  | **Z轴** | `wf-cover` | position: fixed + inset: 0 |
51
- | | `wf-pop` | position: absolute |
51
+ | | `wf-pin` | position: fixed(角标/浮层锚点,坐标由 inline 控制——相对视口) |
52
+ | | `wf-pop` | position: absolute(容器内角标:配合 `wf-layer`/`wf-anchor` 父级) |
52
53
  | | `wf-anchor` | position: relative |
53
54
  | | `wf-layer` | position: relative + z-index |
54
55
  | | `wf-sticky` | position: sticky |
@@ -60,17 +61,24 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
60
61
  | | `wf-scroll` | overflow: auto |
61
62
  | | `wf-clip` | overflow: hidden |
62
63
  | **显隐** | `wf-hidden` `wf-hidden@sm/md/lg` | display: none |
64
+ | | `wf-flex` `wf-flex@sm/md/lg` | display: flex(响应式显隐的 flex 容器恢复用——见「组合规则」) |
63
65
  | | `wf-block` `wf-block@sm/md/lg` | display: block |
64
66
  | | `wf-inline` | display: inline |
65
67
  | | `wf-inline-block` | display: inline-block |
66
68
  | | `wf-contents` | display: contents |
67
- | **间距** | `wf-p-*` / `wf-px-*` / `wf-py-*`(xs~2xl) | padding:全/水平/垂直,引用 `--wf-space-*` |
69
+ | | `wf-dim` | opacity 0.7(运行中/历史态淡化) |
70
+ | **间距** | `wf-p-*` / `wf-px-*` / `wf-py-*`(xs~2xl,另有 `wf-p-0`) | padding:全/水平/垂直,引用 `--wf-space-*` |
71
+ | | `wf-m-0` | margin: 0 |
68
72
  | | `wf-mt-*` / `wf-mb-*` / `wf-my-*`(xs~2xl) | margin:top/bottom/垂直 |
69
- | | `wf-mx-auto` / `wf-my-auto` | margin: auto 居中 |
73
+ | | `wf-pt-*` / `wf-pb-*`(xs~2xl) | padding:单侧顶/底(分隔条场景) |
74
+ | | `wf-mx-auto` / `wf-my-auto` / `wf-mt-auto` / `wf-mb-auto` | margin: auto 居中 / flex 列推底 |
70
75
  | | `wf-gap-*`(xs~2xl) | 为 flex/grid 原语设置 `--wf-gap` |
71
76
  | **尺寸** | `wf-w-full` / `wf-h-full` / `wf-w-auto` | 宽/高 100%、auto |
77
+ | | `wf-w-xs…xl` / `wf-w-max` / `wf-w-md-auto` | 固定宽阶(`--wf-space-*` 派生)/ max-content / md 起 auto |
72
78
  | **边框** | `wf-border` / `wf-border-t/b/l/r` | 1px 边框(`--wf-border-width` + `--wf-color-border`) |
73
- | **面工具** | `wf-bg-secondary/tertiary/brand/success/warning/error/info` | 语义背景色(`--wf-color-*-bg`) |
79
+ | **面工具** | `wf-bg-secondary/tertiary/brand/success/warning/error/info`(`wf-bg-primary` 为 brand 别名) | 语义背景色(`--wf-color-*-bg`) |
80
+ | | `wf-elevate` | 抬升阴影(`--wf-shadow-*`) |
81
+ | | `wf-panel-in` | 内凹面板(次级背景 + 内边距) |
74
82
  | | `wf-pill` | 胶囊圆角(999px,状态徽章/标签/色块) |
75
83
  | | `wf-rounded-sm` `wf-rounded` `wf-rounded-md` `wf-rounded-lg` | 圆角工具(`--wf-radius-*`) |
76
84
  | **气泡** | `wf-bubble` / `wf-bubble--own` / `wf-bubble--ai` | 聊天气泡(pre-wrap + 折行内建) |
@@ -84,13 +92,33 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
84
92
  | | `wf-main` | 主内容区(padding + min-width: 0) |
85
93
  | | `wf-text-*` 排版工具 | 见下文「排版工具」 |
86
94
 
95
+ ### 组合规则(冲突防线)
96
+
97
+ 原语分两类:**布局身份类**(display/position/flex-direction——stack/row/grid/nav/center/cover/pop…)
98
+ 与**叠加工具类**(gap/对齐/显隐——叠加在身份类上)。规则:
99
+
100
+ 1. **同一元素只带一个布局身份类**——两个身份类设置同一属性不同值时 import 顺序定胜负,
101
+ 静默失效(事故例:`wf-nav wf-row` → nav 的 column 胜,横向失效;`wf-grid wf-stack` → stack 胜,网格失效)。
102
+ `style-audit` 静态扫描 class 组合,命中即红。
103
+ 2. **对齐工具可叠加**:`wf-top/right/bottom/stretch/between/around/evenly` 专门用于覆盖
104
+ `wf-row` 等的默认对齐(后导入获胜,组合合法)。`wf-nowrap` 专门关 `wf-row` 的 wrap。
105
+ 3. **`wf-center` 是 column 双轴居中**(不是 justify-center)——要“行内水平居中”用 `wf-cluster`,
106
+ 不要 `wf-row wf-center`(center 的 column 方向会静默覆盖 row)。
107
+ 4. **响应式显隐用 `wf-flex@bp` 恢复**:`wf-hidden wf-flex@lg` = 窄隐宽显(flex 容器)。
108
+ 不可用 `wf-block@lg`——block 会覆盖 `wf-stack` 的 display:flex,gap/mt-auto 静默失效。
109
+ 5. **容器内角标用 `wf-pop`**(父级 `wf-layer`/`wf-anchor`);`wf-pin` 是相对视口的 fixed,
110
+ 嵌进带壳页面会覆盖壳。
111
+
112
+ 完整冲突矩阵由 `node scripts/layout-inventory.mjs --json` 生成(属性指纹自动推导)。
113
+
87
114
  ### 排版工具(`wf-text-*`)
88
115
 
89
116
  | 工具 | 效果 |
90
117
  |------|------|
91
118
  | `wf-text-left/center/right` | text-align |
92
119
  | `wf-text-xs…5xl` | 字号(`--wf-font-size-*`) |
93
- | `wf-text-secondary/tertiary/disabled/brand` | 中性色阶 |
120
+ | `wf-text-secondary/tertiary/disabled/brand`(`wf-text-primary` 为 brand 别名) | 中性色阶 |
121
+ | `wf-nums` | tabular-nums(数值防宽度抖动) |
94
122
  | `wf-text-success/warning/error/info` | 语义色文本(`--wf-color-*`) |
95
123
  | `wf-text-medium/semibold/bold` | 字重 |
96
124
  | `wf-tracking-normal/wide/wider` | letter-spacing |
@@ -101,7 +129,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
101
129
  | `wf-truncate` | 单行省略(ellipsis) |
102
130
  | `wf-line-clamp-2/3` | 多行截断 |
103
131
 
104
- ## 143 个主题 Token
132
+ ## 167 个主题 Token
105
133
 
106
134
  **双层结构**:原始层(Primitive,色值只定义一次,品牌/暗色调校改这里)+ 语义层(Semantic,组件消费)。
107
135
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weifuwu",
3
3
  "type": "module",
4
- "version": "0.70.0",
4
+ "version": "0.71.0",
5
5
  "description": "AI SaaS framework — (req, ctx) => Response",
6
6
  "exports": {
7
7
  ".": {