@happyvertical/smrt-ui 0.55.19 → 0.55.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.
- package/dist/components/data/DataTable.svelte +5 -0
- package/dist/components/feedback/Alert.svelte +5 -0
- package/dist/components/feedback/Progress.svelte +7 -0
- package/dist/components/forms/Checkbox.svelte +9 -0
- package/dist/components/forms/Radio.svelte +9 -0
- package/dist/components/forms/SegmentedControl.svelte +5 -0
- package/dist/components/forms/Switch.svelte +10 -0
- package/dist/components/forms/Toggle.svelte +12 -0
- package/dist/components/forms/ToggleButton.svelte +5 -0
- package/dist/components/nav/FilterChips.svelte +5 -0
- package/dist/components/nav/Tabs.svelte +10 -0
- package/dist/components/ui/Badge.svelte +4 -0
- package/dist/components/ui/Chip.svelte +5 -0
- package/dist/components/ui/IconToggle.svelte +6 -0
- package/dist/themes/README.md +57 -3
- package/dist/themes/ThemeProvider.svelte +9 -0
- package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/themes/forced-colors.d.ts +2 -0
- package/dist/themes/forced-colors.d.ts.map +1 -0
- package/dist/themes/forced-colors.js +49 -0
- package/dist/themes/styles/base.css +80 -1
- package/dist/themes/styles/forced-colors.css +42 -0
- package/package.json +2 -2
|
@@ -2351,5 +2351,10 @@ $effect(() => {
|
|
|
2351
2351
|
.data-table-container--narrow .data-table { table-layout: fixed; }
|
|
2352
2352
|
.data-table-container--narrow .data-table__cell { white-space: normal; overflow-wrap: anywhere; }
|
|
2353
2353
|
.data-table-container--narrow .data-table__sort-button { max-width: 100%; text-align: left; white-space: normal; overflow-wrap: anywhere; }
|
|
2354
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
2355
|
+
@media (forced-colors: active) {
|
|
2356
|
+
.data-table__row--selected, .data-table__row--selected .data-table__cell--pinned-start, .data-table__row--selected .data-table__cell--pinned-end { background: Canvas; }
|
|
2357
|
+
.data-table__row--selected { outline: var(--smrt-focus-ring-width) solid Highlight; outline-offset: calc(-1 * var(--smrt-focus-ring-width)); }
|
|
2358
|
+
}
|
|
2354
2359
|
}
|
|
2355
2360
|
</style>
|
|
@@ -60,5 +60,10 @@ const role = $derived(
|
|
|
60
60
|
.dismiss { width: 2rem; height: 2rem; margin: -.35rem; border: 0; border-radius: var(--smrt-radius-full); background: transparent; color: inherit; font-size: 1.25rem; cursor: pointer; }
|
|
61
61
|
.dismiss:hover { background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent); }
|
|
62
62
|
.dismiss:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
|
|
63
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
64
|
+
@media (forced-colors: active) {
|
|
65
|
+
.alert { background: Canvas; border-color: CanvasText; }
|
|
66
|
+
.marker { background: CanvasText; }
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
</style>
|
|
@@ -68,5 +68,12 @@ const displayValue = $derived(
|
|
|
68
68
|
.progress--circular > span { position: absolute; font: var(--smrt-typography-label-small-font); }
|
|
69
69
|
@keyframes linear-progress { from { left: -40%; } to { left: 105%; } } @keyframes spin { to { transform: rotate(270deg); } }
|
|
70
70
|
@media (prefers-reduced-motion: reduce) { .progress--linear .indicator, .progress--circular .indicator { transition: none; } .progress--linear.indeterminate .indicator { animation-duration: 2.5s; } }
|
|
71
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
72
|
+
@media (forced-colors: active) {
|
|
73
|
+
.progress--linear .track { background: Canvas; border: var(--smrt-hairline-strong); }
|
|
74
|
+
.progress--linear .indicator { background: Highlight; }
|
|
75
|
+
.progress--circular .track { stroke: CanvasText; }
|
|
76
|
+
.progress--circular .indicator { stroke: Highlight; }
|
|
77
|
+
}
|
|
71
78
|
}
|
|
72
79
|
</style>
|
|
@@ -193,5 +193,14 @@ useControlRegistration(() => {
|
|
|
193
193
|
.checkbox[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
|
|
194
194
|
.checkbox[data-density='comfortable'] { --smrt-control-target-min: 0px; }
|
|
195
195
|
.checkbox { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
|
|
196
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
197
|
+
@media (forced-colors: active) {
|
|
198
|
+
.checkbox__box { background: Canvas; border-color: CanvasText; }
|
|
199
|
+
input:checked + .checkbox__box, input:indeterminate + .checkbox__box { background: Highlight; border-color: Highlight; color: HighlightText; }
|
|
200
|
+
.checkbox__dash { background: HighlightText; }
|
|
201
|
+
.checkbox--disabled { opacity: 1; color: GrayText; }
|
|
202
|
+
.checkbox--disabled .checkbox__box, .checkbox--disabled input:checked + .checkbox__box, .checkbox--disabled input:indeterminate + .checkbox__box { background: Canvas; border-color: GrayText; color: GrayText; }
|
|
203
|
+
.checkbox--disabled .checkbox__dash { background: GrayText; }
|
|
204
|
+
}
|
|
196
205
|
}
|
|
197
206
|
</style>
|
|
@@ -48,5 +48,14 @@ function handleChange(event: Event & { currentTarget: HTMLInputElement }) {
|
|
|
48
48
|
|
|
49
49
|
/* Table cells: grow vertically only, never into a neighbouring cell. */
|
|
50
50
|
:global(:is(td, th)) .radio::before { inline-size: var(--smrt-radio-box); margin-inline-start: calc(var(--smrt-radio-box) / -2); }
|
|
51
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
52
|
+
@media (forced-colors: active) {
|
|
53
|
+
.radio__mark { border-color: CanvasText; }
|
|
54
|
+
.radio__mark::after { background: Highlight; }
|
|
55
|
+
input:checked + .radio__mark { border-color: Highlight; }
|
|
56
|
+
.radio.disabled { opacity: 1; color: GrayText; }
|
|
57
|
+
.radio.disabled .radio__mark, .radio.disabled input:checked + .radio__mark { border-color: GrayText; }
|
|
58
|
+
.radio.disabled .radio__mark::after { background: GrayText; }
|
|
59
|
+
}
|
|
51
60
|
}
|
|
52
61
|
</style>
|
|
@@ -263,5 +263,10 @@ useControlRegistration(() => {
|
|
|
263
263
|
.segmented[data-density='comfortable'] { --smrt-control-target-min: 0px; }
|
|
264
264
|
label { min-height: max(2.5rem, var(--smrt-control-target-min, 0px)); min-width: var(--smrt-control-target-min, 0px); }
|
|
265
265
|
input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: inherit; }
|
|
266
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
267
|
+
@media (forced-colors: active) {
|
|
268
|
+
label.selected { background: Highlight; color: HighlightText; }
|
|
269
|
+
label.disabled, label:has(input:disabled), label.selected.disabled, label.selected:has(input:disabled) { opacity: 1; background: Canvas; color: GrayText; }
|
|
270
|
+
}
|
|
266
271
|
}
|
|
267
272
|
</style>
|
|
@@ -155,5 +155,15 @@ useControlRegistration(() => {
|
|
|
155
155
|
.switch[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
|
|
156
156
|
.switch[data-density='comfortable'] { --smrt-control-target-min: 0px; }
|
|
157
157
|
.switch { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
|
|
158
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
159
|
+
@media (forced-colors: active) {
|
|
160
|
+
.switch__track { background: Canvas; border-color: CanvasText; }
|
|
161
|
+
.switch__thumb { background: CanvasText; }
|
|
162
|
+
input:checked + .switch__track { background: Highlight; border-color: Highlight; }
|
|
163
|
+
input:checked + .switch__track .switch__thumb { background: HighlightText; }
|
|
164
|
+
.switch--disabled { opacity: 1; color: GrayText; }
|
|
165
|
+
.switch--disabled .switch__track, .switch--disabled input:checked + .switch__track { background: Canvas; border-color: GrayText; }
|
|
166
|
+
.switch--disabled .switch__thumb, .switch--disabled input:checked + .switch__track .switch__thumb { background: GrayText; }
|
|
167
|
+
}
|
|
158
168
|
}
|
|
159
169
|
</style>
|
|
@@ -223,5 +223,17 @@ const sizeClasses = {
|
|
|
223
223
|
transition: none;
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
227
|
+
@media (forced-colors: active) {
|
|
228
|
+
.toggle__thumb, .toggle:not(.toggle--disabled):hover .toggle__thumb { background: Canvas; outline: var(--smrt-hairline-strong); outline-offset: 0; }
|
|
229
|
+
.toggle__input:checked + .toggle__thumb, .toggle:not(.toggle--disabled):hover .toggle__input:checked + .toggle__thumb { background: Highlight; outline-color: Highlight; }
|
|
230
|
+
.toggle__thumb::after { background: CanvasText; }
|
|
231
|
+
.toggle__input:checked + .toggle__thumb::after { background: HighlightText; }
|
|
232
|
+
.toggle__input:focus-visible + .toggle__thumb { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
|
|
233
|
+
.toggle--disabled { opacity: 1; color: GrayText; }
|
|
234
|
+
.toggle--disabled .toggle__label { color: GrayText; }
|
|
235
|
+
.toggle--disabled .toggle__thumb, .toggle--disabled .toggle__input:checked + .toggle__thumb { background: Canvas; outline-color: GrayText; }
|
|
236
|
+
.toggle--disabled .toggle__thumb::after, .toggle--disabled .toggle__input:checked + .toggle__thumb::after { background: GrayText; }
|
|
237
|
+
}
|
|
226
238
|
}
|
|
227
239
|
</style>
|
|
@@ -99,5 +99,10 @@ useControlRegistration(() => {
|
|
|
99
99
|
.toggle-button:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
|
|
100
100
|
.toggle-button:disabled { opacity: .5; cursor: not-allowed; }
|
|
101
101
|
.toggle-button[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
|
|
102
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
103
|
+
@media (forced-colors: active) {
|
|
104
|
+
.toggle-button.pressed { background: Highlight; color: HighlightText; border-color: Highlight; }
|
|
105
|
+
.toggle-button:disabled, .toggle-button.pressed:disabled { opacity: 1; background: Canvas; color: GrayText; border-color: GrayText; }
|
|
106
|
+
}
|
|
102
107
|
}
|
|
103
108
|
</style>
|
|
@@ -158,5 +158,10 @@ function handleClick(value: string) {
|
|
|
158
158
|
.filter-chips[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
|
|
159
159
|
.filter-chips[data-density='comfortable'] { --smrt-control-target-min: 0px; }
|
|
160
160
|
.filter-chip { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
|
|
161
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
162
|
+
@media (forced-colors: active) {
|
|
163
|
+
.filter-chip.active { background-color: Highlight; color: HighlightText; border-color: Highlight; }
|
|
164
|
+
.filter-chip:disabled, .filter-chip.active:disabled { opacity: 1; background: Canvas; color: GrayText; border-color: GrayText; }
|
|
165
|
+
}
|
|
161
166
|
}
|
|
162
167
|
</style>
|
|
@@ -523,5 +523,15 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
|
|
|
523
523
|
clip: rect(0 0 0 0);
|
|
524
524
|
white-space: nowrap;
|
|
525
525
|
}
|
|
526
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
527
|
+
@media (forced-colors: active) {
|
|
528
|
+
.active-indicator { background-color: Highlight; }
|
|
529
|
+
.tab-button.active, .tab-link.active { color: Highlight; }
|
|
530
|
+
.tab-link { border-bottom-color: Canvas; }
|
|
531
|
+
.tab-link.active { border-bottom-color: Highlight; }
|
|
532
|
+
.tab-dot { background: CanvasText; }
|
|
533
|
+
.tab-badge { background: Canvas; border: var(--smrt-hairline-strong); }
|
|
534
|
+
.tab-button:disabled, .tab-link[aria-disabled='true'] { opacity: 1; color: GrayText; }
|
|
535
|
+
}
|
|
526
536
|
}
|
|
527
537
|
</style>
|
|
@@ -73,5 +73,9 @@ const { variant = 'default', size = 'md', children, ...rest }: Props = $props();
|
|
|
73
73
|
background: var(--smrt-color-primary-container);
|
|
74
74
|
color: var(--smrt-color-on-primary-container);
|
|
75
75
|
}
|
|
76
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
77
|
+
@media (forced-colors: active) {
|
|
78
|
+
.badge { background: Canvas; color: CanvasText; border-color: CanvasText; }
|
|
79
|
+
}
|
|
76
80
|
}
|
|
77
81
|
</style>
|
|
@@ -190,5 +190,10 @@ const {
|
|
|
190
190
|
outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
|
|
191
191
|
outline-offset: var(--smrt-focus-ring-offset);
|
|
192
192
|
}
|
|
193
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
194
|
+
@media (forced-colors: active) {
|
|
195
|
+
.chip.selected { border-color: Highlight; outline: var(--smrt-focus-ring-width) solid Highlight; outline-offset: calc(-1 * var(--smrt-focus-ring-width)); }
|
|
196
|
+
.chip.disabled, .chip.selected.disabled { opacity: 1; color: GrayText; border-color: GrayText; outline-color: GrayText; }
|
|
197
|
+
}
|
|
193
198
|
}
|
|
194
199
|
</style>
|
|
@@ -138,5 +138,11 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
|
|
|
138
138
|
text-align: center;
|
|
139
139
|
font-variant-numeric: tabular-nums;
|
|
140
140
|
}
|
|
141
|
+
/* Forced colors (#3908): system colours where colour alone carries meaning. */
|
|
142
|
+
@media (forced-colors: active) {
|
|
143
|
+
.icon-toggle.pressed { background: Highlight; color: HighlightText; border-color: Highlight; }
|
|
144
|
+
.icon-toggle.pressed:not(:focus-visible) { outline: var(--smrt-focus-ring-width) solid CanvasText; outline-offset: calc(-2 * var(--smrt-focus-ring-width)); }
|
|
145
|
+
.icon-toggle:disabled, .icon-toggle.pressed:disabled { opacity: 1; background: Canvas; color: GrayText; border-color: GrayText; }
|
|
146
|
+
}
|
|
141
147
|
}
|
|
142
148
|
</style>
|
package/dist/themes/README.md
CHANGED
|
@@ -323,11 +323,65 @@ Behaviour notes (state exposed on context: `contrast`, `isChangingScheme`):
|
|
|
323
323
|
the root and `<html>` for one `--smrt-duration-scheme`, so styles can crossfade.
|
|
324
324
|
Never on first paint; skipped under `prefers-reduced-motion: reduce`.
|
|
325
325
|
- Contrast: `data-contrast="standard|more|forced"` on `<html>` and the root;
|
|
326
|
-
`forced-colors: active` also sets `data-forced-colors`.
|
|
327
|
-
forced-colors mode: do not paint over system colours, hairlines become `CanvasText`
|
|
328
|
-
and focus rings `Highlight` (component side: #3867 follow-up).
|
|
326
|
+
`forced-colors: active` also sets `data-forced-colors`. See [Forced colors](#forced-colors).
|
|
329
327
|
- `themeScript()` stamps `data-contrast` and (when persisted) `data-density` pre-paint.
|
|
330
328
|
|
|
329
|
+
## Forced colors
|
|
330
|
+
|
|
331
|
+
Windows High Contrast (`forced-colors: active`) replaces author colours with the user's
|
|
332
|
+
system palette. The theme layer and the components cooperate so nothing paints over it.
|
|
333
|
+
|
|
334
|
+
**Token level** (`styles/base.css`, mirrored by `FORCED_COLORS_TOKENS` in
|
|
335
|
+
`forced-colors.ts`). Under `@media (forced-colors: active)` and `[data-forced-colors]`
|
|
336
|
+
(stamped by `ThemeProvider` and `themeScript()` only while the media query matches; a test
|
|
337
|
+
can set it without emulation) the theme tokens below are redefined. Surface, text and
|
|
338
|
+
primary colours are not mapped: in forced-colors mode the browser already replaces them
|
|
339
|
+
with the system palette, so the map covers only what it cannot see through (shadows,
|
|
340
|
+
blur, translucent state layers, custom-property colours used outside colour properties):
|
|
341
|
+
|
|
342
|
+
| Token | Forced value |
|
|
343
|
+
| --- | --- |
|
|
344
|
+
| `--smrt-hairline-color` / `-strong-color` and the `--smrt-hairline*` shorthands | `CanvasText` |
|
|
345
|
+
| `--smrt-focus-ring-color` / `-width` / `-offset` | `Highlight` / `3px` / `2px` |
|
|
346
|
+
| `--smrt-inset-highlight`, `--smrt-elevation-0..5` | transparent shadows (never `none`: components compose `box-shadow` lists) |
|
|
347
|
+
| `--smrt-glass-blur` / `-saturation` | `0px` / `100%` |
|
|
348
|
+
| `--smrt-glass-surface(-strong)`, `--smrt-surface-overlay` | `Canvas` (scrim and glass edges also `CanvasText`) |
|
|
349
|
+
| `--smrt-state-hover/pressed/selected/focus/dragged` | `0` (a translucent fill cannot be seen) |
|
|
350
|
+
| `--smrt-accent-color`, selection, caret, scrollbar | `Highlight`, `HighlightText`, `CanvasText` |
|
|
351
|
+
|
|
352
|
+
These blocks are deliberately **unlayered**. Preset stylesheets declare their tokens
|
|
353
|
+
unlayered on `[data-theme][data-color-scheme]`, and an unlayered rule always beats a
|
|
354
|
+
layered one, so a layered override would never apply. The selectors out-rank the preset
|
|
355
|
+
block by one attribute. While forced, `ThemeProvider` also appends the same map to the
|
|
356
|
+
root inline style (after generated variables and `primaryColor`, before explicit
|
|
357
|
+
`overrides`), so runtime themes and `primaryColor` cannot win.
|
|
358
|
+
|
|
359
|
+
**Generic rules** (in `smrt.theme`, zero specificity, so a component rule always wins):
|
|
360
|
+
hover is a 1px `Highlight` outline instead of a state-layer fill, disabled controls use
|
|
361
|
+
`GrayText`, and elements that rely on the UA focus ring get the token ring.
|
|
362
|
+
|
|
363
|
+
**Components** add `@media (forced-colors: active)` rules only where colour alone carried
|
|
364
|
+
meaning, using system colours: `Canvas`, `CanvasText`, `Highlight`, `HighlightText`,
|
|
365
|
+
`GrayText` (and `LinkText` and friends for chart series). Switch, Toggle, Checkbox and
|
|
366
|
+
Radio fill their track, box or mark with `Highlight`; Tabs draw the `Highlight` indicator
|
|
367
|
+
only on the current tab; Chip, FilterChips, SegmentedControl, ToggleButton, IconToggle
|
|
368
|
+
and the selected DataTable row get a `Highlight` fill or outline; Badge and Alert keep
|
|
369
|
+
text and border without the tinted fill; Progress uses `Highlight`/`CanvasText`. The shell
|
|
370
|
+
(TenantNav, phone tab bar, dock toggles, account trigger, focus tools) keeps its selected
|
|
371
|
+
and open states, e.g. the tenant nav start bar becomes `Highlight` and the row is bold.
|
|
372
|
+
Component rules use the media query only (no attribute mirror); the attribute path exists
|
|
373
|
+
for the token layer.
|
|
374
|
+
|
|
375
|
+
**Exceptions to `forced-color-adjust: auto`**: none are needed today, because system
|
|
376
|
+
colour keywords survive the adjustment. A component that must show an author colour to be
|
|
377
|
+
understood (a colour picker or swatch) sets `forced-color-adjust: none` on that element
|
|
378
|
+
only and documents why. Chart series cycle system colours by slot and the legend always
|
|
379
|
+
carries label, value and share as text.
|
|
380
|
+
|
|
381
|
+
Verification: Chromium e2e with `page.emulateMedia({ forcedColors: 'active' })` in
|
|
382
|
+
`e2e/forced-colors.spec.ts` (smrt-ui), `packages/smrt-svelte/e2e/forced-colors.spec.ts`
|
|
383
|
+
(shell) and the playground host. Unit parity test: `src/themes/__tests__/forced-colors.test.ts`.
|
|
384
|
+
|
|
331
385
|
## Theme Switching
|
|
332
386
|
|
|
333
387
|
Use the context to switch themes programmatically:
|
|
@@ -5,6 +5,7 @@ import { SMRT_CASCADE_LAYERS_CSS } from '../styles/layers.js';
|
|
|
5
5
|
import { setThemeContext, type ThemeContext } from './context.svelte.js';
|
|
6
6
|
import { derivePrimaryOverrides } from './create-theme.js';
|
|
7
7
|
import { generateThemeVariables } from './css-generator.js';
|
|
8
|
+
import { FORCED_COLORS_TOKENS } from './forced-colors.js';
|
|
8
9
|
import { availablePresets, getTheme, isValidPreset } from './registry.js';
|
|
9
10
|
import type {
|
|
10
11
|
ColorScheme,
|
|
@@ -190,6 +191,14 @@ const styleString = $derived.by(() => {
|
|
|
190
191
|
);
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
// Forced colors (#3908): system palette beats every preset, runtime theme and
|
|
195
|
+
// primaryColor value; explicit `overrides` still win below.
|
|
196
|
+
if (resolvedContrast === 'forced') {
|
|
197
|
+
for (const [key, value] of Object.entries(FORCED_COLORS_TOKENS)) {
|
|
198
|
+
declarations.push(`${key}: ${value}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
193
202
|
// Apply custom overrides
|
|
194
203
|
for (const [key, value] of Object.entries(config.overrides ?? {})) {
|
|
195
204
|
declarations.push(`${key}: ${value}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/themes/ThemeProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"ThemeProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/themes/ThemeProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAQtC,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAGlB,WAAW,EACX,WAAW,EAEZ,MAAM,YAAY,CAAC;AAIpB,UAAU,KAAK;IACb,wFAAwF;IACxF,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,0BAA0B;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;IAC3C,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB;AA8dD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forced-colors.d.ts","sourceRoot":"","sources":["../../src/themes/forced-colors.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgCjE,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forced-colors (Windows High Contrast) token overrides (#3908).
|
|
3
|
+
*
|
|
4
|
+
* The single source for the theme-level contract documented in
|
|
5
|
+
* `themes/README.md` ("Forced colors"). `styles/base.css` carries the same
|
|
6
|
+
* declarations for stylesheet-only delivery (under `@media (forced-colors:
|
|
7
|
+
* active)` and `[data-forced-colors]`); `ThemeProvider` appends them to the
|
|
8
|
+
* root inline style while forced colors are active so a runtime theme or an
|
|
9
|
+
* inline `primaryColor` cannot paint over the system palette. A unit test keeps
|
|
10
|
+
* the two copies in step.
|
|
11
|
+
*
|
|
12
|
+
* Values are system colour keywords or inert shadows. Elevations are
|
|
13
|
+
* transparent shadows, never `none`, because components compose `box-shadow`
|
|
14
|
+
* lists such as `var(--smrt-inset-highlight), var(--smrt-elevation-3)`.
|
|
15
|
+
*/
|
|
16
|
+
const INERT_SHADOW = '0 0 0 0 transparent';
|
|
17
|
+
export const FORCED_COLORS_TOKENS = {
|
|
18
|
+
'--smrt-hairline-color': 'CanvasText',
|
|
19
|
+
'--smrt-hairline-strong-color': 'CanvasText',
|
|
20
|
+
'--smrt-hairline': '1px solid CanvasText',
|
|
21
|
+
'--smrt-hairline-strong': '1px solid CanvasText',
|
|
22
|
+
'--smrt-inset-highlight': 'inset 0 0 0 0 transparent',
|
|
23
|
+
'--smrt-elevation-0': INERT_SHADOW,
|
|
24
|
+
'--smrt-elevation-1': INERT_SHADOW,
|
|
25
|
+
'--smrt-elevation-2': INERT_SHADOW,
|
|
26
|
+
'--smrt-elevation-3': INERT_SHADOW,
|
|
27
|
+
'--smrt-elevation-4': INERT_SHADOW,
|
|
28
|
+
'--smrt-elevation-5': INERT_SHADOW,
|
|
29
|
+
'--smrt-focus-ring-color': 'Highlight',
|
|
30
|
+
'--smrt-focus-ring-width': '3px',
|
|
31
|
+
'--smrt-focus-ring-offset': '2px',
|
|
32
|
+
'--smrt-glass-blur': '0px',
|
|
33
|
+
'--smrt-glass-saturation': '100%',
|
|
34
|
+
'--smrt-glass-surface': 'Canvas',
|
|
35
|
+
'--smrt-glass-surface-strong': 'Canvas',
|
|
36
|
+
'--smrt-glass-border': 'CanvasText',
|
|
37
|
+
'--smrt-surface-overlay': 'Canvas',
|
|
38
|
+
'--smrt-state-hover': '0',
|
|
39
|
+
'--smrt-state-pressed': '0',
|
|
40
|
+
'--smrt-state-selected': '0',
|
|
41
|
+
'--smrt-state-focus': '0',
|
|
42
|
+
'--smrt-state-dragged': '0',
|
|
43
|
+
'--smrt-accent-color': 'Highlight',
|
|
44
|
+
'--smrt-caret-color': 'CanvasText',
|
|
45
|
+
'--smrt-selection-background': 'Highlight',
|
|
46
|
+
'--smrt-selection-color': 'HighlightText',
|
|
47
|
+
'--smrt-scrollbar-thumb': 'CanvasText',
|
|
48
|
+
'--smrt-scrollbar-track': 'Canvas',
|
|
49
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
@layer smrt.reset, smrt.structure, smrt.theme
|
|
2
|
+
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
3
|
+
@import url('./forced-colors.css') (forced-colors: active);
|
|
4
|
+
|
|
5
|
+
/**
|
|
3
6
|
* Application baseline for SMRT interfaces.
|
|
4
7
|
*
|
|
5
8
|
* Import this once at an application entry point, alongside the selected
|
|
@@ -257,3 +260,79 @@ time,
|
|
|
257
260
|
}
|
|
258
261
|
}
|
|
259
262
|
}
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* Forced colors (#3908). Token level: Windows High Contrast / `forced-colors:
|
|
266
|
+
* active` maps the theme onto the system palette. These two token blocks are
|
|
267
|
+
* deliberately UNLAYERED: preset stylesheets declare their tokens unlayered on
|
|
268
|
+
* `[data-theme][data-color-scheme]`, and an unlayered rule always beats a
|
|
269
|
+
* layered one, so a layered override would never apply. The selectors match
|
|
270
|
+
* whichever element carries the theme attributes (document root, ThemeProvider
|
|
271
|
+
* root or a consumer wrapper) with one more attribute selector than the preset
|
|
272
|
+
* block. The `forced-colors: active` media case is the conditional `@import`
|
|
273
|
+
* at the top of this file (forced-colors.css); the attribute case is the
|
|
274
|
+
* block below. The same values ship as
|
|
275
|
+
* `FORCED_COLORS_TOKENS` (src/themes/forced-colors.ts): ThemeProvider appends
|
|
276
|
+
* them inline so runtime themes cannot win; a unit test keeps both in step.
|
|
277
|
+
* `[data-forced-colors]` is stamped by ThemeProvider and `themeScript()`;
|
|
278
|
+
* tests can set it without media emulation. Components only consume tokens.
|
|
279
|
+
*/
|
|
280
|
+
|
|
281
|
+
[data-forced-colors][data-theme][data-color-scheme] {
|
|
282
|
+
--smrt-hairline-color: CanvasText;
|
|
283
|
+
--smrt-hairline-strong-color: CanvasText;
|
|
284
|
+
--smrt-hairline: 1px solid CanvasText;
|
|
285
|
+
--smrt-hairline-strong: 1px solid CanvasText;
|
|
286
|
+
--smrt-inset-highlight: inset 0 0 0 0 transparent;
|
|
287
|
+
--smrt-elevation-0: 0 0 0 0 transparent;
|
|
288
|
+
--smrt-elevation-1: 0 0 0 0 transparent;
|
|
289
|
+
--smrt-elevation-2: 0 0 0 0 transparent;
|
|
290
|
+
--smrt-elevation-3: 0 0 0 0 transparent;
|
|
291
|
+
--smrt-elevation-4: 0 0 0 0 transparent;
|
|
292
|
+
--smrt-elevation-5: 0 0 0 0 transparent;
|
|
293
|
+
--smrt-focus-ring-color: Highlight;
|
|
294
|
+
--smrt-focus-ring-width: 3px;
|
|
295
|
+
--smrt-focus-ring-offset: 2px;
|
|
296
|
+
--smrt-glass-blur: 0px;
|
|
297
|
+
--smrt-glass-saturation: 100%;
|
|
298
|
+
--smrt-glass-surface: Canvas;
|
|
299
|
+
--smrt-glass-surface-strong: Canvas;
|
|
300
|
+
--smrt-glass-border: CanvasText;
|
|
301
|
+
--smrt-surface-overlay: Canvas;
|
|
302
|
+
--smrt-state-hover: 0;
|
|
303
|
+
--smrt-state-pressed: 0;
|
|
304
|
+
--smrt-state-selected: 0;
|
|
305
|
+
--smrt-state-focus: 0;
|
|
306
|
+
--smrt-state-dragged: 0;
|
|
307
|
+
--smrt-accent-color: Highlight;
|
|
308
|
+
--smrt-caret-color: CanvasText;
|
|
309
|
+
--smrt-selection-background: Highlight;
|
|
310
|
+
--smrt-selection-color: HighlightText;
|
|
311
|
+
--smrt-scrollbar-thumb: CanvasText;
|
|
312
|
+
--smrt-scrollbar-track: Canvas;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/*
|
|
316
|
+
* A translucent state-layer fill cannot be seen in forced colors, so hover is
|
|
317
|
+
* an outline instead. :where() keeps specificity at zero: every component's own
|
|
318
|
+
* focus, selected or outline rule still wins.
|
|
319
|
+
*/
|
|
320
|
+
@layer smrt.theme {
|
|
321
|
+
@media (forced-colors: active) {
|
|
322
|
+
:where(button, [role="button"], [role="tab"], [role="menuitem"], [role="option"], a[href], summary, label):where(:hover):where(:not(:disabled, [aria-disabled="true"])) {
|
|
323
|
+
outline: 1px solid Highlight;
|
|
324
|
+
outline-offset: -1px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
:where(:disabled, [aria-disabled="true"]) {
|
|
328
|
+
color: GrayText;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* Elements that rely on the UA focus ring get the token ring instead (a
|
|
332
|
+
solid Highlight ring of a stated width); component rules still win. */
|
|
333
|
+
:where(a[href], button, summary, input, select, textarea, [tabindex], [role="tab"], [role="menuitem"], [role="option"]):where(:focus-visible) {
|
|
334
|
+
outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
|
|
335
|
+
outline-offset: var(--smrt-focus-ring-offset);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Forced colors token layer (#3908), loaded by base.css as
|
|
3
|
+
* `@import url('./forced-colors.css') (forced-colors: active)`. Deliberately
|
|
4
|
+
* UNLAYERED (and a declaration-only block): preset stylesheets declare their
|
|
5
|
+
* tokens unlayered on `[data-theme][data-color-scheme]`, and an unlayered rule
|
|
6
|
+
* always beats a layered one. The repeated `[data-theme]` out-ranks that block
|
|
7
|
+
* by one attribute. Values mirror `FORCED_COLORS_TOKENS`
|
|
8
|
+
* (src/themes/forced-colors.ts); a unit test keeps the copies in step.
|
|
9
|
+
*/
|
|
10
|
+
[data-theme][data-theme][data-color-scheme] {
|
|
11
|
+
--smrt-hairline-color: CanvasText;
|
|
12
|
+
--smrt-hairline-strong-color: CanvasText;
|
|
13
|
+
--smrt-hairline: 1px solid CanvasText;
|
|
14
|
+
--smrt-hairline-strong: 1px solid CanvasText;
|
|
15
|
+
--smrt-inset-highlight: inset 0 0 0 0 transparent;
|
|
16
|
+
--smrt-elevation-0: 0 0 0 0 transparent;
|
|
17
|
+
--smrt-elevation-1: 0 0 0 0 transparent;
|
|
18
|
+
--smrt-elevation-2: 0 0 0 0 transparent;
|
|
19
|
+
--smrt-elevation-3: 0 0 0 0 transparent;
|
|
20
|
+
--smrt-elevation-4: 0 0 0 0 transparent;
|
|
21
|
+
--smrt-elevation-5: 0 0 0 0 transparent;
|
|
22
|
+
--smrt-focus-ring-color: Highlight;
|
|
23
|
+
--smrt-focus-ring-width: 3px;
|
|
24
|
+
--smrt-focus-ring-offset: 2px;
|
|
25
|
+
--smrt-glass-blur: 0px;
|
|
26
|
+
--smrt-glass-saturation: 100%;
|
|
27
|
+
--smrt-glass-surface: Canvas;
|
|
28
|
+
--smrt-glass-surface-strong: Canvas;
|
|
29
|
+
--smrt-glass-border: CanvasText;
|
|
30
|
+
--smrt-surface-overlay: Canvas;
|
|
31
|
+
--smrt-state-hover: 0;
|
|
32
|
+
--smrt-state-pressed: 0;
|
|
33
|
+
--smrt-state-selected: 0;
|
|
34
|
+
--smrt-state-focus: 0;
|
|
35
|
+
--smrt-state-dragged: 0;
|
|
36
|
+
--smrt-accent-color: Highlight;
|
|
37
|
+
--smrt-caret-color: CanvasText;
|
|
38
|
+
--smrt-selection-background: Highlight;
|
|
39
|
+
--smrt-selection-color: HighlightText;
|
|
40
|
+
--smrt-scrollbar-thumb: CanvasText;
|
|
41
|
+
--smrt-scrollbar-track: Canvas;
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-ui",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.21",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Domain-agnostic Svelte 5 UI runtime for SMRT: primitives, i18n client, theme system, and module UI registry",
|
|
6
6
|
"type": "module",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"access": "public"
|
|
156
156
|
},
|
|
157
157
|
"dependencies": {
|
|
158
|
-
"@happyvertical/smrt-types": "0.55.
|
|
158
|
+
"@happyvertical/smrt-types": "0.55.21",
|
|
159
159
|
"esm-env": "^1.2.2"
|
|
160
160
|
},
|
|
161
161
|
"peerDependencies": {
|