@scalewing/react 1.59.3 → 1.59.5
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/CHANGELOG.md +12 -0
- package/dist/components/AppShell.js +15 -4
- package/dist/css/css-action-bar.d.ts +1 -0
- package/dist/css/css-action-bar.js +15 -0
- package/dist/css/css-app-shell-page-bar.d.ts +2 -0
- package/dist/css/css-app-shell-page-bar.js +29 -0
- package/dist/css/css-filter-chips.js +2 -1
- package/dist/css/css-segmented.js +2 -0
- package/dist/styles.css +24 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @scalewing/react
|
|
2
2
|
|
|
3
|
+
## 1.59.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e06c1e2: Forced colours show every choice. The chosen `FilterChips` chip sets `forced-color-adjust: none` on its Highlight face, so Chromium's Canvas backplate no longer hides its HighlightText words, and every `SegmentedControl` variant (plain and filled, not only the icon and raised tracks) outlines its selected item in Highlight. No API change.
|
|
8
|
+
|
|
9
|
+
## 1.59.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 29e682e: Focus stays clear of sticky chrome (WCAG 2.4.11). `AppShell` measures its `pageBar` (as it does the compact bar) and gives everything in `main` a `scroll-margin-block-start` past the page bar and, below `lg`, the compact bar, so a control the browser scrolls into view on focus stops under them, not behind them. A stuck `ActionBar` gives the document a `scroll-padding-block-end` of its one-row height and lift, so a control above it is not scrolled behind it. No API change.
|
|
14
|
+
|
|
3
15
|
## 1.59.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useId, useRef, useState } from 'react';
|
|
4
4
|
import { breakpointQuery } from '../css/breakpoints.js';
|
|
5
5
|
import { appShellWideQuery } from '../css/css-app-shell.js';
|
|
6
|
-
import { appShellBarHeightVariable } from '../css/css-app-shell-page-bar.js';
|
|
6
|
+
import { appShellBarHeightVariable, appShellPageBarHeightVariable, } from '../css/css-app-shell-page-bar.js';
|
|
7
7
|
import { AppShellBar } from './app-shell/AppShellBar.js';
|
|
8
8
|
import { AppShellDrawer } from './app-shell/AppShellDrawer.js';
|
|
9
9
|
import { AppShellSidebar } from './app-shell/AppShellSidebar.js';
|
|
@@ -47,9 +47,20 @@ export function AppShell({ barEnd, barTitle, children, footer, groups, header, l
|
|
|
47
47
|
// make it; the generated CSS falls back to the bar's usual height.
|
|
48
48
|
const hasPageBar = pageBar !== undefined && pageBar !== null && pageBar !== false;
|
|
49
49
|
const barHeight = useElementHeight(barRef, hasPageBar);
|
|
50
|
-
|
|
50
|
+
// The page bar's own height keeps a focused control clear of it when the
|
|
51
|
+
// browser scrolls it into view (WCAG 2.4.11), however much it wraps.
|
|
52
|
+
const pageBarRef = useRef(null);
|
|
53
|
+
const pageBarHeight = useElementHeight(pageBarRef, hasPageBar);
|
|
54
|
+
const shellStyle = barHeight === null && pageBarHeight === null
|
|
51
55
|
? undefined
|
|
52
|
-
: {
|
|
56
|
+
: {
|
|
57
|
+
...(barHeight === null
|
|
58
|
+
? {}
|
|
59
|
+
: { [appShellBarHeightVariable]: `${barHeight}px` }),
|
|
60
|
+
...(pageBarHeight === null
|
|
61
|
+
? {}
|
|
62
|
+
: { [appShellPageBarHeightVariable]: `${pageBarHeight}px` }),
|
|
63
|
+
};
|
|
53
64
|
// The drawer belongs to the bar, which is gone from `lg` up: a window
|
|
54
65
|
// widened while it is open closes it.
|
|
55
66
|
if (sidebarShown && menuOpen)
|
|
@@ -63,7 +74,7 @@ export function AppShell({ barEnd, barTitle, children, footer, groups, header, l
|
|
|
63
74
|
refocusRef.current = false;
|
|
64
75
|
menuRef.current?.focus();
|
|
65
76
|
}, [menuOpen]);
|
|
66
|
-
return (_jsxs("div", { className: "sw-app-shell", style: shellStyle, children: [skipLink ? (_jsx(SkipToMain, { label: skipLink.label, mainId: mainId, mainRef: mainRef })) : null, _jsx(AppShellBar, { barEnd: barEnd, barRef: barRef, barTitle: barTitle, menuCount: menuCount, menuCountLabel: menuCountLabel, menuCountTone: menuCountTone, menuIcon: menuIcon, menuOpen: menuOpen, menuRef: menuRef, onOpenMenu: () => setMenuOpen(true), openMenuLabel: labels.openMenu }), _jsx(AppShellSidebar, { footer: footer, groups: groups, header: header, navLabel: navLabel, onNavigate: onNavigate, rail: sidebarShown && !wide, railFooter: railFooter, railHeader: railHeader }), _jsxs("main", { ref: mainRef, className: "sw-app-shell-main", id: skipLink ? mainId : undefined, tabIndex: skipLink ? -1 : undefined, children: [hasPageBar ? (_jsx("div", { className: "sw-app-shell-page-bar", children: pageBar })) : null, children] }), _jsx(AppShellDrawer, { closeLabel: labels.closeMenu, footer: footer, groups: groups, header: header, navLabel: navLabel, onChoose: () => setMenuOpen(false), onClose: () => {
|
|
77
|
+
return (_jsxs("div", { className: "sw-app-shell", style: shellStyle, children: [skipLink ? (_jsx(SkipToMain, { label: skipLink.label, mainId: mainId, mainRef: mainRef })) : null, _jsx(AppShellBar, { barEnd: barEnd, barRef: barRef, barTitle: barTitle, menuCount: menuCount, menuCountLabel: menuCountLabel, menuCountTone: menuCountTone, menuIcon: menuIcon, menuOpen: menuOpen, menuRef: menuRef, onOpenMenu: () => setMenuOpen(true), openMenuLabel: labels.openMenu }), _jsx(AppShellSidebar, { footer: footer, groups: groups, header: header, navLabel: navLabel, onNavigate: onNavigate, rail: sidebarShown && !wide, railFooter: railFooter, railHeader: railHeader }), _jsxs("main", { ref: mainRef, className: "sw-app-shell-main", id: skipLink ? mainId : undefined, tabIndex: skipLink ? -1 : undefined, children: [hasPageBar ? (_jsx("div", { ref: pageBarRef, className: "sw-app-shell-page-bar", children: pageBar })) : null, children] }), _jsx(AppShellDrawer, { closeLabel: labels.closeMenu, footer: footer, groups: groups, header: header, navLabel: navLabel, onChoose: () => setMenuOpen(false), onClose: () => {
|
|
67
78
|
refocusRef.current = true;
|
|
68
79
|
setMenuOpen(false);
|
|
69
80
|
}, onNavigate: onNavigate, open: menuOpen })] }));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Breakpoint } from './breakpoints.js';
|
|
2
2
|
export declare function actionBarStickyClass(stickyBelow: Breakpoint | undefined): string;
|
|
3
|
+
export declare const actionBarClearance = "calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-3) + 2px + 2 * var(--sw-space-2) + env(safe-area-inset-bottom, 0px))";
|
|
3
4
|
export declare function cssActionBarClasses(): string;
|
|
4
5
|
export declare function actionBarClassCatalog(): string[];
|
|
@@ -18,18 +18,33 @@ export function actionBarStickyClass(stickyBelow) {
|
|
|
18
18
|
? `sw-action-bar-sticky-below-${stickyBelow}`
|
|
19
19
|
: 'sw-action-bar-sticky';
|
|
20
20
|
}
|
|
21
|
+
/*
|
|
22
|
+
* While a bar is stuck to the bottom, the document keeps a focused control
|
|
23
|
+
* clear of it when the browser scrolls it into view (WCAG 2.4.11): the
|
|
24
|
+
* bar's one-row height (an md control in its space-3 padding and hairline
|
|
25
|
+
* border), its lift off the edge, and a space-2 gap. A bar that wraps to a
|
|
26
|
+
* second row is taller than this.
|
|
27
|
+
*/
|
|
28
|
+
export const actionBarClearance = 'calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-3) + 2px + 2 * var(--sw-space-2) + env(safe-area-inset-bottom, 0px))';
|
|
29
|
+
function clearOf(stickyClass, indent) {
|
|
30
|
+
return `${indent}:root:has(.${stickyClass}) { scroll-padding-block-end: ${actionBarClearance}; }`;
|
|
31
|
+
}
|
|
21
32
|
function stickyRules() {
|
|
22
33
|
const below = breakpoints
|
|
23
34
|
.map((breakpoint) => `@media ${breakpointQuery('below', breakpoint)} {
|
|
24
35
|
.${actionBarStickyClass(breakpoint)} {
|
|
25
36
|
${stuckToBottom(' ')}
|
|
26
37
|
}
|
|
38
|
+
|
|
39
|
+
${clearOf(actionBarStickyClass(breakpoint), ' ')}
|
|
27
40
|
}`)
|
|
28
41
|
.join('\n\n');
|
|
29
42
|
return `.sw-action-bar-sticky {
|
|
30
43
|
${stuckToBottom(' ')}
|
|
31
44
|
}
|
|
32
45
|
|
|
46
|
+
${clearOf('sw-action-bar-sticky', '')}
|
|
47
|
+
|
|
33
48
|
${below}`;
|
|
34
49
|
}
|
|
35
50
|
export function cssActionBarClasses() {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export declare const appShellBarHeightVariable = "--sw-app-shell-bar-height";
|
|
2
|
+
/** The page bar's measured height, set beside the bar's. */
|
|
3
|
+
export declare const appShellPageBarHeightVariable = "--sw-app-shell-page-bar-height";
|
|
2
4
|
export declare function cssAppShellPageBarClasses(): string;
|
|
3
5
|
export declare function appShellPageBarClassCatalog(): string[];
|
|
@@ -7,6 +7,8 @@ import { coarsePointerQuery, touchTarget } from './touch-target.js';
|
|
|
7
7
|
* the bar whatever its end controls make it.
|
|
8
8
|
*/
|
|
9
9
|
export const appShellBarHeightVariable = '--sw-app-shell-bar-height';
|
|
10
|
+
/** The page bar's measured height, set beside the bar's. */
|
|
11
|
+
export const appShellPageBarHeightVariable = '--sw-app-shell-page-bar-height';
|
|
10
12
|
/*
|
|
11
13
|
* The bar's usual height before it is measured (and without script): the
|
|
12
14
|
* sm menu button, or the touch target on a coarse pointer, inside the
|
|
@@ -51,6 +53,33 @@ function pageBarRules() {
|
|
|
51
53
|
padding-inline: var(--sw-space-6);
|
|
52
54
|
top: 0;
|
|
53
55
|
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
${focusClearRules()}`;
|
|
59
|
+
}
|
|
60
|
+
/*
|
|
61
|
+
* Focus clear of the sticky chrome (WCAG 2.4.11): when the browser scrolls a
|
|
62
|
+
* focused control into view, it stops a space-2 below the page bar (and the
|
|
63
|
+
* compact bar over it below `lg`), not under them. The page bar's measured
|
|
64
|
+
* height falls back to its usual one: an sm control in its space-2 padding
|
|
65
|
+
* and hairline.
|
|
66
|
+
*/
|
|
67
|
+
function focusClearRules() {
|
|
68
|
+
const pageBar = `var(${appShellPageBarHeightVariable}, ${barHeightFallback('var(--sw-control-sm-min-height)')})`;
|
|
69
|
+
return `.sw-app-shell-main:has(> .sw-app-shell-page-bar) * {
|
|
70
|
+
scroll-margin-block-start: calc(var(${appShellBarHeightVariable}, ${barHeightFallback('var(--sw-control-sm-min-height)')}) + ${pageBar} + var(--sw-space-2));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@media ${coarsePointerQuery} {
|
|
74
|
+
.sw-app-shell-main:has(> .sw-app-shell-page-bar) * {
|
|
75
|
+
scroll-margin-block-start: calc(var(${appShellBarHeightVariable}, ${barHeightFallback(touchTarget)}) + ${pageBar} + var(--sw-space-2));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@media ${fromLg} {
|
|
80
|
+
.sw-app-shell-main:has(> .sw-app-shell-page-bar) * {
|
|
81
|
+
scroll-margin-block-start: calc(${pageBar} + var(--sw-space-2));
|
|
82
|
+
}
|
|
54
83
|
}`;
|
|
55
84
|
}
|
|
56
85
|
/*
|
|
@@ -135,7 +135,8 @@ ${cssFilterChipsRowClasses()}
|
|
|
135
135
|
@media (forced-colors: active) {
|
|
136
136
|
.sw-filter-chip-face { background: Canvas; border-color: CanvasText; color: CanvasText; }
|
|
137
137
|
.sw-filter-chips-error { color: Mark; }
|
|
138
|
-
|
|
138
|
+
/* Chromium paints a Canvas backplate behind text in forced colours; HighlightText on it vanishes. */
|
|
139
|
+
.sw-filter-chip-input:checked + .sw-filter-chip-face { background: Highlight; border-color: Highlight; color: HighlightText; forced-color-adjust: none; }
|
|
139
140
|
.sw-filter-chip-input:focus-visible + .sw-filter-chip-face { outline-color: Highlight; }
|
|
140
141
|
.sw-filter-chip-count { color: inherit; }
|
|
141
142
|
/* After the checked rule, so every chip, the chosen one too, takes the edge. */
|
|
@@ -99,6 +99,8 @@ function fieldRules() {
|
|
|
99
99
|
|
|
100
100
|
@media (forced-colors: active) {
|
|
101
101
|
.sw-segmented[aria-invalid='true'] { border-color: Mark; }
|
|
102
|
+
/* Every variant's chosen item: its surface or accent fill is dropped. */
|
|
103
|
+
.sw-segmented-item-selected { outline: 2px solid Highlight; }
|
|
102
104
|
}`;
|
|
103
105
|
}
|
|
104
106
|
export function cssSegmentedClasses() {
|
package/dist/styles.css
CHANGED
|
@@ -1546,6 +1546,8 @@ body {
|
|
|
1546
1546
|
|
|
1547
1547
|
@media (forced-colors: active) {
|
|
1548
1548
|
.sw-segmented[aria-invalid='true'] { border-color: Mark; }
|
|
1549
|
+
/* Every variant's chosen item: its surface or accent fill is dropped. */
|
|
1550
|
+
.sw-segmented-item-selected { outline: 2px solid Highlight; }
|
|
1549
1551
|
}
|
|
1550
1552
|
|
|
1551
1553
|
.sw-segmented {
|
|
@@ -6029,6 +6031,22 @@ html:has(.sw-site-header-sticky) {
|
|
|
6029
6031
|
}
|
|
6030
6032
|
}
|
|
6031
6033
|
|
|
6034
|
+
.sw-app-shell-main:has(> .sw-app-shell-page-bar) * {
|
|
6035
|
+
scroll-margin-block-start: calc(var(--sw-app-shell-bar-height, calc(var(--sw-control-sm-min-height) + 2 * var(--sw-space-2) + 1px)) + var(--sw-app-shell-page-bar-height, calc(var(--sw-control-sm-min-height) + 2 * var(--sw-space-2) + 1px)) + var(--sw-space-2));
|
|
6036
|
+
}
|
|
6037
|
+
|
|
6038
|
+
@media (pointer: coarse) {
|
|
6039
|
+
.sw-app-shell-main:has(> .sw-app-shell-page-bar) * {
|
|
6040
|
+
scroll-margin-block-start: calc(var(--sw-app-shell-bar-height, calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-2) + 1px)) + var(--sw-app-shell-page-bar-height, calc(var(--sw-control-sm-min-height) + 2 * var(--sw-space-2) + 1px)) + var(--sw-space-2));
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
|
|
6044
|
+
@media (min-width: 64rem) {
|
|
6045
|
+
.sw-app-shell-main:has(> .sw-app-shell-page-bar) * {
|
|
6046
|
+
scroll-margin-block-start: calc(var(--sw-app-shell-page-bar-height, calc(var(--sw-control-sm-min-height) + 2 * var(--sw-space-2) + 1px)) + var(--sw-space-2));
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
|
|
6032
6050
|
.sw-app-shell-menu {
|
|
6033
6051
|
position: relative;
|
|
6034
6052
|
}
|
|
@@ -7924,12 +7942,16 @@ html:has(.sw-site-header-sticky) {
|
|
|
7924
7942
|
z-index: 3;
|
|
7925
7943
|
}
|
|
7926
7944
|
|
|
7945
|
+
:root:has(.sw-action-bar-sticky) { scroll-padding-block-end: calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-3) + 2px + 2 * var(--sw-space-2) + env(safe-area-inset-bottom, 0px)); }
|
|
7946
|
+
|
|
7927
7947
|
@media not all and (min-width: 48rem) {
|
|
7928
7948
|
.sw-action-bar-sticky-below-md {
|
|
7929
7949
|
bottom: calc(var(--sw-space-2) + env(safe-area-inset-bottom, 0px));
|
|
7930
7950
|
position: sticky;
|
|
7931
7951
|
z-index: 3;
|
|
7932
7952
|
}
|
|
7953
|
+
|
|
7954
|
+
:root:has(.sw-action-bar-sticky-below-md) { scroll-padding-block-end: calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-3) + 2px + 2 * var(--sw-space-2) + env(safe-area-inset-bottom, 0px)); }
|
|
7933
7955
|
}
|
|
7934
7956
|
|
|
7935
7957
|
.sw-switch {
|
|
@@ -8933,7 +8955,8 @@ html:has(.sw-site-header-sticky) {
|
|
|
8933
8955
|
@media (forced-colors: active) {
|
|
8934
8956
|
.sw-filter-chip-face { background: Canvas; border-color: CanvasText; color: CanvasText; }
|
|
8935
8957
|
.sw-filter-chips-error { color: Mark; }
|
|
8936
|
-
|
|
8958
|
+
/* Chromium paints a Canvas backplate behind text in forced colours; HighlightText on it vanishes. */
|
|
8959
|
+
.sw-filter-chip-input:checked + .sw-filter-chip-face { background: Highlight; border-color: Highlight; color: HighlightText; forced-color-adjust: none; }
|
|
8937
8960
|
.sw-filter-chip-input:focus-visible + .sw-filter-chip-face { outline-color: Highlight; }
|
|
8938
8961
|
.sw-filter-chip-count { color: inherit; }
|
|
8939
8962
|
/* After the checked rule, so every chip, the chosen one too, takes the edge. */
|