@rt-tools/ui-kit 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/rt-tools-ui-kit.mjs +205 -80
- package/fesm2022/rt-tools-ui-kit.mjs.map +1 -1
- package/package.json +1 -1
- package/rt-tools-ui-kit-0.0.20.tgz +0 -0
- package/src/lib/ui-kit/action-bar/components/bar/rtui-action-bar.component.scss +18 -18
- package/src/lib/ui-kit/action-bar/components/container/rtui-action-bar-container.component.scss +5 -7
- package/src/lib/ui-kit/aside/components/container/aside-container.component.scss +1 -0
- package/src/lib/ui-kit/aside/components/error-notification/aside-error-box.component.scss +6 -6
- package/src/lib/ui-kit/aside/components/panel/aside-panel.component.scss +25 -32
- package/src/lib/ui-kit/aside/stories/aside-component/test-aside.component.scss +1 -1
- package/src/lib/ui-kit/buttons/icon-round/rtui-round-icon-button.component.scss +4 -4
- package/src/lib/ui-kit/buttons/multi-button/rtui-multi-button.component.scss +7 -7
- package/src/lib/ui-kit/checkbox/rtui-checkbox.component.scss +10 -10
- package/src/lib/ui-kit/checkbox/stories/component/test-checkbox.component.scss +2 -2
- package/src/lib/ui-kit/dynamic-selectors/components/actions/rtui-dynamic-selector-list-actions.component.scss +2 -2
- package/src/lib/ui-kit/dynamic-selectors/components/dynamic-selector/rtui-dynamic-selector.component.scss +3 -3
- package/src/lib/ui-kit/dynamic-selectors/components/multi-selector-popup/rtui-multi-selector-popup.component.scss +17 -17
- package/src/lib/ui-kit/dynamic-selectors/components/placeholder/rtui-dynamic-selector-placeholder.component.scss +3 -4
- package/src/lib/ui-kit/dynamic-selectors/components/selected-list/rtui-dynamic-selector-selected-list.component.scss +2 -2
- package/src/lib/ui-kit/dynamic-selectors/strories/component/input/test-dynamic-input.component.scss +2 -3
- package/src/lib/ui-kit/dynamic-selectors/strories/component/selector/test-selector.component.scss +4 -5
- package/src/lib/ui-kit/file-uploader/rtui-file-upload.component.scss +5 -5
- package/src/lib/ui-kit/file-uploader/stories/component/test-file-upload.component.scss +1 -1
- package/src/lib/ui-kit/header/header.component.scss +1 -0
- package/src/lib/ui-kit/header/stories/component/test-header.component.scss +3 -3
- package/src/lib/ui-kit/image-uploader/image-uploader/rtui-image-upload.component.scss +5 -5
- package/src/lib/ui-kit/image-uploader/stories/component/test-image-upload.component.scss +2 -2
- package/src/lib/ui-kit/info-badge/info-badge.component.scss +20 -19
- package/src/lib/ui-kit/info-badge/stories/component/test-info-badge/test-info-badge.component.scss +1 -1
- package/src/lib/ui-kit/modal/modal.component.scss +5 -5
- package/src/lib/ui-kit/popover/rtui-popover-container.component.scss +2 -3
- package/src/lib/ui-kit/scrollable/scrollable-container.component.scss +3 -5
- package/src/lib/ui-kit/side-menu/menu/rtui-side-menu.component.scss +25 -25
- package/src/lib/ui-kit/side-menu/menu-sub-item/rtui-side-menu-sub-item.component.scss +14 -14
- package/src/lib/ui-kit/side-menu/stories/component/test-side-menu-wrapper.component.scss +10 -10
- package/src/lib/ui-kit/snack-bar/snack-bar.component.scss +9 -9
- package/src/lib/ui-kit/snack-bar/stories/component/test-snack-bar.component.scss +3 -3
- package/src/lib/ui-kit/spinner/spinner.component.scss +10 -13
- package/src/lib/ui-kit/table/components/clear-search-button/rtui-clear-button.component.scss +5 -5
- package/src/lib/ui-kit/table/components/pagination-view/rtui-pagination.component.scss +14 -22
- package/src/lib/ui-kit/table/components/table-base-cell/table-base-cell.component.scss +8 -9
- package/src/lib/ui-kit/table/components/table-config-aside/rt-table-config-aside.component.scss +1 -1
- package/src/lib/ui-kit/table/components/table-container/table-container.component.scss +16 -14
- package/src/lib/ui-kit/table/components/table-header-cell/table-header-cell.component.scss +6 -7
- package/src/lib/ui-kit/table/components/table-header-filter-cell/table-header-filter-cell.component.scss +3 -3
- package/src/lib/ui-kit/table/dynamic-list.component.scss +2 -2
- package/src/lib/ui-kit/table/stories/dynamic-list/test-dynamic-list.component.scss +1 -1
- package/src/lib/ui-kit/table/stories/pagination/test-pagination-component.scss +4 -4
- package/src/lib/ui-kit/table/stories/table/test-table-component.scss +1 -1
- package/src/lib/ui-kit/toggle/rtui-toggle.component.scss +15 -21
- package/src/lib/ui-kit/toggle/stories/component/test-toggle.component.scss +2 -2
- package/src/lib/ui-kit/toolbar/toolbar.component.scss +5 -10
- package/src/styles/TOKENS.md +95 -2
- package/src/styles/base/_base.scss +4 -5
- package/src/styles/base/_color-scheme.scss +86 -0
- package/src/styles/base/_mixin.scss +12 -15
- package/src/styles/base/_tokens.scss +139 -99
- package/src/styles/base/_variables.scss +5 -11
- package/src/styles/color-scheme.spec.ts +236 -0
- package/src/styles/components/_button.scss +32 -24
- package/src/styles/components/_dynamic-selectors.scss +9 -10
- package/src/styles/components/_form.scss +8 -13
- package/src/styles/components/_material-bridge.scss +30 -0
- package/src/styles/components/_rtui_button.scss +5 -5
- package/src/styles/components/_table.scss +23 -39
- package/src/styles/main.scss +4 -0
- package/styles/tokens.css +79 -101
- package/types/rt-tools-ui-kit.d.ts +55 -7
- package/rt-tools-ui-kit-0.0.19.tgz +0 -0
package/src/styles/TOKENS.md
CHANGED
|
@@ -18,11 +18,104 @@ Quick facts:
|
|
|
18
18
|
fallback chains. Opt out: `@use '...main' with ($tokens-use-material: false)`.
|
|
19
19
|
- Accent roles `{primary,success,warning,danger,info,neutral}`,
|
|
20
20
|
steps `{subtle,solid,hover,disabled}` (simplified vs GMT).
|
|
21
|
+
- Accent indirection: the accent semantic tier derives from `--rt-color-{role}-{0..100}`
|
|
22
|
+
ramps (`{primary,info,success,warning,danger,brand}`). Custom brand palettes
|
|
23
|
+
override only those rows — see **Custom color schemes** below.
|
|
21
24
|
- Foundations: `--rt-spacing-{0..64}` (px-named, rem values), `--rt-radius-*`,
|
|
22
25
|
`--rt-font-*`, `--rt-shadow-*`, `--rt-transition-*`, `--rt-z-index-*`.
|
|
23
|
-
- Legacy `--clr-*` variables are emitted as **deprecated aliases**.
|
|
24
26
|
- Prebuilt CSS for non-sass consumers: `dist/ui-kit/styles/tokens.css`
|
|
25
27
|
(`pnpm run build:tokens`).
|
|
26
28
|
- Figma parity: collections `core` (`rt/color/*`) and `theme` (`rt/{bg,text,icon,border}/*`,
|
|
27
29
|
Light/Dark modes) in the “RT-Tools UI Kit” file mirror these names 1:1.
|
|
28
|
-
- App-defined (never set by the kit): `--
|
|
30
|
+
- App-defined (never set by the kit): `--font-default`, mat theme colors.
|
|
31
|
+
|
|
32
|
+
## Component theming API (Tier 3 contract)
|
|
33
|
+
|
|
34
|
+
Tier-3 vars are split into a **public theming API** and **internal implementation details**.
|
|
35
|
+
Only the public set is a compatibility promise; internal vars may be renamed or stop being
|
|
36
|
+
emitted in the next major release.
|
|
37
|
+
|
|
38
|
+
**Public** (override these from your app):
|
|
39
|
+
|
|
40
|
+
1. Every var matching `--rt-<component>-*-{color,background-color,bg,shadow,indicator}` —
|
|
41
|
+
the color surface of each component.
|
|
42
|
+
2. Documented size hooks proven by consumers:
|
|
43
|
+
`--rt-table-row-height`, `--rt-toolbar-body-height`, `--rt-toolbar-body-mobile-height`,
|
|
44
|
+
`--rt-aside-error-box-height`, `--rt-aside-host-width`,
|
|
45
|
+
`--rt-image-upload-host-min-height`, `--rt-image-upload-image-container-image-max-height`.
|
|
46
|
+
|
|
47
|
+
**Internal** (everything else): layout paddings/margins/gaps/font-sizes generated from SCSS maps.
|
|
48
|
+
They currently _are_ emitted (≈380 vars total, ~14% actually themed by consumers) — in the next
|
|
49
|
+
major the internal set stops being emitted and gets inlined into rules.
|
|
50
|
+
|
|
51
|
+
### `:root` vs `:host` emission — override semantics
|
|
52
|
+
|
|
53
|
+
- Vars emitted at **`:root`** (global stylesheets + `ViewEncapsulation.None` components like
|
|
54
|
+
aside-panel) are overridable from any ancestor scope: `.dark-mode { --rt-aside-host-background-color: …; }`.
|
|
55
|
+
- Vars emitted at **`:host`** are set on the element itself and **win over inherited values** —
|
|
56
|
+
consumers must target the element (`rtui-toolbar { --rt-toolbar-body-height: … !important; }`).
|
|
57
|
+
This asymmetry is why some consumers need `!important` for toolbar/toggle but not for aside/table.
|
|
58
|
+
- Planned unification (major): emit defaults via `var(--override, default)` indirection so all
|
|
59
|
+
components are ancestor-overridable without `!important`.
|
|
60
|
+
|
|
61
|
+
## Buttons: legacy vs rtui
|
|
62
|
+
|
|
63
|
+
`.c-button` (\_button.scss) is **deprecated**; `.rtui-btn` (\_rtui_button.scss) is the system.
|
|
64
|
+
Both stay until the next major because consumers apply `.c-button` classes and override
|
|
65
|
+
`--rt-button-*` directly. Migration map:
|
|
66
|
+
|
|
67
|
+
| legacy `.c-button` | `.rtui-btn` |
|
|
68
|
+
| ------------------------- | --------------------------------------------------------------------- |
|
|
69
|
+
| `--fill-green` / `-light` | `rtui-btn-success` / `-light` |
|
|
70
|
+
| `--fill-red` / `-light` | `rtui-btn-error` / `-light` |
|
|
71
|
+
| `--fill-blue` | `rtui-btn-accent-light` |
|
|
72
|
+
| `--fill-base/black/gray` | `rtui-btn-secondary` (+`-light`) |
|
|
73
|
+
| `--outline-{blue,base}` | `rtui-btn-{accent,secondary}-outline` |
|
|
74
|
+
| `--txt-*`, `--fab` | no equivalent yet — needs a text/icon appearance in `.rtui-btn` first |
|
|
75
|
+
| `--size-{sm,md,l}` | `rtui-btn-{sm,md,lg}` (+`-full`) |
|
|
76
|
+
|
|
77
|
+
## Material bridge
|
|
78
|
+
|
|
79
|
+
All **global** Material/CDK overrides live in `styles/components/_material-bridge.scss`
|
|
80
|
+
(single file to review on a Material upgrade). Component-scoped piercings stay with their
|
|
81
|
+
component; the bridge header keeps the inventory of those locations.
|
|
82
|
+
|
|
83
|
+
## Custom color schemes (brand palettes)
|
|
84
|
+
|
|
85
|
+
The accent semantic tier never hardcodes color — it derives from an indirection layer,
|
|
86
|
+
the **accent-role ramps** `--rt-color-{role}-{0..100}` for
|
|
87
|
+
`{primary, info, success, warning, danger, brand}`. A custom brand palette overrides
|
|
88
|
+
**only** those rows; the kit derives `--rt-{bg,text,icon,border}-accent-*`,
|
|
89
|
+
hover/subtle/disabled and `--rt-border-focus` from them. The block is scoped to
|
|
90
|
+
`[data-rt-scheme="<name>"]` on `<html>`, set raw → it wins over `--mat-sys-*`.
|
|
91
|
+
|
|
92
|
+
```scss
|
|
93
|
+
@use '@rt-tools/ui-kit/src/styles/main' as rt;
|
|
94
|
+
@include rt.color-scheme(
|
|
95
|
+
'teal',
|
|
96
|
+
(
|
|
97
|
+
primary: (
|
|
98
|
+
20: #b3e3e1,
|
|
99
|
+
40: #5cb8b5,
|
|
100
|
+
60: #1a9d99,
|
|
101
|
+
100: #008582,
|
|
102
|
+
),
|
|
103
|
+
brand: (
|
|
104
|
+
20: #e8e8e8,
|
|
105
|
+
100: #008582,
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// runtime twin (browser-only); prefer the Sass path for SSR/brand-critical schemes
|
|
113
|
+
theme.registerColorScheme('teal', { primary: { 100: '#008582' /* … */ } });
|
|
114
|
+
theme.setColorScheme('teal'); // data-rt-scheme="teal"; persisted to rt-color-scheme
|
|
115
|
+
theme.setColorScheme(null); // back to the default palette
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- Orthogonal to light/dark (one ramp per role serves both modes).
|
|
119
|
+
- Δ0: with no scheme the accent layer is byte-for-byte the historical palette
|
|
120
|
+
(regression-tested in `styles/color-scheme.spec.ts`); Sass↔JS generator parity is tested too.
|
|
121
|
+
- Full guide: Storybook **Foundation/Theming/Custom color schemes** (`docs/ColorSchemes.mdx`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
$palette: (
|
|
2
2
|
white: (
|
|
3
|
-
100: #
|
|
3
|
+
100: #fff,
|
|
4
4
|
),
|
|
5
5
|
gray: (
|
|
6
6
|
5: #f5f6f8,
|
|
@@ -10,7 +10,7 @@ $palette: (
|
|
|
10
10
|
),
|
|
11
11
|
black: (
|
|
12
12
|
10: #f3f3f3,
|
|
13
|
-
15: #
|
|
13
|
+
15: #eee,
|
|
14
14
|
20: #e0e0e0,
|
|
15
15
|
30: #b2cbca,
|
|
16
16
|
40: #a3a3a3,
|
|
@@ -55,10 +55,9 @@ $palette: (
|
|
|
55
55
|
|
|
56
56
|
// NOTE: the $palette map above is kept for backwards compatibility (consumers may @use it).
|
|
57
57
|
// All CSS custom property emission lives in base/_tokens.scss:
|
|
58
|
-
// --rt-color-* primitives, --rt-{bg,text,icon,border}-* semantic, foundations
|
|
59
|
-
// and deprecated --clr-* aliases.
|
|
58
|
+
// --rt-color-* primitives, --rt-{bg,text,icon,border}-* semantic, and foundations.
|
|
60
59
|
|
|
61
60
|
:root {
|
|
62
61
|
// Buttons
|
|
63
|
-
--rt-button-box-shadow: 0 3px 5px -1px
|
|
62
|
+
--rt-button-box-shadow: 0 3px 5px -1px rgb(0 0 0 / 20%), 0 6px 10px 0 rgb(0 0 0 / 12%), 0 1px 18px 0 rgb(0 0 0 / 14%);
|
|
64
63
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
@use 'sass:map';
|
|
2
|
+
@use 'sass:meta';
|
|
3
|
+
@use 'sass:list';
|
|
4
|
+
@use './tokens' as tokens;
|
|
5
|
+
|
|
6
|
+
/// ============================================================================
|
|
7
|
+
/// Custom color schemes (brand palettes)
|
|
8
|
+
/// ============================================================================
|
|
9
|
+
///
|
|
10
|
+
/// A color scheme overrides ONLY the accent-role indirection rows
|
|
11
|
+
/// `--rt-color-{role}-{0..100}` (see `_tokens.scss`). The whole semantic accent
|
|
12
|
+
/// tier (`--rt-bg/text/icon/border-accent-*`, hover/subtle/disabled, `--rt-border-focus`)
|
|
13
|
+
/// re-derives from those rows — so a brand recolors with Δ0 effort and the
|
|
14
|
+
/// component contract (semantic token names) stays untouched.
|
|
15
|
+
///
|
|
16
|
+
/// The emitted block is scoped to `[data-rt-scheme="<name>"]`, which the runtime
|
|
17
|
+
/// (`RtThemeService.setColorScheme`) toggles on `<html>`. Because the scheme sets
|
|
18
|
+
/// raw values, it overrides the default rows that carry the `--mat-sys-*` fallback
|
|
19
|
+
/// → the scheme wins over an active Material theme.
|
|
20
|
+
///
|
|
21
|
+
/// Usage:
|
|
22
|
+
/// @use '@rt-tools/ui-kit/src/styles/main' as rt; // or the package's scss entry
|
|
23
|
+
/// @include rt.color-scheme('teal', (
|
|
24
|
+
/// primary: (20: #b3e3e1, 40: #5cb8b5, 60: #1a9d99, 100: #008582),
|
|
25
|
+
/// brand: (20: #e8e8e8, 100: #008582),
|
|
26
|
+
/// ));
|
|
27
|
+
///
|
|
28
|
+
/// One tonal ramp per role serves both light and dark (M3-style): the mode picks
|
|
29
|
+
/// the tone via `light-dark()` inside the semantic tier — schemes never duplicate
|
|
30
|
+
/// that logic. `$modes` is accepted for forward-compatibility but a single ramp
|
|
31
|
+
/// already covers every mode.
|
|
32
|
+
|
|
33
|
+
/// Roles a scheme may override, with their valid tone steps (from the default ramps).
|
|
34
|
+
$known-roles: tokens.$accent-roles;
|
|
35
|
+
|
|
36
|
+
/// Validate a single role/ramp pair; @error with an actionable message on bad input.
|
|
37
|
+
@function _validate-role($name, $role, $ramp) {
|
|
38
|
+
@if not map.has-key($known-roles, $role) {
|
|
39
|
+
@error 'rt.color-scheme("#{$name}"): unknown role "#{$role}". Allowed roles: #{map.keys($known-roles)}.';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@if meta.type-of($ramp) != 'map' {
|
|
43
|
+
@error 'rt.color-scheme("#{$name}"): role "#{$role}" must be a map of (tone: color), got #{meta.type-of($ramp)}.';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@each $step, $value in $ramp {
|
|
47
|
+
@if meta.type-of($step) != 'number' or not _is-integer-step($step) {
|
|
48
|
+
@error 'rt.color-scheme("#{$name}"): role "#{$role}" tone "#{$step}" must be an integer 0–100.';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Tone steps must be integers within 0–100.
|
|
56
|
+
@function _is-integer-step($step) {
|
|
57
|
+
@return $step >= 0 and $step <= 100 and ($step % 1 == 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Emit a named color scheme as a `[data-rt-scheme]` block (only raw `--rt-color-{role}-{N}` rows).
|
|
61
|
+
///
|
|
62
|
+
/// @param {String} $name Scheme name, matched by `data-rt-scheme="<name>"`.
|
|
63
|
+
/// @param {Map} $roles Map of `role: (tone: color, …)`. Roles ⊆ #{map.keys($known-roles)}.
|
|
64
|
+
/// @param {List} $modes Reserved for forward-compat; one ramp already serves all modes.
|
|
65
|
+
@mixin color-scheme($name, $roles, $modes: (light, dark)) {
|
|
66
|
+
@if meta.type-of($name) != 'string' {
|
|
67
|
+
@error 'rt.color-scheme: $name must be a string, got #{meta.type-of($name)}.';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@if meta.type-of($roles) != 'map' or list.length(map.keys($roles)) == 0 {
|
|
71
|
+
@error 'rt.color-scheme("#{$name}"): $roles must be a non-empty map of (role: ramp).';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@each $role, $ramp in $roles {
|
|
75
|
+
$ok: _validate-role($name, $role, $ramp);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// `:root[...]` (0,2,0) outranks the default `:root` rows (0,1,0) regardless of source order.
|
|
79
|
+
:root[data-rt-scheme='#{$name}'] {
|
|
80
|
+
@each $role, $ramp in $roles {
|
|
81
|
+
@each $step, $value in $ramp {
|
|
82
|
+
--rt-color-#{$role}-#{$step}: #{$value};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
+
@use 'sass:color';
|
|
1
2
|
@use 'sass:math';
|
|
2
3
|
@use './variables' as vars;
|
|
3
4
|
|
|
4
|
-
// css clr var generator
|
|
5
|
-
// #{generateCssVar(red, 100)}: #eb5055;
|
|
6
|
-
@function generateCssClrVar($color, $saturate) {
|
|
7
|
-
@return --#{vars.$styles-clr-prefix}-#{$color}-#{$saturate};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
5
|
// css var generator
|
|
11
6
|
// #{generateCssVar(table-base-cell, suffix-icon, size)}: 1rem;
|
|
12
7
|
@function generateCssVar($component-name, $element, $style-token) {
|
|
@@ -16,9 +11,9 @@
|
|
|
16
11
|
// convert hex to rgb values
|
|
17
12
|
// #{convert-hex-to-rgb-values(#ab2727)}: 171, 39, 39;
|
|
18
13
|
@function convert-hex-to-rgb-values($hex) {
|
|
19
|
-
$r:
|
|
20
|
-
$g:
|
|
21
|
-
$b:
|
|
14
|
+
$r: color.channel($hex, 'red', $space: rgb);
|
|
15
|
+
$g: color.channel($hex, 'green', $space: rgb);
|
|
16
|
+
$b: color.channel($hex, 'blue', $space: rgb);
|
|
22
17
|
|
|
23
18
|
@return $r + ', ' + $g + ', ' + $b;
|
|
24
19
|
}
|
|
@@ -26,6 +21,7 @@
|
|
|
26
21
|
// px to rem
|
|
27
22
|
// font-size: rem(16);
|
|
28
23
|
$browser-context: 16;
|
|
24
|
+
|
|
29
25
|
@function rem($valueRem, $context: $browser-context) {
|
|
30
26
|
@return #{math.div($valueRem, $context)}rem;
|
|
31
27
|
}
|
|
@@ -47,20 +43,21 @@ $browser-context: 16;
|
|
|
47
43
|
@mixin flex-column-center {
|
|
48
44
|
display: flex;
|
|
49
45
|
flex-direction: column;
|
|
50
|
-
justify-content: center;
|
|
51
46
|
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
@mixin visually-hidden {
|
|
55
51
|
position: absolute;
|
|
52
|
+
overflow: hidden;
|
|
56
53
|
width: 1px;
|
|
57
54
|
height: 1px;
|
|
58
|
-
margin: -1px;
|
|
59
|
-
border: 0;
|
|
60
55
|
padding: 0;
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
border: 0;
|
|
57
|
+
margin: -1px;
|
|
58
|
+
/* stylelint-disable-next-line property-no-deprecated -- legacy-browser leg of the visually-hidden pattern */
|
|
63
59
|
clip: rect(0 0 0 0);
|
|
64
|
-
|
|
60
|
+
clip-path: inset(100%);
|
|
65
61
|
pointer-events: none;
|
|
62
|
+
white-space: nowrap;
|
|
66
63
|
}
|
|
@@ -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
|
|
34
|
+
@if $dark {
|
|
35
35
|
$v: light-dark(#{$light}, #{$dark});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
@if $mat
|
|
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: #
|
|
49
|
+
0: #fff,
|
|
50
50
|
5: #f5f6f8,
|
|
51
51
|
10: #f3f3f3,
|
|
52
|
-
15: #
|
|
52
|
+
15: #eee,
|
|
53
53
|
20: #e8e8e8,
|
|
54
54
|
25: #e0e0e0,
|
|
55
55
|
30: #d1d1d1,
|
|
56
|
-
35: #
|
|
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(#
|
|
118
|
-
bg-base-elevated: t(#
|
|
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,
|
|
121
|
-
bg-base-active: t(#
|
|
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, #
|
|
127
|
-
bg-base-overlay: t(
|
|
128
|
-
/* ---- bg / static ---- */ bg-static-light: #
|
|
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}
|
|
132
|
-
bg-accent-primary-subtle: t(
|
|
133
|
-
bg-accent-primary-solid:
|
|
134
|
-
bg-accent-primary-hover: t(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
bg-accent-
|
|
139
|
-
bg-accent-success-
|
|
140
|
-
bg-accent-
|
|
141
|
-
bg-accent-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
bg-accent-
|
|
146
|
-
bg-accent-
|
|
147
|
-
bg-accent-
|
|
148
|
-
bg-accent-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
bg-accent-
|
|
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(
|
|
156
|
-
/* ---- text / base (adaptive) ---- */ text-base-primary: t(
|
|
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(#
|
|
162
|
-
/* ---- text / static ---- */ text-static-light: #
|
|
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(
|
|
165
|
-
text-accent-primary: t(
|
|
166
|
-
text-accent-success:
|
|
167
|
-
text-accent-success-soft:
|
|
168
|
-
text-accent-warning:
|
|
169
|
-
text-accent-warning-soft:
|
|
170
|
-
text-accent-danger:
|
|
171
|
-
text-accent-danger-soft:
|
|
172
|
-
text-accent-info:
|
|
173
|
-
text-accent-info-soft:
|
|
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(#
|
|
178
|
-
/* ---- icon / static ---- */ icon-static-light: #
|
|
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(
|
|
181
|
-
icon-accent-primary: t(
|
|
182
|
-
icon-accent-success:
|
|
183
|
-
icon-accent-warning:
|
|
184
|
-
icon-accent-danger:
|
|
185
|
-
icon-accent-info:
|
|
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(#
|
|
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(
|
|
193
|
-
border-accent-success:
|
|
194
|
-
border-accent-warning:
|
|
195
|
-
border-accent-danger:
|
|
196
|
-
border-accent-danger-soft:
|
|
197
|
-
border-accent-info:
|
|
198
|
-
border-focus: t(
|
|
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(#
|
|
255
|
+
control-thumb: t(#fff, #eee),
|
|
201
256
|
control-checked: t(#323033, #e0e0e0),
|
|
202
|
-
/* ---- misc ---- */ scrollbar-thumb: t(#
|
|
257
|
+
/* ---- misc ---- */ scrollbar-thumb: t(#ccc, #4a494e),
|
|
203
258
|
scrollbar-thumb-hover: t(#a3a3a3, #5c5b60),
|
|
204
|
-
shadow-color: t(#747474,
|
|
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
|
|
304
|
+
0 0.0625rem 0.25rem 0 rgb(0 0 0 / 12%),
|
|
254
305
|
),
|
|
255
306
|
md: (
|
|
256
|
-
0 0.25rem 0.5rem 0
|
|
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) */
|