griffincss-ui 0.24.0-dev → 0.26.1
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/dist/griffincss-ui-scoped.css +1 -1
- package/dist/griffincss-ui.css +1 -1
- package/dist/griffincss-ui.js +1 -1
- package/dist/griffincss-ui.js.map +1 -1
- package/dist/griffinjs-combobox.js +1 -1
- package/dist/griffinjs-combobox.js.map +1 -1
- package/dist/griffinjs-core.js +1 -1
- package/dist/griffinjs-core.js.map +1 -1
- package/dist/griffinjs-datetime.js +1 -1
- package/dist/griffinjs-datetime.js.map +1 -1
- package/dist/griffinjs-dialog.js +1 -1
- package/dist/griffinjs-dialog.js.map +1 -1
- package/dist/griffinjs-dropdown.js +1 -1
- package/dist/griffinjs-dropdown.js.map +1 -1
- package/dist/griffinjs-fields.css +1 -1
- package/dist/griffinjs-fields.js +1 -1
- package/dist/griffinjs-fields.js.map +1 -1
- package/dist/griffinjs-loader.js +3 -0
- package/dist/griffinjs-loader.js.map +1 -0
- package/dist/griffinjs-megamenu.js +1 -1
- package/dist/griffinjs-megamenu.js.map +1 -1
- package/dist/griffinjs-number.js +3 -0
- package/dist/griffinjs-number.js.map +1 -0
- package/dist/griffinjs-range.js +1 -1
- package/dist/griffinjs-range.js.map +1 -1
- package/dist/griffinjs-validate.js +1 -1
- package/dist/griffinjs-validate.js.map +1 -1
- package/dist/griffinjs.css +1 -1
- package/dist/griffinjs.js +1 -1
- package/dist/griffinjs.js.map +1 -1
- package/package.json +4 -3
- package/scss/_accordion.scss +5 -2
- package/scss/_alert.scss +1 -1
- package/scss/_card.scss +33 -7
- package/scss/_choice.scss +126 -4
- package/scss/_dropdown.scss +7 -3
- package/scss/_form.scss +42 -1
- package/scss/_menu.scss +4 -1
- package/scss/_modal.scss +44 -7
- package/scss/_nav.scss +5 -2
- package/scss/_rating.scss +7 -1
- package/scss/_table.scss +91 -0
- package/scss/_tabs.scss +4 -1
- package/scss/griffincss-ui-scoped.scss +17 -7
- package/scss/griffincss-ui.scss +16 -10
- package/scss/griffinjs/_track.scss +43 -4
- package/scss/griffinjs/fields/_datetime.scss +7 -0
- package/scss/griffinjs/fields/_number.scss +10 -0
- package/scss/griffinjs/fields/fields.scss +2 -1
- package/scss/griffinjs/griffinjs.scss +1 -1
- package/src/griffincss-ui.js +2 -2
- package/src/griffinjs/core/griffinjs-core.js +57 -11
- package/src/griffinjs/core/loader.js +124 -0
- package/src/griffinjs/fields/datetime.js +103 -6
- package/src/griffinjs/fields/number.js +327 -0
- package/src/griffinjs/fields/validate.js +24 -3
- package/src/griffinjs/widgets/combobox.js +15 -2
- package/src/griffinjs/widgets/dialog.js +67 -2
- package/src/griffinjs/widgets/dropdown.js +94 -4
- package/src/griffinjs/widgets/megamenu.js +90 -5
- package/src/griffinjs/widgets/range.js +81 -7
package/scss/_choice.scss
CHANGED
|
@@ -134,7 +134,6 @@
|
|
|
134
134
|
display: block;
|
|
135
135
|
padding-block: 0.375rem;
|
|
136
136
|
padding-inline: var(--gr-ui-gap);
|
|
137
|
-
border-inline-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
138
137
|
color: var(--gr-color-text-secondary);
|
|
139
138
|
font-size: 0.875rem;
|
|
140
139
|
text-align: center;
|
|
@@ -143,18 +142,27 @@
|
|
|
143
142
|
transition: background-color var(--gr-transition), color var(--gr-transition);
|
|
144
143
|
}
|
|
145
144
|
|
|
145
|
+
// Внутренняя рамка — у сегмента, перед которым стоит другой сегмент,
|
|
146
|
+
// а не у каждого с последующим снятием у первого. Края и рамка считаются
|
|
147
|
+
// по ярлыкам, а не по любым потомкам: у <fieldset> штатное имя — <legend>
|
|
148
|
+
// (в том числе скрытый, .gr-sr-only), и первым потомком он отнимал
|
|
149
|
+
// у первого сегмента скругление и левую рамку, а хвостовой <script>
|
|
150
|
+
// или скрытое поле — скругление у последнего (фидбек темы griffin, п. 2).
|
|
151
|
+
.gr-segmented label ~ label span {
|
|
152
|
+
border-inline-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
153
|
+
}
|
|
154
|
+
|
|
146
155
|
// Скругление крайних сегментов задаётся явно, а не берётся из overflow:
|
|
147
156
|
// hidden на обёртке. Обрезка по overflow идёт по внутреннему краю рамки,
|
|
148
157
|
// но с ВНЕШНИМ радиусом, поэтому в углах между заливкой и рамкой оставался
|
|
149
158
|
// зазор — фон переключателя не совпадал с его границами. Свои радиусы
|
|
150
159
|
// у сегментов ложатся по рамке точно.
|
|
151
|
-
.gr-segmented label:first-
|
|
152
|
-
border-inline-start: 0;
|
|
160
|
+
.gr-segmented label:first-of-type span {
|
|
153
161
|
border-start-start-radius: var(--gr-segmented-radius);
|
|
154
162
|
border-end-start-radius: var(--gr-segmented-radius);
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
.gr-segmented label:last-
|
|
165
|
+
.gr-segmented label:last-of-type span {
|
|
158
166
|
border-start-end-radius: var(--gr-segmented-radius);
|
|
159
167
|
border-end-end-radius: var(--gr-segmented-radius);
|
|
160
168
|
}
|
|
@@ -200,3 +208,117 @@
|
|
|
200
208
|
color: var(--gr-color-text-disabled);
|
|
201
209
|
cursor: not-allowed;
|
|
202
210
|
}
|
|
211
|
+
|
|
212
|
+
// Два модификатора, а не рецепты из утилит (фидбек темы griffin, п. 6).
|
|
213
|
+
// Утилиты флекса лежат в слое core и проигрывают align-items и display
|
|
214
|
+
// компонента в ui при любой специфичности; .gr-w-full живёт в utils,
|
|
215
|
+
// а пакет компонентов обязан собираться и без него.
|
|
216
|
+
//
|
|
217
|
+
// Согласие «Я прочитал условия…» в две-три строки: контрол стоит
|
|
218
|
+
// по первой строке, а не по центру блока. Сдвиг — к середине первой
|
|
219
|
+
// строки: при line-height 1,4 квадратик 1,15em ложился бы по её верху.
|
|
220
|
+
.gr-choice-start {
|
|
221
|
+
align-items: flex-start;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.gr-choice-start > input {
|
|
225
|
+
margin-block-start: 0.15em;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Строка настроек «подпись — тумблер»: блочный флекс во всю ширину,
|
|
229
|
+
// подпись слева, переключатель у правого края, кликается вся строка.
|
|
230
|
+
// В инлайновом контексте автор берёт .gr-choice.
|
|
231
|
+
.gr-choice-row {
|
|
232
|
+
display: flex;
|
|
233
|
+
justify-content: space-between;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// --- Образец цвета и плашка размера -----------------------------------------
|
|
237
|
+
|
|
238
|
+
// Радио, которую видят как образец: кружок цвета товара или плашка
|
|
239
|
+
// с размером (Этап 42e). Разметка — та же строка выбора: <label
|
|
240
|
+
// class="gr-choice">, внутри <input class="gr-radio"> и образец следом.
|
|
241
|
+
// Композиция из готового не сложилась ровно в одном месте: состояние
|
|
242
|
+
// «выбран» на самом образце требует правила, которого нет ни у одной
|
|
243
|
+
// утилиты, — а без него выбор показывала бы только радио рядом с кружком.
|
|
244
|
+
//
|
|
245
|
+
// Цвет образца — только через переменную на элементе
|
|
246
|
+
// (style="--gr-swatch: …"): цвет товара — данные страницы, литеральных
|
|
247
|
+
// цветов в библиотеке нет. Цвет — не подпись: у радио обязателен aria-label
|
|
248
|
+
// или текст рядом.
|
|
249
|
+
//
|
|
250
|
+
// Радио прячется правилом с :has(): без него (Firefox < 121) правило
|
|
251
|
+
// не применяется вовсе, и радио остаётся видимой рядом с образцом —
|
|
252
|
+
// мягкая деградация по построению. Сама радио остаётся в потоке, 1px
|
|
253
|
+
// и прозрачной — как в .gr-segmented: display: none снял бы клавиатуру.
|
|
254
|
+
.gr-swatch,
|
|
255
|
+
.gr-swatch-tag {
|
|
256
|
+
display: inline-flex;
|
|
257
|
+
box-sizing: border-box;
|
|
258
|
+
align-items: center;
|
|
259
|
+
justify-content: center;
|
|
260
|
+
border: var(--gr-border-width) solid var(--gr-color-border-strong);
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
transition:
|
|
263
|
+
box-shadow var(--gr-transition),
|
|
264
|
+
background-color var(--gr-transition),
|
|
265
|
+
color var(--gr-transition);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.gr-swatch {
|
|
269
|
+
--gr-swatch: var(--gr-color-surface-sunken);
|
|
270
|
+
|
|
271
|
+
inline-size: 1.5rem;
|
|
272
|
+
block-size: 1.5rem;
|
|
273
|
+
border-radius: var(--gr-radius-pill);
|
|
274
|
+
background-color: var(--gr-swatch);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.gr-swatch-tag {
|
|
278
|
+
min-inline-size: 2.25rem;
|
|
279
|
+
padding-block: 0.25rem;
|
|
280
|
+
padding-inline: 0.625rem;
|
|
281
|
+
border-radius: var(--gr-radius);
|
|
282
|
+
background-color: var(--gr-color-surface);
|
|
283
|
+
color: var(--gr-color-text);
|
|
284
|
+
font-size: 0.875rem;
|
|
285
|
+
line-height: 1.4;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.gr-choice:has(> .gr-swatch, > .gr-swatch-tag) {
|
|
289
|
+
position: relative;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.gr-choice:has(> .gr-swatch, > .gr-swatch-tag) > .gr-radio {
|
|
293
|
+
position: absolute;
|
|
294
|
+
inline-size: 1px;
|
|
295
|
+
block-size: 1px;
|
|
296
|
+
opacity: 0;
|
|
297
|
+
pointer-events: none;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Выбранный кружок — кольцо акцента с просветом цвета подложки: и белый
|
|
301
|
+
// образец, и образец цвета акцента читаются на нём одинаково.
|
|
302
|
+
.gr-radio:checked + .gr-swatch {
|
|
303
|
+
box-shadow:
|
|
304
|
+
0 0 0 2px var(--gr-color-surface),
|
|
305
|
+
0 0 0 4px var(--gr-color-accent);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.gr-radio:checked + .gr-swatch-tag {
|
|
309
|
+
border-color: var(--gr-color-accent);
|
|
310
|
+
background-color: var(--gr-color-accent);
|
|
311
|
+
color: var(--gr-color-on-accent);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.gr-radio:focus-visible + .gr-swatch,
|
|
315
|
+
.gr-radio:focus-visible + .gr-swatch-tag {
|
|
316
|
+
outline: var(--gr-focus-width) solid var(--gr-color-focus);
|
|
317
|
+
outline-offset: 2px;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.gr-radio:disabled + .gr-swatch,
|
|
321
|
+
.gr-radio:disabled + .gr-swatch-tag {
|
|
322
|
+
opacity: 0.55;
|
|
323
|
+
cursor: not-allowed;
|
|
324
|
+
}
|
package/scss/_dropdown.scss
CHANGED
|
@@ -38,15 +38,19 @@
|
|
|
38
38
|
|
|
39
39
|
// --- Панель -----------------------------------------------------------------
|
|
40
40
|
|
|
41
|
+
// Ширина и отступ панели — токенами (Этап 46): панель — голая коробка
|
|
42
|
+
// позиционирования, рамку и фон даёт .gr-menu внутри, а теме нужна панель
|
|
43
|
+
// заданной ширины под свою разметку и с полем вокруг неё.
|
|
41
44
|
.gr-dropdown-panel {
|
|
42
45
|
position: absolute;
|
|
43
46
|
z-index: var(--gr-z-dropdown);
|
|
44
47
|
inset-block-start: calc(100% + var(--gr-ui-gap) * 0.5);
|
|
45
48
|
inset-inline-start: 0;
|
|
46
49
|
box-sizing: border-box;
|
|
47
|
-
min-inline-size: max-content;
|
|
50
|
+
min-inline-size: var(--gr-dropdown-min-width, max-content);
|
|
48
51
|
max-inline-size: min(20rem, 90vw);
|
|
49
52
|
margin: 0;
|
|
53
|
+
padding: var(--gr-dropdown-pad, 0);
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
// Раскрытие к концу строки — для панели у правого края шапки, которая иначе
|
|
@@ -74,7 +78,7 @@
|
|
|
74
78
|
max-inline-size: 100%;
|
|
75
79
|
margin: 0;
|
|
76
80
|
border: 0;
|
|
77
|
-
padding: 0;
|
|
81
|
+
padding: var(--gr-dropdown-pad, 0);
|
|
78
82
|
background: none;
|
|
79
83
|
color: inherit;
|
|
80
84
|
overflow: visible;
|
|
@@ -102,7 +106,7 @@
|
|
|
102
106
|
position-anchor: --gr-dropdown-anchor;
|
|
103
107
|
inset-block: calc(anchor(end) + var(--gr-ui-gap) * 0.5) auto;
|
|
104
108
|
inset-inline: anchor(start) auto;
|
|
105
|
-
min-inline-size: max-content;
|
|
109
|
+
min-inline-size: var(--gr-dropdown-min-width, max-content);
|
|
106
110
|
max-inline-size: min(20rem, 90%);
|
|
107
111
|
|
|
108
112
|
// Если снизу места нет — панель переворачивается вверх, если не хватает
|
package/scss/_form.scss
CHANGED
|
@@ -168,6 +168,14 @@
|
|
|
168
168
|
// :user-invalid, а не :invalid — пустое обязательное поле не должно краснеть
|
|
169
169
|
// до того, как человек его тронул. Атрибут aria-invalid рядом: его ставит
|
|
170
170
|
// серверная проверка, которой правила браузера не касаются.
|
|
171
|
+
//
|
|
172
|
+
// Третий источник — состояние на обёртке: .gr-field[data-gr-state="error"]
|
|
173
|
+
// (фидбек темы griffin, п. 7). Скрипты, которые красят ошибки после ответа
|
|
174
|
+
// сервера, получают список полей и ставят состояние на обёртку — так
|
|
175
|
+
// работает OpenCart (.form-group.has-error) и большинство валидаторов,
|
|
176
|
+
// и до сих пор обёртке .gr-field соответствовать было нечему. Состояние
|
|
177
|
+
// красит вложенные контролы; aria-invalid на контроле остаётся источником
|
|
178
|
+
// для программы чтения с экрана — виджет validate ставит оба.
|
|
171
179
|
.gr-input:user-invalid,
|
|
172
180
|
.gr-textarea:user-invalid,
|
|
173
181
|
.gr-select:user-invalid,
|
|
@@ -175,7 +183,11 @@
|
|
|
175
183
|
.gr-input[aria-invalid="true"],
|
|
176
184
|
.gr-textarea[aria-invalid="true"],
|
|
177
185
|
.gr-select[aria-invalid="true"],
|
|
178
|
-
.gr-file[aria-invalid="true"]
|
|
186
|
+
.gr-file[aria-invalid="true"],
|
|
187
|
+
.gr-field[data-gr-state="error"] .gr-input,
|
|
188
|
+
.gr-field[data-gr-state="error"] .gr-textarea,
|
|
189
|
+
.gr-field[data-gr-state="error"] .gr-select,
|
|
190
|
+
.gr-field[data-gr-state="error"] .gr-file {
|
|
179
191
|
border-color: var(--gr-color-danger);
|
|
180
192
|
}
|
|
181
193
|
|
|
@@ -377,6 +389,22 @@
|
|
|
377
389
|
border-radius: 0;
|
|
378
390
|
}
|
|
379
391
|
|
|
392
|
+
// Края — по видимым полям. Скрытые потомки в группе — обычная практика
|
|
393
|
+
// форм: полное значение составного поля (телефон = код + номер) в
|
|
394
|
+
// <input type="hidden">, признак формы, панель подсказки, которую живой
|
|
395
|
+
// поиск дописывает внутрь, спутник поля даты или суммы из второго бандла.
|
|
396
|
+
// По :first-child / :last-child скругление доставалось скрытому узлу,
|
|
397
|
+
// и видимый край группы оставался прямым (фидбек темы griffin, п. 1).
|
|
398
|
+
//
|
|
399
|
+
// Два яруса правил, а не один. Прежние :first-child / :last-child
|
|
400
|
+
// остаются запасным путём: форма `:nth-child(… of S)` появилась
|
|
401
|
+
// в Chrome 111 и Firefox 113 — позже каскадных слоёв (99 / 97), — и там,
|
|
402
|
+
// где она неизвестна, правило отбрасывается целиком, а группа выглядит
|
|
403
|
+
// как раньше. Мягкая деградация названа в docs/compatibility.html.
|
|
404
|
+
// Правила по видимым стоят ниже и специфичнее (0,3,0 против 0,2,0),
|
|
405
|
+
// поэтому в новых движках побеждают они.
|
|
406
|
+
$_gr-group-visible: ':not([type="hidden"], [hidden], template, script, datalist)';
|
|
407
|
+
|
|
380
408
|
.gr-input-group > :first-child {
|
|
381
409
|
border-start-start-radius: var(--gr-radius);
|
|
382
410
|
border-end-start-radius: var(--gr-radius);
|
|
@@ -391,6 +419,19 @@
|
|
|
391
419
|
margin-inline-start: calc(-1 * var(--gr-border-width));
|
|
392
420
|
}
|
|
393
421
|
|
|
422
|
+
// Первое видимое поле — ещё и без сдвига на рамку: скрытый узел перед ним
|
|
423
|
+
// делает его для `* + *` вторым, и без этой строки оно уезжало бы на 1px.
|
|
424
|
+
.gr-input-group > :nth-child(1 of #{$_gr-group-visible}) {
|
|
425
|
+
margin-inline-start: 0;
|
|
426
|
+
border-start-start-radius: var(--gr-radius);
|
|
427
|
+
border-end-start-radius: var(--gr-radius);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.gr-input-group > :nth-last-child(1 of #{$_gr-group-visible}) {
|
|
431
|
+
border-start-end-radius: var(--gr-radius);
|
|
432
|
+
border-end-end-radius: var(--gr-radius);
|
|
433
|
+
}
|
|
434
|
+
|
|
394
435
|
// Поле в группе тянется, аддон — нет.
|
|
395
436
|
.gr-input-group > .gr-input,
|
|
396
437
|
.gr-input-group > .gr-select {
|
package/scss/_menu.scss
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
padding-block: calc(var(--gr-ui-gap) * 0.5);
|
|
18
18
|
padding-inline: 0;
|
|
19
19
|
border: var(--gr-border-width) solid var(--gr-color-border);
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
// Радиус контейнера, как у карточки и окна: меню — видимая коробка
|
|
22
|
+
// выпадающей панели (Этап 46).
|
|
23
|
+
border-radius: var(--gr-radius-container, var(--gr-radius));
|
|
21
24
|
background-color: var(--gr-color-surface-overlay);
|
|
22
25
|
color: var(--gr-color-text);
|
|
23
26
|
box-shadow: var(--gr-shadow-lg);
|
package/scss/_modal.scss
CHANGED
|
@@ -30,16 +30,23 @@
|
|
|
30
30
|
// Размеры — в процентах от окна, а не в 100vw: единица vw включает ширину
|
|
31
31
|
// полосы прокрутки, поэтому окно «во всю ширину минус поля» получалось шире
|
|
32
32
|
// видимой области и центрировалось со сдвигом в половину полосы.
|
|
33
|
+
//
|
|
34
|
+
// Минимальная высота — для окна, чьё содержимое приходит ajax'ом: форма
|
|
35
|
+
// обратного звонка, быстрый заказ, выбор города открываются сразу, а до
|
|
36
|
+
// ответа сервера <dialog> пуст и рисовался полоской высотой в рамку
|
|
37
|
+
// (фидбек темы griffin, п. 3). Плашка в 10rem — место кольцу загрузки
|
|
38
|
+
// ниже; плавный рост высоты с приходом содержимого — работа слоя.
|
|
33
39
|
.gr-modal {
|
|
34
40
|
position: fixed;
|
|
35
41
|
box-sizing: border-box;
|
|
36
42
|
flex-direction: column;
|
|
37
43
|
inline-size: min(32rem, calc(100% - var(--gr-gap) * 2));
|
|
38
44
|
max-inline-size: none;
|
|
45
|
+
min-block-size: 10rem;
|
|
39
46
|
max-block-size: min(85dvh, calc(100% - var(--gr-gap) * 2));
|
|
40
47
|
padding: 0;
|
|
41
48
|
border: var(--gr-border-width) solid var(--gr-color-border);
|
|
42
|
-
border-radius: var(--gr-radius);
|
|
49
|
+
border-radius: var(--gr-radius-container, var(--gr-radius));
|
|
43
50
|
background-color: var(--gr-color-surface-overlay);
|
|
44
51
|
color: var(--gr-color-text);
|
|
45
52
|
box-shadow: var(--gr-shadow-xl);
|
|
@@ -62,6 +69,25 @@
|
|
|
62
69
|
background-color: var(--gr-overlay);
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
// Состояние загрузки: кольцо по центру пустого окна, тем же keyframe,
|
|
73
|
+
// что у .gr-spinner. Одним слоем, а не двумя, как у самого спиннера:
|
|
74
|
+
// у окна псевдоэлемент один, и дорожка красится цветом рамки вместо
|
|
75
|
+
// полупрозрачного currentcolor — окно стоит на известной поверхности.
|
|
76
|
+
// Центр — авто-отступами флекс-элемента: окно в состоянии [open] уже
|
|
77
|
+
// колонка, и ::before в ней — обычный элемент. Ставит и снимает состояние
|
|
78
|
+
// тот же скрипт, что грузит содержимое; рядом уместен aria-busy="true".
|
|
79
|
+
.gr-modal[data-gr-state="loading"]::before {
|
|
80
|
+
content: "";
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
inline-size: 2.5em;
|
|
83
|
+
block-size: 2.5em;
|
|
84
|
+
margin: auto;
|
|
85
|
+
border: 0.18em solid var(--gr-color-border);
|
|
86
|
+
border-block-start-color: var(--gr-color-accent);
|
|
87
|
+
border-radius: var(--gr-radius-pill);
|
|
88
|
+
animation: gr-spin 0.8s linear infinite;
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
// --- Пояса ------------------------------------------------------------------
|
|
66
92
|
|
|
67
93
|
// Шапка и подвал не сжимаются и не прокручиваются: прокручивается тело.
|
|
@@ -86,11 +112,14 @@
|
|
|
86
112
|
line-height: 1.3;
|
|
87
113
|
}
|
|
88
114
|
|
|
115
|
+
// Прокрутка тела — токеном (Этап 46): окну с собственной прокруткой
|
|
116
|
+
// внутри (карта, список) тема ставит --gr-modal-body-overflow: hidden
|
|
117
|
+
// на обёртке, а не борется со слоем ui на самом теле.
|
|
89
118
|
.gr-modal-body {
|
|
90
119
|
flex: 1 1 auto;
|
|
91
120
|
padding: var(--gr-gap);
|
|
92
121
|
line-height: var(--gr-leading-base);
|
|
93
|
-
overflow-y: auto;
|
|
122
|
+
overflow-y: var(--gr-modal-body-overflow, auto);
|
|
94
123
|
overscroll-behavior: contain;
|
|
95
124
|
}
|
|
96
125
|
|
|
@@ -128,16 +157,18 @@
|
|
|
128
157
|
|
|
129
158
|
// Окно проявляется и подъезжает снизу, подложка — только проявляется.
|
|
130
159
|
// display и overlay переводятся через allow-discrete: без этого окно
|
|
131
|
-
// возникало бы мгновенно, минуя переход.
|
|
160
|
+
// возникало бы мгновенно, минуя переход. Длительность и кривая — токеном
|
|
161
|
+
// --gr-modal-transition (Этап 46): тема задаёт окну свой темп на обёртке,
|
|
162
|
+
// не переписывая четыре свойства перехода в правиле слоя ui.
|
|
132
163
|
@supports (transition-behavior: allow-discrete) {
|
|
133
164
|
.gr-modal {
|
|
134
165
|
opacity: 0;
|
|
135
166
|
translate: 0 var(--gr-gap);
|
|
136
167
|
transition:
|
|
137
|
-
opacity var(--gr-transition),
|
|
138
|
-
translate var(--gr-transition),
|
|
139
|
-
overlay var(--gr-transition) allow-discrete,
|
|
140
|
-
display var(--gr-transition) allow-discrete;
|
|
168
|
+
opacity var(--gr-modal-transition, var(--gr-transition)),
|
|
169
|
+
translate var(--gr-modal-transition, var(--gr-transition)),
|
|
170
|
+
overlay var(--gr-modal-transition, var(--gr-transition)) allow-discrete,
|
|
171
|
+
display var(--gr-modal-transition, var(--gr-transition)) allow-discrete;
|
|
141
172
|
}
|
|
142
173
|
|
|
143
174
|
.gr-modal[open] {
|
|
@@ -178,4 +209,10 @@
|
|
|
178
209
|
.gr-modal::backdrop {
|
|
179
210
|
transition-duration: 0.01ms;
|
|
180
211
|
}
|
|
212
|
+
|
|
213
|
+
// Кольцо замедляется, но не останавливается — как .gr-spinner:
|
|
214
|
+
// остановленный индикатор занятости сообщает «зависло».
|
|
215
|
+
.gr-modal[data-gr-state="loading"]::before {
|
|
216
|
+
animation-duration: 2.4s;
|
|
217
|
+
}
|
|
181
218
|
}
|
package/scss/_nav.scss
CHANGED
|
@@ -31,10 +31,13 @@ $gr-nav-expand: map.get(bp.$gr-breakpoints, md);
|
|
|
31
31
|
// Ряд на <ul>: обёртка <li> не должна ни добавлять полей, ни мешать пункту
|
|
32
32
|
// растянуться по ширине колонки. :where() держит специфичность нулевой,
|
|
33
33
|
// чтобы класс на самом <li> оставался сильнее.
|
|
34
|
+
// Отступ пункта — токеном с нулевым умолчанием (Этап 46): правило темы
|
|
35
|
+
// на самом <li> проигрывает этому по слою, и тема оборачивала пункт лишним
|
|
36
|
+
// узлом. То же у радиуса ссылки ниже.
|
|
34
37
|
.gr-nav > :where(li) {
|
|
35
38
|
display: flex;
|
|
36
39
|
margin: 0;
|
|
37
|
-
padding: 0;
|
|
40
|
+
padding: var(--gr-nav-item-pad, 0);
|
|
38
41
|
list-style: none;
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -61,7 +64,7 @@ $gr-nav-expand: map.get(bp.$gr-breakpoints, md);
|
|
|
61
64
|
padding-block: 0;
|
|
62
65
|
padding-inline: calc(var(--gr-control-padding-x) * 0.75);
|
|
63
66
|
border: 0;
|
|
64
|
-
border-radius: var(--gr-radius);
|
|
67
|
+
border-radius: var(--gr-nav-link-radius, var(--gr-radius));
|
|
65
68
|
background-color: transparent;
|
|
66
69
|
color: var(--gr-nav-fg);
|
|
67
70
|
font-family: inherit;
|
package/scss/_rating.scss
CHANGED
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
// не везёт: подставить свой глиф можно одной строкой на весь сайт, а SVG
|
|
14
14
|
// в модуле означал бы иконочный набор, которого здесь не будет.
|
|
15
15
|
//
|
|
16
|
+
// Цвет заливки — семантический токен --gr-color-rating из темы: его же
|
|
17
|
+
// читает всё, что изображает оценку рядом с рядом знаков (Этап 45).
|
|
18
|
+
// --gr-rating-color — переопределение на одном ряду или во всём проекте;
|
|
19
|
+
// в :root оно не объявлено, поэтому без него ряд берёт цвет темы, и
|
|
20
|
+
// вложенная секция с другой темой перекрашивает звёзды вместе с собой.
|
|
21
|
+
//
|
|
16
22
|
// Число ОБЯЗАНО быть и словом: aria-label вида «4,5 из 5». Пять знаков —
|
|
17
23
|
// это цвет и форма, то есть ровно то, что не переживает ни печать,
|
|
18
24
|
// ни программу чтения с экрана; правило «статус словом, а не только
|
|
@@ -74,5 +80,5 @@ $_gr-rating-row: var(--gr-rating-symbol) var(--gr-rating-symbol) var(--gr-rating
|
|
|
74
80
|
inset-inline-start: 0;
|
|
75
81
|
inline-size: var(--gr-rating-fill);
|
|
76
82
|
overflow: hidden;
|
|
77
|
-
color: var(--gr-rating-color);
|
|
83
|
+
color: var(--gr-rating-color, var(--gr-color-rating));
|
|
78
84
|
}
|
package/scss/_table.scss
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// у ячеек остались бы двойные линии и зазоры border-spacing.
|
|
9
9
|
|
|
10
10
|
@use 'sass:string';
|
|
11
|
+
@use 'griffincss-core/scss/breakpoints' as bp;
|
|
12
|
+
@use 'griffincss-core/scss/container-mixins' as cq;
|
|
11
13
|
|
|
12
14
|
.gr-table {
|
|
13
15
|
inline-size: 100%;
|
|
@@ -112,12 +114,101 @@
|
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
// --- Карточки вместо строк --------------------------------------------------
|
|
118
|
+
|
|
119
|
+
// Ниже порога таблица кабинета — заказы, транзакции, бонусы — становится
|
|
120
|
+
// стопкой карточек: строка — блок с обводкой, ячейки в столбик, подпись
|
|
121
|
+
// колонки — из data-label ячейки (фидбек темы griffin по 0.25.0, п. 5).
|
|
122
|
+
//
|
|
123
|
+
// Шапка спрятана по образцу .gr-sr-only (без объявлений для строчных
|
|
124
|
+
// элементов — у блока-обёртки им нечего делать), а не через display: none:
|
|
125
|
+
// программе чтения с экрана заголовки колонок нужны.
|
|
126
|
+
// Подпись — только у ячейки с data-label: пустой ::before у ячейки без
|
|
127
|
+
// атрибута занимал бы свою долю строки и сдвигал значение. Без атрибута
|
|
128
|
+
// ячейка идёт без подписи — не ломается, но и не подписана.
|
|
129
|
+
//
|
|
130
|
+
// Суффикс называет ПОРОГ: .gr-table-stack-md — карточки ниже 768px ширины
|
|
131
|
+
// окна, .gr-table-stack-cmd — ниже 768px ширины ближайшего предка с .gr-cq;
|
|
132
|
+
// от порога и выше — обычная таблица. Это обратная сторона суффиксов
|
|
133
|
+
// «от брейкпоинта» у остальных адаптивных классов: класс описывает узкое
|
|
134
|
+
// состояние, а широкое — просто таблица. Без суффикса — карточки всегда.
|
|
135
|
+
@mixin gr-table-stack($s) {
|
|
136
|
+
#{$s},
|
|
137
|
+
#{$s} tbody,
|
|
138
|
+
#{$s} tfoot,
|
|
139
|
+
#{$s} tr {
|
|
140
|
+
display: block;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#{$s} thead {
|
|
144
|
+
position: absolute;
|
|
145
|
+
inline-size: 1px;
|
|
146
|
+
block-size: 1px;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
clip-path: inset(50%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#{$s} tr {
|
|
152
|
+
margin-block-end: var(--gr-gap);
|
|
153
|
+
border: var(--gr-border-width) solid var(--gr-color-border);
|
|
154
|
+
border-radius: var(--gr-radius-container, var(--gr-radius));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#{$s} tr:last-child {
|
|
158
|
+
margin-block-end: 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Ячейка — строка «подпись — значение». gap даёт зазор между ними,
|
|
162
|
+
// подпись занимает свою долю, значение — остальное.
|
|
163
|
+
#{$s} tr > * {
|
|
164
|
+
display: flex;
|
|
165
|
+
gap: var(--gr-gap);
|
|
166
|
+
white-space: normal;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#{$s} tr > :last-child {
|
|
170
|
+
border-block-end: 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// text-align: start — подпись не следует за .gr-text-end ячейки: класс
|
|
174
|
+
// ставят ради чисел, а подпись обязана стоять в начале своей доли.
|
|
175
|
+
// Остальное (шрифт, кегль через em) подпись наследует от ячейки, как
|
|
176
|
+
// любой ::before; класс шрифта для значения ставится на самом значении.
|
|
177
|
+
#{$s} tr > [data-label]::before {
|
|
178
|
+
content: attr(data-label);
|
|
179
|
+
flex: 0 0 40%;
|
|
180
|
+
color: var(--gr-color-text-secondary);
|
|
181
|
+
font-size: 0.875rem;
|
|
182
|
+
font-weight: 600;
|
|
183
|
+
text-align: start;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@include gr-table-stack('.gr-table-stack');
|
|
188
|
+
|
|
189
|
+
@each $bp-name, $bp-value in bp.$gr-breakpoints {
|
|
190
|
+
@media (width < $bp-value) {
|
|
191
|
+
@include gr-table-stack('.gr-table-stack-#{$bp-name}');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@include cq.gr-container-below($bp-name) {
|
|
195
|
+
@include gr-table-stack('.gr-table-stack-c#{$bp-name}');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
115
199
|
// --- Прокрутка и липкая шапка -----------------------------------------------
|
|
116
200
|
|
|
117
201
|
// Широкая таблица не сжимается ниже своего содержимого, поэтому без обёртки
|
|
118
202
|
// вбок уезжала бы вся страница, а не одна таблица. Прокручиваемая область
|
|
119
203
|
// объявлена tabindex="0" в разметке — иначе до неё не добраться с клавиатуры.
|
|
204
|
+
//
|
|
205
|
+
// position: relative — обёртка становится содержащим блоком для абсолютно
|
|
206
|
+
// позиционированных потомков. Без него .gr-sr-only внутри кнопки-иконки
|
|
207
|
+
// (кнопка не позиционирована) считался от блока снаружи прокрутки, вставал
|
|
208
|
+
// у правого края широкой таблицы за её пределами и растягивал документ:
|
|
209
|
+
// страница уезжала вбок ровно от того, что обёртка должна была удержать.
|
|
120
210
|
.gr-table-wrap {
|
|
211
|
+
position: relative;
|
|
121
212
|
inline-size: 100%;
|
|
122
213
|
max-block-size: 100%;
|
|
123
214
|
overflow: auto;
|
package/scss/_tabs.scss
CHANGED
|
@@ -59,7 +59,10 @@
|
|
|
59
59
|
min-block-size: var(--gr-control-height);
|
|
60
60
|
margin: 0;
|
|
61
61
|
padding-block: 0;
|
|
62
|
-
|
|
62
|
+
|
|
63
|
+
// Отступ вкладки — токеном (Этап 46): правило темы на .gr-tab
|
|
64
|
+
// проигрывает слою ui, а ряд вкладок в шапке витрины хочет быть плотнее.
|
|
65
|
+
padding-inline: var(--gr-tabs-tab-pad, var(--gr-control-padding-x));
|
|
63
66
|
border: 0;
|
|
64
67
|
|
|
65
68
|
// Линия объявлена прозрачной у всех вкладок сразу: появись она только
|
|
@@ -25,16 +25,20 @@
|
|
|
25
25
|
// остаётся в DOM внутри .griffin, поэтому префикс до него достаёт.
|
|
26
26
|
|
|
27
27
|
@use 'sass:meta';
|
|
28
|
-
@layer griffincss.reset, griffincss.core, griffincss.ui, griffincss.utils, griffincss.style;
|
|
28
|
+
@layer griffincss.reset, griffincss.tokens, griffincss.core, griffincss.ui, griffincss.utils, griffincss.style, griffincss.hidden;
|
|
29
29
|
|
|
30
|
-
//
|
|
31
|
-
|
|
30
|
+
// Токены и тема — вне области и в общем слое griffincss.tokens, как
|
|
31
|
+
// в обычной сборке (Этап 46): атрибут data-gr-theme стоит на <html>,
|
|
32
|
+
// то есть заведомо снаружи области .griffin.
|
|
33
|
+
@layer griffincss.tokens {
|
|
32
34
|
@include meta.load-css('griffincss-core/scss/tokens');
|
|
33
|
-
|
|
34
|
-
// Тема — тоже вне области: атрибут data-gr-theme стоит на <html>,
|
|
35
|
-
// то есть заведомо снаружи области .griffin. Оба файла, как в обычной
|
|
36
|
-
// сборке: правила режима обязаны лежать в одном слое с вариантами ссылки.
|
|
37
35
|
@include meta.load-css('griffincss-core/scss/theme-tokens');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Остальной вывод пакета — в слое griffincss.ui.
|
|
39
|
+
@layer griffincss.ui {
|
|
40
|
+
// Правила режима со свойствами — в одном слое с вариантами ссылки,
|
|
41
|
+
// как в обычной сборке.
|
|
38
42
|
@include meta.load-css('griffincss-core/scss/theme-rules');
|
|
39
43
|
|
|
40
44
|
// @keyframes — вне области: имя анимации ищется глобально, префикс
|
|
@@ -71,3 +75,9 @@
|
|
|
71
75
|
@include meta.load-css('tooltip');
|
|
72
76
|
}
|
|
73
77
|
}
|
|
78
|
+
|
|
79
|
+
// [hidden] — вне области: атрибут обязан работать на любом элементе страницы,
|
|
80
|
+
// а не только внутри .griffin.
|
|
81
|
+
@layer griffincss.hidden {
|
|
82
|
+
@include meta.load-css('griffincss-core/scss/hidden');
|
|
83
|
+
}
|
package/scss/griffincss-ui.scss
CHANGED
|
@@ -17,23 +17,25 @@
|
|
|
17
17
|
// margin-bottom.
|
|
18
18
|
|
|
19
19
|
@use 'sass:meta';
|
|
20
|
-
@layer griffincss.reset, griffincss.core, griffincss.ui, griffincss.utils, griffincss.style;
|
|
20
|
+
@layer griffincss.reset, griffincss.tokens, griffincss.core, griffincss.ui, griffincss.utils, griffincss.style, griffincss.hidden;
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
|
|
22
|
+
// Токены и тема подключаются и здесь, а не только в ядре: собранный файл
|
|
23
|
+
// самодостаточен. Но эмитятся они в общий слой griffincss.tokens, а не в ui
|
|
24
|
+
// (Этап 46): копия та же, что в ядре и в утилитах, и все копии стоят в одном
|
|
25
|
+
// слое — подслой темы потребителя сразу после tokens перебивает их при любой
|
|
26
|
+
// специфичности. Раньше копия в ui была старше и подслоя, и ядра.
|
|
27
|
+
@layer griffincss.tokens {
|
|
24
28
|
@include meta.load-css('griffincss-core/scss/tokens');
|
|
29
|
+
@include meta.load-css('griffincss-core/scss/theme-tokens');
|
|
30
|
+
}
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
// независимо от специфичности селектора.
|
|
30
|
-
//
|
|
31
|
-
// Правила режима со свойствами тоже здесь — в отличие от utils. Правило
|
|
32
|
+
// Остальной вывод пакета — в слое griffincss.ui.
|
|
33
|
+
@layer griffincss.ui {
|
|
34
|
+
// Правила режима со свойствами — здесь, в отличие от utils. Правило
|
|
32
35
|
// [data-gr-a11y="low-vision"] a[href] (0,1,1) обязано побеждать варианты
|
|
33
36
|
// ссылки .gr-link-quiet и .gr-link-inherit (0,1,0), а для этого лежать
|
|
34
37
|
// с ними в одном слое: из ядра оно бы им проиграло. Компонент с заливкой
|
|
35
38
|
// защищается от него сам — a.gr-btn в _button.scss стоит позже по порядку.
|
|
36
|
-
@include meta.load-css('griffincss-core/scss/theme-tokens');
|
|
37
39
|
@include meta.load-css('griffincss-core/scss/theme-rules');
|
|
38
40
|
|
|
39
41
|
// @keyframes живут отдельным файлом ради scoped-сборки — см. _keyframes.scss.
|
|
@@ -71,3 +73,7 @@
|
|
|
71
73
|
// модуля нет намеренно, см. его шапку.
|
|
72
74
|
@include meta.load-css('style-rules');
|
|
73
75
|
}
|
|
76
|
+
|
|
77
|
+
@layer griffincss.hidden {
|
|
78
|
+
@include meta.load-css('griffincss-core/scss/hidden');
|
|
79
|
+
}
|