@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
@@ -0,0 +1,482 @@
1
+ /* ─── Slider ──────────────────────────────────────────────────
2
+ * Ползунок: одна или несколько ручек на рельсе, заливка и метки.
3
+ *
4
+ * .so-slider корень — ось, отступы под края хода и ореол
5
+ * .so-slider__track дорожка — ход центров ручек; рельс — её `::before`
6
+ * .so-slider__range заливка
7
+ * .so-slider__marks метки — все в одной ячейке сетки
8
+ * .so-slider__mark метка — точка на рельсе (`::before`) и подпись
9
+ * .so-slider__mark-label подпись метки
10
+ * .so-slider__thumb ручка
11
+ * .so-slider__input поле — нативный range, прозрачный, во всю ручку
12
+ * .so-slider__tooltip подсказка со значением
13
+ *
14
+ * Всё, что зависит от значения, — позиции — приходит от ядра переменными в
15
+ * `style` узла, процентом от начала оси и уже с учётом `inverted`:
16
+ * `--so-slider-position` у ручки и у метки — где её центр,
17
+ * `--so-slider-range-start` и `--so-slider-range-end` у заливки — её края.
18
+ * Ось раскладывается только логическими свойствами: горизонталь — от начала
19
+ * строки (`inset-inline-start`), и в RTL ползунок разворачивается сам, без
20
+ * селектора по направлению; вертикаль — снизу вверх (`inset-block-end`). Ни
21
+ * одного `translate`: у сдвига нет логических направлений. Центруют поля и
22
+ * выравнивание флекса.
23
+ *
24
+ * Контракт с плагином указателя: коробка `__track` — ход центров ручек, 0 % и
25
+ * 100 % — её края по оси, мерить надо её. На краю хода ручка выступает за
26
+ * коробку на полстороны; рельс лежит и там, но псевдоэлементом — коробку он
27
+ * не сдвигает, а место под полстороны корень держит отступом вдоль оси.
28
+ * Поперёк оси отступ — ореол ручки, треть её стороны: ореол и кольцо фокуса
29
+ * остаются внутри коробки ползунка, и их не срежет прокручиваемый контейнер
30
+ * вокруг, а полоса, в которой нажатие ведёт ручку, выходит выше и ниже самой
31
+ * ручки — пальцу нужна цель крупнее неё. Жест поперёк оси остаётся странице.
32
+ *
33
+ * Горизонтальный — блочный, во всю ширину контейнера: ползунок меряют длиной
34
+ * хода. Вертикальный — строчный: несколько вертикальных встают в ряд, как
35
+ * полосы эквалайзера. Длина по умолчанию — нулевой специфичности: у
36
+ * горизонтального — нижняя граница, та же, что у поля Input
37
+ * (`--so-input-min-width`): в форме они стоят рядом, а своего содержимого по
38
+ * ширине у ползунка нет, и там, где ширину берут по содержимому, он схлопнулся
39
+ * бы в одну ручку; у вертикального длины по содержимому нет вовсе, высота
40
+ * своя.
41
+ *
42
+ * Дорожка в потоке держит только метки — ради подписей: ползунок растёт на них
43
+ * поперёк оси, а не наезжает подписями на соседей. Метки — все в одной ячейке
44
+ * сетки, и ячейка растёт по самой крупной подписи; на свою позицию каждая
45
+ * сдвинута относительно. Метка — нулевой протяжённости вдоль оси: её середина
46
+ * и есть позиция, а точка и подпись встают по центру и выходят в обе стороны
47
+ * поровну. Пустая подпись места не занимает: метка без подписи — одна точка.
48
+ * Кегль подписей — справка при контроле из шкалы подписи (`caption.css`).
49
+ *
50
+ * Свой слой (`isolation`): ручки подняты над заливкой и метками (`z-index`),
51
+ * но поверх соседей по странице этот подъём не выходит. Две ручки на одном
52
+ * значении лежат друг на друге, и сверху та, что тянут или в фокусе: её и
53
+ * видно, её и двигают клавиши. Поле — во всю ручку и прозрачное: остаётся в
54
+ * дереве доступности и в порядке фокуса, а указатель не ловит — иначе
55
+ * браузер начал бы свой перетаск поверх плагина. Нажатие в любом месте
56
+ * ползунка ведёт к нему ближнюю ручку, протяжка не выделяет текст, а пока
57
+ * тянут, рука сжата, где бы над ползунком ни был указатель.
58
+ *
59
+ * Подсказка со значением — плашка подсказки, та же, что у Tooltip
60
+ * (`components/tooltip.css`: поля и строка плашки — его переменные). Она
61
+ * позиционирована, но сдвига вдоль оси у неё нет: остаётся статическое место
62
+ * по центру ручки, и она едет за ручкой по построению. Поперёк оси — за
63
+ * ореолом с зазором подписей, у горизонтального — над ручкой, у вертикального
64
+ * — со стороны начала строки: с конца стоят подписи меток. Отступ считается
65
+ * от края ручки внутри кромки, поэтому кромку, которую рисует тема, он
66
+ * прибавляет (`--so-slider-thumb-border`, контракт; без темы — 0). Строка не
67
+ * переносится: ширина по содержимому у позиционированного узла упирается в
68
+ * ширину ручки. `auto` — подсказка видна, пока ручку наводят, держат, тянут
69
+ * или её поле в фокусе с клавиатуры; в покое скрыта `visibility`: невидимая
70
+ * плашка ловила бы наведение. «Тянут» — это и нажатие ручки: захват указателя
71
+ * уводит наведение на корень, а `data-dragging` ставит только первое
72
+ * движение. Наведение — только там, где оно есть: после касания `:hover`
73
+ * залипает. `always` — видна всегда, и горизонтальный держит под неё место над
74
+ * ручками, под плашку в одну строку; вертикальному место не держит — его
75
+ * подсказка сбоку, а её ширины никто не знает.
76
+ *
77
+ * Шкала размеров — переменные корня документа по размеру (`sm`…`2xl`):
78
+ * сторона ручки, толщина рельса и заливки, зазор подписей и подсказки от
79
+ * полосы ручек. Блок берёт ступень своего размера в `--so-slider-thumb-size`,
80
+ * `--so-slider-rail-size` и `--so-slider-gap`, и части считают от них: точка
81
+ * метки — половина рельса и пиксель сверху, ореол — треть ручки. По стороне
82
+ * ручки встаёт и подпись Field сбоку (`components/field.css`), а толщину
83
+ * рельса берёт полоса ProgressLinear. Длина вертикального — тоже переменная
84
+ * корня. Умолчания — числа темы oren; тема задаёт свои на корне документа.
85
+ *
86
+ * Облик — цвет рельса, заливки, ручки и точек, скругления, кромка, тень и
87
+ * ореол ручки, кольцо фокуса, вид плашки, цвет и начертание подписей, щелчок
88
+ * к метке, ход ручки переходом и вид в режиме принудительных цветов — рисует
89
+ * тема. */
90
+
91
+ :root {
92
+ --so-slider-thumb-size-sm: 0.875rem;
93
+ --so-slider-thumb-size-normal: 1.125rem;
94
+ --so-slider-thumb-size-lg: 1.375rem;
95
+ --so-slider-thumb-size-xl: 1.625rem;
96
+ --so-slider-thumb-size-2xl: 1.875rem;
97
+
98
+ --so-slider-rail-size-sm: 0.25rem;
99
+ --so-slider-rail-size-normal: 0.375rem;
100
+ --so-slider-rail-size-lg: 0.5rem;
101
+ --so-slider-rail-size-xl: 0.625rem;
102
+ --so-slider-rail-size-2xl: 0.75rem;
103
+
104
+ --so-slider-gap-sm: 0.25rem;
105
+ --so-slider-gap-normal: 0.25rem;
106
+ --so-slider-gap-lg: 0.375rem;
107
+ --so-slider-gap-xl: 0.375rem;
108
+ --so-slider-gap-2xl: 0.5rem;
109
+
110
+ --so-slider-length: 10rem;
111
+ }
112
+
113
+ /* Размер без модификатора — `normal`: ядро пишет `--size-*` всегда, но блок и
114
+ * без него обязан выглядеть умолчанием. */
115
+ .so-slider {
116
+ --so-slider-thumb-size: var(--so-slider-thumb-size-normal);
117
+ --so-slider-rail-size: var(--so-slider-rail-size-normal);
118
+ --so-slider-gap: var(--so-slider-gap-normal);
119
+ --so-slider-tick-size: calc(var(--so-slider-rail-size) / 2 + 1px);
120
+ --so-slider-halo-size: calc(var(--so-slider-thumb-size) / 3);
121
+ position: relative;
122
+ isolation: isolate;
123
+ -webkit-user-select: none;
124
+ user-select: none;
125
+ cursor: pointer;
126
+ }
127
+
128
+ .so-slider--orientation-horizontal {
129
+ display: flex;
130
+ padding-inline: calc(var(--so-slider-thumb-size) / 2);
131
+ padding-block: var(--so-slider-halo-size);
132
+ touch-action: pan-y;
133
+ }
134
+
135
+ .so-slider--orientation-vertical {
136
+ display: inline-flex;
137
+ flex-direction: column;
138
+ padding-block: calc(var(--so-slider-thumb-size) / 2);
139
+ padding-inline: var(--so-slider-halo-size);
140
+ touch-action: pan-x;
141
+ }
142
+
143
+ .so-slider__track {
144
+ position: relative;
145
+ display: grid;
146
+ flex: 1 1 0;
147
+ }
148
+
149
+ /* Рельс — от края до края корня: за края хода выходит на полстороны ручки, и
150
+ * ручка на краю лежит на нём, а не свисает. */
151
+ .so-slider__track::before {
152
+ content: '';
153
+ position: absolute;
154
+ }
155
+
156
+ /* Полоса ручек — высота дорожки: рельс, точки меток и ручки лежат по её
157
+ * центру. */
158
+ .so-slider--orientation-horizontal > .so-slider__track {
159
+ min-inline-size: 0;
160
+ min-block-size: var(--so-slider-thumb-size);
161
+ }
162
+
163
+ .so-slider--orientation-horizontal > .so-slider__track::before {
164
+ inset-inline: calc(var(--so-slider-thumb-size) / -2);
165
+ inset-block-start: calc((var(--so-slider-thumb-size) - var(--so-slider-rail-size)) / 2);
166
+ block-size: var(--so-slider-rail-size);
167
+ }
168
+
169
+ .so-slider--orientation-vertical > .so-slider__track {
170
+ min-block-size: 0;
171
+ min-inline-size: var(--so-slider-thumb-size);
172
+ }
173
+
174
+ .so-slider--orientation-vertical > .so-slider__track::before {
175
+ inset-block: calc(var(--so-slider-thumb-size) / -2);
176
+ inset-inline-start: calc((var(--so-slider-thumb-size) - var(--so-slider-rail-size)) / 2);
177
+ inline-size: var(--so-slider-rail-size);
178
+ }
179
+
180
+ /* Заливка — от `--so-slider-range-start` до `--so-slider-range-end`, по
181
+ * рельсу. Край, упёршийся в край хода, заливка дотягивает под конец рельса —
182
+ * на полстороны ручки, иначе между концом рельса и нулём остался бы
183
+ * незалитый хвост; край внутри хода остаётся ровно на месте: у заливки между
184
+ * ручками он под ручкой, а у заливки от середины — на самой середине. Край у
185
+ * края хода `clamp()` отличает без признака от ядра: расстояние до края хода,
186
+ * умноженное на тысячу, у любого ненулевого процента больше полустороны
187
+ * ручки, и вынос — ноль, а у нулевого — полсторона. */
188
+ .so-slider__range {
189
+ position: absolute;
190
+ }
191
+
192
+ .so-slider--orientation-horizontal .so-slider__range {
193
+ inset-block-start: calc((var(--so-slider-thumb-size) - var(--so-slider-rail-size)) / 2);
194
+ block-size: var(--so-slider-rail-size);
195
+ inset-inline-start: calc(
196
+ var(--so-slider-range-start, 0%) -
197
+ clamp(
198
+ 0px,
199
+ var(--so-slider-thumb-size) / 2 - var(--so-slider-range-start, 0%) * 1000,
200
+ var(--so-slider-thumb-size) / 2
201
+ )
202
+ );
203
+ inset-inline-end: calc(
204
+ 100% - var(--so-slider-range-end, 0%) -
205
+ clamp(
206
+ 0px,
207
+ var(--so-slider-thumb-size) / 2 - (100% - var(--so-slider-range-end, 0%)) * 1000,
208
+ var(--so-slider-thumb-size) / 2
209
+ )
210
+ );
211
+ }
212
+
213
+ .so-slider--orientation-vertical .so-slider__range {
214
+ inset-inline-start: calc((var(--so-slider-thumb-size) - var(--so-slider-rail-size)) / 2);
215
+ inline-size: var(--so-slider-rail-size);
216
+ inset-block-end: calc(
217
+ var(--so-slider-range-start, 0%) -
218
+ clamp(
219
+ 0px,
220
+ var(--so-slider-thumb-size) / 2 - var(--so-slider-range-start, 0%) * 1000,
221
+ var(--so-slider-thumb-size) / 2
222
+ )
223
+ );
224
+ inset-block-start: calc(
225
+ 100% - var(--so-slider-range-end, 0%) -
226
+ clamp(
227
+ 0px,
228
+ var(--so-slider-thumb-size) / 2 - (100% - var(--so-slider-range-end, 0%)) * 1000,
229
+ var(--so-slider-thumb-size) / 2
230
+ )
231
+ );
232
+ }
233
+
234
+ /* Ручка — центром на своей позиции: полстороны назад логическим полем, в RTL
235
+ * оно само встаёт с другой стороны. Содержимое — по центру ручки: от этого
236
+ * места подсказка считает своё вдоль оси. */
237
+ .so-slider__thumb {
238
+ position: absolute;
239
+ z-index: 1;
240
+ display: flex;
241
+ align-items: center;
242
+ justify-content: center;
243
+ inline-size: var(--so-slider-thumb-size);
244
+ block-size: var(--so-slider-thumb-size);
245
+ cursor: grab;
246
+ }
247
+
248
+ .so-slider--orientation-horizontal .so-slider__thumb {
249
+ inset-block-start: 0;
250
+ inset-inline-start: var(--so-slider-position, 0%);
251
+ margin-inline-start: calc(var(--so-slider-thumb-size) / -2);
252
+ }
253
+
254
+ .so-slider--orientation-vertical .so-slider__thumb {
255
+ inset-inline-start: 0;
256
+ inset-block-end: var(--so-slider-position, 0%);
257
+ margin-block-end: calc(var(--so-slider-thumb-size) / -2);
258
+ }
259
+
260
+ .so-slider__thumb:is([data-dragging='true'], :focus-within) {
261
+ z-index: 2;
262
+ }
263
+
264
+ .so-slider__input {
265
+ position: absolute;
266
+ inset: 0;
267
+ width: 100%;
268
+ height: 100%;
269
+ margin: 0;
270
+ padding: 0;
271
+ opacity: 0;
272
+ appearance: none;
273
+ pointer-events: none;
274
+ }
275
+
276
+ .so-slider__tooltip {
277
+ position: absolute;
278
+ box-sizing: border-box;
279
+ padding: var(--so-tooltip-padding-block) var(--so-tooltip-padding-inline);
280
+ line-height: var(--so-tooltip-line);
281
+ white-space: nowrap;
282
+ }
283
+
284
+ .so-slider--orientation-horizontal .so-slider__tooltip {
285
+ inset-block-end: calc(
286
+ 100% + var(--so-slider-thumb-border, 0px) + var(--so-slider-halo-size) +
287
+ var(--so-slider-gap)
288
+ );
289
+ }
290
+
291
+ .so-slider--orientation-vertical .so-slider__tooltip {
292
+ inset-inline-end: calc(
293
+ 100% + var(--so-slider-thumb-border, 0px) + var(--so-slider-halo-size) +
294
+ var(--so-slider-gap)
295
+ );
296
+ }
297
+
298
+ .so-slider--tooltip-auto .so-slider__tooltip {
299
+ visibility: hidden;
300
+ }
301
+
302
+ .so-slider--tooltip-auto
303
+ .so-slider__thumb:is([data-dragging='true'], :active, :has(> .so-slider__input:focus-visible))
304
+ > .so-slider__tooltip {
305
+ visibility: visible;
306
+ }
307
+
308
+ @media (hover: hover) {
309
+ .so-slider--tooltip-auto .so-slider__thumb:hover > .so-slider__tooltip {
310
+ visibility: visible;
311
+ }
312
+ }
313
+
314
+ .so-slider--orientation-horizontal.so-slider--tooltip-always {
315
+ padding-block-start: calc(
316
+ var(--so-slider-halo-size) + var(--so-slider-gap) + var(--so-tooltip-line) + 2 *
317
+ var(--so-tooltip-padding-block)
318
+ );
319
+ }
320
+
321
+ .so-slider__marks {
322
+ display: grid;
323
+ }
324
+
325
+ .so-slider__mark {
326
+ position: relative;
327
+ display: flex;
328
+ grid-area: 1 / 1;
329
+ }
330
+
331
+ /* Точка на рельсе. */
332
+ .so-slider__mark::before {
333
+ content: '';
334
+ flex-shrink: 0;
335
+ inline-size: var(--so-slider-tick-size);
336
+ block-size: var(--so-slider-tick-size);
337
+ }
338
+
339
+ .so-slider--orientation-horizontal .so-slider__mark {
340
+ flex-direction: column;
341
+ align-items: center;
342
+ justify-self: start;
343
+ inline-size: 0;
344
+ inset-inline-start: var(--so-slider-position, 0%);
345
+ }
346
+
347
+ .so-slider--orientation-horizontal .so-slider__mark::before {
348
+ margin-block: calc((var(--so-slider-thumb-size) - var(--so-slider-tick-size)) / 2);
349
+ }
350
+
351
+ .so-slider--orientation-vertical .so-slider__mark {
352
+ flex-direction: row;
353
+ align-items: center;
354
+ align-self: end;
355
+ block-size: 0;
356
+ inset-block-end: var(--so-slider-position, 0%);
357
+ }
358
+
359
+ .so-slider--orientation-vertical .so-slider__mark::before {
360
+ margin-inline: calc((var(--so-slider-thumb-size) - var(--so-slider-tick-size)) / 2);
361
+ }
362
+
363
+ .so-slider__mark-label {
364
+ white-space: nowrap;
365
+ font-size: var(--so-caption-note-font-size-normal);
366
+ line-height: var(--so-caption-note-line-height-normal);
367
+ }
368
+
369
+ .so-slider__mark-label:empty {
370
+ display: none;
371
+ }
372
+
373
+ .so-slider--orientation-horizontal .so-slider__mark-label {
374
+ margin-block-start: var(--so-slider-gap);
375
+ }
376
+
377
+ .so-slider--orientation-vertical .so-slider__mark-label {
378
+ margin-inline-start: var(--so-slider-gap);
379
+ }
380
+
381
+ .so-slider[data-dragging='true'],
382
+ .so-slider[data-dragging='true'] * {
383
+ cursor: grabbing;
384
+ }
385
+
386
+ /* Выключенный — `data-disabled`: ядро пишет его на корне при любом теге.
387
+ * Гаснет ползунок целиком (`disabled.css`) и нажатий не ловит. */
388
+ .so-slider[data-disabled='true'] {
389
+ opacity: var(--so-disabled-opacity);
390
+ cursor: default;
391
+ pointer-events: none;
392
+ }
393
+
394
+ .so-slider[data-disabled='true'] > * {
395
+ --so-disabled-opacity: 1;
396
+ }
397
+
398
+ .so-slider--size-sm {
399
+ --so-slider-thumb-size: var(--so-slider-thumb-size-sm);
400
+ --so-slider-rail-size: var(--so-slider-rail-size-sm);
401
+ --so-slider-gap: var(--so-slider-gap-sm);
402
+ }
403
+
404
+ .so-slider--size-sm .so-slider__mark-label {
405
+ font-size: var(--so-caption-note-font-size-sm);
406
+ line-height: var(--so-caption-note-line-height-sm);
407
+ }
408
+
409
+ .so-slider--size-normal {
410
+ --so-slider-thumb-size: var(--so-slider-thumb-size-normal);
411
+ --so-slider-rail-size: var(--so-slider-rail-size-normal);
412
+ --so-slider-gap: var(--so-slider-gap-normal);
413
+ }
414
+
415
+ .so-slider--size-normal .so-slider__mark-label {
416
+ font-size: var(--so-caption-note-font-size-normal);
417
+ line-height: var(--so-caption-note-line-height-normal);
418
+ }
419
+
420
+ .so-slider--size-lg {
421
+ --so-slider-thumb-size: var(--so-slider-thumb-size-lg);
422
+ --so-slider-rail-size: var(--so-slider-rail-size-lg);
423
+ --so-slider-gap: var(--so-slider-gap-lg);
424
+ }
425
+
426
+ .so-slider--size-lg .so-slider__mark-label {
427
+ font-size: var(--so-caption-note-font-size-lg);
428
+ line-height: var(--so-caption-note-line-height-lg);
429
+ }
430
+
431
+ .so-slider--size-xl {
432
+ --so-slider-thumb-size: var(--so-slider-thumb-size-xl);
433
+ --so-slider-rail-size: var(--so-slider-rail-size-xl);
434
+ --so-slider-gap: var(--so-slider-gap-xl);
435
+ }
436
+
437
+ .so-slider--size-xl .so-slider__mark-label {
438
+ font-size: var(--so-caption-note-font-size-xl);
439
+ line-height: var(--so-caption-note-line-height-xl);
440
+ }
441
+
442
+ .so-slider--size-2xl {
443
+ --so-slider-thumb-size: var(--so-slider-thumb-size-2xl);
444
+ --so-slider-rail-size: var(--so-slider-rail-size-2xl);
445
+ --so-slider-gap: var(--so-slider-gap-2xl);
446
+ }
447
+
448
+ .so-slider--size-2xl .so-slider__mark-label {
449
+ font-size: var(--so-caption-note-font-size-2xl);
450
+ line-height: var(--so-caption-note-line-height-2xl);
451
+ }
452
+
453
+ /* Длина по умолчанию — нулевой специфичности: класс или стиль потребителя её
454
+ * перекрывает, не споря. */
455
+ :where(.so-slider--orientation-horizontal) {
456
+ min-width: var(--so-input-min-width);
457
+ }
458
+
459
+ :where(.so-slider--orientation-vertical) {
460
+ height: var(--so-slider-length);
461
+ }
462
+
463
+ /* Ползунок в подписи `Label`. Обёртка контрола в подписи жмётся к началу
464
+ * строки и берёт ширину по содержимому, а у ползунка своего содержимого по
465
+ * ширине нет: он встал бы в свою нижнюю границу, какой бы широкой ни была
466
+ * подпись. Поэтому горизонтальный ползунок забирает ширину подписи — под
467
+ * текстом и над ним обёртка тянется поперёк столбца, рядом с текстом забирает
468
+ * остаток ряда. Растяжка поперёк — только в столбце: в ряду обёртка стоит по
469
+ * базовой линии первой строки текста, и растяжка её бы сбила. */
470
+ .so-label:is(.so-label--position-top, .so-label--position-bottom)
471
+ > .so-label__control:has(> .so-slider--orientation-horizontal) {
472
+ align-self: stretch;
473
+ }
474
+
475
+ .so-label:not(.so-label--position-top, .so-label--position-bottom)
476
+ > .so-label__control:has(> .so-slider--orientation-horizontal) {
477
+ flex-grow: 1;
478
+ }
479
+
480
+ .so-label__control > .so-slider--orientation-horizontal {
481
+ flex: 1 1 auto;
482
+ }
@@ -0,0 +1,142 @@
1
+ /* ─── Switch ──────────────────────────────────────────────────
2
+ * Дорожка с ручкой и невидимое поле поверх них: клик по дорожке — клик по
3
+ * полю, и переключает его браузер. Поле — нативный `<input type="checkbox">`:
4
+ * состояние, фокус, участие в форме и доступное имя у него свои, а дорожка —
5
+ * декор (`aria-hidden`).
6
+ *
7
+ * Ручка стоит в дорожке на отступе `--so-switch-inset` от краёв: выключенная
8
+ * — от начала строки, включённая — от конца. Сторона ручки поэтому — высота
9
+ * дорожки без двух отступов, а включённая отходит от начала на ширину
10
+ * дорожки без ручки и отступа. Ходит ручка от начала строки
11
+ * (`inset-inline-start`), как ручка Slider, и в RTL переключатель
12
+ * разворачивается сам: выключенная ручка стоит справа, включённая уходит
13
+ * влево. Не `translate`: у сдвига нет логических направлений, и в RTL он
14
+ * увёл бы ручку вправо. Селектора по `dir` здесь нет: он развернул бы и
15
+ * переключатель с `direction="ltr"` внутри RTL-предка.
16
+ *
17
+ * Ширина и высота дорожки по размеру — переменные корня документа
18
+ * (`sm`…`2xl`); блок берёт ступень своего размера в `--so-switch-width` и
19
+ * `--so-switch-height`, и части считают от них. Умолчания — числа темы oren;
20
+ * тема задаёт свои на корне документа.
21
+ *
22
+ * Облик — цвет дорожки и ручки, скругления, тень, кольцо фокуса, ход ручки
23
+ * переходом (движение) и вид только для чтения — рисует тема. */
24
+
25
+ :root {
26
+ --so-switch-width-sm: 1.5rem;
27
+ --so-switch-width-normal: 2rem;
28
+ --so-switch-width-lg: 2.5rem;
29
+ --so-switch-width-xl: 3rem;
30
+ --so-switch-width-2xl: 3.5rem;
31
+
32
+ --so-switch-height-sm: 1rem;
33
+ --so-switch-height-normal: 1.25rem;
34
+ --so-switch-height-lg: 1.5rem;
35
+ --so-switch-height-xl: 1.75rem;
36
+ --so-switch-height-2xl: 2rem;
37
+
38
+ --so-switch-inset: 0.125rem;
39
+ --so-switch-required-offset: -0.125rem;
40
+ }
41
+
42
+ .so-switch {
43
+ position: relative;
44
+ display: inline-flex;
45
+ align-items: center;
46
+ -webkit-user-select: none;
47
+ user-select: none;
48
+ }
49
+
50
+ .so-switch--size-sm {
51
+ --so-switch-width: var(--so-switch-width-sm);
52
+ --so-switch-height: var(--so-switch-height-sm);
53
+ }
54
+
55
+ .so-switch--size-normal {
56
+ --so-switch-width: var(--so-switch-width-normal);
57
+ --so-switch-height: var(--so-switch-height-normal);
58
+ }
59
+
60
+ .so-switch--size-lg {
61
+ --so-switch-width: var(--so-switch-width-lg);
62
+ --so-switch-height: var(--so-switch-height-lg);
63
+ }
64
+
65
+ .so-switch--size-xl {
66
+ --so-switch-width: var(--so-switch-width-xl);
67
+ --so-switch-height: var(--so-switch-height-xl);
68
+ }
69
+
70
+ .so-switch--size-2xl {
71
+ --so-switch-width: var(--so-switch-width-2xl);
72
+ --so-switch-height: var(--so-switch-height-2xl);
73
+ }
74
+
75
+ /* Поле — поверх дорожки, во весь корень, и невидимо: нажатие ловит оно. */
76
+ .so-switch input {
77
+ position: absolute;
78
+ top: 0;
79
+ left: 0;
80
+ z-index: 10;
81
+ width: 100%;
82
+ height: 100%;
83
+ margin: 0;
84
+ padding: 0;
85
+ opacity: 0;
86
+ cursor: pointer;
87
+ }
88
+
89
+ .so-switch input:disabled {
90
+ cursor: default;
91
+ pointer-events: none;
92
+ }
93
+
94
+ /* Выключенная дорожка гаснет целиком, с ручкой (`disabled.css`). */
95
+ .so-switch input:disabled + .so-switch__track {
96
+ opacity: var(--so-disabled-opacity);
97
+ }
98
+
99
+ .so-switch input:disabled + .so-switch__track > * {
100
+ --so-disabled-opacity: 1;
101
+ }
102
+
103
+ /* Только для чтения: клик переключатель не меняет — его отменяет
104
+ * `TInputBoolPlugin`, — и поле нажатий не ловит. */
105
+ .so-switch:where([data-readonly='true']) input {
106
+ pointer-events: none;
107
+ }
108
+
109
+ /* Блочная при любом теге: у строчного `span` без `display` размер дорожки
110
+ * схлопнулся бы. */
111
+ .so-switch__track {
112
+ position: relative;
113
+ display: block;
114
+ width: var(--so-switch-width);
115
+ height: var(--so-switch-height);
116
+ }
117
+
118
+ .so-switch__track--thumb {
119
+ position: absolute;
120
+ top: var(--so-switch-inset);
121
+ inset-inline-start: var(--so-switch-inset);
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ width: calc(var(--so-switch-height) - 2 * var(--so-switch-inset));
126
+ height: calc(var(--so-switch-height) - 2 * var(--so-switch-inset));
127
+ }
128
+
129
+ .so-switch input:checked + .so-switch__track .so-switch__track--thumb {
130
+ inset-inline-start: calc(
131
+ var(--so-switch-width) - var(--so-switch-height) + var(--so-switch-inset)
132
+ );
133
+ }
134
+
135
+ /* Маркер обязательного поля (`required.css`) — на своём отступе от угла
136
+ * дорожки (у oren точка ближе к углу, чем у коробки CheckBox: её дорожка
137
+ * скруглена) и над полем, которое лежит поверх дорожки. */
138
+ .so-switch:where([data-required='true'])::after {
139
+ top: var(--so-switch-required-offset);
140
+ inset-inline-start: var(--so-switch-required-offset);
141
+ z-index: 20;
142
+ }