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/_link.scss
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Griffincss UI — Link
|
|
2
|
+
// Ссылка. Компонент нужен ровно потому, что ресет снимает с <a> и цвет,
|
|
3
|
+
// и подчёркивание (`color: inherit; text-decoration: none`): без него
|
|
4
|
+
// ссылка в тексте ничем не отличается от текста.
|
|
5
|
+
//
|
|
6
|
+
// Отличие от .gr-btn-link из _button.scss: там кнопка, которая выглядит
|
|
7
|
+
// ссылкой, — элемент, выполняющий действие на текущей странице. Здесь
|
|
8
|
+
// ссылка, которая выглядит ссылкой, — переход по адресу. Выбирает разметка,
|
|
9
|
+
// оформление лишь подтверждает выбор.
|
|
10
|
+
//
|
|
11
|
+
// Отличие от .gr-text-link из griffincss-utils: та задаёт только цвет.
|
|
12
|
+
// Здесь — цвет, подчёркивание, три состояния и контур фокуса.
|
|
13
|
+
//
|
|
14
|
+
// Линия объявляется ТОЛЬКО длинными свойствами — text-decoration-line,
|
|
15
|
+
// -thickness, -style. Сокращённое text-decoration сбрасывает все остальные
|
|
16
|
+
// длинные в initial, поэтому одно `text-decoration: underline` в правиле
|
|
17
|
+
// :hover обнуляло бы толщину from-font, заданную в базовом классе:
|
|
18
|
+
// подчёркивание у тихой ссылки получалось не таким, как у обычной.
|
|
19
|
+
|
|
20
|
+
@use 'sass:string';
|
|
21
|
+
|
|
22
|
+
.gr-link {
|
|
23
|
+
color: var(--gr-color-link);
|
|
24
|
+
text-decoration-line: underline;
|
|
25
|
+
|
|
26
|
+
// from-font, а не фиксированная толщина: линия остаётся соразмерной кеглю
|
|
27
|
+
// и в подписи, и в заголовке. Задана один раз здесь и не сбрасывается
|
|
28
|
+
// ни одним вариантом — они трогают только -line и -style.
|
|
29
|
+
text-decoration-thickness: from-font;
|
|
30
|
+
text-underline-offset: 0.2em;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
transition: color var(--gr-transition), text-decoration-color var(--gr-transition);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Порядок LVHA обязателен: :visited объявлен до :hover, иначе посещённая
|
|
36
|
+
// ссылка не меняла бы цвет под указателем.
|
|
37
|
+
.gr-link:visited {
|
|
38
|
+
color: var(--gr-color-link-visited);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@media (hover: hover) {
|
|
42
|
+
.gr-link:hover {
|
|
43
|
+
color: var(--gr-color-link-hover);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.gr-link:active {
|
|
48
|
+
color: var(--gr-color-link-hover);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.gr-link:focus-visible {
|
|
52
|
+
outline: var(--gr-focus-width) solid var(--gr-color-focus);
|
|
53
|
+
outline-offset: 2px;
|
|
54
|
+
border-radius: calc(var(--gr-radius) * 0.25);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- Варианты ---------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
// Приглушённая: цвет вторичного текста. Для подписей, футеров и служебных
|
|
60
|
+
// переходов, где акцентная ссылка перетягивала бы внимание на себя.
|
|
61
|
+
.gr-link-muted {
|
|
62
|
+
color: var(--gr-color-text-secondary);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.gr-link-muted:visited {
|
|
66
|
+
color: var(--gr-color-text-secondary);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@media (hover: hover) {
|
|
70
|
+
.gr-link-muted:hover {
|
|
71
|
+
color: var(--gr-color-text);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Цвет берётся у окружающего текста. Для ссылки-заголовка и ссылки внутри
|
|
76
|
+
// цветной плашки, где акцентный синий столкнулся бы с фоном.
|
|
77
|
+
.gr-link-inherit {
|
|
78
|
+
color: inherit;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.gr-link-inherit:visited {
|
|
82
|
+
color: inherit;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Цвет на наведении менять нечем — он наследуется. Отзывается линия:
|
|
86
|
+
// толщину трогать нельзя, поэтому меняется её цвет.
|
|
87
|
+
@media (hover: hover) {
|
|
88
|
+
.gr-link-inherit:hover {
|
|
89
|
+
color: inherit;
|
|
90
|
+
text-decoration-color: var(--gr-color-link);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Без подчёркивания, пока на неё не навели. Только для мест, где ссылка
|
|
95
|
+
// опознаётся по положению: список навигации, ряд карточек, футер. В тексте
|
|
96
|
+
// абзаца подчёркивание снимать нельзя — там положение ни о чём не говорит,
|
|
97
|
+
// и ссылка остаётся отличима только цветом, то есть не отличима вовсе
|
|
98
|
+
// для дальтоника и на чёрно-белой печати.
|
|
99
|
+
.gr-link-quiet {
|
|
100
|
+
text-decoration-line: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (hover: hover) {
|
|
104
|
+
.gr-link-quiet:hover {
|
|
105
|
+
text-decoration-line: underline;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Подчёркивание возвращается и по фокусу — вне @media (hover: hover):
|
|
110
|
+
// с клавиатуры ссылку находят и на сенсорном экране.
|
|
111
|
+
.gr-link-quiet:focus-visible {
|
|
112
|
+
text-decoration-line: underline;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// --- Стиль линии ------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
// Линия несёт смысл, которого нет у цвета. Пунктир и точки традиционно
|
|
118
|
+
// помечают ссылку-пояснение: сноску, термин со справкой, — то, что откроется
|
|
119
|
+
// рядом, а не уведёт со страницы. Волна помечает проблемное: устаревший
|
|
120
|
+
// адрес, неточный перевод. Двойная линия выделяет главную ссылку абзаца.
|
|
121
|
+
//
|
|
122
|
+
// Стиль сочетается с любым вариантом: .gr-link-quiet.gr-link-dotted
|
|
123
|
+
// покажет точки при наведении, .gr-link-muted.gr-link-dashed — пунктир
|
|
124
|
+
// приглушённым цветом.
|
|
125
|
+
@each $gr-style in (dotted, dashed, wavy, double) {
|
|
126
|
+
.gr-link-#{$gr-style} {
|
|
127
|
+
text-decoration-style: $gr-style;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Волне и двойной линии нужен зазор больше обычного: первая занимает
|
|
132
|
+
// по высоте вдвое больше сплошной, вторая — это две линии с просветом,
|
|
133
|
+
// и на стандартном отступе обе задевают нижние выносные элементы букв.
|
|
134
|
+
.gr-link-wavy,
|
|
135
|
+
.gr-link-double {
|
|
136
|
+
text-underline-offset: 0.3em;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// --- Внешняя ссылка ---------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
// Стрелка-указатель после текста. Своих иконок библиотека не везёт: это
|
|
142
|
+
// обычный символ, поэтому он масштабируется вместе с кеглем и переживает
|
|
143
|
+
// чёрно-белую печать.
|
|
144
|
+
//
|
|
145
|
+
// Стрелка — признак для глаза. Скринридеру о переходе на чужой сайт или
|
|
146
|
+
// об открытии новой вкладки сообщает разметка, а не она: псевдоэлемент
|
|
147
|
+
// пометить aria-hidden нельзя, поэтому предупреждение ставится текстом
|
|
148
|
+
// в .gr-sr-only рядом.
|
|
149
|
+
//
|
|
150
|
+
// Символ записан CSS-экранированием, а не сам собой: literal ↗ сделал бы
|
|
151
|
+
// собранный файл не-ASCII, а Sass в ответ приписал бы к нему BOM. Тогда
|
|
152
|
+
// объявление порядка слоёв перестало бы быть первым в файле — проверка 4
|
|
153
|
+
// в check-dist.mjs на это и падает. Экранирование разворачивает браузер,
|
|
154
|
+
// и вывод остаётся независимым от кодировки, в которой файл отдали.
|
|
155
|
+
.gr-link-external::after {
|
|
156
|
+
content: string.unquote('"\\2197"');
|
|
157
|
+
|
|
158
|
+
// inline-block, а не inline: подчёркивание родителя протягивается сквозь
|
|
159
|
+
// строчных потомков и снять его с них нельзя, а атомарный строчный бокс
|
|
160
|
+
// оно не пересекает. Стрелка остаётся неподчёркнутой.
|
|
161
|
+
display: inline-block;
|
|
162
|
+
margin-inline-start: 0.15em;
|
|
163
|
+
font-size: 0.85em;
|
|
164
|
+
vertical-align: baseline;
|
|
165
|
+
}
|
package/scss/_menu.scss
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Griffincss UI — Menu
|
|
2
|
+
// Список действий: то, что видно внутри выпадающего меню, контекстного меню
|
|
3
|
+
// или боковой панели. Собственного раскрытия у модуля нет — его даёт
|
|
4
|
+
// .gr-dropdown; здесь только оформление списка.
|
|
5
|
+
//
|
|
6
|
+
// Пункт — <button> или <a>. Выбор между ними решает, что произойдёт: ссылка
|
|
7
|
+
// ведёт по адресу, кнопка выполняет действие на месте. Оформление у них
|
|
8
|
+
// общее, поэтому кнопке здесь возвращают то, что забрал браузер: шрифт,
|
|
9
|
+
// выравнивание по началу строки и полную ширину.
|
|
10
|
+
|
|
11
|
+
.gr-menu {
|
|
12
|
+
display: flex;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
min-inline-size: 11rem;
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding-block: calc(var(--gr-ui-gap) * 0.5);
|
|
18
|
+
padding-inline: 0;
|
|
19
|
+
border: var(--gr-border-width) solid var(--gr-color-border);
|
|
20
|
+
border-radius: var(--gr-radius);
|
|
21
|
+
background-color: var(--gr-color-surface-overlay);
|
|
22
|
+
color: var(--gr-color-text);
|
|
23
|
+
box-shadow: var(--gr-shadow-lg);
|
|
24
|
+
list-style: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Меню на <ul>: обёртки <li> не должны добавлять ни отступов, ни маркеров,
|
|
28
|
+
// а пункт внутри — растягиваться на всю ширину строки.
|
|
29
|
+
//
|
|
30
|
+
// Селектор обёрнут в :where() ради нулевой специфичности: иначе правило
|
|
31
|
+
// «.gr-menu > li» (0,1,1) перебивало бы .gr-menu-header и .gr-menu-sep
|
|
32
|
+
// (0,1,0), поставленные на сам <li>, — заголовок группы терял отступы,
|
|
33
|
+
// а разделитель превращался в линию во всю ширину без полей.
|
|
34
|
+
.gr-menu > :where(li) {
|
|
35
|
+
display: flex;
|
|
36
|
+
margin: 0;
|
|
37
|
+
padding: 0;
|
|
38
|
+
list-style: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.gr-menu > :where(li) > :where(*) {
|
|
42
|
+
flex: 1 1 auto;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.gr-menu-item {
|
|
46
|
+
--gr-menu-fg: var(--gr-color-text);
|
|
47
|
+
|
|
48
|
+
display: flex;
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--gr-ui-gap);
|
|
52
|
+
inline-size: 100%;
|
|
53
|
+
min-block-size: var(--gr-control-height-sm);
|
|
54
|
+
margin: 0;
|
|
55
|
+
padding-block: calc(var(--gr-ui-gap) * 0.5);
|
|
56
|
+
padding-inline: var(--gr-control-padding-x);
|
|
57
|
+
border: 0;
|
|
58
|
+
background-color: transparent;
|
|
59
|
+
color: var(--gr-menu-fg);
|
|
60
|
+
font-family: inherit;
|
|
61
|
+
font-size: var(--gr-control-font-size);
|
|
62
|
+
line-height: 1.3;
|
|
63
|
+
text-align: start;
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (hover: hover) {
|
|
69
|
+
.gr-menu-item:hover:not(:disabled, [aria-disabled="true"]) {
|
|
70
|
+
background-color: color-mix(in srgb, currentcolor 10%, transparent);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Контур внутренний: пункт лежит вплотную к краю меню, и обычный outline
|
|
75
|
+
// с отступом срезался бы скруглением обёртки.
|
|
76
|
+
.gr-menu-item:focus-visible {
|
|
77
|
+
outline: var(--gr-focus-width) solid var(--gr-color-focus);
|
|
78
|
+
outline-offset: calc(-1 * var(--gr-focus-width));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.gr-menu-item:disabled,
|
|
82
|
+
.gr-menu-item[aria-disabled="true"] {
|
|
83
|
+
--gr-menu-fg: var(--gr-color-text-disabled);
|
|
84
|
+
|
|
85
|
+
cursor: not-allowed;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Выбранный пункт: aria-checked для переключателей меню, aria-current —
|
|
89
|
+
// для пункта, ведущего на текущую страницу.
|
|
90
|
+
.gr-menu-item[aria-checked="true"],
|
|
91
|
+
.gr-menu-item[aria-current] {
|
|
92
|
+
--gr-menu-fg: var(--gr-color-accent);
|
|
93
|
+
|
|
94
|
+
background-color: color-mix(in srgb, var(--gr-color-accent) 12%, transparent);
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Опасное действие — удаление и подобное. Красный цвет здесь не единственный
|
|
99
|
+
// признак: такой пункт стоит последним, за разделителем, и назван глаголом.
|
|
100
|
+
.gr-menu-danger {
|
|
101
|
+
--gr-menu-fg: var(--gr-color-danger);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@media (hover: hover) {
|
|
105
|
+
.gr-menu-danger:hover:not(:disabled, [aria-disabled="true"]) {
|
|
106
|
+
background-color: color-mix(in srgb, var(--gr-color-danger) 12%, transparent);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Части меню -------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
// Заголовок группы. Не кнопка и не ссылка: текст, объясняющий, что за
|
|
113
|
+
// пункты идут ниже.
|
|
114
|
+
.gr-menu-header {
|
|
115
|
+
padding-block: calc(var(--gr-ui-gap) * 0.5);
|
|
116
|
+
padding-inline: var(--gr-control-padding-x);
|
|
117
|
+
color: var(--gr-color-text-muted);
|
|
118
|
+
font-size: 0.8125rem;
|
|
119
|
+
font-weight: 600;
|
|
120
|
+
line-height: 1.3;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Разделитель. На <hr> — собственная граница элемента снимается, линию
|
|
124
|
+
// рисует border-block-start, поэтому толщина следует токену.
|
|
125
|
+
.gr-menu-sep {
|
|
126
|
+
block-size: 0;
|
|
127
|
+
margin-block: calc(var(--gr-ui-gap) * 0.5);
|
|
128
|
+
margin-inline: 0;
|
|
129
|
+
border: 0;
|
|
130
|
+
border-block-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Слот под иконку и под сочетание клавиш. Иконка стоит в начале строки,
|
|
134
|
+
// подсказка — в конце: сдвинуть её туда достаточно одному правилу,
|
|
135
|
+
// потому что пункт — flex.
|
|
136
|
+
.gr-menu-icon {
|
|
137
|
+
flex: none;
|
|
138
|
+
inline-size: 1.25em;
|
|
139
|
+
block-size: 1.25em;
|
|
140
|
+
text-align: center;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.gr-menu-shortcut {
|
|
144
|
+
margin-inline-start: auto;
|
|
145
|
+
padding-inline-start: var(--gr-ui-gap);
|
|
146
|
+
color: var(--gr-color-text-muted);
|
|
147
|
+
font-size: 0.8125rem;
|
|
148
|
+
font-variant-numeric: tabular-nums;
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
}
|
package/scss/_modal.scss
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Griffincss UI — Modal
|
|
2
|
+
// Модальное окно на <dialog>. Всё, ради чего обычно пишут скрипт, платформа
|
|
3
|
+
// даёт сама: верхний слой без z-index, фокус внутри окна, закрытие по Esc,
|
|
4
|
+
// возврат фокуса на кнопку, инертность страницы под окном. Библиотеке
|
|
5
|
+
// остаётся вид.
|
|
6
|
+
//
|
|
7
|
+
// Открывается окно вызовом showModal() — в разметке это одна строка:
|
|
8
|
+
//
|
|
9
|
+
// <button onclick="document.getElementById('m').showModal()">Открыть</button>
|
|
10
|
+
// <dialog class="gr-modal" id="m">…</dialog>
|
|
11
|
+
//
|
|
12
|
+
// Закрывается без скрипта вовсе: <form method="dialog"> внутри окна или
|
|
13
|
+
// атрибут formmethod="dialog" у кнопки. Именно так закрывают окна примеры
|
|
14
|
+
// в документации — ни одной строки JS.
|
|
15
|
+
//
|
|
16
|
+
// В состоянии [open] задан ОДИН display, и это принципиально: у <dialog>
|
|
17
|
+
// он none по умолчанию, поэтому безусловный display: flex показал бы окно
|
|
18
|
+
// сразу, — а всё остальное обязано жить в базовом правиле. Атрибут снимается
|
|
19
|
+
// в первый же кадр close(), тогда как переход по display: allow-discrete
|
|
20
|
+
// держит окно на экране до конца анимации: любое свойство, оставленное
|
|
21
|
+
// в [open], пропадает раньше самого окна. Так строение окна (колонка)
|
|
22
|
+
// на время исчезновения превращалось в строку — пояса вставали в ряд,
|
|
23
|
+
// и окно меняло размеры прямо в момент ухода.
|
|
24
|
+
//
|
|
25
|
+
// По той же причине position задан явно. Модальному <dialog> его даёт
|
|
26
|
+
// UA-стиль (position: fixed от :modal), но в top layer окно уже не считается
|
|
27
|
+
// модальным к концу перехода: containing block меняется на предка, и проценты
|
|
28
|
+
// в размерах пересчитываются от него — окно теряло ширину на исчезновении.
|
|
29
|
+
|
|
30
|
+
// Размеры — в процентах от окна, а не в 100vw: единица vw включает ширину
|
|
31
|
+
// полосы прокрутки, поэтому окно «во всю ширину минус поля» получалось шире
|
|
32
|
+
// видимой области и центрировалось со сдвигом в половину полосы.
|
|
33
|
+
.gr-modal {
|
|
34
|
+
position: fixed;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
inline-size: min(32rem, calc(100% - var(--gr-gap) * 2));
|
|
38
|
+
max-inline-size: none;
|
|
39
|
+
max-block-size: min(85dvh, calc(100% - var(--gr-gap) * 2));
|
|
40
|
+
padding: 0;
|
|
41
|
+
border: var(--gr-border-width) solid var(--gr-color-border);
|
|
42
|
+
border-radius: var(--gr-radius);
|
|
43
|
+
background-color: var(--gr-color-surface-overlay);
|
|
44
|
+
color: var(--gr-color-text);
|
|
45
|
+
box-shadow: var(--gr-shadow-xl);
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Единственное, что различает открытое и закрытое окно, — показано оно или
|
|
50
|
+
// нет. display остаётся здесь и только здесь: объявленный в базовом правиле,
|
|
51
|
+
// он показал бы закрытое окно прямо в потоке страницы — авторский стиль
|
|
52
|
+
// сильнее UA-правила dialog:not([open]) { display: none } независимо
|
|
53
|
+
// от специфичности. Направление же колонки живёт в базовом правиле и потому
|
|
54
|
+
// переживает закрытие.
|
|
55
|
+
.gr-modal[open] {
|
|
56
|
+
display: flex;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Подложка. Цвет — токен --gr-overlay: он одинаково тёмен в обеих темах,
|
|
60
|
+
// потому что подложка затемняет страницу, а не перекрашивает её.
|
|
61
|
+
.gr-modal::backdrop {
|
|
62
|
+
background-color: var(--gr-overlay);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// --- Пояса ------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
// Шапка и подвал не сжимаются и не прокручиваются: прокручивается тело.
|
|
68
|
+
// Так заголовок остаётся виден, а кнопки — доступны на длинном содержимом.
|
|
69
|
+
.gr-modal-header {
|
|
70
|
+
display: flex;
|
|
71
|
+
flex: none;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: space-between;
|
|
74
|
+
gap: var(--gr-ui-gap);
|
|
75
|
+
padding-block: calc(var(--gr-gap) * 0.75);
|
|
76
|
+
padding-inline: var(--gr-gap);
|
|
77
|
+
border-block-end: var(--gr-border-width) solid var(--gr-color-border);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Заголовок окна: на него указывает aria-labelledby у <dialog>, поэтому
|
|
81
|
+
// в разметке это настоящий заголовок, а не строка текста.
|
|
82
|
+
.gr-modal-title {
|
|
83
|
+
margin: 0;
|
|
84
|
+
font-size: 1.125rem;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
line-height: 1.3;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.gr-modal-body {
|
|
90
|
+
flex: 1 1 auto;
|
|
91
|
+
padding: var(--gr-gap);
|
|
92
|
+
line-height: var(--gr-leading-base);
|
|
93
|
+
overflow-y: auto;
|
|
94
|
+
overscroll-behavior: contain;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.gr-modal-footer {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex: none;
|
|
100
|
+
flex-wrap: wrap;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: flex-end;
|
|
103
|
+
gap: var(--gr-ui-gap);
|
|
104
|
+
padding-block: calc(var(--gr-gap) * 0.75);
|
|
105
|
+
padding-inline: var(--gr-gap);
|
|
106
|
+
border-block-start: var(--gr-border-width) solid var(--gr-color-border);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// --- Размеры ----------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
.gr-modal-sm {
|
|
112
|
+
inline-size: min(24rem, calc(100% - var(--gr-gap) * 2));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.gr-modal-lg {
|
|
116
|
+
inline-size: min(48rem, calc(100% - var(--gr-gap) * 2));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Во весь экран, с полями по краям. Пригодно для просмотрщика и для
|
|
120
|
+
// длинной формы на узком экране.
|
|
121
|
+
.gr-modal-full {
|
|
122
|
+
inline-size: calc(100% - var(--gr-gap) * 2);
|
|
123
|
+
block-size: calc(100% - var(--gr-gap) * 2);
|
|
124
|
+
max-block-size: none;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- Появление --------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
// Окно проявляется и подъезжает снизу, подложка — только проявляется.
|
|
130
|
+
// display и overlay переводятся через allow-discrete: без этого окно
|
|
131
|
+
// возникало бы мгновенно, минуя переход.
|
|
132
|
+
@supports (transition-behavior: allow-discrete) {
|
|
133
|
+
.gr-modal {
|
|
134
|
+
opacity: 0;
|
|
135
|
+
translate: 0 var(--gr-gap);
|
|
136
|
+
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;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.gr-modal[open] {
|
|
144
|
+
opacity: 1;
|
|
145
|
+
translate: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.gr-modal::backdrop {
|
|
149
|
+
opacity: 0;
|
|
150
|
+
transition:
|
|
151
|
+
opacity var(--gr-transition),
|
|
152
|
+
overlay var(--gr-transition) allow-discrete,
|
|
153
|
+
display var(--gr-transition) allow-discrete;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.gr-modal[open]::backdrop {
|
|
157
|
+
opacity: 1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@starting-style {
|
|
161
|
+
.gr-modal[open] {
|
|
162
|
+
opacity: 0;
|
|
163
|
+
translate: 0 var(--gr-gap);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.gr-modal[open]::backdrop {
|
|
167
|
+
opacity: 0;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.gr-modal {
|
|
174
|
+
translate: none;
|
|
175
|
+
transition-duration: 0.01ms;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.gr-modal::backdrop {
|
|
179
|
+
transition-duration: 0.01ms;
|
|
180
|
+
}
|
|
181
|
+
}
|