@zhang_jifan/fanui 2.1.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 (127) hide show
  1. package/ACCESSIBILITY.md +61 -0
  2. package/CHANGELOG.md +112 -0
  3. package/COMMERCIAL-LICENSE.md +71 -0
  4. package/DESIGN-SYSTEM.md +294 -0
  5. package/LICENSE +661 -0
  6. package/README.md +348 -0
  7. package/USAGE.md +1926 -0
  8. package/_index.scss +20 -0
  9. package/dist/fanui.cjs +15 -0
  10. package/dist/fanui.css +28209 -0
  11. package/dist/fanui.d.ts +353 -0
  12. package/dist/fanui.esm.js +71 -0
  13. package/dist/fanui.js +4782 -0
  14. package/dist/fanui.min.css +1 -0
  15. package/dist/fanui.mjs +71 -0
  16. package/dist/modules/fanui-advanced.css +830 -0
  17. package/dist/modules/fanui-advanced.min.css +129 -0
  18. package/dist/modules/fanui-alert.css +575 -0
  19. package/dist/modules/fanui-alert.min.css +122 -0
  20. package/dist/modules/fanui-badge.css +426 -0
  21. package/dist/modules/fanui-badge.min.css +77 -0
  22. package/dist/modules/fanui-base.css +1394 -0
  23. package/dist/modules/fanui-base.min.css +236 -0
  24. package/dist/modules/fanui-blog.css +762 -0
  25. package/dist/modules/fanui-blog.min.css +108 -0
  26. package/dist/modules/fanui-button.css +526 -0
  27. package/dist/modules/fanui-button.min.css +56 -0
  28. package/dist/modules/fanui-card.css +291 -0
  29. package/dist/modules/fanui-card.min.css +52 -0
  30. package/dist/modules/fanui-choice.css +539 -0
  31. package/dist/modules/fanui-choice.min.css +83 -0
  32. package/dist/modules/fanui-dashboard.css +1057 -0
  33. package/dist/modules/fanui-dashboard.min.css +160 -0
  34. package/dist/modules/fanui-data.css +687 -0
  35. package/dist/modules/fanui-data.min.css +125 -0
  36. package/dist/modules/fanui-feedback.css +432 -0
  37. package/dist/modules/fanui-feedback.min.css +79 -0
  38. package/dist/modules/fanui-form.css +779 -0
  39. package/dist/modules/fanui-form.min.css +123 -0
  40. package/dist/modules/fanui-glass.css +497 -0
  41. package/dist/modules/fanui-glass.min.css +46 -0
  42. package/dist/modules/fanui-input-plus.css +799 -0
  43. package/dist/modules/fanui-input-plus.min.css +124 -0
  44. package/dist/modules/fanui-interactive.css +960 -0
  45. package/dist/modules/fanui-interactive.min.css +138 -0
  46. package/dist/modules/fanui-layout.css +4399 -0
  47. package/dist/modules/fanui-layout.min.css +1172 -0
  48. package/dist/modules/fanui-marketing.css +1125 -0
  49. package/dist/modules/fanui-marketing.min.css +144 -0
  50. package/dist/modules/fanui-nav.css +458 -0
  51. package/dist/modules/fanui-nav.min.css +76 -0
  52. package/dist/modules/fanui-overlay.css +547 -0
  53. package/dist/modules/fanui-overlay.min.css +91 -0
  54. package/dist/modules/fanui-table.css +290 -0
  55. package/dist/modules/fanui-table.min.css +62 -0
  56. package/dist/modules/fanui-tabs.css +333 -0
  57. package/dist/modules/fanui-tabs.min.css +58 -0
  58. package/dist/modules/fanui-themes.css +2728 -0
  59. package/dist/modules/fanui-themes.min.css +40 -0
  60. package/dist/modules/fanui-tokens.css +587 -0
  61. package/dist/modules/fanui-tokens.min.css +3 -0
  62. package/dist/modules/fanui-utilities.css +6557 -0
  63. package/dist/modules/fanui-utilities.min.css +1762 -0
  64. package/dist/modules/sizes.json +103 -0
  65. package/docs/MIGRATION-v2.md +179 -0
  66. package/examples/01-script-tag.html +149 -0
  67. package/examples/02-react/README.md +39 -0
  68. package/examples/02-react/index.html +12 -0
  69. package/examples/02-react/package.json +20 -0
  70. package/examples/02-react/src/App.jsx +108 -0
  71. package/examples/02-react/src/main.jsx +13 -0
  72. package/examples/02-react/src/useFanUI.js +87 -0
  73. package/examples/02-react/vite.config.js +6 -0
  74. package/examples/03-vue3/README.md +67 -0
  75. package/examples/03-vue3/index.html +12 -0
  76. package/examples/03-vue3/package.json +19 -0
  77. package/examples/03-vue3/src/App.vue +93 -0
  78. package/examples/03-vue3/src/main.js +8 -0
  79. package/examples/03-vue3/src/useFanUI.js +85 -0
  80. package/examples/03-vue3/vite.config.js +6 -0
  81. package/examples/04-vue2-cdn.html +93 -0
  82. package/examples/README.md +34 -0
  83. package/package.json +127 -0
  84. package/src/_functions.scss +319 -0
  85. package/src/_mixins.scss +1202 -0
  86. package/src/_tokens.scss +180 -0
  87. package/src/_variables.scss +491 -0
  88. package/src/base/_animations.scss +210 -0
  89. package/src/base/_reset.scss +359 -0
  90. package/src/base/_rtl.scss +133 -0
  91. package/src/base/_typography.scss +353 -0
  92. package/src/components/_advanced.scss +763 -0
  93. package/src/components/_alert.scss +322 -0
  94. package/src/components/_badge.scss +279 -0
  95. package/src/components/_blog.scss +693 -0
  96. package/src/components/_button.scss +351 -0
  97. package/src/components/_capabilities.scss +463 -0
  98. package/src/components/_card.scss +281 -0
  99. package/src/components/_choice.scss +444 -0
  100. package/src/components/_dashboard.scss +950 -0
  101. package/src/components/_data.scss +618 -0
  102. package/src/components/_feedback.scss +403 -0
  103. package/src/components/_form.scss +408 -0
  104. package/src/components/_glass.scss +374 -0
  105. package/src/components/_input-plus.scss +730 -0
  106. package/src/components/_interactive.scss +885 -0
  107. package/src/components/_marketing.scss +982 -0
  108. package/src/components/_nav.scss +480 -0
  109. package/src/components/_overlay.scss +517 -0
  110. package/src/components/_table.scss +276 -0
  111. package/src/components/_tabs.scss +338 -0
  112. package/src/fanui.d.ts +353 -0
  113. package/src/fanui.js +4782 -0
  114. package/src/fanui.scss +79 -0
  115. package/src/glass/_switches.scss +36 -0
  116. package/src/glass/_tokens.scss +259 -0
  117. package/src/layout/_container.scss +111 -0
  118. package/src/layout/_flex.scss +164 -0
  119. package/src/layout/_grid.scss +180 -0
  120. package/src/themes/_accents.scss +201 -0
  121. package/src/themes/_palettes.scss +180 -0
  122. package/src/utilities/_borders.scss +104 -0
  123. package/src/utilities/_display.scss +145 -0
  124. package/src/utilities/_effects.scss +210 -0
  125. package/src/utilities/_sizing.scss +123 -0
  126. package/src/utilities/_spacing.scss +118 -0
  127. package/src/utilities/_texture.scss +323 -0
@@ -0,0 +1,322 @@
1
+ // =============================================================================
2
+ // fanUI · 警告框 Alert / 提示 Message
3
+ // -----------------------------------------------------------------------------
4
+ // .fanui-alert 基础警告框(默认 info 风格)
5
+ // .fanui-alert--<semantic> 语义色:primary/accent/success/warning/danger/info
6
+ // .fanui-alert--solid 实心底色
7
+ // .fanui-alert--outline 描边风格
8
+ // .fanui-alert__icon / __title / __desc / __close / __actions
9
+ // .fanui-alert--dismissible 可关闭
10
+ // .fanui-alert--banner 顶部横幅
11
+ // .fanui-alert.is-hidden 隐藏
12
+ // =============================================================================
13
+
14
+ @use "sass:map";
15
+ @use "../functions" as fn;
16
+ @use "../variables" as v;
17
+ @use "../mixins" as mx;
18
+
19
+ .fanui-alert {
20
+ position: relative;
21
+ display: flex;
22
+ align-items: flex-start;
23
+ gap: fn.fanui-var("space-3");
24
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
25
+ font-size: fn.fanui-var("font-size-base");
26
+ line-height: 1.6;
27
+ color: fn.fanui-var("color-text");
28
+ background-color: fn.fanui-var("color-surface-sunken");
29
+ border: 1px solid fn.fanui-var("color-border");
30
+ border-inline-start: 3px solid fn.fanui-var("color-text-subtle");
31
+ border-radius: fn.fanui-var("radius-md");
32
+ box-sizing: border-box;
33
+ @include mx.fanui-transition((opacity, transform), v.$fanui-duration-base);
34
+
35
+ &.is-hidden,
36
+ &[hidden] {
37
+ display: none;
38
+ }
39
+
40
+ &__icon {
41
+ flex: 0 0 auto;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ width: 20px;
46
+ height: 20px;
47
+ margin-top: 1px;
48
+ font-size: 16px;
49
+ line-height: 1;
50
+ }
51
+
52
+ &__content {
53
+ flex: 1 1 auto;
54
+ min-width: 0;
55
+ }
56
+
57
+ &__title {
58
+ margin: 0 0 2px;
59
+ font-size: fn.fanui-var("font-size-base");
60
+ font-weight: 600;
61
+ line-height: 1.5;
62
+ color: fn.fanui-var("color-text-strong");
63
+ }
64
+
65
+ &__desc {
66
+ margin: 0;
67
+ font-size: fn.fanui-var("font-size-base");
68
+ color: fn.fanui-var("color-text-muted");
69
+ word-break: break-word;
70
+ }
71
+
72
+ &__actions {
73
+ margin-top: fn.fanui-var("space-3");
74
+ display: flex;
75
+ align-items: center;
76
+ gap: fn.fanui-var("space-2");
77
+ }
78
+
79
+ &__close {
80
+ flex: 0 0 auto;
81
+ display: inline-flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ width: 22px;
85
+ height: 22px;
86
+ margin: -2px -4px 0 0;
87
+ padding: 0;
88
+ font-size: 14px;
89
+ line-height: 1;
90
+ color: inherit;
91
+ opacity: 0.6;
92
+ background: none;
93
+ border: 0;
94
+ border-radius: fn.fanui-var("radius-sm");
95
+ cursor: pointer;
96
+ @include mx.fanui-transition((opacity, background-color), v.$fanui-duration-fast);
97
+
98
+ &:hover {
99
+ opacity: 1;
100
+ background-color: fn.fanui-alpha(#0f172a, 0.08);
101
+ }
102
+
103
+ &:focus-visible { @include mx.fanui-focus-ring(); }
104
+ }
105
+
106
+ // 内嵌列表
107
+ ul, ol {
108
+ margin: fn.fanui-var("space-2") 0 0;
109
+ padding-inline-start: 1.25em;
110
+ color: fn.fanui-var("color-text-muted");
111
+ }
112
+ }
113
+
114
+ // -----------------------------------------------------------------------------
115
+ // 1. 语义变体(柔和 / solid / outline)
116
+ // -----------------------------------------------------------------------------
117
+
118
+ @each $name, $color in v.$fanui-theme-colors {
119
+ // 柔和(默认)
120
+ .fanui-alert--#{$name} {
121
+ background-color: #{fn.fanui-var("#{$name}-soft", fn.fanui-alpha($color, 0.10))};
122
+ border-color: #{fn.fanui-var("#{$name}-border", fn.fanui-alpha($color, 0.28))};
123
+ border-left-color: #{fn.fanui-var("#{$name}", $color)};
124
+
125
+ .fanui-alert__icon { color: #{fn.fanui-var("#{$name}", $color)}; }
126
+ .fanui-alert__title { color: #{fn.fanui-shade($color, 26%)}; }
127
+ .fanui-alert__desc { color: #{fn.fanui-shade($color, 12%)}; }
128
+
129
+ [data-fanui-theme="dark"] & {
130
+ background-color: #{fn.fanui-alpha($color, 0.16)};
131
+ border-color: #{fn.fanui-alpha($color, 0.34)};
132
+
133
+ // C1:暗色下不能再从「浅色基准色」推导文字(tint 只会得到一档中间色,
134
+ // 落在半透明语义底上约 3.9 ~ 4.5:1,正文级不达标)。
135
+ // 改为读取暗色令牌:-ink 是暗色下最亮的一档,--#{$name} 是 400 阶。
136
+ .fanui-alert__title { color: #{fn.fanui-var("#{$name}-ink", fn.fanui-tint($color, 32%))}; }
137
+ .fanui-alert__desc { color: #{fn.fanui-var("#{$name}", fn.fanui-tint($color, 12%))}; }
138
+ }
139
+ }
140
+
141
+ // 实心
142
+ .fanui-alert--solid.fanui-alert--#{$name} {
143
+ background-color: #{fn.fanui-var("#{$name}", $color)};
144
+ border-color: #{fn.fanui-var("#{$name}", $color)};
145
+ border-left-color: #{fn.fanui-shade($color, 25%)};
146
+ // C1:前景色必须跟随运行时令牌。暗色下 --#{$name} 是 400 阶(浅色底),
147
+ // 若沿用构建期按浅色基准算出的「白字」,对比度只剩 2.7:1。
148
+ color: #{fn.fanui-var("#{$name}-fg", fn.fanui-contrast($color))};
149
+
150
+ .fanui-alert__title,
151
+ .fanui-alert__desc,
152
+ .fanui-alert__icon { color: inherit; }
153
+ .fanui-alert__desc { opacity: 0.88; }
154
+ }
155
+
156
+ // 描边
157
+ .fanui-alert--outline.fanui-alert--#{$name} {
158
+ background-color: transparent;
159
+ border-color: #{fn.fanui-alpha($color, 0.45)};
160
+ border-left-color: #{fn.fanui-var("#{$name}", $color)};
161
+
162
+ .fanui-alert__icon { color: #{fn.fanui-var("#{$name}", $color)}; }
163
+ }
164
+ }
165
+
166
+ // -----------------------------------------------------------------------------
167
+ // 2. 尺寸与布局
168
+ // -----------------------------------------------------------------------------
169
+
170
+ .fanui-alert--sm {
171
+ padding: fn.fanui-var("space-2") fn.fanui-var("space-3");
172
+ font-size: fn.fanui-var("font-size-sm");
173
+
174
+ .fanui-alert__icon { width: 16px; height: 16px; font-size: 14px; }
175
+ }
176
+
177
+ .fanui-alert--lg {
178
+ padding: fn.fanui-var("space-4") fn.fanui-var("space-5");
179
+
180
+ .fanui-alert__title { font-size: fn.fanui-var("font-size-md"); }
181
+ }
182
+
183
+ .fanui-alert--center {
184
+ align-items: center;
185
+ text-align: center;
186
+ flex-direction: column;
187
+
188
+ .fanui-alert__icon { margin-top: 0; }
189
+ }
190
+
191
+ .fanui-alert--flat {
192
+ border-radius: 0;
193
+ border-inline-start-width: 0;
194
+ border-top-width: 0;
195
+ border-inline-end-width: 0;
196
+ }
197
+
198
+ .fanui-alert--bordered-all {
199
+ border-inline-start-width: 1px;
200
+ }
201
+
202
+ // -----------------------------------------------------------------------------
203
+ // 3. 横幅 / 内联提示
204
+ // -----------------------------------------------------------------------------
205
+
206
+ .fanui-alert--banner {
207
+ border-radius: 0;
208
+ border-width: 0 0 1px 0;
209
+ border-inline-start-width: 0;
210
+ padding: fn.fanui-var("space-3") fn.fanui-var("container-px", 16px);
211
+ justify-content: center;
212
+
213
+ .fanui-alert__content {
214
+ flex: 0 1 auto;
215
+ display: flex;
216
+ align-items: center;
217
+ gap: fn.fanui-var("space-2");
218
+ flex-wrap: wrap;
219
+ }
220
+ }
221
+
222
+ // 行内提示(表单旁)
223
+ .fanui-alert--inline {
224
+ display: inline-flex;
225
+ padding: 4px 10px;
226
+ font-size: fn.fanui-var("font-size-sm");
227
+ border-radius: fn.fanui-var("radius-full");
228
+ border-inline-start-width: 1px;
229
+ }
230
+
231
+ // -----------------------------------------------------------------------------
232
+ // 4. Toast / 轻提示(可独立使用)
233
+ // -----------------------------------------------------------------------------
234
+
235
+ .fanui-toast-region {
236
+ position: fixed;
237
+ top: fn.fanui-var("space-5");
238
+ left: 50%;
239
+ transform: translateX(-50%);
240
+ z-index: fn.fanui-var("z-toast", v.$fanui-zindex-toast);
241
+ display: flex;
242
+ flex-direction: column;
243
+ align-items: center;
244
+ gap: fn.fanui-var("space-3");
245
+ pointer-events: none;
246
+ max-width: min(92vw, 480px);
247
+
248
+ &--top-right {
249
+ left: auto;
250
+ right: fn.fanui-var("space-5");
251
+ transform: none;
252
+ align-items: flex-end;
253
+ }
254
+
255
+ &--top-left {
256
+ left: fn.fanui-var("space-5");
257
+ transform: none;
258
+ align-items: flex-start;
259
+ }
260
+
261
+ &--bottom {
262
+ top: auto;
263
+ bottom: fn.fanui-var("space-5");
264
+ }
265
+
266
+ &--bottom-right {
267
+ top: auto;
268
+ bottom: fn.fanui-var("space-5");
269
+ left: auto;
270
+ right: fn.fanui-var("space-5");
271
+ transform: none;
272
+ align-items: flex-end;
273
+ }
274
+ }
275
+
276
+ .fanui-toast {
277
+ display: flex;
278
+ align-items: center;
279
+ gap: fn.fanui-var("space-3");
280
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
281
+ min-width: 220px;
282
+ // 提示默认「不拦截指针事件」。
283
+ // 原因:提示区常固定在右上/右下,与页面控件(如主题切换按钮)几何重叠;
284
+ // 一旦捕获指针,底下控件在提示存续期间完全点不到(实测复现:page.click 30s 超时)。
285
+ // 只有确实包含可交互内容(操作按钮 / 链接)的提示才用 --interactive 恢复命中。
286
+ pointer-events: none;
287
+ font-size: fn.fanui-var("font-size-base");
288
+ color: fn.fanui-var("color-text");
289
+ background-color: fn.fanui-var("color-surface");
290
+ border: 1px solid fn.fanui-var("color-border");
291
+ border-radius: fn.fanui-var("radius-md");
292
+ box-shadow: fn.fanui-var("shadow-lg");
293
+ animation: fanui-toast-in v.$fanui-duration-base v.$fanui-ease-out both;
294
+
295
+ &--interactive { pointer-events: auto; }
296
+
297
+ &__icon { flex: 0 0 auto; display: flex; font-size: 16px; }
298
+ &__content { flex: 1 1 auto; min-width: 0; }
299
+ &__title { font-weight: 600; color: fn.fanui-var("color-text-strong"); }
300
+ &__desc { font-size: fn.fanui-var("font-size-sm"); color: fn.fanui-var("color-text-muted"); }
301
+ &__actions { display: flex; gap: fn.fanui-var("space-2"); margin-inline-start: fn.fanui-var("space-2"); flex: 0 0 auto; }
302
+
303
+ &.is-leaving {
304
+ animation: fanui-fade-out v.$fanui-duration-base v.$fanui-ease-in both;
305
+ }
306
+
307
+ @each $name, $color in v.$fanui-theme-colors {
308
+ &--#{$name} {
309
+ border-inline-start: 3px solid #{fn.fanui-var("#{$name}", $color)};
310
+ .fanui-toast__icon { color: #{fn.fanui-var("#{$name}", $color)}; }
311
+ }
312
+ }
313
+
314
+ &--solid {
315
+ background-color: fn.fanui-var("color-text-strong");
316
+ color: fn.fanui-var("color-surface");
317
+ border-color: transparent;
318
+
319
+ .fanui-toast__title { color: inherit; }
320
+ .fanui-toast__desc { color: rgba(255, 255, 255, 0.72); }
321
+ }
322
+ }
@@ -0,0 +1,279 @@
1
+ // =============================================================================
2
+ // fanUI · 徽标 Badge / 标签 Tag / 圆点 Dot
3
+ // -----------------------------------------------------------------------------
4
+ // .fanui-badge 徽标(实心)
5
+ // .fanui-badge--soft 柔和背景
6
+ // .fanui-badge--outline 描边
7
+ // .fanui-badge--dot 状态点
8
+ // .fanui-badge--pill 胶囊
9
+ // .fanui-badge--xs|sm|md|lg
10
+ // .fanui-tag 可关闭标签
11
+ // =============================================================================
12
+
13
+ @use "sass:map";
14
+ @use "../functions" as fn;
15
+ @use "../variables" as v;
16
+ @use "../mixins" as mx;
17
+
18
+ // -----------------------------------------------------------------------------
19
+ // 1. 基础徽标
20
+ // -----------------------------------------------------------------------------
21
+
22
+ .fanui-badge {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ gap: 4px;
27
+ height: 20px;
28
+ padding: 0 8px;
29
+ font-size: fn.fanui-var("font-size-xs");
30
+ font-weight: 500;
31
+ line-height: 1;
32
+ white-space: nowrap;
33
+ vertical-align: middle;
34
+ color: fn.fanui-var("color-text-inverse");
35
+ background-color: fn.fanui-var("color-text-muted");
36
+ border: 1px solid transparent;
37
+ border-radius: fn.fanui-var("radius-sm");
38
+ box-sizing: border-box;
39
+
40
+ // 计数徽标(更大圆角)
41
+ &--pill { border-radius: 9999px; padding: 0 8px; }
42
+ &--square { border-radius: fn.fanui-var("radius-sm"); }
43
+ &--circle {
44
+ width: 20px;
45
+ padding: 0;
46
+ border-radius: 50%;
47
+ font-variant-numeric: tabular-nums;
48
+ }
49
+
50
+ // 尺寸
51
+ &--xs { height: 16px; padding: 0 6px; font-size: 10px; }
52
+ &--sm { height: 18px; padding: 0 7px; font-size: 11px; }
53
+ &--md { height: 22px; padding: 0 9px; font-size: fn.fanui-var("font-size-xs"); }
54
+ &--lg { height: 26px; padding: 0 11px; font-size: fn.fanui-var("font-size-sm"); }
55
+
56
+ // 后缀/前缀元素
57
+ &__dot {
58
+ width: 6px;
59
+ height: 6px;
60
+ border-radius: 50%;
61
+ background-color: currentColor;
62
+ flex: 0 0 auto;
63
+ }
64
+
65
+ // 数值
66
+ &__count { font-variant-numeric: tabular-nums; }
67
+ }
68
+
69
+ // -----------------------------------------------------------------------------
70
+ // 2. 语义色变体
71
+ // -----------------------------------------------------------------------------
72
+
73
+ @each $name, $color in v.$fanui-theme-colors {
74
+ // 实心
75
+ .fanui-badge--#{$name} {
76
+ @include mx.fanui-badge-variant(fn.fanui-var("#{$name}", $color), fn.fanui-var("#{$name}-fg", fn.fanui-contrast($color)));
77
+ }
78
+
79
+ // 柔和
80
+ .fanui-badge--soft.fanui-badge--#{$name} {
81
+ background-color: fn.fanui-var("#{$name}-soft");
82
+ color: fn.fanui-var("#{$name}");
83
+ border-color: fn.fanui-var("#{$name}-border");
84
+
85
+ [data-fanui-theme="dark"] & {
86
+ // C1:暗色下改读 -ink(该模式下最亮的一档)。
87
+ // 早期用 tint(浅色基准色, 30%) 会得到中间色(如 primary 的 #569eb1),
88
+ // 在本就带语义色调的半透明底上只有 4.24:1;叠在选中行上更低到 3.0:1。
89
+ color: fn.fanui-var("#{$name}-ink", fn.fanui-tint($color, 30%));
90
+ }
91
+ }
92
+
93
+ // 描边
94
+ .fanui-badge--outline.fanui-badge--#{$name} {
95
+ background-color: transparent;
96
+ color: fn.fanui-var("#{$name}");
97
+ border-color: fn.fanui-var("#{$name}");
98
+ }
99
+
100
+ // 全部圆点
101
+ .fanui-badge--dot.fanui-badge--#{$name} {
102
+ background-color: fn.fanui-var("#{$name}");
103
+ }
104
+ }
105
+
106
+ // 中性
107
+ .fanui-badge--neutral,
108
+ .fanui-badge--gray {
109
+ @include mx.fanui-badge-variant(fn.fanui-var("color-text-muted"), fn.fanui-var("color-text-inverse"));
110
+ }
111
+ .fanui-badge--soft.fanui-badge--neutral,
112
+ .fanui-badge--soft.fanui-badge--gray {
113
+ background-color: fn.fanui-var("color-surface-sunken");
114
+ color: fn.fanui-var("color-text-muted");
115
+ border-color: fn.fanui-var("color-border");
116
+ }
117
+
118
+ // -----------------------------------------------------------------------------
119
+ // 3. 状态点
120
+ // -----------------------------------------------------------------------------
121
+
122
+ .fanui-badge--dot {
123
+ width: 8px;
124
+ height: 8px;
125
+ min-width: 8px;
126
+ padding: 0;
127
+ border-radius: 50%;
128
+ background-color: fn.fanui-var("color-text-muted");
129
+ border: 0;
130
+
131
+ &.fanui-badge--lg { width: 10px; height: 10px; min-width: 10px; }
132
+ &.fanui-badge--sm { width: 6px; height: 6px; min-width: 6px; }
133
+
134
+ // 呼吸效果
135
+ &--pulse {
136
+ position: relative;
137
+
138
+ &::after {
139
+ content: "";
140
+ @include mx.fanui-absolute-fill();
141
+ border-radius: 50%;
142
+ background-color: inherit;
143
+ animation: fanui-pulse 1.8s v.$fanui-ease-in-out infinite;
144
+ }
145
+ }
146
+ }
147
+
148
+ // -----------------------------------------------------------------------------
149
+ // 4. 角标(叠加在头像/图标上)
150
+ // -----------------------------------------------------------------------------
151
+
152
+ .fanui-badge-sup {
153
+ position: absolute;
154
+ top: 0;
155
+ right: 0;
156
+ transform: translate(50%, -50%);
157
+ z-index: 1;
158
+ transform-origin: 100% 0;
159
+
160
+ &--dot {
161
+ width: 8px;
162
+ height: 8px;
163
+ padding: 0;
164
+ border-radius: 50%;
165
+ transform: translate(35%, -35%);
166
+ }
167
+
168
+ &--bordered {
169
+ border: 2px solid fn.fanui-var("color-surface");
170
+ }
171
+ }
172
+
173
+ .fanui-badge-wrap {
174
+ position: relative;
175
+ display: inline-flex;
176
+ }
177
+
178
+ // -----------------------------------------------------------------------------
179
+ // 5. 标签 Tag(可关闭)
180
+ // -----------------------------------------------------------------------------
181
+
182
+ .fanui-tag {
183
+ display: inline-flex;
184
+ align-items: center;
185
+ gap: 6px;
186
+ height: 24px;
187
+ padding: 0 8px;
188
+ font-size: fn.fanui-var("font-size-sm");
189
+ line-height: 1;
190
+ color: fn.fanui-var("color-text");
191
+ background-color: fn.fanui-var("color-surface-sunken");
192
+ border: 1px solid fn.fanui-var("color-border");
193
+ border-radius: fn.fanui-var("radius-base");
194
+ box-sizing: border-box;
195
+ white-space: nowrap;
196
+ @include mx.fanui-transition((background-color, border-color, color), v.$fanui-duration-fast);
197
+
198
+ &--pill { border-radius: 9999px; }
199
+ &--solid {
200
+ background-color: fn.fanui-var("color-text");
201
+ color: fn.fanui-var("color-surface");
202
+ border-color: transparent;
203
+ }
204
+ &--outline { background-color: transparent; }
205
+
206
+ &__close {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ width: 14px;
211
+ height: 14px;
212
+ margin-inline-end: -2px;
213
+ padding: 0;
214
+ font-size: 12px;
215
+ line-height: 1;
216
+ color: fn.fanui-var("color-text-subtle");
217
+ background: none;
218
+ border: 0;
219
+ border-radius: 50%;
220
+ cursor: pointer;
221
+ @include mx.fanui-transition((background-color, color), v.$fanui-duration-fast);
222
+
223
+ &:hover {
224
+ color: fn.fanui-var("color-text-strong");
225
+ background-color: fn.fanui-alpha(#0f172a, 0.12);
226
+ }
227
+ }
228
+
229
+ &--interactive {
230
+ cursor: pointer;
231
+
232
+ &:hover { border-color: fn.fanui-var("color-border-strong"); background-color: fn.fanui-var("color-hover"); }
233
+ }
234
+
235
+ &--selected {
236
+ background-color: fn.fanui-var("primary-soft");
237
+ border-color: fn.fanui-var("primary");
238
+ color: fn.fanui-var("primary");
239
+ }
240
+ }
241
+
242
+ @each $name, $color in v.$fanui-theme-colors {
243
+ .fanui-tag--#{$name} {
244
+ background-color: fn.fanui-var("#{$name}-soft");
245
+ border-color: fn.fanui-var("#{$name}-border");
246
+ color: fn.fanui-var("#{$name}");
247
+ }
248
+ }
249
+
250
+ .fanui-tag-list {
251
+ display: flex;
252
+ flex-wrap: wrap;
253
+ gap: fn.fanui-var("space-2");
254
+ }
255
+
256
+ // -----------------------------------------------------------------------------
257
+ // 6. 状态徽标组合(点 + 文案)
258
+ // -----------------------------------------------------------------------------
259
+
260
+ .fanui-status {
261
+ display: inline-flex;
262
+ align-items: center;
263
+ gap: 6px;
264
+ font-size: fn.fanui-var("font-size-sm");
265
+ color: fn.fanui-var("color-text-muted");
266
+
267
+ &__dot {
268
+ width: 6px;
269
+ height: 6px;
270
+ border-radius: 50%;
271
+ background-color: fn.fanui-var("color-text-muted");
272
+ flex: 0 0 auto;
273
+ }
274
+
275
+ &--online { color: fn.fanui-var("success"); .fanui-status__dot { background-color: fn.fanui-var("success"); } }
276
+ &--offline { color: fn.fanui-var("color-text-subtle"); .fanui-status__dot { background-color: fn.fanui-var("color-text-subtle"); } }
277
+ &--busy { color: fn.fanui-var("danger"); .fanui-status__dot { background-color: fn.fanui-var("danger"); } }
278
+ &--away { color: fn.fanui-var("warning"); .fanui-status__dot { background-color: fn.fanui-var("warning"); } }
279
+ }