@icen.ai/ui 0.4.0 → 0.8.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 (155) hide show
  1. package/AGENTS.md +256 -0
  2. package/README.md +47 -13
  3. package/dist/base.css +31 -4
  4. package/dist/behaviors/accordion.d.mts +4 -0
  5. package/dist/behaviors/accordion.mjs +4 -3
  6. package/dist/behaviors/ai-chat.d.mts +57 -0
  7. package/dist/behaviors/ai-chat.mjs +557 -0
  8. package/dist/behaviors/ai-composer.d.mts +112 -0
  9. package/dist/behaviors/ai-composer.mjs +2032 -0
  10. package/dist/behaviors/ai-core.d.mts +151 -0
  11. package/dist/behaviors/ai-core.mjs +491 -0
  12. package/dist/behaviors/ai-diff.d.mts +53 -0
  13. package/dist/behaviors/ai-diff.mjs +282 -0
  14. package/dist/behaviors/ai-panel.d.mts +98 -0
  15. package/dist/behaviors/ai-panel.mjs +1012 -0
  16. package/dist/behaviors/ai-provider.d.mts +198 -0
  17. package/dist/behaviors/ai-provider.mjs +1055 -0
  18. package/dist/behaviors/ai-tool.d.mts +18 -0
  19. package/dist/behaviors/ai-tool.mjs +2125 -0
  20. package/dist/behaviors/ai-tools.d.mts +81 -0
  21. package/dist/behaviors/ai-tools.mjs +1658 -0
  22. package/dist/behaviors/back-top.d.mts +20 -0
  23. package/dist/behaviors/back-top.mjs +101 -0
  24. package/dist/behaviors/carousel.d.mts +4 -0
  25. package/dist/behaviors/carousel.mjs +34 -16
  26. package/dist/behaviors/charts.d.mts +250 -0
  27. package/dist/behaviors/charts.mjs +1301 -448
  28. package/dist/behaviors/command-palette.d.mts +8 -0
  29. package/dist/behaviors/command-palette.mjs +189 -0
  30. package/dist/behaviors/context-menu.d.mts +36 -0
  31. package/dist/behaviors/context-menu.mjs +6 -2
  32. package/dist/behaviors/copy.d.mts +4 -0
  33. package/dist/behaviors/copy.mjs +7 -6
  34. package/dist/behaviors/datatable.d.mts +78 -0
  35. package/dist/behaviors/datatable.mjs +54 -11
  36. package/dist/behaviors/date-picker.d.mts +4 -0
  37. package/dist/behaviors/date-picker.mjs +296 -0
  38. package/dist/behaviors/dropdown.d.mts +4 -0
  39. package/dist/behaviors/dropdown.mjs +7 -3
  40. package/dist/behaviors/input.d.mts +4 -0
  41. package/dist/behaviors/input.mjs +184 -8
  42. package/dist/behaviors/modal.d.mts +11 -0
  43. package/dist/behaviors/modal.mjs +11 -6
  44. package/dist/behaviors/nav.d.mts +7 -0
  45. package/dist/behaviors/nav.mjs +25 -9
  46. package/dist/behaviors/notification.d.mts +172 -0
  47. package/dist/behaviors/notification.mjs +908 -0
  48. package/dist/behaviors/popover.d.mts +46 -0
  49. package/dist/behaviors/popover.mjs +2 -1
  50. package/dist/behaviors/select.d.mts +4 -0
  51. package/dist/behaviors/select.mjs +268 -34
  52. package/dist/behaviors/sidebar.d.mts +4 -0
  53. package/dist/behaviors/sidebar.mjs +4 -3
  54. package/dist/behaviors/slider.d.mts +4 -0
  55. package/dist/behaviors/slider.mjs +4 -3
  56. package/dist/behaviors/split-pane.d.mts +4 -0
  57. package/dist/behaviors/split-pane.mjs +102 -0
  58. package/dist/behaviors/tabs.d.mts +8 -0
  59. package/dist/behaviors/tabs.mjs +44 -19
  60. package/dist/behaviors/tag-input.d.mts +4 -0
  61. package/dist/behaviors/tag-input.mjs +12 -3
  62. package/dist/behaviors/theme.d.mts +25 -0
  63. package/dist/behaviors/theme.mjs +3 -1
  64. package/dist/behaviors/toast.d.mts +26 -0
  65. package/dist/behaviors/toast.mjs +24 -18
  66. package/dist/behaviors/tree.d.mts +4 -0
  67. package/dist/behaviors/tree.mjs +4 -3
  68. package/dist/behaviors/upload.d.mts +4 -0
  69. package/dist/behaviors/upload.mjs +205 -17
  70. package/dist/components/ai-chat.css +1037 -0
  71. package/dist/components/ai-chat.mjs +3 -0
  72. package/dist/components/ai-composer.mjs +3 -0
  73. package/dist/components/ai-context.mjs +2 -0
  74. package/dist/components/ai-diff.css +311 -0
  75. package/dist/components/ai-diff.mjs +3 -0
  76. package/dist/components/ai-files.mjs +3 -0
  77. package/dist/components/ai-message.mjs +3 -0
  78. package/dist/components/ai-panel.css +682 -0
  79. package/dist/components/ai-reasoning.mjs +3 -0
  80. package/dist/components/ai-subagent.mjs +2 -0
  81. package/dist/components/ai-todo.mjs +2 -0
  82. package/dist/components/ai-tool-call.mjs +2 -0
  83. package/dist/components/ai-tool.css +532 -0
  84. package/dist/components/ai-usage.mjs +3 -0
  85. package/dist/components/back-top.css +88 -0
  86. package/dist/components/back-top.mjs +3 -0
  87. package/dist/components/badge.css +127 -0
  88. package/dist/components/badge.mjs +2 -0
  89. package/dist/components/breadcrumb.css +53 -0
  90. package/dist/components/btn.css +4 -7
  91. package/dist/components/card.css +66 -1
  92. package/dist/components/chart-bar.css +10 -0
  93. package/dist/components/chart-calendar.mjs +5 -0
  94. package/dist/components/chart-gauge.css +1 -1
  95. package/dist/components/chart-heatmap.css +40 -0
  96. package/dist/components/chart-scatter.css +24 -0
  97. package/dist/components/chart-scatter.mjs +4 -0
  98. package/dist/components/charts.css +86 -1
  99. package/dist/components/charts.mjs +1 -0
  100. package/dist/components/command-palette.css +246 -0
  101. package/dist/components/command-palette.mjs +3 -0
  102. package/dist/components/content.css +221 -8
  103. package/dist/components/copy.css +93 -0
  104. package/dist/components/copy.mjs +3 -0
  105. package/dist/components/datatable.css +4 -8
  106. package/dist/components/date-picker.css +240 -0
  107. package/dist/components/date-picker.mjs +3 -0
  108. package/dist/components/empty.css +69 -3
  109. package/dist/components/feedback.css +241 -10
  110. package/dist/components/form.css +129 -11
  111. package/dist/components/form.mjs +2 -0
  112. package/dist/components/input.css +25 -2
  113. package/dist/components/layout.css +36 -36
  114. package/dist/components/media.css +153 -6
  115. package/dist/components/menu.css +2 -2
  116. package/dist/components/modal.css +1 -1
  117. package/dist/components/nav.css +106 -2
  118. package/dist/components/notification.css +387 -0
  119. package/dist/components/notification.mjs +3 -0
  120. package/dist/components/pagination.css +1 -1
  121. package/dist/components/panel.css +104 -9
  122. package/dist/components/pill.css +82 -12
  123. package/dist/components/scroll-area.css +112 -0
  124. package/dist/components/scroll-area.mjs +2 -0
  125. package/dist/components/segmented.css +80 -5
  126. package/dist/components/segmented.mjs +1 -0
  127. package/dist/components/select.css +104 -17
  128. package/dist/components/sidebar.css +119 -1
  129. package/dist/components/slider.css +113 -0
  130. package/dist/components/split-pane.css +111 -0
  131. package/dist/components/split-pane.mjs +3 -0
  132. package/dist/components/stat.css +131 -12
  133. package/dist/components/steps.css +98 -1
  134. package/dist/components/switch.css +145 -7
  135. package/dist/components/table.css +111 -29
  136. package/dist/components/tabs.css +132 -22
  137. package/dist/components/tag-input.css +4 -4
  138. package/dist/components/tag.css +164 -0
  139. package/dist/components/tag.mjs +2 -0
  140. package/dist/components/toast.css +122 -5
  141. package/dist/components/toolbar.css +101 -0
  142. package/dist/components/toolbar.mjs +2 -0
  143. package/dist/components/tooltip.css +2 -2
  144. package/dist/components/upload.css +61 -4
  145. package/dist/index.d.mts +37 -0
  146. package/dist/index.mjs +9409 -929
  147. package/dist/registry.json +176 -4
  148. package/dist/retro-effects.css +21 -17
  149. package/dist/tokens/colors.css +4 -1
  150. package/dist/tokens/retro-effects.css +21 -17
  151. package/dist/tokens/style-profiles.css +21 -0
  152. package/dist/tokens.css +25 -1
  153. package/dist/ui.css +9616 -2839
  154. package/docs/spec/ai-native.md +659 -0
  155. package/package.json +15 -5
@@ -3,22 +3,30 @@
3
3
  *
4
4
  * 完整契约:
5
5
  * <div class="table-wrap [table-wrap--bordered]">
6
- * <table class="admin-table [admin-table--compact|admin-table--dense]">
6
+ * <table class="table [--zebra|--compact|--dense] [--sticky-col]">
7
+ * <caption class="table-caption">标题说明</caption> ← 可选
7
8
  * <thead>
8
- * <tr><th [colspan=rowspan]>列名</th>…</tr>
9
+ * <tr><th [colspan=rowspan] [data-sort]>列名</th>…</tr>
9
10
  * </thead>
10
11
  * <tbody>
11
- * <tr class="row-link [.is-selected]"> ← 整行可点
12
+ * <tr class="row-link [.is-selected] [.is-expanded]> ← 整行可点
13
+ * <td class="col-fixed">…</td> ← 固定列(sticky-col 模式)
12
14
  * <td>…</td>
13
15
  * <td class="ops"><button class="btn btn-sm">…</button></td>
14
16
  * </tr>
17
+ * <tr class="row-expand"> ← 展开行(配合 .is-expanded)
18
+ * <td colspan="99">…展开详情…</td>
19
+ * </tr>
15
20
  * </tbody>
16
21
  * <tfoot><tr><td>合计</td>…</tr></tfoot>
17
22
  * </table>
18
23
  * </div>
19
24
  *
20
25
  * .table-wrap:窄屏横向滚动(滚动条样式见 base.css);--bordered 加外边框。
21
- * .admin-table--compact:紧凑(cell padding 缩小);--dense:极紧凑(行高更紧)。
26
+ * .table--zebra:斑马纹(奇数行底色)。
27
+ * .table--compact:紧凑(cell padding 缩小);--dense:极紧凑(行高更紧)。
28
+ * .table--sticky-col:首列水平粘滞(配合 td/th .col-fixed,长表横向滚动时保持可见)。
29
+ * .row-expand[hidden]:配合 .row-link.is-expanded 控制展开行显隐。
22
30
  */
23
31
 
24
32
  .table-wrap {
@@ -30,18 +38,18 @@
30
38
  border: 1px solid var(--token-line-soft);
31
39
  }
32
40
 
33
- .admin-table {
41
+ .table {
34
42
  width: 100%;
35
43
  border-collapse: collapse;
36
44
  font-size: var(--density-font-size-md);
37
45
  font-variant-numeric: tabular-nums;
38
46
  }
39
- .admin-table th,
40
- .admin-table td {
47
+ .table th,
48
+ .table td {
41
49
  padding: var(--density-space-sm) calc(var(--density-space-md) + 2px);
42
50
  text-align: left;
43
51
  }
44
- .admin-table th {
52
+ .table th {
45
53
  font-weight: 600;
46
54
  font-size: var(--density-font-size-xs);
47
55
  letter-spacing: 0.05em;
@@ -52,27 +60,27 @@
52
60
  background: var(--token-bg-soft);
53
61
  position: sticky;
54
62
  top: 0;
55
- z-index: 1;
63
+ z-index: var(--z-raised);
56
64
  }
57
- .admin-table td {
65
+ .table td {
58
66
  border-bottom: 1px solid var(--token-line-soft);
59
67
  vertical-align: middle;
60
68
  color: var(--token-text);
61
69
  }
62
- .admin-table tbody tr:last-child td { border-bottom: 0; }
63
- .admin-table tbody tr {
70
+ .table tbody tr:last-child td { border-bottom: 0; }
71
+ .table tbody tr {
64
72
  transition: background-color var(--icen-tx-fast);
65
73
  }
66
- .admin-table tbody tr:hover {
74
+ .table tbody tr:hover {
67
75
  background: var(--token-surface-strong);
68
76
  }
69
- .admin-table tbody tr.row-link {
77
+ .table tbody tr.row-link {
70
78
  cursor: pointer;
71
79
  }
72
- .admin-table tbody tr.is-selected {
80
+ .table tbody tr.is-selected {
73
81
  background: var(--token-accent-subtle);
74
82
  }
75
- .admin-table tfoot td {
83
+ .table tfoot td {
76
84
  border-top: 1px solid var(--token-line);
77
85
  border-bottom: 0;
78
86
  font-weight: 500;
@@ -80,42 +88,116 @@
80
88
  }
81
89
 
82
90
  /* 紧凑变体 */
83
- .admin-table--compact th,
84
- .admin-table--compact td {
91
+ .table--compact th,
92
+ .table--compact td {
85
93
  padding: var(--density-space-xs) var(--density-space-sm);
86
94
  }
87
- .admin-table--dense th,
88
- .admin-table--dense td {
95
+ .table--dense th,
96
+ .table--dense td {
89
97
  padding: 4px var(--density-space-xs);
90
98
  font-size: var(--density-font-size-sm);
91
99
  }
92
100
 
93
101
  /* 右对齐数字列 */
94
- .admin-table td.num {
102
+ .table td.num {
95
103
  text-align: right;
96
104
  font-variant-numeric: tabular-nums lining-nums;
97
105
  }
98
106
 
99
- /* 操作列 */
100
- .admin-table .ops {
107
+ /* 操作列(.btn-sm 必伴 .btn,无需单独列) */
108
+ .table .ops {
101
109
  white-space: nowrap;
102
110
  text-align: right;
103
111
  }
104
- .admin-table .ops .btn,
105
- .admin-table .ops .btn-sm {
112
+ .table .ops .btn {
106
113
  margin-left: var(--density-space-xs);
107
114
  }
108
115
 
109
116
  /* 可排序表头(使用方 data-sort 指示) */
110
- .admin-table th[data-sort] {
117
+ .table th[data-sort] {
111
118
  cursor: pointer;
112
119
  user-select: none;
113
120
  }
114
- .admin-table th[data-sort]::after {
121
+ .table th[data-sort]::after {
115
122
  content: '↕';
116
123
  margin-left: var(--density-space-xs);
117
124
  color: var(--token-text-faint);
118
125
  opacity: 0.6;
119
126
  }
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); }
127
+ .table th[data-sort='asc']::after { content: '↑'; opacity: 1; color: var(--token-accent); }
128
+ .table th[data-sort='desc']::after { content: '↓'; opacity: 1; color: var(--token-accent); }
129
+
130
+ /* ── 斑马纹 ── */
131
+ .table--zebra tbody tr:nth-child(odd) {
132
+ background: color-mix(in srgb, var(--token-bg-soft) 50%, transparent);
133
+ }
134
+ .table--zebra tbody tr:hover {
135
+ background: var(--token-surface-strong);
136
+ }
137
+
138
+ /* ── 表格标题(caption) ── */
139
+ .table-caption {
140
+ text-align: left;
141
+ padding: var(--density-space-sm) calc(var(--density-space-md) + 2px);
142
+ font-size: var(--density-font-size-md);
143
+ font-weight: 600;
144
+ color: var(--token-text);
145
+ caption-side: top;
146
+ }
147
+
148
+ /* ── 首列粘滞(长表横向滚动时保持可见) ── */
149
+ .table--sticky-col th:first-child,
150
+ .table--sticky-col .col-fixed {
151
+ position: sticky;
152
+ left: 0;
153
+ z-index: calc(var(--z-raised) + 1);
154
+ background: var(--token-bg-soft);
155
+ box-shadow: 2px 0 4px color-mix(in srgb, var(--token-text) 5%, transparent);
156
+ }
157
+ .table--sticky-col tbody td.col-fixed {
158
+ background: var(--token-surface);
159
+ }
160
+ .table--sticky-col tbody tr:hover td.col-fixed {
161
+ background: var(--token-surface-strong);
162
+ }
163
+
164
+ /* ── 展开行 ── */
165
+ .table .row-expand > td {
166
+ padding: 0 var(--density-space-md);
167
+ background: var(--token-bg-soft);
168
+ border-bottom: 1px solid var(--token-line-soft);
169
+ font-size: var(--density-font-size-sm);
170
+ color: var(--token-text-muted);
171
+ }
172
+ .table .row-expand[hidden] { display: none; }
173
+ /* 展开行的内容容器(使用方可加 padding/动画) */
174
+ .row-expand-content {
175
+ padding: var(--density-space-sm) 0;
176
+ animation: row-expand-in var(--icen-tx-fast) var(--ease-out);
177
+ }
178
+ @keyframes row-expand-in {
179
+ from { opacity: 0; transform: translateY(-4px); }
180
+ to { opacity: 1; transform: translateY(0); }
181
+ }
182
+
183
+ /* ── 列宽工具类 ── */
184
+ .table .col-narrow { width: 80px; }
185
+ .table .col-wide { width: 240px; }
186
+ .table .col-action { width: 1%; white-space: nowrap; }
187
+
188
+ /* ── 行状态色 ── */
189
+ .table tbody tr.row-success { background: color-mix(in srgb, var(--token-success) 6%, transparent); }
190
+ .table tbody tr.row-warning { background: color-mix(in srgb, var(--token-warning) 6%, transparent); }
191
+ .table tbody tr.row-error { background: color-mix(in srgb, var(--token-error) 6%, transparent); }
192
+
193
+ /* ── 粘滞表头阴影提示 ── */
194
+ .table thead th {
195
+ box-shadow: 0 1px 0 var(--token-line);
196
+ }
197
+
198
+ /* ── 表头分组(rowspan/colspan 样式) ── */
199
+ .table th[rowspan] { vertical-align: bottom; }
200
+
201
+ @media (prefers-reduced-motion: reduce) {
202
+ .row-expand-content { animation: none; }
203
+ }
@@ -2,20 +2,19 @@
2
2
  * @icen.ai/ui — Component: tabs(分区导航 / 子导航)
3
3
  *
4
4
  * 完整契约:
5
- * <div class="page-tabs" role="tablist"> ← 页面内分区
6
- * <button class="page-tab [.active]" role="tab" [aria-selected="true"]>…</button>
5
+ * <div class="tabs" role="tablist"> ← 页面内分区
6
+ * <button class="page-tab [.is-active]" role="tab" [aria-selected="true"]>…</button>
7
7
  * </div>
8
8
  *
9
- * <nav class="admin-tabs" role="navigation"> ← 顶部子导航(a 链接)
10
- * <a class="[.active]" [aria-current="page"] href="…">…</a>
9
+ * <nav class="tabs" role="navigation"> ← 顶部子导航(a 链接)
10
+ * <a class="[.is-active]" [aria-current="page"] href="…">…</a>
11
11
  * </nav>
12
12
  *
13
13
  * 与 behaviors/tabs 配套:[data-tabs] > [data-tab] + [data-tab-panel],行为层维护 aria。
14
- * 窄屏换行不滚动(CLAUDE.md 纪律:tab 条永不出现滚动条)。
14
+ * 窄屏换行不滚动(纪律:tab 条永不出现滚动条,一律 flex-wrap: wrap)。
15
15
  */
16
16
 
17
- .page-tabs,
18
- .admin-tabs {
17
+ .tabs {
19
18
  display: flex;
20
19
  gap: 2px;
21
20
  flex-wrap: wrap;
@@ -47,14 +46,14 @@
47
46
  border-radius: var(--radius-sm);
48
47
  box-shadow: var(--icen-ring);
49
48
  }
50
- .page-tab.active,
49
+ .page-tab.is-active,
51
50
  .page-tab[aria-selected='true'] {
52
51
  color: var(--token-accent);
53
52
  border-bottom-color: var(--token-accent);
54
53
  font-weight: 700;
55
54
  }
56
55
 
57
- .admin-tabs a {
56
+ .tabs a {
58
57
  padding: var(--density-space-sm) var(--density-space-md);
59
58
  font-size: var(--density-font-size-md);
60
59
  color: var(--token-text-muted);
@@ -66,48 +65,159 @@
66
65
  color var(--icen-tx-fast),
67
66
  border-color var(--icen-tx-fast);
68
67
  }
69
- .admin-tabs a:hover {
68
+ .tabs a:hover {
70
69
  color: var(--token-accent);
71
70
  text-decoration: none;
72
71
  }
73
- .admin-tabs a:focus-visible {
72
+ .tabs a:focus-visible {
74
73
  outline: none;
75
74
  border-radius: var(--radius-sm);
76
75
  box-shadow: var(--icen-ring);
77
76
  }
78
- .admin-tabs a.active,
79
- .admin-tabs a[aria-current='page'] {
77
+ .tabs a.is-active,
78
+ .tabs a[aria-current='page'] {
80
79
  color: var(--token-accent);
81
80
  border-bottom-color: var(--token-accent);
82
81
  }
83
82
 
84
- /* 卡片变体(可选 .page-tabs--cards):tab 变成 pill,没有底边线 */
85
- .page-tabs--cards {
83
+ /* 卡片变体(可选 .tabs--cards):tab 变成 pill,没有底边线 */
84
+ .tabs--cards {
86
85
  border-bottom: 0;
87
86
  gap: var(--density-space-xs);
88
87
  margin-bottom: var(--density-space-md);
89
88
  }
90
- .page-tabs--cards .page-tab {
89
+ .tabs--cards .page-tab {
91
90
  padding: var(--density-space-xs) var(--density-space-md);
92
91
  border: 1px solid var(--token-outline-soft);
93
92
  border-radius: var(--radius-full);
94
93
  margin-bottom: 0;
95
94
  }
96
- .page-tabs--cards .page-tab.active,
97
- .page-tabs--cards .page-tab[aria-selected='true'] {
95
+ .tabs--cards .page-tab.is-active,
96
+ .tabs--cards .page-tab[aria-selected='true'] {
98
97
  background: var(--token-accent);
99
- color: var(--token-on-accent, #fff);
98
+ color: var(--token-on-accent);
100
99
  border-color: var(--token-accent);
101
100
  font-weight: 500;
102
101
  }
103
102
 
104
103
  /* 等宽变体:tab 平均占满容器 */
105
- .page-tabs--equal .page-tab,
106
- .admin-tabs--equal a {
104
+ .tabs--equal .page-tab,
105
+ .tabs--equal a {
107
106
  flex: 1;
108
107
  text-align: center;
109
108
  }
110
109
 
110
+ /* ── 增强 ── */
111
+
112
+ /* 尺寸变体 */
113
+ .tabs--sm .page-tab {
114
+ padding: var(--density-space-xs) var(--density-space-sm);
115
+ font-size: var(--density-font-size-sm);
116
+ }
117
+ .tabs--lg .page-tab {
118
+ padding: var(--density-space-md) calc(var(--density-space-md) + 4px);
119
+ font-size: var(--density-font-size-lg);
120
+ }
121
+
122
+ /* Tab 带图标 */
123
+ .page-tab-icon {
124
+ width: 14px;
125
+ height: 14px;
126
+ flex-shrink: 0;
127
+ margin-right: 2px;
128
+ }
129
+
130
+ /* Tab 带徽章/计数 */
131
+ .page-tab-badge {
132
+ display: inline-flex;
133
+ align-items: center;
134
+ justify-content: center;
135
+ min-width: 18px;
136
+ height: 18px;
137
+ padding: 0 5px;
138
+ margin-left: var(--density-space-xs);
139
+ font-size: var(--density-font-size-2xs);
140
+ font-weight: 600;
141
+ color: var(--token-text-faint);
142
+ background: var(--token-fill-2);
143
+ border-radius: var(--radius-full);
144
+ font-variant-numeric: tabular-nums;
145
+ line-height: 1;
146
+ }
147
+ .page-tab.is-active .page-tab-badge,
148
+ .page-tab[aria-selected='true'] .page-tab-badge {
149
+ background: var(--token-accent);
150
+ color: var(--token-on-accent);
151
+ }
152
+
153
+ /* 可关闭 tab */
154
+ .page-tab-close {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ width: 16px;
159
+ height: 16px;
160
+ margin-left: var(--density-space-xs);
161
+ margin-right: -4px;
162
+ padding: 0;
163
+ border: 0;
164
+ border-radius: var(--radius-full);
165
+ background: transparent;
166
+ color: var(--token-text-faint);
167
+ cursor: pointer;
168
+ transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
169
+ }
170
+ .page-tab-close:hover {
171
+ color: var(--token-error);
172
+ background: var(--token-error-muted);
173
+ }
174
+ .page-tab-close svg { width: 10px; height: 10px; }
175
+
176
+ /* 分段式 tab(替代底边线,用 segmented 视觉) */
177
+ .tabs--segment {
178
+ display: inline-flex;
179
+ border-bottom: none;
180
+ gap: 2px;
181
+ padding: 2px;
182
+ background: var(--token-panel);
183
+ border: 1px solid var(--token-outline-soft);
184
+ border-radius: var(--radius-md);
185
+ box-shadow: var(--token-shadow-inset);
186
+ }
187
+ .tabs--segment .page-tab {
188
+ border-bottom: none;
189
+ margin-bottom: 0;
190
+ padding: var(--density-space-xs) var(--density-space-md);
191
+ border-radius: var(--radius-sm);
192
+ }
193
+ .tabs--segment .page-tab.is-active,
194
+ .tabs--segment .page-tab[aria-selected='true'] {
195
+ background: var(--token-accent);
196
+ color: var(--token-on-accent);
197
+ border-bottom-color: transparent;
198
+ font-weight: 500;
199
+ }
200
+
201
+ /* 垂直 tab(侧栏式) */
202
+ .tabs--vertical {
203
+ flex-direction: column;
204
+ border-bottom: none;
205
+ border-right: 1px solid var(--token-line-soft);
206
+ margin-bottom: 0;
207
+ min-width: 160px;
208
+ }
209
+ .tabs--vertical .page-tab {
210
+ border-bottom: none;
211
+ border-right: 2px solid transparent;
212
+ margin-bottom: 0;
213
+ margin-right: -1px;
214
+ }
215
+ .tabs--vertical .page-tab.is-active,
216
+ .tabs--vertical .page-tab[aria-selected='true'] {
217
+ border-right-color: var(--token-accent);
218
+ border-bottom-color: transparent;
219
+ }
220
+
111
221
  @media (prefers-reduced-motion: reduce) {
112
- .page-tab, .admin-tabs a { transition: none; }
222
+ .page-tab, .tabs a, .page-tab-close { transition: none; }
113
223
  }
@@ -40,7 +40,7 @@
40
40
  .tag-input.is-max,
41
41
  .tag-input.is-max:focus-within {
42
42
  border-color: color-mix(in srgb, var(--token-warning) 60%, transparent);
43
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--token-warning) 14%, transparent);
43
+ box-shadow: var(--icen-ring-warning);
44
44
  }
45
45
  .tag-input.is-disabled,
46
46
  .tag-input[aria-disabled='true'],
@@ -98,7 +98,7 @@
98
98
  cursor: pointer;
99
99
  border-radius: var(--radius-full);
100
100
  color: inherit;
101
- font-size: 11px;
101
+ font-size: var(--density-font-size-xs);
102
102
  line-height: 1;
103
103
  opacity: 0.7;
104
104
  transition:
@@ -109,7 +109,7 @@
109
109
  .tag-chip-x:hover {
110
110
  opacity: 1;
111
111
  background: color-mix(in srgb, currentColor 18%, transparent);
112
- color: var(--token-on-accent, #fff);
112
+ color: var(--token-on-accent);
113
113
  }
114
114
  .tag-chip-x:focus-visible {
115
115
  outline: none;
@@ -135,7 +135,7 @@
135
135
 
136
136
  /* 重复值抖动反馈(behavior 短暂挂 is-shake) */
137
137
  .tag-input.is-shake {
138
- animation: tag-shake 0.32s var(--ease-out);
138
+ animation: tag-shake var(--tag-shake-duration, 0.32s) var(--ease-out);
139
139
  }
140
140
  @keyframes tag-shake {
141
141
  0%, 100% { transform: translateX(0); }
@@ -0,0 +1,164 @@
1
+ /*
2
+ * @icen.ai/ui — Component: tag(展示标签 / Chip)
3
+ *
4
+ * 与 pill 的差异(各司其职,不互相替代):
5
+ * - pill = 状态语义徽章(绿/黄/红/信息/accent),小尺寸,行内只读
6
+ * - tag = 中性展示标签,可选关闭按钮、可选中态、可带图标
7
+ * 常用于:分类、筛选条件、属性标记、邮件标签、文件 tag
8
+ *
9
+ * 完整契约:
10
+ * <span class="tag [.tag--sm|.tag--lg] [.is-selected] [.is-disabled]
11
+ * [.tag--accent|.tag--success|.tag--warning|.tag--error|.tag--info]">
12
+ * <span class="tag-icon">…svg…</span> <!-- 可选左图标 -->
13
+ * <span class="tag-label">标签文本</span>
14
+ * <button class="tag-close" type="button" aria-label="移除">×</button> <!-- 可选 -->
15
+ * </span>
16
+ *
17
+ * 选中态 .is-selected 由消费方驱动(用于筛选条件等);
18
+ * .tag-close 点击后消费方决定是否从 DOM 移除本节点(本组件不自动删除)。
19
+ */
20
+
21
+ .tag {
22
+ display: inline-flex;
23
+ align-items: center;
24
+ gap: 4px;
25
+ padding: 0 var(--density-space-sm);
26
+ height: 24px;
27
+ background: var(--token-fill-1);
28
+ color: var(--token-text-muted);
29
+ border: 1px solid var(--token-line-soft);
30
+ border-radius: var(--radius-sm);
31
+ font: inherit;
32
+ font-size: var(--density-font-size-sm);
33
+ letter-spacing: 0.01em;
34
+ white-space: nowrap;
35
+ vertical-align: middle;
36
+ transition:
37
+ background-color var(--icen-tx-fast),
38
+ border-color var(--icen-tx-fast),
39
+ color var(--icen-tx-fast);
40
+ }
41
+ .tag:hover {
42
+ background: var(--token-fill-2);
43
+ color: var(--token-text);
44
+ }
45
+
46
+ /* 尺寸变体 */
47
+ .tag--sm {
48
+ height: 20px;
49
+ padding: 0 6px;
50
+ font-size: var(--density-font-size-xs);
51
+ gap: 3px;
52
+ }
53
+ .tag--lg {
54
+ height: 28px;
55
+ padding: 0 10px;
56
+ font-size: var(--density-font-size-md);
57
+ gap: 6px;
58
+ }
59
+
60
+ /* ── 图标 ── */
61
+ .tag-icon {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ flex-shrink: 0;
66
+ color: var(--token-text-faint);
67
+ }
68
+ .tag-icon svg { width: 12px; height: 12px; }
69
+ .tag--sm .tag-icon svg { width: 10px; height: 10px; }
70
+ .tag--lg .tag-icon svg { width: 14px; height: 14px; }
71
+ .tag-label {
72
+ overflow: hidden;
73
+ text-overflow: ellipsis;
74
+ max-width: 240px;
75
+ }
76
+
77
+ /* ── 关闭按钮(消费方点击后自行决定是否从 DOM 移除) ── */
78
+ .tag-close {
79
+ display: inline-flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ width: 16px;
83
+ height: 16px;
84
+ padding: 0;
85
+ margin-right: -4px;
86
+ margin-left: 2px;
87
+ border: 0;
88
+ border-radius: var(--radius-xs);
89
+ background: transparent;
90
+ color: inherit;
91
+ opacity: 0.6;
92
+ cursor: pointer;
93
+ -webkit-tap-highlight-color: transparent;
94
+ transition: opacity var(--icen-tx-fast), background-color var(--icen-tx-fast);
95
+ }
96
+ .tag-close:hover {
97
+ opacity: 1;
98
+ background: color-mix(in srgb, var(--token-text-faint) 22%, transparent);
99
+ }
100
+ .tag-close:focus-visible {
101
+ outline: none;
102
+ box-shadow: var(--icen-ring);
103
+ }
104
+ .tag-close svg { width: 10px; height: 10px; }
105
+ .tag--sm .tag-close { width: 14px; height: 14px; margin-right: -2px; }
106
+ .tag--lg .tag-close { width: 18px; height: 18px; margin-right: -6px; }
107
+
108
+ /* ── 选中态(用于筛选条件等)── */
109
+ .tag.is-selected {
110
+ background: var(--token-accent-subtle);
111
+ color: var(--token-accent-deep, var(--token-accent));
112
+ border-color: color-mix(in srgb, var(--token-accent) 40%, transparent);
113
+ }
114
+ .tag.is-selected .tag-icon { color: var(--token-accent); }
115
+
116
+ /* ── 语义色变体(与 pill 对齐,但底色更淡,作分类着色用)── */
117
+ .tag--accent {
118
+ background: color-mix(in srgb, var(--token-accent) 10%, transparent);
119
+ color: var(--token-accent-deep, var(--token-accent));
120
+ border-color: color-mix(in srgb, var(--token-accent) 30%, transparent);
121
+ }
122
+ .tag--success {
123
+ background: color-mix(in srgb, var(--token-success) 10%, transparent);
124
+ color: var(--token-success);
125
+ border-color: color-mix(in srgb, var(--token-success) 30%, transparent);
126
+ }
127
+ .tag--warning {
128
+ background: color-mix(in srgb, var(--token-warning) 10%, transparent);
129
+ color: var(--token-warning);
130
+ border-color: color-mix(in srgb, var(--token-warning) 30%, transparent);
131
+ }
132
+ .tag--error {
133
+ background: color-mix(in srgb, var(--token-error) 10%, transparent);
134
+ color: var(--token-error);
135
+ border-color: color-mix(in srgb, var(--token-error) 30%, transparent);
136
+ }
137
+ .tag--info {
138
+ background: color-mix(in srgb, var(--token-info) 10%, transparent);
139
+ color: var(--token-info);
140
+ border-color: color-mix(in srgb, var(--token-info) 30%, transparent);
141
+ }
142
+
143
+ /* ── 禁用态 ── */
144
+ .tag.is-disabled,
145
+ .tag[aria-disabled='true'] {
146
+ opacity: var(--icen-disabled-opacity);
147
+ cursor: not-allowed;
148
+ pointer-events: none;
149
+ }
150
+
151
+ /* ── 可点击(用于可筛选 tag) ── */
152
+ button.tag,
153
+ .tag[role='button'] {
154
+ cursor: pointer;
155
+ -webkit-tap-highlight-color: transparent;
156
+ }
157
+ .tag:where(button, [role='button']):focus-visible {
158
+ outline: none;
159
+ box-shadow: var(--icen-ring);
160
+ }
161
+
162
+ @media (prefers-reduced-motion: reduce) {
163
+ .tag, .tag-close { transition: none; }
164
+ }
@@ -0,0 +1,2 @@
1
+ // @icen.ai/ui — kit: tag(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './tag.css';