@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.
Files changed (103) hide show
  1. package/README.md +182 -0
  2. package/index.scss +1 -0
  3. package/package.json +10 -0
  4. package/scss/.sassdocrc +85 -0
  5. package/scss/_font-vars.scss +25 -0
  6. package/scss/_fonts.scss +139 -0
  7. package/scss/abstracts/_animations.scss +819 -0
  8. package/scss/abstracts/_breakpoints.scss +865 -0
  9. package/scss/abstracts/_colors.scss +256 -0
  10. package/scss/abstracts/_functions.scss +3 -0
  11. package/scss/abstracts/_grid.scss +816 -0
  12. package/scss/abstracts/_index.scss +134 -0
  13. package/scss/abstracts/_mixins.scss +1142 -0
  14. package/scss/abstracts/_patterns.scss +657 -0
  15. package/scss/abstracts/_radius.scss +279 -0
  16. package/scss/abstracts/_search-form.scss +271 -0
  17. package/scss/abstracts/_spacing.scss +158 -0
  18. package/scss/abstracts/_typography.scss +477 -0
  19. package/scss/abstracts/_utilities.scss +0 -0
  20. package/scss/abstracts/_variables.scss +3 -0
  21. package/scss/abstracts/breadcrumb/_base.scss +194 -0
  22. package/scss/abstracts/breadcrumb/_index.scss +12 -0
  23. package/scss/abstracts/breadcrumb/_variables.scss +54 -0
  24. package/scss/abstracts/buttons/_base.scss +92 -0
  25. package/scss/abstracts/buttons/_index.scss +31 -0
  26. package/scss/abstracts/buttons/_modifiers.scss +114 -0
  27. package/scss/abstracts/buttons/_variables.scss +98 -0
  28. package/scss/abstracts/buttons/_variants.scss +295 -0
  29. package/scss/abstracts/callout/_base.scss +142 -0
  30. package/scss/abstracts/callout/_index.scss +23 -0
  31. package/scss/abstracts/callout/_variables.scss +27 -0
  32. package/scss/abstracts/cards/_base.scss +74 -0
  33. package/scss/abstracts/cards/_blur.scss +65 -0
  34. package/scss/abstracts/cards/_featured.scss +363 -0
  35. package/scss/abstracts/cards/_hover.scss +99 -0
  36. package/scss/abstracts/cards/_index.scss +51 -0
  37. package/scss/abstracts/cards/_parts.scss +135 -0
  38. package/scss/abstracts/cards/_sizes.scss +52 -0
  39. package/scss/abstracts/cards/_variables.scss +72 -0
  40. package/scss/abstracts/cards/_variants.scss +143 -0
  41. package/scss/abstracts/cards/_vertical.scss +218 -0
  42. package/scss/abstracts/chip/_base.scss +99 -0
  43. package/scss/abstracts/chip/_icon.scss +73 -0
  44. package/scss/abstracts/chip/_index.scss +21 -0
  45. package/scss/abstracts/chip/_variables.scss +57 -0
  46. package/scss/abstracts/chip/_variants.scss +98 -0
  47. package/scss/abstracts/file-card/_base.scss +326 -0
  48. package/scss/abstracts/file-card/_index.scss +12 -0
  49. package/scss/abstracts/file-card/_variables.scss +79 -0
  50. package/scss/abstracts/hero/_base.scss +182 -0
  51. package/scss/abstracts/hero/_index.scss +23 -0
  52. package/scss/abstracts/hero/_variables.scss +59 -0
  53. package/scss/abstracts/info-card/_base.scss +152 -0
  54. package/scss/abstracts/info-card/_index.scss +12 -0
  55. package/scss/abstracts/info-card/_variables.scss +44 -0
  56. package/scss/abstracts/news-card/_base.scss +143 -0
  57. package/scss/abstracts/news-card/_compact.scss +24 -0
  58. package/scss/abstracts/news-card/_featured.scss +83 -0
  59. package/scss/abstracts/news-card/_index.scss +31 -0
  60. package/scss/abstracts/news-card/_variables.scss +44 -0
  61. package/scss/abstracts/project-card/_base.scss +109 -0
  62. package/scss/abstracts/project-card/_index.scss +23 -0
  63. package/scss/abstracts/project-card/_variables.scss +20 -0
  64. package/scss/abstracts/search-form/_base.scss +132 -0
  65. package/scss/abstracts/search-form/_composite.scss +54 -0
  66. package/scss/abstracts/search-form/_index.scss +31 -0
  67. package/scss/abstracts/search-form/_theme.scss +89 -0
  68. package/scss/abstracts/search-form/_variables.scss +39 -0
  69. package/scss/abstracts/section-title/_base.scss +127 -0
  70. package/scss/abstracts/section-title/_index.scss +23 -0
  71. package/scss/abstracts/section-title/_variables.scss +27 -0
  72. package/scss/abstracts/service-card/_base.scss +77 -0
  73. package/scss/abstracts/service-card/_index.scss +23 -0
  74. package/scss/abstracts/service-card/_variables.scss +23 -0
  75. package/scss/abstracts/sidebar-menu/_base.scss +148 -0
  76. package/scss/abstracts/sidebar-menu/_index.scss +12 -0
  77. package/scss/abstracts/sidebar-menu/_variables.scss +55 -0
  78. package/scss/abstracts/stats-card/_base.scss +99 -0
  79. package/scss/abstracts/stats-card/_index.scss +23 -0
  80. package/scss/abstracts/stats-card/_variables.scss +32 -0
  81. package/scss/api.scss +12 -0
  82. package/scss/components/_breadcrumb.scss +140 -0
  83. package/scss/components/_buttons.scss +226 -0
  84. package/scss/components/_callout.scss +118 -0
  85. package/scss/components/_chip.scss +56 -0
  86. package/scss/components/_file-card.scss +182 -0
  87. package/scss/components/_hero.scss +103 -0
  88. package/scss/components/_index.scss +110 -0
  89. package/scss/components/_info-card.scss +103 -0
  90. package/scss/components/_news-card-compact.scss +33 -0
  91. package/scss/components/_news-card-featured.scss +49 -0
  92. package/scss/components/_news-card.scss +303 -0
  93. package/scss/components/_project-card.scss +93 -0
  94. package/scss/components/_search-form.scss +181 -0
  95. package/scss/components/_section-title.scss +94 -0
  96. package/scss/components/_service-card.scss +83 -0
  97. package/scss/components/_sidebar-menu.scss +82 -0
  98. package/scss/components/_stats-card.scss +92 -0
  99. package/scss/components/_tag.scss +144 -0
  100. package/scss/components/_top-nav.scss +537 -0
  101. package/scss/main.scss +73 -0
  102. package/scss/utilities/_index.scss +40 -0
  103. 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
+ }