@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,210 @@
1
+ // =============================================================================
2
+ // fanUI · 动画与动效 (Animations & Motion)
3
+ // -----------------------------------------------------------------------------
4
+ // 统一动效语言:命名 `fanui-*`,时长/缓动均取自设计令牌。
5
+ // 所有动画默认在 prefers-reduced-motion: reduce 下自动降级。
6
+ // =============================================================================
7
+
8
+ @use "sass:map";
9
+ @use "sass:list";
10
+ @use "../functions" as fn;
11
+ @use "../variables" as v;
12
+ @use "../mixins" as mx;
13
+
14
+ // -----------------------------------------------------------------------------
15
+ // 1. 关键帧
16
+ // -----------------------------------------------------------------------------
17
+
18
+ @keyframes fanui-fade-in {
19
+ from { opacity: 0; }
20
+ to { opacity: 1; }
21
+ }
22
+
23
+ @keyframes fanui-fade-out {
24
+ from { opacity: 1; }
25
+ to { opacity: 0; }
26
+ }
27
+
28
+ @keyframes fanui-slide-in-up {
29
+ from { opacity: 0; transform: translateY(12px); }
30
+ to { opacity: 1; transform: translateY(0); }
31
+ }
32
+
33
+ @keyframes fanui-slide-in-down {
34
+ from { opacity: 0; transform: translateY(-12px); }
35
+ to { opacity: 1; transform: translateY(0); }
36
+ }
37
+
38
+ @keyframes fanui-slide-in-start {
39
+ from { opacity: 0; transform: translateX(-16px); }
40
+ to { opacity: 1; transform: translateX(0); }
41
+ }
42
+
43
+ @keyframes fanui-slide-in-end {
44
+ from { opacity: 0; transform: translateX(16px); }
45
+ to { opacity: 1; transform: translateX(0); }
46
+ }
47
+
48
+ @keyframes fanui-zoom-in {
49
+ from { opacity: 0; transform: scale(0.94); }
50
+ to { opacity: 1; transform: scale(1); }
51
+ }
52
+
53
+ @keyframes fanui-zoom-out {
54
+ from { opacity: 1; transform: scale(1); }
55
+ to { opacity: 0; transform: scale(0.94); }
56
+ }
57
+
58
+ @keyframes fanui-pop-in {
59
+ from { opacity: 0; transform: scale(0.86) translateY(8px); }
60
+ 60% { opacity: 1; transform: scale(1.02) translateY(0); }
61
+ to { opacity: 1; transform: scale(1) translateY(0); }
62
+ }
63
+
64
+ @keyframes fanui-spin {
65
+ to { transform: rotate(360deg); }
66
+ }
67
+
68
+ @keyframes fanui-pulse {
69
+ 0%, 100% { opacity: 1; }
70
+ 50% { opacity: 0.45; }
71
+ }
72
+
73
+ @keyframes fanui-bounce {
74
+ 0%, 100% { transform: translateY(0); }
75
+ 50% { transform: translateY(-6px); }
76
+ }
77
+
78
+ @keyframes fanui-shake {
79
+ 0%, 100% { transform: translateX(0); }
80
+ 20% { transform: translateX(-6px); }
81
+ 40% { transform: translateX(6px); }
82
+ 60% { transform: translateX(-4px); }
83
+ 80% { transform: translateX(4px); }
84
+ }
85
+
86
+ @keyframes fanui-progress-stripes {
87
+ from { background-position: 1rem 0; }
88
+ to { background-position: 0 0; }
89
+ }
90
+
91
+ @keyframes fanui-progress-indeterminate {
92
+ 0% { left: -35%; width: 35%; }
93
+ 60% { left: 100%; width: 60%; }
94
+ 100% { left: 100%; width: 60%; }
95
+ }
96
+
97
+ @keyframes fanui-skeleton-shimmer {
98
+ from { background-position: 200% 0; }
99
+ to { background-position: -200% 0; }
100
+ }
101
+
102
+ @keyframes fanui-toast-in {
103
+ from { opacity: 0; transform: translateY(-8px) scale(0.98); }
104
+ to { opacity: 1; transform: translateY(0) scale(1); }
105
+ }
106
+
107
+ @keyframes fanui-ripple {
108
+ from { transform: scale(0); opacity: 0.45; }
109
+ to { transform: scale(2.6); opacity: 0; }
110
+ }
111
+
112
+ @keyframes fanui-blink {
113
+ 0%, 100% { opacity: 1; }
114
+ 50% { opacity: 0; }
115
+ }
116
+
117
+ // -----------------------------------------------------------------------------
118
+ // 2. 动画工具类
119
+ // -----------------------------------------------------------------------------
120
+
121
+ $fanui-animations: (
122
+ "fade-in": (fanui-fade-in, v.$fanui-duration-base, v.$fanui-ease-out, both),
123
+ "fade-out": (fanui-fade-out, v.$fanui-duration-base, v.$fanui-ease-in, both),
124
+ "slide-in-up": (fanui-slide-in-up, v.$fanui-duration-slow, v.$fanui-ease-out, both),
125
+ "slide-in-down": (fanui-slide-in-down, v.$fanui-duration-slow, v.$fanui-ease-out, both),
126
+ "slide-in-start":(fanui-slide-in-start, v.$fanui-duration-slow, v.$fanui-ease-out, both),
127
+ "slide-in-end": (fanui-slide-in-end, v.$fanui-duration-slow, v.$fanui-ease-out, both),
128
+ "zoom-in": (fanui-zoom-in, v.$fanui-duration-base, v.$fanui-ease-out, both),
129
+ "zoom-out": (fanui-zoom-out, v.$fanui-duration-base, v.$fanui-ease-in, both),
130
+ "pop-in": (fanui-pop-in, v.$fanui-duration-slow, v.$fanui-ease-spring, both),
131
+ "spin": (fanui-spin, 900ms, linear, infinite),
132
+ "pulse": (fanui-pulse, 1.6s, v.$fanui-ease-in-out, infinite),
133
+ "bounce": (fanui-bounce, 1s, v.$fanui-ease-in-out, infinite),
134
+ "shake": (fanui-shake, 420ms, v.$fanui-ease-in-out, both),
135
+ "blink": (fanui-blink, 1s, step-end, infinite),
136
+ ) !default;
137
+
138
+ @each $name, $config in $fanui-animations {
139
+ .fanui-animate-#{$name} {
140
+ animation-name: list.nth($config, 1);
141
+ animation-duration: list.nth($config, 2);
142
+ animation-timing-function: list.nth($config, 3);
143
+ animation-fill-mode: list.nth($config, 4);
144
+ animation-iteration-count: 1;
145
+ }
146
+ }
147
+
148
+ // 无限循环类需单独纠正 iteration-count
149
+ .fanui-animate-spin,
150
+ .fanui-animate-pulse,
151
+ .fanui-animate-bounce,
152
+ .fanui-animate-blink {
153
+ animation-iteration-count: infinite;
154
+ }
155
+
156
+ .fanui-animate-infinite { animation-iteration-count: infinite; }
157
+ .fanui-animate-once { animation-iteration-count: 1; }
158
+
159
+ @each $name, $value in v.$fanui-durations {
160
+ .fanui-duration-#{$name} { animation-duration: $value; transition-duration: $value; }
161
+ }
162
+
163
+ .fanui-ease-linear { animation-timing-function: linear; transition-timing-function: linear; }
164
+ .fanui-ease-in { animation-timing-function: v.$fanui-ease-in; transition-timing-function: v.$fanui-ease-in; }
165
+ .fanui-ease-out { animation-timing-function: v.$fanui-ease-out; transition-timing-function: v.$fanui-ease-out; }
166
+ .fanui-ease-in-out { animation-timing-function: v.$fanui-ease-in-out; transition-timing-function: v.$fanui-ease-in-out; }
167
+ .fanui-ease-spring { animation-timing-function: v.$fanui-ease-spring; transition-timing-function: v.$fanui-ease-spring; }
168
+
169
+ .fanui-delay-75 { animation-delay: 75ms; }
170
+ .fanui-delay-150 { animation-delay: 150ms; }
171
+ .fanui-delay-300 { animation-delay: 300ms; }
172
+ .fanui-delay-500 { animation-delay: 500ms; }
173
+
174
+ .fanui-paused { animation-play-state: paused; }
175
+ .fanui-running { animation-play-state: running; }
176
+
177
+ // 过渡工具类
178
+ @each $prop in (all, color, background-color, border-color, box-shadow, opacity, transform) {
179
+ .fanui-transition-#{$prop} {
180
+ @include mx.fanui-transition($prop);
181
+ }
182
+ }
183
+
184
+ // -----------------------------------------------------------------------------
185
+ // 3. 无障碍降级:尊重用户的「减少动态效果」偏好
186
+ // -----------------------------------------------------------------------------
187
+
188
+ @include mx.fanui-motion-safe() {
189
+ .fanui-animate-fade-in,
190
+ .fanui-animate-fade-out,
191
+ .fanui-animate-slide-in-up,
192
+ .fanui-animate-slide-in-down,
193
+ .fanui-animate-slide-in-start,
194
+ .fanui-animate-slide-in-end,
195
+ .fanui-animate-zoom-in,
196
+ .fanui-animate-zoom-out,
197
+ .fanui-animate-pop-in,
198
+ .fanui-animate-shake {
199
+ animation: none !important;
200
+ opacity: 1 !important;
201
+ transform: none !important;
202
+ }
203
+
204
+ .fanui-animate-spin,
205
+ .fanui-animate-pulse,
206
+ .fanui-animate-bounce,
207
+ .fanui-animate-blink {
208
+ animation-duration: 3s !important;
209
+ }
210
+ }
@@ -0,0 +1,359 @@
1
+ // =============================================================================
2
+ // fanUI · CSS 重置 (Reset / Normalize)
3
+ // -----------------------------------------------------------------------------
4
+ // 目标:抹平浏览器差异 + 提供合理的默认排版,不破坏语义。
5
+ // 说明:所有规则使用低特异性选择器,便于业务侧覆盖。
6
+ // =============================================================================
7
+
8
+ @use "sass:map";
9
+ @use "../functions" as fn;
10
+ @use "../variables" as v;
11
+ @use "../mixins" as mx;
12
+
13
+ *,
14
+ *::before,
15
+ *::after {
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ html {
20
+ -webkit-text-size-adjust: 100%;
21
+ -moz-text-size-adjust: 100%;
22
+ text-size-adjust: 100%;
23
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
24
+ scroll-behavior: smooth;
25
+ font-size: 100%; // 尊重用户浏览器默认字号,配合 rem 缩放
26
+ line-height: 1;
27
+ tab-size: 4;
28
+ font-family: v.$fanui-font-family-base;
29
+ text-rendering: optimizeLegibility;
30
+ }
31
+
32
+ @include mx.fanui-motion-safe() {
33
+ html { scroll-behavior: auto; }
34
+ }
35
+
36
+ body {
37
+ margin: 0;
38
+ min-height: 100vh;
39
+ font-family: fn.fanui-var("font-base", v.$fanui-font-family-base);
40
+ font-size: fn.fanui-var("font-size-base", v.$fanui-control-font-size);
41
+ font-weight: map.get(v.$fanui-font-weights, "normal");
42
+ line-height: fn.fanui-var("line-height-base", v.$fanui-line-height-base);
43
+ color: fn.fanui-var("color-text");
44
+ background-color: fn.fanui-var("color-bg");
45
+ @include mx.fanui-transition((color, background-color), v.$fanui-duration-slow);
46
+
47
+ @if v.$fanui-body-font-smoothing {
48
+ -webkit-font-smoothing: antialiased;
49
+ -moz-osx-font-smoothing: grayscale;
50
+ }
51
+ }
52
+
53
+ // -----------------------------------------------------------------------------
54
+ // 元素默认值
55
+ // -----------------------------------------------------------------------------
56
+
57
+ main,
58
+ article,
59
+ aside,
60
+ figure,
61
+ footer,
62
+ header,
63
+ hgroup,
64
+ nav,
65
+ section,
66
+ details,
67
+ summary,
68
+ figcaption {
69
+ display: block;
70
+ }
71
+
72
+ h1, h2, h3, h4, h5, h6,
73
+ p,
74
+ figure,
75
+ blockquote,
76
+ dl,
77
+ dd,
78
+ ol,
79
+ ul,
80
+ pre {
81
+ margin: 0;
82
+ }
83
+
84
+ ol[class],
85
+ ul[class],
86
+ li[class] {
87
+ list-style: none;
88
+ padding: 0;
89
+ }
90
+
91
+ a {
92
+ color: fn.fanui-var("primary");
93
+ text-decoration: none;
94
+ background-color: transparent;
95
+ @include mx.fanui-transition(color, v.$fanui-duration-fast);
96
+
97
+ &:hover {
98
+ color: fn.fanui-var("primary-hover");
99
+ text-decoration: underline;
100
+ }
101
+ &:active {
102
+ color: fn.fanui-var("primary-active");
103
+ }
104
+ &:focus-visible {
105
+ @include mx.fanui-focus-ring();
106
+ border-radius: 2px;
107
+ }
108
+ }
109
+
110
+ b,
111
+ strong {
112
+ font-weight: map.get(v.$fanui-font-weights, "bold");
113
+ }
114
+
115
+ small {
116
+ font-size: 0.875em;
117
+ }
118
+
119
+ sub,
120
+ sup {
121
+ position: relative;
122
+ font-size: 0.75em;
123
+ line-height: 0;
124
+ vertical-align: baseline;
125
+ }
126
+
127
+ sub { bottom: -0.25em; }
128
+ sup { top: -0.5em; }
129
+
130
+ img,
131
+ picture,
132
+ video,
133
+ canvas,
134
+ svg {
135
+ display: block;
136
+ max-width: 100%;
137
+ }
138
+
139
+ img,
140
+ video {
141
+ height: auto;
142
+ border-style: none;
143
+ }
144
+
145
+ svg {
146
+ overflow: hidden;
147
+ vertical-align: middle;
148
+ }
149
+
150
+ // 表单元素继承字体
151
+ button,
152
+ input,
153
+ optgroup,
154
+ select,
155
+ textarea {
156
+ margin: 0;
157
+ font-family: inherit;
158
+ font-size: 100%;
159
+ line-height: inherit;
160
+ color: inherit;
161
+ }
162
+
163
+ button,
164
+ [type="button"],
165
+ [type="reset"],
166
+ [type="submit"],
167
+ [role="button"] {
168
+ -webkit-appearance: button;
169
+ cursor: pointer;
170
+ text-transform: none;
171
+ background-color: transparent;
172
+ background-image: none;
173
+ border: 0;
174
+ padding: 0;
175
+ }
176
+
177
+ ::-moz-focus-inner {
178
+ padding: 0;
179
+ border-style: none;
180
+ }
181
+
182
+ :-moz-ui-invalid {
183
+ box-shadow: none;
184
+ }
185
+
186
+ fieldset {
187
+ min-width: 0;
188
+ padding: 0;
189
+ margin: 0;
190
+ border: 0;
191
+ }
192
+
193
+ legend {
194
+ padding: 0;
195
+ display: table;
196
+ max-width: 100%;
197
+ white-space: normal;
198
+ color: inherit;
199
+ }
200
+
201
+ progress {
202
+ vertical-align: baseline;
203
+ }
204
+
205
+ textarea {
206
+ resize: vertical;
207
+ overflow: auto;
208
+ }
209
+
210
+ [type="search"] {
211
+ outline-offset: -2px;
212
+ -webkit-appearance: textfield;
213
+ appearance: textfield;
214
+
215
+ &::-webkit-search-decoration {
216
+ -webkit-appearance: none;
217
+ }
218
+ }
219
+
220
+ ::-webkit-file-upload-button {
221
+ -webkit-appearance: button;
222
+ font: inherit;
223
+ }
224
+
225
+ [type="number"] {
226
+ &::-webkit-outer-spin-button,
227
+ &::-webkit-inner-spin-button {
228
+ height: auto;
229
+ -webkit-appearance: none;
230
+ margin: 0;
231
+ }
232
+ -moz-appearance: textfield;
233
+ }
234
+
235
+ [hidden],
236
+ template {
237
+ display: none !important;
238
+ }
239
+
240
+ iframe {
241
+ border: 0;
242
+ display: block;
243
+ }
244
+
245
+ hr {
246
+ box-sizing: content-box;
247
+ height: 0;
248
+ overflow: visible;
249
+ margin: 1em 0;
250
+ border: 0;
251
+ border-top: fn.fanui-var("border-width", v.$fanui-border-width) solid fn.fanui-var("color-border");
252
+ opacity: 1;
253
+ color: inherit;
254
+ }
255
+
256
+ abbr[title] {
257
+ text-decoration: underline dotted;
258
+ cursor: help;
259
+ }
260
+
261
+ code,
262
+ kbd,
263
+ samp,
264
+ pre {
265
+ font-family: fn.fanui-var("font-mono", v.$fanui-font-family-mono);
266
+ font-size: 1em;
267
+ }
268
+
269
+ pre {
270
+ overflow: auto;
271
+ -webkit-overflow-scrolling: touch;
272
+ }
273
+
274
+ summary {
275
+ display: list-item;
276
+ cursor: pointer;
277
+ }
278
+
279
+ details > summary::-webkit-details-marker {
280
+ display: none;
281
+ }
282
+
283
+ table {
284
+ border-collapse: collapse;
285
+ border-spacing: 0;
286
+ text-indent: 0;
287
+ }
288
+
289
+ th {
290
+ text-align: inherit;
291
+ text-align: -webkit-match-parent;
292
+ }
293
+
294
+ // 全局滚动条(仅 WebKit / Firefox),可通过 .fanui-no-scrollbar 关闭
295
+ html {
296
+ @include mx.fanui-scrollbar();
297
+ }
298
+
299
+ // 选中文本
300
+ ::selection {
301
+ background-color: fn.fanui-var("primary-soft");
302
+ color: fn.fanui-var("primary-active");
303
+ }
304
+
305
+ // 无障碍:跳过导航链接(C4)—— 键盘 Tab 聚焦后悬浮于左上角,点击直达主内容
306
+ .fanui-skip-link {
307
+ position: fixed;
308
+ top: -100%;
309
+ left: fn.fanui-var("space-4");
310
+ z-index: calc(fn.fanui-var("z-toast") + 1);
311
+ padding: 10px 18px;
312
+ border-radius: fn.fanui-var("radius-md");
313
+ background-color: fn.fanui-var("primary");
314
+ color: fn.fanui-var("primary-fg");
315
+ font-size: fn.fanui-var("font-size-sm");
316
+ font-weight: fn.fanui-var("font-weight-semibold");
317
+ text-decoration: none;
318
+ box-shadow: fn.fanui-var("shadow-lg");
319
+ @include mx.fanui-transition((top), v.$fanui-duration-fast);
320
+
321
+ &:focus {
322
+ top: fn.fanui-var("space-3");
323
+ outline: 3px solid fn.fanui-var("primary-ring");
324
+ outline-offset: 2px;
325
+ }
326
+ }
327
+
328
+ // 无障碍辅助类
329
+ .fanui-visually-hidden,
330
+ .fanui-sr-only {
331
+ @include mx.fanui-visually-hidden();
332
+ }
333
+
334
+ .fanui-sr-only-focusable {
335
+ @include mx.fanui-visually-hidden($focusable: true);
336
+ }
337
+
338
+ .fanui-no-scrollbar {
339
+ scrollbar-width: none;
340
+ -ms-overflow-style: none;
341
+
342
+ &::-webkit-scrollbar {
343
+ display: none;
344
+ width: 0;
345
+ height: 0;
346
+ }
347
+ }
348
+
349
+ // 全局焦点可见性兜底(不覆盖组件自定义环)
350
+ :focus-visible {
351
+ outline: 2px solid fn.fanui-var("primary-ring");
352
+ outline-offset: 1px;
353
+ }
354
+
355
+ @media (forced-colors: active) {
356
+ :focus-visible {
357
+ outline: 2px solid transparent;
358
+ }
359
+ }
@@ -0,0 +1,133 @@
1
+ // =============================================================================
2
+ // fanUI · RTL 双向布局覆盖层
3
+ // -----------------------------------------------------------------------------
4
+ // 策略(两层):
5
+ // ① 主力:**逻辑属性**。src 中的行内轴属性(margin/padding/border/text-align)
6
+ // 已统一改写为 *-inline-start / *-inline-end / *-inline(见 utilities/_spacing.scss),
7
+ // 在 [dir="rtl"] 下自动镜像,无需任何覆盖规则。当前产物逻辑属性约 690 处。
8
+ // ② 补齐层:本文件,只处理**本质物理**的东西 ——
9
+ // 浮层锚点方向、抽屉滑入方向、动画/位移方向、图标指向、非对称圆角。
10
+ //
11
+ // 用法:<html dir="rtl"> 整站镜像;局部 <div dir="rtl"> 同样生效。
12
+ // 审计:scripts/audit-coverage.cjs 统计产物中残留的物理行内轴属性数量。
13
+ // =============================================================================
14
+
15
+ @use "../functions" as fn;
16
+
17
+ [dir="rtl"] {
18
+ /* ---------------------------------------------------------------------------
19
+ * 1. 下拉菜单 / 弹出面板:默认锚点由「左上」变为「右上」
20
+ * ------------------------------------------------------------------------- */
21
+ .fanui-dropdown__menu {
22
+ transform-origin: top right;
23
+
24
+ &--start { left: auto; right: 0; transform-origin: top right; }
25
+ &--end { left: 0; right: auto; transform-origin: top left; }
26
+
27
+ &--up {
28
+ transform-origin: bottom right;
29
+
30
+ &.fanui-dropdown__menu--end { transform-origin: bottom left; }
31
+ }
32
+
33
+ &--center { left: 50%; right: auto; }
34
+ }
35
+
36
+ .fanui-popover__panel {
37
+ transform-origin: top right;
38
+
39
+ &--end { left: auto; right: 0; transform-origin: top right; }
40
+ &--start { left: 0; right: auto; transform-origin: top left; }
41
+ &--center { left: 50%; right: auto; transform: translateX(-50%) translateY(-4px); }
42
+ &--center.is-open,
43
+ &--center.is-active { transform: translateX(-50%) translateY(0); }
44
+ }
45
+
46
+ /* ---------------------------------------------------------------------------
47
+ * 2. 气泡提示:左右指向对调
48
+ * ------------------------------------------------------------------------- */
49
+ .fanui-tooltip--right .fanui-tooltip__bubble {
50
+ left: auto;
51
+ right: calc(100% + 8px);
52
+
53
+ &::after { left: auto; right: 0; margin: 0 -3px 0 0; }
54
+ }
55
+
56
+ .fanui-tooltip--left .fanui-tooltip__bubble {
57
+ left: calc(100% + 8px);
58
+ right: auto;
59
+
60
+ &::after { left: 0; right: auto; margin: 0 100% 0 -3px; }
61
+ }
62
+
63
+ /* ---------------------------------------------------------------------------
64
+ * 3. 抽屉:start / end 语义随方向对调(滑入位移必须反向)
65
+ * ------------------------------------------------------------------------- */
66
+ .fanui-drawer--start .fanui-drawer__panel {
67
+ left: auto;
68
+ right: 0;
69
+ transform: translateX(100%);
70
+ }
71
+
72
+ .fanui-drawer--end .fanui-drawer__panel {
73
+ left: 0;
74
+ right: auto;
75
+ transform: translateX(-100%);
76
+ }
77
+
78
+ .fanui-drawer--start.is-open > .fanui-drawer__panel,
79
+ .fanui-drawer--end.is-open > .fanui-drawer__panel { transform: translate(0, 0); }
80
+
81
+ .fanui-sidebar--end { left: 0; right: auto; }
82
+
83
+ /* ---------------------------------------------------------------------------
84
+ * 4. 日期 / 时间 / 颜色 / 级联面板:展开原点镜像
85
+ * ------------------------------------------------------------------------- */
86
+ .fanui-datepanel,
87
+ .fanui-timepanel,
88
+ .fanui-colorpanel,
89
+ .fanui-cascader__panel { transform-origin: top right; }
90
+
91
+ /* ---------------------------------------------------------------------------
92
+ * 5. 提示条区域:左右位置对调
93
+ * ------------------------------------------------------------------------- */
94
+ .fanui-toast-region {
95
+ &--top-right { left: fn.fanui-var("space-5"); right: auto; align-items: flex-start; }
96
+ &--top-left { left: auto; right: fn.fanui-var("space-5"); align-items: flex-end; }
97
+ &--bottom-right { left: fn.fanui-var("space-5"); right: auto; align-items: flex-start; }
98
+ }
99
+
100
+ /* ---------------------------------------------------------------------------
101
+ * 6. 开关:轨道镜像(滑块的 left:2px 与 translateX 一并翻转)
102
+ * ------------------------------------------------------------------------- */
103
+ .fanui-switch__track { transform: scaleX(-1); }
104
+
105
+ /* ---------------------------------------------------------------------------
106
+ * 7. 进度 / 加载条 / 跑马灯:起始边与流向镜像
107
+ * ------------------------------------------------------------------------- */
108
+ .fanui-scroll-progress__bar,
109
+ .fanui-loading-bar__inner { transform-origin: right center; }
110
+
111
+ .fanui-marquee__track { animation-direction: reverse; }
112
+
113
+ /* ---------------------------------------------------------------------------
114
+ * 8. 方向性图标:必须水平翻转,否则语义相反
115
+ * ------------------------------------------------------------------------- */
116
+ .fanui-breadcrumb__separator,
117
+ .fanui-sidebar__item__arrow,
118
+ .fanui-cascader__arrow,
119
+ .fanui-cascader__caret,
120
+ .fanui-tree__caret,
121
+ .fanui-carousel__nav,
122
+ .fanui-datepanel__nav { display: inline-block; transform: scaleX(-1); }
123
+
124
+ /* ---------------------------------------------------------------------------
125
+ * 9. 悬浮操作球:贴边位置对调
126
+ * ------------------------------------------------------------------------- */
127
+ .fanui-fab { left: fn.fanui-var("space-5"); right: auto; }
128
+
129
+ /* ---------------------------------------------------------------------------
130
+ * 10. 跳转链接
131
+ * ------------------------------------------------------------------------- */
132
+ .fanui-skip-link { left: auto; right: fn.fanui-var("space-4"); }
133
+ }