@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,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
|
+
}
|