@rt-tools/ui-kit 0.0.19 → 0.0.21

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 (71) hide show
  1. package/fesm2022/rt-tools-ui-kit.mjs +338 -144
  2. package/fesm2022/rt-tools-ui-kit.mjs.map +1 -1
  3. package/package.json +5 -5
  4. package/rt-tools-ui-kit-0.0.21.tgz +0 -0
  5. package/src/lib/ui-kit/action-bar/components/bar/rtui-action-bar.component.scss +18 -18
  6. package/src/lib/ui-kit/action-bar/components/container/rtui-action-bar-container.component.scss +5 -7
  7. package/src/lib/ui-kit/aside/components/container/aside-container.component.scss +1 -0
  8. package/src/lib/ui-kit/aside/components/error-notification/aside-error-box.component.scss +6 -6
  9. package/src/lib/ui-kit/aside/components/panel/aside-panel.component.scss +25 -32
  10. package/src/lib/ui-kit/aside/stories/aside-component/test-aside.component.scss +1 -1
  11. package/src/lib/ui-kit/buttons/multi-button/rtui-multi-button.component.scss +7 -7
  12. package/src/lib/ui-kit/buttons/unified-button/rtui-button.component.scss +247 -0
  13. package/src/lib/ui-kit/checkbox/rtui-checkbox.component.scss +10 -10
  14. package/src/lib/ui-kit/checkbox/stories/component/test-checkbox.component.scss +2 -2
  15. package/src/lib/ui-kit/dynamic-selectors/components/actions/rtui-dynamic-selector-list-actions.component.scss +2 -2
  16. package/src/lib/ui-kit/dynamic-selectors/components/dynamic-selector/rtui-dynamic-selector.component.scss +3 -3
  17. package/src/lib/ui-kit/dynamic-selectors/components/multi-selector-popup/rtui-multi-selector-popup.component.scss +17 -17
  18. package/src/lib/ui-kit/dynamic-selectors/components/placeholder/rtui-dynamic-selector-placeholder.component.scss +3 -4
  19. package/src/lib/ui-kit/dynamic-selectors/components/selected-list/rtui-dynamic-selector-selected-list.component.scss +2 -2
  20. package/src/lib/ui-kit/dynamic-selectors/strories/component/input/test-dynamic-input.component.scss +2 -3
  21. package/src/lib/ui-kit/dynamic-selectors/strories/component/selector/test-selector.component.scss +4 -5
  22. package/src/lib/ui-kit/file-uploader/rtui-file-upload.component.scss +5 -5
  23. package/src/lib/ui-kit/file-uploader/stories/component/test-file-upload.component.scss +1 -1
  24. package/src/lib/ui-kit/header/header.component.scss +1 -0
  25. package/src/lib/ui-kit/header/stories/component/test-header.component.scss +3 -3
  26. package/src/lib/ui-kit/icon/rtui-icon.component.scss +110 -0
  27. package/src/lib/ui-kit/image-uploader/image-uploader/rtui-image-upload.component.scss +5 -5
  28. package/src/lib/ui-kit/image-uploader/stories/component/test-image-upload.component.scss +2 -2
  29. package/src/lib/ui-kit/info-badge/info-badge.component.scss +20 -19
  30. package/src/lib/ui-kit/info-badge/stories/component/test-info-badge/test-info-badge.component.scss +1 -1
  31. package/src/lib/ui-kit/modal/modal.component.scss +5 -5
  32. package/src/lib/ui-kit/popover/rtui-popover-container.component.scss +2 -3
  33. package/src/lib/ui-kit/scrollable/scrollable-container.component.scss +3 -5
  34. package/src/lib/ui-kit/side-menu/menu/rtui-side-menu.component.scss +25 -25
  35. package/src/lib/ui-kit/side-menu/menu-sub-item/rtui-side-menu-sub-item.component.scss +14 -14
  36. package/src/lib/ui-kit/side-menu/stories/component/test-side-menu-wrapper.component.scss +10 -10
  37. package/src/lib/ui-kit/snack-bar/snack-bar.component.scss +9 -9
  38. package/src/lib/ui-kit/snack-bar/stories/component/test-snack-bar.component.scss +3 -3
  39. package/src/lib/ui-kit/spinner/spinner.component.scss +10 -13
  40. package/src/lib/ui-kit/table/components/clear-search-button/rtui-clear-button.component.scss +5 -5
  41. package/src/lib/ui-kit/table/components/pagination-view/rtui-pagination.component.scss +14 -22
  42. package/src/lib/ui-kit/table/components/table-base-cell/table-base-cell.component.scss +8 -9
  43. package/src/lib/ui-kit/table/components/table-config-aside/rt-table-config-aside.component.scss +1 -1
  44. package/src/lib/ui-kit/table/components/table-container/table-container.component.scss +16 -14
  45. package/src/lib/ui-kit/table/components/table-header-cell/table-header-cell.component.scss +6 -7
  46. package/src/lib/ui-kit/table/components/table-header-filter-cell/table-header-filter-cell.component.scss +3 -3
  47. package/src/lib/ui-kit/table/dynamic-list.component.scss +2 -2
  48. package/src/lib/ui-kit/table/stories/dynamic-list/test-dynamic-list.component.scss +1 -1
  49. package/src/lib/ui-kit/table/stories/pagination/test-pagination-component.scss +4 -4
  50. package/src/lib/ui-kit/table/stories/table/test-table-component.scss +1 -1
  51. package/src/lib/ui-kit/toggle/rtui-toggle.component.scss +15 -21
  52. package/src/lib/ui-kit/toggle/stories/component/test-toggle.component.scss +2 -2
  53. package/src/lib/ui-kit/toolbar/toolbar.component.scss +5 -10
  54. package/src/styles/TOKENS.md +95 -2
  55. package/src/styles/base/_base.scss +4 -5
  56. package/src/styles/base/_color-scheme.scss +86 -0
  57. package/src/styles/base/_mixin.scss +12 -15
  58. package/src/styles/base/_tokens.scss +139 -99
  59. package/src/styles/base/_variables.scss +5 -11
  60. package/src/styles/color-scheme.spec.ts +236 -0
  61. package/src/styles/components/_button.scss +32 -24
  62. package/src/styles/components/_dynamic-selectors.scss +9 -10
  63. package/src/styles/components/_form.scss +8 -13
  64. package/src/styles/components/_material-bridge.scss +30 -0
  65. package/src/styles/components/_rtui_button.scss +100 -5
  66. package/src/styles/components/_table.scss +23 -39
  67. package/src/styles/main.scss +4 -0
  68. package/styles/tokens.css +79 -101
  69. package/types/rt-tools-ui-kit.d.ts +99 -37
  70. package/rt-tools-ui-kit-0.0.19.tgz +0 -0
  71. package/src/lib/ui-kit/buttons/icon-round/rtui-round-icon-button.component.scss +0 -44
@@ -31,11 +31,11 @@ $use-material: true !default;
31
31
  @function t($light, $dark: null, $mat: null) {
32
32
  $v: $light;
33
33
 
34
- @if $dark != null {
34
+ @if $dark {
35
35
  $v: light-dark(#{$light}, #{$dark});
36
36
  }
37
37
 
38
- @if $mat != null and $use-material {
38
+ @if $mat and $use-material {
39
39
  @return var(--mat-sys-#{$mat}, #{$v});
40
40
  }
41
41
 
@@ -46,14 +46,14 @@ $use-material: true !default;
46
46
 
47
47
  // Unified neutral scale (sorted by lightness, sourced from the legacy white/gray/black families)
48
48
  $neutral: (
49
- 0: #ffffff,
49
+ 0: #fff,
50
50
  5: #f5f6f8,
51
51
  10: #f3f3f3,
52
- 15: #eeeeee,
52
+ 15: #eee,
53
53
  20: #e8e8e8,
54
54
  25: #e0e0e0,
55
55
  30: #d1d1d1,
56
- 35: #cccccc,
56
+ 35: #ccc,
57
57
  40: #a3a3a3,
58
58
  60: #747474,
59
59
  80: #323033,
@@ -105,103 +105,158 @@ $hue-base: (
105
105
  'green': #01af8d,
106
106
  'brand': #0d1c2b,
107
107
  );
108
-
109
108
  $opacity-steps: (5, 10, 20, 30, 40, 50, 60, 70, 80, 90);
110
109
  $overlay-steps: (5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90);
111
110
 
111
+ /* ------------------------------ Accent-role ramps (indirection layer) ------------------------------ */
112
+ // The semantic accent tier references ONLY these `--rt-color-{role}-{0..100}` rows — never raw hues.
113
+ // A custom color scheme overrides just these rows (see `_color-scheme.scss`) and the entire
114
+ // accent layer (bg/text/icon/border + hover/subtle/disabled/focus derivations) recolors with Δ0 effort.
115
+ // ---
116
+ // Defaults reproduce the historical own-palette byte-for-byte. Tones that previously mapped to a
117
+ // Material system color carry the `--mat-sys-*` fallback INSIDE the tone (`t(..., null, $mat)`), so:
118
+ // - default render honors Material (as before);
119
+ // - a scheme override replaces the tone with a raw value → the scheme wins over Material.
120
+ // `brand` is navy in light (100) / near-white in dark (20); both carry mat-sys-primary because the
121
+ // original text/icon-accent-brand mapped to --mat-sys-primary.
122
+ $accent-roles: (
123
+ 'primary': (
124
+ 20: t(#eaedfc, null, primary-container),
125
+ 40: #b3ceef,
126
+ 60: t(#6d96e8, null, primary),
127
+ 100: t(#4284d7, null, primary),
128
+ ),
129
+ 'info': (
130
+ 20: #eaedfc,
131
+ 80: #4285f4,
132
+ 100: #4284d7,
133
+ ),
134
+ 'success': (
135
+ 10: #e5f8f4,
136
+ 80: #21b18e,
137
+ 100: #01af8d,
138
+ ),
139
+ 'warning': (
140
+ 10: #e8cbbf,
141
+ 80: #ee7a34,
142
+ 100: #ef7128,
143
+ ),
144
+ 'danger': (
145
+ 10: t(#fdedee, null, error-container),
146
+ 60: #e88487,
147
+ 100: t(#eb5055, null, error),
148
+ ),
149
+ 'brand': (
150
+ 20: t(#e8e8e8, null, primary),
151
+ 100: t(#0d1c2b, null, primary),
152
+ ),
153
+ );
154
+
112
155
  /* ============================== Tier 2: semantic ============================== */
113
156
  // NOTE: function calls in this map are evaluated when the module loads,
114
157
  // honoring a `with ($use-material: ...)` configuration.
115
158
 
116
159
  $semantic: (
117
- /* ---- bg / base (adaptive) ---- */ bg-base-base: t(#ffffff, #1c1b1e, surface),
118
- bg-base-elevated: t(#ffffff, #2a292d, surface-container),
160
+ /* ---- bg / base (adaptive) ---- */ bg-base-base: t(#fff, #1c1b1e, surface),
161
+ bg-base-elevated: t(#fff, #2a292d, surface-container),
119
162
  bg-base-subtle: t(#f5f6f8, #232226),
120
- bg-base-hover: t(#f3f3f3, rgba(255, 255, 255, 0.06)),
121
- bg-base-active: t(#eeeeee, rgba(255, 255, 255, 0.1)),
163
+ bg-base-hover: t(#f3f3f3, rgb(255 255 255 / 6%)),
164
+ bg-base-active: t(#eee, rgb(255 255 255 / 10%)),
122
165
  bg-base-strong: t(#e0e0e0, #3f3e43),
123
166
  bg-base-emphasis: t(#747474, #a3a3a3),
124
167
  bg-base-emphasis-soft: t(#a3a3a3, #747474),
125
168
  bg-base-inverse: t(#181818, #f3f3f3, inverse-surface),
126
- bg-base-inverse-soft: t(#323033, #eeeeee),
127
- bg-base-overlay: t(rgba(0, 0, 0, 0.32), rgba(0, 0, 0, 0.6)),
128
- /* ---- bg / static ---- */ bg-static-light: #ffffff,
169
+ bg-base-inverse-soft: t(#323033, #eee),
170
+ bg-base-overlay: t(rgb(0 0 0 / 32%), rgb(0 0 0 / 60%)),
171
+ /* ---- bg / static ---- */ bg-static-light: #fff,
129
172
  bg-static-dark: #181818,
130
173
  bg-static-none: transparent,
131
- /* ---- bg / accent: {subtle, solid, hover, disabled} (simplified GMT scale) ---- */
132
- bg-accent-primary-subtle: t(#eaedfc, color-mix(in srgb, #4284d7 18%, #1c1b1e), primary-container),
133
- bg-accent-primary-solid: t(#4284d7, #4284d7, primary),
134
- bg-accent-primary-hover: t(color-mix(in srgb, #4284d7 90%, #000000), color-mix(in srgb, #4284d7 90%, #ffffff)),
135
- bg-accent-primary-disabled: color-mix(in srgb, #4284d7 38%, transparent),
136
- bg-accent-success-subtle: t(#e5f8f4, color-mix(in srgb, #01af8d 18%, #1c1b1e)),
137
- bg-accent-success-solid: #21b18e,
138
- bg-accent-success-hover: t(color-mix(in srgb, #21b18e 90%, #000000), color-mix(in srgb, #21b18e 90%, #ffffff)),
139
- bg-accent-success-disabled: color-mix(in srgb, #21b18e 38%, transparent),
140
- bg-accent-warning-subtle: t(#e8cbbf, color-mix(in srgb, #ef7128 18%, #1c1b1e)),
141
- bg-accent-warning-solid: #ee7a34,
142
- bg-accent-warning-hover: t(color-mix(in srgb, #ee7a34 90%, #000000), color-mix(in srgb, #ee7a34 90%, #ffffff)),
143
- bg-accent-warning-disabled: color-mix(in srgb, #ee7a34 38%, transparent),
144
- bg-accent-danger-subtle: t(#fdedee, color-mix(in srgb, #eb5055 18%, #1c1b1e), error-container),
145
- bg-accent-danger-solid: t(#eb5055, #eb5055, error),
146
- bg-accent-danger-hover: t(color-mix(in srgb, #eb5055 90%, #000000), color-mix(in srgb, #eb5055 90%, #ffffff)),
147
- bg-accent-danger-disabled: color-mix(in srgb, #eb5055 38%, transparent),
148
- bg-accent-info-subtle: t(#eaedfc, color-mix(in srgb, #4284d7 18%, #1c1b1e)),
149
- bg-accent-info-solid: #4284d7,
150
- bg-accent-info-hover: t(color-mix(in srgb, #4284d7 90%, #000000), color-mix(in srgb, #4284d7 90%, #ffffff)),
151
- bg-accent-info-disabled: color-mix(in srgb, #4284d7 38%, transparent),
152
- bg-accent-neutral-subtle: t(#f3f3f3, rgba(255, 255, 255, 0.06)),
174
+ /* ---- bg / accent: {subtle, solid, hover, disabled} driven by --rt-color-{role}-{N} ---- */
175
+ bg-accent-primary-subtle: t(var(--rt-color-primary-20), color-mix(in srgb, var(--rt-color-primary-100) 18%, #1c1b1e)),
176
+ bg-accent-primary-solid: var(--rt-color-primary-100),
177
+ bg-accent-primary-hover: t(
178
+ color-mix(in srgb, var(--rt-color-primary-100) 90%, #000),
179
+ color-mix(in srgb, var(--rt-color-primary-100) 90%, #fff)
180
+ ),
181
+ bg-accent-primary-disabled: color-mix(in srgb, var(--rt-color-primary-100) 38%, transparent),
182
+ bg-accent-success-subtle: t(var(--rt-color-success-10), color-mix(in srgb, var(--rt-color-success-100) 18%, #1c1b1e)),
183
+ bg-accent-success-solid: var(--rt-color-success-80),
184
+ bg-accent-success-hover: t(
185
+ color-mix(in srgb, var(--rt-color-success-80) 90%, #000),
186
+ color-mix(in srgb, var(--rt-color-success-80) 90%, #fff)
187
+ ),
188
+ bg-accent-success-disabled: color-mix(in srgb, var(--rt-color-success-80) 38%, transparent),
189
+ bg-accent-warning-subtle: t(var(--rt-color-warning-10), color-mix(in srgb, var(--rt-color-warning-100) 18%, #1c1b1e)),
190
+ bg-accent-warning-solid: var(--rt-color-warning-80),
191
+ bg-accent-warning-hover: t(
192
+ color-mix(in srgb, var(--rt-color-warning-80) 90%, #000),
193
+ color-mix(in srgb, var(--rt-color-warning-80) 90%, #fff)
194
+ ),
195
+ bg-accent-warning-disabled: color-mix(in srgb, var(--rt-color-warning-80) 38%, transparent),
196
+ bg-accent-danger-subtle: t(var(--rt-color-danger-10), color-mix(in srgb, var(--rt-color-danger-100) 18%, #1c1b1e)),
197
+ bg-accent-danger-solid: var(--rt-color-danger-100),
198
+ bg-accent-danger-hover: t(
199
+ color-mix(in srgb, var(--rt-color-danger-100) 90%, #000),
200
+ color-mix(in srgb, var(--rt-color-danger-100) 90%, #fff)
201
+ ),
202
+ bg-accent-danger-disabled: color-mix(in srgb, var(--rt-color-danger-100) 38%, transparent),
203
+ bg-accent-info-subtle: t(var(--rt-color-info-20), color-mix(in srgb, var(--rt-color-info-100) 18%, #1c1b1e)),
204
+ bg-accent-info-solid: var(--rt-color-info-100),
205
+ bg-accent-info-hover: t(color-mix(in srgb, var(--rt-color-info-100) 90%, #000), color-mix(in srgb, var(--rt-color-info-100) 90%, #fff)),
206
+ bg-accent-info-disabled: color-mix(in srgb, var(--rt-color-info-100) 38%, transparent),
207
+ bg-accent-neutral-subtle: t(#f3f3f3, rgb(255 255 255 / 6%)),
153
208
  bg-accent-neutral-solid: t(#747474, #a3a3a3),
154
209
  bg-accent-neutral-hover: t(#a3a3a3, #747474),
155
- bg-accent-neutral-disabled: t(rgba(116, 116, 116, 0.38), rgba(163, 163, 163, 0.38)),
156
- /* ---- text / base (adaptive) ---- */ text-base-primary: t(rgba(0, 0, 0, 0.87), rgba(255, 255, 255, 0.87), on-surface),
210
+ bg-accent-neutral-disabled: t(rgb(116 116 116 / 38%), rgb(163 163 163 / 38%)),
211
+ /* ---- text / base (adaptive) ---- */ text-base-primary: t(rgb(0 0 0 / 87%), rgb(255 255 255 / 87%), on-surface),
157
212
  text-base-strong: t(#181818, #f3f3f3),
158
213
  text-base-soft: t(#323033, #e0e0e0),
159
214
  text-base-secondary: t(#747474, #a3a3a3, on-surface-variant),
160
215
  text-base-disabled: t(#a3a3a3, #747474),
161
- text-base-inverse: t(#ffffff, #181818, inverse-on-surface),
162
- /* ---- text / static ---- */ text-static-light: #ffffff,
216
+ text-base-inverse: t(#fff, #181818, inverse-on-surface),
217
+ /* ---- text / static ---- */ text-static-light: #fff,
163
218
  text-static-dark: #181818,
164
- /* ---- text / accent ---- */ text-accent-brand: t(#0d1c2b, #e8e8e8, primary),
165
- text-accent-primary: t(#4284d7, #6d96e8, primary),
166
- text-accent-success: #01af8d,
167
- text-accent-success-soft: #21b18e,
168
- text-accent-warning: #ef7128,
169
- text-accent-warning-soft: #ee7a34,
170
- text-accent-danger: t(#eb5055, #eb5055, error),
171
- text-accent-danger-soft: #e88487,
172
- text-accent-info: #4284d7,
173
- text-accent-info-soft: #4285f4,
219
+ /* ---- text / accent ---- */ text-accent-brand: t(var(--rt-color-brand-100), var(--rt-color-brand-20)),
220
+ text-accent-primary: t(var(--rt-color-primary-100), var(--rt-color-primary-60)),
221
+ text-accent-success: var(--rt-color-success-100),
222
+ text-accent-success-soft: var(--rt-color-success-80),
223
+ text-accent-warning: var(--rt-color-warning-100),
224
+ text-accent-warning-soft: var(--rt-color-warning-80),
225
+ text-accent-danger: var(--rt-color-danger-100),
226
+ text-accent-danger-soft: var(--rt-color-danger-60),
227
+ text-accent-info: var(--rt-color-info-100),
228
+ text-accent-info-soft: var(--rt-color-info-80),
174
229
  /* ---- icon / neutral (adaptive) ---- */ icon-neutral-default: t(#323033, #e0e0e0),
175
230
  icon-neutral-soft: t(#747474, #a3a3a3),
176
231
  icon-neutral-disabled: t(#a3a3a3, #747474),
177
- icon-neutral-inverse: t(#ffffff, #181818),
178
- /* ---- icon / static ---- */ icon-static-light: #ffffff,
232
+ icon-neutral-inverse: t(#fff, #181818),
233
+ /* ---- icon / static ---- */ icon-static-light: #fff,
179
234
  icon-static-dark: #181818,
180
- /* ---- icon / accent ---- */ icon-accent-brand: t(#0d1c2b, #e8e8e8, primary),
181
- icon-accent-primary: t(#4284d7, #6d96e8, primary),
182
- icon-accent-success: #01af8d,
183
- icon-accent-warning: #ef7128,
184
- icon-accent-danger: t(#eb5055, #eb5055, error),
185
- icon-accent-info: #4284d7,
235
+ /* ---- icon / accent ---- */ icon-accent-brand: t(var(--rt-color-brand-100), var(--rt-color-brand-20)),
236
+ icon-accent-primary: t(var(--rt-color-primary-100), var(--rt-color-primary-60)),
237
+ icon-accent-success: var(--rt-color-success-100),
238
+ icon-accent-warning: var(--rt-color-warning-100),
239
+ icon-accent-danger: var(--rt-color-danger-100),
240
+ icon-accent-info: var(--rt-color-info-100),
186
241
  /* ---- border / neutral (adaptive) ---- */ border-neutral-subtle: t(#e8e8e8, #2e2d31),
187
242
  border-neutral-default: t(#e0e0e0, #3f3e43, outline-variant),
188
243
  border-neutral-medium: t(#d1d1d1, #4a494e),
189
- border-neutral-divider: t(#cccccc, #4a494e),
244
+ border-neutral-divider: t(#ccc, #4a494e),
190
245
  border-neutral-strong: t(#a3a3a3, #5c5b60, outline),
191
246
  border-neutral-emphasis: t(#747474, #a3a3a3),
192
- /* ---- border / accent ---- */ border-accent-primary: t(#4284d7, #6d96e8, primary),
193
- border-accent-success: #01af8d,
194
- border-accent-warning: #ef7128,
195
- border-accent-danger: t(#eb5055, #eb5055, error),
196
- border-accent-danger-soft: #e88487,
197
- border-accent-info: #4284d7,
198
- border-focus: t(#b3ceef, #6d96e8),
247
+ /* ---- border / accent ---- */ border-accent-primary: t(var(--rt-color-primary-100), var(--rt-color-primary-60)),
248
+ border-accent-success: var(--rt-color-success-100),
249
+ border-accent-warning: var(--rt-color-warning-100),
250
+ border-accent-danger: var(--rt-color-danger-100),
251
+ border-accent-danger-soft: var(--rt-color-danger-60),
252
+ border-accent-info: var(--rt-color-info-100),
253
+ border-focus: t(var(--rt-color-primary-40), var(--rt-color-primary-60)),
199
254
  /* ---- form controls (rt extension) ---- */ control-track: t(#e8e8e8, #4a494e),
200
- control-thumb: t(#ffffff, #eeeeee),
255
+ control-thumb: t(#fff, #eee),
201
256
  control-checked: t(#323033, #e0e0e0),
202
- /* ---- misc ---- */ scrollbar-thumb: t(#cccccc, #4a494e),
257
+ /* ---- misc ---- */ scrollbar-thumb: t(#ccc, #4a494e),
203
258
  scrollbar-thumb-hover: t(#a3a3a3, #5c5b60),
204
- shadow-color: t(#747474, rgba(0, 0, 0, 0.6))
259
+ shadow-color: t(#747474, rgb(0 0 0 / 60%))
205
260
  );
206
261
 
207
262
  /* ============================== Foundations (mode-independent) ============================== */
@@ -222,7 +277,6 @@ $spacing: (
222
277
  56: 3.5rem,
223
278
  64: 4rem,
224
279
  );
225
-
226
280
  $radius: (
227
281
  xs: 0.25rem,
228
282
  sm: 0.5rem,
@@ -232,7 +286,6 @@ $radius: (
232
286
  2xl: 2rem,
233
287
  full: 624.9375rem,
234
288
  );
235
-
236
289
  $font-size: (
237
290
  xs: 0.75rem,
238
291
  sm: 0.875rem,
@@ -240,32 +293,28 @@ $font-size: (
240
293
  lg: 1.25rem,
241
294
  xl: 1.5rem,
242
295
  );
243
-
244
296
  $font-weight: (
245
297
  regular: 400,
246
298
  medium: 500,
247
299
  semibold: 600,
248
300
  bold: 700,
249
301
  );
250
-
251
302
  $shadow: (
252
303
  sm: (
253
- 0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.12),
304
+ 0 0.0625rem 0.25rem 0 rgb(0 0 0 / 12%),
254
305
  ),
255
306
  md: (
256
- 0 0.25rem 0.5rem 0 rgba(0, 0, 0, 0.14),
307
+ 0 0.25rem 0.5rem 0 rgb(0 0 0 / 14%),
257
308
  ),
258
309
  lg: (
259
310
  0 0.5rem 1rem 0 var(--rt-shadow-color),
260
311
  ),
261
312
  );
262
-
263
313
  $transition: (
264
314
  fast: 0.15s ease-in-out,
265
315
  base: 0.25s ease-in-out,
266
316
  slow: 0.4s ease-in-out,
267
317
  );
268
-
269
318
  $z-index: (
270
319
  dropdown: 1000,
271
320
  sticky: 1020,
@@ -315,6 +364,13 @@ $breakpoint: (
315
364
  --rt-color-light-a#{$a}: rgba(255, 255, 255, #{calc($a / 100)});
316
365
  }
317
366
 
367
+ // accent-role ramps (indirection layer) — overridable per color scheme
368
+ @each $role, $tones in $accent-roles {
369
+ @each $step, $value in $tones {
370
+ --rt-color-#{$role}-#{$step}: #{$value};
371
+ }
372
+ }
373
+
318
374
  /* --- Tier 2: semantic --- */
319
375
  @each $token, $value in $semantic {
320
376
  --rt-#{$token}: #{$value};
@@ -324,50 +380,34 @@ $breakpoint: (
324
380
  @each $token, $value in $spacing {
325
381
  --rt-spacing-#{$token}: #{$value};
326
382
  }
383
+
327
384
  @each $token, $value in $radius {
328
385
  --rt-radius-#{$token}: #{$value};
329
386
  }
387
+
330
388
  @each $token, $value in $font-size {
331
389
  --rt-font-size-#{$token}: #{$value};
332
390
  }
391
+
333
392
  @each $token, $value in $font-weight {
334
393
  --rt-font-weight-#{$token}: #{$value};
335
394
  }
395
+
336
396
  @each $token, $value in $shadow {
337
397
  --rt-shadow-#{$token}: #{$value};
338
398
  }
399
+
339
400
  @each $token, $value in $transition {
340
401
  --rt-transition-#{$token}: #{$value};
341
402
  }
403
+
342
404
  @each $token, $value in $z-index {
343
405
  --rt-z-index-#{$token}: #{$value};
344
406
  }
407
+
345
408
  @each $token, $value in $breakpoint {
346
409
  --rt-breakpoint-#{$token}: #{$value};
347
410
  }
348
-
349
- /* --- Deprecated aliases (legacy --clr-*; remove after consumers migrate) --- */
350
- --clr-white-100: var(--rt-color-neutral-0);
351
- --clr-gray-5: var(--rt-color-neutral-5);
352
- --clr-gray-10: var(--rt-color-neutral-20);
353
- --clr-gray-15: var(--rt-color-neutral-30);
354
- --clr-gray-20: var(--rt-color-neutral-35);
355
- --clr-black-10: var(--rt-color-neutral-10);
356
- --clr-black-15: var(--rt-color-neutral-15);
357
- --clr-black-20: var(--rt-color-neutral-25);
358
- --clr-black-30: #b2cbca; // palette outlier, kept verbatim
359
- --clr-black-40: var(--rt-color-neutral-40);
360
- --clr-black-60: var(--rt-color-neutral-60);
361
- --clr-black-80: var(--rt-color-neutral-80);
362
- --clr-black-100: var(--rt-color-neutral-100);
363
- @each $hue, $steps in $hues {
364
- @each $step, $value in $steps {
365
- --clr-#{$hue}-#{$step}: var(--rt-color-#{$hue}-#{$step});
366
- }
367
- }
368
- --clr-txt: var(--rt-text-base-primary);
369
- --clr-base-accent: var(--rt-color-brand);
370
- --clr-white-rgb: 255, 255, 255;
371
411
  }
372
412
 
373
413
  /* Theme switching: global class + nested local contexts (GMT data-theme analogue) */
@@ -1,11 +1,8 @@
1
1
  /* Material Theme */
2
2
  $main-theme: light;
3
-
4
- $styles-clr-prefix: 'clr';
5
3
  $styles-prefix: 'rt';
6
4
 
7
5
  /* Base */
8
- $avalon-color: var(--clr-avalon);
9
6
  $base-accent: var(--rt-text-accent-brand);
10
7
 
11
8
  /* Device Definitions */
@@ -21,14 +18,11 @@ $base-font-weight: 400;
21
18
  $base-font-size: 1rem;
22
19
  $base-text-color: var(--rt-text-base-primary);
23
20
 
24
- /* Colors */
25
- $clr-red-100: #eb5055;
26
- $clr-red-10: #fdedee;
27
-
28
- $clr-green-80: #00b894;
29
- $clr-green-10: #e5f8f4;
21
+ /* Button palette seeds (static — fed to sass color.scale() for hover/active states) */
22
+ $btn-danger: #eb5055;
23
+ $btn-danger-soft: #fdedee;
24
+ $btn-success: #00b894; // outlier: not on the --rt-color-green scale
25
+ $btn-success-soft: #e5f8f4;
30
26
 
31
27
  /* Components */
32
- $side-panel-dynamic-clr: var(--rt-text-accent-brand);
33
- $pagination-dynamic-clr: var(--rt-text-accent-brand);
34
28
  $text-highlight-color: #0077bf;
@@ -0,0 +1,236 @@
1
+ import * as path from 'path';
2
+ import * as sass from 'sass';
3
+
4
+ /**
5
+ * Build-time guarantees for the custom color-scheme mechanism:
6
+ * 1. Δ0 — moving the accent tier onto the `--rt-color-{role}-{N}` indirection
7
+ * keeps every accent semantic token byte-for-byte identical (own palette).
8
+ * 2. The `rt.color-scheme` Sass mixin emits a scoped `[data-rt-scheme]` block.
9
+ * 3. Input validation rejects unknown roles / out-of-range tones.
10
+ * 4. The teal case: a custom teal ramp drives `--rt-bg-accent-primary-solid` to teal.
11
+ */
12
+
13
+ const STYLES_DIR: string = __dirname;
14
+
15
+ /** Frozen resolved (own-palette) values of every accent token BEFORE the indirection refactor. */
16
+ const BASELINE: Record<string, string> = {
17
+ '--rt-bg-accent-primary-subtle': 'light-dark(#eaedfc, color-mix(in srgb, #4284d7 18%, #1c1b1e))',
18
+ '--rt-bg-accent-primary-solid': '#4284d7',
19
+ '--rt-bg-accent-primary-hover': 'light-dark(color-mix(in srgb, #4284d7 90%, #000), color-mix(in srgb, #4284d7 90%, #fff))',
20
+ '--rt-bg-accent-primary-disabled': 'color-mix(in srgb, #4284d7 38%, transparent)',
21
+ '--rt-bg-accent-success-subtle': 'light-dark(#e5f8f4, color-mix(in srgb, #01af8d 18%, #1c1b1e))',
22
+ '--rt-bg-accent-success-solid': '#21b18e',
23
+ '--rt-bg-accent-success-hover': 'light-dark(color-mix(in srgb, #21b18e 90%, #000), color-mix(in srgb, #21b18e 90%, #fff))',
24
+ '--rt-bg-accent-success-disabled': 'color-mix(in srgb, #21b18e 38%, transparent)',
25
+ '--rt-bg-accent-warning-subtle': 'light-dark(#e8cbbf, color-mix(in srgb, #ef7128 18%, #1c1b1e))',
26
+ '--rt-bg-accent-warning-solid': '#ee7a34',
27
+ '--rt-bg-accent-warning-hover': 'light-dark(color-mix(in srgb, #ee7a34 90%, #000), color-mix(in srgb, #ee7a34 90%, #fff))',
28
+ '--rt-bg-accent-warning-disabled': 'color-mix(in srgb, #ee7a34 38%, transparent)',
29
+ '--rt-bg-accent-danger-subtle': 'light-dark(#fdedee, color-mix(in srgb, #eb5055 18%, #1c1b1e))',
30
+ '--rt-bg-accent-danger-solid': '#eb5055',
31
+ '--rt-bg-accent-danger-hover': 'light-dark(color-mix(in srgb, #eb5055 90%, #000), color-mix(in srgb, #eb5055 90%, #fff))',
32
+ '--rt-bg-accent-danger-disabled': 'color-mix(in srgb, #eb5055 38%, transparent)',
33
+ '--rt-bg-accent-info-subtle': 'light-dark(#eaedfc, color-mix(in srgb, #4284d7 18%, #1c1b1e))',
34
+ '--rt-bg-accent-info-solid': '#4284d7',
35
+ '--rt-bg-accent-info-hover': 'light-dark(color-mix(in srgb, #4284d7 90%, #000), color-mix(in srgb, #4284d7 90%, #fff))',
36
+ '--rt-bg-accent-info-disabled': 'color-mix(in srgb, #4284d7 38%, transparent)',
37
+ '--rt-text-accent-brand': 'light-dark(#0d1c2b, #e8e8e8)',
38
+ '--rt-text-accent-primary': 'light-dark(#4284d7, #6d96e8)',
39
+ '--rt-text-accent-success': '#01af8d',
40
+ '--rt-text-accent-success-soft': '#21b18e',
41
+ '--rt-text-accent-warning': '#ef7128',
42
+ '--rt-text-accent-warning-soft': '#ee7a34',
43
+ '--rt-text-accent-danger': '#eb5055',
44
+ '--rt-text-accent-danger-soft': '#e88487',
45
+ '--rt-text-accent-info': '#4284d7',
46
+ '--rt-text-accent-info-soft': '#4285f4',
47
+ '--rt-icon-accent-brand': 'light-dark(#0d1c2b, #e8e8e8)',
48
+ '--rt-icon-accent-primary': 'light-dark(#4284d7, #6d96e8)',
49
+ '--rt-icon-accent-success': '#01af8d',
50
+ '--rt-icon-accent-warning': '#ef7128',
51
+ '--rt-icon-accent-danger': '#eb5055',
52
+ '--rt-icon-accent-info': '#4284d7',
53
+ '--rt-border-accent-primary': 'light-dark(#4284d7, #6d96e8)',
54
+ '--rt-border-accent-success': '#01af8d',
55
+ '--rt-border-accent-warning': '#ef7128',
56
+ '--rt-border-accent-danger': '#eb5055',
57
+ '--rt-border-accent-danger-soft': '#e88487',
58
+ '--rt-border-accent-info': '#4284d7',
59
+ '--rt-border-focus': 'light-dark(#b3ceef, #6d96e8)',
60
+ };
61
+
62
+ /** Resolve `var(--mat-sys-X, FALLBACK)` to its fallback (own-palette, no Material configured). */
63
+ function stripMat(input: string): string {
64
+ let value: string = input;
65
+ let index: number = value.indexOf('var(--mat-sys-');
66
+
67
+ while (index >= 0) {
68
+ let depth: number = 0;
69
+ let j: number = index + 4;
70
+
71
+ for (; j < value.length; j++) {
72
+ if (value[j] === '(') {
73
+ depth++;
74
+ } else if (value[j] === ')') {
75
+ if (depth === 0) {
76
+ break;
77
+ }
78
+ depth--;
79
+ }
80
+ }
81
+
82
+ const inner: string = value.slice(index + 4, j);
83
+ const comma: number = inner.indexOf(',');
84
+ value = value.slice(0, index) + inner.slice(comma + 1).trim() + value.slice(j + 1);
85
+ index = value.indexOf('var(--mat-sys-');
86
+ }
87
+
88
+ return value;
89
+ }
90
+
91
+ /** Collapse `light-dark(X, X)` to `X` (identical branches render the same in both modes). */
92
+ function normalize(input: string): string {
93
+ let value: string = input.replace(/\s+/g, ' ').trim();
94
+ let prev: string = '';
95
+
96
+ while (value !== prev) {
97
+ prev = value;
98
+ value = value.replace(/light-dark\(\s*([^,()]+?)\s*,\s*\1\s*\)/g, '$1');
99
+ }
100
+
101
+ return value.replace(/\s+/g, ' ').trim();
102
+ }
103
+
104
+ /** Extract `--name: value;` declarations matching a name pattern from compiled CSS. */
105
+ function extractDeclarations(css: string, namePattern: string): Record<string, string> {
106
+ const out: Record<string, string> = {};
107
+ const re: RegExp = new RegExp(`(${namePattern}):\\s*([^;]+);`, 'g');
108
+ let match: RegExpExecArray | null = re.exec(css);
109
+
110
+ while (match !== null) {
111
+ out[match[1]] = match[2].trim();
112
+ match = re.exec(css);
113
+ }
114
+
115
+ return out;
116
+ }
117
+
118
+ function compileTokens(): string {
119
+ return sass.compile(path.join(STYLES_DIR, 'tokens.scss')).css;
120
+ }
121
+
122
+ function compileWithMixin(body: string): sass.CompileResult {
123
+ return sass.compileString(`@use 'main' as rt;\n${body}`, { loadPaths: [STYLES_DIR] });
124
+ }
125
+
126
+ describe('rt-tools color schemes', () => {
127
+ describe('Δ0 regression — accent indirection keeps the own palette byte-for-byte', () => {
128
+ it('every accent semantic token resolves to its pre-refactor value', () => {
129
+ const css: string = compileTokens();
130
+ const ramp: Record<string, string> = extractDeclarations(css, '--rt-color-(?:primary|info|success|warning|danger|brand)-\\d+');
131
+ const semantic: Record<string, string> = extractDeclarations(
132
+ css,
133
+ '--rt-(?:bg|text|icon|border)-accent[\\w-]*|--rt-border-focus'
134
+ );
135
+
136
+ const resolveRamp: (value: string) => string = (value: string): string => {
137
+ let resolved: string = value;
138
+ let prev: string = '';
139
+
140
+ while (resolved !== prev) {
141
+ prev = resolved;
142
+ for (const key of Object.keys(ramp)) {
143
+ resolved = resolved.split(`var(${key})`).join(stripMat(ramp[key]));
144
+ }
145
+ }
146
+
147
+ return normalize(stripMat(resolved));
148
+ };
149
+
150
+ for (const token of Object.keys(BASELINE)) {
151
+ expect(semantic[token]).toBeDefined();
152
+ expect(resolveRamp(semantic[token])).toBe(BASELINE[token]);
153
+ }
154
+ });
155
+ });
156
+
157
+ describe('Material hybrid — default honors --mat-sys-*, a scheme overrides it', () => {
158
+ it('default ramp tones carry the --mat-sys-* fallback where the original token did', () => {
159
+ const ramp: Record<string, string> = extractDeclarations(
160
+ compileTokens(),
161
+ '--rt-color-(?:primary|info|success|warning|danger|brand)-\\d+'
162
+ );
163
+
164
+ // tones that mapped to a Material system color carry the fallback (default honors Material)
165
+ expect(ramp['--rt-color-primary-100']).toBe('var(--mat-sys-primary, #4284d7)');
166
+ expect(ramp['--rt-color-primary-20']).toBe('var(--mat-sys-primary-container, #eaedfc)');
167
+ expect(ramp['--rt-color-danger-100']).toBe('var(--mat-sys-error, #eb5055)');
168
+ expect(ramp['--rt-color-brand-100']).toBe('var(--mat-sys-primary, #0d1c2b)');
169
+
170
+ // roles with no Material mapping stay raw — and a scheme overriding ANY tone with a raw
171
+ // value drops the fallback entirely, so the scheme wins over an active Material theme.
172
+ expect(ramp['--rt-color-info-100']).toBe('#4284d7');
173
+ expect(ramp['--rt-color-success-100']).toBe('#01af8d');
174
+ expect(ramp['--rt-color-warning-100']).toBe('#ef7128');
175
+ });
176
+ });
177
+
178
+ describe('dark mode — a scheme drives both modes via distinct ramp tones', () => {
179
+ it('accent tokens pick tone-100 in light and tone-60 in dark (scheme controls each)', () => {
180
+ const semantic: Record<string, string> = extractDeclarations(
181
+ compileTokens(),
182
+ '--rt-text-accent-primary|--rt-border-accent-primary|--rt-icon-accent-primary'
183
+ );
184
+
185
+ // light-dark(primary-100, primary-60): the kit fixes WHICH tone per mode; the scheme
186
+ // supplies the VALUE of each tone → a scheme can set a different dark tone (tone-60) than light.
187
+ for (const token of Object.keys(semantic)) {
188
+ expect(semantic[token]).toContain('var(--rt-color-primary-100)');
189
+ expect(semantic[token]).toContain('var(--rt-color-primary-60)');
190
+ expect(semantic[token].indexOf('primary-100')).toBeLessThan(semantic[token].indexOf('primary-60'));
191
+ }
192
+ });
193
+ });
194
+
195
+ describe('rt.color-scheme mixin', () => {
196
+ it('emits a scoped [data-rt-scheme] block with only raw role rows', () => {
197
+ const css: string = compileWithMixin(
198
+ '@include rt.color-scheme("teal", (primary: (20: #b3e3e1, 100: #008582), brand: (100: #008582)));'
199
+ ).css;
200
+
201
+ const block: string = css.slice(css.indexOf('[data-rt-scheme=teal]'));
202
+
203
+ expect(block).toContain('[data-rt-scheme=teal]');
204
+ expect(block).toContain('--rt-color-primary-100: #008582');
205
+ expect(block).toContain('--rt-color-primary-20: #b3e3e1');
206
+ expect(block).toContain('--rt-color-brand-100: #008582');
207
+ // schemes never duplicate the semantic derivation layer
208
+ expect(block).not.toContain('--rt-bg-accent-primary-solid:');
209
+ });
210
+
211
+ it('rejects an unknown role', () => {
212
+ expect(() => compileWithMixin('@include rt.color-scheme("x", (foo: (100: #000000)));')).toThrow(/unknown role/i);
213
+ });
214
+
215
+ it('rejects an out-of-range tone', () => {
216
+ expect(() => compileWithMixin('@include rt.color-scheme("x", (primary: (150: #000000)));')).toThrow(/integer 0–100/i);
217
+ });
218
+
219
+ it('rejects an empty role map', () => {
220
+ expect(() => compileWithMixin('@include rt.color-scheme("x", ());')).toThrow(/non-empty map/i);
221
+ });
222
+ });
223
+
224
+ describe('teal reference case', () => {
225
+ it('a teal primary ramp recolors --rt-bg-accent-primary-solid to teal', () => {
226
+ const css: string = compileWithMixin(
227
+ '@include rt.color-scheme("teal", (primary: (20: #b3e3e1, 40: #5cb8b5, 60: #1a9d99, 100: #008582)));'
228
+ ).css;
229
+
230
+ const scheme: Record<string, string> = extractDeclarations(css, '--rt-color-primary-\\d+');
231
+ // bg-accent-primary-solid === var(--rt-color-primary-100); under the teal scheme that is #008582
232
+ expect(scheme['--rt-color-primary-100']).toBe('#008582');
233
+ expect(normalize(stripMat(css.match(/--rt-bg-accent-primary-solid:\s*([^;]+);/)![1]))).toBe('var(--rt-color-primary-100)');
234
+ });
235
+ });
236
+ });