hallo-kit 0.3.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 (128) hide show
  1. package/README.md +168 -0
  2. package/bin/cli.mjs +136 -0
  3. package/fonts/Manrope/Manrope-VariableFont_wght.woff2 +0 -0
  4. package/fonts/Manrope/OFL.txt +93 -0
  5. package/fonts/index.ts +30 -0
  6. package/package.json +77 -0
  7. package/src/config.ts +11 -0
  8. package/src/hooks/index.ts +14 -0
  9. package/src/hooks/useAutoHideOnScroll.ts +26 -0
  10. package/src/hooks/useCountdown.ts +44 -0
  11. package/src/hooks/useDevice.ts +63 -0
  12. package/src/hooks/useIsTelegramMiniApp.ts +17 -0
  13. package/src/hooks/useIsTouch.ts +23 -0
  14. package/src/hooks/useKeyboardOpen.ts +35 -0
  15. package/src/hooks/useMediaQuery.ts +24 -0
  16. package/src/hooks/usePlatform.ts +102 -0
  17. package/src/hooks/useQueryParams.tsx +70 -0
  18. package/src/hooks/useRevealOnce.ts +21 -0
  19. package/src/hooks/useScrolledPast.ts +19 -0
  20. package/src/hooks/useSettleOnce.ts +26 -0
  21. package/src/hooks/useTelegram.tsx +57 -0
  22. package/src/i18n/index.tsx +92 -0
  23. package/src/index.ts +35 -0
  24. package/src/lib/device.ts +28 -0
  25. package/src/lib/platform.ts +19 -0
  26. package/src/lib/utils.ts +36 -0
  27. package/src/logo/LogoFull.tsx +59 -0
  28. package/src/logo/LogoMark.tsx +31 -0
  29. package/src/logo/index.ts +2 -0
  30. package/src/next.ts +13 -0
  31. package/src/server.ts +4 -0
  32. package/src/theme-color-sync.tsx +43 -0
  33. package/src/theme-provider.tsx +31 -0
  34. package/src/types/css.d.ts +6 -0
  35. package/src/types/globals.d.ts +47 -0
  36. package/src/ui/Icon/Icon.module.css +25 -0
  37. package/src/ui/Icon/Icon.tsx +45 -0
  38. package/src/ui/Icon/Icon.types.ts +60 -0
  39. package/src/ui/Icon/data/brand.data.tsx +75 -0
  40. package/src/ui/Icon/data/index.ts +2 -0
  41. package/src/ui/Icon/data/ui.data.tsx +780 -0
  42. package/src/ui/Icon/index.ts +3 -0
  43. package/src/ui/badge/badge.tsx +185 -0
  44. package/src/ui/badge/index.ts +1 -0
  45. package/src/ui/breadcrumbs/breadcrumbs.tsx +63 -0
  46. package/src/ui/breadcrumbs/index.ts +1 -0
  47. package/src/ui/burger-icon/burger-icon.tsx +22 -0
  48. package/src/ui/burger-icon/index.ts +1 -0
  49. package/src/ui/button/button.tsx +179 -0
  50. package/src/ui/button/index.ts +1 -0
  51. package/src/ui/card/card.tsx +100 -0
  52. package/src/ui/card/index.ts +1 -0
  53. package/src/ui/confirm/confirm-dialog-host.tsx +89 -0
  54. package/src/ui/confirm/index.ts +1 -0
  55. package/src/ui/count-up/count-up.tsx +96 -0
  56. package/src/ui/count-up/index.ts +1 -0
  57. package/src/ui/data-list/data-list.tsx +331 -0
  58. package/src/ui/data-list/index.ts +1 -0
  59. package/src/ui/drawer/drawer.tsx +166 -0
  60. package/src/ui/drawer/index.ts +1 -0
  61. package/src/ui/flag/flag.data.ts +240 -0
  62. package/src/ui/flag/flag.tsx +57 -0
  63. package/src/ui/flag/index.ts +1 -0
  64. package/src/ui/image/image.tsx +67 -0
  65. package/src/ui/image/index.ts +1 -0
  66. package/src/ui/input/amount-input.tsx +55 -0
  67. package/src/ui/input/index.ts +2 -0
  68. package/src/ui/input/input.tsx +391 -0
  69. package/src/ui/label/index.ts +1 -0
  70. package/src/ui/label/label.tsx +34 -0
  71. package/src/ui/method-card/index.ts +1 -0
  72. package/src/ui/method-card/method-card.tsx +101 -0
  73. package/src/ui/modal/index.ts +1 -0
  74. package/src/ui/modal/modal.tsx +128 -0
  75. package/src/ui/otp-input/index.ts +1 -0
  76. package/src/ui/otp-input/otp-input.tsx +78 -0
  77. package/src/ui/pagination/index.ts +1 -0
  78. package/src/ui/pagination/pagination.tsx +138 -0
  79. package/src/ui/radio-group/index.ts +1 -0
  80. package/src/ui/radio-group/radio-group.tsx +81 -0
  81. package/src/ui/select/index.ts +1 -0
  82. package/src/ui/select/select.tsx +215 -0
  83. package/src/ui/separator/index.ts +1 -0
  84. package/src/ui/separator/separator.tsx +54 -0
  85. package/src/ui/skeleton/index.ts +1 -0
  86. package/src/ui/skeleton/skeleton.tsx +44 -0
  87. package/src/ui/spinner/index.ts +1 -0
  88. package/src/ui/spinner/spinner.tsx +33 -0
  89. package/src/ui/switch/index.ts +1 -0
  90. package/src/ui/switch/switch.tsx +48 -0
  91. package/src/ui/tabs/index.ts +1 -0
  92. package/src/ui/tabs/tabs.tsx +190 -0
  93. package/src/ui/textarea/index.ts +1 -0
  94. package/src/ui/textarea/textarea.tsx +151 -0
  95. package/src/ui/theme-switch/index.ts +1 -0
  96. package/src/ui/theme-switch/theme-switch.tsx +161 -0
  97. package/src/ui/toast/index.ts +1 -0
  98. package/src/ui/toast/toast.tsx +159 -0
  99. package/src/utils/api/stringFormat.ts +5 -0
  100. package/src/utils/clearAllCookies.ts +7 -0
  101. package/src/utils/clsx.ts +18 -0
  102. package/src/utils/confirm.ts +48 -0
  103. package/src/utils/copy-to-clipboard.ts +12 -0
  104. package/src/utils/date/convertTimestamp.ts +44 -0
  105. package/src/utils/date/formatCountdown.ts +9 -0
  106. package/src/utils/date/formatDayLabel.ts +50 -0
  107. package/src/utils/date/formatRelativeDateTime.ts +34 -0
  108. package/src/utils/date/locales.ts +9 -0
  109. package/src/utils/form/validationSchema.ts +46 -0
  110. package/src/utils/getByKeyString.ts +15 -0
  111. package/src/utils/getCssVar.ts +2 -0
  112. package/src/utils/getDeepType.ts +3 -0
  113. package/src/utils/getQueryParams.ts +21 -0
  114. package/src/utils/inAppBrowser.ts +151 -0
  115. package/src/utils/index.ts +28 -0
  116. package/src/utils/isRTL.ts +29 -0
  117. package/src/utils/notify.ts +46 -0
  118. package/src/utils/openExternal.ts +27 -0
  119. package/src/utils/system/getMillisecondsPeriod.ts +31 -0
  120. package/src/utils/validation/authValidators.ts +23 -0
  121. package/src/utils/validation/refCode.ts +30 -0
  122. package/styles/base.css +97 -0
  123. package/styles/brand.css +36 -0
  124. package/styles/index.css +19 -0
  125. package/styles/motion.css +207 -0
  126. package/styles/semantic.css +151 -0
  127. package/styles/theme.css +143 -0
  128. package/styles/utilities.css +289 -0
@@ -0,0 +1,151 @@
1
+ /* СЕМАНТИЧЕСКИЙ СЛОЙ — контракт дизайн-системы (light + dark).
2
+ Имена по назначению; тема меняет значения, классы остаются те же.
3
+ Источник: hallo/client — src/app/globals.css. Файл раздаётся пакетом @hallo/kit;
4
+ правки вносить ЗДЕСЬ, кабинет и сайты получают их обновлением пакета. */
5
+
6
+ /* Тёмная тема — классом .dark на <html> (его ставит next-themes, attribute="class"). */
7
+ @custom-variant dark (&:where(.dark, .dark *));
8
+
9
+ :root {
10
+ /* Схема для НАТИВНОЙ отрисовки WebKit/Blink (каретка ввода, скроллбары,
11
+ автозаполнение). Тему мы переключаем классом `.dark`, а не media, поэтому без
12
+ явного color-scheme движок берёт СИСТЕМНУЮ схему — в Telegram MiniApp на iOS
13
+ WebView навязывает свою (обычно dark), из-за чего на светлой теме каретка в
14
+ инпутах белая = невидимая. Привязываем схему к теме → каретка красится верно. */
15
+ color-scheme: light;
16
+
17
+ /* ============================================================
18
+ СЕМАНТИЧЕСКИЙ СЛОЙ — светлая тема.
19
+ Имена по назначению; тема меняет значения, а классы остаются те же.
20
+ ============================================================ */
21
+ /* поверхности и текст */
22
+ --background: var(--sky-mist); /* Sky Mist — весь задний фон страницы */
23
+ --foreground: var(--deep-navy);
24
+ --card: var(
25
+ --cloud-white
26
+ ); /* Cloud White — блок/карточка поверх фона, внутри контент */
27
+ --card-foreground: var(--deep-navy);
28
+ --popover: var(--cloud-white); /* всплывающие поверхности — тоже белые */
29
+ --popover-foreground: var(--deep-navy);
30
+
31
+ /* приглушённые элементы (вторичный текст, фоны-заглушки) */
32
+ --muted: var(--sky-mist);
33
+ --muted-foreground: #556a92; /* вторичный текст */
34
+ /* Здесь стоял --disabled-blue (#8ea6d8) с пометкой «при нехватке контраста
35
+ заменить». Замерено: 2.42:1 на карточке (#fefefe), 2.17:1 на фоне страницы
36
+ (#eaf2ff), 2.23:1 на soft-карточках — при пороге WCAG AA 4.5:1 для обычного
37
+ текста. Этим токеном набран весь вторичный текст кабинета: подписи полей,
38
+ описания карточек, подвал, пустые состояния. #556a92 даёт 5.39 / 4.83 / 4.96.
39
+ Роль «неактивный контрол» осталась за --disabled, он не менялся.
40
+ В тёмной теме токен другой, там 7.2:1 — её не трогаем. */
41
+
42
+ /* границы, поля ввода, кольцо фокуса */
43
+ --border: var(--ice-blue);
44
+ --input: var(--ice-blue);
45
+ --ring: var(--brand-blue);
46
+
47
+ /* основной бренд */
48
+ --primary: var(--brand-blue);
49
+ --primary-foreground: var(--cloud-white);
50
+ /* вторичный / акцентный фон (hover-состояния, чипы) */
51
+ --secondary: var(--sky-mist);
52
+ --secondary-foreground: var(--deep-navy);
53
+ --accent: var(--ice-blue);
54
+ --accent-foreground: var(--deep-navy);
55
+
56
+ /* статус: успех / подключение */
57
+ --success: var(--connection-green);
58
+ --success-strong: var(--connection-green-dark);
59
+ --success-foreground: var(--cloud-white);
60
+ --success-soft: var(--green-soft);
61
+
62
+ /* статус: ошибка / алерт */
63
+ --error: var(--error-red);
64
+ --error-foreground: var(--cloud-white);
65
+ --error-soft: var(--error-red-soft);
66
+ --error-strong: var(--error-red-dark); /* текст/hover на soft-фоне */
67
+
68
+ /* статус: предупреждение / warning */
69
+ --warning: var(--warning-gold);
70
+ --warning-strong: var(--warning-gold-dark);
71
+ --warning-foreground: var(--deep-navy);
72
+ --warning-soft: var(--gold-soft);
73
+
74
+ /* неактивные элементы */
75
+ --disabled: var(--disabled-blue);
76
+ --disabled-foreground: var(--cloud-white);
77
+
78
+ /* блик skeleton-заглушки (shimmer) поверх bg-accent — в light яркий белый */
79
+ --skeleton-sheen: rgba(255, 255, 255, 0.7);
80
+
81
+ /* радиус скругления (базовый, остальные считаются от него) */
82
+ --radius: 1rem;
83
+ }
84
+
85
+ /* ============================================================
86
+ ТЁМНАЯ ТЕМА. Переопределяем СЕМАНТИЧЕСКИЙ слой — формы и логика компонентов
87
+ те же, меняются только поверхности и текст. Бренд (primary/синий) и hue
88
+ статусов (success/error/warning) сохраняем; у статусов меняем лишь soft-подложки
89
+ на тёмные тонированные. Палитра — navy-tinted dark (в тон бренду, не серый).
90
+
91
+ Поверхности от тёмного к светлому: background → card → popover/secondary → accent.
92
+ Raw-цвета (bg-blue, bg-navy и т.п.) НЕ адаптируются — это фиксированный бренд.
93
+ ============================================================ */
94
+ .dark {
95
+ /* Нативная схема — тёмная (см. пояснение к color-scheme в :root). */
96
+ color-scheme: dark;
97
+
98
+ /* поверхности и текст */
99
+ --background: #0a0e1a; /* фон страницы — почти чёрный с синевой */
100
+ --foreground: #e8edf7; /* основной текст — почти белый, прохладный */
101
+ --card: #121829; /* карточка над фоном */
102
+ --card-foreground: #e8edf7;
103
+ --popover: #161d31; /* меню/селект — чуть светлее карточки */
104
+ --popover-foreground: #e8edf7;
105
+
106
+ /* приглушённые */
107
+ --muted: #1b2440;
108
+ --muted-foreground: var(
109
+ --disabled-blue
110
+ ); /* читается как вторичный текст и на тёмном */
111
+
112
+ /* границы, поля ввода, кольцо фокуса */
113
+ --border: #232c46; /* тонкая граница чуть светлее поверхностей */
114
+ --input: #232c46;
115
+ --ring: var(--brand-blue); /* фокус — бренд-синий */
116
+
117
+ /* основной бренд — НЕ меняем, кнопки остаются те же */
118
+ --primary: var(--brand-blue);
119
+ --primary-foreground: var(--cloud-white);
120
+
121
+ /* вторичный/акцентный фон: заливка инпутов, hover, неактивные табы */
122
+ --secondary: #1b2440; /* фон инпута на тёмной карточке (роль как у sky-mist на светлой) */
123
+ --secondary-foreground: #e8edf7;
124
+ --accent: #243057; /* hover — чуть светлее secondary */
125
+ --accent-foreground: #e8edf7;
126
+
127
+ /* статусы: hue прежний, soft-подложки — тёмные тонированные */
128
+ --success: var(--connection-green);
129
+ --success-strong: #4ade80;
130
+ --success-foreground: var(--cloud-white);
131
+ --success-soft: #132a1f;
132
+
133
+ --error: var(--error-red);
134
+ --error-foreground: var(--cloud-white);
135
+ --error-soft: #2b1618;
136
+ --error-strong: #f87171; /* светлее красный — текст/hover на тёмном soft */
137
+
138
+ --warning: var(--warning-gold);
139
+ --warning-strong: #facc15;
140
+ --warning-foreground: var(
141
+ --deep-navy
142
+ ); /* на gold-кнопке текст тёмный, как и в light */
143
+ --warning-soft: #2b2412;
144
+
145
+ /* неактивные: disabled-blue оставляем — он же цвет разделителей, должен быть виден на тёмном */
146
+ --disabled: var(--disabled-blue);
147
+ --disabled-foreground: var(--cloud-white);
148
+
149
+ /* блик skeleton — на тёмном navy мягкий (низкая альфа), иначе слишком резко */
150
+ --skeleton-sheen: rgba(255, 255, 255, 0.12);
151
+ }
@@ -0,0 +1,143 @@
1
+ /* Проброс токенов в Tailwind: bg-primary, text-foreground, text-h1, rounded-lg …
2
+ Источник: hallo/client — src/app/globals.css. Файл раздаётся пакетом @hallo/kit;
3
+ правки вносить ЗДЕСЬ, кабинет и сайты получают их обновлением пакета. */
4
+
5
+ /* ============================================================
6
+ Прокидываем токены в Tailwind → появляются утилиты:
7
+ bg-primary, text-foreground, border-border, ring-ring, bg-success,
8
+ text-error-strong, bg-warning-soft, bg-brand, text-navy,
9
+ rounded-lg, text-h1 …
10
+ ============================================================ */
11
+ @theme inline {
12
+ /* — семантические — */
13
+ --color-background: var(--background);
14
+ --color-foreground: var(--foreground);
15
+ --color-card: var(--card);
16
+ --color-card-foreground: var(--card-foreground);
17
+ --color-popover: var(--popover);
18
+ --color-popover-foreground: var(--popover-foreground);
19
+ --color-muted: var(--muted);
20
+ --color-muted-foreground: var(--muted-foreground);
21
+ --color-border: var(--border);
22
+ --color-input: var(--input);
23
+ --color-ring: var(--ring);
24
+ --color-primary: var(--primary);
25
+ --color-primary-foreground: var(--primary-foreground);
26
+ --color-secondary: var(--secondary);
27
+ --color-secondary-foreground: var(--secondary-foreground);
28
+ --color-accent: var(--accent);
29
+ --color-accent-foreground: var(--accent-foreground);
30
+ --color-success: var(--success);
31
+ --color-success-strong: var(--success-strong);
32
+ --color-success-foreground: var(--success-foreground);
33
+ --color-success-soft: var(--success-soft);
34
+ --color-error: var(--error);
35
+ --color-error-foreground: var(--error-foreground);
36
+ --color-error-soft: var(--error-soft);
37
+ --color-error-strong: var(--error-strong);
38
+ --color-warning: var(--warning);
39
+ --color-warning-strong: var(--warning-strong);
40
+ --color-warning-foreground: var(--warning-foreground);
41
+ --color-warning-soft: var(--warning-soft);
42
+ --color-disabled: var(--disabled);
43
+ --color-disabled-foreground: var(--disabled-foreground);
44
+ --color-skeleton-sheen: var(--skeleton-sheen);
45
+
46
+ /* — ПРЯМОЙ ДОСТУП КО ВСЕЙ ПАЛИТРЕ по «цветовым» именам (bg-blue, text-navy,
47
+ bg-sky, bg-green, bg-red, bg-gold …). Удобно, когда проще думать цветом.
48
+ Для обычного UI (фоны/текст/кнопки) лучше семантика (bg-primary, text-foreground)
49
+ — она адаптируется к теме; raw-цвета остаются неизменными. — */
50
+ --color-blue: var(--brand-blue);
51
+ --color-blue-light: var(--blue-light);
52
+ --color-blue-pressed: var(--blue-pressed);
53
+ --color-line-strong: var(--line-strong);
54
+ --color-navy: var(--deep-navy);
55
+ --color-cloud: var(--cloud-white);
56
+ --color-sky: var(--sky-mist);
57
+ --color-ice: var(--ice-blue);
58
+ --color-green: var(--connection-green);
59
+ --color-green-soft: var(--green-soft);
60
+ --color-red: var(--error-red);
61
+ --color-red-soft: var(--error-red-soft);
62
+ --color-red-dark: var(--error-red-dark);
63
+ --color-gold: var(--warning-gold);
64
+ --color-gold-soft: var(--gold-soft);
65
+ --color-disabled: var(--disabled-blue);
66
+
67
+ /* шрифты: --font-manrope и --font-geist-mono приходят из next/font в layout.tsx */
68
+ --font-sans: var(--font-manrope);
69
+ --font-mono: var(--font-geist-mono);
70
+ --font-display: var(--font-manrope);
71
+
72
+ /* шкала радиусов от базового --radius */
73
+ --radius-sm: calc(var(--radius) - 4px);
74
+ --radius-md: calc(var(--radius) - 2px);
75
+ --radius-lg: var(--radius);
76
+ --radius-xl: calc(var(--radius) + 4px);
77
+
78
+ /* ============================================================
79
+ ТИПОГРАФИКА (Manrope). Каждая утилита ставит размер + line-height +
80
+ letter-spacing + вес разом: text-h1, text-body-l, text-button-m, text-caption …
81
+ letter-spacing -2% = -0.02em везде.
82
+ Заголовки и Body (L/M/S) — fluid через clamp() между 320px и 1280px,
83
+ Caption и Button — фиксированный размер. (комментарий справа: моб → десктоп, px)
84
+ ============================================================ */
85
+
86
+ /* Заголовки — Bold (700), line-height 110% */
87
+ --text-h1: clamp(2.25rem, 1.833rem + 2.083vw, 3.5rem); /* 36 → 56 */
88
+ --text-h1--line-height: 1.1;
89
+ --text-h1--letter-spacing: -0.02em;
90
+ --text-h1--font-weight: 700;
91
+ --text-h2: clamp(1.875rem, 1.667rem + 1.042vw, 2.5rem); /* 30 → 40 */
92
+ --text-h2--line-height: 1.1;
93
+ --text-h2--letter-spacing: -0.02em;
94
+ --text-h2--font-weight: 700;
95
+ --text-h3: clamp(1.5rem, 1.333rem + 0.833vw, 2rem); /* 24 → 32 */
96
+ --text-h3--line-height: 1.1;
97
+ --text-h3--letter-spacing: -0.02em;
98
+ --text-h3--font-weight: 700;
99
+ --text-h4: clamp(1.25rem, 1.167rem + 0.417vw, 1.5rem); /* 20 → 24 */
100
+ --text-h4--line-height: 1.1;
101
+ --text-h4--letter-spacing: -0.02em;
102
+ --text-h4--font-weight: 700;
103
+ --text-h5: clamp(1.125rem, 1.083rem + 0.208vw, 1.25rem); /* 18 → 20 */
104
+ --text-h5--line-height: 1.1;
105
+ --text-h5--letter-spacing: -0.02em;
106
+ --text-h5--font-weight: 700;
107
+
108
+ /* Body — Medium (500), line-height 145% */
109
+ --text-body-l: clamp(1rem, 0.958rem + 0.208vw, 1.125rem); /* 16 → 18 */
110
+ --text-body-l--line-height: 1.45;
111
+ --text-body-l--letter-spacing: -0.02em;
112
+ --text-body-l--font-weight: 500;
113
+ --text-body-m: clamp(0.875rem, 0.833rem + 0.208vw, 1rem); /* 14 → 16 */
114
+ --text-body-m--line-height: 1.45;
115
+ --text-body-m--letter-spacing: -0.02em;
116
+ --text-body-m--font-weight: 500;
117
+ --text-body-s: clamp(0.75rem, 0.708rem + 0.208vw, 0.875rem); /* 12 → 14 */
118
+ --text-body-s--line-height: 1.45;
119
+ --text-body-s--letter-spacing: -0.02em;
120
+ --text-body-s--font-weight: 500;
121
+
122
+ /* Caption — Medium (500), 12px, line-height 145% */
123
+ --text-caption: 0.75rem; /* 12 */
124
+ --text-caption--line-height: 1.45;
125
+ --text-caption--letter-spacing: -0.02em;
126
+ --text-caption--font-weight: 500;
127
+
128
+ /* Buttons — SemiBold (600), line-height 100%. Fluid: клампятся 320→1280px (как body-*). */
129
+ --text-button-l: clamp(1rem, 0.958rem + 0.208vw, 1.125rem); /* 16 → 18 */
130
+ --text-button-l--line-height: 1;
131
+ --text-button-l--letter-spacing: -0.02em;
132
+ --text-button-l--font-weight: 600;
133
+ --text-button-m: clamp(0.875rem, 0.833rem + 0.208vw, 1rem); /* 14 → 16 */
134
+ --text-button-m--line-height: 1;
135
+ --text-button-m--letter-spacing: -0.02em;
136
+ --text-button-m--font-weight: 600;
137
+ --text-button-s: clamp(0.75rem, 0.708rem + 0.208vw, 0.875rem); /* 12 → 14 */
138
+ --text-button-s--line-height: 1;
139
+ --text-button-s--letter-spacing: -0.02em;
140
+ --text-button-s--font-weight: 600;
141
+ }
142
+
143
+ /* Утилита фирменного градиента: bg-brand-gradient */
@@ -0,0 +1,289 @@
1
+ /* Утилиты дизайн-системы: фирменный градиент, тонкий скроллбар и FLUID-размеры
2
+ (text-fl-14/18, p-fl-18/20 …) — плавный clamp между 320 и 1280px.
3
+ Источник: hallo/client — src/app/globals.css. Файл раздаётся пакетом @hallo/kit;
4
+ правки вносить ЗДЕСЬ, кабинет и сайты получают их обновлением пакета. */
5
+
6
+ @utility bg-brand-gradient {
7
+ background-image: var(--gradient-brand);
8
+ }
9
+
10
+ /* Тонкий ненавязчивый скроллбар для overflow-контейнеров (webkit + firefox).
11
+ Системный «жирный» скроллбар наезжает на контент и выглядит грубо — этот узкий
12
+ (6px), трек прозрачный, бегунок — приглушённый bg-border со скруглением, ярче на
13
+ hover. Вешать класс `scrollbar-slim` на элемент с overflow-y-auto. */
14
+ @utility scrollbar-slim {
15
+ scrollbar-width: thin;
16
+ scrollbar-color: var(--color-border) transparent;
17
+
18
+ &::-webkit-scrollbar {
19
+ width: 10px;
20
+ height: 10px;
21
+ }
22
+ &::-webkit-scrollbar-track {
23
+ background: transparent;
24
+ }
25
+ /* Бегунок — «плавающая» скруглённая пилюля: прозрачная рамка 3px + background-clip
26
+ поджимают заливку внутрь дорожки (реальная ширина ~4px), поэтому виден зазор от
27
+ края и скругление читается, а не «выпирает» встык к контенту/скруглению карточки. */
28
+ &::-webkit-scrollbar-thumb {
29
+ border-radius: 9999px;
30
+ background-color: var(--color-border);
31
+ border: 3px solid transparent;
32
+ background-clip: padding-box;
33
+ }
34
+ &:hover::-webkit-scrollbar-thumb {
35
+ background-color: var(--color-muted-foreground);
36
+ }
37
+ }
38
+
39
+ /* Динамический fluid-размер шрифта: text-fl-{минPx}/{максPx} между 320px и 1280px.
40
+ Напр. text-fl-14/18 — плавно 14 в 18px; clamp считается прямо в CSS, без предрасчёта.
41
+ Для размеров дизайн-системы бери именованные (text-h1 … text-body-s) — они с
42
+ готовыми line-height/letter-spacing/весом; эта утилита — для разовых случаев. */
43
+ @utility text-fl-* {
44
+ font-size: clamp(
45
+ calc(--value(integer) * 1px),
46
+ calc(
47
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
48
+ (100vw - 320px) / 960
49
+ ),
50
+ calc(--modifier(integer) * 1px)
51
+ );
52
+ }
53
+
54
+ /* Динамический fluid-spacing: <свойство>-fl-{минPx}/{максPx} между 320px и 1280px.
55
+ Тот же приём, что у text-fl. Напр. gap-fl-12/24, px-fl-16/32, mt-fl-8/20, m-fl-16/40.
56
+ --fl-clamp — общая формула clamp(min, наклон·vw + база, max), считается в CSS. */
57
+ @utility gap-fl-* {
58
+ gap: clamp(
59
+ calc(--value(integer) * 1px),
60
+ calc(
61
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
62
+ (100vw - 320px) / 960
63
+ ),
64
+ calc(--modifier(integer) * 1px)
65
+ );
66
+ }
67
+ @utility gap-x-fl-* {
68
+ column-gap: clamp(
69
+ calc(--value(integer) * 1px),
70
+ calc(
71
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
72
+ (100vw - 320px) / 960
73
+ ),
74
+ calc(--modifier(integer) * 1px)
75
+ );
76
+ }
77
+ @utility gap-y-fl-* {
78
+ row-gap: clamp(
79
+ calc(--value(integer) * 1px),
80
+ calc(
81
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
82
+ (100vw - 320px) / 960
83
+ ),
84
+ calc(--modifier(integer) * 1px)
85
+ );
86
+ }
87
+ @utility p-fl-* {
88
+ padding: clamp(
89
+ calc(--value(integer) * 1px),
90
+ calc(
91
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
92
+ (100vw - 320px) / 960
93
+ ),
94
+ calc(--modifier(integer) * 1px)
95
+ );
96
+ }
97
+ @utility px-fl-* {
98
+ padding-inline: clamp(
99
+ calc(--value(integer) * 1px),
100
+ calc(
101
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
102
+ (100vw - 320px) / 960
103
+ ),
104
+ calc(--modifier(integer) * 1px)
105
+ );
106
+ }
107
+ @utility py-fl-* {
108
+ padding-block: clamp(
109
+ calc(--value(integer) * 1px),
110
+ calc(
111
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
112
+ (100vw - 320px) / 960
113
+ ),
114
+ calc(--modifier(integer) * 1px)
115
+ );
116
+ }
117
+ /* Fluid-высота-«пол» и квадрат: min-h-fl-{min}/{max}, size-fl-{min}/{max}. Для
118
+ контролов, чтобы высота/квадрат тоже клампились (а не держались фиксом). */
119
+ @utility min-h-fl-* {
120
+ min-height: clamp(
121
+ calc(--value(integer) * 1px),
122
+ calc(
123
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
124
+ (100vw - 320px) / 960
125
+ ),
126
+ calc(--modifier(integer) * 1px)
127
+ );
128
+ }
129
+ @utility size-fl-* {
130
+ width: clamp(
131
+ calc(--value(integer) * 1px),
132
+ calc(
133
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
134
+ (100vw - 320px) / 960
135
+ ),
136
+ calc(--modifier(integer) * 1px)
137
+ );
138
+ height: clamp(
139
+ calc(--value(integer) * 1px),
140
+ calc(
141
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
142
+ (100vw - 320px) / 960
143
+ ),
144
+ calc(--modifier(integer) * 1px)
145
+ );
146
+ }
147
+ @utility h-fl-* {
148
+ height: clamp(
149
+ calc(--value(integer) * 1px),
150
+ calc(
151
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
152
+ (100vw - 320px) / 960
153
+ ),
154
+ calc(--modifier(integer) * 1px)
155
+ );
156
+ }
157
+ @utility w-fl-* {
158
+ width: clamp(
159
+ calc(--value(integer) * 1px),
160
+ calc(
161
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
162
+ (100vw - 320px) / 960
163
+ ),
164
+ calc(--modifier(integer) * 1px)
165
+ );
166
+ }
167
+ /* Fluid translateX через ту же механику, что у Tailwind (--tw-translate-x + translate). */
168
+ @utility translate-x-fl-* {
169
+ --tw-translate-x: clamp(
170
+ calc(--value(integer) * 1px),
171
+ calc(
172
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
173
+ (100vw - 320px) / 960
174
+ ),
175
+ calc(--modifier(integer) * 1px)
176
+ );
177
+ translate: var(--tw-translate-x) var(--tw-translate-y);
178
+ }
179
+ @utility pt-fl-* {
180
+ padding-top: clamp(
181
+ calc(--value(integer) * 1px),
182
+ calc(
183
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
184
+ (100vw - 320px) / 960
185
+ ),
186
+ calc(--modifier(integer) * 1px)
187
+ );
188
+ }
189
+ @utility pr-fl-* {
190
+ padding-right: clamp(
191
+ calc(--value(integer) * 1px),
192
+ calc(
193
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
194
+ (100vw - 320px) / 960
195
+ ),
196
+ calc(--modifier(integer) * 1px)
197
+ );
198
+ }
199
+ @utility pb-fl-* {
200
+ padding-bottom: clamp(
201
+ calc(--value(integer) * 1px),
202
+ calc(
203
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
204
+ (100vw - 320px) / 960
205
+ ),
206
+ calc(--modifier(integer) * 1px)
207
+ );
208
+ }
209
+ @utility pl-fl-* {
210
+ padding-left: clamp(
211
+ calc(--value(integer) * 1px),
212
+ calc(
213
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
214
+ (100vw - 320px) / 960
215
+ ),
216
+ calc(--modifier(integer) * 1px)
217
+ );
218
+ }
219
+ @utility m-fl-* {
220
+ margin: clamp(
221
+ calc(--value(integer) * 1px),
222
+ calc(
223
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
224
+ (100vw - 320px) / 960
225
+ ),
226
+ calc(--modifier(integer) * 1px)
227
+ );
228
+ }
229
+ @utility mx-fl-* {
230
+ margin-inline: clamp(
231
+ calc(--value(integer) * 1px),
232
+ calc(
233
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
234
+ (100vw - 320px) / 960
235
+ ),
236
+ calc(--modifier(integer) * 1px)
237
+ );
238
+ }
239
+ @utility my-fl-* {
240
+ margin-block: clamp(
241
+ calc(--value(integer) * 1px),
242
+ calc(
243
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
244
+ (100vw - 320px) / 960
245
+ ),
246
+ calc(--modifier(integer) * 1px)
247
+ );
248
+ }
249
+ @utility mt-fl-* {
250
+ margin-top: clamp(
251
+ calc(--value(integer) * 1px),
252
+ calc(
253
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
254
+ (100vw - 320px) / 960
255
+ ),
256
+ calc(--modifier(integer) * 1px)
257
+ );
258
+ }
259
+ @utility mr-fl-* {
260
+ margin-right: clamp(
261
+ calc(--value(integer) * 1px),
262
+ calc(
263
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
264
+ (100vw - 320px) / 960
265
+ ),
266
+ calc(--modifier(integer) * 1px)
267
+ );
268
+ }
269
+ @utility mb-fl-* {
270
+ margin-bottom: clamp(
271
+ calc(--value(integer) * 1px),
272
+ calc(
273
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
274
+ (100vw - 320px) / 960
275
+ ),
276
+ calc(--modifier(integer) * 1px)
277
+ );
278
+ }
279
+ @utility ml-fl-* {
280
+ margin-left: clamp(
281
+ calc(--value(integer) * 1px),
282
+ calc(
283
+ --value(integer) * 1px + (--modifier(integer) - --value(integer)) *
284
+ (100vw - 320px) / 960
285
+ ),
286
+ calc(--modifier(integer) * 1px)
287
+ );
288
+ }
289
+