@soxa-ui/theme-base 0.0.0-stage → 0.3.0-beta.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/LICENSE +21 -0
- package/README.md +399 -2
- package/dist/setup/index.d.ts +31 -0
- package/dist/setup/index.js +4 -0
- package/dist/setup/plugins/index.d.ts +3 -0
- package/dist/setup/plugins/table-head.plugin.d.ts +44 -0
- package/dist/setup/plugins/table-head.plugin.js +86 -0
- package/dist/setup/plugins/tabs-view.plugin.d.ts +32 -0
- package/dist/setup/plugins/tabs-view.plugin.js +100 -0
- package/dist/setup/plugins/toasts-front.plugin.d.ts +63 -0
- package/dist/setup/plugins/toasts-front.plugin.js +131 -0
- package/dist/setup/plugins/transitions.d.ts +43 -0
- package/dist/setup/plugins/transitions.js +75 -0
- package/package.json +55 -4
- package/src/anchored.css +75 -0
- package/src/caption.css +71 -0
- package/src/components/accordion.css +96 -0
- package/src/components/button.css +139 -0
- package/src/components/calendar.css +594 -0
- package/src/components/check-box.css +121 -0
- package/src/components/date-input.css +224 -0
- package/src/components/date-picker.css +208 -0
- package/src/components/dialog.css +95 -0
- package/src/components/drag-and-drop.css +18 -0
- package/src/components/drawer.css +171 -0
- package/src/components/field.css +440 -0
- package/src/components/icon.css +61 -0
- package/src/components/input.css +189 -0
- package/src/components/label.css +160 -0
- package/src/components/list-box.css +165 -0
- package/src/components/menu.css +224 -0
- package/src/components/number-input.css +183 -0
- package/src/components/pagination.css +164 -0
- package/src/components/popover.css +164 -0
- package/src/components/progress-linear.css +88 -0
- package/src/components/progress-spinner.css +127 -0
- package/src/components/radio-group.css +130 -0
- package/src/components/rating.css +141 -0
- package/src/components/scroller.css +136 -0
- package/src/components/select.css +233 -0
- package/src/components/skeleton.css +33 -0
- package/src/components/slider.css +482 -0
- package/src/components/switch.css +142 -0
- package/src/components/table.css +1513 -0
- package/src/components/tabs.css +166 -0
- package/src/components/tags.css +371 -0
- package/src/components/textarea.css +179 -0
- package/src/components/toasts.css +447 -0
- package/src/components/tooltip.css +59 -0
- package/src/direction.css +30 -0
- package/src/disabled.css +26 -0
- package/src/group.css +90 -0
- package/src/index.css +97 -0
- package/src/modal.css +183 -0
- package/src/place.css +30 -0
- package/src/required.css +40 -0
- package/src/reset.css +172 -0
- package/src/swipe.css +71 -0
- package/src/target.css +46 -0
- package/theme-contract.json +3373 -0
- package/theme-variables.json +336 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* ─── Направление письма ─────────────────────────────────────
|
|
2
|
+
* Знак строки числом: 1 — слева направо, −1 — справа налево.
|
|
3
|
+
*
|
|
4
|
+
* Логические свойства (`inset-inline-start`, `margin-inline`,
|
|
5
|
+
* `padding-inline`) браузер считает по направленности сам. Знак нужен тому,
|
|
6
|
+
* у чего логических направлений нет: зеркалу иконки (`scaleX`), стороне
|
|
7
|
+
* градиента (угол), сдвигу (`translate`). Такое зеркало тема пишет числом —
|
|
8
|
+
* умножением на знак, — а не селектором по направлению.
|
|
9
|
+
*
|
|
10
|
+
* Знак задаёт атрибут `dir`, а до блока его доносит наследование: блок
|
|
11
|
+
* получает значение ближайшего `dir` — того же, по которому браузер
|
|
12
|
+
* раскладывает логические свойства. Нет `dir` у предков — слева направо.
|
|
13
|
+
* Селектор по атрибуту у предка (`[dir='rtl'] .so-блок`) ближайшего не видит,
|
|
14
|
+
* и блок с `dir="ltr"` в RTL-предке получал бы зеркало RTL. `:dir()` его
|
|
15
|
+
* видит, но сборка переписывает `:dir()` в список языков (AGENTS.md пакета,
|
|
16
|
+
* «Знак направления письма»). `dir="auto"` знака не меняет: направленность
|
|
17
|
+
* по тексту знает только браузер, и блок под таким предком берёт знак от
|
|
18
|
+
* `dir` выше.
|
|
19
|
+
*
|
|
20
|
+
* `:root` — умолчание страницы без `dir`. Правило RTL стоит ниже него: у
|
|
21
|
+
* `<html dir="rtl">` совпадают оба, специфичность одна, побеждает нижнее. */
|
|
22
|
+
|
|
23
|
+
:root,
|
|
24
|
+
[dir='ltr'] {
|
|
25
|
+
--so-dir-sign: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[dir='rtl'] {
|
|
29
|
+
--so-dir-sign: -1;
|
|
30
|
+
}
|
package/src/disabled.css
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* ─── Выключенное гаснет поддеревом ──────────────────────────
|
|
2
|
+
* Выключенный компонент гаснет целиком — коробкой или корнем — одной
|
|
3
|
+
* прозрачностью, а выключенные части внутри него — кнопки поля, теги, дни,
|
|
4
|
+
* строки — выключены вместе с ним и второй раз не гаснут: под погасшим
|
|
5
|
+
* предком их прозрачность — 1. Так сколько выключенных ни вложено, видно их с
|
|
6
|
+
* одной прозрачностью, как выключенный Button.
|
|
7
|
+
*
|
|
8
|
+
* Прозрачность — переменная `--so-disabled-opacity`, умолчание — здесь, у
|
|
9
|
+
* корня документа. Гасит часть пара правил в файле её блока:
|
|
10
|
+
*
|
|
11
|
+
* .so-x[data-disabled='true'] { opacity: var(--so-disabled-opacity); }
|
|
12
|
+
* .so-x[data-disabled='true'] > * { --so-disabled-opacity: 1; }
|
|
13
|
+
*
|
|
14
|
+
* Второе ставит единицу детям погасшего узла, а не ему самому: переменную,
|
|
15
|
+
* объявленную на себе, узел прочёл бы сам. Наследуется она всему поддереву, и
|
|
16
|
+
* выключенное внутри погасшего получает 1 на любой глубине. Поэтому
|
|
17
|
+
* выключенное гаснет только так: своя прозрачность в правиле выключенного —
|
|
18
|
+
* число, сброс у части — легла бы под погасшим предком второй раз. Сторожа —
|
|
19
|
+
* `__tests__/disabled.spec.ts` (прозрачность — только из переменной, одна на
|
|
20
|
+
* тему) и `__tests__/base-css.spec.ts` (у каждого гасящего правила — пара).
|
|
21
|
+
* Тема меняет прозрачность переменной, а не своим правилом: гасит
|
|
22
|
+
* выключенное у каждой части общий слой, и своего приёма гашения у темы нет. */
|
|
23
|
+
|
|
24
|
+
:root {
|
|
25
|
+
--so-disabled-opacity: 0.35;
|
|
26
|
+
}
|
package/src/group.css
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* ─── Groups of items ───────────────────────────────────────
|
|
2
|
+
* Sections of grouping in Menu, ListBox and Select — groups with a label and
|
|
3
|
+
* items without a group — and the separator line between them.
|
|
4
|
+
*
|
|
5
|
+
* The label of a group is a note above the items, not an item: one line, with
|
|
6
|
+
* an ellipsis, and an empty one takes no room (a group without a label renders
|
|
7
|
+
* no node for it). Its font is the note at a control of the size of the rows
|
|
8
|
+
* of the group (the caption scale, `caption.css`): a step smaller than the
|
|
9
|
+
* rows, as a helper is a step smaller than its field. The field along the line
|
|
10
|
+
* is the field of a row on the Button scale of the same size: the label stands
|
|
11
|
+
* where the first column of the rows begins. Fields above and below belong to
|
|
12
|
+
* the block.
|
|
13
|
+
*
|
|
14
|
+
* The size of the label is the size of the rows. The size modifier is carried
|
|
15
|
+
* by the row — the Button inside the item — and in ListBox and Select the
|
|
16
|
+
* label reads it at the rows of its own group (`:has`): the panel of Select is
|
|
17
|
+
* teleported and has no size modifier of Select, while its rows always carry
|
|
18
|
+
* one. Child combinators keep the rule in its own group — a list in the slot
|
|
19
|
+
* of an item of the group has its own. In Menu the panel carries the size, and
|
|
20
|
+
* the group stands in it.
|
|
21
|
+
*
|
|
22
|
+
* The separator is a line across the whole list between sections. The theme
|
|
23
|
+
* draws it and sets its thickness (`--so-group-separator-width`, contract; 0
|
|
24
|
+
* without a theme): the place of a section without a separator counts from it
|
|
25
|
+
* (the sibling rule in the file of the block). Here is what a separator has in
|
|
26
|
+
* common: it does not shrink in the column of the list.
|
|
27
|
+
*
|
|
28
|
+
* The look — capitals and colour of the label, colour of the line — is drawn by
|
|
29
|
+
* the theme. */
|
|
30
|
+
|
|
31
|
+
.so-list-box-group__label,
|
|
32
|
+
.so-select-group__label,
|
|
33
|
+
.so-menu-group__label {
|
|
34
|
+
flex-shrink: 0;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
text-overflow: ellipsis;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.so-list-box-group__label:empty,
|
|
41
|
+
.so-select-group__label:empty,
|
|
42
|
+
.so-menu-group__label:empty {
|
|
43
|
+
display: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.so-list-box-group:has(> .so-list-box-item > .so-button--size-sm) > .so-list-box-group__label,
|
|
47
|
+
.so-select-group:has(> .so-select-item > .so-button--size-sm) > .so-select-group__label,
|
|
48
|
+
.so-menu__panel--size-sm .so-menu-group__label {
|
|
49
|
+
padding-inline: var(--so-button-padding-sm);
|
|
50
|
+
font-size: var(--so-caption-note-font-size-sm);
|
|
51
|
+
line-height: var(--so-caption-note-line-height-sm);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.so-list-box-group:has(> .so-list-box-item > .so-button--size-normal) > .so-list-box-group__label,
|
|
55
|
+
.so-select-group:has(> .so-select-item > .so-button--size-normal) > .so-select-group__label,
|
|
56
|
+
.so-menu__panel--size-normal .so-menu-group__label {
|
|
57
|
+
padding-inline: var(--so-button-padding-normal);
|
|
58
|
+
font-size: var(--so-caption-note-font-size-normal);
|
|
59
|
+
line-height: var(--so-caption-note-line-height-normal);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.so-list-box-group:has(> .so-list-box-item > .so-button--size-lg) > .so-list-box-group__label,
|
|
63
|
+
.so-select-group:has(> .so-select-item > .so-button--size-lg) > .so-select-group__label,
|
|
64
|
+
.so-menu__panel--size-lg .so-menu-group__label {
|
|
65
|
+
padding-inline: var(--so-button-padding-lg);
|
|
66
|
+
font-size: var(--so-caption-note-font-size-lg);
|
|
67
|
+
line-height: var(--so-caption-note-line-height-lg);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.so-list-box-group:has(> .so-list-box-item > .so-button--size-xl) > .so-list-box-group__label,
|
|
71
|
+
.so-select-group:has(> .so-select-item > .so-button--size-xl) > .so-select-group__label,
|
|
72
|
+
.so-menu__panel--size-xl .so-menu-group__label {
|
|
73
|
+
padding-inline: var(--so-button-padding-xl);
|
|
74
|
+
font-size: var(--so-caption-note-font-size-xl);
|
|
75
|
+
line-height: var(--so-caption-note-line-height-xl);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.so-list-box-group:has(> .so-list-box-item > .so-button--size-2xl) > .so-list-box-group__label,
|
|
79
|
+
.so-select-group:has(> .so-select-item > .so-button--size-2xl) > .so-select-group__label,
|
|
80
|
+
.so-menu__panel--size-2xl .so-menu-group__label {
|
|
81
|
+
padding-inline: var(--so-button-padding-2xl);
|
|
82
|
+
font-size: var(--so-caption-note-font-size-2xl);
|
|
83
|
+
line-height: var(--so-caption-note-line-height-2xl);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.so-list-box__separator,
|
|
87
|
+
.so-select__separator,
|
|
88
|
+
.so-menu-separator {
|
|
89
|
+
flex-shrink: 0;
|
|
90
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* ─── The shared layer of themes ─────────────────────────────
|
|
2
|
+
* What the parts of soxa need in any theme, not the look of one: a reset of
|
|
3
|
+
* the library's nodes (`reset.css`), the writing-direction sign
|
|
4
|
+
* (`direction.css`), the fading of a disabled part (`disabled.css`) and the
|
|
5
|
+
* structure of the parts — layout and mechanics (`components/`), whose numbers
|
|
6
|
+
* are variables with defaults: the shared scales (`caption.css`, the marker of
|
|
7
|
+
* a required field — `required.css`, the tap zone of a small button —
|
|
8
|
+
* `target.css`, the label and the separator of groups — `group.css`), what
|
|
9
|
+
* several layers share (a panel at its anchor — `anchored.css`, the swipe
|
|
10
|
+
* strip — `swipe.css`, the surface of the place in panels — `place.css`, the
|
|
11
|
+
* modal layer — `modal.css`) and the scales of blocks in their files. The look
|
|
12
|
+
* — color, borders, radii, shadows, views and motion — is the theme's. Plain
|
|
13
|
+
* CSS, with no preprocessor and no Tailwind: a theme takes the file as is, with
|
|
14
|
+
* its own bundler, and the whole of this layer goes into its CSS.
|
|
15
|
+
*
|
|
16
|
+
* The rules live in the layer `so`: a layer name is shared by the whole
|
|
17
|
+
* document, like a class name, and so it carries the prefix. Each part is a
|
|
18
|
+
* sublayer of its own — `so.<file>` — and the statement below declares their
|
|
19
|
+
* order before any rule: a sublayer named later wins over an earlier one
|
|
20
|
+
* whatever the specificity, and a rule of one part never overrides a rule of
|
|
21
|
+
* another by its weight or by its place in the file. The order stays the same
|
|
22
|
+
* however a theme splits this layer into files and in whatever order an
|
|
23
|
+
* application loads them (`AGENTS.md` of the package, «Слои»). The reset is the
|
|
24
|
+
* first sublayer: every other rule of this layer overrides it. The rules of a
|
|
25
|
+
* theme in its own later layer, or outside layers, override this whole layer.
|
|
26
|
+
*
|
|
27
|
+
* The order of the parts: the shared mechanics and scales first, then a part
|
|
28
|
+
* before the parts that hold it and set its nodes. The clear square and the
|
|
29
|
+
* header buttons of a window take the scale padding off a button with the same
|
|
30
|
+
* weight, the paging buttons and the title of Calendar, the buttons of
|
|
31
|
+
* DateInput and DatePicker, the arrows of Pagination and the close button of a
|
|
32
|
+
* toast take its padding and height — so all of them stand after
|
|
33
|
+
* `button.css`; the modal layer stands right before the window for that
|
|
34
|
+
* reason, not among the shared scales. Calendar sets its month and year picker
|
|
35
|
+
* — a Popover with a ListBox inside — over the rules of those two, so it and
|
|
36
|
+
* DatePicker, which holds it, stand after Popover and ListBox.
|
|
37
|
+
*
|
|
38
|
+
* A theme takes this layer by the name of the package: `@import
|
|
39
|
+
* '@soxa-ui/theme-base';` — the layer `so` stands where it was imported. A
|
|
40
|
+
* theme on Tailwind puts it into Tailwind's layer `base`, in place of
|
|
41
|
+
* preflight (`@import '@soxa-ui/theme-base' layer(base);`, as oren does): its
|
|
42
|
+
* order against the utilities of the application's Tailwind is the one of
|
|
43
|
+
* preflight. The layer order of an application that loads this layer itself —
|
|
44
|
+
* the README of the package. */
|
|
45
|
+
|
|
46
|
+
@layer so.reset, so.direction, so.disabled, so.caption, so.required, so.target, so.group,
|
|
47
|
+
so.anchored, so.swipe, so.place, so.icon, so.button, so.check-box, so.accordion, so.date-input,
|
|
48
|
+
so.modal, so.dialog, so.drag-and-drop, so.drawer, so.input, so.label, so.field, so.list-box,
|
|
49
|
+
so.menu, so.number-input, so.pagination, so.popover, so.calendar, so.date-picker,
|
|
50
|
+
so.progress-linear, so.progress-spinner, so.radio-group, so.rating, so.scroller, so.select,
|
|
51
|
+
so.skeleton, so.slider, so.switch, so.table, so.tabs, so.tags, so.textarea, so.toasts,
|
|
52
|
+
so.tooltip;
|
|
53
|
+
|
|
54
|
+
@import './reset.css' layer(so.reset);
|
|
55
|
+
@import './direction.css' layer(so.direction);
|
|
56
|
+
@import './disabled.css' layer(so.disabled);
|
|
57
|
+
@import './caption.css' layer(so.caption);
|
|
58
|
+
@import './required.css' layer(so.required);
|
|
59
|
+
@import './target.css' layer(so.target);
|
|
60
|
+
@import './group.css' layer(so.group);
|
|
61
|
+
@import './anchored.css' layer(so.anchored);
|
|
62
|
+
@import './swipe.css' layer(so.swipe);
|
|
63
|
+
@import './place.css' layer(so.place);
|
|
64
|
+
@import './components/icon.css' layer(so.icon);
|
|
65
|
+
@import './components/button.css' layer(so.button);
|
|
66
|
+
@import './components/check-box.css' layer(so.check-box);
|
|
67
|
+
@import './components/accordion.css' layer(so.accordion);
|
|
68
|
+
@import './components/date-input.css' layer(so.date-input);
|
|
69
|
+
@import './modal.css' layer(so.modal);
|
|
70
|
+
@import './components/dialog.css' layer(so.dialog);
|
|
71
|
+
@import './components/drag-and-drop.css' layer(so.drag-and-drop);
|
|
72
|
+
@import './components/drawer.css' layer(so.drawer);
|
|
73
|
+
@import './components/input.css' layer(so.input);
|
|
74
|
+
@import './components/label.css' layer(so.label);
|
|
75
|
+
@import './components/field.css' layer(so.field);
|
|
76
|
+
@import './components/list-box.css' layer(so.list-box);
|
|
77
|
+
@import './components/menu.css' layer(so.menu);
|
|
78
|
+
@import './components/number-input.css' layer(so.number-input);
|
|
79
|
+
@import './components/pagination.css' layer(so.pagination);
|
|
80
|
+
@import './components/popover.css' layer(so.popover);
|
|
81
|
+
@import './components/calendar.css' layer(so.calendar);
|
|
82
|
+
@import './components/date-picker.css' layer(so.date-picker);
|
|
83
|
+
@import './components/progress-linear.css' layer(so.progress-linear);
|
|
84
|
+
@import './components/progress-spinner.css' layer(so.progress-spinner);
|
|
85
|
+
@import './components/radio-group.css' layer(so.radio-group);
|
|
86
|
+
@import './components/rating.css' layer(so.rating);
|
|
87
|
+
@import './components/scroller.css' layer(so.scroller);
|
|
88
|
+
@import './components/select.css' layer(so.select);
|
|
89
|
+
@import './components/skeleton.css' layer(so.skeleton);
|
|
90
|
+
@import './components/slider.css' layer(so.slider);
|
|
91
|
+
@import './components/switch.css' layer(so.switch);
|
|
92
|
+
@import './components/table.css' layer(so.table);
|
|
93
|
+
@import './components/tabs.css' layer(so.tabs);
|
|
94
|
+
@import './components/tags.css' layer(so.tags);
|
|
95
|
+
@import './components/textarea.css' layer(so.textarea);
|
|
96
|
+
@import './components/toasts.css' layer(so.toasts);
|
|
97
|
+
@import './components/tooltip.css' layer(so.tooltip);
|
package/src/modal.css
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/* ─── Модальный слой ─────────────────────────────────────────
|
|
2
|
+
* Общее у модального окна (Dialog) и выезжающей панели (Drawer). Анатомия у
|
|
3
|
+
* них одна: корень — сама панель, подложка — её сосед, раньше в DOM, а внутри
|
|
4
|
+
* панели — заголовок, тело, подвал и кнопки шапки, которые стоят в DOM
|
|
5
|
+
* последними, а в ряд заголовка их переносят области сетки. Своё у
|
|
6
|
+
* компонента — место и размер, колонки и строки сетки и состав кнопок шапки
|
|
7
|
+
* (`components/dialog.css`, `components/drawer.css`).
|
|
8
|
+
*
|
|
9
|
+
* .so-dialog__backdrop, .so-drawer__backdrop подложка
|
|
10
|
+
* .so-dialog, .so-drawer панель — сетка
|
|
11
|
+
* .so-…__title заголовок, `h2`
|
|
12
|
+
* .so-…__body тело — прокручивается оно
|
|
13
|
+
* .so-…__footer подвал — ряд действий
|
|
14
|
+
* .so-dialog__maximize, .so-…__close кнопки шапки — Button
|
|
15
|
+
*
|
|
16
|
+
* Подложка — на весь экран, под панелью. Она обязательная разметка, а не
|
|
17
|
+
* украшение: фон под панелью скрыт от скринридера, но не от указателя, и к
|
|
18
|
+
* странице его не пускает только она. Нажатие по ней плагины слоя считают
|
|
19
|
+
* нажатием мимо панели. `z-index` подложке и панели пишет код, одним числом:
|
|
20
|
+
* панель рисуется поверх просто порядком.
|
|
21
|
+
*
|
|
22
|
+
* Прокручивается тело, а не панель, поэтому шапка, подвал и кнопки остаются на
|
|
23
|
+
* месте: панель — сетка, и строка тела сжимается до остатка высоты. Отступ —
|
|
24
|
+
* внутри прокрутки: у края прокручиваемой области обрезалось бы кольцо фокуса
|
|
25
|
+
* вложенного контрола. Прокрутка до конца не переходит на страницу под
|
|
26
|
+
* панелью, а длинное слово переносится, а не заводит полосу по горизонтали.
|
|
27
|
+
* Пустые заголовок и подвал места не занимают: тело тогда начинается или
|
|
28
|
+
* кончается у края панели полным отступом.
|
|
29
|
+
*
|
|
30
|
+
* Кнопки шапки — квадрат высоты Button своего размера (`normal`: размера у них
|
|
31
|
+
* нет), в конце строки заголовка, каждая по центру его первой строки:
|
|
32
|
+
* заголовок переносится — кнопки остаются наверху. Строка заголовка —
|
|
33
|
+
* переменная, и от неё считается место кнопок: разойдись они, кнопка съехала
|
|
34
|
+
* бы с заголовка молча. Промежуток между кнопками держит первая из них, а
|
|
35
|
+
* угол — последняя: её значок отстоит от края панели на тот же отступ, что
|
|
36
|
+
* текст заголовка с другой стороны. Значок в квадрате отстоит от его края на
|
|
37
|
+
* четверть высоты, поэтому до края панели — отступ без этой четверти.
|
|
38
|
+
*
|
|
39
|
+
* Таблица прямо в теле — её окно (`.so-table__viewport`) прямой ребёнок тела:
|
|
40
|
+
* закреплённая шапка встаёт к краю тела, а не под отступ
|
|
41
|
+
* (`--so-table-head-offset` — минус отступ сверху, таблица наследует его от
|
|
42
|
+
* окна). Закреплённое браузер ставит под отступ контейнера, и в отступе над
|
|
43
|
+
* шапкой проступали бы строки, которые уходят под неё. Только прямой ребёнок:
|
|
44
|
+
* глубже у таблицы бывает свой контейнер прокрутки, и сдвиг тела унёс бы её
|
|
45
|
+
* шапку за его край. Контекст нулевой специфичности — свой сдвиг таблицы
|
|
46
|
+
* сильнее.
|
|
47
|
+
*
|
|
48
|
+
* Отступы, промежутки и строка заголовка — переменные корня документа;
|
|
49
|
+
* умолчания — числа темы oren, тема задаёт свои на корне документа.
|
|
50
|
+
*
|
|
51
|
+
* Облик — вуаль подложки, поверхность, кромка, тень и скругление панели, кегль
|
|
52
|
+
* и начертание заголовка, вид кнопок шапки, проявление, въезд и жест — рисует
|
|
53
|
+
* тема. */
|
|
54
|
+
|
|
55
|
+
:root {
|
|
56
|
+
--so-modal-padding: 1.25rem;
|
|
57
|
+
--so-modal-gap: 0.5rem;
|
|
58
|
+
--so-modal-title-line: 1.75rem;
|
|
59
|
+
--so-modal-footer-gap: 0.5rem;
|
|
60
|
+
--so-modal-button-gap: 0.25rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.so-dialog__backdrop,
|
|
64
|
+
.so-drawer__backdrop {
|
|
65
|
+
position: fixed;
|
|
66
|
+
inset: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.so-dialog,
|
|
70
|
+
.so-drawer {
|
|
71
|
+
display: grid;
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Заголовок рисуется всегда: на него панель ссылается `aria-labelledby`. */
|
|
76
|
+
.so-dialog__title,
|
|
77
|
+
.so-drawer__title {
|
|
78
|
+
grid-area: title;
|
|
79
|
+
padding: var(--so-modal-padding) var(--so-modal-padding) 0;
|
|
80
|
+
line-height: var(--so-modal-title-line);
|
|
81
|
+
overflow-wrap: break-word;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.so-dialog__title:empty,
|
|
85
|
+
.so-drawer__title:empty {
|
|
86
|
+
display: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.so-dialog__body,
|
|
90
|
+
.so-drawer__body {
|
|
91
|
+
grid-area: body;
|
|
92
|
+
min-height: 0;
|
|
93
|
+
padding: var(--so-modal-gap) var(--so-modal-padding);
|
|
94
|
+
overflow-y: auto;
|
|
95
|
+
overscroll-behavior: contain;
|
|
96
|
+
overflow-wrap: break-word;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
:where(.so-dialog__body > .so-table__viewport, .so-drawer__body > .so-table__viewport) {
|
|
100
|
+
--so-table-head-offset: calc(-1 * var(--so-modal-gap));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Подвал — ряд действий в конце строки, в RTL он сам встаёт слева. */
|
|
104
|
+
.so-dialog__footer,
|
|
105
|
+
.so-drawer__footer {
|
|
106
|
+
grid-area: footer;
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-wrap: wrap;
|
|
109
|
+
align-items: center;
|
|
110
|
+
justify-content: flex-end;
|
|
111
|
+
gap: var(--so-modal-footer-gap);
|
|
112
|
+
padding: calc(var(--so-modal-gap) * 1.5) var(--so-modal-padding) var(--so-modal-padding);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.so-dialog__footer:empty,
|
|
116
|
+
.so-drawer__footer:empty {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Шапки нет — ни заголовка, ни кнопок: тело начинается от верхнего края
|
|
121
|
+
* панели полным отступом, а не промежутком после шапки. */
|
|
122
|
+
.so-dialog:not(:has(> .so-dialog__title:not(:empty), > .so-dialog__maximize, > .so-dialog__close))
|
|
123
|
+
> .so-dialog__body,
|
|
124
|
+
.so-drawer:not(:has(> .so-drawer__title:not(:empty), > .so-drawer__close)) > .so-drawer__body {
|
|
125
|
+
padding-block-start: var(--so-modal-padding);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
:where(
|
|
129
|
+
.so-dialog:not(
|
|
130
|
+
:has(> .so-dialog__title:not(:empty), > .so-dialog__maximize, > .so-dialog__close)
|
|
131
|
+
)
|
|
132
|
+
> .so-dialog__body
|
|
133
|
+
> .so-table__viewport,
|
|
134
|
+
.so-drawer:not(:has(> .so-drawer__title:not(:empty), > .so-drawer__close))
|
|
135
|
+
> .so-drawer__body
|
|
136
|
+
> .so-table__viewport
|
|
137
|
+
) {
|
|
138
|
+
--so-table-head-offset: calc(-1 * var(--so-modal-padding));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Подвала нет — тело кончается у нижнего края панели полным отступом. */
|
|
142
|
+
.so-dialog:has(> .so-dialog__footer:empty) > .so-dialog__body,
|
|
143
|
+
.so-drawer:has(> .so-drawer__footer:empty) > .so-drawer__body {
|
|
144
|
+
padding-block-end: var(--so-modal-padding);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.so-dialog__maximize,
|
|
148
|
+
.so-dialog__close,
|
|
149
|
+
.so-drawer__close {
|
|
150
|
+
flex-shrink: 0;
|
|
151
|
+
align-self: flex-start;
|
|
152
|
+
aspect-ratio: 1;
|
|
153
|
+
padding-inline: 0;
|
|
154
|
+
margin-block-start: calc(
|
|
155
|
+
var(--so-modal-padding) + (var(--so-modal-title-line) - var(--so-button-height-normal)) / 2
|
|
156
|
+
);
|
|
157
|
+
margin-inline-end: var(--so-modal-button-gap);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.so-dialog__maximize:last-child,
|
|
161
|
+
.so-dialog__close:last-child,
|
|
162
|
+
.so-drawer__close:last-child {
|
|
163
|
+
margin-inline-end: calc(var(--so-modal-padding) - var(--so-button-height-normal) / 4);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Значок — по центру квадрата. Иконка блочная (`icon.css`), и выравнивание
|
|
167
|
+
* текста области её не двигает; флекс центрует и значок из слота
|
|
168
|
+
* потребителя, что бы в нём ни лежало. */
|
|
169
|
+
.so-dialog__maximize > .so-button__text,
|
|
170
|
+
.so-dialog__close > .so-button__text,
|
|
171
|
+
.so-drawer__close > .so-button__text {
|
|
172
|
+
display: flex;
|
|
173
|
+
justify-content: center;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.so-dialog__maximize {
|
|
177
|
+
grid-area: maximize;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.so-dialog__close,
|
|
181
|
+
.so-drawer__close {
|
|
182
|
+
grid-area: close;
|
|
183
|
+
}
|
package/src/place.css
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* ─── Поверхность места ──────────────────────────────────────
|
|
2
|
+
* Фон своего места — того, на чём стоят компоненты внутри, — место объявляет
|
|
3
|
+
* переменной `--so-place-surface`, а читает её тот, кому нужен непрозрачный
|
|
4
|
+
* фон «как под ним»: закреплённой шапке Table нужен фон места, иначе под ней
|
|
5
|
+
* просвечивают строки, которые уходят под шапку. Вуаль не годится — сквозь
|
|
6
|
+
* неё видно то, что под ней, — а мест не перечислить.
|
|
7
|
+
*
|
|
8
|
+
* Панели оверлеев — Popover, Select, DatePicker, панель Menu и панель его
|
|
9
|
+
* пункта, окно Dialog и панель Drawer, — карточка выбора месяца Calendar и
|
|
10
|
+
* плашка тоста объявляют своё место здесь: поверхность места внутри панели —
|
|
11
|
+
* поверхность самой панели (`--so-panel-surface`, контракт). Её задаёт тема —
|
|
12
|
+
* на панели, там же, где красит ею фон: фон и сообщение о нём читают одну
|
|
13
|
+
* переменную, и разойтись им нечему. Без темы поверхности у панели нет, и
|
|
14
|
+
* поверхности места внутри неё тоже.
|
|
15
|
+
*
|
|
16
|
+
* Страницу (корень документа) и карточку приложения объявляют тема и
|
|
17
|
+
* приложение. Список — панели и карточки библиотеки; новая панель встаёт в
|
|
18
|
+
* этот список. */
|
|
19
|
+
|
|
20
|
+
.so-popover__panel,
|
|
21
|
+
.so-select__panel,
|
|
22
|
+
.so-date-picker__panel,
|
|
23
|
+
.so-calendar__picker,
|
|
24
|
+
.so-menu__panel,
|
|
25
|
+
.so-menu-item__panel,
|
|
26
|
+
.so-dialog,
|
|
27
|
+
.so-drawer,
|
|
28
|
+
.so-toasts-item__card {
|
|
29
|
+
--so-place-surface: var(--so-panel-surface);
|
|
30
|
+
}
|
package/src/required.css
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* ─── Маркер обязательного поля ──────────────────────────────
|
|
2
|
+
* Точка у угла контрола — у начала строки, а не слева: сторона логическая
|
|
3
|
+
* (`inset-inline-start`), и в RTL маркер стоит справа, у начала поля. Левым
|
|
4
|
+
* он был у каждого контрола, и в RTL съезжал к концу. Здесь — место и размер
|
|
5
|
+
* точки; цвет и форму рисует тема.
|
|
6
|
+
*
|
|
7
|
+
* Маркер у поля один. В поле формы `Field` с подписью он переезжает к
|
|
8
|
+
* подписи (`components/field.css`), а точку контрола гасит переменная
|
|
9
|
+
* `--so-required-dot: none` на обёртке контрола. Переменной, а не
|
|
10
|
+
* селектором: селектор перечислял бы контролы поимённо, а точка у каждого
|
|
11
|
+
* стоит на своём узле — на корне Switch и полей семьи Input, на коробке
|
|
12
|
+
* CheckBox. У диапазона DatePicker точка — у коробки на корне, а у полей
|
|
13
|
+
* концов внутри неё её нет (`components/date-picker.css`); у одной даты она —
|
|
14
|
+
* у самого поля, DateInput.
|
|
15
|
+
*
|
|
16
|
+
* Список — контролы с маркером; место точки у своего контрола уточняет файл
|
|
17
|
+
* его блока (у Switch оно ближе к углу). Новый контрол с маркером встаёт в
|
|
18
|
+
* этот список. */
|
|
19
|
+
|
|
20
|
+
:root {
|
|
21
|
+
--so-required-size: 0.25rem;
|
|
22
|
+
--so-required-offset: -0.25rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.so-check-box:where([data-required='true']) .so-check-box__container::after,
|
|
26
|
+
.so-switch:where([data-required='true'])::after,
|
|
27
|
+
.so-input:where([data-required='true'])::after,
|
|
28
|
+
.so-number-input:where([data-required='true'])::after,
|
|
29
|
+
.so-textarea:where([data-required='true'])::after,
|
|
30
|
+
.so-date-input:where([data-required='true'])::after,
|
|
31
|
+
.so-date-picker--mode-range.so-date-picker:where([data-required='true'])::after {
|
|
32
|
+
content: '';
|
|
33
|
+
display: var(--so-required-dot, block);
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: var(--so-required-offset);
|
|
36
|
+
inset-inline-start: var(--so-required-offset);
|
|
37
|
+
width: var(--so-required-size);
|
|
38
|
+
height: var(--so-required-size);
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|