@mr.slonn/my-scss-theme 1.0.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/README.md +182 -0
- package/index.scss +1 -0
- package/package.json +10 -0
- package/scss/.sassdocrc +85 -0
- package/scss/_font-vars.scss +25 -0
- package/scss/_fonts.scss +139 -0
- package/scss/abstracts/_animations.scss +819 -0
- package/scss/abstracts/_breakpoints.scss +865 -0
- package/scss/abstracts/_colors.scss +256 -0
- package/scss/abstracts/_functions.scss +3 -0
- package/scss/abstracts/_grid.scss +816 -0
- package/scss/abstracts/_index.scss +134 -0
- package/scss/abstracts/_mixins.scss +1142 -0
- package/scss/abstracts/_patterns.scss +657 -0
- package/scss/abstracts/_radius.scss +279 -0
- package/scss/abstracts/_search-form.scss +271 -0
- package/scss/abstracts/_spacing.scss +158 -0
- package/scss/abstracts/_typography.scss +477 -0
- package/scss/abstracts/_utilities.scss +0 -0
- package/scss/abstracts/_variables.scss +3 -0
- package/scss/abstracts/breadcrumb/_base.scss +194 -0
- package/scss/abstracts/breadcrumb/_index.scss +12 -0
- package/scss/abstracts/breadcrumb/_variables.scss +54 -0
- package/scss/abstracts/buttons/_base.scss +92 -0
- package/scss/abstracts/buttons/_index.scss +31 -0
- package/scss/abstracts/buttons/_modifiers.scss +114 -0
- package/scss/abstracts/buttons/_variables.scss +98 -0
- package/scss/abstracts/buttons/_variants.scss +295 -0
- package/scss/abstracts/callout/_base.scss +142 -0
- package/scss/abstracts/callout/_index.scss +23 -0
- package/scss/abstracts/callout/_variables.scss +27 -0
- package/scss/abstracts/cards/_base.scss +74 -0
- package/scss/abstracts/cards/_blur.scss +65 -0
- package/scss/abstracts/cards/_featured.scss +363 -0
- package/scss/abstracts/cards/_hover.scss +99 -0
- package/scss/abstracts/cards/_index.scss +51 -0
- package/scss/abstracts/cards/_parts.scss +135 -0
- package/scss/abstracts/cards/_sizes.scss +52 -0
- package/scss/abstracts/cards/_variables.scss +72 -0
- package/scss/abstracts/cards/_variants.scss +143 -0
- package/scss/abstracts/cards/_vertical.scss +218 -0
- package/scss/abstracts/chip/_base.scss +99 -0
- package/scss/abstracts/chip/_icon.scss +73 -0
- package/scss/abstracts/chip/_index.scss +21 -0
- package/scss/abstracts/chip/_variables.scss +57 -0
- package/scss/abstracts/chip/_variants.scss +98 -0
- package/scss/abstracts/file-card/_base.scss +326 -0
- package/scss/abstracts/file-card/_index.scss +12 -0
- package/scss/abstracts/file-card/_variables.scss +79 -0
- package/scss/abstracts/hero/_base.scss +182 -0
- package/scss/abstracts/hero/_index.scss +23 -0
- package/scss/abstracts/hero/_variables.scss +59 -0
- package/scss/abstracts/info-card/_base.scss +152 -0
- package/scss/abstracts/info-card/_index.scss +12 -0
- package/scss/abstracts/info-card/_variables.scss +44 -0
- package/scss/abstracts/news-card/_base.scss +143 -0
- package/scss/abstracts/news-card/_compact.scss +24 -0
- package/scss/abstracts/news-card/_featured.scss +83 -0
- package/scss/abstracts/news-card/_index.scss +31 -0
- package/scss/abstracts/news-card/_variables.scss +44 -0
- package/scss/abstracts/project-card/_base.scss +109 -0
- package/scss/abstracts/project-card/_index.scss +23 -0
- package/scss/abstracts/project-card/_variables.scss +20 -0
- package/scss/abstracts/search-form/_base.scss +132 -0
- package/scss/abstracts/search-form/_composite.scss +54 -0
- package/scss/abstracts/search-form/_index.scss +31 -0
- package/scss/abstracts/search-form/_theme.scss +89 -0
- package/scss/abstracts/search-form/_variables.scss +39 -0
- package/scss/abstracts/section-title/_base.scss +127 -0
- package/scss/abstracts/section-title/_index.scss +23 -0
- package/scss/abstracts/section-title/_variables.scss +27 -0
- package/scss/abstracts/service-card/_base.scss +77 -0
- package/scss/abstracts/service-card/_index.scss +23 -0
- package/scss/abstracts/service-card/_variables.scss +23 -0
- package/scss/abstracts/sidebar-menu/_base.scss +148 -0
- package/scss/abstracts/sidebar-menu/_index.scss +12 -0
- package/scss/abstracts/sidebar-menu/_variables.scss +55 -0
- package/scss/abstracts/stats-card/_base.scss +99 -0
- package/scss/abstracts/stats-card/_index.scss +23 -0
- package/scss/abstracts/stats-card/_variables.scss +32 -0
- package/scss/api.scss +12 -0
- package/scss/components/_breadcrumb.scss +140 -0
- package/scss/components/_buttons.scss +226 -0
- package/scss/components/_callout.scss +118 -0
- package/scss/components/_chip.scss +56 -0
- package/scss/components/_file-card.scss +182 -0
- package/scss/components/_hero.scss +103 -0
- package/scss/components/_index.scss +110 -0
- package/scss/components/_info-card.scss +103 -0
- package/scss/components/_news-card-compact.scss +33 -0
- package/scss/components/_news-card-featured.scss +49 -0
- package/scss/components/_news-card.scss +303 -0
- package/scss/components/_project-card.scss +93 -0
- package/scss/components/_search-form.scss +181 -0
- package/scss/components/_section-title.scss +94 -0
- package/scss/components/_service-card.scss +83 -0
- package/scss/components/_sidebar-menu.scss +82 -0
- package/scss/components/_stats-card.scss +92 -0
- package/scss/components/_tag.scss +144 -0
- package/scss/components/_top-nav.scss +537 -0
- package/scss/main.scss +73 -0
- package/scss/utilities/_index.scss +40 -0
- package/scss/utilities/_visibility.scss +224 -0
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// Анимации и переходы темы MOEXP
|
|
3
|
+
/// Timing, easing, keyframes, transition и hover/focus миксины
|
|
4
|
+
/// @group animations
|
|
5
|
+
/// @author MOEXP Team
|
|
6
|
+
////
|
|
7
|
+
|
|
8
|
+
// ============================================
|
|
9
|
+
// ANIMATIONS / TRANSITIONS
|
|
10
|
+
// local\common\theme-moexp\src\scss\abstracts\_animations.scss
|
|
11
|
+
// ============================================
|
|
12
|
+
|
|
13
|
+
@use "sass:list";
|
|
14
|
+
@use "mixins";
|
|
15
|
+
@use "colors";
|
|
16
|
+
@use "sass:meta";
|
|
17
|
+
|
|
18
|
+
// ============================================
|
|
19
|
+
// TIMING
|
|
20
|
+
// ============================================
|
|
21
|
+
|
|
22
|
+
/// Мгновенная длительность (50ms)
|
|
23
|
+
/// @type Number
|
|
24
|
+
/// @group animations-timing
|
|
25
|
+
$duration-instant: 50ms;
|
|
26
|
+
|
|
27
|
+
/// Быстрая длительность (150ms) — для микровзаимодействий
|
|
28
|
+
/// @type Number
|
|
29
|
+
/// @group animations-timing
|
|
30
|
+
$duration-fast: 150ms;
|
|
31
|
+
|
|
32
|
+
/// Нормальная длительность (250ms) — стандарт
|
|
33
|
+
/// @type Number
|
|
34
|
+
/// @group animations-timing
|
|
35
|
+
$duration-normal: 250ms;
|
|
36
|
+
|
|
37
|
+
/// Медленная длительность (350ms) — для крупных элементов
|
|
38
|
+
/// @type Number
|
|
39
|
+
/// @group animations-timing
|
|
40
|
+
$duration-slow: 350ms;
|
|
41
|
+
|
|
42
|
+
/// Очень медленная длительность (500ms) — для модалок, оверлеев
|
|
43
|
+
/// @type Number
|
|
44
|
+
/// @group animations-timing
|
|
45
|
+
$duration-slower: 500ms;
|
|
46
|
+
|
|
47
|
+
/// Алиас: XS длительность
|
|
48
|
+
/// @type Number
|
|
49
|
+
/// @group animations-timing
|
|
50
|
+
/// @alias duration-instant
|
|
51
|
+
$duration-xs: $duration-instant;
|
|
52
|
+
|
|
53
|
+
/// Алиас: SM длительность
|
|
54
|
+
/// @type Number
|
|
55
|
+
/// @group animations-timing
|
|
56
|
+
/// @alias duration-fast
|
|
57
|
+
$duration-sm: $duration-fast;
|
|
58
|
+
|
|
59
|
+
/// Алиас: MD длительность
|
|
60
|
+
/// @type Number
|
|
61
|
+
/// @group animations-timing
|
|
62
|
+
/// @alias duration-normal
|
|
63
|
+
$duration-md: $duration-normal;
|
|
64
|
+
|
|
65
|
+
/// Алиас: LG длительность
|
|
66
|
+
/// @type Number
|
|
67
|
+
/// @group animations-timing
|
|
68
|
+
/// @alias duration-slow
|
|
69
|
+
$duration-lg: $duration-slow;
|
|
70
|
+
|
|
71
|
+
/// Алиас: XL длительность
|
|
72
|
+
/// @type Number
|
|
73
|
+
/// @group animations-timing
|
|
74
|
+
/// @alias duration-slower
|
|
75
|
+
$duration-xl: $duration-slower;
|
|
76
|
+
|
|
77
|
+
// ============================================
|
|
78
|
+
// EASING
|
|
79
|
+
// ============================================
|
|
80
|
+
|
|
81
|
+
/// Линейная функция времени
|
|
82
|
+
/// @type String
|
|
83
|
+
/// @group animations-easing
|
|
84
|
+
$ease-linear: linear;
|
|
85
|
+
|
|
86
|
+
/// Ease-in — медленное начало, быстрый конец
|
|
87
|
+
/// @type String
|
|
88
|
+
/// @group animations-easing
|
|
89
|
+
$ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
90
|
+
|
|
91
|
+
/// Ease-out — быстрое начало, медленный конец (рекомендуется для UI)
|
|
92
|
+
/// @type String
|
|
93
|
+
/// @group animations-easing
|
|
94
|
+
$ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
95
|
+
|
|
96
|
+
/// Ease-in-out — медленные начало и конец
|
|
97
|
+
/// @type String
|
|
98
|
+
/// @group animations-easing
|
|
99
|
+
$ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
100
|
+
|
|
101
|
+
/// Bounce — с отскоком в конце
|
|
102
|
+
/// @type String
|
|
103
|
+
/// @group animations-easing
|
|
104
|
+
$ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
105
|
+
|
|
106
|
+
/// Smooth — плавная, естественная
|
|
107
|
+
/// @type String
|
|
108
|
+
/// @group animations-easing
|
|
109
|
+
$ease-smooth: cubic-bezier(0.25, 0.1, 0.25, 1);
|
|
110
|
+
|
|
111
|
+
/// Snappy — резкая, энергичная
|
|
112
|
+
/// @type String
|
|
113
|
+
/// @group animations-easing
|
|
114
|
+
$ease-snappy: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
115
|
+
|
|
116
|
+
/// Функция времени по умолчанию
|
|
117
|
+
/// @type String
|
|
118
|
+
/// @group animations-easing
|
|
119
|
+
$ease-default: $ease-smooth;
|
|
120
|
+
|
|
121
|
+
/// Длительность по умолчанию
|
|
122
|
+
/// @type Number
|
|
123
|
+
/// @group animations-timing
|
|
124
|
+
$duration-default: $duration-normal;
|
|
125
|
+
|
|
126
|
+
// ============================================
|
|
127
|
+
// KEYFRAMES
|
|
128
|
+
// ============================================
|
|
129
|
+
|
|
130
|
+
/// Плавное появление (opacity 0→1)
|
|
131
|
+
/// @group animations-keyframes
|
|
132
|
+
/// @output @keyframes fade-in
|
|
133
|
+
@mixin keyframes-fade-in {
|
|
134
|
+
@keyframes fade-in {
|
|
135
|
+
from {
|
|
136
|
+
opacity: 0;
|
|
137
|
+
}
|
|
138
|
+
to {
|
|
139
|
+
opacity: 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Появление снизу вверх с fade
|
|
145
|
+
/// @group animations-keyframes
|
|
146
|
+
/// @output @keyframes fade-in-up
|
|
147
|
+
@mixin keyframes-fade-in-up {
|
|
148
|
+
@keyframes fade-in-up {
|
|
149
|
+
from {
|
|
150
|
+
opacity: 0;
|
|
151
|
+
transform: translateY(8px);
|
|
152
|
+
}
|
|
153
|
+
to {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
transform: translateY(0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// Появление сверху вниз с fade
|
|
161
|
+
/// @group animations-keyframes
|
|
162
|
+
/// @output @keyframes fade-in-down
|
|
163
|
+
@mixin keyframes-fade-in-down {
|
|
164
|
+
@keyframes fade-in-down {
|
|
165
|
+
from {
|
|
166
|
+
opacity: 0;
|
|
167
|
+
transform: translateY(-8px);
|
|
168
|
+
}
|
|
169
|
+
to {
|
|
170
|
+
opacity: 1;
|
|
171
|
+
transform: translateY(0);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Появление с увеличением масштаба
|
|
177
|
+
/// @group animations-keyframes
|
|
178
|
+
/// @output @keyframes fade-in-scale
|
|
179
|
+
@mixin keyframes-fade-in-scale {
|
|
180
|
+
@keyframes fade-in-scale {
|
|
181
|
+
from {
|
|
182
|
+
opacity: 0;
|
|
183
|
+
transform: scale(0.95);
|
|
184
|
+
}
|
|
185
|
+
to {
|
|
186
|
+
opacity: 1;
|
|
187
|
+
transform: scale(1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Выезд справа
|
|
193
|
+
/// @group animations-keyframes
|
|
194
|
+
/// @output @keyframes slide-in-right
|
|
195
|
+
@mixin keyframes-slide-in-right {
|
|
196
|
+
@keyframes slide-in-right {
|
|
197
|
+
from {
|
|
198
|
+
transform: translateX(100%);
|
|
199
|
+
}
|
|
200
|
+
to {
|
|
201
|
+
transform: translateX(0);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// Выезд слева
|
|
207
|
+
/// @group animations-keyframes
|
|
208
|
+
/// @output @keyframes slide-in-left
|
|
209
|
+
@mixin keyframes-slide-in-left {
|
|
210
|
+
@keyframes slide-in-left {
|
|
211
|
+
from {
|
|
212
|
+
transform: translateX(-100%);
|
|
213
|
+
}
|
|
214
|
+
to {
|
|
215
|
+
transform: translateX(0);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Бесконечное вращение (для лоадеров)
|
|
221
|
+
/// @group animations-keyframes
|
|
222
|
+
/// @output @keyframes spin
|
|
223
|
+
@mixin keyframes-spin {
|
|
224
|
+
@keyframes spin {
|
|
225
|
+
from {
|
|
226
|
+
transform: rotate(0deg);
|
|
227
|
+
}
|
|
228
|
+
to {
|
|
229
|
+
transform: rotate(360deg);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/// Пульсация opacity (для скелетонов)
|
|
235
|
+
/// @group animations-keyframes
|
|
236
|
+
/// @output @keyframes pulse
|
|
237
|
+
@mixin keyframes-pulse {
|
|
238
|
+
@keyframes pulse {
|
|
239
|
+
0%,
|
|
240
|
+
100% {
|
|
241
|
+
opacity: 1;
|
|
242
|
+
}
|
|
243
|
+
50% {
|
|
244
|
+
opacity: 0.5;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/// Подпрыгивание
|
|
250
|
+
/// @group animations-keyframes
|
|
251
|
+
/// @output @keyframes bounce
|
|
252
|
+
@mixin keyframes-bounce {
|
|
253
|
+
@keyframes bounce {
|
|
254
|
+
0%,
|
|
255
|
+
100% {
|
|
256
|
+
transform: translateY(0);
|
|
257
|
+
}
|
|
258
|
+
50% {
|
|
259
|
+
transform: translateY(-25%);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ============================================
|
|
265
|
+
// TRANSITION MIXINS
|
|
266
|
+
// ============================================
|
|
267
|
+
|
|
268
|
+
/// Универсальный transition с поддержкой списка свойств
|
|
269
|
+
/// @param {String | List} $properties [all] - Свойство или список свойств
|
|
270
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
271
|
+
/// @param {String} $easing [$ease-default] - Функция времени
|
|
272
|
+
/// @param {Number} $delay [0ms] - Задержка
|
|
273
|
+
/// @example scss - Одно свойство
|
|
274
|
+
/// .element {
|
|
275
|
+
/// @include transition(opacity);
|
|
276
|
+
/// }
|
|
277
|
+
/// // => transition: opacity 250ms cubic-bezier(0.25, 0.1, 0.25, 1) 0ms;
|
|
278
|
+
/// @example scss - Несколько свойств
|
|
279
|
+
/// .button {
|
|
280
|
+
/// @include transition((color, background-color), $duration-fast);
|
|
281
|
+
/// }
|
|
282
|
+
/// // => transition: color 150ms ..., background-color 150ms ...;
|
|
283
|
+
/// @example scss - Кастомный easing
|
|
284
|
+
/// .modal {
|
|
285
|
+
/// @include transition(transform, $duration-slow, $ease-bounce);
|
|
286
|
+
/// }
|
|
287
|
+
/// @output transition: property duration easing delay;
|
|
288
|
+
/// @group animations-transition
|
|
289
|
+
/// @access public
|
|
290
|
+
@mixin transition($properties: all, $duration: $duration-default, $easing: $ease-default, $delay: 0ms) {
|
|
291
|
+
$result: ();
|
|
292
|
+
|
|
293
|
+
@if meta.type-of($properties) == list {
|
|
294
|
+
@each $prop in $properties {
|
|
295
|
+
$result: list.append($result, $prop $duration $easing $delay, comma);
|
|
296
|
+
}
|
|
297
|
+
} @else {
|
|
298
|
+
$result: $properties $duration $easing $delay;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
transition: $result;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/// Отключение transition
|
|
305
|
+
/// @example scss
|
|
306
|
+
/// .no-animation {
|
|
307
|
+
/// @include transition-none;
|
|
308
|
+
/// }
|
|
309
|
+
/// @output transition: none;
|
|
310
|
+
/// @group animations-transition
|
|
311
|
+
/// @access public
|
|
312
|
+
@mixin transition-none {
|
|
313
|
+
transition: none;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// Transition для интерактивных элементов (кнопки, ссылки)
|
|
317
|
+
/// Включает: color, background-color, border-color, box-shadow, transform
|
|
318
|
+
/// @require {mixin} transition
|
|
319
|
+
/// @example scss
|
|
320
|
+
/// .button {
|
|
321
|
+
/// @include transition-interactive;
|
|
322
|
+
/// }
|
|
323
|
+
/// @output transition для 5 свойств
|
|
324
|
+
/// @group animations-transition
|
|
325
|
+
/// @access public
|
|
326
|
+
@mixin transition-interactive {
|
|
327
|
+
@include transition((color, background-color, border-color, box-shadow, transform), $duration-fast);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/// Transition только для transform
|
|
331
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
332
|
+
/// @require {mixin} transition
|
|
333
|
+
/// @example scss
|
|
334
|
+
/// .card {
|
|
335
|
+
/// @include transition-transform;
|
|
336
|
+
///
|
|
337
|
+
/// &:hover {
|
|
338
|
+
/// transform: scale(1.02);
|
|
339
|
+
/// }
|
|
340
|
+
/// }
|
|
341
|
+
/// @output transition: transform duration ease-out;
|
|
342
|
+
/// @group animations-transition
|
|
343
|
+
/// @access public
|
|
344
|
+
@mixin transition-transform($duration: $duration-default) {
|
|
345
|
+
@include transition(transform, $duration, $ease-out);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/// Transition только для opacity
|
|
349
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
350
|
+
/// @require {mixin} transition
|
|
351
|
+
/// @example scss
|
|
352
|
+
/// .fade-element {
|
|
353
|
+
/// @include transition-opacity;
|
|
354
|
+
/// }
|
|
355
|
+
/// @output transition: opacity duration easing;
|
|
356
|
+
/// @group animations-transition
|
|
357
|
+
/// @access public
|
|
358
|
+
@mixin transition-opacity($duration: $duration-default) {
|
|
359
|
+
@include transition(opacity, $duration);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/// Transition для цветовых свойств
|
|
363
|
+
/// Включает: color, background-color, border-color
|
|
364
|
+
/// @param {Number} $duration [$duration-fast] - Длительность
|
|
365
|
+
/// @require {mixin} transition
|
|
366
|
+
/// @example scss
|
|
367
|
+
/// .link {
|
|
368
|
+
/// @include transition-colors;
|
|
369
|
+
/// }
|
|
370
|
+
/// @output transition для 3 цветовых свойств
|
|
371
|
+
/// @group animations-transition
|
|
372
|
+
/// @access public
|
|
373
|
+
@mixin transition-colors($duration: $duration-fast) {
|
|
374
|
+
@include transition((color, background-color, border-color), $duration);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ============================================
|
|
378
|
+
// ANIMATION MIXINS
|
|
379
|
+
// ============================================
|
|
380
|
+
|
|
381
|
+
/// Применяет заранее определённую keyframe-анимацию
|
|
382
|
+
/// @param {String} $name - Имя keyframes
|
|
383
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
384
|
+
/// @param {String} $easing [$ease-default] - Функция времени
|
|
385
|
+
/// @param {String} $fill-mode [forwards] - animation-fill-mode
|
|
386
|
+
/// @example scss
|
|
387
|
+
/// .modal {
|
|
388
|
+
/// @include animate(fade-in-scale, $duration-slow);
|
|
389
|
+
/// }
|
|
390
|
+
/// @output animation: name duration easing fill-mode;
|
|
391
|
+
/// @group animations-animate
|
|
392
|
+
/// @access public
|
|
393
|
+
@mixin animate($name, $duration: $duration-default, $easing: $ease-default, $fill-mode: forwards) {
|
|
394
|
+
animation: $name $duration $easing $fill-mode;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/// Анимация fade-in
|
|
398
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
399
|
+
/// @require {mixin} animate
|
|
400
|
+
/// @example scss
|
|
401
|
+
/// .element {
|
|
402
|
+
/// @include animate-fade-in;
|
|
403
|
+
/// }
|
|
404
|
+
/// @group animations-animate
|
|
405
|
+
/// @access public
|
|
406
|
+
@mixin animate-fade-in($duration: $duration-default) {
|
|
407
|
+
@include animate(fade-in, $duration);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/// Анимация fade-in с движением снизу
|
|
411
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
412
|
+
/// @require {mixin} animate
|
|
413
|
+
/// @example scss
|
|
414
|
+
/// .card {
|
|
415
|
+
/// @include animate-fade-in-up;
|
|
416
|
+
/// }
|
|
417
|
+
/// @group animations-animate
|
|
418
|
+
/// @access public
|
|
419
|
+
@mixin animate-fade-in-up($duration: $duration-default) {
|
|
420
|
+
@include animate(fade-in-up, $duration, $ease-out);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/// Анимация fade-in с масштабированием
|
|
424
|
+
/// @param {Number} $duration [$duration-default] - Длительность
|
|
425
|
+
/// @require {mixin} animate
|
|
426
|
+
/// @example scss
|
|
427
|
+
/// .modal {
|
|
428
|
+
/// @include animate-fade-in-scale;
|
|
429
|
+
/// }
|
|
430
|
+
/// @group animations-animate
|
|
431
|
+
/// @access public
|
|
432
|
+
@mixin animate-fade-in-scale($duration: $duration-default) {
|
|
433
|
+
@include animate(fade-in-scale, $duration, $ease-out);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/// Бесконечное вращение (для лоадеров)
|
|
437
|
+
/// @param {Number} $duration [1s] - Длительность одного оборота
|
|
438
|
+
/// @example scss
|
|
439
|
+
/// .spinner {
|
|
440
|
+
/// @include animate-spin;
|
|
441
|
+
/// }
|
|
442
|
+
/// @output animation: spin 1s linear infinite;
|
|
443
|
+
/// @group animations-animate
|
|
444
|
+
/// @access public
|
|
445
|
+
@mixin animate-spin($duration: 1s) {
|
|
446
|
+
animation: spin $duration linear infinite;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/// Бесконечная пульсация (для скелетонов)
|
|
450
|
+
/// @param {Number} $duration [2s] - Длительность цикла
|
|
451
|
+
/// @example scss
|
|
452
|
+
/// .skeleton {
|
|
453
|
+
/// @include animate-pulse;
|
|
454
|
+
/// }
|
|
455
|
+
/// @output animation: pulse 2s ease-in-out infinite;
|
|
456
|
+
/// @group animations-animate
|
|
457
|
+
/// @access public
|
|
458
|
+
@mixin animate-pulse($duration: 2s) {
|
|
459
|
+
animation: pulse $duration $ease-in-out infinite;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ============================================
|
|
463
|
+
// HOVER STATES
|
|
464
|
+
// ============================================
|
|
465
|
+
|
|
466
|
+
/// Hover с изменением цвета текста
|
|
467
|
+
/// @param {String} $color [accent] - Имя цвета при hover
|
|
468
|
+
/// @param {Number} $duration [$duration-fast] - Длительность transition
|
|
469
|
+
/// @require {mixin} transition
|
|
470
|
+
/// @require {mixin} mixins.text-color
|
|
471
|
+
/// @example scss
|
|
472
|
+
/// .link {
|
|
473
|
+
/// @include hover-text-color('accent');
|
|
474
|
+
/// }
|
|
475
|
+
/// @output transition + &:hover { color }
|
|
476
|
+
/// @group animations-hover
|
|
477
|
+
/// @access public
|
|
478
|
+
@mixin hover-text-color($color: accent, $duration: $duration-fast) {
|
|
479
|
+
@include transition(color, $duration);
|
|
480
|
+
|
|
481
|
+
&:hover {
|
|
482
|
+
@include mixins.text-color($color);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/// Hover с изменением фона
|
|
487
|
+
/// @param {String} $color [accent] - Имя цвета при hover
|
|
488
|
+
/// @param {Number} $alpha [1] - Прозрачность
|
|
489
|
+
/// @param {Number} $duration [$duration-fast] - Длительность transition
|
|
490
|
+
/// @require {mixin} transition
|
|
491
|
+
/// @require {mixin} mixins.bg-color
|
|
492
|
+
/// @example scss
|
|
493
|
+
/// .menu-item {
|
|
494
|
+
/// @include hover-bg-color('accent', 0.1);
|
|
495
|
+
/// }
|
|
496
|
+
/// @output transition + &:hover { background-color }
|
|
497
|
+
/// @group animations-hover
|
|
498
|
+
/// @access public
|
|
499
|
+
@mixin hover-bg-color($color: accent, $alpha: 1, $duration: $duration-fast) {
|
|
500
|
+
@include transition(background-color, $duration);
|
|
501
|
+
|
|
502
|
+
&:hover {
|
|
503
|
+
@include mixins.bg-color($color, $alpha);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/// Hover с масштабированием
|
|
508
|
+
/// @param {Number} $scale [1.05] - Масштаб при hover
|
|
509
|
+
/// @param {Boolean} $lift [false] - Добавить тень при hover
|
|
510
|
+
/// @require {mixin} transition-transform
|
|
511
|
+
/// @require {mixin} mixins.shadow-md
|
|
512
|
+
/// @example scss - Без тени
|
|
513
|
+
/// .card {
|
|
514
|
+
/// @include hover-scale(1.02);
|
|
515
|
+
/// }
|
|
516
|
+
/// @example scss - С тенью
|
|
517
|
+
/// .card {
|
|
518
|
+
/// @include hover-scale(1.02, true);
|
|
519
|
+
/// }
|
|
520
|
+
/// @output transition + &:hover { transform, box-shadow? }
|
|
521
|
+
/// @group animations-hover
|
|
522
|
+
/// @access public
|
|
523
|
+
@mixin hover-scale($scale: 1.05, $lift: false) {
|
|
524
|
+
@include transition-transform($duration-fast);
|
|
525
|
+
|
|
526
|
+
@if $lift {
|
|
527
|
+
@include transition((transform, box-shadow), $duration-fast);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
&:hover {
|
|
531
|
+
transform: scale($scale);
|
|
532
|
+
|
|
533
|
+
@if $lift {
|
|
534
|
+
@include mixins.shadow-md();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/// Hover lift — подъём вверх с тенью
|
|
540
|
+
/// @param {Number} $y [-2px] - Смещение по Y
|
|
541
|
+
/// @require {mixin} transition
|
|
542
|
+
/// @require {mixin} mixins.shadow-lg
|
|
543
|
+
/// @example scss
|
|
544
|
+
/// .card {
|
|
545
|
+
/// @include hover-lift(-4px);
|
|
546
|
+
/// }
|
|
547
|
+
/// @output transition + &:hover { transform, box-shadow }
|
|
548
|
+
/// @group animations-hover
|
|
549
|
+
/// @access public
|
|
550
|
+
@mixin hover-lift($y: -2px) {
|
|
551
|
+
@include transition((transform, box-shadow), $duration-fast);
|
|
552
|
+
|
|
553
|
+
&:hover {
|
|
554
|
+
transform: translateY($y);
|
|
555
|
+
@include mixins.shadow-lg();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// ============================================
|
|
560
|
+
// FOCUS STATES
|
|
561
|
+
// ============================================
|
|
562
|
+
|
|
563
|
+
/// Базовый focus ring (только для :focus-visible)
|
|
564
|
+
/// @param {String} $color [ring] - Имя цвета кольца
|
|
565
|
+
/// @param {Number} $width [2px] - Толщина кольца
|
|
566
|
+
/// @param {Number} $offset [2px] - Отступ от элемента
|
|
567
|
+
/// @require {mixin} mixins.focus-ring
|
|
568
|
+
/// @example scss
|
|
569
|
+
/// .button {
|
|
570
|
+
/// @include focus-visible-ring;
|
|
571
|
+
/// }
|
|
572
|
+
/// @example scss - Кастомный цвет
|
|
573
|
+
/// .link {
|
|
574
|
+
/// @include focus-visible-ring('accent', 2px, 4px);
|
|
575
|
+
/// }
|
|
576
|
+
/// @output &:focus { outline: none } + &:focus-visible { outline }
|
|
577
|
+
/// @group animations-focus
|
|
578
|
+
/// @access public
|
|
579
|
+
@mixin focus-visible-ring($color: ring, $width: 2px, $offset: 2px) {
|
|
580
|
+
&:focus {
|
|
581
|
+
outline: none;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
&:focus-visible {
|
|
585
|
+
@include mixins.focus-ring($color, $width, $offset);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/// Focus для инпутов — с изменением border и ring-shadow
|
|
590
|
+
/// @param {String} $color [ring] - Имя цвета
|
|
591
|
+
/// @require {mixin} transition
|
|
592
|
+
/// @require {mixin} mixins.border-color-hsl
|
|
593
|
+
/// @require {mixin} mixins.box-shadow-hsl
|
|
594
|
+
/// @example scss
|
|
595
|
+
/// .input {
|
|
596
|
+
/// @include input-focus;
|
|
597
|
+
/// }
|
|
598
|
+
/// @output transition + &:focus { border-color, box-shadow }
|
|
599
|
+
/// @group animations-focus
|
|
600
|
+
/// @access public
|
|
601
|
+
@mixin input-focus($color: ring) {
|
|
602
|
+
@include transition((border-color, box-shadow), $duration-fast);
|
|
603
|
+
|
|
604
|
+
&:focus {
|
|
605
|
+
outline: none;
|
|
606
|
+
@include mixins.border-color-hsl($color);
|
|
607
|
+
@include mixins.box-shadow-hsl($color, 0.25, 0, 0, 0, 3px);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/// Focus для инпутов — лёгкий вариант (только border)
|
|
612
|
+
/// @param {String} $color [ring] - Имя цвета
|
|
613
|
+
/// @require {mixin} transition
|
|
614
|
+
/// @require {mixin} mixins.border-color-hsl
|
|
615
|
+
/// @example scss
|
|
616
|
+
/// .input--minimal {
|
|
617
|
+
/// @include input-focus-light;
|
|
618
|
+
/// }
|
|
619
|
+
/// @output transition + &:focus { border-color }
|
|
620
|
+
/// @group animations-focus
|
|
621
|
+
/// @access public
|
|
622
|
+
@mixin input-focus-light($color: ring) {
|
|
623
|
+
@include transition(border-color, $duration-fast);
|
|
624
|
+
|
|
625
|
+
&:focus {
|
|
626
|
+
outline: none;
|
|
627
|
+
@include mixins.border-color-hsl($color);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// ============================================
|
|
632
|
+
// BUTTON STATES
|
|
633
|
+
// ============================================
|
|
634
|
+
|
|
635
|
+
/// Полный набор состояний для кнопки
|
|
636
|
+
/// Включает: base, hover, active, focus-visible
|
|
637
|
+
/// @param {String} $bg [primary] - Цвет фона
|
|
638
|
+
/// @param {String} $bg-hover [null] - Цвет фона при hover (null = brightness filter)
|
|
639
|
+
/// @param {String} $color [primary-foreground] - Цвет текста
|
|
640
|
+
/// @param {Number} $scale-active [0.98] - Масштаб при active
|
|
641
|
+
/// @require {mixin} mixins.bg-color
|
|
642
|
+
/// @require {mixin} mixins.text-color
|
|
643
|
+
/// @require {mixin} transition-interactive
|
|
644
|
+
/// @require {mixin} focus-visible-ring
|
|
645
|
+
/// @example scss - Базовая кнопка
|
|
646
|
+
/// .btn-primary {
|
|
647
|
+
/// @include button-states;
|
|
648
|
+
/// }
|
|
649
|
+
/// @example scss - Кастомные цвета
|
|
650
|
+
/// .btn-accent {
|
|
651
|
+
/// @include button-states('accent', 'accent-on-light', 'accent-foreground');
|
|
652
|
+
/// }
|
|
653
|
+
/// @output background, color, transition, &:hover, &:active, &:focus-visible
|
|
654
|
+
/// @group animations-button
|
|
655
|
+
/// @access public
|
|
656
|
+
@mixin button-states($bg: primary, $bg-hover: null, $color: primary-foreground, $scale-active: 0.98) {
|
|
657
|
+
@include mixins.bg-color($bg);
|
|
658
|
+
@include mixins.text-color($color);
|
|
659
|
+
@include transition-interactive;
|
|
660
|
+
|
|
661
|
+
@if $bg-hover {
|
|
662
|
+
&:hover {
|
|
663
|
+
@include mixins.bg-color($bg-hover);
|
|
664
|
+
}
|
|
665
|
+
} @else {
|
|
666
|
+
&:hover {
|
|
667
|
+
filter: brightness(1.1);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
&:active {
|
|
672
|
+
transform: scale($scale-active);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
@include focus-visible-ring;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/// Ghost button — прозрачный с обводкой
|
|
679
|
+
/// @param {String} $color [primary] - Цвет текста и обводки
|
|
680
|
+
/// @require {mixin} mixins.text-color
|
|
681
|
+
/// @require {mixin} mixins.border-hsl
|
|
682
|
+
/// @require {mixin} mixins.bg-color
|
|
683
|
+
/// @require {mixin} mixins.border-color-hsl
|
|
684
|
+
/// @require {mixin} transition-interactive
|
|
685
|
+
/// @require {mixin} focus-visible-ring
|
|
686
|
+
/// @example scss
|
|
687
|
+
/// .btn-ghost {
|
|
688
|
+
/// @include button-ghost;
|
|
689
|
+
/// }
|
|
690
|
+
/// @example scss - Акцентный ghost
|
|
691
|
+
/// .btn-ghost-accent {
|
|
692
|
+
/// @include button-ghost('accent');
|
|
693
|
+
/// }
|
|
694
|
+
/// @output transparent bg, border, &:hover { bg, border-color }
|
|
695
|
+
/// @group animations-button
|
|
696
|
+
/// @access public
|
|
697
|
+
@mixin button-ghost($color: primary) {
|
|
698
|
+
background: transparent;
|
|
699
|
+
@include mixins.text-color($color);
|
|
700
|
+
@include mixins.border-hsl(1px, solid, $color, 0.3);
|
|
701
|
+
@include transition-interactive;
|
|
702
|
+
|
|
703
|
+
&:hover {
|
|
704
|
+
@include mixins.bg-color($color, 0.1);
|
|
705
|
+
@include mixins.border-color-hsl($color, 0.5);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
@include focus-visible-ring($color);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ============================================
|
|
712
|
+
// ICON INTERACTIONS
|
|
713
|
+
// ============================================
|
|
714
|
+
|
|
715
|
+
/// Hover для иконки — масштаб + цвет
|
|
716
|
+
/// @param {String} $color-hover [accent] - Цвет при hover
|
|
717
|
+
/// @param {Number} $scale [1.1] - Масштаб при hover
|
|
718
|
+
/// @require {mixin} transition
|
|
719
|
+
/// @require {mixin} mixins.text-color
|
|
720
|
+
/// @require {mixin} focus-visible-ring
|
|
721
|
+
/// @example scss
|
|
722
|
+
/// .social-icon {
|
|
723
|
+
/// @include icon-hover('accent', 1.15);
|
|
724
|
+
/// }
|
|
725
|
+
/// @output transition + &:hover { transform, color } + focus-ring
|
|
726
|
+
/// @group animations-icon
|
|
727
|
+
/// @access public
|
|
728
|
+
@mixin icon-hover($color-hover: accent, $scale: 1.1) {
|
|
729
|
+
@include transition((color, transform), $duration-fast);
|
|
730
|
+
|
|
731
|
+
&:hover {
|
|
732
|
+
transform: scale($scale);
|
|
733
|
+
@include mixins.text-color($color-hover);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
@include focus-visible-ring;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/// Кнопка-иконка — круглая с hover-эффектом
|
|
740
|
+
/// @param {Number} $size [40px] - Размер кнопки
|
|
741
|
+
/// @param {String} $color [foreground-muted] - Цвет иконки
|
|
742
|
+
/// @param {String} $color-hover [accent] - Цвет при hover
|
|
743
|
+
/// @require {mixin} mixins.button-reset
|
|
744
|
+
/// @require {mixin} mixins.text-color
|
|
745
|
+
/// @require {mixin} mixins.bg-color
|
|
746
|
+
/// @require {mixin} transition-interactive
|
|
747
|
+
/// @require {mixin} focus-visible-ring
|
|
748
|
+
/// @example scss
|
|
749
|
+
/// .icon-btn {
|
|
750
|
+
/// @include icon-button(48px, 'foreground', 'primary');
|
|
751
|
+
/// }
|
|
752
|
+
/// @output Круглая кнопка с hover bg и color
|
|
753
|
+
/// @group animations-icon
|
|
754
|
+
/// @access public
|
|
755
|
+
@mixin icon-button($size: 40px, $color: foreground-muted, $color-hover: accent) {
|
|
756
|
+
@include mixins.button-reset;
|
|
757
|
+
display: inline-flex;
|
|
758
|
+
align-items: center;
|
|
759
|
+
justify-content: center;
|
|
760
|
+
width: $size;
|
|
761
|
+
height: $size;
|
|
762
|
+
border-radius: 50%;
|
|
763
|
+
@include mixins.text-color($color);
|
|
764
|
+
@include transition-interactive;
|
|
765
|
+
|
|
766
|
+
&:hover {
|
|
767
|
+
@include mixins.text-color($color-hover);
|
|
768
|
+
@include mixins.bg-color($color-hover, 0.1);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
@include focus-visible-ring;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// ============================================
|
|
775
|
+
// ACCESSIBILITY
|
|
776
|
+
// ============================================
|
|
777
|
+
|
|
778
|
+
/// Отключение анимаций для prefers-reduced-motion
|
|
779
|
+
/// Применяется к конкретному элементу
|
|
780
|
+
/// @example scss
|
|
781
|
+
/// .animated-element {
|
|
782
|
+
/// @include animate-fade-in;
|
|
783
|
+
/// @include disable-animation;
|
|
784
|
+
/// }
|
|
785
|
+
/// @output @media (prefers-reduced-motion) { animation: none; transition: none; }
|
|
786
|
+
/// @group animations-a11y
|
|
787
|
+
/// @access public
|
|
788
|
+
@mixin disable-animation {
|
|
789
|
+
@media (prefers-reduced-motion: reduce) {
|
|
790
|
+
animation: none;
|
|
791
|
+
transition: none;
|
|
792
|
+
|
|
793
|
+
&::before,
|
|
794
|
+
&::after {
|
|
795
|
+
animation: none;
|
|
796
|
+
transition: none;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/// Глобальное отключение анимаций для prefers-reduced-motion
|
|
802
|
+
/// Применить один раз в base/_reset.scss
|
|
803
|
+
/// @example scss
|
|
804
|
+
/// // В base/_reset.scss
|
|
805
|
+
/// @include disable-all-animations;
|
|
806
|
+
/// @output @media (prefers-reduced-motion) { *, *::before, *::after { ... } }
|
|
807
|
+
/// @group animations-a11y
|
|
808
|
+
/// @access public
|
|
809
|
+
@mixin disable-all-animations {
|
|
810
|
+
@media (prefers-reduced-motion: reduce) {
|
|
811
|
+
*,
|
|
812
|
+
*::before,
|
|
813
|
+
*::after {
|
|
814
|
+
animation-duration: 0.01ms !important;
|
|
815
|
+
animation-iteration-count: 1 !important;
|
|
816
|
+
transition-duration: 0.01ms !important;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|