griffincss-ui 0.16.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/LICENSE +21 -0
- package/dist/griffincss-ui-scoped.css +1 -0
- package/dist/griffincss-ui.css +1 -0
- package/dist/griffincss-ui.js +3 -0
- package/dist/griffincss-ui.js.map +1 -0
- package/package.json +53 -0
- package/scss/_accordion.scss +155 -0
- package/scss/_alert.scss +90 -0
- package/scss/_avatar.scss +90 -0
- package/scss/_badge.scss +162 -0
- package/scss/_breadcrumb.scss +86 -0
- package/scss/_button.scss +216 -0
- package/scss/_card.scss +110 -0
- package/scss/_choice.scss +202 -0
- package/scss/_drawer.scss +183 -0
- package/scss/_dropdown.scss +229 -0
- package/scss/_empty.scss +70 -0
- package/scss/_form.scss +236 -0
- package/scss/_keyframes.scss +39 -0
- package/scss/_link.scss +165 -0
- package/scss/_menu.scss +150 -0
- package/scss/_modal.scss +181 -0
- package/scss/_nav.scss +233 -0
- package/scss/_pagination.scss +142 -0
- package/scss/_progress.scss +188 -0
- package/scss/_skeleton.scss +95 -0
- package/scss/_spinner.scss +59 -0
- package/scss/_steps.scss +193 -0
- package/scss/_style-rules.scss +75 -0
- package/scss/_table.scss +141 -0
- package/scss/_tabs.scss +166 -0
- package/scss/_toast.scss +173 -0
- package/scss/_tooltip.scss +143 -0
- package/scss/griffincss-ui-scoped.scss +61 -0
- package/scss/griffincss-ui.scss +64 -0
- package/src/griffincss-ui.js +661 -0
package/scss/_card.scss
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Griffincss UI — Card
|
|
2
|
+
// Карточка: поверхность с границей, скруглением и тремя необязательными
|
|
3
|
+
// поясами — шапкой, телом и подвалом.
|
|
4
|
+
//
|
|
5
|
+
// Колонка на flex, а не блок: подвал должен прижиматься к низу, когда
|
|
6
|
+
// карточки в ряду выровнены по высоте (.gr-grid-rows, align-items: stretch),
|
|
7
|
+
// а тело — забирать остаток.
|
|
8
|
+
|
|
9
|
+
.gr-card {
|
|
10
|
+
display: flex;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
min-inline-size: 0;
|
|
14
|
+
border: var(--gr-border-width) solid var(--gr-color-border);
|
|
15
|
+
border-radius: var(--gr-radius);
|
|
16
|
+
background-color: var(--gr-color-surface);
|
|
17
|
+
color: var(--gr-color-text);
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Тело забирает остаток высоты, поэтому подвал остаётся внизу
|
|
22
|
+
// независимо от того, сколько в теле текста.
|
|
23
|
+
.gr-card-body {
|
|
24
|
+
flex: 1 1 auto;
|
|
25
|
+
padding: var(--gr-gap);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.gr-card-header,
|
|
29
|
+
.gr-card-footer {
|
|
30
|
+
padding-block: calc(var(--gr-gap) * 0.75);
|
|
31
|
+
padding-inline: var(--gr-gap);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.gr-card-header {
|
|
35
|
+
border-block-end: var(--gr-border-width) solid var(--gr-color-border);
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.gr-card-footer {
|
|
40
|
+
border-block-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
41
|
+
color: var(--gr-color-text-secondary);
|
|
42
|
+
font-size: 0.875rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Картинка во всю ширину карточки. Скругление ей не нужно: его срезает
|
|
46
|
+
// overflow: hidden обёртки, поэтому медиа одинаково лежит и сверху, и снизу.
|
|
47
|
+
.gr-card-media {
|
|
48
|
+
display: block;
|
|
49
|
+
inline-size: 100%;
|
|
50
|
+
block-size: auto;
|
|
51
|
+
object-fit: cover;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// --- Варианты ---------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
// Приподнятая: тень вместо границы. Плотность тени задаёт тема
|
|
57
|
+
// (--gr-shadow-strength), поэтому на тёмном фоне карточка не растворяется.
|
|
58
|
+
.gr-card-raised {
|
|
59
|
+
border-color: transparent;
|
|
60
|
+
background-color: var(--gr-color-surface-raised);
|
|
61
|
+
box-shadow: var(--gr-shadow-md);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Плоская: только фон, ни границы, ни тени.
|
|
65
|
+
.gr-card-flat {
|
|
66
|
+
border-color: transparent;
|
|
67
|
+
background-color: var(--gr-color-surface);
|
|
68
|
+
box-shadow: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- Кликабельная карточка --------------------------------------------------
|
|
72
|
+
|
|
73
|
+
// Кликабельна вся площадь, но фокус остаётся на ссылке. Приём известен как
|
|
74
|
+
// «растянутая ссылка»: ::after ссылки покрывает карточку целиком, поэтому
|
|
75
|
+
// мышь попадает по всей плитке, а Tab по-прежнему приводит к одному
|
|
76
|
+
// элементу с настоящим href — вторая обёртка <a> вокруг карточки такого
|
|
77
|
+
// не даёт и ломает вложенные ссылки.
|
|
78
|
+
//
|
|
79
|
+
// Условие работы: у карточки position: relative, у ссылки — нет.
|
|
80
|
+
.gr-card-interactive {
|
|
81
|
+
position: relative;
|
|
82
|
+
transition: border-color var(--gr-transition), box-shadow var(--gr-transition);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.gr-card-interactive :where(a[href])::after {
|
|
86
|
+
content: "";
|
|
87
|
+
position: absolute;
|
|
88
|
+
inset: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@media (hover: hover) {
|
|
92
|
+
.gr-card-interactive:hover {
|
|
93
|
+
border-color: var(--gr-color-border-strong);
|
|
94
|
+
box-shadow: var(--gr-shadow-md);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Контур рисуется вокруг карточки, а не вокруг строчки текста ссылки:
|
|
99
|
+
// мишень у указателя и мишень у клавиатуры должны совпадать.
|
|
100
|
+
.gr-card-interactive:focus-within {
|
|
101
|
+
outline: var(--gr-focus-width) solid var(--gr-color-focus);
|
|
102
|
+
outline-offset: 2px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Текст ссылки внутри кликабельной карточки не подчёркивается и не красится:
|
|
106
|
+
// ссылка здесь — вся карточка, а не одно её слово.
|
|
107
|
+
.gr-card-interactive :where(a[href]) {
|
|
108
|
+
color: inherit;
|
|
109
|
+
text-decoration: none;
|
|
110
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Griffincss UI — Choice
|
|
2
|
+
// Флажок, радиокнопка, переключатель и сегментный переключатель.
|
|
3
|
+
//
|
|
4
|
+
// Элемент не подменяется: класс ставится на сам <input>, а не на скрытый
|
|
5
|
+
// input рядом с оформленным <span>. Родная разметка сохраняет и участие
|
|
6
|
+
// в форме, и клавиатуру, и объявление состояния скринридером — всё то,
|
|
7
|
+
// что приходится восстанавливать вручную, когда input прячут.
|
|
8
|
+
//
|
|
9
|
+
// Флажок и радиокнопка остаются системными: их рисует ОС, а цвет отметки
|
|
10
|
+
// задаётся одним свойством accent-color. Переключателя среди типов <input>
|
|
11
|
+
// нет — его дорожка и бегунок рисуются фоном, но сам элемент по-прежнему
|
|
12
|
+
// <input type="checkbox">.
|
|
13
|
+
|
|
14
|
+
// --- Флажок и радиокнопка ---------------------------------------------------
|
|
15
|
+
|
|
16
|
+
.gr-checkbox,
|
|
17
|
+
.gr-radio {
|
|
18
|
+
--gr-choice-size: 1.15em;
|
|
19
|
+
|
|
20
|
+
flex: none;
|
|
21
|
+
inline-size: var(--gr-choice-size);
|
|
22
|
+
block-size: var(--gr-choice-size);
|
|
23
|
+
margin: 0;
|
|
24
|
+
accent-color: var(--gr-color-accent);
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.gr-checkbox:focus-visible,
|
|
29
|
+
.gr-radio:focus-visible,
|
|
30
|
+
.gr-switch:focus-visible {
|
|
31
|
+
outline: var(--gr-focus-width) solid var(--gr-color-focus);
|
|
32
|
+
outline-offset: 2px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.gr-checkbox:disabled,
|
|
36
|
+
.gr-radio:disabled,
|
|
37
|
+
.gr-switch:disabled {
|
|
38
|
+
opacity: 0.55;
|
|
39
|
+
cursor: not-allowed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Переключатель ----------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
// Бегунок — фоновое изображение, а не ::before: псевдоэлементы на <input>
|
|
45
|
+
// поддерживаются не везде, а фон работает всюду. background-origin:
|
|
46
|
+
// content-box отводит бегунку внутреннюю область, поэтому отступ от края
|
|
47
|
+
// дорожки задаётся обычным padding.
|
|
48
|
+
.gr-switch {
|
|
49
|
+
--gr-switch-track: 2.25rem;
|
|
50
|
+
--gr-switch-thumb: 1rem;
|
|
51
|
+
|
|
52
|
+
box-sizing: border-box;
|
|
53
|
+
flex: none;
|
|
54
|
+
inline-size: var(--gr-switch-track);
|
|
55
|
+
block-size: calc(var(--gr-switch-thumb) + 0.375rem);
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 0.1875rem;
|
|
58
|
+
border: var(--gr-border-width) solid var(--gr-color-border-strong);
|
|
59
|
+
border-radius: var(--gr-radius-pill);
|
|
60
|
+
background-color: var(--gr-color-surface-sunken);
|
|
61
|
+
background-image: radial-gradient(circle at 50% 50%, var(--gr-color-text-secondary) 45%, transparent 46%);
|
|
62
|
+
background-repeat: no-repeat;
|
|
63
|
+
background-position: left center;
|
|
64
|
+
background-size: var(--gr-switch-thumb) var(--gr-switch-thumb);
|
|
65
|
+
background-origin: content-box;
|
|
66
|
+
appearance: none;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
transition:
|
|
69
|
+
background-color var(--gr-transition),
|
|
70
|
+
background-position var(--gr-transition),
|
|
71
|
+
border-color var(--gr-transition);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.gr-switch:checked {
|
|
75
|
+
border-color: var(--gr-color-accent);
|
|
76
|
+
background-color: var(--gr-color-accent);
|
|
77
|
+
background-image: radial-gradient(circle at 50% 50%, var(--gr-color-on-accent) 45%, transparent 46%);
|
|
78
|
+
background-position: right center;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Единственное движение в модуле — переезд бегунка. Тем, кто просил
|
|
82
|
+
// уменьшить анимацию, он достаётся мгновенным; цвет продолжает меняться
|
|
83
|
+
// плавно, это не движение.
|
|
84
|
+
@media (prefers-reduced-motion: reduce) {
|
|
85
|
+
.gr-switch {
|
|
86
|
+
transition:
|
|
87
|
+
background-color var(--gr-transition),
|
|
88
|
+
border-color var(--gr-transition);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- Сегментный переключатель -----------------------------------------------
|
|
93
|
+
|
|
94
|
+
// Ряд взаимоисключающих вариантов из обычной радиогруппы. Оформляется
|
|
95
|
+
// <fieldset>, поэтому у группы есть имя для скринридера (<legend> или
|
|
96
|
+
// aria-label), а выбор переключается стрелками — это даёт платформа.
|
|
97
|
+
//
|
|
98
|
+
// Селекторы по элементам, а не по классам: разметка внутри фиксирована
|
|
99
|
+
// (label > input + span), и требовать класс на каждом из трёх уровней
|
|
100
|
+
// значило бы разменивать краткость на ничто.
|
|
101
|
+
.gr-segmented {
|
|
102
|
+
// Внутренний радиус: скругление рамки за вычетом её толщины. Он нужен
|
|
103
|
+
// крайним сегментам ниже, чтобы их заливка легла ровно по рамке.
|
|
104
|
+
// Считается, а не задаётся: в режиме для слабовидящих --gr-border-width
|
|
105
|
+
// вырастает до 2px, и скругление обязано подвинуться следом.
|
|
106
|
+
--gr-segmented-radius: calc(var(--gr-radius) - var(--gr-border-width));
|
|
107
|
+
|
|
108
|
+
display: inline-flex;
|
|
109
|
+
box-sizing: border-box;
|
|
110
|
+
min-inline-size: 0;
|
|
111
|
+
margin: 0;
|
|
112
|
+
padding: 0;
|
|
113
|
+
border: var(--gr-border-width) solid var(--gr-color-border-strong);
|
|
114
|
+
border-radius: var(--gr-radius);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.gr-segmented label {
|
|
118
|
+
display: block;
|
|
119
|
+
position: relative;
|
|
120
|
+
flex: 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Сам input не прячется display: none и не убирается из потока навсегда —
|
|
124
|
+
// он остаётся фокусируемым, иначе группа перестала бы работать с клавиатуры.
|
|
125
|
+
.gr-segmented input {
|
|
126
|
+
position: absolute;
|
|
127
|
+
inline-size: 1px;
|
|
128
|
+
block-size: 1px;
|
|
129
|
+
opacity: 0;
|
|
130
|
+
pointer-events: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.gr-segmented span {
|
|
134
|
+
display: block;
|
|
135
|
+
padding-block: 0.375rem;
|
|
136
|
+
padding-inline: var(--gr-ui-gap);
|
|
137
|
+
border-inline-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
138
|
+
color: var(--gr-color-text-secondary);
|
|
139
|
+
font-size: 0.875rem;
|
|
140
|
+
text-align: center;
|
|
141
|
+
white-space: nowrap;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
transition: background-color var(--gr-transition), color var(--gr-transition);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Скругление крайних сегментов задаётся явно, а не берётся из overflow:
|
|
147
|
+
// hidden на обёртке. Обрезка по overflow идёт по внутреннему краю рамки,
|
|
148
|
+
// но с ВНЕШНИМ радиусом, поэтому в углах между заливкой и рамкой оставался
|
|
149
|
+
// зазор — фон переключателя не совпадал с его границами. Свои радиусы
|
|
150
|
+
// у сегментов ложатся по рамке точно.
|
|
151
|
+
.gr-segmented label:first-child span {
|
|
152
|
+
border-inline-start: 0;
|
|
153
|
+
border-start-start-radius: var(--gr-segmented-radius);
|
|
154
|
+
border-end-start-radius: var(--gr-segmented-radius);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.gr-segmented label:last-child span {
|
|
158
|
+
border-start-end-radius: var(--gr-segmented-radius);
|
|
159
|
+
border-end-end-radius: var(--gr-segmented-radius);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@media (hover: hover) {
|
|
163
|
+
.gr-segmented span:hover {
|
|
164
|
+
background-color: var(--gr-color-surface-raised);
|
|
165
|
+
color: var(--gr-color-text);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.gr-segmented input:checked + span {
|
|
170
|
+
background-color: var(--gr-color-accent);
|
|
171
|
+
color: var(--gr-color-on-accent);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Контур внутрь: снаружи он налез бы на соседний сегмент и на рамку
|
|
175
|
+
// обёртки, а у среднего сегмента ещё и на оба сразу.
|
|
176
|
+
.gr-segmented input:focus-visible + span {
|
|
177
|
+
outline: var(--gr-focus-width) solid var(--gr-color-focus);
|
|
178
|
+
outline-offset: calc(-1 * var(--gr-focus-width) - 1px);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.gr-segmented input:disabled + span {
|
|
182
|
+
opacity: 0.55;
|
|
183
|
+
cursor: not-allowed;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// --- Строка выбора ----------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
// Флажок и подпись в одну строку, с общим кликом по подписи: <label>
|
|
189
|
+
// целиком становится мишенью, а не одни 16 пикселей квадратика.
|
|
190
|
+
.gr-choice {
|
|
191
|
+
display: inline-flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
gap: var(--gr-ui-gap);
|
|
194
|
+
color: var(--gr-color-text);
|
|
195
|
+
line-height: 1.4;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.gr-choice:has(:disabled) {
|
|
200
|
+
color: var(--gr-color-text-disabled);
|
|
201
|
+
cursor: not-allowed;
|
|
202
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Griffincss UI — Drawer
|
|
2
|
+
// Выдвижная панель — тот же <dialog>, что и модальное окно, но прижатый
|
|
3
|
+
// к краю экрана. Верхний слой, фокус, Esc и инертность страницы под панелью
|
|
4
|
+
// платформа даёт так же, как модалке.
|
|
5
|
+
//
|
|
6
|
+
// Пояса берутся у модалки: .gr-modal-header, -body, -footer работают
|
|
7
|
+
// и здесь. Дублировать их незачем — панель отличается только положением
|
|
8
|
+
// и размерами, а не устройством.
|
|
9
|
+
//
|
|
10
|
+
// <dialog class="gr-drawer gr-drawer-end">
|
|
11
|
+
// <div class="gr-modal-header">…</div>
|
|
12
|
+
// <div class="gr-modal-body">…</div>
|
|
13
|
+
// </dialog>
|
|
14
|
+
//
|
|
15
|
+
// Сторона задаётся вылетами (inset), а не полями. У модального <dialog>
|
|
16
|
+
// UA-стиль ставит inset: 0 и margin: auto, и при таком наборе высота auto
|
|
17
|
+
// перестаёт считаться по содержимому: два заданных вылета растягивают
|
|
18
|
+
// элемент, а auto-поле забирает остаток. Верхняя панель из-за этого
|
|
19
|
+
// занимала ровно половину экрана и стояла полупустой. Отпущенный вылет
|
|
20
|
+
// на противоположной стороне возвращает высоту содержимому.
|
|
21
|
+
//
|
|
22
|
+
// Размеры — в процентах, а не в 100vw: единица vw включает ширину полосы
|
|
23
|
+
// прокрутки, и панель во «всю ширину» оказывалась шире видимой области,
|
|
24
|
+
// уезжая правым краем под полосу.
|
|
25
|
+
|
|
26
|
+
// Строение панели и её position живут в базовом правиле, а не в [open]:
|
|
27
|
+
// атрибут снимается в первый кадр close(), а переход по display держит
|
|
28
|
+
// панель на экране до конца анимации — всё, что осталось в [open],
|
|
29
|
+
// пропадает раньше самой панели. См. подробный разбор в _modal.scss.
|
|
30
|
+
.gr-drawer {
|
|
31
|
+
--gr-drawer-size: 20rem;
|
|
32
|
+
|
|
33
|
+
// Направление выезда. Объявлено здесь, а не в блоке анимации: там оно
|
|
34
|
+
// стояло бы ПОСЛЕ классов сторон с той же специфичностью и перебивало бы
|
|
35
|
+
// их значение — панель начала строки выезжала бы справа.
|
|
36
|
+
--gr-drawer-slide: 100%;
|
|
37
|
+
|
|
38
|
+
position: fixed;
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
flex-direction: column;
|
|
41
|
+
inset-block: 0;
|
|
42
|
+
inline-size: min(var(--gr-drawer-size), 100%);
|
|
43
|
+
max-inline-size: none;
|
|
44
|
+
block-size: auto;
|
|
45
|
+
max-block-size: none;
|
|
46
|
+
margin: 0;
|
|
47
|
+
padding: 0;
|
|
48
|
+
border: 0;
|
|
49
|
+
border-radius: 0;
|
|
50
|
+
background-color: var(--gr-color-surface-overlay);
|
|
51
|
+
color: var(--gr-color-text);
|
|
52
|
+
box-shadow: var(--gr-shadow-xl);
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.gr-drawer[open] {
|
|
57
|
+
display: flex;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.gr-drawer::backdrop {
|
|
61
|
+
background-color: var(--gr-overlay);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Стороны ----------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
// Начало и конец строки — не «слева» и «справа»: в разметке на арабском
|
|
67
|
+
// панель конца строки выедет с левого края, и это правильное поведение.
|
|
68
|
+
// Сдвиг для выезда задан в процентах от самой панели, поэтому он совпадает
|
|
69
|
+
// с её шириной при любом --gr-drawer-size.
|
|
70
|
+
.gr-drawer-start {
|
|
71
|
+
--gr-drawer-slide: -100%;
|
|
72
|
+
|
|
73
|
+
inset-inline: 0 auto;
|
|
74
|
+
border-inline-end: var(--gr-border-width) solid var(--gr-color-border);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.gr-drawer-end {
|
|
78
|
+
--gr-drawer-slide: 100%;
|
|
79
|
+
|
|
80
|
+
inset-inline: auto 0;
|
|
81
|
+
border-inline-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Верхняя и нижняя занимают всю ширину, а высоту берут по содержимому,
|
|
85
|
+
// но не выше половины экрана: панель, закрывшая страницу целиком, — это
|
|
86
|
+
// уже модальное окно.
|
|
87
|
+
.gr-drawer-top,
|
|
88
|
+
.gr-drawer-bottom {
|
|
89
|
+
inset-inline: 0;
|
|
90
|
+
inline-size: auto;
|
|
91
|
+
block-size: auto;
|
|
92
|
+
max-block-size: 50dvh;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.gr-drawer-top {
|
|
96
|
+
--gr-drawer-slide: -100%;
|
|
97
|
+
|
|
98
|
+
inset-block: 0 auto;
|
|
99
|
+
border-block-end: var(--gr-border-width) solid var(--gr-color-border);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.gr-drawer-bottom {
|
|
103
|
+
--gr-drawer-slide: 100%;
|
|
104
|
+
|
|
105
|
+
inset-block: auto 0;
|
|
106
|
+
border-block-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// --- Размеры ----------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
.gr-drawer-sm {
|
|
112
|
+
--gr-drawer-size: 15rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.gr-drawer-lg {
|
|
116
|
+
--gr-drawer-size: 28rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// --- Появление --------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
// Панель выезжает с той стороны, к которой прижата: направление задаёт
|
|
122
|
+
// --gr-drawer-slide, объявленная вместе со стороной. У верхней и нижней
|
|
123
|
+
// сдвиг по другой оси, поэтому translate записан двумя значениями.
|
|
124
|
+
@supports (transition-behavior: allow-discrete) {
|
|
125
|
+
.gr-drawer {
|
|
126
|
+
opacity: 0;
|
|
127
|
+
translate: var(--gr-drawer-slide) 0;
|
|
128
|
+
transition:
|
|
129
|
+
opacity var(--gr-transition),
|
|
130
|
+
translate var(--gr-transition),
|
|
131
|
+
overlay var(--gr-transition) allow-discrete,
|
|
132
|
+
display var(--gr-transition) allow-discrete;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.gr-drawer-top,
|
|
136
|
+
.gr-drawer-bottom {
|
|
137
|
+
translate: 0 var(--gr-drawer-slide);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.gr-drawer[open] {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
translate: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.gr-drawer::backdrop {
|
|
146
|
+
opacity: 0;
|
|
147
|
+
transition:
|
|
148
|
+
opacity var(--gr-transition),
|
|
149
|
+
overlay var(--gr-transition) allow-discrete,
|
|
150
|
+
display var(--gr-transition) allow-discrete;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.gr-drawer[open]::backdrop {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@starting-style {
|
|
158
|
+
.gr-drawer[open] {
|
|
159
|
+
opacity: 0;
|
|
160
|
+
translate: var(--gr-drawer-slide) 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.gr-drawer-top[open],
|
|
164
|
+
.gr-drawer-bottom[open] {
|
|
165
|
+
translate: 0 var(--gr-drawer-slide);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.gr-drawer[open]::backdrop {
|
|
169
|
+
opacity: 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@media (prefers-reduced-motion: reduce) {
|
|
175
|
+
.gr-drawer {
|
|
176
|
+
translate: none;
|
|
177
|
+
transition-duration: 0.01ms;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.gr-drawer::backdrop {
|
|
181
|
+
transition-duration: 0.01ms;
|
|
182
|
+
}
|
|
183
|
+
}
|