@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,1142 @@
1
+ ////
2
+ /// Миксины темы MOEXP
3
+ /// Цвета, тени, градиенты, flex-gap, утилиты и фоновые изображения
4
+ /// @group mixins
5
+ /// @author MOEXP Team
6
+ ////
7
+
8
+ // ============================================
9
+ // MIXINS
10
+ // local\common\theme-moexp\src\scss\abstracts\_mixins.scss
11
+ // ============================================
12
+
13
+ @use "sass:math";
14
+ @use "sass:list";
15
+ @use "sass:map";
16
+ @use "sass:meta";
17
+ @use "colors";
18
+ @use "breakpoints";
19
+
20
+ // ============================================
21
+ // ЦВЕТОВЫЕ МИКСИНЫ
22
+ // ============================================
23
+ /// Генерирует fallback + modern hsl() для любого CSS-свойства
24
+ /// Обеспечивает поддержку браузеров без CSS custom properties
25
+ /// @param {String} $property - CSS-свойство (color, background-color, border-color и т.д.)
26
+ /// @param {String} $var-name - Имя цвета из палитры (без "--")
27
+ /// @param {Number} $alpha [1] - Прозрачность от 0 до 1
28
+ /// @require {function} colors.color-static
29
+ /// @require {function} colors.color-var
30
+ /// @example scss - Цвет текста
31
+ /// .element {
32
+ /// @include hsl-prop(color, 'primary');
33
+ /// }
34
+ /// // => color: hsl(209, 32%, 35%);
35
+ /// // => color: hsl(var(--primary));
36
+ /// @example scss - Полупрозрачный фон
37
+ /// .overlay {
38
+ /// @include hsl-prop(background-color, 'overlay', 0.5);
39
+ /// }
40
+ /// @output Два объявления: fallback и modern
41
+ /// @group mixins-color
42
+ /// @access public
43
+ @mixin hsl-prop($property, $var-name, $alpha: 1) {
44
+ // Fallback: статический цвет для браузеров без CSS переменных
45
+ #{$property}: colors.color-static($var-name, $alpha);
46
+
47
+ // Modern: CSS переменные
48
+ #{$property}: colors.color-var($var-name, $alpha);
49
+ }
50
+
51
+ /// Shortcut для color с fallback
52
+ /// @param {String} $var-name - Имя цвета из палитры
53
+ /// @param {Number} $alpha [1] - Прозрачность от 0 до 1
54
+ /// @require {mixin} hsl-prop
55
+ /// @example scss - Основной текст
56
+ /// .text {
57
+ /// @include text-color('foreground-body');
58
+ /// }
59
+ /// @example scss - Приглушённый текст
60
+ /// .muted {
61
+ /// @include text-color('foreground-muted', 0.8);
62
+ /// }
63
+ /// @output color с fallback
64
+ /// @group mixins-color
65
+ /// @access public
66
+ @mixin text-color($var-name, $alpha: 1) {
67
+ @include hsl-prop(color, $var-name, $alpha);
68
+ }
69
+
70
+ /// Shortcut для background-color с fallback
71
+ /// @param {String} $var-name - Имя цвета из палитры
72
+ /// @param {Number} $alpha [1] - Прозрачность от 0 до 1
73
+ /// @require {mixin} hsl-prop
74
+ /// @example scss - Фон карточки
75
+ /// .card {
76
+ /// @include bg-color('background-card');
77
+ /// }
78
+ /// @example scss - Полупрозрачный оверлей
79
+ /// .overlay {
80
+ /// @include bg-color('overlay', 0.7);
81
+ /// }
82
+ /// @output background-color с fallback
83
+ /// @group mixins-color
84
+ /// @access public
85
+ @mixin bg-color($var-name, $alpha: 1) {
86
+ @include hsl-prop(background-color, $var-name, $alpha);
87
+ }
88
+
89
+ /// Border с полным объявлением и fallback
90
+ /// @param {Number} $width - Толщина границы
91
+ /// @param {String} $style - Стиль границы (solid, dashed, dotted и т.д.)
92
+ /// @param {String} $var-name - Имя цвета из палитры
93
+ /// @param {Number} $alpha [1] - Прозрачность от 0 до 1
94
+ /// @require {function} colors.color-static
95
+ /// @require {function} colors.color-var
96
+ /// @example scss - Обычная граница
97
+ /// .card {
98
+ /// @include border-hsl(1px, solid, 'border');
99
+ /// }
100
+ /// @example scss - Полупрозрачная граница
101
+ /// .subtle {
102
+ /// @include border-hsl(1px, solid, 'border', 0.5);
103
+ /// }
104
+ /// @output border с fallback
105
+ /// @group mixins-color
106
+ /// @access public
107
+ @mixin border-hsl($width, $style, $var-name, $alpha: 1) {
108
+ border: $width $style colors.color-static($var-name, $alpha);
109
+ border: $width $style colors.color-var($var-name, $alpha);
110
+ }
111
+
112
+ /// Только border-color с fallback
113
+ /// @param {String} $var-name - Имя цвета из палитры
114
+ /// @param {Number} $alpha [1] - Прозрачность от 0 до 1
115
+ /// @require {mixin} hsl-prop
116
+ /// @example scss - Изменение цвета границы при hover
117
+ /// .input:focus {
118
+ /// @include border-color-hsl('ring');
119
+ /// }
120
+ /// @output border-color с fallback
121
+ /// @group mixins-color
122
+ /// @access public
123
+ @mixin border-color-hsl($var-name, $alpha: 1) {
124
+ @include hsl-prop(border-color, $var-name, $alpha);
125
+ }
126
+
127
+ // ============================================
128
+ // BOX-SHADOW
129
+ // ============================================
130
+
131
+ /// Box-shadow с одной тенью и fallback
132
+ /// @param {String} $var-name - Имя цвета из палитры
133
+ /// @param {Number} $alpha - Прозрачность тени
134
+ /// @param {Number} $x [0] - Смещение по X
135
+ /// @param {Number} $y [4px] - Смещение по Y
136
+ /// @param {Number} $blur [8px] - Радиус размытия
137
+ /// @param {Number} $spread [0] - Радиус распространения
138
+ /// @param {Boolean} $inset [false] - Внутренняя тень
139
+ /// @require {function} colors.color-static
140
+ /// @require {function} colors.color-var
141
+ /// @example scss - Обычная тень
142
+ /// .card {
143
+ /// @include box-shadow-hsl('shadow', 0.15);
144
+ /// }
145
+ /// @example scss - Внутренняя тень
146
+ /// .input {
147
+ /// @include box-shadow-hsl('shadow', 0.1, 0, 2px, 4px, 0, true);
148
+ /// }
149
+ /// @output box-shadow с fallback
150
+ /// @group mixins-shadow
151
+ /// @access public
152
+ @mixin box-shadow-hsl($var-name, $alpha, $x: 0, $y: 4px, $blur: 8px, $spread: 0, $inset: false) {
153
+ @if $inset {
154
+ // Fallback
155
+ box-shadow: inset $x $y $blur $spread colors.color-static($var-name, $alpha);
156
+ // Modern
157
+ box-shadow: inset $x $y $blur $spread colors.color-var($var-name, $alpha);
158
+ } @else {
159
+ // Fallback
160
+ box-shadow: $x $y $blur $spread colors.color-static($var-name, $alpha);
161
+ // Modern
162
+ box-shadow: $x $y $blur $spread colors.color-var($var-name, $alpha);
163
+ }
164
+ }
165
+
166
+ /// Многослойная тень с fallback
167
+ /// @param {String} $color - Имя цвета из палитры
168
+ /// @param {List} $layers - Список слоёв: ((x, y, blur, spread, alpha), ...)
169
+ /// @require {function} colors.color-static
170
+ /// @require {function} colors.color-var
171
+ /// @example scss - Двухслойная тень
172
+ /// .card {
173
+ /// @include box-shadow-layers('shadow', (
174
+ /// (0, 4px, 6px, -1px, 0.1),
175
+ /// (0, 2px, 4px, -2px, 0.1)
176
+ /// ));
177
+ /// }
178
+ /// @output box-shadow с несколькими слоями и fallback
179
+ /// @group mixins-shadow
180
+ /// @access public
181
+ @mixin box-shadow-layers($color, $layers) {
182
+ $fallback: ();
183
+ $modern: ();
184
+
185
+ @each $layer in $layers {
186
+ $x: list.nth($layer, 1);
187
+ $y: list.nth($layer, 2);
188
+
189
+ $blur: 0;
190
+ @if list.length($layer) >= 3 {
191
+ $blur: list.nth($layer, 3);
192
+ }
193
+
194
+ $spread: 0;
195
+ @if list.length($layer) >= 4 {
196
+ $spread: list.nth($layer, 4);
197
+ }
198
+
199
+ $alpha: 0.15;
200
+ @if list.length($layer) >= 5 {
201
+ $alpha: list.nth($layer, 5);
202
+ }
203
+
204
+ $fallback: list.append($fallback, #{$x} #{$y} #{$blur} #{$spread} colors.color-static($color, $alpha), comma);
205
+ $modern: list.append($modern, #{$x} #{$y} #{$blur} #{$spread} colors.color-var($color, $alpha), comma);
206
+ }
207
+
208
+ box-shadow: $fallback;
209
+ box-shadow: $modern;
210
+ }
211
+ /// Маленькая тень (SM)
212
+ /// @param {String} $color [shadow] - Имя цвета
213
+ /// @require {mixin} box-shadow-hsl
214
+ /// @example scss
215
+ /// .button {
216
+ /// @include shadow-sm;
217
+ /// }
218
+ /// @output box-shadow: 0 1px 2px 0 color/0.05
219
+ /// @group mixins-shadow
220
+ /// @access public
221
+ @mixin shadow-sm($color: shadow) {
222
+ @include box-shadow-hsl($color, 0.05, 0, 1px, 2px, 0);
223
+ }
224
+
225
+ /// Средняя тень (MD) — двухслойная
226
+ /// @param {String} $color [shadow] - Имя цвета
227
+ /// @require {mixin} box-shadow-layers
228
+ /// @example scss
229
+ /// .card {
230
+ /// @include shadow-md;
231
+ /// }
232
+ /// @group mixins-shadow
233
+ /// @access public
234
+ @mixin shadow-md($color: shadow) {
235
+ @include box-shadow-layers($color, ((0, 4px, 6px, -1px, 0.1), (0, 2px, 4px, -2px, 0.1)));
236
+ }
237
+
238
+ /// Большая тень (LG) — двухслойная
239
+ /// @param {String} $color [shadow] - Имя цвета
240
+ /// @require {mixin} box-shadow-layers
241
+ /// @example scss
242
+ /// .modal {
243
+ /// @include shadow-lg;
244
+ /// }
245
+ /// @group mixins-shadow
246
+ /// @access public
247
+ @mixin shadow-lg($color: shadow) {
248
+ @include box-shadow-layers($color, ((0, 10px, 15px, -3px, 0.1), (0, 4px, 6px, -4px, 0.1)));
249
+ }
250
+
251
+ /// Очень большая тень (XL) — двухслойная
252
+ /// @param {String} $color [shadow] - Имя цвета
253
+ /// @require {mixin} box-shadow-layers
254
+ /// @example scss
255
+ /// .dropdown {
256
+ /// @include shadow-xl;
257
+ /// }
258
+ /// @group mixins-shadow
259
+ /// @access public
260
+ @mixin shadow-xl($color: shadow) {
261
+ @include box-shadow-layers($color, ((0, 20px, 25px, -5px, 0.1), (0, 8px, 10px, -6px, 0.1)));
262
+ }
263
+
264
+ // ============================================
265
+ // OUTLINE / FOCUS RING
266
+ // ============================================
267
+
268
+ /// Outline с fallback
269
+ /// @param {String} $var-name - Имя цвета из палитры
270
+ /// @param {Number} $alpha [1] - Прозрачность
271
+ /// @param {Number} $width [2px] - Толщина
272
+ /// @param {String} $style [solid] - Стиль
273
+ /// @require {function} colors.color-static
274
+ /// @require {function} colors.color-var
275
+ /// @example scss
276
+ /// .input:focus {
277
+ /// @include outline-hsl('ring', 0.5);
278
+ /// }
279
+ /// @output outline с fallback
280
+ /// @group mixins-focus
281
+ /// @access public
282
+ @mixin outline-hsl($var-name, $alpha: 1, $width: 2px, $style: solid) {
283
+ outline: $width $style colors.color-static($var-name, $alpha);
284
+ outline: $width $style colors.color-var($var-name, $alpha);
285
+ }
286
+
287
+ /// Focus ring — современный подход для фокуса
288
+ /// @param {String} $color [ring] - Имя цвета
289
+ /// @param {Number} $width [2px] - Толщина кольца
290
+ /// @param {Number} $offset [-2px] - Отступ от элемента
291
+ /// @require {function} colors.color-static
292
+ /// @require {function} colors.color-var
293
+ /// @example scss - На кнопке
294
+ /// .button:focus-visible {
295
+ /// @include focus-ring;
296
+ /// }
297
+ /// @example scss - Кастомный цвет
298
+ /// .link:focus-visible {
299
+ /// @include focus-ring('accent', 2px, 2px);
300
+ /// }
301
+ /// @output outline + outline-offset
302
+ /// @group mixins-focus
303
+ /// @access public
304
+ @mixin focus-ring($color: ring, $width: 2px, $offset: -2px) {
305
+ outline: $width solid colors.color-static($color, 0.5);
306
+ outline: $width solid colors.color-var($color, 0.5);
307
+ outline-offset: $offset;
308
+ }
309
+
310
+ // ============================================
311
+ // TEXT-SHADOW
312
+ // ============================================
313
+
314
+ /// Text-shadow с fallback
315
+ /// @param {String} $var-name - Имя цвета из палитры
316
+ /// @param {Number} $alpha - Прозрачность
317
+ /// @param {Number} $x [0] - Смещение по X
318
+ /// @param {Number} $y [1px] - Смещение по Y
319
+ /// @param {Number} $blur [2px] - Радиус размытия
320
+ /// @require {function} colors.color-static
321
+ /// @require {function} colors.color-var
322
+ /// @example scss
323
+ /// .hero__title {
324
+ /// @include text-shadow-hsl('shadow', 0.3, 0, 2px, 4px);
325
+ /// }
326
+ /// @output text-shadow с fallback
327
+ /// @group mixins-shadow
328
+ /// @access public
329
+ @mixin text-shadow-hsl($var-name, $alpha, $x: 0, $y: 1px, $blur: 2px) {
330
+ text-shadow: $x $y $blur colors.color-static($var-name, $alpha);
331
+ text-shadow: $x $y $blur colors.color-var($var-name, $alpha);
332
+ }
333
+
334
+ // ============================================
335
+ // ГРАДИЕНТЫ
336
+ // ============================================
337
+
338
+ /// Линейный градиент с двумя цветами
339
+ /// @param {String} $direction - Направление (to right, to bottom, 45deg и т.д.)
340
+ /// @param {String} $color1 - Имя первого цвета
341
+ /// @param {String} $color2 - Имя второго цвета
342
+ /// @param {Number} $alpha1 [1] - Прозрачность первого цвета
343
+ /// @param {Number} $alpha2 [1] - Прозрачность второго цвета
344
+ /// @require {function} colors.color-static
345
+ /// @require {function} colors.color-var
346
+ /// @example scss
347
+ /// .banner {
348
+ /// @include gradient-linear(to right, 'primary', 'secondary');
349
+ /// }
350
+ /// @output background: linear-gradient с fallback
351
+ /// @group mixins-gradient
352
+ /// @access public
353
+ @mixin gradient-linear($direction, $color1, $color2, $alpha1: 1, $alpha2: 1) {
354
+ // Fallback
355
+ background: linear-gradient($direction, colors.color-static($color1, $alpha1), colors.color-static($color2, $alpha2));
356
+ // Modern
357
+ background: linear-gradient($direction, colors.color-var($color1, $alpha1), colors.color-var($color2, $alpha2));
358
+ }
359
+
360
+ /// Overlay градиент (для затемнения изображений)
361
+ /// @param {String} $color [overlay] - Имя цвета
362
+ /// @param {Number} $alpha-start [0.8] - Прозрачность начала
363
+ /// @param {Number} $alpha-end [0.4] - Прозрачность конца
364
+ /// @require {mixin} gradient-linear
365
+ /// @example scss
366
+ /// .hero::before {
367
+ /// @include gradient-overlay('overlay', 0.9, 0.5);
368
+ /// }
369
+ /// @group mixins-gradient
370
+ /// @access public
371
+ @mixin gradient-overlay($color: overlay, $alpha-start: 0.8, $alpha-end: 0.4) {
372
+ @include gradient-linear(to bottom, $color, $color, $alpha-start, $alpha-end);
373
+ }
374
+
375
+ /// Линейный градиент с 3 точками (from/via/to)
376
+ /// @param {String} $direction - Направление (to right, to bottom, 45deg и т.д.)
377
+ /// @param {String} $color - Имя цвета
378
+ /// @param {Number} $alpha-from [1] - Прозрачность начала
379
+ /// @param {Number} $alpha-via [0.5] - Прозрачность середины
380
+ /// @param {Number} $alpha-to [0] - Прозрачность конца
381
+ /// @require {function} colors.color-static
382
+ /// @require {function} colors.color-var
383
+ /// @example scss - Fade эффект
384
+ /// .fade-right {
385
+ /// @include gradient-fade(to right, 'background', 1, 0.5, 0);
386
+ /// }
387
+ /// @output background: linear-gradient с 3 точками и fallback
388
+ /// @group mixins-gradient
389
+ /// @access public
390
+ @mixin gradient-fade($direction, $color, $alpha-from: 1, $alpha-via: 0.5, $alpha-to: 0) {
391
+ // Fallback
392
+ background: linear-gradient($direction, colors.color-static($color, $alpha-from), colors.color-static($color, $alpha-via), colors.color-static($color, $alpha-to));
393
+
394
+ // Modern
395
+ background: linear-gradient($direction, colors.color-var($color, $alpha-from), colors.color-var($color, $alpha-via), colors.color-var($color, $alpha-to));
396
+ }
397
+
398
+ /// Универсальный градиент с любым количеством точек
399
+ /// @param {String} $direction - Направление
400
+ /// @param {List} $stops - Список точек: ((color, alpha, position?), ...)
401
+ /// @require {function} colors.color-static
402
+ /// @require {function} colors.color-var
403
+ /// @example scss - Сложный градиент
404
+ /// .rainbow {
405
+ /// @include gradient-multi(to right, (
406
+ /// ('primary', 1, 0%),
407
+ /// ('accent', 0.8, 50%),
408
+ /// ('secondary', 1, 100%)
409
+ /// ));
410
+ /// }
411
+ /// @output background: linear-gradient с несколькими точками
412
+ /// @group mixins-gradient
413
+ /// @access public
414
+ @mixin gradient-multi($direction, $stops) {
415
+ $fallback-stops: ();
416
+ $modern-stops: ();
417
+
418
+ @each $stop in $stops {
419
+ $color: list.nth($stop, 1);
420
+ $alpha: 1;
421
+ @if list.length($stop) >= 2 {
422
+ $alpha: list.nth($stop, 2);
423
+ }
424
+ $position: meta.if(length($stop) >= 3, list.nth($stop, 3), null);
425
+
426
+ $fallback-stop: colors.color-static($color, $alpha);
427
+ $modern-stop: colors.color-var($color, $alpha);
428
+
429
+ @if $position {
430
+ $fallback-stop: $fallback-stop $position;
431
+ $modern-stop: $modern-stop $position;
432
+ }
433
+
434
+ $fallback-stops: list.append($fallback-stops, $fallback-stop, comma);
435
+ $modern-stops: list.append($modern-stops, $modern-stop, comma);
436
+ }
437
+
438
+ background: linear-gradient($direction, $fallback-stops);
439
+ background: linear-gradient($direction, $modern-stops);
440
+ }
441
+
442
+ /// Fade overlay справа (для изображений)
443
+ /// @param {String} $color [primary] - Имя цвета
444
+ /// @require {mixin} gradient-fade
445
+ /// @example scss
446
+ /// .hero::after {
447
+ /// @include gradient-overlay-right;
448
+ /// }
449
+ /// @group mixins-gradient
450
+ /// @access public
451
+ @mixin gradient-overlay-right($color: primary) {
452
+ @include gradient-fade(to right, $color, 0.9, 0.7, 0.4);
453
+ }
454
+
455
+ /// Fade overlay снизу (для изображений)
456
+ /// @param {String} $color [overlay] - Имя цвета
457
+ /// @require {mixin} gradient-fade
458
+ /// @example scss
459
+ /// .card__image::after {
460
+ /// @include gradient-overlay-bottom;
461
+ /// }
462
+ /// @group mixins-gradient
463
+ /// @access public
464
+ @mixin gradient-overlay-bottom($color: overlay) {
465
+ @include gradient-fade(to bottom, $color, 0.8, 0.5, 0.2);
466
+ }
467
+
468
+ /// Hero gradient — диагональный fade
469
+ /// @param {String} $color [primary] - Имя цвета
470
+ /// @require {mixin} gradient-fade
471
+ /// @example scss
472
+ /// .hero::before {
473
+ /// @include gradient-hero;
474
+ /// }
475
+ /// @group mixins-gradient
476
+ /// @access public
477
+ @mixin gradient-hero($color: primary) {
478
+ @include gradient-fade(135deg, $color, 0.95, 0.7, 0.4);
479
+ }
480
+
481
+ // ============================================
482
+ // FLEX GAP FALLBACK
483
+ // ============================================
484
+
485
+ /// Flex gap с fallback через margin для старых браузеров
486
+ /// @param {Number | List} $gap - Одно значение или (row-gap, column-gap)
487
+ /// @param {String} $direction [both] - Направление: 'row', 'column', 'both'
488
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов
489
+ /// @example scss - Одинаковый gap
490
+ /// .flex-container {
491
+ /// display: flex;
492
+ /// @include flex-gap(1rem);
493
+ /// }
494
+ /// @example scss - Разный gap
495
+ /// .flex-container {
496
+ /// display: flex;
497
+ /// @include flex-gap((1rem, 2rem)); // row: 1rem, column: 2rem
498
+ /// }
499
+ /// @example scss - Только горизонтальный
500
+ /// .row {
501
+ /// display: flex;
502
+ /// @include flex-gap(1rem, 'row');
503
+ /// }
504
+ /// @output margin fallback + @supports (gap) modern
505
+ /// @group mixins-layout
506
+ /// @access public
507
+ @mixin flex-gap($gap, $direction: both, $selector: "> *") {
508
+ // Нормализация gap в row и column
509
+ $row-gap: $gap;
510
+ $col-gap: $gap;
511
+
512
+ @if meta.type-of($gap) == list {
513
+ $row-gap: list.nth($gap, 1);
514
+ $col-gap: list.nth($gap, 1);
515
+ @if list.length($gap) >= 2 {
516
+ $col-gap: list.nth($gap, 2);
517
+ }
518
+ }
519
+
520
+ // Fallback через margin
521
+ #{$selector} {
522
+ @if $direction == both {
523
+ margin: math.div($row-gap, 2) math.div($col-gap, 2);
524
+
525
+ // Убираем внешние margin
526
+ &:first-child {
527
+ margin-left: 0;
528
+ }
529
+ &:last-child {
530
+ margin-right: 0;
531
+ }
532
+ } @else if $direction == row {
533
+ margin-left: math.div($col-gap, 2);
534
+ margin-right: math.div($col-gap, 2);
535
+
536
+ &:first-child {
537
+ margin-left: 0;
538
+ }
539
+ &:last-child {
540
+ margin-right: 0;
541
+ }
542
+ } @else if $direction == column {
543
+ margin-top: math.div($row-gap, 2);
544
+ margin-bottom: math.div($row-gap, 2);
545
+
546
+ &:first-child {
547
+ margin-top: 0;
548
+ }
549
+ &:last-child {
550
+ margin-bottom: 0;
551
+ }
552
+ }
553
+ }
554
+
555
+ // Modern: native gap (если поддерживается)
556
+ @supports (gap: 1px) {
557
+ #{$selector} {
558
+ margin: 0;
559
+ }
560
+
561
+ @if $direction == both {
562
+ gap: $row-gap $col-gap;
563
+ } @else if $direction == row {
564
+ column-gap: $col-gap;
565
+ } @else if $direction == column {
566
+ row-gap: $row-gap;
567
+ }
568
+ }
569
+ }
570
+
571
+ /// Flex gap с поддержкой wrap
572
+ /// Использует отрицательный margin на контейнере для компенсации
573
+ /// @param {Number | List} $gap - Одно значение или (row-gap, column-gap)
574
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов
575
+ /// @example scss
576
+ /// .tags {
577
+ /// display: flex;
578
+ /// flex-wrap: wrap;
579
+ /// @include flex-gap-wrap(0.5rem);
580
+ /// }
581
+ /// @output Отрицательный margin + margin детей + @supports modern
582
+ /// @group mixins-layout
583
+ /// @access public
584
+ @mixin flex-gap-wrap($gap, $selector: "> *") {
585
+ $row-gap: $gap;
586
+ $col-gap: $gap;
587
+
588
+ @if meta.type-of($gap) == list {
589
+ $row-gap: list.nth($gap, 1);
590
+ $col-gap: $row-gap;
591
+ @if list.length($gap) >= 2 {
592
+ $col-gap: list.nth($gap, 2);
593
+ }
594
+ }
595
+
596
+ $row-offset: math.div($row-gap, 2);
597
+ $col-offset: math.div($col-gap, 2);
598
+
599
+ // Отрицательный margin компенсирует margin детей
600
+ margin: -#{$row-offset} -#{$col-offset};
601
+
602
+ #{$selector} {
603
+ margin: $row-offset $col-offset;
604
+ }
605
+
606
+ // Modern
607
+ @supports (gap: 1px) {
608
+ margin: 0;
609
+ gap: $row-gap $col-gap;
610
+
611
+ #{$selector} {
612
+ margin: 0;
613
+ }
614
+ }
615
+ }
616
+
617
+ // ============================================
618
+ // УТИЛИТЫ
619
+ // ============================================
620
+
621
+ /// Визуально скрытый элемент, но доступный для screen readers
622
+ /// @example scss
623
+ /// .skip-link {
624
+ /// @include visually-hidden;
625
+ ///
626
+ /// &:focus {
627
+ /// // показать при фокусе
628
+ /// }
629
+ /// }
630
+ /// @output Абсолютное позиционирование 1x1px с clip
631
+ /// @group mixins-a11y
632
+ /// @access public
633
+ @mixin visually-hidden {
634
+ position: absolute;
635
+ width: 1px;
636
+ height: 1px;
637
+ padding: 0;
638
+ margin: -1px;
639
+ overflow: hidden;
640
+ clip: rect(0, 0, 0, 0);
641
+ white-space: nowrap;
642
+ border: 0;
643
+ }
644
+
645
+ /// Сброс стилей кнопки
646
+ /// @example scss
647
+ /// .custom-button {
648
+ /// @include button-reset;
649
+ /// // кастомные стили
650
+ /// }
651
+ /// @output Сброс appearance, background, border, padding, margin
652
+ /// @group mixins-reset
653
+ /// @access public
654
+ @mixin button-reset {
655
+ appearance: none;
656
+ background: none;
657
+ border: none;
658
+ padding: 0;
659
+ margin: 0;
660
+ font: inherit;
661
+ color: inherit;
662
+ cursor: pointer;
663
+
664
+ &:focus {
665
+ outline: none;
666
+ }
667
+ }
668
+
669
+ /// Обрезка текста с многоточием
670
+ /// @param {Number} $lines [1] - Количество строк (1 = однострочная, >1 = многострочная)
671
+ /// @example scss - Одна строка
672
+ /// .title {
673
+ /// @include text-truncate;
674
+ /// }
675
+ /// @example scss - Три строки
676
+ /// .description {
677
+ /// @include text-truncate(3);
678
+ /// }
679
+ /// @output overflow: hidden + text-overflow: ellipsis (или -webkit-line-clamp)
680
+ /// @group mixins-text
681
+ /// @access public
682
+ @mixin text-truncate($lines: 1) {
683
+ @if $lines == 1 {
684
+ overflow: hidden;
685
+ text-overflow: ellipsis;
686
+ white-space: nowrap;
687
+ } @else {
688
+ display: -webkit-box;
689
+ -webkit-line-clamp: $lines;
690
+ -webkit-box-orient: vertical;
691
+ overflow: hidden;
692
+ }
693
+ }
694
+
695
+ /// Background image с WebP и fallback
696
+ /// Использует image-set для современных браузеров
697
+ /// @param {String} $path - Путь к изображению без расширения
698
+ /// @param {String} $ext [jpg] - Расширение fallback-изображения
699
+ /// @param {Boolean} $fallback-is-first [true] - (не используется, для совместимости)
700
+ /// @example scss
701
+ /// .hero {
702
+ /// @include bg-webp('/images/hero', 'jpg');
703
+ /// }
704
+ /// @output background-image с fallback и image-set
705
+ /// @group mixins-image
706
+ /// @access public
707
+ @mixin bg-webp($path, $ext: "jpg", $fallback-is-first: true) {
708
+ // 1. Стандартный Fallback для совсем старых браузеров (IE11 и старые Safari),
709
+ // которые не понимают image-set. Они просто проигнорируют следующие строки.
710
+ background-image: url("#{$path}.#{$ext}");
711
+
712
+ // 2. Современное решение
713
+ // Синтаксис image-set позволяет указать варианты и их типы
714
+ $mime-ext: $ext;
715
+ @if $ext == "jpg" {
716
+ $mime-ext: "jpeg";
717
+ }
718
+ background-image: image-set(url("#{$path}.webp") type("image/webp"), url("#{$path}.#{$ext}") type("image/#{$mime-ext}"));
719
+ }
720
+
721
+ // ─────────────────────────────────────────
722
+ // BACKGROUND IMAGE (через <img> в HTML)
723
+ // ─────────────────────────────────────────
724
+
725
+ /// Контейнер с фоновым изображением
726
+ /// Применяется к секции (hero, banner)
727
+ /// @example scss
728
+ /// .hero {
729
+ /// @include bg-image-container;
730
+ /// }
731
+ /// @output position: relative + overflow: hidden
732
+ /// @group mixins-image
733
+ /// @access public
734
+ @mixin bg-image-container {
735
+ position: relative;
736
+ overflow: hidden;
737
+ }
738
+
739
+ /// Обёртка изображения (picture или div)
740
+ /// Абсолютно позиционируется на весь контейнер
741
+ /// @param {Number} $z-index [0] - z-index обёртки
742
+ /// @example scss
743
+ /// .hero__bg {
744
+ /// @include bg-image-wrap(-1);
745
+ /// }
746
+ /// @output position: absolute + inset: 0
747
+ /// @group mixins-image
748
+ /// @access public
749
+ @mixin bg-image-wrap($z-index: 0) {
750
+ position: absolute;
751
+ inset: 0;
752
+ z-index: $z-index;
753
+ }
754
+
755
+ /// Само изображение — заполняет контейнер
756
+ /// С fallback для IE11 (translate) и modern (object-fit)
757
+ /// @param {String} $position [center] - object-position
758
+ /// @param {String} $fit [cover] - object-fit
759
+ /// @example scss
760
+ /// .hero__image {
761
+ /// @include bg-image(center, cover);
762
+ /// }
763
+ /// @example scss - Верх изображения
764
+ /// .portrait {
765
+ /// @include bg-image(top, cover);
766
+ /// }
767
+ /// @output object-fit + object-position с fallback для IE11
768
+ /// @group mixins-image
769
+ /// @access public
770
+ @mixin bg-image($position: center, $fit: cover) {
771
+ // FALLBACK: для IE11
772
+ position: absolute;
773
+ top: 50%;
774
+ left: 50%;
775
+ transform: translate(-50%, -50%);
776
+ min-width: 100%;
777
+ min-height: 100%;
778
+ width: auto;
779
+ height: auto;
780
+
781
+ // MODERN: object-fit
782
+ @supports (object-fit: #{$fit}) {
783
+ position: static;
784
+ transform: none;
785
+ width: 100%;
786
+ height: 100%;
787
+ object-fit: $fit;
788
+ object-position: $position;
789
+ }
790
+ }
791
+
792
+ /// Комбинированный миксин для picture элемента с img внутри
793
+ /// @param {Number} $z-index [0] - z-index
794
+ /// @param {String} $position [center] - object-position
795
+ /// @require {mixin} bg-image-wrap
796
+ /// @require {mixin} bg-image
797
+ /// @example scss
798
+ /// .hero__picture {
799
+ /// @include bg-picture(-1, center);
800
+ /// }
801
+ /// @example html
802
+ /// <picture class="hero__picture">
803
+ /// <source srcset="image.webp" type="image/webp">
804
+ /// <img src="image.jpg" alt="">
805
+ /// </picture>
806
+ /// @output Обёртка + стили для img внутри
807
+ /// @group mixins-image
808
+ /// @access public
809
+ @mixin bg-picture($z-index: 0, $position: center) {
810
+ @include bg-image-wrap($z-index);
811
+
812
+ img {
813
+ @include bg-image($position);
814
+ }
815
+ }
816
+
817
+ // ─────────────────────────────────────────
818
+ // FULL-BLEED SECTION (фон на всю ширину, контент в container)
819
+ // ─────────────────────────────────────────
820
+
821
+ /// Секция с фоном на всю ширину
822
+ /// Применяется к внешней обёртке
823
+ /// @example scss
824
+ /// .about {
825
+ /// @include section-full-bleed;
826
+ /// }
827
+ /// @output width: 100%
828
+ /// @group mixins-layout
829
+ /// @access public
830
+ @mixin section-full-bleed {
831
+ width: 100%;
832
+ }
833
+
834
+ /// Внутренний контейнер секции
835
+ /// Применяется к .container внутри
836
+ /// @require {mixin} container
837
+ /// @example scss
838
+ /// .about__container {
839
+ /// @include section-contained;
840
+ /// }
841
+ /// @output @include container
842
+ /// @group mixins-layout
843
+ /// @access public
844
+ @mixin section-contained {
845
+ @include container;
846
+ }
847
+
848
+ /// Контейнер с ограниченной шириной
849
+ /// Используется для центрирования контента
850
+ /// @output width, max-width, margin-inline, padding-inline
851
+ /// @group mixins-layout
852
+ /// @access public
853
+ @mixin container {
854
+ width: 100%;
855
+ max-width: breakpoints.$breakpoint-xxxxl;
856
+ margin-inline: auto;
857
+ padding-inline: 1rem;
858
+ }
859
+
860
+ /// Контейнер с шириной от края до края
861
+ /// Используется растягивания
862
+ /// @output margin
863
+ /// @group mixins-layout
864
+ /// @access public
865
+ @mixin container-fluid {
866
+ margin: 0 auto;
867
+ }
868
+
869
+ // ─────────────────────────────────────────
870
+ // FLEX SHORTCUTS
871
+ // ─────────────────────────────────────────
872
+
873
+ /// Flex row с gap и fallback
874
+ /// Горизонтальный flex-контейнер
875
+ /// @param {Number | List} $gap [0] - Gap или (row-gap, column-gap)
876
+ /// @param {String} $justify [flex-start] - justify-content значение
877
+ /// @param {String} $align [stretch] - align-items значение
878
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
879
+ /// @require {mixin} flex-gap
880
+ /// @example scss - Простой список
881
+ /// .nav__list {
882
+ /// @include flex-row($gap-sm);
883
+ /// }
884
+ /// @example scss - С выравниванием
885
+ /// .card__actions {
886
+ /// @include flex-row($gap-md, flex-end, center);
887
+ /// }
888
+ /// @example scss - С кастомным селектором
889
+ /// .header__nav {
890
+ /// @include flex-row($gap-lg, flex-start, center, ".nav__item");
891
+ /// }
892
+ /// @output display: flex + flex-direction: row + gap с fallback
893
+ /// @group mixins-flex
894
+ /// @access public
895
+ @mixin flex-row($gap: 0, $justify: flex-start, $align: stretch, $selector: "> *") {
896
+ display: flex;
897
+ flex-direction: row;
898
+ justify-content: $justify;
899
+ align-items: $align;
900
+
901
+ @if $gap != 0 {
902
+ @include flex-gap($gap, row, $selector);
903
+ }
904
+ }
905
+
906
+ /// Flex column с gap и fallback
907
+ /// Вертикальный flex-контейнер (стек)
908
+ /// @param {Number | List} $gap [0] - Gap или (row-gap, column-gap)
909
+ /// @param {String} $justify [flex-start] - justify-content значение
910
+ /// @param {String} $align [stretch] - align-items значение
911
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
912
+ /// @require {mixin} flex-gap
913
+ /// @example scss - Вертикальный стек
914
+ /// .card__content {
915
+ /// @include flex-column($gap-sm);
916
+ /// }
917
+ /// @example scss - Центрированный стек
918
+ /// .hero__content {
919
+ /// @include flex-column($gap-lg, center, center);
920
+ /// }
921
+ /// @example scss - Растянутый на всю высоту
922
+ /// .sidebar {
923
+ /// @include flex-column($gap-md, space-between);
924
+ /// }
925
+ /// @output display: flex + flex-direction: column + gap с fallback
926
+ /// @group mixins-flex
927
+ /// @access public
928
+ @mixin flex-column($gap: 0, $justify: flex-start, $align: stretch, $selector: "> *") {
929
+ display: flex;
930
+ flex-direction: column;
931
+ justify-content: $justify;
932
+ align-items: $align;
933
+
934
+ @if $gap != 0 {
935
+ @include flex-gap($gap, column, $selector);
936
+ }
937
+ }
938
+
939
+ /// Flex space-between с gap
940
+ /// Элементы распределены по краям с промежутками между
941
+ /// @param {Number | List} $gap [0] - Gap между элементами
942
+ /// @param {String} $align [center] - align-items значение
943
+ /// @param {Boolean} $wrap [false] - Включить flex-wrap
944
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
945
+ /// @require {mixin} flex-gap
946
+ /// @require {mixin} flex-gap-wrap
947
+ /// @example scss - Без wrap
948
+ /// .header__wrapper {
949
+ /// @include flex-between($gap-md);
950
+ /// }
951
+ /// @example scss - С wrap
952
+ /// .footer__links {
953
+ /// @include flex-between($gap-md, center, true);
954
+ /// }
955
+ /// @example scss - С кастомным селектором
956
+ /// .toolbar {
957
+ /// @include flex-between($gap-lg, center, true, ".toolbar__item");
958
+ /// }
959
+ /// @output display: flex + justify-content: space-between + gap с fallback
960
+ /// @group mixins-flex
961
+ /// @access public
962
+ @mixin flex-between($gap: 0, $align: center, $wrap: false, $selector: "> *") {
963
+ display: flex;
964
+ justify-content: space-between;
965
+ align-items: $align;
966
+
967
+ @if $wrap {
968
+ flex-wrap: wrap;
969
+
970
+ @if $gap != 0 {
971
+ @include flex-gap-wrap($gap, $selector);
972
+ }
973
+ } @else {
974
+ flex-wrap: nowrap;
975
+ @if $gap != 0 {
976
+ @include flex-gap($gap, row, $selector);
977
+ }
978
+ }
979
+ }
980
+
981
+ /// Flex space-around с gap
982
+ /// Элементы распределены с равными промежутками вокруг
983
+ /// @param {Number | List} $gap [0] - Gap между элементами
984
+ /// @param {String} $align [center] - align-items значение
985
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
986
+ /// @require {mixin} flex-gap
987
+ /// @example scss
988
+ /// .features__list {
989
+ /// @include flex-around($gap-md);
990
+ /// }
991
+ /// @output display: flex + justify-content: space-around + gap с fallback
992
+ /// @group mixins-flex
993
+ /// @access public
994
+ @mixin flex-around($gap: 0, $align: center, $selector: "> *") {
995
+ display: flex;
996
+ justify-content: space-around;
997
+ align-items: $align;
998
+
999
+ @if $gap != 0 {
1000
+ @include flex-gap($gap, row, $selector);
1001
+ }
1002
+ }
1003
+
1004
+ /// Flex center — центрирование по обеим осям
1005
+ /// Идеально для центрирования иконок, модалок, лоадеров
1006
+ /// @param {Number | List} $gap [0] - Gap между элементами (если несколько)
1007
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
1008
+ /// @require {mixin} flex-gap
1009
+ /// @example scss - Центрирование иконки
1010
+ /// .btn__icon-wrapper {
1011
+ /// @include flex-center;
1012
+ /// }
1013
+ /// @example scss - Группа элементов по центру
1014
+ /// .social-links {
1015
+ /// @include flex-center($gap-sm);
1016
+ /// }
1017
+ /// @example scss - Модальное окно
1018
+ /// .modal-overlay {
1019
+ /// @include flex-center;
1020
+ /// }
1021
+ /// @output display: flex + justify-content: center + align-items: center
1022
+ /// @group mixins-flex
1023
+ /// @access public
1024
+ @mixin flex-center($gap: 0, $selector: "> *") {
1025
+ display: flex;
1026
+ justify-content: center;
1027
+ align-items: center;
1028
+
1029
+ @if $gap != 0 {
1030
+ @include flex-gap($gap, both, $selector);
1031
+ }
1032
+ }
1033
+
1034
+ /// Flex wrap с gap и fallback
1035
+ /// Для элементов, которые переносятся на новую строку (теги, галереи)
1036
+ /// @param {Number | List} $gap [0] - Gap или (row-gap, column-gap)
1037
+ /// @param {String} $justify [flex-start] - justify-content значение
1038
+ /// @param {String} $align [stretch] - align-items значение
1039
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
1040
+ /// @require {mixin} flex-gap-wrap
1041
+ /// @example scss - Теги
1042
+ /// .tags {
1043
+ /// @include flex-wrap($gap-xs);
1044
+ /// }
1045
+ /// @example scss - Галерея
1046
+ /// .gallery {
1047
+ /// @include flex-wrap($gap-sm, center);
1048
+ /// }
1049
+ /// @example scss - Разный gap по осям
1050
+ /// .grid-fallback {
1051
+ /// @include flex-wrap(($gap-lg, $gap-md));
1052
+ /// }
1053
+ /// @output display: flex + flex-wrap: wrap + gap с fallback через margin
1054
+ /// @group mixins-flex
1055
+ /// @access public
1056
+ @mixin flex-wrap($gap: 0, $justify: flex-start, $align: stretch, $selector: "> *") {
1057
+ display: flex;
1058
+ flex-wrap: wrap;
1059
+ justify-content: $justify;
1060
+ align-items: $align;
1061
+
1062
+ @if $gap != 0 {
1063
+ @include flex-gap-wrap($gap, $selector);
1064
+ }
1065
+ }
1066
+
1067
+ /// Flex end — выравнивание к концу
1068
+ /// Для кнопок действий, pagination и т.п.
1069
+ /// @param {Number | List} $gap [0] - Gap между элементами
1070
+ /// @param {String} $align [center] - align-items значение
1071
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
1072
+ /// @require {mixin} flex-gap
1073
+ /// @example scss - Кнопки формы
1074
+ /// .form__actions {
1075
+ /// @include flex-end($gap-sm);
1076
+ /// }
1077
+ /// @example scss - Pagination
1078
+ /// .pagination {
1079
+ /// @include flex-end($gap-xs);
1080
+ /// }
1081
+ /// @output display: flex + justify-content: flex-end + gap с fallback
1082
+ /// @group mixins-flex
1083
+ /// @access public
1084
+ @mixin flex-end($gap: 0, $align: center, $selector: "> *") {
1085
+ display: flex;
1086
+ justify-content: flex-end;
1087
+ align-items: $align;
1088
+
1089
+ @if $gap != 0 {
1090
+ @include flex-gap($gap, row, $selector);
1091
+ }
1092
+ }
1093
+
1094
+ /// Flex start — выравнивание к началу (явное)
1095
+ /// @param {Number | List} $gap [0] - Gap между элементами
1096
+ /// @param {String} $align [center] - align-items значение
1097
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
1098
+ /// @require {mixin} flex-gap
1099
+ /// @example scss
1100
+ /// .breadcrumbs {
1101
+ /// @include flex-start($gap-xs);
1102
+ /// }
1103
+ /// @output display: flex + justify-content: flex-start + gap с fallback
1104
+ /// @group mixins-flex
1105
+ /// @access public
1106
+ @mixin flex-start($gap: 0, $align: center, $selector: "> *") {
1107
+ display: flex;
1108
+ justify-content: flex-start;
1109
+ align-items: $align;
1110
+
1111
+ @if $gap != 0 {
1112
+ @include flex-gap($gap, row, $selector);
1113
+ }
1114
+ }
1115
+
1116
+ /// Inline flex с gap
1117
+ /// Для инлайновых flex-контейнеров
1118
+ /// @param {Number | List} $gap [0] - Gap между элементами
1119
+ /// @param {String} $justify [flex-start] - justify-content значение
1120
+ /// @param {String} $align [center] - align-items значение
1121
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов для fallback
1122
+ /// @require {mixin} flex-gap
1123
+ /// @example scss - Иконка с текстом
1124
+ /// .link-with-icon {
1125
+ /// @include inline-flex($gap-xs);
1126
+ /// }
1127
+ /// @example scss - Badge
1128
+ /// .badge {
1129
+ /// @include inline-flex($gap-xs, center, center);
1130
+ /// }
1131
+ /// @output display: inline-flex + gap с fallback
1132
+ /// @group mixins-flex
1133
+ /// @access public
1134
+ @mixin inline-flex($gap: 0, $justify: flex-start, $align: center, $selector: "> *") {
1135
+ display: inline-flex;
1136
+ justify-content: $justify;
1137
+ align-items: $align;
1138
+
1139
+ @if $gap != 0 {
1140
+ @include flex-gap($gap, row, $selector);
1141
+ }
1142
+ }