@rt-tools/ui-kit-v2 0.8.0 → 0.10.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.
Files changed (69) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2.mjs +608 -82
  2. package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
  3. package/package.json +5 -5
  4. package/rt-tools-ui-kit-v2-0.10.0.tgz +0 -0
  5. package/src/assets/icons/social-facebook.svg +7 -0
  6. package/src/assets/icons/social-instagram.svg +14 -0
  7. package/src/assets/icons/social-kakaotalk.svg +7 -0
  8. package/src/assets/icons/social-line.svg +7 -0
  9. package/src/assets/icons/social-naver.svg +7 -0
  10. package/src/assets/icons/social-pinterest.svg +7 -0
  11. package/src/assets/icons/social-tiktok.svg +7 -0
  12. package/src/assets/icons/social-vk.svg +7 -0
  13. package/src/assets/icons/social-wechat.svg +7 -0
  14. package/src/assets/icons/social-x.svg +7 -0
  15. package/src/assets/icons/social-xiaohongshu.svg +7 -0
  16. package/src/assets/icons/social-youtube.svg +7 -0
  17. package/src/assets/icons-material/NOTICE.md +20 -0
  18. package/src/assets/icons-material/arrow-left.svg +3 -0
  19. package/src/assets/icons-material/ban.svg +5 -0
  20. package/src/assets/icons-material/bars.svg +3 -0
  21. package/src/assets/icons-material/check.svg +3 -0
  22. package/src/assets/icons-material/chevron-down.svg +3 -0
  23. package/src/assets/icons-material/chevron-left.svg +3 -0
  24. package/src/assets/icons-material/chevron-right.svg +3 -0
  25. package/src/assets/icons-material/close.svg +3 -0
  26. package/src/assets/icons-material/cloud-upload.svg +5 -0
  27. package/src/assets/icons-material/cog.svg +5 -0
  28. package/src/assets/icons-material/copy.svg +5 -0
  29. package/src/assets/icons-material/ellipsis-h.svg +5 -0
  30. package/src/assets/icons-material/ellipsis-v.svg +5 -0
  31. package/src/assets/icons-material/external-link.svg +5 -0
  32. package/src/assets/icons-material/eye-slash.svg +5 -0
  33. package/src/assets/icons-material/eye.svg +5 -0
  34. package/src/assets/icons-material/ico-download.svg +5 -0
  35. package/src/assets/icons-material/ico-plus.svg +3 -0
  36. package/src/assets/icons-material/info-circle.svg +5 -0
  37. package/src/assets/icons-material/pencil.svg +5 -0
  38. package/src/assets/icons-material/refresh.svg +5 -0
  39. package/src/assets/icons-material/search.svg +5 -0
  40. package/src/assets/icons-material/star.svg +5 -0
  41. package/src/assets/icons-material/sync.svg +5 -0
  42. package/src/assets/icons-material/table.svg +5 -0
  43. package/src/assets/icons-material/tag.svg +5 -0
  44. package/src/assets/icons-material/trash.svg +5 -0
  45. package/src/assets/icons-material/user.svg +5 -0
  46. package/src/lib/components/aside/CONTEXT.md +15 -0
  47. package/src/lib/components/aside/_rt-aside-overlay.scss +35 -22
  48. package/src/lib/components/aside-section/CONTEXT.md +8 -0
  49. package/src/lib/components/aside-section/rt-aside-section.component.scss +4 -16
  50. package/src/lib/components/button/rt-button.directive.scss +10 -1
  51. package/src/lib/components/icon/CONTEXT.md +23 -0
  52. package/src/lib/components/icon/rt-icon.component.scss +3 -0
  53. package/src/lib/components/icon-button/rt-icon-button.component.scss +31 -1
  54. package/src/lib/components/page-header/CONTEXT.md +19 -7
  55. package/src/lib/components/page-header/rt-page-header-compact.scss +149 -0
  56. package/src/lib/components/photo-viewer/rt-photo-viewer.component.scss +11 -4
  57. package/src/lib/components/ripple/rt-ripple.directive.scss +46 -0
  58. package/src/lib/components/skeleton/rt-skeleton.component.scss +3 -3
  59. package/src/lib/components/tabs/CONTEXT.md +4 -0
  60. package/src/styles/_form.scss +240 -0
  61. package/src/styles/_index.scss +16 -0
  62. package/src/styles/_preset-material.scss +102 -0
  63. package/src/styles/_primitives.scss +74 -4
  64. package/src/styles/_scrollbar.scss +39 -0
  65. package/src/styles/_semantic.scss +35 -24
  66. package/src/styles/_theme-dark.scss +16 -12
  67. package/styles/tokens.css +520 -38
  68. package/types/rt-tools-ui-kit-v2.d.ts +184 -20
  69. package/rt-tools-ui-kit-v2-0.8.0.tgz +0 -0
package/styles/tokens.css CHANGED
@@ -21,10 +21,10 @@
21
21
  Кит его за потребителя не подключает: редактор текста опционален, и тянуть его тему в
22
22
  пакет значило бы отдать её всем. */
23
23
  @layer rt-kit.vendor, rt-kit.base, rt-kit.components;
24
- /* Собрано генератором `tools/build-tokens-v2.mjs` из `tokens.source.mjs` — правится там, не здесь:
25
- правка на месте теряется на следующей сборке, и её называет `pnpm run check:tokens-build`. */
26
- /* Ступени шкал — значения, из которых слой назначений выбирает. Единственное место кита, где
27
- код цвета и размер числом законны по определению. */
24
+ /* Built by the generator `tools/build-tokens-v2.mjs` from `tokens.source.mjs` — edited there, not here:
25
+ an edit on the spot is lost on the next build, and `pnpm run check:tokens-build` names it. */
26
+ /* The scale steps — the values the assignment layer chooses from. The only place in the kit where
27
+ a colour code and a size as a number are lawful by definition. */
28
28
  :root {
29
29
  /* Neutral scale — серая, без цветности.
30
30
 
@@ -282,16 +282,78 @@
282
282
  --rt-bp-md: 768px;
283
283
  --rt-bp-lg: 1080px;
284
284
  --rt-bp-xl: 1380px;
285
+ /* Материальный набор: нейтральный ряд первого кита */
286
+ --rt-mat-neutral-0: #fff;
287
+ --rt-mat-neutral-5: #f5f6f8;
288
+ --rt-mat-neutral-10: #f3f3f3;
289
+ --rt-mat-neutral-15: #eee;
290
+ --rt-mat-neutral-20: #e8e8e8;
291
+ --rt-mat-neutral-25: #e0e0e0;
292
+ --rt-mat-neutral-30: #d1d1d1;
293
+ --rt-mat-neutral-35: #ccc;
294
+ --rt-mat-neutral-40: #a3a3a3;
295
+ --rt-mat-neutral-60: #747474;
296
+ --rt-mat-neutral-80: #323033;
297
+ --rt-mat-neutral-100: #181818;
298
+ /* Материальный набор: синий — действие, ссылка, кольцо фокуса */
299
+ --rt-mat-blue-20: #eaedfc;
300
+ --rt-mat-blue-40: #b3ceef;
301
+ --rt-mat-blue-60: #6d96e8;
302
+ --rt-mat-blue-80: #4285f4;
303
+ --rt-mat-blue-100: #4284d7;
304
+ /* Материальный набор: красный — опасное действие и ошибка */
305
+ --rt-mat-red-10: #fdedee;
306
+ --rt-mat-red-20: #efc0c1;
307
+ --rt-mat-red-40: #f7b9bb;
308
+ --rt-mat-red-60: #e88487;
309
+ --rt-mat-red-80: #df6064;
310
+ --rt-mat-red-100: #eb5055;
311
+ /* Материальный набор: оранжевый — предупреждение */
312
+ --rt-mat-orange-5: #f6e4d9;
313
+ --rt-mat-orange-10: #e8cbbf;
314
+ --rt-mat-orange-20: #e1ba9e;
315
+ --rt-mat-orange-40: #e4a985;
316
+ --rt-mat-orange-60: #f1a05d;
317
+ --rt-mat-orange-70: #f2994a;
318
+ --rt-mat-orange-80: #ee7a34;
319
+ --rt-mat-orange-100: #ef7128;
320
+ /* Материальный набор: зелёный — успех */
321
+ --rt-mat-green-10: #e5f8f4;
322
+ --rt-mat-green-20: #baf4e0;
323
+ --rt-mat-green-40: #5dbfbc;
324
+ --rt-mat-green-60: #46c4c0;
325
+ --rt-mat-green-80: #21b18e;
326
+ --rt-mat-green-100: #01af8d;
327
+ /* Материальный набор: марка первого кита и подложка его обвязки */
328
+ --rt-mat-navy-100: #0d1c2b;
329
+ /* Прозрачные оттенки считаются от своих ступеней, а не повторяют их коды цвета. */
330
+ --rt-mat-overlay-black-4: color-mix(in srgb, var(--rt-mat-neutral-100) 4%, transparent);
331
+ --rt-mat-overlay-black-32: color-mix(in srgb, var(--rt-mat-neutral-100) 32%, transparent);
332
+ --rt-mat-overlay-blue-8: color-mix(in srgb, var(--rt-mat-blue-100) 8%, transparent);
333
+ --rt-mat-overlay-blue-24: color-mix(in srgb, var(--rt-mat-blue-100) 24%, transparent);
334
+ /* Наведение и нажатие первый кит считает от своей ступени тем же приёмом — подмешивая
335
+ чёрное. Ступени здесь считаются так же, а не подбираются на глаз: иначе перекрашенная
336
+ марка перестанет темнеть под курсором вместе с остальным. */
337
+ --rt-mat-blue-hover: color-mix(in srgb, var(--rt-mat-blue-100) 90%, #000);
338
+ --rt-mat-blue-active: color-mix(in srgb, var(--rt-mat-blue-100) 80%, #000);
339
+ --rt-mat-green-hover: color-mix(in srgb, var(--rt-mat-green-80) 90%, #000);
340
+ --rt-mat-green-active: color-mix(in srgb, var(--rt-mat-green-80) 80%, #000);
341
+ --rt-mat-orange-hover: color-mix(in srgb, var(--rt-mat-orange-80) 90%, #000);
342
+ --rt-mat-orange-active: color-mix(in srgb, var(--rt-mat-orange-80) 80%, #000);
343
+ --rt-mat-red-hover: color-mix(in srgb, var(--rt-mat-red-100) 90%, #000);
344
+ --rt-mat-red-active: color-mix(in srgb, var(--rt-mat-red-100) 80%, #000);
345
+ --rt-mat-neutral-hover: color-mix(in srgb, var(--rt-mat-neutral-60) 90%, #000);
346
+ --rt-mat-neutral-active: color-mix(in srgb, var(--rt-mat-neutral-60) 80%, #000);
285
347
  }
286
348
 
287
- /* Собрано генератором `tools/build-tokens-v2.mjs` из `tokens.source.mjs` — правится там, не здесь:
288
- правка на месте теряется на следующей сборке, и её называет `pnpm run check:tokens-build`. */
289
- /* Назначения светлой темы.
349
+ /* Built by the generator `tools/build-tokens-v2.mjs` from `tokens.source.mjs` — edited there, not here:
350
+ an edit on the spot is lost on the next build, and `pnpm run check:tokens-build` names it. */
351
+ /* The light theme's assignments.
290
352
 
291
- Тело вынесено в @mixin по той же причине, что и у тёмной темы: набор нужен не
292
- только на `:root`, но и на отдельном контейнере. Без этого светлую половину
293
- пары «светлая ↔ тёмная» нельзя показать, пока приложение стоит в тёмной теме, —
294
- контейнер унаследует тёмные значения, и обе половины покажут одно и то же. */
353
+ The body is carried into an @mixin for the same reason as the dark theme's: the set is needed not
354
+ only on `:root` but on a separate container too. Without that the light half of the
355
+ pair «light ↔ dark» cannot be shown while the application stands in the dark theme —
356
+ the container would inherit the dark values, and both halves would show the same. */
295
357
  :root {
296
358
  /* Background */
297
359
  --rt-color-bg-page: var(--rt-neutral-50);
@@ -404,16 +466,6 @@
404
466
  --rt-shadow-focus-ring-danger:
405
467
  0 0 0 var(--rt-focus-ring-offset) var(--rt-color-bg-surface),
406
468
  0 0 0 calc(var(--rt-focus-ring-offset) + var(--rt-focus-ring-width)) var(--rt-focus-ring-color-danger);
407
- /* Stepper (rt-stepper) — рельса прогресса. Текущий шаг — янтарный (одинаков
408
- в обеих темах); трек/прогресс переопределяются в dark на приглушённые. */
409
- --rt-color-stepper-current: var(--rt-amber-400); /* rt-theme-shared: янтарь текущего шага один в обеих темах */
410
- --rt-color-stepper-current-subtle: var(--rt-overlay-amber-16); /* rt-theme-shared: янтарь текущего шага один в обеих темах */
411
- --rt-color-stepper-track: var(--rt-gray-350);
412
- --rt-color-stepper-progress: var(--rt-color-action-primary);
413
- /* Step-indicator ring — halo вокруг активного пункта rt-stepper. */
414
- --rt-shadow-step-indicator: 0 0 0 4px var(--rt-color-stepper-current-subtle);
415
- /* Status-dot ring — halo вокруг точки «объект занят сейчас». */
416
- --rt-shadow-status-dot: 0 0 0 3px var(--rt-color-state-success-bg);
417
469
  /* State */
418
470
  --rt-color-state-success: var(--rt-success-500); /* rt-theme-shared: цвет статуса один в обеих темах */
419
471
  --rt-color-state-success-bg: var(--rt-success-50);
@@ -427,6 +479,16 @@
427
479
  --rt-color-state-error-text: var(--rt-danger-600);
428
480
  --rt-color-state-info: var(--rt-info-500); /* rt-theme-shared: цвет статуса один в обеих темах */
429
481
  --rt-color-state-info-bg: var(--rt-info-50);
482
+ /* Stepper (rt-stepper) — рельса прогресса. Текущий шаг — янтарный (одинаков
483
+ в обеих темах); трек/прогресс переопределяются в dark на приглушённые. */
484
+ --rt-color-stepper-current: var(--rt-amber-400); /* rt-theme-shared: янтарь текущего шага один в обеих темах */
485
+ --rt-color-stepper-current-subtle: var(--rt-overlay-amber-16); /* rt-theme-shared: янтарь текущего шага один в обеих темах */
486
+ --rt-color-stepper-track: var(--rt-gray-350);
487
+ --rt-color-stepper-progress: var(--rt-color-action-primary);
488
+ /* Step-indicator ring — halo вокруг активного пункта rt-stepper. */
489
+ --rt-shadow-step-indicator: 0 0 0 4px var(--rt-color-stepper-current-subtle);
490
+ /* Status-dot ring — halo вокруг точки «объект занят сейчас». */
491
+ --rt-shadow-status-dot: 0 0 0 3px var(--rt-color-state-success-bg);
430
492
  /* Chat — пузыри переписки. Входящее и своё различаются фоном, а не рамкой:
431
493
  рамка на каждом пузыре дробит ленту. Отметка прочтения синяя — единственный
432
494
  цветной элемент подвала пузыря, остальные статусы приглушены. */
@@ -439,6 +501,11 @@
439
501
  /* Special — Auth surface (gradient bg + transparent card в dark) */
440
502
  --rt-color-bg-page-auth: var(--rt-neutral-50);
441
503
  --rt-color-bg-surface-auth-card: var(--rt-neutral-0);
504
+ /* Skeleton (rt-skeleton) — заглушка загрузки. Два конца перелива: покой и гребень
505
+ волны. Имена свои, а не роли подложки и рамки: в светлом наборе те ведут на одну краску,
506
+ и перелив между ними стоял ровной заливкой. */
507
+ --rt-color-skeleton-base: var(--rt-neutral-100);
508
+ --rt-color-skeleton-wave: var(--rt-neutral-50);
442
509
  /* Spacing aliases — named semantic aliases на primitive numeric scale */
443
510
  --rt-space-2xs: var(--rt-space-0-5); /* 2px */
444
511
  --rt-space-xs: var(--rt-space-1); /* 4px */
@@ -471,8 +538,12 @@
471
538
  /* Кап ширины одиночного контрола (input/select) в тулбарах list-страниц —
472
539
  внутри flex-баров контрол без ограничения растягивается на всю строку. */
473
540
  --rt-input-max-width: var(--rt-control-max-width);
541
+ /* Скругление контрола — назначение, а не ступень на месте. Кнопка, поле ввода и
542
+ всё, что стоит с ними в строке, берут его отсюда: набор оформления живёт слоем
543
+ назначений и до ступени, взятой компонентом напрямую, не достаёт. */
544
+ --rt-radius-control: var(--rt-radius-lg);
474
545
  /* Radius / spacing / border */
475
- --rt-input-radius: var(--rt-radius-lg);
546
+ --rt-input-radius: var(--rt-radius-control);
476
547
  --rt-input-padding-x: var(--rt-space-4);
477
548
  --rt-input-padding-y: var(--rt-space-0);
478
549
  --rt-input-border-width: var(--rt-border-width-thin);
@@ -624,29 +695,119 @@
624
695
  --rt-field-value-empty-color: var(--rt-color-text-muted);
625
696
  }
626
697
 
627
- /* На тач-устройствах инпут не меньше 16px: WebKit (весь iOS, включая Chrome)
628
- зумит страницу при фокусе на поле с font-size < 16px — отсюда горизонтальный
629
- сдвиг вёрстки. Десктоп сохраняет плотный 14px. */
698
+ /* On touch devices an input is not smaller than 16px: WebKit (all of iOS, Chrome included)
699
+ zooms the page on focusing a field with font-size < 16px — hence the horizontal
700
+ shift of the layout. The desktop keeps the dense 14px. */
630
701
  @media (pointer: coarse) {
631
702
  :root {
632
703
  --rt-input-font-size: var(--rt-text-md);
633
704
  }
634
705
  }
635
- /* Собрано генератором `tools/build-tokens-v2.mjs` из `tokens.source.mjs` — правится там, не здесь:
636
- правка на месте теряется на следующей сборке, и её называет `pnpm run check:tokens-build`. */
637
- /* Тёмная тема — переопределение назначений; ступени шкалы неизменны.
706
+ /* Built by the generator `tools/build-tokens-v2.mjs` from `tokens.source.mjs` — edited there, not here:
707
+ an edit on the spot is lost on the next build, and `pnpm run check:tokens-build` names it. */
708
+ /* The material preset — a second layer of assignments: the kit drawn in the look of the first kit.
709
+
710
+ It does not touch the scale steps: a rewritten step would repaint the dark theme as well,
711
+ and the dark theme refers to those same steps. The values come from the material steps of the scale.
638
712
 
639
- Тело вынесено в @mixin, чтобы тот же набор переопределений можно было
640
- применить не только на html-уровне (тема приложения), но и к локальному
641
- контейнеру (side-by-side light/dark в Storybook) — без дублирования значений.
713
+ The body is taken into a @mixin for the same reason as the dark theme's: the preset is needed not
714
+ only on `:root` but on a separate container too — otherwise two presets cannot be shown side by side.
715
+
716
+ The preset rule is declared before the dark theme on purpose: the root signs have equal
717
+ specificity, and the order in the file is the only thing by which the dark theme wins over the preset. */
718
+ :root[data-preset=material],
719
+ [data-preset=material],
720
+ .rt-preset-material {
721
+ /* Подложки */
722
+ --rt-color-bg-page: var(--rt-mat-neutral-5);
723
+ --rt-color-bg-surface: var(--rt-mat-neutral-0);
724
+ --rt-color-bg-surface-subtle: var(--rt-mat-overlay-black-4);
725
+ --rt-color-bg-surface-subtle-2: var(--rt-mat-neutral-15);
726
+ --rt-color-bg-hover: var(--rt-mat-neutral-10);
727
+ --rt-color-bg-subtle: var(--rt-mat-neutral-10);
728
+ --rt-color-bg-overlay: var(--rt-mat-overlay-black-32);
729
+ --rt-color-bg-inverse: var(--rt-mat-neutral-100);
730
+ --rt-color-bg-nav: var(--rt-mat-navy-100); /* марка первого кита: обвязка тёмная в обоих наборах */
731
+ --rt-color-bg-page-auth: var(--rt-mat-neutral-5);
732
+ --rt-color-bg-surface-auth-card: var(--rt-mat-neutral-0);
733
+ /* Текст */
734
+ --rt-color-text-primary: var(--rt-mat-neutral-100);
735
+ --rt-color-text-muted: var(--rt-mat-neutral-60);
736
+ --rt-color-text-disabled: var(--rt-mat-neutral-40);
737
+ --rt-color-text-inverse: var(--rt-mat-neutral-0);
738
+ --rt-color-text-link: var(--rt-mat-blue-100);
739
+ --rt-color-text-link-hover: var(--rt-mat-blue-hover);
740
+ /* Рамки */
741
+ --rt-color-border-default: var(--rt-mat-neutral-25);
742
+ --rt-color-border-strong: var(--rt-mat-neutral-30);
743
+ --rt-color-border-subtle: var(--rt-mat-neutral-20);
744
+ --rt-color-border-focus: var(--rt-mat-blue-40); /* кольцо фокуса первого кита светлее его действия */
745
+ /* Действие: подложка, наведение, нажатие, подпись на них и цвет роли на поверхности */
746
+ --rt-color-action-primary: var(--rt-mat-blue-100);
747
+ --rt-color-action-primary-hover: var(--rt-mat-blue-hover);
748
+ --rt-color-action-primary-active: var(--rt-mat-blue-active);
749
+ --rt-color-action-primary-subtle: var(--rt-mat-overlay-blue-8);
750
+ --rt-color-action-primary-soft: var(--rt-mat-overlay-blue-24);
751
+ --rt-color-action-on-primary: var(--rt-mat-neutral-0);
752
+ --rt-color-action-secondary: var(--rt-mat-neutral-60);
753
+ --rt-color-action-secondary-hover: var(--rt-mat-neutral-hover);
754
+ --rt-color-action-secondary-active: var(--rt-mat-neutral-active);
755
+ --rt-color-action-on-secondary: var(--rt-mat-neutral-0);
756
+ --rt-color-action-secondary-on-surface: var(--rt-mat-neutral-60);
757
+ --rt-color-action-success: var(--rt-mat-green-80);
758
+ --rt-color-action-success-hover: var(--rt-mat-green-hover);
759
+ --rt-color-action-success-active: var(--rt-mat-green-active);
760
+ --rt-color-action-on-success: var(--rt-mat-neutral-0);
761
+ --rt-color-action-warning: var(--rt-mat-orange-80);
762
+ --rt-color-action-warning-hover: var(--rt-mat-orange-hover);
763
+ --rt-color-action-warning-active: var(--rt-mat-orange-active);
764
+ --rt-color-action-on-warning: var(--rt-mat-neutral-0);
765
+ --rt-color-action-danger: var(--rt-mat-red-100);
766
+ --rt-color-action-danger-hover: var(--rt-mat-red-hover);
767
+ --rt-color-action-danger-active: var(--rt-mat-red-active);
768
+ --rt-color-action-on-danger: var(--rt-mat-neutral-0);
769
+ --rt-color-action-info: var(--rt-mat-blue-100);
770
+ --rt-color-action-info-hover: var(--rt-mat-blue-hover);
771
+ --rt-color-action-info-active: var(--rt-mat-blue-active);
772
+ --rt-color-action-on-info: var(--rt-mat-neutral-0);
773
+ --rt-color-action-disabled: var(--rt-mat-neutral-25);
774
+ /* Состояния: цвет роли и её светлая подложка */
775
+ --rt-color-state-success: var(--rt-mat-green-100);
776
+ --rt-color-state-success-bg: var(--rt-mat-green-10);
777
+ --rt-color-state-warning: var(--rt-mat-orange-100);
778
+ --rt-color-state-warning-bg: var(--rt-mat-orange-5);
779
+ --rt-color-state-warning-text: var(--rt-mat-orange-100);
780
+ --rt-color-state-danger: var(--rt-mat-red-100);
781
+ --rt-color-state-danger-bg: var(--rt-mat-red-10);
782
+ --rt-color-state-error-text: var(--rt-mat-red-100);
783
+ --rt-color-state-info: var(--rt-mat-blue-100);
784
+ --rt-color-state-info-bg: var(--rt-mat-blue-20);
785
+ /* Рельса шагов */
786
+ --rt-color-stepper-track: var(--rt-mat-neutral-30);
787
+ /* Заглушка загрузки: краски те же, что набор давал ей прежде через подложку и
788
+ рамку, — вид под набором от разведения концов не поехал. */
789
+ --rt-color-skeleton-base: var(--rt-mat-neutral-10);
790
+ --rt-color-skeleton-wave: var(--rt-mat-neutral-25);
791
+ /* Кнопка и поле первого кита скруглены заметно сильнее: 1.5rem против 10px.
792
+ Значение снято числом — ссылки на токены первого кита во второй кит не едут. */
793
+ --rt-radius-control: 1.5rem;
794
+ }
642
795
 
643
- База (`rt-theme-dark-tokens`) — тёплый графит для приложений и всех
644
- CDK-overlay'ев (асайды/диалоги/меню рендерятся на уровне body, вне
645
- host-классов шеллов — поэтому графит держим на :root, иначе overlay'и
646
- не наследовали бы палитру).
796
+ /* Built by the generator `tools/build-tokens-v2.mjs` from `tokens.source.mjs` — edited there, not here:
797
+ an edit on the spot is lost on the next build, and `pnpm run check:tokens-build` names it. */
798
+ /* The dark theme — an override of the assignments; the scale steps are unchanging.
647
799
 
648
- Значения ответов живут в источнике рядом со своим светлым назначением: забытая половина
649
- пары видна там же, а не вылавливается сверкой двух файлов. */
800
+ The body is carried into an @mixin so that the same set of overrides could be
801
+ applied not only at the html level (the application's theme) but to a local
802
+ container too (side-by-side light/dark in Storybook) — without duplicating the values.
803
+
804
+ The base (`rt-theme-dark-tokens`) is warm graphite for the applications and all the
805
+ CDK overlays (asides/dialogs/menus are rendered at the body level, outside the
806
+ shells' host classes — so the graphite is held on :root, otherwise the overlays
807
+ would not inherit the palette).
808
+
809
+ The values of the answers live in the source next to their light assignment: a forgotten half of
810
+ a pair is visible right there rather than caught by matching two files. */
650
811
  :root[data-theme=dark],
651
812
  html.rt-theme-dark {
652
813
  /* Background — тёплый графит */
@@ -750,6 +911,9 @@ html.rt-theme-dark {
750
911
  /* Special — Auth surface (градиентный фон логина + прозрачная карточка). */
751
912
  --rt-color-bg-page-auth: linear-gradient(156deg, var(--rt-neutral-950) 12.68%, var(--rt-navy-900) 99.28%);
752
913
  --rt-color-bg-surface-auth-card: transparent;
914
+ /* Skeleton — заглушка загрузки: покой и гребень волны. */
915
+ --rt-color-skeleton-base: var(--rt-charcoal-950);
916
+ --rt-color-skeleton-wave: var(--rt-charcoal-border);
753
917
  }
754
918
 
755
919
  /* Правила тёмной темы, которые свойств не объявляют. Стоят отдельно от `_theme-dark.scss`:
@@ -774,6 +938,14 @@ html.rt-theme-dark {
774
938
  }
775
939
  }
776
940
  @layer rt-kit.base {
941
+ * {
942
+ scrollbar-color: transparent transparent;
943
+ scrollbar-width: thin;
944
+ }
945
+ *:hover,
946
+ *:focus-within {
947
+ scrollbar-color: var(--rt-color-border-default) transparent;
948
+ }
777
949
  ::-webkit-scrollbar {
778
950
  width: 12px;
779
951
  }
@@ -783,6 +955,11 @@ html.rt-theme-dark {
783
955
  ::-webkit-scrollbar-thumb {
784
956
  border: var(--rt-border-width-thick) solid transparent;
785
957
  border-radius: var(--rt-radius-md);
958
+ background: transparent;
959
+ background-clip: content-box;
960
+ }
961
+ *:hover::-webkit-scrollbar-thumb,
962
+ *:focus-within::-webkit-scrollbar-thumb {
786
963
  background: var(--rt-color-border-default);
787
964
  background-clip: content-box;
788
965
  }
@@ -794,6 +971,15 @@ html.rt-theme-dark {
794
971
  ::-webkit-scrollbar-track:hover {
795
972
  border: var(--rt-border-width-thin) solid var(--rt-color-bg-subtle);
796
973
  }
974
+ @media (hover: none) {
975
+ * {
976
+ scrollbar-color: var(--rt-color-border-default) transparent;
977
+ }
978
+ ::-webkit-scrollbar-thumb {
979
+ background: var(--rt-color-border-default);
980
+ background-clip: content-box;
981
+ }
982
+ }
797
983
  }
798
984
  @layer rt-kit.base {
799
985
  .login__viewport {
@@ -883,6 +1069,257 @@ html.rt-theme-dark {
883
1069
  outline-offset: 2px;
884
1070
  }
885
1071
  }
1072
+ /* Словарь формы панели: раздел, колонка контролов, строка контрола, элемент в ряду. Объявлен
1073
+ классами, а не компонентом, поэтому доехать до потребителя может только через агрегатор
1074
+ стилей. Иерархия одна на кит: до неё каждое приложение заводило свою, и одно имя значило в
1075
+ двух деревьях разное — `controls` было и колонкой контролов раздела, и полосой кнопок внизу
1076
+ панели.
1077
+
1078
+ Блок формы объявляется ровно один раз на панель и стоит НАД полосой вкладок: вкладки лежат
1079
+ внутри формы, а не наоборот. Носителем бывает и `<form>`, и `<div>` — форма Angular и блок
1080
+ формы разные вещи, и `[formGroup]` законно живёт ниже, обёрткой над одной строкой контрола.
1081
+
1082
+ Промежуток задаёт контейнер своим зазором, а своего отступа нет ни у одного элемента. Так у
1083
+ каждого промежутка один источник: зазор не достаётся первому ребёнку, не складывается с
1084
+ зазором зоны содержимого панели и действует на проецируемое содержимое наравне со своей
1085
+ разметкой — а правило соседства знает только те имена, которые перечислены в нём самом. */
1086
+ @layer rt-kit.components {
1087
+ .rt-form {
1088
+ display: flex;
1089
+ width: 100%;
1090
+ flex: 1 1 100%;
1091
+ flex-direction: column;
1092
+ gap: var(--rt-space-xl);
1093
+ /* Раздел: смысловая группа полей. Вкладывается в себя — цвет внутри сезона остаётся
1094
+ разделом, а не заводит третье имя. */
1095
+ }
1096
+ .rt-form__item {
1097
+ display: flex;
1098
+ width: 100%;
1099
+ flex-direction: column;
1100
+ gap: var(--rt-space-xl);
1101
+ }
1102
+ .rt-form {
1103
+ /* Колонка контролов раздела: всё, что в разделе выстроено сверху вниз. Её зазор — ритм
1104
+ строк, и он же отбивает заголовок раздела от первой строки. */
1105
+ }
1106
+ .rt-form__controls {
1107
+ position: relative;
1108
+ display: flex;
1109
+ width: 100%;
1110
+ flex-direction: column;
1111
+ gap: var(--rt-space-md);
1112
+ }
1113
+ .rt-form {
1114
+ /* Заголовок раздела и описание под ним. Оба живут внутри колонки контролов: подпись
1115
+ принадлежит столбцу, а не разделу целиком. */
1116
+ }
1117
+ .rt-form__title {
1118
+ margin: 0;
1119
+ color: var(--rt-color-text-primary);
1120
+ font-size: var(--rt-text-md);
1121
+ font-weight: var(--rt-font-weight-semibold);
1122
+ line-height: var(--rt-leading-snug);
1123
+ }
1124
+ .rt-form__title-descr {
1125
+ margin: 0;
1126
+ color: var(--rt-color-text-muted);
1127
+ font-size: var(--rt-text-sm);
1128
+ line-height: var(--rt-leading-normal);
1129
+ }
1130
+ .rt-form {
1131
+ /* Строка контрола: один ряд колонки. Поле занимает строку целиком — иначе каждый
1132
+ потребитель дописывает ширину сам. Зазор строки разводит элементы в ряду. */
1133
+ }
1134
+ .rt-form__control-item {
1135
+ display: flex;
1136
+ width: 100%;
1137
+ align-items: center;
1138
+ justify-content: flex-start;
1139
+ gap: var(--rt-space-xl);
1140
+ /* Поле, стоящее в строке одно, занимает её целиком. Без этого оно получает ширину
1141
+ по содержимому и обрывается посреди строки, оставляя справа пустое место. */
1142
+ }
1143
+ .rt-form__control-item > rt-field {
1144
+ min-width: 0;
1145
+ flex: 1 1 auto;
1146
+ }
1147
+ .rt-form {
1148
+ /* Строка, разведённая по краям: подпись у начала, значение у конца. Так стоит
1149
+ карточка просмотра — записи в ней показывают, а не правят. */
1150
+ }
1151
+ .rt-form__control-item--split {
1152
+ justify-content: space-between;
1153
+ }
1154
+ .rt-form {
1155
+ /* Строка колонкой: подпись сверху, контрол под ней. Берётся там, где подпись рядом с
1156
+ контролом не встаёт — в узкой панели три состояния права рядом с его именем
1157
+ переносились бы по слогам. */
1158
+ }
1159
+ .rt-form__control-item--stack {
1160
+ flex-direction: column;
1161
+ align-items: stretch;
1162
+ gap: var(--rt-space-xs);
1163
+ }
1164
+ .rt-form {
1165
+ /* Элемент в ряду: два и больше контролов, стоящих рядом в одной строке. Нулевая нижняя
1166
+ граница ширины обязательна: без неё длинное значение не даёт строке перенестись, и
1167
+ соседи сжимаются до столбика по одной букве. */
1168
+ }
1169
+ .rt-form__control-sub-item {
1170
+ display: flex;
1171
+ min-width: 0;
1172
+ align-items: center;
1173
+ justify-content: flex-start;
1174
+ /* Поле внутри элемента ряда занимает его целиком. Без этого элемент растянут своим
1175
+ видом, а поле в нём стоит по ширине содержимого: в панели внешней брони шесть
1176
+ полей занимали меньше половины своей колонки. */
1177
+ }
1178
+ .rt-form__control-sub-item > rt-field {
1179
+ min-width: 0;
1180
+ flex: 1 1 auto;
1181
+ }
1182
+ .rt-form {
1183
+ /* Подпись в паре «подпись — значение»: приглушённый цвет отличает её от значения. */
1184
+ }
1185
+ .rt-form__control-sub-item--label {
1186
+ color: var(--rt-color-text-muted);
1187
+ font-size: var(--rt-text-sm);
1188
+ }
1189
+ .rt-form {
1190
+ /* Значение рядом с подписью: длинное переносится в любом месте — адрес почты и имя
1191
+ процедуры по словам не переносятся. */
1192
+ }
1193
+ .rt-form__control-sub-item--value {
1194
+ color: var(--rt-color-text-primary);
1195
+ font-size: var(--rt-text-sm);
1196
+ overflow-wrap: anywhere;
1197
+ }
1198
+ .rt-form {
1199
+ /* Элемент, делящий ширину строки поровну с соседями: пара дат встаёт одной ширины, а не
1200
+ по ширине своего содержимого. */
1201
+ }
1202
+ .rt-form__control-sub-item--grow {
1203
+ flex: 1;
1204
+ }
1205
+ .rt-form {
1206
+ /* Значение снятой записи: адреса, которого больше нет. */
1207
+ }
1208
+ .rt-form__control-sub-item--gone {
1209
+ color: var(--rt-color-text-muted);
1210
+ }
1211
+ .rt-form {
1212
+ /* Приписка о неудаче: приёмник запрос не принял. */
1213
+ }
1214
+ .rt-form__control-sub-item--warning {
1215
+ color: var(--rt-color-state-warning);
1216
+ }
1217
+ .rt-form {
1218
+ /* Подпись над контролом — там, где своей подписи у контрола нет. */
1219
+ }
1220
+ .rt-form__label {
1221
+ display: block;
1222
+ color: var(--rt-color-text-primary);
1223
+ font-size: var(--rt-text-sm);
1224
+ font-weight: var(--rt-font-weight-medium);
1225
+ }
1226
+ .rt-form {
1227
+ /* Ссылка на связанную запись: обычная ссылка с иконкой перехода. */
1228
+ }
1229
+ .rt-form__related {
1230
+ display: inline-flex;
1231
+ align-items: center;
1232
+ align-self: flex-start;
1233
+ color: var(--rt-color-text-link);
1234
+ font-size: var(--rt-text-sm);
1235
+ gap: var(--rt-space-xs);
1236
+ }
1237
+ .rt-form {
1238
+ /* Полоса меток записи: их бывает много, и в одну строку они не влезают. */
1239
+ }
1240
+ .rt-form__tags {
1241
+ display: flex;
1242
+ flex-flow: row wrap;
1243
+ align-items: center;
1244
+ gap: var(--rt-space-xs);
1245
+ }
1246
+ .rt-form {
1247
+ /* Строки одной службы в общей рамке: ключей у службы бывает два, и рамка на каждом
1248
+ читалась бы как две разные карточки. */
1249
+ }
1250
+ .rt-form__framed-list {
1251
+ display: flex;
1252
+ width: 100%;
1253
+ flex-direction: column;
1254
+ border: var(--rt-border-width-thin) solid var(--rt-color-border-subtle);
1255
+ border-radius: var(--rt-radius-md);
1256
+ }
1257
+ .rt-form__framed-list > * + * {
1258
+ border-top: var(--rt-border-width-thin) solid var(--rt-color-border-subtle);
1259
+ }
1260
+ .rt-form {
1261
+ /* Длинное тело запроса или ответа: по словам оно не переносится и прокручивается внутри
1262
+ своего блока, чтобы не растягивать панель. */
1263
+ }
1264
+ .rt-form__code {
1265
+ overflow: auto;
1266
+ padding: var(--rt-space-xs);
1267
+ border-radius: var(--rt-radius-sm);
1268
+ margin: 0;
1269
+ background: var(--rt-color-bg-surface-subtle);
1270
+ color: var(--rt-color-text-primary);
1271
+ font-size: var(--rt-text-xs);
1272
+ max-block-size: 12rem;
1273
+ overflow-wrap: anywhere;
1274
+ white-space: pre-wrap;
1275
+ }
1276
+ .rt-form {
1277
+ /* Просмотр готового документа: лист виден целиком, рамка отделяет его от полей. */
1278
+ }
1279
+ .rt-form__preview {
1280
+ width: 100%;
1281
+ height: 460px;
1282
+ flex-shrink: 0;
1283
+ border: var(--rt-border-width-thin) solid var(--rt-color-border-subtle);
1284
+ border-radius: var(--rt-radius-sm);
1285
+ }
1286
+ .rt-form {
1287
+ /* Запись внутри общей рамки: свой внутренний отступ и ритм строк. Без него строки
1288
+ записей лежат вплотную к рамке и к соседней записи, и перечень читается сплошной
1289
+ лентой слов. */
1290
+ }
1291
+ .rt-form__framed-item {
1292
+ display: flex;
1293
+ flex-direction: column;
1294
+ padding: var(--rt-space-sm) var(--rt-space-md);
1295
+ gap: var(--rt-space-xs);
1296
+ }
1297
+ .rt-form {
1298
+ /* Маячок догрузки в конце списка: он невидим и только отмечает низ, до которого
1299
+ дочитали. */
1300
+ }
1301
+ .rt-form__sentinel {
1302
+ block-size: 1px;
1303
+ }
1304
+ .rt-form {
1305
+ /* Зона содержимого вкладки внутри формы: разделы лежат в ней, и ритм у них тот же, что
1306
+ у разделов вне вкладок. Своим зазором зона вкладок отбивает произвольное содержимое,
1307
+ и разделы формы получали от неё чужой шаг — панель с вкладками и панель без них
1308
+ показывали одни и те же разделы по-разному. */
1309
+ }
1310
+ .rt-form .rt-tabs__content {
1311
+ gap: var(--rt-space-xl);
1312
+ }
1313
+ /* На узком экране панель занимает весь экран, и ритм разделов сжимается вдвое. Порог тот же,
1314
+ что у самой панели. Ритм строк не трогается: строки и на узком идут тем же шагом. */
1315
+ @media (width <= 768px) {
1316
+ .rt-form,
1317
+ .rt-form__item,
1318
+ .rt-form .rt-tabs__content {
1319
+ gap: var(--rt-space-md);
1320
+ }
1321
+ }
1322
+ }
886
1323
  @layer rt-kit.components {
887
1324
  .rt-button {
888
1325
  /* Высота приходит общей шкалой контролов, а не своим числом: кнопка, поле и
@@ -890,7 +1327,7 @@ html.rt-theme-dark {
890
1327
  --rt-btn-height: var(--rt-control-height-md);
891
1328
  --rt-btn-padding: 0 1.25rem;
892
1329
  --rt-btn-gap: 0.5rem;
893
- --rt-btn-radius: var(--rt-radius-lg);
1330
+ --rt-btn-radius: var(--rt-radius-control);
894
1331
  --rt-btn-border-width: var(--rt-border-width-thin);
895
1332
  --rt-btn-transition-duration: var(--rt-duration-fast);
896
1333
  --rt-btn-focus-shadow: var(--rt-shadow-focus-ring);
@@ -939,6 +1376,9 @@ html.rt-theme-dark {
939
1376
  .rt-button:active:not(:disabled) {
940
1377
  background: var(--rt-btn-bg-active);
941
1378
  }
1379
+ .rt-button--pressed {
1380
+ background: var(--rt-btn-bg-active);
1381
+ }
942
1382
  .rt-button:focus-visible {
943
1383
  box-shadow: var(--rt-btn-focus-shadow);
944
1384
  outline: 2px solid transparent;
@@ -1169,3 +1609,45 @@ html.rt-theme-dark {
1169
1609
  }
1170
1610
  }
1171
1611
  }
1612
+ @layer rt-kit.components {
1613
+ /* Позиции здесь нет намеренно. Блок висит на каждой кнопке кита, и `position: relative`,
1614
+ поставленный всем подряд, меняет растеризацию у восемнадцати кадров витрины, ничего не
1615
+ двигая в раскладке. Позицию ставит сама директива — тому элементу, который нажали, и
1616
+ только если он до сих пор `static`. */
1617
+ .rt-ripple {
1618
+ --rt-ripple-color: currentcolor;
1619
+ --rt-ripple-opacity: 0.24;
1620
+ --rt-ripple-duration: 550ms;
1621
+ --rt-ripple-radius: var(--rt-radius-full);
1622
+ }
1623
+ /* Обрезает волну по форме элемента. Отдельным слоем, а не overflow на самом
1624
+ элементе: у кнопки-значка метка вынесена за край, и она была бы срезана. */
1625
+ .rt-ripple__area {
1626
+ position: absolute;
1627
+ overflow: hidden;
1628
+ border-radius: inherit;
1629
+ inset: 0;
1630
+ pointer-events: none;
1631
+ }
1632
+ .rt-ripple__wave {
1633
+ position: absolute;
1634
+ border-radius: var(--rt-ripple-radius);
1635
+ animation: rt-ripple-spread var(--rt-ripple-duration) ease-out forwards;
1636
+ background-color: var(--rt-ripple-color);
1637
+ opacity: var(--rt-ripple-opacity);
1638
+ transform: scale(0);
1639
+ }
1640
+ /* Волна гаснет вместе с остальным движением: тот, кто попросил систему не
1641
+ двигать интерфейс, получает подсветку без расхождения круга. */
1642
+ @media (prefers-reduced-motion: reduce) {
1643
+ .rt-ripple__wave {
1644
+ animation-duration: 1ms;
1645
+ }
1646
+ }
1647
+ @keyframes rt-ripple-spread {
1648
+ to {
1649
+ opacity: 0;
1650
+ transform: scale(1);
1651
+ }
1652
+ }
1653
+ }