@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,693 @@
1
+ // =============================================================================
2
+ // fanUI · 博客 / 内容场景组件 (Blog & Content)
3
+ // -----------------------------------------------------------------------------
4
+ // .fanui-post-card 文章卡片(列表 / 网格 / 横向三种形态)
5
+ // .fanui-article 正文容器(配合 .fanui-prose 使用)
6
+ // .fanui-author 作者卡
7
+ // .fanui-toc 目录(支持 scrollspy 高亮)
8
+ // .fanui-share 分享条(横向 / 悬浮侧栏)
9
+ // .fanui-comment 评论区
10
+ // .fanui-newsletter 订阅框
11
+ // .fanui-related 相关阅读
12
+ // .fanui-tag-cloud 标签云
13
+ // .fanui-read-meta 阅读元信息(作者 · 日期 · 时长)
14
+ // =============================================================================
15
+
16
+ @use "sass:map";
17
+ @use "../functions" as fn;
18
+ @use "../variables" as v;
19
+ @use "../mixins" as mx;
20
+
21
+ // -----------------------------------------------------------------------------
22
+ // 1. 阅读元信息
23
+ // -----------------------------------------------------------------------------
24
+
25
+ .fanui-read-meta {
26
+ display: flex;
27
+ flex-wrap: wrap;
28
+ align-items: center;
29
+ gap: fn.fanui-var("space-2");
30
+ font-size: fn.fanui-var("font-size-sm");
31
+ color: fn.fanui-var("color-text-subtle");
32
+
33
+ &__dot {
34
+ width: 3px;
35
+ height: 3px;
36
+ border-radius: fn.fanui-var("radius-full");
37
+ background-color: currentColor;
38
+ opacity: 0.6;
39
+ }
40
+
41
+ &__author {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ gap: fn.fanui-var("space-2");
45
+ color: fn.fanui-var("color-text-muted");
46
+ font-weight: fn.fanui-var("font-weight-medium");
47
+ }
48
+
49
+ &__badge {
50
+ padding: 1px 8px;
51
+ font-size: fn.fanui-var("font-size-xs");
52
+ font-weight: fn.fanui-var("font-weight-semibold");
53
+ color: fn.fanui-var("primary-ink");
54
+ background-color: fn.fanui-var("primary-soft-2");
55
+ border-radius: fn.fanui-var("radius-full");
56
+ }
57
+ }
58
+
59
+ // -----------------------------------------------------------------------------
60
+ // 2. 文章卡片
61
+ // -----------------------------------------------------------------------------
62
+
63
+ .fanui-post-card {
64
+ position: relative;
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: fn.fanui-var("space-3");
68
+ overflow: hidden;
69
+ border: 1px solid fn.fanui-var("color-border");
70
+ border-radius: fn.fanui-var("radius-xl");
71
+ background-color: fn.fanui-var("color-surface");
72
+ text-decoration: none;
73
+ color: inherit;
74
+ @include mx.fanui-transition((transform, box-shadow, border-color), v.$fanui-duration-base);
75
+
76
+ &:hover {
77
+ transform: translateY(-3px);
78
+ border-color: fn.fanui-var("primary-border");
79
+ box-shadow: fn.fanui-var("shadow-lg");
80
+
81
+ .fanui-post-card__cover img { transform: scale(1.04); }
82
+ .fanui-post-card__title { color: fn.fanui-var("primary"); }
83
+ }
84
+
85
+ &__cover {
86
+ position: relative;
87
+ aspect-ratio: 16 / 9;
88
+ overflow: hidden;
89
+ background-image: var(--fanui-brand-grad-soft);
90
+
91
+ img {
92
+ width: 100%;
93
+ height: 100%;
94
+ object-fit: cover;
95
+ @include mx.fanui-transition(transform, v.$fanui-duration-slower);
96
+ }
97
+
98
+ &::after {
99
+ content: "";
100
+ position: absolute;
101
+ inset: 0;
102
+ box-shadow: inset 0 -1px 0 fn.fanui-alpha(#020617, 0.06);
103
+ }
104
+ }
105
+
106
+ &__body {
107
+ display: flex;
108
+ flex-direction: column;
109
+ gap: fn.fanui-var("space-2");
110
+ padding: fn.fanui-var("space-5");
111
+ }
112
+
113
+ &__eyebrow {
114
+ display: inline-flex;
115
+ align-items: center;
116
+ gap: fn.fanui-var("space-2");
117
+ font-size: fn.fanui-var("font-size-xs");
118
+ font-weight: fn.fanui-var("font-weight-semibold");
119
+ letter-spacing: fn.fanui-var("tracking-wide");
120
+ text-transform: uppercase;
121
+ color: fn.fanui-var("primary");
122
+ }
123
+
124
+ &__title {
125
+ margin: 0;
126
+ font-size: fn.fanui-var("font-size-lg");
127
+ font-weight: fn.fanui-var("font-weight-semibold");
128
+ line-height: fn.fanui-var("line-height-snug");
129
+ color: fn.fanui-var("color-text-strong");
130
+ @include mx.fanui-transition(color, v.$fanui-duration-fast);
131
+ @include mx.fanui-truncate(2);
132
+ }
133
+
134
+ &__excerpt {
135
+ margin: 0;
136
+ font-size: fn.fanui-var("font-size-sm");
137
+ line-height: fn.fanui-var("line-height-relaxed");
138
+ color: fn.fanui-var("color-text-muted");
139
+ @include mx.fanui-truncate(3);
140
+ }
141
+
142
+ &__foot {
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: space-between;
146
+ gap: fn.fanui-var("space-3");
147
+ margin-top: auto;
148
+ padding-top: fn.fanui-var("space-3");
149
+ border-top: 1px solid fn.fanui-var("color-border-subtle");
150
+ font-size: fn.fanui-var("font-size-xs");
151
+ color: fn.fanui-var("color-text-subtle");
152
+ }
153
+
154
+ // 横向版(用于"最新文章"列表)
155
+ &--horizontal {
156
+ flex-direction: row;
157
+ align-items: stretch;
158
+
159
+ .fanui-post-card__cover {
160
+ flex: 0 0 38%;
161
+ aspect-ratio: auto;
162
+ min-height: 132px;
163
+ }
164
+
165
+ .fanui-post-card__body { justify-content: center; }
166
+
167
+ @include mx.fanui-media-down("sm") {
168
+ flex-direction: column;
169
+
170
+ .fanui-post-card__cover { aspect-ratio: 16 / 9; flex: none; }
171
+ }
172
+ }
173
+
174
+ // 纯文字版(紧凑列表)
175
+ &--plain {
176
+ border: 0;
177
+ border-radius: 0;
178
+ border-bottom: 1px solid fn.fanui-var("color-border-subtle");
179
+ background: transparent;
180
+
181
+ &:hover { transform: none; box-shadow: none; }
182
+
183
+ .fanui-post-card__body { padding: fn.fanui-var("space-4") 0; }
184
+ }
185
+
186
+ // 玻璃版(叠在图片/渐变之上)
187
+ &--overlay {
188
+ position: relative;
189
+ border: 0;
190
+ justify-content: flex-end;
191
+ min-height: 260px;
192
+ background: linear-gradient(180deg, transparent 30%, fn.fanui-alpha(#020617, 0.72) 100%);
193
+
194
+ .fanui-post-card__cover {
195
+ position: absolute;
196
+ inset: 0;
197
+ aspect-ratio: auto;
198
+
199
+ &::after {
200
+ background: linear-gradient(180deg, transparent 24%, fn.fanui-alpha(#020617, 0.78) 100%);
201
+ }
202
+ }
203
+
204
+ .fanui-post-card__body {
205
+ position: relative;
206
+ z-index: 1;
207
+ color: #ffffff;
208
+ }
209
+
210
+ .fanui-post-card__title,
211
+ .fanui-post-card__excerpt,
212
+ .fanui-post-card__eyebrow { color: #ffffff; }
213
+
214
+ .fanui-post-card__excerpt { opacity: 0.85; }
215
+ .fanui-post-card__foot { border-top-color: fn.fanui-alpha(#ffffff, 0.24); color: fn.fanui-alpha(#ffffff, 0.8); }
216
+ }
217
+ }
218
+
219
+ // -----------------------------------------------------------------------------
220
+ // 3. 正文容器
221
+ // -----------------------------------------------------------------------------
222
+
223
+ .fanui-article {
224
+ max-width: var(--fanui-article-width, 720px);
225
+ margin-inline: auto;
226
+
227
+ &__header {
228
+ display: flex;
229
+ flex-direction: column;
230
+ gap: fn.fanui-var("space-4");
231
+ padding-block: fn.fanui-var("space-8") fn.fanui-var("space-6");
232
+ text-align: center;
233
+ align-items: center;
234
+ }
235
+
236
+ &__eyebrow {
237
+ font-size: fn.fanui-var("font-size-sm");
238
+ font-weight: fn.fanui-var("font-weight-semibold");
239
+ letter-spacing: fn.fanui-var("tracking-wider");
240
+ text-transform: uppercase;
241
+ color: fn.fanui-var("primary");
242
+ }
243
+
244
+ &__title {
245
+ margin: 0;
246
+ font-size: clamp(1.75rem, 4vw, 2.75rem);
247
+ font-weight: fn.fanui-var("font-weight-extrabold");
248
+ line-height: 1.15;
249
+ letter-spacing: fn.fanui-var("tracking-tight");
250
+ color: fn.fanui-var("color-text-strong");
251
+ text-wrap: balance;
252
+ }
253
+
254
+ &__lead {
255
+ margin: 0;
256
+ max-width: 620px;
257
+ font-size: fn.fanui-var("font-size-lg");
258
+ line-height: fn.fanui-var("line-height-relaxed");
259
+ color: fn.fanui-var("color-text-muted");
260
+ text-wrap: pretty;
261
+ }
262
+
263
+ &__cover {
264
+ margin-block: fn.fanui-var("space-6");
265
+ border-radius: fn.fanui-var("radius-2xl");
266
+ overflow: hidden;
267
+
268
+ img { display: block; width: 100%; height: auto; }
269
+ }
270
+
271
+ &__footer {
272
+ display: flex;
273
+ flex-direction: column;
274
+ gap: fn.fanui-var("space-6");
275
+ margin-top: fn.fanui-var("space-10");
276
+ padding-top: fn.fanui-var("space-8");
277
+ border-top: 1px solid fn.fanui-var("color-border");
278
+ }
279
+
280
+ // 正文内的提示块(呼应 .fanui-prose)
281
+ &__callout {
282
+ display: flex;
283
+ gap: fn.fanui-var("space-3");
284
+ margin-block: fn.fanui-var("space-6");
285
+ padding: fn.fanui-var("space-4") fn.fanui-var("space-5");
286
+ border-inline-start: 3px solid fn.fanui-var("primary");
287
+ border-radius: fn.fanui-var("radius-md");
288
+ background-color: fn.fanui-var("primary-soft");
289
+ color: fn.fanui-var("primary-ink");
290
+ font-size: fn.fanui-var("font-size-sm");
291
+ line-height: fn.fanui-var("line-height-relaxed");
292
+ }
293
+
294
+ // 阅读区右侧留白给悬浮目录
295
+ &--with-toc {
296
+ display: grid;
297
+ grid-template-columns: minmax(0, 1fr) 240px;
298
+ gap: fn.fanui-var("space-12");
299
+ max-width: 1080px;
300
+
301
+ @include mx.fanui-media-down("lg") {
302
+ grid-template-columns: minmax(0, 1fr);
303
+ }
304
+ }
305
+ }
306
+
307
+ // -----------------------------------------------------------------------------
308
+ // 4. 目录(TOC)
309
+ // -----------------------------------------------------------------------------
310
+
311
+ .fanui-toc {
312
+ position: sticky;
313
+ top: calc(#{fn.fanui-var("space-10")} + 60px);
314
+ align-self: start;
315
+ max-height: calc(100vh - 140px);
316
+ overflow-y: auto;
317
+ padding-inline-start: fn.fanui-var("space-4");
318
+ border-inline-start: 1px solid fn.fanui-var("color-border");
319
+ font-size: fn.fanui-var("font-size-sm");
320
+ @include mx.fanui-scrollbar(6px);
321
+
322
+ @include mx.fanui-media-down("lg") {
323
+ position: static;
324
+ max-height: none;
325
+ margin-bottom: fn.fanui-var("space-6");
326
+ }
327
+
328
+ &__title {
329
+ margin-bottom: fn.fanui-var("space-3");
330
+ font-size: fn.fanui-var("font-size-xs");
331
+ font-weight: fn.fanui-var("font-weight-semibold");
332
+ letter-spacing: fn.fanui-var("tracking-wider");
333
+ text-transform: uppercase;
334
+ color: fn.fanui-var("color-text-subtle");
335
+ }
336
+
337
+ &__list {
338
+ display: flex;
339
+ flex-direction: column;
340
+ gap: 2px;
341
+ margin: 0;
342
+ padding: 0;
343
+ list-style: none;
344
+ }
345
+
346
+ &__link {
347
+ display: block;
348
+ padding: 5px 10px;
349
+ border-radius: fn.fanui-var("radius-sm");
350
+ color: fn.fanui-var("color-text-muted");
351
+ text-decoration: none;
352
+ line-height: fn.fanui-var("line-height-snug");
353
+ border-inline-start: 2px solid transparent;
354
+ @include mx.fanui-transition((color, background-color, border-color), v.$fanui-duration-fast);
355
+
356
+ &:hover { color: fn.fanui-var("color-text"); background-color: fn.fanui-var("color-hover"); }
357
+
358
+ &.is-active {
359
+ color: fn.fanui-var("primary");
360
+ border-inline-start-color: fn.fanui-var("primary");
361
+ background-color: fn.fanui-var("primary-soft");
362
+ font-weight: fn.fanui-var("font-weight-medium");
363
+ }
364
+ }
365
+
366
+ // 二级目录缩进
367
+ &__list &__list { margin-inline-start: fn.fanui-var("space-3"); }
368
+
369
+ &__progress {
370
+ margin-top: fn.fanui-var("space-4");
371
+ padding-top: fn.fanui-var("space-3");
372
+ border-top: 1px solid fn.fanui-var("color-border-subtle");
373
+ font-size: fn.fanui-var("font-size-xs");
374
+ color: fn.fanui-var("color-text-subtle");
375
+ font-variant-numeric: tabular-nums;
376
+ }
377
+ }
378
+
379
+ // -----------------------------------------------------------------------------
380
+ // 5. 作者卡
381
+ // -----------------------------------------------------------------------------
382
+
383
+ .fanui-author {
384
+ display: flex;
385
+ gap: fn.fanui-var("space-4");
386
+ padding: fn.fanui-var("space-5");
387
+ border-radius: fn.fanui-var("radius-xl");
388
+ @include mx.fanui-liquid-glass();
389
+
390
+ &__avatar {
391
+ flex: 0 0 auto;
392
+ width: 56px;
393
+ height: 56px;
394
+ border-radius: fn.fanui-var("radius-full");
395
+ overflow: hidden;
396
+ background-image: var(--fanui-brand-grad);
397
+ display: grid;
398
+ place-items: center;
399
+ color: #fff;
400
+ font-weight: fn.fanui-var("font-weight-semibold");
401
+
402
+ img { width: 100%; height: 100%; object-fit: cover; }
403
+ }
404
+
405
+ &__body { display: flex; flex-direction: column; gap: fn.fanui-var("space-1"); min-width: 0; }
406
+
407
+ &__name {
408
+ font-size: fn.fanui-var("font-size-md");
409
+ font-weight: fn.fanui-var("font-weight-semibold");
410
+ color: fn.fanui-var("color-text-strong");
411
+ }
412
+
413
+ &__role {
414
+ font-size: fn.fanui-var("font-size-xs");
415
+ color: fn.fanui-var("color-text-subtle");
416
+ }
417
+
418
+ &__bio {
419
+ margin: fn.fanui-var("space-1") 0 0;
420
+ font-size: fn.fanui-var("font-size-sm");
421
+ line-height: fn.fanui-var("line-height-relaxed");
422
+ color: fn.fanui-var("color-text-muted");
423
+ }
424
+
425
+ &__links {
426
+ display: flex;
427
+ gap: fn.fanui-var("space-2");
428
+ margin-top: fn.fanui-var("space-2");
429
+
430
+ a {
431
+ font-size: fn.fanui-var("font-size-xs");
432
+ color: fn.fanui-var("primary");
433
+ text-decoration: none;
434
+
435
+ &:hover { text-decoration: underline; }
436
+ }
437
+ }
438
+ }
439
+
440
+ // -----------------------------------------------------------------------------
441
+ // 6. 分享条
442
+ // -----------------------------------------------------------------------------
443
+
444
+ .fanui-share {
445
+ display: flex;
446
+ align-items: center;
447
+ gap: fn.fanui-var("space-2");
448
+
449
+ &__label {
450
+ font-size: fn.fanui-var("font-size-xs");
451
+ color: fn.fanui-var("color-text-subtle");
452
+ margin-inline-end: fn.fanui-var("space-1");
453
+ }
454
+
455
+ &__btn {
456
+ display: grid;
457
+ place-items: center;
458
+ width: 34px;
459
+ height: 34px;
460
+ border: 1px solid fn.fanui-var("color-border");
461
+ border-radius: fn.fanui-var("radius-full");
462
+ background-color: fn.fanui-var("color-surface");
463
+ color: fn.fanui-var("color-text-muted");
464
+ cursor: pointer;
465
+ text-decoration: none;
466
+ @include mx.fanui-transition((color, border-color, transform, background-color), v.$fanui-duration-fast);
467
+
468
+ &:hover {
469
+ color: fn.fanui-var("primary-fg");
470
+ background-color: fn.fanui-var("primary");
471
+ border-color: fn.fanui-var("primary");
472
+ transform: translateY(-2px);
473
+ }
474
+ }
475
+
476
+ // 悬浮竖排(桌面端文章左侧)
477
+ &--sticky {
478
+ position: sticky;
479
+ top: calc(#{fn.fanui-var("space-10")} + 60px);
480
+ flex-direction: column;
481
+ align-self: start;
482
+
483
+ .fanui-share__label { writing-mode: vertical-rl; margin: 0 0 fn.fanui-var("space-1"); }
484
+
485
+ @include mx.fanui-media-down("lg") {
486
+ position: static;
487
+ flex-direction: row;
488
+
489
+ .fanui-share__label { writing-mode: horizontal-tb; margin: 0 fn.fanui-var("space-1") 0 0; }
490
+ }
491
+ }
492
+ }
493
+
494
+ // -----------------------------------------------------------------------------
495
+ // 7. 评论区
496
+ // -----------------------------------------------------------------------------
497
+
498
+ .fanui-comment {
499
+ display: flex;
500
+ gap: fn.fanui-var("space-3");
501
+
502
+ &__avatar {
503
+ flex: 0 0 auto;
504
+ width: 36px;
505
+ height: 36px;
506
+ border-radius: fn.fanui-var("radius-full");
507
+ overflow: hidden;
508
+ display: grid;
509
+ place-items: center;
510
+ background-color: fn.fanui-var("primary-soft-2");
511
+ color: fn.fanui-var("primary-ink");
512
+ font-size: fn.fanui-var("font-size-sm");
513
+ font-weight: fn.fanui-var("font-weight-semibold");
514
+ }
515
+
516
+ &__body {
517
+ flex: 1 1 auto;
518
+ min-width: 0;
519
+ display: flex;
520
+ flex-direction: column;
521
+ gap: fn.fanui-var("space-1");
522
+ }
523
+
524
+ &__head {
525
+ display: flex;
526
+ align-items: center;
527
+ gap: fn.fanui-var("space-2");
528
+ font-size: fn.fanui-var("font-size-sm");
529
+ }
530
+
531
+ &__name { font-weight: fn.fanui-var("font-weight-semibold"); color: fn.fanui-var("color-text-strong"); }
532
+ &__time { font-size: fn.fanui-var("font-size-xs"); color: fn.fanui-var("color-text-subtle"); }
533
+
534
+ &__text {
535
+ font-size: fn.fanui-var("font-size-sm");
536
+ line-height: fn.fanui-var("line-height-relaxed");
537
+ color: fn.fanui-var("color-text");
538
+ }
539
+
540
+ &__actions {
541
+ display: flex;
542
+ gap: fn.fanui-var("space-3");
543
+ margin-top: fn.fanui-var("space-1");
544
+ font-size: fn.fanui-var("font-size-xs");
545
+ color: fn.fanui-var("color-text-subtle");
546
+
547
+ button {
548
+ border: 0;
549
+ background: transparent;
550
+ padding: 0;
551
+ font: inherit;
552
+ color: inherit;
553
+ cursor: pointer;
554
+
555
+ &:hover { color: fn.fanui-var("primary"); }
556
+ }
557
+ }
558
+
559
+ &__replies {
560
+ display: flex;
561
+ flex-direction: column;
562
+ gap: fn.fanui-var("space-4");
563
+ margin-top: fn.fanui-var("space-4");
564
+ padding-inline-start: fn.fanui-var("space-5");
565
+ border-inline-start: 2px solid fn.fanui-var("color-border-subtle");
566
+ }
567
+
568
+ &--pinned {
569
+ padding: fn.fanui-var("space-4");
570
+ border-radius: fn.fanui-var("radius-lg");
571
+ background-color: fn.fanui-var("primary-soft");
572
+ }
573
+ }
574
+
575
+ .fanui-comment-list {
576
+ display: flex;
577
+ flex-direction: column;
578
+ gap: fn.fanui-var("space-6");
579
+ margin: 0;
580
+ padding: 0;
581
+ list-style: none;
582
+ }
583
+
584
+ // -----------------------------------------------------------------------------
585
+ // 8. 订阅框
586
+ // -----------------------------------------------------------------------------
587
+
588
+ .fanui-newsletter {
589
+ display: flex;
590
+ flex-direction: column;
591
+ gap: fn.fanui-var("space-3");
592
+ padding: fn.fanui-var("space-6");
593
+ border-radius: fn.fanui-var("radius-2xl");
594
+ @include mx.fanui-liquid-glass();
595
+
596
+ &__title {
597
+ margin: 0;
598
+ font-size: fn.fanui-var("font-size-lg");
599
+ font-weight: fn.fanui-var("font-weight-semibold");
600
+ color: fn.fanui-var("color-text-strong");
601
+ }
602
+
603
+ &__desc {
604
+ margin: 0;
605
+ font-size: fn.fanui-var("font-size-sm");
606
+ line-height: fn.fanui-var("line-height-relaxed");
607
+ color: fn.fanui-var("color-text-muted");
608
+ }
609
+
610
+ &__form {
611
+ display: flex;
612
+ gap: fn.fanui-var("space-2");
613
+
614
+ @include mx.fanui-media-down("sm") { flex-direction: column; }
615
+ }
616
+
617
+ &__input {
618
+ @include mx.fanui-form-control();
619
+ flex: 1 1 auto;
620
+ }
621
+
622
+ &__note {
623
+ font-size: fn.fanui-var("font-size-xs");
624
+ color: fn.fanui-var("color-text-subtle");
625
+ }
626
+
627
+ &--inline {
628
+ flex-direction: row;
629
+ align-items: center;
630
+ justify-content: space-between;
631
+ gap: fn.fanui-var("space-6");
632
+ flex-wrap: wrap;
633
+
634
+ .fanui-newsletter__form { flex: 1 1 320px; }
635
+ }
636
+ }
637
+
638
+ // -----------------------------------------------------------------------------
639
+ // 9. 相关阅读 / 标签云
640
+ // -----------------------------------------------------------------------------
641
+
642
+ .fanui-related {
643
+ display: grid;
644
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
645
+ gap: fn.fanui-var("space-5");
646
+
647
+ &__title {
648
+ grid-column: 1 / -1;
649
+ margin: 0 0 fn.fanui-var("space-2");
650
+ font-size: fn.fanui-var("font-size-lg");
651
+ font-weight: fn.fanui-var("font-weight-semibold");
652
+ color: fn.fanui-var("color-text-strong");
653
+ }
654
+ }
655
+
656
+ .fanui-tag-cloud {
657
+ display: flex;
658
+ flex-wrap: wrap;
659
+ gap: fn.fanui-var("space-2");
660
+
661
+ &__item {
662
+ display: inline-flex;
663
+ align-items: center;
664
+ gap: fn.fanui-var("space-1");
665
+ padding: 4px 12px;
666
+ font-size: fn.fanui-var("font-size-sm");
667
+ color: fn.fanui-var("color-text-muted");
668
+ background-color: fn.fanui-var("color-surface");
669
+ border: 1px solid fn.fanui-var("color-border");
670
+ border-radius: fn.fanui-var("radius-full");
671
+ text-decoration: none;
672
+ @include mx.fanui-transition((color, border-color, background-color, transform), v.$fanui-duration-fast);
673
+
674
+ &:hover {
675
+ color: fn.fanui-var("primary-ink");
676
+ background-color: fn.fanui-var("primary-soft");
677
+ border-color: fn.fanui-var("primary-border");
678
+ transform: translateY(-1px);
679
+ }
680
+
681
+ &--active {
682
+ color: fn.fanui-var("primary-fg");
683
+ background-color: fn.fanui-var("primary");
684
+ border-color: fn.fanui-var("primary");
685
+ }
686
+ }
687
+
688
+ &__count {
689
+ font-size: fn.fanui-var("font-size-xs");
690
+ opacity: 0.7;
691
+ font-variant-numeric: tabular-nums;
692
+ }
693
+ }