@icen.ai/ui 0.2.1 → 0.4.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.
Files changed (121) hide show
  1. package/README.md +29 -16
  2. package/dist/base.css +389 -48
  3. package/dist/behaviors/accordion.mjs +20 -0
  4. package/dist/behaviors/carousel.mjs +30 -1
  5. package/dist/behaviors/charts.mjs +356 -0
  6. package/dist/behaviors/copy.mjs +4 -3
  7. package/dist/behaviors/datatable.mjs +968 -0
  8. package/dist/behaviors/input.mjs +20 -1
  9. package/dist/behaviors/modal.mjs +14 -0
  10. package/dist/behaviors/nav.mjs +31 -4
  11. package/dist/behaviors/sidebar.mjs +12 -0
  12. package/dist/behaviors/slider.mjs +35 -3
  13. package/dist/behaviors/tabs.mjs +100 -9
  14. package/dist/behaviors/tag-input.mjs +11 -0
  15. package/dist/behaviors/theme.mjs +26 -0
  16. package/dist/behaviors/toast.mjs +83 -15
  17. package/dist/behaviors/tree.mjs +57 -1
  18. package/dist/behaviors/upload.mjs +2 -3
  19. package/dist/cli.mjs +74 -0
  20. package/dist/components/accordion.mjs +3 -0
  21. package/dist/components/alert.mjs +2 -0
  22. package/dist/components/avatar.mjs +2 -0
  23. package/dist/components/breadcrumb.css +91 -18
  24. package/dist/components/breadcrumb.mjs +2 -0
  25. package/dist/components/btn.css +174 -14
  26. package/dist/components/btn.mjs +2 -0
  27. package/dist/components/card.css +118 -18
  28. package/dist/components/card.mjs +2 -0
  29. package/dist/components/carousel.mjs +3 -0
  30. package/dist/components/chart-area.css +15 -0
  31. package/dist/components/chart-area.mjs +4 -0
  32. package/dist/components/chart-bar.css +79 -0
  33. package/dist/components/chart-bar.mjs +4 -0
  34. package/dist/components/chart-gauge.css +38 -0
  35. package/dist/components/chart-gauge.mjs +4 -0
  36. package/dist/components/chart-heatmap.css +52 -0
  37. package/dist/components/chart-heatmap.mjs +4 -0
  38. package/dist/components/chart-line.css +15 -0
  39. package/dist/components/chart-line.mjs +4 -0
  40. package/dist/components/chart-pie.css +26 -0
  41. package/dist/components/chart-pie.mjs +4 -0
  42. package/dist/components/chart-radar.css +19 -0
  43. package/dist/components/chart-radar.mjs +4 -0
  44. package/dist/components/chart-sparkline.css +14 -0
  45. package/dist/components/chart-sparkline.mjs +4 -0
  46. package/dist/components/chart-stack.css +30 -0
  47. package/dist/components/chart-stack.mjs +4 -0
  48. package/dist/components/charts.css +66 -74
  49. package/dist/components/charts.mjs +12 -0
  50. package/dist/components/content.css +256 -74
  51. package/dist/components/context-menu.mjs +3 -0
  52. package/dist/components/datatable.css +429 -0
  53. package/dist/components/datatable.mjs +4 -0
  54. package/dist/components/desc.mjs +2 -0
  55. package/dist/components/dropdown.mjs +3 -0
  56. package/dist/components/empty.css +50 -10
  57. package/dist/components/empty.mjs +2 -0
  58. package/dist/components/feedback.css +158 -47
  59. package/dist/components/form.css +139 -22
  60. package/dist/components/input.css +165 -45
  61. package/dist/components/input.mjs +3 -0
  62. package/dist/components/kbd.mjs +2 -0
  63. package/dist/components/layout.mjs +2 -0
  64. package/dist/components/list.mjs +2 -0
  65. package/dist/components/media-card.mjs +2 -0
  66. package/dist/components/media.css +187 -40
  67. package/dist/components/menu.css +159 -32
  68. package/dist/components/modal.css +140 -34
  69. package/dist/components/modal.mjs +3 -0
  70. package/dist/components/nav.css +134 -32
  71. package/dist/components/nav.mjs +3 -0
  72. package/dist/components/pagination.css +105 -19
  73. package/dist/components/pagination.mjs +2 -0
  74. package/dist/components/panel.css +61 -6
  75. package/dist/components/panel.mjs +2 -0
  76. package/dist/components/pill.css +74 -14
  77. package/dist/components/pill.mjs +2 -0
  78. package/dist/components/popover.css +109 -7
  79. package/dist/components/popover.mjs +3 -0
  80. package/dist/components/progress.mjs +2 -0
  81. package/dist/components/rating.mjs +2 -0
  82. package/dist/components/result.mjs +2 -0
  83. package/dist/components/segmented.css +199 -50
  84. package/dist/components/segmented.mjs +2 -0
  85. package/dist/components/select.css +140 -39
  86. package/dist/components/select.mjs +3 -0
  87. package/dist/components/sidebar.css +162 -34
  88. package/dist/components/sidebar.mjs +3 -0
  89. package/dist/components/skeleton.mjs +2 -0
  90. package/dist/components/slider.css +169 -31
  91. package/dist/components/slider.mjs +3 -0
  92. package/dist/components/spinner.mjs +2 -0
  93. package/dist/components/stat.css +103 -8
  94. package/dist/components/stat.mjs +2 -0
  95. package/dist/components/steps.css +105 -31
  96. package/dist/components/steps.mjs +2 -0
  97. package/dist/components/switch.css +171 -54
  98. package/dist/components/switch.mjs +2 -0
  99. package/dist/components/table.css +114 -12
  100. package/dist/components/table.mjs +2 -0
  101. package/dist/components/tabs.css +104 -17
  102. package/dist/components/tabs.mjs +3 -0
  103. package/dist/components/tag-input.css +119 -23
  104. package/dist/components/tag-input.mjs +3 -0
  105. package/dist/components/timeline.mjs +2 -0
  106. package/dist/components/toast.css +85 -12
  107. package/dist/components/toast.mjs +3 -0
  108. package/dist/components/tooltip.css +86 -26
  109. package/dist/components/tooltip.mjs +2 -0
  110. package/dist/components/tree.mjs +3 -0
  111. package/dist/components/upload.css +144 -17
  112. package/dist/components/upload.mjs +3 -0
  113. package/dist/index.mjs +811 -42
  114. package/dist/registry.json +310 -2
  115. package/dist/retro-effects.css +264 -0
  116. package/dist/tokens/colors.css +18 -90
  117. package/dist/tokens/retro-effects.css +264 -0
  118. package/dist/tokens/style-profiles.css +47 -47
  119. package/dist/tokens.css +65 -137
  120. package/dist/ui.css +5069 -1026
  121. package/package.json +9 -1
@@ -1,61 +1,135 @@
1
1
  /*
2
2
  * @icen.ai/ui — Component: steps(步骤条,横向默认 / .steps--vertical 垂直)
3
- * 用法:<ol class="steps">
4
- * <li class="step step--finish">
5
- * <span class="step-line"></span>
6
- * <span class="step-dot">✓</span>
7
- * <div class="step-body">
8
- * <div class="step-title">已完成</div>
9
- * <div class="step-desc">描述</div>
10
- * </div>
11
- * </li>
12
- * </ol>
13
- * 三态:.step--finish(accent 实底,内容放 ✓)/ .step--process(accent 描边)/ .step--wait(灰)。
14
- * 连接线画在「当前步 → 下一步」之间,颜色跟随当前步状态;末步不显示(.step:last-child)。
15
- * 垂直版同一 DOM:.step-line 改为绝对左侧竖线,穿过 16px 间隙接到下一颗圆点。
3
+ *
4
+ * 完整契约:
5
+ * <ol class="steps [steps--vertical] [steps--sm|steps--lg]" aria-label="进度">
6
+ * <li class="step [step--finish|step--process|step--wait] [.step--error]">
7
+ * <span class="step-line"></span> ← 横向:右半段连线;末步自动隐藏
8
+ * <span class="step-dot">[✓ | ! | 数字]</span>
9
+ * <div class="step-body">
10
+ * <div class="step-title">已完成</div>
11
+ * <div class="step-desc">描述(可选)</div>
12
+ * </div>
13
+ * </li>
14
+ * </ol>
15
+ *
16
+ * 四态:finish / process / wait / error(错误用 error 色 dot + 描边)。
17
+ * 点击 .step[role=button] 由使用方实现跳转。
16
18
  */
17
19
 
18
- .steps { display: flex; list-style: none; margin: 0; padding: 0; }
19
- .step { flex: 1; position: relative; text-align: center; }
20
+ .steps {
21
+ display: flex;
22
+ list-style: none;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+ .step {
27
+ flex: 1;
28
+ position: relative;
29
+ text-align: center;
30
+ padding: 0 var(--density-space-xs);
31
+ }
20
32
 
21
33
  .step-line {
22
- position: absolute; top: 16px; left: calc(50% + 20px);
23
- width: calc(100% - 40px); height: 1px;
34
+ position: absolute;
35
+ top: 16px;
36
+ left: calc(50% + 20px);
37
+ width: calc(100% - 40px);
38
+ height: 1px;
24
39
  background: var(--token-outline-soft);
40
+ transition: background-color var(--icen-tx-normal);
25
41
  }
26
42
  .step:last-child .step-line { display: none; }
27
43
  .step--finish .step-line { background: color-mix(in srgb, var(--token-accent) 40%, transparent); }
28
44
 
29
45
  .step-dot {
30
- width: 32px; height: 32px; border-radius: var(--radius-full);
31
- display: inline-flex; align-items: center; justify-content: center;
32
- font-size: 12px; font-weight: 600; position: relative; z-index: 1;
46
+ width: 32px;
47
+ height: 32px;
48
+ border-radius: var(--radius-full);
49
+ display: inline-flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ font-size: var(--density-font-size-sm);
53
+ font-weight: 600;
54
+ font-variant-numeric: tabular-nums;
55
+ position: relative;
56
+ z-index: 1;
57
+ transition:
58
+ background-color var(--icen-tx-fast),
59
+ border-color var(--icen-tx-fast),
60
+ color var(--icen-tx-fast);
61
+ }
62
+ .step--finish .step-dot {
63
+ background: var(--token-accent);
64
+ color: var(--token-on-accent, #fff);
33
65
  }
34
- .step--finish .step-dot { background: var(--token-accent); color: var(--token-on-accent); }
35
66
  .step--process .step-dot {
36
- background: var(--token-accent-subtle); color: var(--token-accent);
67
+ background: var(--token-accent-subtle);
68
+ color: var(--token-accent);
37
69
  border: 2px solid var(--token-accent);
70
+ box-shadow: var(--icen-ring-soft);
38
71
  }
39
72
  .step--wait .step-dot {
40
- background: var(--token-control); color: var(--token-text-faint);
73
+ background: var(--token-control);
74
+ color: var(--token-text-faint);
41
75
  border: 1px solid var(--token-outline-soft);
42
76
  }
77
+ .step--error .step-dot {
78
+ background: var(--token-error-muted);
79
+ color: var(--token-error);
80
+ border: 2px solid var(--token-error);
81
+ }
43
82
 
44
- .step-title { font-size: 13px; font-weight: 500; margin-top: 6px; }
83
+ .step-title {
84
+ font-size: var(--density-font-size-md);
85
+ font-weight: 500;
86
+ margin-top: var(--density-space-xs);
87
+ color: var(--token-text);
88
+ }
45
89
  .step--wait .step-title { color: var(--token-text-muted); }
90
+ .step--error .step-title { color: var(--token-error); }
46
91
  .step-desc {
47
- font-size: 11px; color: var(--token-text-faint);
48
- margin-top: 2px; max-width: 160px; margin-inline: auto;
92
+ font-size: var(--density-font-size-xs);
93
+ color: var(--token-text-faint);
94
+ margin-top: 2px;
95
+ max-width: 160px;
96
+ margin-inline: auto;
97
+ line-height: 1.4;
49
98
  }
50
99
 
100
+ /* 尺寸变体 */
101
+ .steps--sm .step-dot { width: 24px; height: 24px; font-size: var(--density-font-size-xs); }
102
+ .steps--sm .step-line { top: 12px; }
103
+ .steps--lg .step-dot { width: 40px; height: 40px; font-size: var(--density-font-size-md); }
104
+ .steps--lg .step-line { top: 20px; }
105
+
51
106
  /* ── 垂直变体 ── */
52
- .steps--vertical { flex-direction: column; gap: 16px; }
53
- .steps--vertical .step { flex: none; display: flex; gap: 12px; text-align: left; }
107
+ .steps--vertical {
108
+ flex-direction: column;
109
+ gap: var(--density-space-md);
110
+ }
111
+ .steps--vertical .step {
112
+ flex: none;
113
+ display: flex;
114
+ gap: var(--density-space-sm);
115
+ text-align: left;
116
+ padding: 0;
117
+ }
54
118
  .steps--vertical .step-line {
55
- top: 36px; bottom: -16px; left: 16px;
56
- width: 1px; height: auto;
119
+ top: 36px;
120
+ bottom: calc(-1 * var(--density-space-md));
121
+ left: 16px;
122
+ width: 1px;
123
+ height: auto;
57
124
  }
58
125
  .steps--vertical .step-dot { flex-shrink: 0; }
59
126
  .steps--vertical .step-body { padding-top: 4px; }
60
127
  .steps--vertical .step-title { margin-top: 0; }
61
- .steps--vertical .step-desc { max-width: none; margin-inline: 0; }
128
+ .steps--vertical .step-desc {
129
+ max-width: none;
130
+ margin-inline: 0;
131
+ }
132
+
133
+ @media (prefers-reduced-motion: reduce) {
134
+ .step-dot, .step-line { transition: none; }
135
+ }
@@ -0,0 +1,2 @@
1
+ // @icen.ai/ui — kit: steps(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './steps.css';
@@ -1,95 +1,212 @@
1
1
  /*
2
- * @icen.ai/ui — Component: switch(开关 / 勾选框 / 单选行)
3
- * 纯 CSS 状态组件:状态全走 aria-checked / .is-selected,交互由使用方(或演示页脚本)切换。
2
+ * @icen.ai/ui — Component: switch / checkbox / radio(开关 / 勾选 / 单选行)
3
+ * 纯 CSS 状态组件:状态全走 aria-checked / .is-selected;交互由使用方或演示页脚本切换。
4
4
  *
5
- * 开关:<button class="switch [--sm]" role="switch" aria-checked="true"><span class="switch-thumb"></span></button>
6
- * 勾选:<button class="checkbox focus-ring" role="checkbox" aria-checked="true|false|mixed"></button>
7
- * 单选:<div class="radio-group" role="radiogroup">
8
- * <button class="radio-item motion is-selected"><span class="radio-dot"></span>…</button>
9
- * </div>
5
+ * 完整契约:
6
+ * <button class="switch [--sm|--lg]" role="switch" aria-checked="true|false" [disabled]>
7
+ * <span class="switch-thumb"></span>
8
+ * </button>
9
+ *
10
+ * <button class="checkbox focus-ring [--sm|--lg]" role="checkbox"
11
+ * aria-checked="true|false|mixed" [disabled]></button>
12
+ *
13
+ * <div class="radio-group" role="radiogroup">
14
+ * <button class="radio-item motion [.is-selected]" role="radio"
15
+ * aria-checked="true|false" [disabled]>
16
+ * <span class="radio-dot"></span>
17
+ * <span>标签</span>
18
+ * </button>
19
+ * </div>
10
20
  */
11
21
 
12
22
  /* ── 开关 ── */
13
23
  .switch {
14
- position: relative; display: inline-flex; flex-shrink: 0;
15
- width: 44px; height: 24px; padding: 0;
16
- border: none; border-radius: var(--radius-full);
17
- background: var(--token-control-hover); cursor: pointer;
18
- transition: background-color var(--duration-normal) var(--ease-out);
19
- }
20
- .switch[aria-checked='true'] { background: var(--token-accent); }
21
- .switch:disabled { opacity: 0.4; cursor: not-allowed; }
24
+ --switch-w: 44px;
25
+ --switch-h: 24px;
26
+ --switch-thumb: 20px;
27
+ position: relative;
28
+ display: inline-flex;
29
+ flex-shrink: 0;
30
+ width: var(--switch-w);
31
+ height: var(--switch-h);
32
+ padding: 0;
33
+ border: none;
34
+ border-radius: var(--radius-full);
35
+ background: var(--token-control-hover);
36
+ cursor: pointer;
37
+ -webkit-tap-highlight-color: transparent;
38
+ transition: background-color var(--icen-tx-normal);
39
+ }
40
+ .switch[aria-checked='true'] {
41
+ background: var(--token-accent);
42
+ }
43
+ .switch:disabled,
44
+ .switch[aria-disabled='true'],
45
+ .switch[data-disabled] {
46
+ opacity: var(--icen-disabled-opacity);
47
+ cursor: not-allowed;
48
+ }
49
+ .switch:focus-visible {
50
+ outline: none;
51
+ box-shadow: var(--icen-ring);
52
+ }
22
53
 
23
54
  .switch-thumb {
24
- position: absolute; top: 2px; left: 2px;
25
- width: 20px; height: 20px; border-radius: var(--radius-full);
55
+ position: absolute;
56
+ top: 50%;
57
+ left: calc((var(--switch-h) - var(--switch-thumb)) / 2);
58
+ transform: translateY(-50%);
59
+ width: var(--switch-thumb);
60
+ height: var(--switch-thumb);
61
+ border-radius: var(--radius-full);
26
62
  background: var(--token-on-accent, #fff);
27
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
28
- transition: transform var(--duration-normal) var(--ease-out);
63
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
64
+ transition: transform var(--icen-tx-bounce);
29
65
  }
30
- .switch[aria-checked='true'] .switch-thumb { transform: translateX(20px); }
66
+ .switch[aria-checked='true'] .switch-thumb {
67
+ transform: translateY(-50%) translateX(calc(var(--switch-w) - var(--switch-h)));
68
+ }
69
+
70
+ /* 尺寸变体 */
71
+ .switch--sm { --switch-w: 36px; --switch-h: 20px; --switch-thumb: 16px; }
72
+ .switch--lg { --switch-w: 56px; --switch-h: 28px; --switch-thumb: 24px; }
31
73
 
32
- .switch--sm { width: 36px; height: 20px; }
33
- .switch--sm .switch-thumb { width: 16px; height: 16px; }
34
- .switch--sm[aria-checked='true'] .switch-thumb { transform: translateX(16px); }
74
+ @media (prefers-reduced-motion: reduce) {
75
+ .switch-thumb { transition: none; }
76
+ }
35
77
 
36
- /* ── 勾选框(✓ 与半选横条均 ::before 自绘,无需内联 svg) ── */
78
+ /* ── 勾选框(✓ 与半选横条均 ::before 自绘) ── */
37
79
  .checkbox {
38
- display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
39
- width: 16px; height: 16px; padding: 0;
40
- background: var(--token-control); color: transparent;
80
+ --checkbox-size: 16px;
81
+ display: inline-flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ flex-shrink: 0;
85
+ width: var(--checkbox-size);
86
+ height: var(--checkbox-size);
87
+ padding: 0;
88
+ background: var(--token-control);
89
+ color: transparent;
41
90
  border: 1px solid var(--token-outline-soft);
42
- border-radius: var(--radius-sm); cursor: pointer;
43
- transition: background-color var(--duration-normal) var(--ease-out),
44
- border-color var(--duration-normal) var(--ease-out),
45
- color var(--duration-normal) var(--ease-out);
91
+ border-radius: var(--radius-sm);
92
+ cursor: pointer;
93
+ -webkit-tap-highlight-color: transparent;
94
+ transition:
95
+ background-color var(--icen-tx-fast),
96
+ border-color var(--icen-tx-fast),
97
+ color var(--icen-tx-fast),
98
+ box-shadow var(--icen-tx-fast);
46
99
  }
47
- .checkbox:hover:not([aria-checked='true']):not([aria-checked='mixed']) {
100
+ .checkbox:hover:not([aria-checked='true']):not([aria-checked='mixed']):not(:disabled) {
48
101
  border-color: color-mix(in srgb, var(--token-accent) 50%, transparent);
49
102
  }
50
- .checkbox[aria-checked='true'], .checkbox[aria-checked='mixed'] {
103
+ .checkbox[aria-checked='true'],
104
+ .checkbox[aria-checked='mixed'] {
51
105
  background: var(--token-accent);
52
106
  border-color: var(--token-accent);
53
107
  color: var(--token-on-accent, #fff);
54
108
  }
55
109
  .checkbox::before {
56
- content: ''; width: 10px; height: 10px;
57
- background: currentColor; opacity: 0;
110
+ content: '';
111
+ width: 60%;
112
+ height: 60%;
113
+ background: currentColor;
114
+ opacity: 0;
58
115
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
59
116
  }
60
117
  .checkbox[aria-checked='true']::before { opacity: 1; }
61
118
  .checkbox[aria-checked='mixed']::before {
62
- opacity: 1; width: 8px; height: 2px;
63
- clip-path: none; border-radius: var(--radius-full);
119
+ opacity: 1;
120
+ width: 60%;
121
+ height: 2px;
122
+ clip-path: none;
123
+ border-radius: var(--radius-full);
124
+ }
125
+ .checkbox:disabled,
126
+ .checkbox[aria-disabled='true'],
127
+ .checkbox[data-disabled] {
128
+ opacity: var(--icen-disabled-opacity);
129
+ cursor: not-allowed;
64
130
  }
65
- .checkbox:disabled { opacity: 0.4; cursor: not-allowed; }
131
+ .checkbox:focus-visible {
132
+ outline: none;
133
+ box-shadow: var(--icen-ring);
134
+ }
135
+
136
+ .checkbox--sm { --checkbox-size: 14px; }
137
+ .checkbox--lg { --checkbox-size: 20px; }
66
138
 
67
- /* ── 单选行 ── */
68
- .radio-group { display: flex; flex-direction: column; gap: 4px; }
139
+ /* ── 单选行(radio item:整行可点) ── */
140
+ .radio-group {
141
+ display: flex;
142
+ flex-direction: column;
143
+ gap: var(--density-space-xs);
144
+ }
69
145
 
70
146
  .radio-item {
71
- display: flex; align-items: center; gap: 8px; width: 100%;
72
- padding: 8px 12px; font-size: 13px; text-align: left;
73
- border: none; background: transparent; color: var(--token-text);
74
- border-radius: var(--radius-sm); cursor: pointer;
147
+ display: flex;
148
+ align-items: center;
149
+ gap: var(--density-space-sm);
150
+ width: 100%;
151
+ padding: var(--density-space-sm) var(--density-space-md);
152
+ font-size: var(--density-font-size-md);
153
+ text-align: left;
154
+ border: 1px solid transparent;
155
+ background: transparent;
156
+ color: var(--token-text);
157
+ border-radius: var(--radius-md);
158
+ cursor: pointer;
159
+ -webkit-tap-highlight-color: transparent;
160
+ transition:
161
+ background-color var(--icen-tx-fast),
162
+ color var(--icen-tx-fast),
163
+ border-color var(--icen-tx-fast);
75
164
  }
76
- .radio-item.is-selected {
77
- background: var(--token-accent-subtle); color: var(--token-accent); font-weight: 500;
165
+ .radio-item:hover:not(.is-disabled) {
166
+ background: var(--token-fill-1);
167
+ }
168
+ .radio-item.is-selected,
169
+ .radio-item[aria-checked='true'] {
170
+ background: var(--token-accent-subtle);
171
+ color: var(--token-accent);
172
+ font-weight: 500;
173
+ border-color: color-mix(in srgb, var(--token-accent) 25%, transparent);
174
+ }
175
+ .radio-item:focus-visible {
176
+ outline: none;
177
+ box-shadow: var(--icen-ring);
178
+ }
179
+ .radio-item:disabled,
180
+ .radio-item.is-disabled,
181
+ .radio-item[aria-disabled='true'] {
182
+ opacity: var(--icen-disabled-opacity);
183
+ cursor: not-allowed;
78
184
  }
79
- .radio-item:disabled { opacity: 0.4; cursor: not-allowed; }
80
185
 
81
186
  .radio-dot {
82
- position: relative; width: 16px; height: 16px; flex-shrink: 0;
187
+ position: relative;
188
+ width: 16px;
189
+ height: 16px;
190
+ flex-shrink: 0;
83
191
  border: 1px solid var(--token-outline-soft);
84
192
  border-radius: var(--radius-full);
85
- transition: border-color var(--duration-normal) var(--ease-out);
193
+ background: var(--token-control);
194
+ transition: border-color var(--icen-tx-fast);
86
195
  }
87
- .radio-item.is-selected .radio-dot { border-color: var(--token-accent); }
196
+ .radio-item.is-selected .radio-dot,
197
+ .radio-item[aria-checked='true'] .radio-dot { border-color: var(--token-accent); }
88
198
  .radio-dot::after {
89
- content: ''; position: absolute; top: 50%; left: 50%;
90
- width: 8px; height: 8px; border-radius: var(--radius-full);
199
+ content: '';
200
+ position: absolute;
201
+ top: 50%; left: 50%;
202
+ width: 8px;
203
+ height: 8px;
204
+ border-radius: var(--radius-full);
91
205
  background: var(--token-accent);
92
206
  transform: translate(-50%, -50%) scale(0);
93
- transition: transform var(--duration-fast) var(--ease-out);
207
+ transition: transform var(--icen-tx-bounce);
208
+ }
209
+ .radio-item.is-selected .radio-dot::after,
210
+ .radio-item[aria-checked='true'] .radio-dot::after {
211
+ transform: translate(-50%, -50%) scale(1);
94
212
  }
95
- .radio-item.is-selected .radio-dot::after { transform: translate(-50%, -50%) scale(1); }
@@ -0,0 +1,2 @@
1
+ // @icen.ai/ui — kit: switch(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './switch.css';
@@ -1,19 +1,121 @@
1
1
  /*
2
2
  * @icen.ai/ui — Component: table(数据表)
3
- * 用法:<div class="table-wrap"><table class="admin-table">…
4
- * .table-wrap 负责窄屏横向滚动(滚动条样式见 base.css);行内操作列用 .ops。
3
+ *
4
+ * 完整契约:
5
+ * <div class="table-wrap [table-wrap--bordered]">
6
+ * <table class="admin-table [admin-table--compact|admin-table--dense]">
7
+ * <thead>
8
+ * <tr><th [colspan=rowspan]>列名</th>…</tr>
9
+ * </thead>
10
+ * <tbody>
11
+ * <tr class="row-link [.is-selected]"> ← 整行可点
12
+ * <td>…</td>
13
+ * <td class="ops"><button class="btn btn-sm">…</button></td>
14
+ * </tr>
15
+ * </tbody>
16
+ * <tfoot><tr><td>合计</td>…</tr></tfoot>
17
+ * </table>
18
+ * </div>
19
+ *
20
+ * .table-wrap:窄屏横向滚动(滚动条样式见 base.css);--bordered 加外边框。
21
+ * .admin-table--compact:紧凑(cell padding 缩小);--dense:极紧凑(行高更紧)。
5
22
  */
6
23
 
7
- .table-wrap { overflow-x: auto; }
8
- .admin-table { width: 100%; border-collapse: collapse; font-size: 13px; }
24
+ .table-wrap {
25
+ overflow-x: auto;
26
+ overscroll-behavior: contain;
27
+ border-radius: var(--radius-lg);
28
+ }
29
+ .table-wrap--bordered {
30
+ border: 1px solid var(--token-line-soft);
31
+ }
32
+
33
+ .admin-table {
34
+ width: 100%;
35
+ border-collapse: collapse;
36
+ font-size: var(--density-font-size-md);
37
+ font-variant-numeric: tabular-nums;
38
+ }
39
+ .admin-table th,
40
+ .admin-table td {
41
+ padding: var(--density-space-sm) calc(var(--density-space-md) + 2px);
42
+ text-align: left;
43
+ }
9
44
  .admin-table th {
10
- text-align: left; font-weight: 400; font-size: 11px; letter-spacing: 1px;
11
- color: var(--token-text-faint); padding: 10px 14px;
12
- border-bottom: 1px solid var(--token-line-soft); white-space: nowrap;
45
+ font-weight: 600;
46
+ font-size: var(--density-font-size-xs);
47
+ letter-spacing: 0.05em;
48
+ color: var(--token-text-faint);
49
+ border-bottom: 1px solid var(--token-line);
50
+ white-space: nowrap;
51
+ text-transform: uppercase;
52
+ background: var(--token-bg-soft);
53
+ position: sticky;
54
+ top: 0;
55
+ z-index: 1;
56
+ }
57
+ .admin-table td {
58
+ border-bottom: 1px solid var(--token-line-soft);
59
+ vertical-align: middle;
60
+ color: var(--token-text);
13
61
  }
14
- .admin-table td { padding: 10px 14px; border-bottom: 1px solid var(--token-line-soft); vertical-align: middle; }
15
62
  .admin-table tbody tr:last-child td { border-bottom: 0; }
16
- .admin-table tbody tr:hover { background: var(--token-surface-strong); }
17
- .admin-table tbody tr.row-link { cursor: pointer; }
18
- .admin-table .ops { white-space: nowrap; text-align: right; }
19
- .admin-table .ops .btn-sm { margin-left: 6px; }
63
+ .admin-table tbody tr {
64
+ transition: background-color var(--icen-tx-fast);
65
+ }
66
+ .admin-table tbody tr:hover {
67
+ background: var(--token-surface-strong);
68
+ }
69
+ .admin-table tbody tr.row-link {
70
+ cursor: pointer;
71
+ }
72
+ .admin-table tbody tr.is-selected {
73
+ background: var(--token-accent-subtle);
74
+ }
75
+ .admin-table tfoot td {
76
+ border-top: 1px solid var(--token-line);
77
+ border-bottom: 0;
78
+ font-weight: 500;
79
+ background: var(--token-bg-soft);
80
+ }
81
+
82
+ /* 紧凑变体 */
83
+ .admin-table--compact th,
84
+ .admin-table--compact td {
85
+ padding: var(--density-space-xs) var(--density-space-sm);
86
+ }
87
+ .admin-table--dense th,
88
+ .admin-table--dense td {
89
+ padding: 4px var(--density-space-xs);
90
+ font-size: var(--density-font-size-sm);
91
+ }
92
+
93
+ /* 右对齐数字列 */
94
+ .admin-table td.num {
95
+ text-align: right;
96
+ font-variant-numeric: tabular-nums lining-nums;
97
+ }
98
+
99
+ /* 操作列 */
100
+ .admin-table .ops {
101
+ white-space: nowrap;
102
+ text-align: right;
103
+ }
104
+ .admin-table .ops .btn,
105
+ .admin-table .ops .btn-sm {
106
+ margin-left: var(--density-space-xs);
107
+ }
108
+
109
+ /* 可排序表头(使用方 data-sort 指示) */
110
+ .admin-table th[data-sort] {
111
+ cursor: pointer;
112
+ user-select: none;
113
+ }
114
+ .admin-table th[data-sort]::after {
115
+ content: '↕';
116
+ margin-left: var(--density-space-xs);
117
+ color: var(--token-text-faint);
118
+ opacity: 0.6;
119
+ }
120
+ .admin-table th[data-sort='asc']::after { content: '↑'; opacity: 1; color: var(--token-accent); }
121
+ .admin-table th[data-sort='desc']::after { content: '↓'; opacity: 1; color: var(--token-accent); }
@@ -0,0 +1,2 @@
1
+ // @icen.ai/ui — kit: table(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './table.css';