@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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +399 -2
  3. package/dist/setup/index.d.ts +31 -0
  4. package/dist/setup/index.js +4 -0
  5. package/dist/setup/plugins/index.d.ts +3 -0
  6. package/dist/setup/plugins/table-head.plugin.d.ts +44 -0
  7. package/dist/setup/plugins/table-head.plugin.js +86 -0
  8. package/dist/setup/plugins/tabs-view.plugin.d.ts +32 -0
  9. package/dist/setup/plugins/tabs-view.plugin.js +100 -0
  10. package/dist/setup/plugins/toasts-front.plugin.d.ts +63 -0
  11. package/dist/setup/plugins/toasts-front.plugin.js +131 -0
  12. package/dist/setup/plugins/transitions.d.ts +43 -0
  13. package/dist/setup/plugins/transitions.js +75 -0
  14. package/package.json +55 -4
  15. package/src/anchored.css +75 -0
  16. package/src/caption.css +71 -0
  17. package/src/components/accordion.css +96 -0
  18. package/src/components/button.css +139 -0
  19. package/src/components/calendar.css +594 -0
  20. package/src/components/check-box.css +121 -0
  21. package/src/components/date-input.css +224 -0
  22. package/src/components/date-picker.css +208 -0
  23. package/src/components/dialog.css +95 -0
  24. package/src/components/drag-and-drop.css +18 -0
  25. package/src/components/drawer.css +171 -0
  26. package/src/components/field.css +440 -0
  27. package/src/components/icon.css +61 -0
  28. package/src/components/input.css +189 -0
  29. package/src/components/label.css +160 -0
  30. package/src/components/list-box.css +165 -0
  31. package/src/components/menu.css +224 -0
  32. package/src/components/number-input.css +183 -0
  33. package/src/components/pagination.css +164 -0
  34. package/src/components/popover.css +164 -0
  35. package/src/components/progress-linear.css +88 -0
  36. package/src/components/progress-spinner.css +127 -0
  37. package/src/components/radio-group.css +130 -0
  38. package/src/components/rating.css +141 -0
  39. package/src/components/scroller.css +136 -0
  40. package/src/components/select.css +233 -0
  41. package/src/components/skeleton.css +33 -0
  42. package/src/components/slider.css +482 -0
  43. package/src/components/switch.css +142 -0
  44. package/src/components/table.css +1513 -0
  45. package/src/components/tabs.css +166 -0
  46. package/src/components/tags.css +371 -0
  47. package/src/components/textarea.css +179 -0
  48. package/src/components/toasts.css +447 -0
  49. package/src/components/tooltip.css +59 -0
  50. package/src/direction.css +30 -0
  51. package/src/disabled.css +26 -0
  52. package/src/group.css +90 -0
  53. package/src/index.css +97 -0
  54. package/src/modal.css +183 -0
  55. package/src/place.css +30 -0
  56. package/src/required.css +40 -0
  57. package/src/reset.css +172 -0
  58. package/src/swipe.css +71 -0
  59. package/src/target.css +46 -0
  60. package/theme-contract.json +3373 -0
  61. package/theme-variables.json +336 -0
package/src/reset.css ADDED
@@ -0,0 +1,172 @@
1
+ /* ─── Сброс узлов библиотеки ─────────────────────────────────
2
+ * Тема не стилизует страницу (корневой `AGENTS.md`, «Темы: у пакета темы
3
+ * свои инструкции»): глобального сброса у неё нет, а то, на что опираются
4
+ * части soxa, даёт этот сброс — только узлам библиотеки. Нужен он частям, а
5
+ * не облику, поэтому он в общем слое, а не в теме.
6
+ *
7
+ * Выведен из `tailwindcss/preflight.css` правило за правилом: на preflight
8
+ * части опирались, пока тема oren подключала Tailwind целиком. Правило,
9
+ * которое задевает узел с классом `so-*` или его псевдоэлемент, перенесено
10
+ * тем же селектором, суженным до таких узлов, с нулевой специфичностью
11
+ * (`:where`): правила темы перекрывают его, как перекрывали preflight. Узел
12
+ * библиотеки — элемент, у которого класс начинается с `so-`
13
+ * (`[class^='so-'], [class*=' so-']`): у каждого узла разметки библиотеки
14
+ * такой класс есть. Внутренние узлы без класса сброс называет через класс их
15
+ * компонента: поле `<input>` у Input, CheckBox и Switch, `<textarea>` у
16
+ * Textarea, `<svg>` рисунка в иконке Web Components (`<span class="so-icon">`)
17
+ * и группы строк сетки Calendar (`thead`, `tbody`). Узлы самого рисунка
18
+ * иконки (`g`, `path`) сброс не называет: свойства коробки рисунок SVG не
19
+ * читает, а свой рисунок в иконке бывает и у приложения (`<svg so-icon>` с
20
+ * путями у Angular).
21
+ *
22
+ * Чего здесь нет — намеренно:
23
+ * - правил `html`, `:host` и `body`: шрифт, высота строки, `tab-size`,
24
+ * подсветка касания и отступ `body` — типографика страницы, дело
25
+ * приложения, и части берут шрифт приложения;
26
+ * - правил на всё внутри части (`.so-x *`): содержимое слотов — узлы
27
+ * приложения;
28
+ * - правил тегов, которых узлами библиотеки не бывает (`hr`, `abbr`, `b`,
29
+ * `code`, `small`, `sub`, `img`, `select`, `summary`, `progress`…), и
30
+ * псевдоэлементов, которых у них нет: `::backdrop` (верхнего слоя
31
+ * библиотека не берёт), `::file-selector-button`, спиннеров числа и
32
+ * частей поля даты (полей `file`, `number`, `date` и `search` у неё нет).
33
+ * Ссылка `a` — узел библиотеки: её тегом корня ставит потребитель
34
+ * (`<Button tag="a">`).
35
+ *
36
+ * Слой — `so.reset`, нижний в слое базы (`index.css`): остальные правила
37
+ * базы и правила темы встают над ним без спора специфичностью. Сторож того,
38
+ * что тема не задевает страницу, — `__tests__/sealed.spec.ts`; что облик
39
+ * частей не изменился — `npm run compare:styles` (`packages/playground/AGENTS.md`,
40
+ * «Сравнение стилей»). */
41
+
42
+ /* Коробка от рамки, без полей и отступов браузера, рамка — сплошная нулевой
43
+ * толщины: ширину рамки задаёт тема, и рамка сразу видна. */
44
+ :where([class^='so-'], [class*=' so-']),
45
+ :where([class^='so-'], [class*=' so-'])::before,
46
+ :where([class^='so-'], [class*=' so-'])::after,
47
+ :where(
48
+ .so-input > input,
49
+ .so-check-box > input,
50
+ .so-switch > input,
51
+ .so-textarea > textarea,
52
+ .so-icon > svg,
53
+ .so-calendar__grid > thead,
54
+ .so-calendar__grid > tbody
55
+ ) {
56
+ box-sizing: border-box;
57
+ margin: 0;
58
+ padding: 0;
59
+ border: 0 solid;
60
+ }
61
+
62
+ /* Ссылка — цвета и оформления текста места, как кнопка. */
63
+ :where(a:is([class^='so-'], [class*=' so-'])) {
64
+ color: inherit;
65
+ -webkit-text-decoration: inherit;
66
+ text-decoration: inherit;
67
+ }
68
+
69
+ /* Заголовок окна и выезжающей панели — кеглем и начертанием места: их задаёт
70
+ * тема. */
71
+ :where(h2:is([class^='so-'], [class*=' so-'])) {
72
+ font-size: inherit;
73
+ font-weight: inherit;
74
+ }
75
+
76
+ /* Таблица и сетка Calendar: без отступа строки, цвет рамки — от места, рамки
77
+ * ячеек слиты. */
78
+ :where(table:is([class^='so-'], [class*=' so-'])) {
79
+ text-indent: 0;
80
+ border-color: inherit;
81
+ border-collapse: collapse;
82
+ }
83
+
84
+ /* Кольцо фокуса Firefox — нынешнее, `auto`: там, где кольцо не рисует тема.
85
+ * Другие браузеры этот псевдокласс не знают, и `:where` для них пуст. */
86
+ :where(:is([class^='so-'], [class*=' so-']):-moz-focusring) {
87
+ outline: auto;
88
+ }
89
+
90
+ /* Списки Pagination и Toasts — без маркеров. */
91
+ :where(ol:is([class^='so-'], [class*=' so-'])) {
92
+ list-style: none;
93
+ }
94
+
95
+ /* Рисунок — блоком, а не строкой текста: без зазора под базовой линией. */
96
+ :where(svg:is([class^='so-'], [class*=' so-']), .so-icon > svg) {
97
+ display: block;
98
+ vertical-align: middle;
99
+ }
100
+
101
+ /* Кнопки и поля — шрифтом и цветом места, без фона, скругления и
102
+ * прозрачности браузера. */
103
+ :where(
104
+ button:is([class^='so-'], [class*=' so-']),
105
+ input:is([class^='so-'], [class*=' so-']),
106
+ textarea:is([class^='so-'], [class*=' so-']),
107
+ .so-input > input,
108
+ .so-check-box > input,
109
+ .so-switch > input,
110
+ .so-textarea > textarea
111
+ ) {
112
+ font: inherit;
113
+ font-feature-settings: inherit;
114
+ font-variation-settings: inherit;
115
+ letter-spacing: inherit;
116
+ color: inherit;
117
+ border-radius: 0;
118
+ background-color: transparent;
119
+ opacity: 1;
120
+ }
121
+
122
+ /* Подсказка пустого поля — полупрозрачный текст поля, а не серый браузера.
123
+ * Смесь — в браузерах, которые с `currentcolor` в ней не падают (Safari до 17
124
+ * падал). */
125
+ :where(
126
+ input:is([class^='so-'], [class*=' so-']),
127
+ textarea:is([class^='so-'], [class*=' so-']),
128
+ .so-input > input,
129
+ .so-textarea > textarea
130
+ )::placeholder {
131
+ opacity: 1;
132
+ }
133
+
134
+ @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
135
+ :where(
136
+ input:is([class^='so-'], [class*=' so-']),
137
+ textarea:is([class^='so-'], [class*=' so-']),
138
+ .so-input > input,
139
+ .so-textarea > textarea
140
+ )::placeholder {
141
+ color: color-mix(in oklab, currentcolor 50%, transparent);
142
+ }
143
+ }
144
+
145
+ /* Многострочное поле тянется только по высоте. */
146
+ :where(textarea:is([class^='so-'], [class*=' so-']), .so-textarea > textarea) {
147
+ resize: vertical;
148
+ }
149
+
150
+ /* Без красной тени Firefox у невалидного поля: ошибку рисует тема. */
151
+ :where(
152
+ :is(
153
+ input:is([class^='so-'], [class*=' so-']),
154
+ textarea:is([class^='so-'], [class*=' so-']),
155
+ .so-input > input,
156
+ .so-check-box > input,
157
+ .so-switch > input,
158
+ .so-textarea > textarea
159
+ ):-moz-ui-invalid
160
+ ) {
161
+ box-shadow: none;
162
+ }
163
+
164
+ /* Скругление кнопки в Safari на iOS. */
165
+ :where(button:is([class^='so-'], [class*=' so-'])) {
166
+ appearance: button;
167
+ }
168
+
169
+ /* `hidden` на части прячет её и тогда, когда тема задаёт ей `display`. */
170
+ :where(:is([class^='so-'], [class*=' so-'])[hidden]:not([hidden='until-found'])) {
171
+ display: none !important;
172
+ }
package/src/swipe.css ADDED
@@ -0,0 +1,71 @@
1
+ /* ─── Полоса жеста ───────────────────────────────────────────
2
+ * «Смахнуть, чтобы закрыть» — у слоёв, которые смахивают: панелей Popover,
3
+ * Select и DatePicker, выезжающей панели Drawer. Полосу, за которую тянут,
4
+ * рисует разметка прямым потомком панели, пока жест включён (`swipe`), под
5
+ * `aria-hidden`: сама она ничего не делает, тянут её жестом.
6
+ *
7
+ * Зона захвата — сторона в минимум цели указателя (`--so-target-size`,
8
+ * `target.css`): полосу берут пальцем. Вдоль края она во всю его длину, и
9
+ * цель не меньше квадрата минимума. Где зона стоит, решает слой — край
10
+ * панели у якоря (`anchored.css`), край панели Popover в контейнере
11
+ * (`components/popover.css`) и низ карточки выбора месяца Calendar
12
+ * (`components/calendar.css`), край Drawer (`components/drawer.css`); здесь
13
+ * — то, что у полосы одно: курсор «можно взять» и штрих посередине зоны.
14
+ *
15
+ * Штрих — псевдоэлемент полосы: здесь его место и размер, вдоль края. У
16
+ * бокового края он поперёк строки — длину и толщину меняет правило края у
17
+ * слоя. Длина и толщина — переменные корня документа; умолчания — числа темы
18
+ * oren, тема задаёт свои на корне документа.
19
+ *
20
+ * Сдвиг во время жеста плагин жеста пишет инлайном в слой, который тянут, —
21
+ * переменной `--so-swipe-offset` (px со знаком оси окна), — а в `translate`
22
+ * его кладёт база, у каждого слоя в его файле: слой идёт за указателем в
23
+ * любом режиме движения, это его место, а не переход.
24
+ *
25
+ * Сдвиг принадлежит слою, который тянут, и другой слой его не наследует:
26
+ * свой ноль у каждого слоя с жестом объявлен здесь, а инлайн плагина у того,
27
+ * который тянут, его перекрывает. Слой, открытый внутри смахиваемой панели
28
+ * (`contained`), иначе взял бы её сдвиг себе в `translate` и уехал вдвое.
29
+ * Регистрация `@property` с `inherits: false` дала бы то же не во всех
30
+ * браузерах темы: Firefox до 128 её не знает, а полифил Tailwind регистрацию
31
+ * в слое не видит. Потомок, который не слой, сдвиг наследует — так карточка
32
+ * выбора месяца Calendar идёт за жестом своей панели
33
+ * (`components/calendar.css`).
34
+ *
35
+ * Облик — цвет и форма штриха, его вид в режиме принудительных цветов — и
36
+ * движение — возврат отпущенного слоя, уход смахнутого и въезд — рисует
37
+ * тема. */
38
+
39
+ .so-popover__panel,
40
+ .so-select__panel,
41
+ .so-date-picker__panel,
42
+ .so-drawer,
43
+ .so-toasts-item {
44
+ --so-swipe-offset: 0px;
45
+ }
46
+
47
+ :root {
48
+ --so-swipe-grip-length: 2.25rem;
49
+ --so-swipe-grip-thickness: 0.25rem;
50
+ }
51
+
52
+ .so-popover__handle,
53
+ .so-select__handle,
54
+ .so-date-picker__handle,
55
+ .so-drawer__handle {
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ cursor: grab;
60
+ }
61
+
62
+ .so-popover__handle::before,
63
+ .so-select__handle::before,
64
+ .so-date-picker__handle::before,
65
+ .so-drawer__handle::before {
66
+ content: '';
67
+ display: block;
68
+ flex-shrink: 0;
69
+ inline-size: var(--so-swipe-grip-length);
70
+ block-size: var(--so-swipe-grip-thickness);
71
+ }
package/src/target.css ADDED
@@ -0,0 +1,46 @@
1
+ /* ─── Зона нажатия мелкой кнопки ─────────────────────────────
2
+ * Минимум цели указателя — 24 px (WCAG 2.5.8, AA). Кнопки в строке поля —
3
+ * очистка Input, NumberInput и DateInput, кнопки шага NumberInput, стрелка
4
+ * Select, очистка и кнопка календаря DatePicker — на мелких размерах меньше
5
+ * него: строка слота на `sm` — 20 px. Цель им добирает зона — невидимый
6
+ * квадрат по центру кнопки со стороной в минимум цели: нажатие по ней —
7
+ * нажатие по кнопке, а сама кнопка выглядит по-прежнему. Зона — псевдоэлемент
8
+ * кнопки и стоит от неё, а кнопка позиционирована (`relative` у Button,
9
+ * `button.css`; стрелка Select — не кнопка, а обёртка слота, и `relative` ей
10
+ * ставит её правило, `components/select.css`). Зона симметрична: зеркало под
11
+ * RTL ей не нужно, а поворот стрелки Select её не сдвигает.
12
+ *
13
+ * С `normal` кнопка не меньше зоны сама. На `sm` зона выходит за кнопку на
14
+ * 2 px с каждой стороны: по высоте — в просвет слота до рамки, по строке — в
15
+ * поле слота у края поля или в зазор до соседа в слоте. Соседа без зоны
16
+ * (кнопку потребителя в слоте) она не задевает, а с зоной сходится встык —
17
+ * кнопки шага между собой, очистка со стрелкой Select или с кнопкой календаря
18
+ * DatePicker. У начала первой кнопки слота вплотную стоит ввод — поле или ряд
19
+ * частей даты, — и зона заходит на него на те же 2 px: нажатие у самого края
20
+ * ввода достаётся кнопке — очистке, а без неё стрелке Select или кнопке
21
+ * календаря, которые открывают панель.
22
+ *
23
+ * Сторона зоны — переменная корня документа, одна на всех, у кого зона есть:
24
+ * её же берут крестик тега (`components/tags.css`), кнопка сортировки Table
25
+ * (`components/table.css`) и зона захвата полосы жеста (`swipe.css`).
26
+ * Умолчание — число темы oren; тема задаёт своё на корне документа.
27
+ *
28
+ * Список — кнопки в строке поля; у крестика тега и кнопки сортировки Table
29
+ * зона — в файле их блока, своим селектором. */
30
+
31
+ :root {
32
+ --so-target-size: 1.5rem;
33
+ }
34
+
35
+ .so-input__clear::before,
36
+ .so-number-input__clear::before,
37
+ .so-number-input__decrement::before,
38
+ .so-number-input__increment::before,
39
+ .so-select__arrow::before,
40
+ .so-date-input__clear::before,
41
+ .so-date-picker__clear::before,
42
+ .so-date-picker__trigger::before {
43
+ content: '';
44
+ position: absolute;
45
+ inset: calc(50% - var(--so-target-size) / 2);
46
+ }