@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.
- package/ACCESSIBILITY.md +61 -0
- package/CHANGELOG.md +112 -0
- package/COMMERCIAL-LICENSE.md +71 -0
- package/DESIGN-SYSTEM.md +294 -0
- package/LICENSE +661 -0
- package/README.md +348 -0
- package/USAGE.md +1926 -0
- package/_index.scss +20 -0
- package/dist/fanui.cjs +15 -0
- package/dist/fanui.css +28209 -0
- package/dist/fanui.d.ts +353 -0
- package/dist/fanui.esm.js +71 -0
- package/dist/fanui.js +4782 -0
- package/dist/fanui.min.css +1 -0
- package/dist/fanui.mjs +71 -0
- package/dist/modules/fanui-advanced.css +830 -0
- package/dist/modules/fanui-advanced.min.css +129 -0
- package/dist/modules/fanui-alert.css +575 -0
- package/dist/modules/fanui-alert.min.css +122 -0
- package/dist/modules/fanui-badge.css +426 -0
- package/dist/modules/fanui-badge.min.css +77 -0
- package/dist/modules/fanui-base.css +1394 -0
- package/dist/modules/fanui-base.min.css +236 -0
- package/dist/modules/fanui-blog.css +762 -0
- package/dist/modules/fanui-blog.min.css +108 -0
- package/dist/modules/fanui-button.css +526 -0
- package/dist/modules/fanui-button.min.css +56 -0
- package/dist/modules/fanui-card.css +291 -0
- package/dist/modules/fanui-card.min.css +52 -0
- package/dist/modules/fanui-choice.css +539 -0
- package/dist/modules/fanui-choice.min.css +83 -0
- package/dist/modules/fanui-dashboard.css +1057 -0
- package/dist/modules/fanui-dashboard.min.css +160 -0
- package/dist/modules/fanui-data.css +687 -0
- package/dist/modules/fanui-data.min.css +125 -0
- package/dist/modules/fanui-feedback.css +432 -0
- package/dist/modules/fanui-feedback.min.css +79 -0
- package/dist/modules/fanui-form.css +779 -0
- package/dist/modules/fanui-form.min.css +123 -0
- package/dist/modules/fanui-glass.css +497 -0
- package/dist/modules/fanui-glass.min.css +46 -0
- package/dist/modules/fanui-input-plus.css +799 -0
- package/dist/modules/fanui-input-plus.min.css +124 -0
- package/dist/modules/fanui-interactive.css +960 -0
- package/dist/modules/fanui-interactive.min.css +138 -0
- package/dist/modules/fanui-layout.css +4399 -0
- package/dist/modules/fanui-layout.min.css +1172 -0
- package/dist/modules/fanui-marketing.css +1125 -0
- package/dist/modules/fanui-marketing.min.css +144 -0
- package/dist/modules/fanui-nav.css +458 -0
- package/dist/modules/fanui-nav.min.css +76 -0
- package/dist/modules/fanui-overlay.css +547 -0
- package/dist/modules/fanui-overlay.min.css +91 -0
- package/dist/modules/fanui-table.css +290 -0
- package/dist/modules/fanui-table.min.css +62 -0
- package/dist/modules/fanui-tabs.css +333 -0
- package/dist/modules/fanui-tabs.min.css +58 -0
- package/dist/modules/fanui-themes.css +2728 -0
- package/dist/modules/fanui-themes.min.css +40 -0
- package/dist/modules/fanui-tokens.css +587 -0
- package/dist/modules/fanui-tokens.min.css +3 -0
- package/dist/modules/fanui-utilities.css +6557 -0
- package/dist/modules/fanui-utilities.min.css +1762 -0
- package/dist/modules/sizes.json +103 -0
- package/docs/MIGRATION-v2.md +179 -0
- package/examples/01-script-tag.html +149 -0
- package/examples/02-react/README.md +39 -0
- package/examples/02-react/index.html +12 -0
- package/examples/02-react/package.json +20 -0
- package/examples/02-react/src/App.jsx +108 -0
- package/examples/02-react/src/main.jsx +13 -0
- package/examples/02-react/src/useFanUI.js +87 -0
- package/examples/02-react/vite.config.js +6 -0
- package/examples/03-vue3/README.md +67 -0
- package/examples/03-vue3/index.html +12 -0
- package/examples/03-vue3/package.json +19 -0
- package/examples/03-vue3/src/App.vue +93 -0
- package/examples/03-vue3/src/main.js +8 -0
- package/examples/03-vue3/src/useFanUI.js +85 -0
- package/examples/03-vue3/vite.config.js +6 -0
- package/examples/04-vue2-cdn.html +93 -0
- package/examples/README.md +34 -0
- package/package.json +127 -0
- package/src/_functions.scss +319 -0
- package/src/_mixins.scss +1202 -0
- package/src/_tokens.scss +180 -0
- package/src/_variables.scss +491 -0
- package/src/base/_animations.scss +210 -0
- package/src/base/_reset.scss +359 -0
- package/src/base/_rtl.scss +133 -0
- package/src/base/_typography.scss +353 -0
- package/src/components/_advanced.scss +763 -0
- package/src/components/_alert.scss +322 -0
- package/src/components/_badge.scss +279 -0
- package/src/components/_blog.scss +693 -0
- package/src/components/_button.scss +351 -0
- package/src/components/_capabilities.scss +463 -0
- package/src/components/_card.scss +281 -0
- package/src/components/_choice.scss +444 -0
- package/src/components/_dashboard.scss +950 -0
- package/src/components/_data.scss +618 -0
- package/src/components/_feedback.scss +403 -0
- package/src/components/_form.scss +408 -0
- package/src/components/_glass.scss +374 -0
- package/src/components/_input-plus.scss +730 -0
- package/src/components/_interactive.scss +885 -0
- package/src/components/_marketing.scss +982 -0
- package/src/components/_nav.scss +480 -0
- package/src/components/_overlay.scss +517 -0
- package/src/components/_table.scss +276 -0
- package/src/components/_tabs.scss +338 -0
- package/src/fanui.d.ts +353 -0
- package/src/fanui.js +4782 -0
- package/src/fanui.scss +79 -0
- package/src/glass/_switches.scss +36 -0
- package/src/glass/_tokens.scss +259 -0
- package/src/layout/_container.scss +111 -0
- package/src/layout/_flex.scss +164 -0
- package/src/layout/_grid.scss +180 -0
- package/src/themes/_accents.scss +201 -0
- package/src/themes/_palettes.scss +180 -0
- package/src/utilities/_borders.scss +104 -0
- package/src/utilities/_display.scss +145 -0
- package/src/utilities/_effects.scss +210 -0
- package/src/utilities/_sizing.scss +123 -0
- package/src/utilities/_spacing.scss +118 -0
- package/src/utilities/_texture.scss +323 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// fanUI · 响应式栅格系统 (Grid System)
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// 命名规范:
|
|
5
|
+
// .fanui-row 行(flex 容器 + 负外边距中和列的内边距)
|
|
6
|
+
// .fanui-col 等分列(自动均分剩余空间)
|
|
7
|
+
// .fanui-col-<n> 占 n/12 列(n = 1..12)
|
|
8
|
+
// .fanui-col-<bp>-<n> 指定断点起生效(bp = sm|md|lg|xl|xxl)
|
|
9
|
+
// .fanui-col-<bp>-auto 内容自适应宽度
|
|
10
|
+
// .fanui-offset-<bp>-<n> 向右偏移 n 列
|
|
11
|
+
// .fanui-order-<bp>-<n> 排序(n = 0..12)
|
|
12
|
+
// .fanui-g-<n> / .fanui-gx-<n> / .fanui-gy-<n> 行/列间距
|
|
13
|
+
// 断点采用移动优先:xs(0) < sm(576) < md(768) < lg(992) < xl(1200) < xxl(1400)
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
@use "sass:map";
|
|
17
|
+
@use "sass:list";
|
|
18
|
+
@use "sass:math";
|
|
19
|
+
@use "../functions" as fn;
|
|
20
|
+
@use "../variables" as v;
|
|
21
|
+
@use "../mixins" as mx;
|
|
22
|
+
|
|
23
|
+
// -----------------------------------------------------------------------------
|
|
24
|
+
// 1. 行 Row
|
|
25
|
+
// -----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
.fanui-row {
|
|
28
|
+
@include mx.fanui-make-row();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 列数为 0 时取消内边距(常用于嵌套全宽)
|
|
32
|
+
.fanui-row--no-gutters {
|
|
33
|
+
--fanui-gutter-x: 0;
|
|
34
|
+
--fanui-gutter-y: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.fanui-row--inline {
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// -----------------------------------------------------------------------------
|
|
42
|
+
// 2. 基础列 Col
|
|
43
|
+
// -----------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
.fanui-col {
|
|
46
|
+
flex: 1 0 0%;
|
|
47
|
+
min-width: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.fanui-col-auto {
|
|
51
|
+
flex: 0 0 auto;
|
|
52
|
+
width: auto;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// -----------------------------------------------------------------------------
|
|
56
|
+
// 3. 固定列宽 .fanui-col-<n>
|
|
57
|
+
// -----------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
@for $i from 1 through v.$fanui-grid-columns {
|
|
60
|
+
.fanui-col-#{$i} {
|
|
61
|
+
flex: 0 0 auto;
|
|
62
|
+
width: fn.fanui-percentage($i, v.$fanui-grid-columns);
|
|
63
|
+
max-width: 100%;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 整行独占
|
|
68
|
+
.fanui-col-12 { flex: 0 0 auto; width: 100%; }
|
|
69
|
+
|
|
70
|
+
// -----------------------------------------------------------------------------
|
|
71
|
+
// 4. 响应式列 .fanui-col-<bp>-<n>
|
|
72
|
+
// -----------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
@each $bp, $min in v.$fanui-breakpoints {
|
|
75
|
+
@include mx.fanui-media($bp, min) {
|
|
76
|
+
$infix: fn.fanui-bp-infix($bp);
|
|
77
|
+
|
|
78
|
+
.fanui-col#{$infix} {
|
|
79
|
+
flex: 1 0 0%;
|
|
80
|
+
min-width: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.fanui-col#{$infix}-auto {
|
|
84
|
+
flex: 0 0 auto;
|
|
85
|
+
width: auto;
|
|
86
|
+
max-width: 100%;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@for $i from 1 through v.$fanui-grid-columns {
|
|
90
|
+
.fanui-col#{$infix}-#{$i} {
|
|
91
|
+
flex: 0 0 auto;
|
|
92
|
+
width: fn.fanui-percentage($i, v.$fanui-grid-columns);
|
|
93
|
+
max-width: 100%;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 偏移
|
|
98
|
+
@for $i from 0 through (v.$fanui-grid-columns - 1) {
|
|
99
|
+
@if $i == 0 {
|
|
100
|
+
.fanui-offset#{$infix}-0 { margin-inline-start: 0; }
|
|
101
|
+
} @else {
|
|
102
|
+
.fanui-offset#{$infix}-#{$i} {
|
|
103
|
+
margin-inline-start: fn.fanui-percentage($i, v.$fanui-grid-columns);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 排序
|
|
109
|
+
@for $i from 0 through v.$fanui-grid-columns {
|
|
110
|
+
.fanui-order#{$infix}-#{$i} { order: $i; }
|
|
111
|
+
}
|
|
112
|
+
.fanui-order#{$infix}-first { order: -1; }
|
|
113
|
+
.fanui-order#{$infix}-last { order: v.$fanui-grid-columns + 1; }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// -----------------------------------------------------------------------------
|
|
118
|
+
// 5. 行间距 / 列间距(gutter)
|
|
119
|
+
// -----------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
$fanui-gutter-steps: (0: 0, 1: 4px, 2: 8px, 3: 12px, 4: 16px, 5: 20px, 6: 24px, 8: 32px, 10: 40px) !default;
|
|
122
|
+
|
|
123
|
+
@each $key, $value in $fanui-gutter-steps {
|
|
124
|
+
.fanui-g#{$key} { --fanui-gutter-x: #{$value}; --fanui-gutter-y: #{$value}; }
|
|
125
|
+
.fanui-gx#{$key} { --fanui-gutter-x: #{$value}; }
|
|
126
|
+
.fanui-gy#{$key} { --fanui-gutter-y: #{$value}; }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 响应式 gutter
|
|
130
|
+
@each $bp, $min in v.$fanui-breakpoints {
|
|
131
|
+
@include mx.fanui-media($bp, min) {
|
|
132
|
+
$infix: fn.fanui-bp-infix($bp);
|
|
133
|
+
|
|
134
|
+
@each $key, $value in $fanui-gutter-steps {
|
|
135
|
+
.fanui-g#{$infix}-#{$key} { --fanui-gutter-x: #{$value}; --fanui-gutter-y: #{$value}; }
|
|
136
|
+
.fanui-gx#{$infix}-#{$key} { --fanui-gutter-x: #{$value}; }
|
|
137
|
+
.fanui-gy#{$infix}-#{$key} { --fanui-gutter-y: #{$value}; }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// -----------------------------------------------------------------------------
|
|
143
|
+
// 6. CSS Grid 工具(独立于 12 栅格,用于卡片瀑布流等)
|
|
144
|
+
// -----------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
.fanui-grid {
|
|
147
|
+
display: grid;
|
|
148
|
+
gap: fn.fanui-var("space-4");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@for $i from 1 through 12 {
|
|
152
|
+
.fanui-grid-cols-#{$i} {
|
|
153
|
+
grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 自适应列(auto-fit + minmax),无需媒体查询
|
|
158
|
+
.fanui-grid-auto-xs { @include mx.fanui-grid-auto(120px); }
|
|
159
|
+
.fanui-grid-auto-sm { @include mx.fanui-grid-auto(180px); }
|
|
160
|
+
.fanui-grid-auto-md { @include mx.fanui-grid-auto(240px); }
|
|
161
|
+
.fanui-grid-auto-lg { @include mx.fanui-grid-auto(300px); }
|
|
162
|
+
.fanui-grid-auto-xl { @include mx.fanui-grid-auto(380px); }
|
|
163
|
+
|
|
164
|
+
@each $bp, $min in v.$fanui-breakpoints {
|
|
165
|
+
@include mx.fanui-media($bp, min) {
|
|
166
|
+
$infix: fn.fanui-bp-infix($bp);
|
|
167
|
+
|
|
168
|
+
@for $i from 1 through 12 {
|
|
169
|
+
.fanui-grid-cols#{$infix}-#{$i} {
|
|
170
|
+
grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.fanui-grid-span#{$infix}-full { grid-column: 1 / -1; }
|
|
175
|
+
@for $i from 1 through 12 {
|
|
176
|
+
.fanui-grid-span#{$infix}-#{$i} { grid-column: span #{$i} / span #{$i}; }
|
|
177
|
+
.fanui-grid-rows#{$infix}-#{$i} { grid-row: span #{$i} / span #{$i}; }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// fanUI · 主题色令牌输出层 (Accent Roles)
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// 本层负责把「12 套主题色」翻译成一整套 CSS 自定义属性,并处理三种作用域:
|
|
5
|
+
//
|
|
6
|
+
// 1. 全局默认 :root → cyan(可配置)
|
|
7
|
+
// 2. 全局显式切换 <html data-fanui-accent="indigo">
|
|
8
|
+
// 3. 局部作用域 <section data-fanui-accent="rose"> → 子树内换色不污染全局
|
|
9
|
+
// <div class="fanui-accent-rose"> → 等价的类名写法
|
|
10
|
+
//
|
|
11
|
+
// 深浅模式与主题色是正交的两条轴:
|
|
12
|
+
// 浅色 + cyan / 深色 + cyan / 浅色 + purple / 深色 + purple … 24 种组合全部覆盖。
|
|
13
|
+
//
|
|
14
|
+
// 输出的令牌清单(每个角色 name 都完整产出,全组件统一引用这些名字):
|
|
15
|
+
// --fanui-<name> --fanui-<name>-fg --fanui-<name>-hover
|
|
16
|
+
// --fanui-<name>-active --fanui-<name>-vivid --fanui-<name>-soft
|
|
17
|
+
// --fanui-<name>-soft-2 --fanui-<name>-border --fanui-<name>-ink
|
|
18
|
+
// --fanui-<name>-ring --fanui-<name>-glow --fanui-<name>-grad-a/b
|
|
19
|
+
// --fanui-<name>-50…950 (原始色阶,随主题色一起重映射)
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
@use "sass:map";
|
|
23
|
+
@use "sass:list";
|
|
24
|
+
@use "../functions" as fn;
|
|
25
|
+
@use "../variables" as v;
|
|
26
|
+
@use "../mixins" as mx;
|
|
27
|
+
@use "palettes" as pal;
|
|
28
|
+
|
|
29
|
+
// -----------------------------------------------------------------------------
|
|
30
|
+
// 1. 主题色 → 令牌
|
|
31
|
+
// -----------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/// 输出一套主题色(主色 + 和声副色 + 品牌渐变 + 极光色)在指定模式下的全部令牌。
|
|
34
|
+
///
|
|
35
|
+
/// @param $accent 主题色 key(cyan / sky / blue / …)
|
|
36
|
+
/// @param $mode light | dark
|
|
37
|
+
@mixin fanui-emit-accent-theme($accent, $mode: light) {
|
|
38
|
+
$pal: pal.fanui-accent-palette($accent);
|
|
39
|
+
$secondary: pal.fanui-accent-meta($accent, "secondary");
|
|
40
|
+
$sec-pal: pal.fanui-accent-palette($secondary);
|
|
41
|
+
|
|
42
|
+
$vivid-tone: 500;
|
|
43
|
+
@if $mode == dark {
|
|
44
|
+
$vivid-tone: 400;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
$p-vivid: map.get($pal, $vivid-tone);
|
|
48
|
+
$s-vivid: map.get($sec-pal, $vivid-tone);
|
|
49
|
+
|
|
50
|
+
// ---- 主色(所有组件的默认品牌色)--------------------------------------
|
|
51
|
+
@include mx.fanui-emit-accent-roles("primary", $pal, $mode);
|
|
52
|
+
|
|
53
|
+
// ---- 和声副色(图表第二序列 / 装饰 / 强调)----------------------------
|
|
54
|
+
@include mx.fanui-emit-accent-roles("accent", $sec-pal, $mode);
|
|
55
|
+
|
|
56
|
+
// ---- 品牌渐变 & 极光色(官网 Hero、卡片高光、图表渐变共用)------------
|
|
57
|
+
--fanui-brand-grad-a: var(--fanui-primary-vivid);
|
|
58
|
+
--fanui-brand-grad-b: var(--fanui-accent-vivid);
|
|
59
|
+
--fanui-brand-grad: linear-gradient(120deg, var(--fanui-primary-vivid), var(--fanui-accent-vivid));
|
|
60
|
+
--fanui-brand-grad-soft: linear-gradient(
|
|
61
|
+
120deg,
|
|
62
|
+
#{fn.fanui-alpha($p-vivid, 0.16)},
|
|
63
|
+
#{fn.fanui-alpha($s-vivid, 0.14)}
|
|
64
|
+
);
|
|
65
|
+
--fanui-brand-aura-a: #{fn.fanui-alpha($p-vivid, 0.38)};
|
|
66
|
+
--fanui-brand-aura-b: #{fn.fanui-alpha($s-vivid, 0.30)};
|
|
67
|
+
--fanui-brand-name: "#{$accent}";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// -----------------------------------------------------------------------------
|
|
71
|
+
// 2. 全局默认(浅色)
|
|
72
|
+
// -----------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
:root {
|
|
75
|
+
@include fanui-emit-accent-theme(v.$fanui-accent-default, light);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// -----------------------------------------------------------------------------
|
|
79
|
+
// 3. 12 套主题色 × 浅色模式
|
|
80
|
+
// -----------------------------------------------------------------------------
|
|
81
|
+
// 同时支持属性写法与类名写法,便于「子树局部换色」:
|
|
82
|
+
// <section data-fanui-accent="rose">…</section>
|
|
83
|
+
// <div class="fanui-accent-rose">…</div>
|
|
84
|
+
|
|
85
|
+
@if v.$fanui-enable-accent-themes {
|
|
86
|
+
@each $name, $config in pal.$fanui-accents {
|
|
87
|
+
[data-fanui-accent="#{$name}"],
|
|
88
|
+
.fanui-accent-#{$name} {
|
|
89
|
+
@include fanui-emit-accent-theme($name, light);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// -----------------------------------------------------------------------------
|
|
95
|
+
// 4. 全局默认(深色)
|
|
96
|
+
// -----------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
[data-fanui-theme="dark"],
|
|
99
|
+
:root[data-fanui-theme="dark"] {
|
|
100
|
+
@include fanui-emit-accent-theme(v.$fanui-accent-default, dark);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// -----------------------------------------------------------------------------
|
|
104
|
+
// 5. 12 套主题色 × 深色模式
|
|
105
|
+
// -----------------------------------------------------------------------------
|
|
106
|
+
// 选择器组合保证三层覆盖关系正确(全局默认 < 显式主题色 < 深色 + 显式主题色):
|
|
107
|
+
// [data-fanui-theme="dark"][data-fanui-accent="x"] 同元素
|
|
108
|
+
// [data-fanui-theme="dark"] [data-fanui-accent="x"] 祖先为深色
|
|
109
|
+
// [data-fanui-theme="dark"] .fanui-accent-x 祖先为深色(类名写法)
|
|
110
|
+
|
|
111
|
+
@if v.$fanui-enable-accent-themes {
|
|
112
|
+
@each $name, $config in pal.$fanui-accents {
|
|
113
|
+
[data-fanui-theme="dark"][data-fanui-accent="#{$name}"],
|
|
114
|
+
[data-fanui-theme="dark"] [data-fanui-accent="#{$name}"],
|
|
115
|
+
[data-fanui-theme="dark"] .fanui-accent-#{$name} {
|
|
116
|
+
@include fanui-emit-accent-theme($name, dark);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// -----------------------------------------------------------------------------
|
|
122
|
+
// 6. 跟随系统(auto)
|
|
123
|
+
// -----------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
@media (prefers-color-scheme: dark) {
|
|
126
|
+
[data-fanui-theme="auto"],
|
|
127
|
+
:root[data-fanui-theme="auto"] {
|
|
128
|
+
@include fanui-emit-accent-theme(v.$fanui-accent-default, dark);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@if v.$fanui-enable-accent-themes {
|
|
132
|
+
@each $name, $config in pal.$fanui-accents {
|
|
133
|
+
[data-fanui-theme="auto"][data-fanui-accent="#{$name}"],
|
|
134
|
+
[data-fanui-theme="auto"] [data-fanui-accent="#{$name}"],
|
|
135
|
+
[data-fanui-theme="auto"] .fanui-accent-#{$name} {
|
|
136
|
+
@include fanui-emit-accent-theme($name, dark);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// -----------------------------------------------------------------------------
|
|
143
|
+
// 7. 语义色角色(success / warning / danger / info)
|
|
144
|
+
// -----------------------------------------------------------------------------
|
|
145
|
+
// 它们不随主题色变化,但使用与主题色完全相同的令牌口径。
|
|
146
|
+
// 实心色取 700 阶以保证白字达到 WCAG AA;500/600 阶保留为 -vivid 供装饰使用。
|
|
147
|
+
|
|
148
|
+
:root {
|
|
149
|
+
@each $name in v.$fanui-semantic-names {
|
|
150
|
+
$pal: map.get(v.$fanui-palette, $name);
|
|
151
|
+
@include mx.fanui-emit-color-roles(
|
|
152
|
+
$name,
|
|
153
|
+
map.get($pal, 700),
|
|
154
|
+
map.get($pal, 500),
|
|
155
|
+
map.get($pal, 600),
|
|
156
|
+
map.get($pal, 50),
|
|
157
|
+
map.get($pal, 100),
|
|
158
|
+
map.get($pal, 200),
|
|
159
|
+
map.get($pal, 800),
|
|
160
|
+
light
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[data-fanui-theme="dark"],
|
|
166
|
+
:root[data-fanui-theme="dark"] {
|
|
167
|
+
@each $name in v.$fanui-semantic-names {
|
|
168
|
+
$pal: map.get(v.$fanui-palette, $name);
|
|
169
|
+
@include mx.fanui-emit-color-roles(
|
|
170
|
+
$name,
|
|
171
|
+
map.get($pal, 400),
|
|
172
|
+
map.get($pal, 400),
|
|
173
|
+
map.get($pal, 300),
|
|
174
|
+
null,
|
|
175
|
+
null,
|
|
176
|
+
null,
|
|
177
|
+
map.get($pal, 200),
|
|
178
|
+
dark
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@media (prefers-color-scheme: dark) {
|
|
184
|
+
[data-fanui-theme="auto"],
|
|
185
|
+
:root[data-fanui-theme="auto"] {
|
|
186
|
+
@each $name in v.$fanui-semantic-names {
|
|
187
|
+
$pal: map.get(v.$fanui-palette, $name);
|
|
188
|
+
@include mx.fanui-emit-color-roles(
|
|
189
|
+
$name,
|
|
190
|
+
map.get($pal, 400),
|
|
191
|
+
map.get($pal, 400),
|
|
192
|
+
map.get($pal, 300),
|
|
193
|
+
null,
|
|
194
|
+
null,
|
|
195
|
+
null,
|
|
196
|
+
map.get($pal, 200),
|
|
197
|
+
dark
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// fanUI · 12 套主题色(Accent Themes)
|
|
3
|
+
// -----------------------------------------------------------------------------
|
|
4
|
+
// 每套主题包含:11 阶调色板(50 → 950)+ 元数据(名称、推荐配对副色、气质量表)
|
|
5
|
+
//
|
|
6
|
+
// 设计原则
|
|
7
|
+
// 1. 色相覆盖完整色环:青 → 天蓝 → 蓝 → 靛 → 紫罗兰 → 紫 → 粉 → 玫红 → 赤 → 橙 → 翡翠 → 青碧
|
|
8
|
+
// 2. 同一套阶差体系(50/100…900/950),保证明度节奏一致,任意主题互换后版式不塌
|
|
9
|
+
// 3. 不硬编码「品牌主色阶」:由 fanui-emit-accent-roles() 按 WCAG 对比度自动择优,
|
|
10
|
+
// 因此换主题、加主题都不需要人工逐个调校颜色
|
|
11
|
+
// 4. 每套主题配一个「和声副色」(secondary),用于品牌渐变、图表第二序列、装饰元素,
|
|
12
|
+
// 避免用户自己乱搭导致色彩失衡
|
|
13
|
+
//
|
|
14
|
+
// 默认主题:cyan(青)—— 理由见 docs/DESIGN-SYSTEM.md「默认主题色论证」
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
@use "sass:map";
|
|
18
|
+
|
|
19
|
+
/// 默认主题色
|
|
20
|
+
$fanui-accent-default: "cyan" !default;
|
|
21
|
+
|
|
22
|
+
/// 12 套主题色定义
|
|
23
|
+
/// @prop label 中文名(用于主题面板展示)
|
|
24
|
+
/// @prop hue 代表色相角(用于派生极光背景、光晕等装饰色)
|
|
25
|
+
/// @prop palette 11 阶调色板
|
|
26
|
+
/// @prop secondary 和声副色(另一个 accent 的 key)
|
|
27
|
+
/// @prop vibe 气质标签(B 端 / C 端适配建议)
|
|
28
|
+
$fanui-accents: (
|
|
29
|
+
"cyan": (
|
|
30
|
+
"label": "青",
|
|
31
|
+
"en": "Cyan",
|
|
32
|
+
"hue": 189,
|
|
33
|
+
"vibe": "科技 · 清爽",
|
|
34
|
+
"secondary": "violet",
|
|
35
|
+
"palette": (
|
|
36
|
+
50: #ecfeff, 100: #cffafe, 200: #a5f3fc, 300: #67e8f9, 400: #22d3ee,
|
|
37
|
+
500: #06b6d4, 600: #0891b2, 700: #0e7490, 800: #155e75, 900: #164e63, 950: #083344
|
|
38
|
+
)
|
|
39
|
+
),
|
|
40
|
+
"sky": (
|
|
41
|
+
"label": "天蓝",
|
|
42
|
+
"en": "Sky",
|
|
43
|
+
"hue": 199,
|
|
44
|
+
"vibe": "轻盈 · 开放",
|
|
45
|
+
"secondary": "violet",
|
|
46
|
+
"palette": (
|
|
47
|
+
50: #f0f9ff, 100: #e0f2fe, 200: #bae6fd, 300: #7dd3fc, 400: #38bdf8,
|
|
48
|
+
500: #0ea5e9, 600: #0284c7, 700: #0369a1, 800: #075985, 900: #0c4a6e, 950: #082f49
|
|
49
|
+
)
|
|
50
|
+
),
|
|
51
|
+
"blue": (
|
|
52
|
+
"label": "经典蓝",
|
|
53
|
+
"en": "Blue",
|
|
54
|
+
"hue": 217,
|
|
55
|
+
"vibe": "稳健 · 通用",
|
|
56
|
+
"secondary": "cyan",
|
|
57
|
+
"palette": (
|
|
58
|
+
50: #eff6ff, 100: #dbeafe, 200: #bfdbfe, 300: #93c5fd, 400: #60a5fa,
|
|
59
|
+
500: #3b82f6, 600: #2563eb, 700: #1d4ed8, 800: #1e40af, 900: #1e3a8a, 950: #172554
|
|
60
|
+
)
|
|
61
|
+
),
|
|
62
|
+
"indigo": (
|
|
63
|
+
"label": "靛蓝",
|
|
64
|
+
"en": "Indigo",
|
|
65
|
+
"hue": 239,
|
|
66
|
+
"vibe": "专业 · 前沿",
|
|
67
|
+
"secondary": "cyan",
|
|
68
|
+
"palette": (
|
|
69
|
+
50: #eef2ff, 100: #e0e7ff, 200: #c7d2fe, 300: #a5b4fc, 400: #818cf8,
|
|
70
|
+
500: #6366f1, 600: #4f46e5, 700: #4338ca, 800: #3730a3, 900: #312e81, 950: #1e1b4b
|
|
71
|
+
)
|
|
72
|
+
),
|
|
73
|
+
"violet": (
|
|
74
|
+
"label": "紫罗兰",
|
|
75
|
+
"en": "Violet",
|
|
76
|
+
"hue": 258,
|
|
77
|
+
"vibe": "创意 · 智能",
|
|
78
|
+
"secondary": "cyan",
|
|
79
|
+
"palette": (
|
|
80
|
+
50: #f5f3ff, 100: #ede9fe, 200: #ddd6fe, 300: #c4b5fd, 400: #a78bfa,
|
|
81
|
+
500: #8b5cf6, 600: #7c3aed, 700: #6d28d9, 800: #5b21b6, 900: #4c1d95, 950: #2e1065
|
|
82
|
+
)
|
|
83
|
+
),
|
|
84
|
+
"purple": (
|
|
85
|
+
"label": "紫",
|
|
86
|
+
"en": "Purple",
|
|
87
|
+
"hue": 272,
|
|
88
|
+
"vibe": "艺术 · 张扬",
|
|
89
|
+
"secondary": "pink",
|
|
90
|
+
"palette": (
|
|
91
|
+
50: #faf5ff, 100: #f3e8ff, 200: #e9d5ff, 300: #d8b4fe, 400: #c084fc,
|
|
92
|
+
500: #a855f7, 600: #9333ea, 700: #7e22ce, 800: #6b21a8, 900: #581c87, 950: #3b0764
|
|
93
|
+
)
|
|
94
|
+
),
|
|
95
|
+
"pink": (
|
|
96
|
+
"label": "品粉",
|
|
97
|
+
"en": "Pink",
|
|
98
|
+
"hue": 330,
|
|
99
|
+
"vibe": "年轻 · 潮流",
|
|
100
|
+
"secondary": "violet",
|
|
101
|
+
"palette": (
|
|
102
|
+
50: #fdf2f8, 100: #fce7f3, 200: #fbcfe8, 300: #f9a8d4, 400: #f472b6,
|
|
103
|
+
500: #ec4899, 600: #db2777, 700: #be185d, 800: #9d174d, 900: #831843, 950: #500724
|
|
104
|
+
)
|
|
105
|
+
),
|
|
106
|
+
"rose": (
|
|
107
|
+
"label": "玫红",
|
|
108
|
+
"en": "Rose",
|
|
109
|
+
"hue": 350,
|
|
110
|
+
"vibe": "浪漫 · 亲和",
|
|
111
|
+
"secondary": "orange",
|
|
112
|
+
"palette": (
|
|
113
|
+
50: #fff1f2, 100: #ffe4e6, 200: #fecdd3, 300: #fda4af, 400: #fb7185,
|
|
114
|
+
500: #f43f5e, 600: #e11d48, 700: #be123c, 800: #9f1239, 900: #881337, 950: #4c0519
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
"red": (
|
|
118
|
+
"label": "赤红",
|
|
119
|
+
"en": "Red",
|
|
120
|
+
"hue": 0,
|
|
121
|
+
"vibe": "强烈 · 行动",
|
|
122
|
+
"secondary": "rose",
|
|
123
|
+
"palette": (
|
|
124
|
+
50: #fef2f2, 100: #fee2e2, 200: #fecaca, 300: #fca5a5, 400: #f87171,
|
|
125
|
+
500: #ef4444, 600: #dc2626, 700: #b91c1c, 800: #991b1b, 900: #7f1d1d, 950: #450a0a
|
|
126
|
+
)
|
|
127
|
+
),
|
|
128
|
+
"orange": (
|
|
129
|
+
"label": "橙",
|
|
130
|
+
"en": "Orange",
|
|
131
|
+
"hue": 25,
|
|
132
|
+
"vibe": "活力 · 温暖",
|
|
133
|
+
"secondary": "pink",
|
|
134
|
+
"palette": (
|
|
135
|
+
50: #fff7ed, 100: #ffedd5, 200: #fed7aa, 300: #fdba74, 400: #fb923c,
|
|
136
|
+
500: #f97316, 600: #ea580c, 700: #c2410c, 800: #9a3412, 900: #7c2d12, 950: #431407
|
|
137
|
+
)
|
|
138
|
+
),
|
|
139
|
+
"emerald": (
|
|
140
|
+
"label": "翡翠",
|
|
141
|
+
"en": "Emerald",
|
|
142
|
+
"hue": 160,
|
|
143
|
+
"vibe": "成长 · 健康",
|
|
144
|
+
"secondary": "cyan",
|
|
145
|
+
"palette": (
|
|
146
|
+
50: #ecfdf5, 100: #d1fae5, 200: #a7f3d0, 300: #6ee7b7, 400: #34d399,
|
|
147
|
+
500: #10b981, 600: #059669, 700: #047857, 800: #065f46, 900: #064e3b, 950: #022c22
|
|
148
|
+
)
|
|
149
|
+
),
|
|
150
|
+
"teal": (
|
|
151
|
+
"label": "青碧",
|
|
152
|
+
"en": "Teal",
|
|
153
|
+
"hue": 173,
|
|
154
|
+
"vibe": "沉静 · 高效",
|
|
155
|
+
"secondary": "emerald",
|
|
156
|
+
"palette": (
|
|
157
|
+
50: #f0fdfa, 100: #ccfbf1, 200: #99f6e4, 300: #5eead4, 400: #2dd4bf,
|
|
158
|
+
500: #14b8a6, 600: #0d9488, 700: #0f766e, 800: #115e59, 900: #134e4a, 950: #042f2e
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
) !default;
|
|
162
|
+
|
|
163
|
+
/// 主题色 key 列表(顺序即主题面板展示顺序)
|
|
164
|
+
$fanui-accent-names: map.keys($fanui-accents) !default;
|
|
165
|
+
|
|
166
|
+
/// 取某主题色的调色板。
|
|
167
|
+
@function fanui-accent-palette($name) {
|
|
168
|
+
@if not map.has-key($fanui-accents, $name) {
|
|
169
|
+
@error "fanUI: 未知主题色 `#{$name}`,可用值:#{$fanui-accent-names}。";
|
|
170
|
+
}
|
|
171
|
+
@return map.get(map.get($fanui-accents, $name), "palette");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// 取某主题色的元数据字段。
|
|
175
|
+
@function fanui-accent-meta($name, $key) {
|
|
176
|
+
@if not map.has-key($fanui-accents, $name) {
|
|
177
|
+
@error "fanUI: 未知主题色 `#{$name}`。";
|
|
178
|
+
}
|
|
179
|
+
@return map.get(map.get($fanui-accents, $name), $key);
|
|
180
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// fanUI · 边框 / 圆角 工具类 (Borders)
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
@use "sass:map";
|
|
6
|
+
@use "../functions" as fn;
|
|
7
|
+
@use "../variables" as v;
|
|
8
|
+
@use "../mixins" as mx;
|
|
9
|
+
|
|
10
|
+
// -----------------------------------------------------------------------------
|
|
11
|
+
// 1. 边框宽度
|
|
12
|
+
// -----------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
.fanui-border { border: fn.fanui-var("border-width", 1px) solid fn.fanui-var("color-border"); }
|
|
15
|
+
.fanui-border-0 { border-width: 0; }
|
|
16
|
+
.fanui-border-top { border-top: 1px solid fn.fanui-var("color-border"); }
|
|
17
|
+
.fanui-border-right { border-inline-end: 1px solid fn.fanui-var("color-border"); }
|
|
18
|
+
.fanui-border-bottom { border-bottom: 1px solid fn.fanui-var("color-border"); }
|
|
19
|
+
.fanui-border-left { border-inline-start: 1px solid fn.fanui-var("color-border"); }
|
|
20
|
+
.fanui-border-top-0 { border-top-width: 0; }
|
|
21
|
+
.fanui-border-right-0 { border-inline-end-width: 0; }
|
|
22
|
+
.fanui-border-bottom-0 { border-bottom-width: 0; }
|
|
23
|
+
.fanui-border-left-0 { border-inline-start-width: 0; }
|
|
24
|
+
|
|
25
|
+
.fanui-border-thick { border-width: 2px; }
|
|
26
|
+
.fanui-border-dashed { border-style: dashed; }
|
|
27
|
+
.fanui-border-dotted { border-style: dotted; }
|
|
28
|
+
.fanui-border-none { border: none; }
|
|
29
|
+
|
|
30
|
+
// -----------------------------------------------------------------------------
|
|
31
|
+
// 2. 边框颜色
|
|
32
|
+
// -----------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
.fanui-border-default { border-color: fn.fanui-var("color-border"); }
|
|
35
|
+
.fanui-border-subtle { border-color: fn.fanui-var("color-border-subtle"); }
|
|
36
|
+
.fanui-border-strong { border-color: fn.fanui-var("color-border-strong"); }
|
|
37
|
+
.fanui-border-primary { border-color: fn.fanui-var("primary"); }
|
|
38
|
+
.fanui-border-current { border-color: currentColor; }
|
|
39
|
+
.fanui-border-transparent { border-color: transparent; }
|
|
40
|
+
|
|
41
|
+
@each $name, $color in v.$fanui-theme-colors {
|
|
42
|
+
.fanui-border-#{$name} { border-color: fn.fanui-var("#{$name}"); }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// -----------------------------------------------------------------------------
|
|
46
|
+
// 3. 圆角
|
|
47
|
+
// -----------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
@each $name, $value in v.$fanui-radii {
|
|
50
|
+
.fanui-rounded-#{$name} {
|
|
51
|
+
border-radius: fn.fanui-var("radius-#{$name}", $value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.fanui-rounded { border-radius: fn.fanui-var("radius-base"); }
|
|
56
|
+
.fanui-rounded-full { border-radius: 9999px; }
|
|
57
|
+
|
|
58
|
+
.fanui-rounded-top { border-radius: fn.fanui-var("radius-base") fn.fanui-var("radius-base") 0 0; }
|
|
59
|
+
.fanui-rounded-bottom { border-radius: 0 0 fn.fanui-var("radius-base") fn.fanui-var("radius-base"); }
|
|
60
|
+
.fanui-rounded-start { border-radius: fn.fanui-var("radius-base") 0 0 fn.fanui-var("radius-base"); }
|
|
61
|
+
.fanui-rounded-end { border-radius: 0 fn.fanui-var("radius-base") fn.fanui-var("radius-base") 0; }
|
|
62
|
+
|
|
63
|
+
// -----------------------------------------------------------------------------
|
|
64
|
+
// 4. 分割线 / 分隔符
|
|
65
|
+
// -----------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
.fanui-divider {
|
|
68
|
+
height: 1px;
|
|
69
|
+
margin: fn.fanui-var("space-4") 0;
|
|
70
|
+
background-color: fn.fanui-var("color-border");
|
|
71
|
+
border: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.fanui-divider--vertical {
|
|
75
|
+
width: 1px;
|
|
76
|
+
height: auto;
|
|
77
|
+
min-height: 1em;
|
|
78
|
+
margin: 0 fn.fanui-var("space-3");
|
|
79
|
+
align-self: stretch;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.fanui-divider--dashed {
|
|
83
|
+
background: none;
|
|
84
|
+
border-top: 1px dashed fn.fanui-var("color-border");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 带文字的分割线
|
|
88
|
+
.fanui-divider-text {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
gap: fn.fanui-var("space-3");
|
|
92
|
+
margin: fn.fanui-var("space-4") 0;
|
|
93
|
+
font-size: fn.fanui-var("font-size-sm");
|
|
94
|
+
color: fn.fanui-var("color-text-subtle");
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
|
|
97
|
+
&::before,
|
|
98
|
+
&::after {
|
|
99
|
+
content: "";
|
|
100
|
+
flex: 1 1 auto;
|
|
101
|
+
height: 1px;
|
|
102
|
+
background-color: fn.fanui-var("color-border");
|
|
103
|
+
}
|
|
104
|
+
}
|