@rt-tools/ui-kit-v2 0.6.0 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/ui-kit-v2",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Yauheni Krumin",
6
6
  "type": "module",
Binary file
@@ -16,12 +16,14 @@
16
16
 
17
17
  ## Главное, что нужно знать
18
18
 
19
- **Компонент ничего не грузит.** Он рисует `<use href="#rt-icon-<name>">` — ссылку на symbol
20
- внутри спрайта, который собирает `RtIconRegistry` и вставляет в `<body>`
21
- провайдер `provideRtIcons()`. Без этого провайдера разметка верна, а на экране пусто.
19
+ **Значок едет по требованию.** Компонент рисует `<use href="#rt-icon-<name>">` — ссылку на
20
+ symbol внутри спрайта — и тем же движением просит имя у `RtIconRegistry`. Реестр ходит за файлом
21
+ один раз на страницу и дописывает символ в спрайт; страница платит ровно за те значки, которые
22
+ нарисовала, а не за весь набор.
22
23
 
23
- Приложение обязано опубликовать набор из `assets/icons` пакета по адресу, который знает реестр
24
- (по умолчанию `/icons`, иначе `provideRtIcons('/my/icons')`).
24
+ `provideRtIcons()` называет адрес набора и сам не грузит ничего. Без него реестр берёт умолчание
25
+ `/icons`; приложение обязано опубликовать туда набор из `assets/icons` пакета, иначе разметка
26
+ верна, а на экране пусто.
25
27
 
26
28
  ## Как этим пользоваться
27
29
 
@@ -34,11 +36,15 @@
34
36
 
35
37
  ## Края
36
38
 
37
- - **Имя без файла роняет весь набор.** Реестр собирает спрайт одним `forkJoin`; один отказ
38
- гасит иконки во всём приложении. Добавляя имя в `rt-icon-names.ts`, положи SVG рядом.
39
- - Реестр пропускает загрузку, если спрайт уже в DOM (`#rt-icon-sprite`) — так витрина не гоняет
40
- сотни запросов между историями.
41
- - На сервере (SSR) спрайт не грузится: относительных HTTP-запросов к статике там нет, иконки
39
+ - **Имя без файла гасит только свой значок.** Отказ остаётся внутри реестра, соседние значки
40
+ приезжают своими потоками, и на месте промаха остаётся пустое место размера значка. Второй раз
41
+ за отказавшим именем реестр не ходит. Добавляя имя в `rt-icon-names.ts`, положи SVG рядом:
42
+ иначе промах виден только глазами.
43
+ - **Значок появляется после первой отрисовки.** Место под него занято сразу — размер задан на
44
+ хосте, — поэтому приход символа соседей не двигает.
45
+ - Символ, уже лежащий в спрайте страницы, запроса не даёт: так витрина не ходит за одним и тем
46
+ же между историями, а приложение вправе положить свой спрайт до старта.
47
+ - На сервере (SSR) реестр молчит: относительных HTTP-запросов к статике там нет, значки
42
48
  дорисуются после гидрации.
43
49
 
44
50
  ## Рядом
@@ -11,13 +11,13 @@
11
11
  Свои входы поверх общих из [`RtFormControlBase`](../form-control/CONTEXT.md)
12
12
  (`size`, `disabled`, `controlId`, `ariaLabel`, `clearable`, `bordered`):
13
13
 
14
- | вход | тип | умолчание |
15
- | ------------------------ | ------------------------------------------- | --------- |
16
- | `type` | `'text' \| 'password' \| 'email' \| 'time'` | `'text'` |
17
- | `placeholder` | `string` | `''` |
18
- | `iconLeft` / `iconRight` | `IRtIcon.Name \| null` | `null` |
19
- | `passwordToggle` | `boolean` | `false` |
20
- | `autocomplete` | `string \| null` | `null` |
14
+ | вход | тип | умолчание |
15
+ | ------------------------ | ---------------------------------------------------- | --------- |
16
+ | `type` | `'text' \| 'password' \| 'email' \| 'time' \| 'url'` | `'text'` |
17
+ | `placeholder` | `string` | `''` |
18
+ | `iconLeft` / `iconRight` | `IRtIcon.Name \| null` | `null` |
19
+ | `passwordToggle` | `boolean` | `false` |
20
+ | `autocomplete` | `string \| null` | `null` |
21
21
 
22
22
  Значение — `string`.
23
23
 
@@ -32,6 +32,8 @@
32
32
 
33
33
  ## Как этим пользоваться
34
34
 
35
+ - `type="url"` объявляет браузеру род значения, а не требование к нему: на телефоне он даёт
36
+ клавиатуру для адреса и разбор вставленного, а проверка введённого остаётся за приложением.
35
37
  - `type="time"` заведён намеренно: браузер сам отдаёт `HH:MM` и раскладку под локаль, а разбор
36
38
  строки руками означал бы своё поле ввода времени рядом с китом.
37
39
  - Клик по рамке (падинги принадлежат host-у, а не `<input>`) переводит фокус в поле — кроме
@@ -0,0 +1,40 @@
1
+ # `rt-markdown-text`
2
+
3
+ Текст, написанный разметкой, показанный узлами: заголовки, списки, таблицы, цитаты и код вместо
4
+ частокола знаков.
5
+
6
+ ```html
7
+ <rt-markdown-text [text]="record.text" />
8
+ ```
9
+
10
+ | вход | тип | умолчание |
11
+ | ------ | ---------------- | -------------- |
12
+ | `text` | `string \| null` | **обязателен** |
13
+
14
+ Выходов нет.
15
+
16
+ ## Главное, что нужно знать
17
+
18
+ **Перечень разметки закрыт и зашит.** Компонент понимает заголовки, абзацы, списки — простые и
19
+ нумерованные, — таблицы, цитаты, код строкой и блоком, жирный, курсив, зачёркнутый и ссылку.
20
+ Настроить перечень нечем: вход один — сам текст.
21
+
22
+ **Всё, чего в перечне нет, остаётся видимым текстом.** Сырой HTML не вычищается, а не
23
+ разбирается вовсе: узла для него разборщик не строит, и присланная строка видна как есть.
24
+ Отсюда же — картинки в вывод не попадают, а ссылка становится ссылкой только при схеме `http`,
25
+ `https` или `mailto`.
26
+
27
+ **Присланная строка в страницу не отдаётся ни разу.** Компонент рисует дерево узлов, которое
28
+ вернул разбор; другого пути у текста внутрь страницы нет.
29
+
30
+ ## Края
31
+
32
+ - **Одиночный перенос строки остаётся переносом.** По правилам `.md` он схлопнулся бы в пробел,
33
+ но тексты, которые сюда приходят, пишут строками осмысленно.
34
+ - **Блок кода не раскрашивается по языкам.** Слово языка у ограды разбирается и на вид не
35
+ влияет: раскраска потребовала бы второй библиотеки в рантайме.
36
+ - **Таблица и блок кода прокручиваются вбок сами** и ширину родителя не растягивают.
37
+ - **Таблица рисуется своей разметкой, а не `rt-table`.** У той колонки объявляются заранее
38
+ директивами, а число столбцов здесь приходит из текста.
39
+ - **Пустой текст не рисует ничего** — ни пустого абзаца, ни отступа: показывать или прятать
40
+ заголовок раздела, решает потребитель.
@@ -0,0 +1,130 @@
1
+ @layer rt-kit.components {
2
+ .rt-markdown-text {
3
+ --rt-markdown-text-radius: var(--rt-radius-sm);
4
+ --rt-markdown-text-border-width: var(--rt-border-width-thin);
5
+
6
+ display: flex;
7
+ min-width: 0;
8
+ max-width: 100%;
9
+ flex-direction: column;
10
+ color: var(--rt-color-text-primary);
11
+ font-family: var(--rt-font-family-sans);
12
+ font-size: var(--rt-text-xs);
13
+ font-weight: var(--rt-font-weight-regular);
14
+ gap: var(--rt-space-sm);
15
+ letter-spacing: var(--rt-letter-spacing-wide);
16
+ line-height: var(--rt-leading-snug);
17
+
18
+ &__heading {
19
+ margin: 0;
20
+ color: var(--rt-color-text-primary);
21
+ font-weight: var(--rt-font-weight-semibold);
22
+
23
+ &--level--1 {
24
+ font-size: var(--rt-text-lg);
25
+ }
26
+
27
+ &--level--2 {
28
+ font-size: var(--rt-text-md);
29
+ }
30
+
31
+ &--level--3,
32
+ &--level--4,
33
+ &--level--5,
34
+ &--level--6 {
35
+ font-size: var(--rt-text-sm);
36
+ }
37
+ }
38
+
39
+ &__paragraph {
40
+ margin: 0;
41
+ }
42
+
43
+ &__list {
44
+ padding-left: var(--rt-space-md);
45
+ margin: 0;
46
+ list-style: disc;
47
+
48
+ &--ordered {
49
+ list-style: decimal;
50
+ }
51
+ }
52
+
53
+ &__item {
54
+ &--level--1 {
55
+ margin-left: var(--rt-space-md);
56
+ }
57
+
58
+ &--level--2 {
59
+ margin-left: var(--rt-space-lg);
60
+ }
61
+ }
62
+
63
+ &__quote {
64
+ padding-left: var(--rt-space-sm);
65
+ border-left: var(--rt-markdown-text-border-width) solid var(--rt-color-border-default);
66
+ margin: 0;
67
+ color: var(--rt-color-text-muted);
68
+ }
69
+
70
+ &__code {
71
+ max-width: 100%;
72
+ padding: var(--rt-space-sm);
73
+ border-radius: var(--rt-markdown-text-radius);
74
+ margin: 0;
75
+ background: var(--rt-color-bg-subtle);
76
+ color: var(--rt-color-text-primary);
77
+ font-family: var(--rt-font-family-mono);
78
+ font-size: var(--rt-text-2xs);
79
+ line-height: var(--rt-leading-snug);
80
+ overflow-x: auto;
81
+ }
82
+
83
+ &__code-inline {
84
+ padding: 0 var(--rt-space-2xs);
85
+ border-radius: var(--rt-markdown-text-radius);
86
+ background: var(--rt-color-bg-subtle);
87
+ font-family: var(--rt-font-family-mono);
88
+ font-size: var(--rt-text-2xs);
89
+ }
90
+
91
+ &__table-scroll {
92
+ max-width: 100%;
93
+ overflow-x: auto;
94
+ }
95
+
96
+ &__table {
97
+ width: 100%;
98
+ border-collapse: collapse;
99
+ }
100
+
101
+ &__head-cell,
102
+ &__cell {
103
+ padding: var(--rt-space-2xs) var(--rt-space-sm);
104
+ border: var(--rt-markdown-text-border-width) solid var(--rt-color-border-default);
105
+ text-align: left;
106
+ }
107
+
108
+ &__head-cell {
109
+ color: var(--rt-color-text-muted);
110
+ font-weight: var(--rt-font-weight-semibold);
111
+ }
112
+
113
+ &__strong {
114
+ font-weight: var(--rt-font-weight-semibold);
115
+ }
116
+
117
+ &__emphasis {
118
+ font-style: italic;
119
+ }
120
+
121
+ &__strike {
122
+ text-decoration: line-through;
123
+ }
124
+
125
+ &__link {
126
+ color: var(--rt-color-text-link);
127
+ text-decoration: underline;
128
+ }
129
+ }
130
+ }
@@ -18,7 +18,7 @@
18
18
  | `accept` | `string` | `''` |
19
19
  | `sending` / `disabled` | `boolean` | `false` |
20
20
  | `formatting` | `boolean` | `false` |
21
- | `toolbar` | `IRtRichEditorToolbar` | `'full'` |
21
+ | `toolbar` | `TRtRichEditorToolbar` | `'full'` |
22
22
  | `minRows` / `maxRows` | `number` | 1 / 6 |
23
23
  | `droppedFiles` | `File[] \| null` | `null` |
24
24
 
@@ -13,7 +13,7 @@
13
13
  | вход | тип | умолчание |
14
14
  | ------------- | ---------------------- | --------- |
15
15
  | `placeholder` | `string` | `''` |
16
- | `toolbar` | `IRtRichEditorToolbar` | `'full'` |
16
+ | `toolbar` | `TRtRichEditorToolbar` | `'full'` |
17
17
 
18
18
  Выход: `enterPressed` — Enter без Shift (перенос при этом гасится).
19
19
 
@@ -12,8 +12,13 @@
12
12
  align-items: center;
13
13
  gap: var(--rt-space-xs);
14
14
 
15
+ /* Значение длиннее места обрезается многоточием, а не наезжает на кнопку:
16
+ min-width: 0 разрешает flex-элементу сжаться, но сам по себе ничего не прячет. */
15
17
  &__content {
18
+ overflow: hidden;
16
19
  min-width: 0;
20
+ text-overflow: ellipsis;
21
+ white-space: nowrap;
17
22
  }
18
23
 
19
24
  &__button {
@@ -58,6 +58,13 @@
58
58
  gap: var(--rt-space-sm);
59
59
  }
60
60
 
61
+ // Зона стекает в колонку, а её содержимое — нет: третий отбор уезжал за
62
+ // правый край окна и обрезался прокруткой, не сказав об этом. Перенос
63
+ // объявлен здесь, а не у потребителя: зона узкого экрана — забота тулбара.
64
+ .rt-toolbar__bar {
65
+ flex-wrap: wrap;
66
+ }
67
+
61
68
  .rt-toolbar__bar--center {
62
69
  justify-content: stretch;
63
70
  }
@@ -16,6 +16,10 @@
16
16
  font-size: var(--rt-text-xs);
17
17
  line-height: var(--rt-leading-snug);
18
18
 
19
+ /* Длинное значение без пробелов — код, признак, ключ — переносится внутри подсказки:
20
+ иначе оно вылезает за её край, и подсказка обрезает ровно то, ради чего показана. */
21
+ overflow-wrap: anywhere;
22
+
19
23
  /* Tooltip — чисто визуальная подсказка: не перехватывает указатель, чтобы
20
24
  не мешать hover/leave логике host'а. */
21
25
  pointer-events: none;
@@ -9,8 +9,15 @@
9
9
  @forward './primitives';
10
10
  @forward './semantic';
11
11
  @forward './theme-dark';
12
+ @forward './theme-dark-rules';
12
13
  @forward './scrollbar';
13
14
 
15
+ // Раскладка экрана входа: она объявлена классами, а не компонентом, поэтому доехать до
16
+ // потребителя может только отсюда. Без этой строки приложение получает разметку, у которой ни
17
+ // один класс не имеет правила: экран рисуется потоком, и ни сборка, ни линтер, ни снимки витрин
18
+ // этого не видят — истории у экрана входа нет.
19
+ @forward './login';
20
+
14
21
  // Стили директивы кнопки: своего представления у директивы нет, поэтому её `.scss` не
15
22
  // подключён ни одним `styleUrl`. Без этой строки потребитель, взявший `tokens.css` по
16
23
  // инструкции README, получает `rtButton`, нарисованный браузерной кнопкой.
@@ -1,30 +1,40 @@
1
- /* Primitive design tokens — single source of truth для шкал. */
1
+ /* Собрано генератором `tools/build-tokens-v2.mjs` из `tokens.source.mjs` — правится там, не здесь:
2
+ правка на месте теряется на следующей сборке, и её называет `pnpm run check:tokens-build`. */
3
+
4
+ /* Ступени шкал — значения, из которых слой назначений выбирает. Единственное место кита, где
5
+ код цвета и размер числом законны по определению. */
2
6
 
3
7
  :root {
4
- /* Neutral scale — серо-сине-стальная.
5
-
6
- Ряд посчитан в OKLCH: тон 264.4° и цветность 0.0234 взяты у прежней ступени 600,
7
- светлота идёт ровным шагом 0.0785 от 0.985 к 0.20. Ноль остаётся чистым белым —
8
- это бумага, а не ступень ряда. В самом светлом конце цветность ужата охватом
9
- sRGB: синеватого такой светлоты не существует.
10
-
11
- До пересчёта ряд шкалой не был. Он держал два семейства сразу — чисто серое
12
- (0, 50, 200, 500, 900) и синеватое (100, 300, 400, 600, 700, 800, 950), —
13
- шаг светлоты гулял от 0.009 до 0.173, а ступень 900 была темнее ступени 950,
14
- то есть ряд шёл назад. Ещё девять ступеней стояли между этими: заведённые под
15
- одно место, они выглядели шкалой, но выбрать из такого ряда было нельзя. */
8
+ /* Neutral scale — серая, без цветности.
9
+
10
+ Ряд посчитан в OKLCH: цветность нулевая у всех ступеней, светлота идёт ровным
11
+ шагом 0.0785 от 0.985 к 0.20. Ноль остаётся чистым белым — это бумага, а не
12
+ ступень ряда.
13
+
14
+ Цветность снята потому, что ею красились три роли сразу: фон поля ввода,
15
+ приглушённая поверхность и подсветка наведения. Все три разворачивались в одну
16
+ ступень 100, а на тоне 264° с цветностью 0.0234 она давала #d8e0f0 — сине-серый
17
+ блок на почти белой странице, в котором тонула и собственная рамка поля. Синева
18
+ была следствием того, что тон и цветность ряда взяли у ступени 600: у тёмного
19
+ конца они незаметны, у светлого — нет.
20
+
21
+ Ряд остаётся ровным, и две опорные ступени сходятся с прежним выпуском кита:
22
+ 50 и 500. Светлый конец с ним не сходится и сойтись не может: там три ступени
23
+ подряд лежат в полосе светлоты шириной 0.06, а ровный ряд из одиннадцати
24
+ ступеней, доходящий до почти чёрного, столько туда не вмещает. Полосу закрывает
25
+ назначение прозрачным оттенком, а не правка шага. */
16
26
  --rt-neutral-0: #fff;
17
- --rt-neutral-50: #f8faff;
18
- --rt-neutral-100: #d8e0f0;
19
- --rt-neutral-200: #bfc7d6;
20
- --rt-neutral-300: #a6aebd;
21
- --rt-neutral-400: #8e96a4;
22
- --rt-neutral-500: #777e8c;
23
- --rt-neutral-600: #606775;
24
- --rt-neutral-700: #4b515f;
25
- --rt-neutral-800: #363c49;
26
- --rt-neutral-900: #232934;
27
- --rt-neutral-950: #111621;
27
+ --rt-neutral-50: #fafafa;
28
+ --rt-neutral-100: #e0e0e0;
29
+ --rt-neutral-200: #c6c6c6;
30
+ --rt-neutral-300: #aeaeae;
31
+ --rt-neutral-400: #959595;
32
+ --rt-neutral-500: #7e7e7e;
33
+ --rt-neutral-600: #676767;
34
+ --rt-neutral-700: #515151;
35
+ --rt-neutral-800: #3c3c3c;
36
+ --rt-neutral-900: #282828;
37
+ --rt-neutral-950: #161616;
28
38
 
29
39
  /* Бренд — линейка цвета марки. Потребитель перекрашивает кит объявлением этих
30
40
  одиннадцати ступеней после tokens.css: назначения, прозрачные оттенки и кольцо
@@ -244,9 +254,12 @@
244
254
 
245
255
  /* Alpha overlays — для backdrop'ов и focus-ring tint'ов. */
246
256
  --rt-overlay-black-4: rgb(0 0 0 / 4%);
257
+ --rt-overlay-black-8: rgb(0 0 0 / 8%); /* наведение поверх приглушённой поверхности: она сама стоит на четырёх процентах */
247
258
  --rt-overlay-white-4: rgb(255 255 255 / 4%);
259
+ --rt-overlay-white-8: rgb(255 255 255 / 8%); /* то же на графите */
248
260
  --rt-overlay-white-25: rgb(250 250 250 / 25%); /* disabled-текст на графите */
249
- --rt-overlay-white-40: rgb(250 250 250 / 40%); /* muted-текст на графите */
261
+ --rt-overlay-white-40: rgb(250 250 250 / 40%); /* приглушённый знак: порога не берёт */
262
+ --rt-overlay-white-50: rgb(250 250 250 / 50%); /* приглушённый текст: слабее порог не берётся */
250
263
  --rt-overlay-black-50: rgb(0 0 0 / 50%);
251
264
 
252
265
  /* Оттенки марки считаются от её ступени, а не повторяют код цвета: повторённое
@@ -1,4 +1,7 @@
1
- /* Semantic design tokens — light defaults.
1
+ /* Собрано генератором `tools/build-tokens-v2.mjs` из `tokens.source.mjs` — правится там, не здесь:
2
+ правка на месте теряется на следующей сборке, и её называет `pnpm run check:tokens-build`. */
3
+
4
+ /* Назначения светлой темы.
2
5
 
3
6
  Тело вынесено в @mixin по той же причине, что и у тёмной темы: набор нужен не
4
7
  только на `:root`, но и на отдельном контейнере. Без этого светлую половину
@@ -9,9 +12,9 @@
9
12
  /* Background */
10
13
  --rt-color-bg-page: var(--rt-neutral-50);
11
14
  --rt-color-bg-surface: var(--rt-neutral-0);
12
- --rt-color-bg-surface-subtle: var(--rt-neutral-100);
15
+ --rt-color-bg-surface-subtle: var(--rt-overlay-black-4);
13
16
  --rt-color-bg-surface-subtle-2: var(--rt-neutral-200);
14
- --rt-color-bg-hover: var(--rt-neutral-100);
17
+ --rt-color-bg-hover: var(--rt-overlay-black-4);
15
18
  --rt-color-bg-subtle: var(--rt-neutral-100);
16
19
  --rt-color-bg-overlay: var(--rt-overlay-black-50);
17
20
  --rt-color-bg-inverse: var(--rt-neutral-900);
@@ -26,7 +29,13 @@
26
29
 
27
30
  /* Ступень 600, а не 500: приглушённый текст обязан брать порог, а после
28
31
  пересчёта ряда самая светлая ступень, которая его берёт, — шестисотая.
29
- Прежде эту роль держала off-grid ступень 510, заведённая ровно под неё. */
32
+ Прежде эту роль держала off-grid ступень 510, заведённая ровно под неё.
33
+
34
+ Тёмный ответ — пятьдесят процентов белого, а не сорок: на сорока он давал
35
+ 3.6:1 над поверхностью, 3.7:1 над страницей и 3.7:1 над фоном поля, то есть
36
+ не брал порога ни над одной подложкой. Светлую сторону под порог подводили,
37
+ тёмную забыли, и держал её список принятого. Пятьдесят — самая слабая доля,
38
+ берущая порог над всеми тремя: 4.86, 5.02 и 5.09. */
30
39
  --rt-color-text-muted: var(--rt-neutral-600);
31
40
  --rt-color-text-disabled: var(--rt-neutral-400);
32
41
  --rt-color-text-inverse: var(--rt-neutral-0);
@@ -34,9 +43,9 @@
34
43
  --rt-color-text-link-hover: var(--rt-brand-600);
35
44
 
36
45
  /* Border */
37
- --rt-color-border-default: var(--rt-neutral-200);
46
+ --rt-color-border-default: var(--rt-neutral-100);
38
47
  --rt-color-border-strong: var(--rt-neutral-300);
39
- --rt-color-border-subtle: var(--rt-neutral-200);
48
+ --rt-color-border-subtle: var(--rt-neutral-100);
40
49
  --rt-color-border-focus: var(--rt-brand-500);
41
50
 
42
51
  /* Action — роли действия.
@@ -187,7 +196,7 @@
187
196
  /* Colors */
188
197
  --rt-input-color-bg: var(--rt-color-bg-surface-subtle);
189
198
  --rt-input-color-bg-disabled: var(--rt-color-bg-surface-subtle);
190
- --rt-input-color-bg-hover: var(--rt-overlay-black-4);
199
+ --rt-input-color-bg-hover: var(--rt-overlay-black-8);
191
200
  --rt-input-color-border: var(--rt-color-border-default);
192
201
  --rt-input-color-border-focus: var(--rt-color-action-primary);
193
202
  --rt-input-color-border-error: var(--rt-color-state-danger);
@@ -0,0 +1,23 @@
1
+ /* Правила тёмной темы, которые свойств не объявляют. Стоят отдельно от `_theme-dark.scss`:
2
+ тот собирается генератором из источника и держит только объявления. */
3
+
4
+ /* Body-level градиент — подложка экранов авторизации: их вьюпорт прозрачен
5
+ (`--rt-color-bg-surface-auth-card: transparent`) и просвечивает body.
6
+ Остальные экраны его не видят — rt-container кроет body непрозрачным фоном
7
+ страницы. Не shorthand `background:`, чтобы не сбросить body{}'ный
8
+ `background-color`. */
9
+ @layer rt-kit.base {
10
+ :root[data-theme='dark'] body,
11
+ html.rt-theme-dark body {
12
+ background-attachment: fixed;
13
+ background-image: linear-gradient(156deg, #122341 12.68%, #182142 99.28%);
14
+ }
15
+ }
16
+
17
+ /* rt-logo (чёрный SVG) — инверсия для читаемости на тёмном фоне */
18
+ @layer rt-kit.base {
19
+ :root[data-theme='dark'] .rt-logo,
20
+ html.rt-theme-dark .rt-logo {
21
+ filter: invert(1) brightness(1.5);
22
+ }
23
+ }
@@ -1,19 +1,26 @@
1
- /* Dark theme — переопределение СЕМАНТИЧЕСКИХ токенов (примитивы неизменны).
1
+ /* Собрано генератором `tools/build-tokens-v2.mjs` из `tokens.source.mjs` — правится там, не здесь:
2
+ правка на месте теряется на следующей сборке, и её называет `pnpm run check:tokens-build`. */
2
3
 
3
- Тело токенов вынесено в @mixin, чтобы тот же набор переопределений можно было
4
+ /* Тёмная тема — переопределение назначений; ступени шкалы неизменны.
5
+
6
+ Тело вынесено в @mixin, чтобы тот же набор переопределений можно было
4
7
  применить не только на html-уровне (тема приложения), но и к локальному
5
8
  контейнеру (side-by-side light/dark в Storybook) — без дублирования значений.
6
9
 
7
10
  База (`rt-theme-dark-tokens`) — тёплый графит для приложений и всех
8
11
  CDK-overlay'ев (асайды/диалоги/меню рендерятся на уровне body, вне
9
12
  host-классов шеллов — поэтому графит держим на :root, иначе overlay'и
10
- не наследовали бы палитру). */
13
+ не наследовали бы палитру).
14
+
15
+ Значения ответов живут в источнике рядом со своим светлым назначением: забытая половина
16
+ пары видна там же, а не вылавливается сверкой двух файлов. */
11
17
 
12
18
  @mixin rt-theme-dark-tokens {
13
19
  /* Background — тёплый графит */
14
20
  --rt-color-bg-page: var(--rt-charcoal-900);
15
21
  --rt-color-bg-surface: var(--rt-charcoal-800);
16
22
  --rt-color-bg-surface-subtle: var(--rt-charcoal-950);
23
+ --rt-color-bg-surface-subtle-2: var(--rt-charcoal-950);
17
24
  --rt-color-bg-hover: var(--rt-charcoal-700);
18
25
  --rt-color-bg-subtle: var(--rt-charcoal-950);
19
26
  --rt-color-bg-overlay: rgb(0 0 0 / 70%);
@@ -21,7 +28,7 @@
21
28
 
22
29
  /* Text — почти белый на графите */
23
30
  --rt-color-text-primary: var(--rt-neutral-50);
24
- --rt-color-text-muted: var(--rt-overlay-white-40);
31
+ --rt-color-text-muted: var(--rt-overlay-white-50);
25
32
  --rt-color-text-disabled: var(--rt-overlay-white-25);
26
33
  --rt-color-text-inverse: var(--rt-neutral-900);
27
34
  --rt-color-text-link: var(--rt-brand-300);
@@ -93,7 +100,7 @@
93
100
  --rt-shadow-card-soft: 0 0 40px rgb(0 0 0 / 25%);
94
101
 
95
102
  /* Hover-overlay инвертирован для dark (light overlay поверх тёмного фона). */
96
- --rt-input-color-bg-hover: var(--rt-overlay-white-4);
103
+ --rt-input-color-bg-hover: var(--rt-overlay-white-8);
97
104
  --rt-icon-button-color-bg-hover: var(--rt-overlay-white-4);
98
105
 
99
106
  /* Toggle-switch — трек уходит в подложку, а бегунок остаётся светлым:
@@ -133,24 +140,3 @@
133
140
  html.rt-theme-dark {
134
141
  @include rt-theme-dark-tokens;
135
142
  }
136
-
137
- /* Body-level градиент — подложка экранов авторизации: их вьюпорт прозрачен
138
- (`--rt-color-bg-surface-auth-card: transparent`) и просвечивает body.
139
- Остальные экраны его не видят — rt-container кроет body непрозрачным фоном
140
- страницы. Не shorthand `background:`, чтобы не сбросить body{}'ный
141
- `background-color`. */
142
- @layer rt-kit.base {
143
- :root[data-theme='dark'] body,
144
- html.rt-theme-dark body {
145
- background-attachment: fixed;
146
- background-image: linear-gradient(156deg, #122341 12.68%, #182142 99.28%);
147
- }
148
- }
149
-
150
- /* rt-logo (чёрный SVG) — инверсия для читаемости на тёмном фоне */
151
- @layer rt-kit.base {
152
- :root[data-theme='dark'] .rt-logo,
153
- html.rt-theme-dark .rt-logo {
154
- filter: invert(1) brightness(1.5);
155
- }
156
- }