@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,816 @@
1
+ ////
2
+ /// Система сетки темы MOEXP
3
+ /// CSS Grid с Flexbox fallback для старых браузеров
4
+ /// @group grid
5
+ /// @author MOEXP Team
6
+ ////
7
+
8
+ // ============================================
9
+ // GRID SYSTEM WITH FLEX FALLBACK (FIXED v3)
10
+ // local\common\theme-moexp\src\scss\abstracts\_grid.scss
11
+ // ============================================
12
+
13
+ @use "sass:math";
14
+ @use "sass:map";
15
+ @use "sass:list";
16
+ @use "sass:meta";
17
+ @use "spacing";
18
+ @use "breakpoints";
19
+
20
+ // ─────────────────────────────────────────
21
+ // ПЕРЕМЕННЫЕ
22
+ // ─────────────────────────────────────────
23
+
24
+ /// Количество колонок в сетке по умолчанию
25
+ /// @type Number
26
+ /// @group grid-variables
27
+ $grid-columns: 12 !default;
28
+
29
+ /// Gap по умолчанию (1rem / 16px)
30
+ /// @type Number
31
+ /// @group grid-variables
32
+ $grid-gap-default: spacing.$gap-md !default;
33
+
34
+ // ─────────────────────────────────────────
35
+ // ВСПОМОГАТЕЛЬНАЯ ФУНКЦИЯ
36
+ // ─────────────────────────────────────────
37
+
38
+ /// Вычисляет ширину элемента для flex fallback
39
+ /// @param {Number} $columns - Количество колонок
40
+ /// @param {Number} $gap - Gap между элементами
41
+ /// @return {String} calc() значение ширины
42
+ /// @example scss
43
+ /// width: _flex-width(3, 1rem);
44
+ /// // => calc(33.3333% - 1rem)
45
+ /// @group grid-functions
46
+ /// @access private
47
+ @function _flex-width($columns, $gap) {
48
+ @if $columns == 1 {
49
+ @return 100%;
50
+ }
51
+ @return calc(#{math.percentage(math.div(1, $columns))} - #{$gap});
52
+ }
53
+
54
+ // ─────────────────────────────────────────
55
+ // GRID ITEM
56
+ // ─────────────────────────────────────────
57
+
58
+ /// Базовые стили для элемента сетки
59
+ /// Предотвращает overflow и проблемы с min-width
60
+ /// @example scss
61
+ /// .grid__item {
62
+ /// @include grid-item;
63
+ /// }
64
+ /// @output box-sizing, min-width, min-height
65
+ /// @group grid-item
66
+ /// @access public
67
+ @mixin grid-item {
68
+ box-sizing: border-box;
69
+ min-width: 0;
70
+ min-height: 0;
71
+ }
72
+
73
+ // ─────────────────────────────────────────
74
+ // БАЗОВЫЙ GRID КОНТЕЙНЕР
75
+ // ─────────────────────────────────────────
76
+
77
+ /// Базовый grid контейнер с фиксированным количеством колонок
78
+ /// Flexbox fallback для IE11 и старых браузеров
79
+ /// @param {Number} $columns [3] - Количество колонок
80
+ /// @param {Number} $gap [$grid-gap-default] - Gap между элементами
81
+ /// @param {String} $align [stretch] - align-items значение
82
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов
83
+ /// @param {Boolean} $test [false] - Отключить @supports для тестирования fallback
84
+ /// @require {function} _flex-width
85
+ /// @example scss - 4 колонки
86
+ /// .products {
87
+ /// @include grid-container(4, spacing.$gap-lg);
88
+ /// }
89
+ /// @example scss - С кастомным селектором
90
+ /// .cards {
91
+ /// @include grid-container(3, 1rem, stretch, ".card");
92
+ /// }
93
+ /// @output Flexbox fallback + CSS Grid через @supports
94
+ /// @group grid-container
95
+ /// @access public
96
+ @mixin grid-container($columns: 3, $gap: $grid-gap-default, $align: stretch, $selector: "> *", $test: false) {
97
+ $gap-half: math.div($gap, 2);
98
+
99
+ // FALLBACK: Flexbox
100
+ display: flex;
101
+ flex-wrap: wrap;
102
+ align-items: $align;
103
+ margin: -#{$gap-half};
104
+
105
+ #{$selector} {
106
+ box-sizing: border-box;
107
+ flex: 0 0 _flex-width($columns, $gap);
108
+ width: _flex-width($columns, $gap);
109
+ max-width: _flex-width($columns, $gap);
110
+ min-width: 0;
111
+ margin: $gap-half;
112
+ }
113
+
114
+ // MODERN: CSS Grid
115
+ @if $test != true {
116
+ @supports (display: grid) {
117
+ display: grid;
118
+ grid-template-columns: repeat($columns, 1fr);
119
+ gap: $gap;
120
+ margin: 0;
121
+
122
+ #{$selector} {
123
+ width: auto;
124
+ max-width: none;
125
+ margin: 0;
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ // ─────────────────────────────────────────
132
+ // GRID AUTO-FIT
133
+ // ─────────────────────────────────────────
134
+
135
+ /// Grid с auto-fit — колонки автоматически подстраиваются под ширину
136
+ /// @param {Number} $min-width [280px] - Минимальная ширина колонки
137
+ /// @param {Number} $gap [$grid-gap-default] - Gap между элементами
138
+ /// @param {Number} $max-columns [4] - Максимум колонок (для fallback)
139
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов
140
+ /// @param {Boolean} $test [false] - Отключить @supports для тестирования
141
+ /// @require {function} _flex-width
142
+ /// @require {mixin} breakpoints.lg
143
+ /// @example scss - Карточки минимум 300px
144
+ /// .cards-grid {
145
+ /// @include grid-auto-fit(300px, spacing.$gap-lg);
146
+ /// }
147
+ /// @example scss - Галерея с мелкими элементами
148
+ /// .gallery {
149
+ /// @include grid-auto-fit(150px, spacing.$gap-sm, 6);
150
+ /// }
151
+ /// @output Flex fallback + grid auto-fit через @supports
152
+ /// @group grid-container
153
+ /// @access public
154
+ @mixin grid-auto-fit($min-width: 280px, $gap: $grid-gap-default, $max-columns: 4, $selector: "> *", $test: false) {
155
+ $gap-half: math.div($gap, 2);
156
+
157
+ // FALLBACK
158
+ display: flex;
159
+ flex-wrap: wrap;
160
+ margin: -#{$gap-half};
161
+
162
+ #{$selector} {
163
+ box-sizing: border-box;
164
+ flex: 1 1 $min-width;
165
+ min-width: $min-width;
166
+ max-width: 100%;
167
+ margin: $gap-half;
168
+
169
+ @if $max-columns {
170
+ @include breakpoints.lg {
171
+ max-width: _flex-width($max-columns, $gap);
172
+ }
173
+ }
174
+ }
175
+
176
+ // MODERN
177
+ @if $test != true {
178
+ @supports (display: grid) {
179
+ display: grid;
180
+ grid-template-columns: repeat(auto-fit, minmax($min-width, 1fr));
181
+ gap: $gap;
182
+ margin: 0;
183
+
184
+ #{$selector} {
185
+ min-width: 0;
186
+ max-width: none;
187
+ margin: 0;
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ // ─────────────────────────────────────────
194
+ // РЕСПОНСИВНЫЙ GRID
195
+ // ─────────────────────────────────────────
196
+
197
+ /// Респонсивный grid с разным количеством колонок на брейкпоинтах
198
+ /// @param {Map} $breakpoint-columns - Карта (breakpoint: columns)
199
+ /// @param {Number} $gap [$grid-gap-default] - Gap между элементами
200
+ /// @param {String} $selector ["> *"] - Селектор дочерних элементов
201
+ /// @param {Boolean} $test [false] - Отключить @supports для тестирования
202
+ /// @require {function} _flex-width
203
+ /// @require {mixin} breakpoints.media
204
+ /// @example scss - 1→2→3 колонки
205
+ /// .services {
206
+ /// @include grid-responsive((
207
+ /// xs: 1,
208
+ /// md: 2,
209
+ /// lg: 3
210
+ /// ));
211
+ /// }
212
+ /// @example scss - Сложная раскладка
213
+ /// .products {
214
+ /// @include grid-responsive((
215
+ /// xs: 1,
216
+ /// sm: 2,
217
+ /// lg: 3,
218
+ /// xl: 4,
219
+ /// xxl: 5
220
+ /// ), spacing.$gap-lg);
221
+ /// }
222
+ /// @output Flex fallback с медиа-запросами + CSS Grid через @supports
223
+ /// @group grid-responsive
224
+ /// @access public
225
+ @mixin grid-responsive($breakpoint-columns, $gap: $grid-gap-default, $selector: "> *", $test: false) {
226
+ $gap-half: math.div($gap, 2);
227
+
228
+ // ═══════════════════════════════════════
229
+ // FALLBACK: Flexbox (весь блок)
230
+ // ═══════════════════════════════════════
231
+ display: flex;
232
+ flex-wrap: wrap;
233
+ margin: -#{$gap-half};
234
+
235
+ #{$selector} {
236
+ box-sizing: border-box;
237
+ min-width: 0;
238
+ margin: $gap-half;
239
+ }
240
+
241
+ // Flex: респонсивные колонки
242
+ @each $bp, $cols in $breakpoint-columns {
243
+ @if $bp == xs or $bp == base {
244
+ #{$selector} {
245
+ flex: 0 0 _flex-width($cols, $gap);
246
+ width: _flex-width($cols, $gap);
247
+ max-width: _flex-width($cols, $gap);
248
+ }
249
+ } @else {
250
+ @include breakpoints.media($bp) {
251
+ #{$selector} {
252
+ flex: 0 0 _flex-width($cols, $gap);
253
+ width: _flex-width($cols, $gap);
254
+ max-width: _flex-width($cols, $gap);
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ // ═══════════════════════════════════════
261
+ // MODERN: CSS Grid (внутри @supports!)
262
+ // ═══════════════════════════════════════
263
+ @if $test != true {
264
+ @supports (display: grid) {
265
+ display: grid;
266
+ gap: $gap;
267
+ margin: 0;
268
+
269
+ // Базовые стили элементов — сбрасываем flex!
270
+ #{$selector} {
271
+ width: auto;
272
+ max-width: none;
273
+ margin: 0;
274
+ }
275
+
276
+ // Grid: респонсивные колонки
277
+ @each $bp, $cols in $breakpoint-columns {
278
+ @if $bp == xs or $bp == base {
279
+ grid-template-columns: repeat($cols, 1fr);
280
+ } @else {
281
+ @include breakpoints.media($bp) {
282
+ grid-template-columns: repeat($cols, 1fr);
283
+
284
+ // КЛЮЧЕВОЙ МОМЕНТ: сбрасываем flex стили ВНУТРИ каждого медиа-запроса!
285
+ #{$selector} {
286
+ width: auto;
287
+ max-width: none;
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ // ─────────────────────────────────────────
297
+ // ГОТОВЫЕ ПРЕСЕТЫ
298
+ // ─────────────────────────────────────────
299
+
300
+ /// Пресет: 2 колонки (1→2)
301
+ /// @param {Number} $gap [$grid-gap-default] - Gap
302
+ /// @param {String} $selector ["> *"] - Селектор
303
+ /// @param {Boolean} $test [false] - Тестовый режим
304
+ /// @require {mixin} grid-responsive
305
+ /// @example scss
306
+ /// .two-columns {
307
+ /// @include grid-2-cols;
308
+ /// }
309
+ /// @group grid-presets
310
+ /// @access public
311
+ @mixin grid-2-cols($gap: $grid-gap-default, $selector: "> *", $test: false) {
312
+ @include grid-responsive(
313
+ (
314
+ xs: 1,
315
+ md: 2,
316
+ ),
317
+ $gap,
318
+ $selector,
319
+ $test
320
+ );
321
+ }
322
+
323
+ /// Пресет: 3 колонки (1→2→3)
324
+ /// @param {Number} $gap [$grid-gap-default] - Gap
325
+ /// @param {String} $selector ["> *"] - Селектор
326
+ /// @param {Boolean} $test [false] - Тестовый режим
327
+ /// @require {mixin} grid-responsive
328
+ /// @example scss
329
+ /// .three-columns {
330
+ /// @include grid-3-cols(spacing.$gap-lg);
331
+ /// }
332
+ /// @group grid-presets
333
+ /// @access public
334
+ @mixin grid-3-cols($gap: $grid-gap-default, $selector: "> *", $test: false) {
335
+ @include grid-responsive(
336
+ (
337
+ xs: 1,
338
+ sm: 2,
339
+ lg: 3,
340
+ ),
341
+ $gap,
342
+ $selector,
343
+ $test
344
+ );
345
+ }
346
+
347
+ /// Пресет: 4 колонки (1→2→3→4)
348
+ /// @param {Number} $gap [$grid-gap-default] - Gap
349
+ /// @param {String} $selector ["> *"] - Селектор
350
+ /// @param {Boolean} $test [false] - Тестовый режим
351
+ /// @require {mixin} grid-responsive
352
+ /// @example scss
353
+ /// .four-columns {
354
+ /// @include grid-4-cols;
355
+ /// }
356
+ /// @group grid-presets
357
+ /// @access public
358
+ @mixin grid-4-cols($gap: $grid-gap-default, $selector: "> *", $test: false) {
359
+ @include grid-responsive(
360
+ (
361
+ xs: 1,
362
+ sm: 2,
363
+ lg: 3,
364
+ xl: 4,
365
+ ),
366
+ $gap,
367
+ $selector,
368
+ $test
369
+ );
370
+ }
371
+
372
+ /// Пресет: сетка услуг (1→2→3)
373
+ /// Увеличенный gap для карточек услуг
374
+ /// @param {Number} $gap [spacing.$gap-lg] - Gap
375
+ /// @param {String} $selector ["> *"] - Селектор
376
+ /// @param {Boolean} $test [false] - Тестовый режим
377
+ /// @require {mixin} grid-responsive
378
+ /// @example scss
379
+ /// .services__list {
380
+ /// @include grid-services;
381
+ /// }
382
+ /// @group grid-presets
383
+ /// @access public
384
+ @mixin grid-services($gap: spacing.$gap-lg, $selector: "> *", $test: false) {
385
+ @include grid-responsive(
386
+ (
387
+ xs: 1,
388
+ md: 2,
389
+ xl: 3,
390
+ ),
391
+ $gap,
392
+ $selector,
393
+ $test
394
+ );
395
+ }
396
+
397
+ /// Пресет: сетка прогресса/статистики (1→3)
398
+ /// @param {Number} $gap [spacing.$gap-lg] - Gap
399
+ /// @param {String} $selector ["> *"] - Селектор
400
+ /// @param {Boolean} $test [false] - Тестовый режим
401
+ /// @require {mixin} grid-responsive
402
+ /// @example scss
403
+ /// .stats__list {
404
+ /// @include grid-progress;
405
+ /// }
406
+ /// @group grid-presets
407
+ /// @access public
408
+ @mixin grid-progress($gap: spacing.$gap-lg, $selector: "> *", $test: false) {
409
+ @include grid-responsive(
410
+ (
411
+ xs: 1,
412
+ md: 3,
413
+ ),
414
+ $gap,
415
+ $selector,
416
+ $test
417
+ );
418
+ }
419
+
420
+ /// Пресет: сетка товаров (1→2→3→4)
421
+ /// @param {Number} $gap [spacing.$gap-md] - Gap
422
+ /// @param {String} $selector ["> *"] - Селектор
423
+ /// @param {Boolean} $test [false] - Тестовый режим
424
+ /// @require {mixin} grid-responsive
425
+ /// @example scss
426
+ /// .products__grid {
427
+ /// @include grid-products;
428
+ /// }
429
+ /// @group grid-presets
430
+ /// @access public
431
+ @mixin grid-products($gap: spacing.$gap-md, $selector: "> *", $test: false) {
432
+ @include grid-responsive(
433
+ (
434
+ xs: 1,
435
+ sm: 2,
436
+ lg: 3,
437
+ xxl: 4,
438
+ ),
439
+ $gap,
440
+ $selector,
441
+ $test
442
+ );
443
+ }
444
+
445
+ /// Пресет: галерея (2→3→4→5)
446
+ /// Минимальный gap, начинается с 2 колонок
447
+ /// @param {Number} $gap [spacing.$gap-sm] - Gap
448
+ /// @param {String} $selector ["> *"] - Селектор
449
+ /// @param {Boolean} $test [false] - Тестовый режим
450
+ /// @require {mixin} grid-responsive
451
+ /// @example scss
452
+ /// .gallery__grid {
453
+ /// @include grid-gallery;
454
+ /// }
455
+ /// @group grid-presets
456
+ /// @access public
457
+ @mixin grid-gallery($gap: spacing.$gap-sm, $selector: "> *", $test: false) {
458
+ @include grid-responsive(
459
+ (
460
+ xs: 2,
461
+ sm: 3,
462
+ lg: 4,
463
+ xl: 5,
464
+ ),
465
+ $gap,
466
+ $selector,
467
+ $test
468
+ );
469
+ }
470
+
471
+ // ─────────────────────────────────────────
472
+ // GRID ITEM HELPERS
473
+ // ─────────────────────────────────────────
474
+
475
+ /// Растянуть элемент на N колонок
476
+ /// @param {Number} $span - Количество колонок
477
+ /// @example scss
478
+ /// .featured-item {
479
+ /// @include grid-span(2);
480
+ /// }
481
+ /// @output grid-column: span N;
482
+ /// @group grid-item
483
+ /// @access public
484
+ @mixin grid-span($span) {
485
+ grid-column: span $span;
486
+ }
487
+
488
+ /// Растянуть элемент на всю ширину
489
+ /// @example scss
490
+ /// .full-width-item {
491
+ /// @include grid-full;
492
+ /// }
493
+ /// @output grid-column: 1 / -1;
494
+ /// @group grid-item
495
+ /// @access public
496
+ @mixin grid-full {
497
+ grid-column: 1 / -1;
498
+ }
499
+
500
+ /// Растянуть элемент на N строк
501
+ /// @param {Number} $span - Количество строк
502
+ /// @example scss
503
+ /// .tall-item {
504
+ /// @include grid-row-span(2);
505
+ /// }
506
+ /// @output grid-row: span N;
507
+ /// @group grid-item
508
+ /// @access public
509
+ @mixin grid-row-span($span) {
510
+ grid-row: span $span;
511
+ }
512
+
513
+ // ─────────────────────────────────────────
514
+ // GRID LIMIT ITEMS
515
+ // ─────────────────────────────────────────
516
+
517
+ /// Ограничивает количество видимых элементов на разных брейкпоинтах
518
+ /// @param {Map} $limits - Карта (breakpoint: max-items)
519
+ /// @param {String} $selector ["> *"] - Селектор
520
+ /// @require {mixin} breakpoints.media
521
+ /// @example scss - Показать 3 на lg, 4 на xl
522
+ /// .features {
523
+ /// @include grid-limit-items((
524
+ /// lg: 3,
525
+ /// xl: 4
526
+ /// ));
527
+ /// }
528
+ /// @output :nth-child(n + N) { display: none } для каждого брейкпоинта
529
+ /// @group grid-limit
530
+ /// @access public
531
+ @mixin grid-limit-items($limits, $selector: "> *") {
532
+ // Сортируем брейкпоинты и создаём диапазоны
533
+ $breakpoint-keys: map.keys($limits);
534
+ $length: list.length($breakpoint-keys);
535
+
536
+ @for $i from 1 through $length {
537
+ $bp: list.nth($breakpoint-keys, $i);
538
+ $limit: map.get($limits, $bp);
539
+ $next-bp: null;
540
+ @if $i < $length {
541
+ $next-bp: list.nth($breakpoint-keys, $i + 1);
542
+ }
543
+
544
+ @if $next-bp {
545
+ // Диапазон: от $bp до $next-bp
546
+ @include breakpoints.media($bp, min-max, $next-bp) {
547
+ #{$selector}:nth-child(n + #{$limit + 1}) {
548
+ display: none;
549
+ }
550
+ }
551
+ } @else {
552
+ // Последний брейкпоинт — от $bp и выше
553
+ @include breakpoints.media($bp) {
554
+ #{$selector}:nth-child(n + #{$limit + 1}) {
555
+ display: none;
556
+ }
557
+ }
558
+ }
559
+ }
560
+ }
561
+
562
+ /// Скрыть элементы после N на конкретном брейкпоинте и выше
563
+ /// @param {String} $breakpoint - Брейкпоинт (lg, xl, etc.)
564
+ /// @param {Number} $show - Сколько показывать
565
+ /// @param {String} $selector ["> *"] - Селектор
566
+ /// @require {mixin} breakpoints.media
567
+ /// @example scss - Показать только 4 на lg+
568
+ /// .cards {
569
+ /// @include grid-hide-after(lg, 4);
570
+ /// }
571
+ /// @output @media (min-width) { :nth-child(n+N) { display: none } }
572
+ /// @group grid-limit
573
+ /// @access public
574
+ @mixin grid-hide-after($breakpoint, $show, $selector: "> *") {
575
+ @include breakpoints.media($breakpoint) {
576
+ #{$selector}:nth-child(n + #{$show + 1}) {
577
+ display: none;
578
+ }
579
+ }
580
+ }
581
+
582
+ /// Скрыть элементы после N только в диапазоне брейкпоинтов
583
+ /// @param {String} $from - Начальный брейкпоинт
584
+ /// @param {String} $to - Конечный брейкпоинт
585
+ /// @param {Number} $show - Сколько показывать
586
+ /// @param {String} $selector ["> *"] - Селектор
587
+ /// @require {mixin} breakpoints.media
588
+ /// @example scss - Показать 3 только между md и lg
589
+ /// .cards {
590
+ /// @include grid-hide-after-between(md, lg, 3);
591
+ /// }
592
+ /// @output @media (min-width) and (max-width) { ... }
593
+ /// @group grid-limit
594
+ /// @access public
595
+ @mixin grid-hide-after-between($from, $to, $show, $selector: "> *") {
596
+ @include breakpoints.media($from, min-max, $to) {
597
+ #{$selector}:nth-child(n + #{$show + 1}) {
598
+ display: none;
599
+ }
600
+ }
601
+ }
602
+
603
+ // ─────────────────────────────────────────
604
+ // GRID AREAS
605
+ // ─────────────────────────────────────────
606
+
607
+ /// Grid с именованными областями и простым flex fallback
608
+ /// @param {List} $areas - Строка областей ("header header" "sidebar main")
609
+ /// @param {List} $columns [null] - Колонки (1fr 2fr)
610
+ /// @param {Number} $gap [$grid-gap-default] - Gap
611
+ /// @param {Number} $cols-count [1] - Колонок для flex fallback
612
+ /// @param {Boolean} $test [false] - Тестовый режим
613
+ /// @example scss
614
+ /// .layout {
615
+ /// @include grid-areas(
616
+ /// "header header" "sidebar main" "footer footer",
617
+ /// 1fr 3fr,
618
+ /// spacing.$gap-lg,
619
+ /// 2
620
+ /// );
621
+ /// }
622
+ /// @output Flex fallback + grid-template-areas через @supports
623
+ /// @group grid-areas
624
+ /// @access public
625
+ @mixin grid-areas($areas, $columns: null, $gap: $grid-gap-default, $cols-count: 1, $test: false) {
626
+ $gap-half: math.div($gap, 2);
627
+
628
+ // FALLBACK: простой Flex
629
+ display: flex;
630
+ flex-wrap: wrap;
631
+ margin: -#{$gap-half};
632
+
633
+ > * {
634
+ box-sizing: border-box;
635
+ margin: $gap-half;
636
+ flex: 0 0 calc(#{math.percentage(math.div(1, $cols-count))} - #{$gap});
637
+ }
638
+
639
+ // MODERN: CSS Grid
640
+ @if $test != true {
641
+ @supports (display: grid) {
642
+ display: grid;
643
+ gap: $gap;
644
+ margin: 0;
645
+ grid-template-areas: $areas;
646
+
647
+ @if $columns {
648
+ grid-template-columns: $columns;
649
+ }
650
+
651
+ > * {
652
+ margin: 0;
653
+ }
654
+ }
655
+ }
656
+ }
657
+
658
+ /// Присвоить элементу grid-area
659
+ /// @param {String} $name - Имя области
660
+ /// @example scss
661
+ /// .sidebar {
662
+ /// @include grid-area(sidebar);
663
+ /// }
664
+ /// @output grid-area: name (только в @supports)
665
+ /// @group grid-areas
666
+ /// @access public
667
+ @mixin grid-area($name) {
668
+ @supports (display: grid) {
669
+ grid-area: $name;
670
+ }
671
+ }
672
+
673
+ // ─────────────────────────────────────────
674
+ // DISPLAY UTILITIES
675
+ // ─────────────────────────────────────────
676
+
677
+ /// Display: contents — элемент "исчезает", дети становятся частью родительского grid/flex
678
+ /// @param {String} $fallback [block] - display для старых браузеров
679
+ /// @param {Boolean} $test [false] - Тестовый режим
680
+ /// @example scss
681
+ /// .wrapper {
682
+ /// @include display-contents;
683
+ /// }
684
+ /// @output display: block (fallback) + display: contents (modern)
685
+ /// @group grid-display
686
+ /// @access public
687
+ @mixin display-contents($fallback: block, $test: false) {
688
+ // FALLBACK: обычный display
689
+ display: $fallback;
690
+
691
+ // MODERN: contents
692
+ @if $test != true {
693
+ @supports (display: contents) {
694
+ display: contents;
695
+ }
696
+ }
697
+ }
698
+
699
+ /// Display: contents только на определённом брейкпоинте и выше
700
+ /// @param {String} $breakpoint - Брейкпоинт (lg, xl, etc.)
701
+ /// @param {String} $fallback [block] - display для старых браузеров
702
+ /// @param {Boolean} $test [false] - Тестовый режим
703
+ /// @require {mixin} breakpoints.media
704
+ /// @example scss
705
+ /// .mobile-wrapper {
706
+ /// @include display-contents-up(lg);
707
+ /// }
708
+ /// @output display: block + @media { display: contents }
709
+ /// @group grid-display
710
+ /// @access public
711
+ @mixin display-contents-up($breakpoint, $fallback: block, $test: false) {
712
+ display: $fallback;
713
+
714
+ @if $test != true {
715
+ @include breakpoints.media($breakpoint) {
716
+ @supports (display: contents) {
717
+ display: contents;
718
+ }
719
+ }
720
+ }
721
+ }
722
+
723
+ // ─────────────────────────────────────────
724
+ // GRID EXPAND
725
+ // ─────────────────────────────────────────
726
+
727
+ /// Разворачивает обёртки в плоский grid
728
+ /// Использует display: contents для "растворения" обёрток
729
+ /// @param {List} $areas - Области ("address info docs contacts")
730
+ /// @param {List} $columns - Колонки (repeat(4, 1fr))
731
+ /// @param {Number} $gap [$grid-gap-default] - Gap
732
+ /// @example scss
733
+ /// .contacts-grid {
734
+ /// @include grid-expand(
735
+ /// "address info docs contacts",
736
+ /// repeat(4, 1fr)
737
+ /// );
738
+ /// }
739
+ /// @output grid layout только если contents поддерживается
740
+ /// @group grid-expand
741
+ /// @access public
742
+ @mixin grid-expand($areas, $columns, $gap: $grid-gap-default) {
743
+ @supports (display: contents) {
744
+ display: grid;
745
+ grid-template-columns: $columns;
746
+ grid-template-areas: $areas;
747
+ gap: $gap;
748
+ }
749
+ }
750
+
751
+ /// Применить display: contents к обёртке
752
+ /// Обёртка "исчезает", её дети становятся прямыми потомками grid
753
+ /// @example scss
754
+ /// .contact-group {
755
+ /// @include grid-expand-wrapper;
756
+ /// }
757
+ /// @output display: contents (только если поддерживается)
758
+ /// @group grid-expand
759
+ /// @access public
760
+ @mixin grid-expand-wrapper {
761
+ @supports (display: contents) {
762
+ display: contents;
763
+ }
764
+ }
765
+
766
+ /// Применить grid-area к элементу внутри "развёрнутой" обёртки
767
+ /// @param {String} $name - Имя области
768
+ /// @example scss
769
+ /// .contact-address {
770
+ /// @include grid-expand-area(address);
771
+ /// }
772
+ /// @output grid-area: name (только если contents поддерживается)
773
+ /// @group grid-expand
774
+ /// @access public
775
+ @mixin grid-expand-area($name) {
776
+ @supports (display: contents) {
777
+ grid-area: $name;
778
+ }
779
+ }
780
+
781
+ // // Простой grid — пресет
782
+ // .cards {
783
+ // @include grid-3-cols(spacing.$gap-lg);
784
+ // }
785
+
786
+ // // Кастомный респонсивный grid
787
+ // .products {
788
+ // @include grid-responsive((
789
+ // xs: 1,
790
+ // sm: 2,
791
+ // lg: 3,
792
+ // xxl: 4
793
+ // ));
794
+ // }
795
+
796
+ // // Auto-fit — сам подстраивается
797
+ // .features {
798
+ // @include grid-auto-fit(250px);
799
+ // }
800
+
801
+ // // Grid areas для сложных лейаутов
802
+ // .page-layout {
803
+ // @include grid-areas(
804
+ // "header header" "sidebar main" "footer footer",
805
+ // 200px 1fr
806
+ // );
807
+
808
+ // .header { @include grid-area(header); }
809
+ // .sidebar { @include grid-area(sidebar); }
810
+ // }
811
+
812
+ // // Ограничение элементов
813
+ // .news {
814
+ // @include grid-3-cols;
815
+ // @include grid-limit-items((lg: 3, xl: 6));
816
+ // }