@scalewing/react 1.47.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +171 -4
  3. package/dist/command-palette.test-support.d.ts +21 -0
  4. package/dist/command-palette.test-support.js +55 -0
  5. package/dist/components/AppHeader.d.ts +3 -0
  6. package/dist/components/AppShell.d.ts +4 -3
  7. package/dist/components/AppShell.js +24 -7
  8. package/dist/components/Band.d.ts +30 -0
  9. package/dist/components/Band.js +23 -0
  10. package/dist/components/ButtonLink.d.ts +33 -0
  11. package/dist/components/ButtonLink.js +27 -0
  12. package/dist/components/CommandPalette.d.ts +12 -0
  13. package/dist/components/CommandPalette.js +143 -0
  14. package/dist/components/CommandPaletteTrigger.d.ts +9 -0
  15. package/dist/components/CommandPaletteTrigger.js +16 -0
  16. package/dist/components/ContextSwitcher.d.ts +12 -0
  17. package/dist/components/ContextSwitcher.js +62 -0
  18. package/dist/components/FixtureGroup.d.ts +9 -0
  19. package/dist/components/FixtureGroup.js +16 -0
  20. package/dist/components/FixtureRow.d.ts +10 -0
  21. package/dist/components/FixtureRow.js +46 -0
  22. package/dist/components/GlyphTile.d.ts +9 -3
  23. package/dist/components/GlyphTile.js +15 -4
  24. package/dist/components/SiteFooter.d.ts +12 -0
  25. package/dist/components/SiteFooter.js +23 -0
  26. package/dist/components/SiteHeader.d.ts +14 -0
  27. package/dist/components/SiteHeader.js +24 -0
  28. package/dist/components/Skeleton.d.ts +22 -0
  29. package/dist/components/Skeleton.js +30 -0
  30. package/dist/components/app-shell/AppShellBar.d.ts +23 -0
  31. package/dist/components/app-shell/AppShellBar.js +25 -0
  32. package/dist/components/app-shell/types.d.ts +36 -1
  33. package/dist/components/app-shell/validate.d.ts +4 -3
  34. package/dist/components/app-shell/validate.js +21 -3
  35. package/dist/components/command-palette/PaletteList.d.ts +20 -0
  36. package/dist/components/command-palette/PaletteList.js +21 -0
  37. package/dist/components/command-palette/shortcut.d.ts +19 -0
  38. package/dist/components/command-palette/shortcut.js +75 -0
  39. package/dist/components/command-palette/types.d.ts +72 -0
  40. package/dist/components/command-palette/types.js +2 -0
  41. package/dist/components/command-palette/validate.d.ts +10 -0
  42. package/dist/components/command-palette/validate.js +50 -0
  43. package/dist/components/context-switcher/SwitcherOption.d.ts +17 -0
  44. package/dist/components/context-switcher/SwitcherOption.js +20 -0
  45. package/dist/components/context-switcher/SwitcherPopup.d.ts +31 -0
  46. package/dist/components/context-switcher/SwitcherPopup.js +111 -0
  47. package/dist/components/context-switcher/options.d.ts +33 -0
  48. package/dist/components/context-switcher/options.js +60 -0
  49. package/dist/components/context-switcher/types.d.ts +96 -0
  50. package/dist/components/context-switcher/types.js +1 -0
  51. package/dist/components/context-switcher/use-switcher-list.d.ts +29 -0
  52. package/dist/components/context-switcher/use-switcher-list.js +93 -0
  53. package/dist/components/context-switcher/validate.d.ts +8 -0
  54. package/dist/components/context-switcher/validate.js +94 -0
  55. package/dist/components/fixture/fixture-list-context.d.ts +4 -0
  56. package/dist/components/fixture/fixture-list-context.js +9 -0
  57. package/dist/components/fixture/types.d.ts +50 -0
  58. package/dist/components/fixture/types.js +1 -0
  59. package/dist/components/fixture/validate.d.ts +8 -0
  60. package/dist/components/fixture/validate.js +36 -0
  61. package/dist/components/site-footer/types.d.ts +31 -0
  62. package/dist/components/site-footer/types.js +1 -0
  63. package/dist/components/site-footer/validate.d.ts +7 -0
  64. package/dist/components/site-footer/validate.js +22 -0
  65. package/dist/components/site-header/SiteHeaderLinks.d.ts +7 -0
  66. package/dist/components/site-header/SiteHeaderLinks.js +7 -0
  67. package/dist/components/site-header/types.d.ts +38 -0
  68. package/dist/components/site-header/types.js +1 -0
  69. package/dist/components/site-header/use-menu-dismiss.d.ts +8 -0
  70. package/dist/components/site-header/use-menu-dismiss.js +62 -0
  71. package/dist/components/site-header/validate.d.ts +7 -0
  72. package/dist/components/site-header/validate.js +17 -0
  73. package/dist/components/use-element-height.d.ts +10 -0
  74. package/dist/components/use-element-height.js +35 -0
  75. package/dist/context-switcher.test-support.d.ts +9 -0
  76. package/dist/context-switcher.test-support.js +41 -0
  77. package/dist/css/css-app-shell-page-bar.d.ts +3 -0
  78. package/dist/css/css-app-shell-page-bar.js +81 -0
  79. package/dist/css/css-app-shell.js +6 -1
  80. package/dist/css/css-band.d.ts +10 -0
  81. package/dist/css/css-band.js +101 -0
  82. package/dist/css/css-button-link.d.ts +3 -0
  83. package/dist/css/css-button-link.js +48 -0
  84. package/dist/css/css-command-palette-key.d.ts +1 -0
  85. package/dist/css/css-command-palette-key.js +33 -0
  86. package/dist/css/css-command-palette-trigger.d.ts +2 -0
  87. package/dist/css/css-command-palette-trigger.js +88 -0
  88. package/dist/css/css-command-palette.d.ts +2 -0
  89. package/dist/css/css-command-palette.js +209 -0
  90. package/dist/css/css-components.js +24 -0
  91. package/dist/css/css-context-switcher-trigger.d.ts +15 -0
  92. package/dist/css/css-context-switcher-trigger.js +175 -0
  93. package/dist/css/css-context-switcher.d.ts +4 -0
  94. package/dist/css/css-context-switcher.js +219 -0
  95. package/dist/css/css-count-pill.d.ts +2 -2
  96. package/dist/css/css-count-pill.js +11 -3
  97. package/dist/css/css-fixture.d.ts +5 -0
  98. package/dist/css/css-fixture.js +260 -0
  99. package/dist/css/css-glyph-tile.d.ts +2 -0
  100. package/dist/css/css-glyph-tile.js +29 -1
  101. package/dist/css/css-site-footer.d.ts +2 -0
  102. package/dist/css/css-site-footer.js +152 -0
  103. package/dist/css/css-site-header.d.ts +2 -0
  104. package/dist/css/css-site-header.js +262 -0
  105. package/dist/css/css-skeleton.d.ts +10 -0
  106. package/dist/css/css-skeleton.js +110 -0
  107. package/dist/css/stacking.js +7 -4
  108. package/dist/css/stylesheet.js +16 -0
  109. package/dist/index.d.ts +13 -0
  110. package/dist/index.js +10 -0
  111. package/dist/styles.css +2008 -719
  112. package/package.json +2 -2
@@ -0,0 +1,152 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
4
+ const label = typographyVariants.label;
5
+ const caption = typographyVariants.caption;
6
+ /** The font declarations of a typography variant, weight included or not. */
7
+ function type(variant, weight = variant.fontWeight) {
8
+ return `font-size: ${variant.fontSize}px;
9
+ font-weight: ${weight};
10
+ letter-spacing: ${variant.letterSpacing}px;
11
+ line-height: ${variant.lineHeight}px;`;
12
+ }
13
+ /*
14
+ * The footer sits on the inverse band (`Band tone="inverse"`), which
15
+ * re-scopes the theme's words, muted words and hairline, so these rules
16
+ * read the ordinary variables. Group labels are the band's words at label
17
+ * weight; links are its muted words in label size, no underline (a list
18
+ * of links in a named `nav`), the band's words with an underline on hover
19
+ * and focus, and the current page in the band's words at label weight. The
20
+ * link rules match the band's running-text link rule's specificity and
21
+ * win by coming after it (`cssSiteFooterClasses` follows `cssBandClasses`;
22
+ * a test holds the order).
23
+ * Below `md` the brand spans two columns of groups; from `md` the brand
24
+ * and the groups share one wrapping row, spread apart.
25
+ */
26
+ export function cssSiteFooterClasses() {
27
+ return `.sw-site-footer-top {
28
+ display: grid;
29
+ gap: var(--sw-space-6) var(--sw-space-5);
30
+ grid-template-columns: repeat(2, minmax(0, 1fr));
31
+ }
32
+
33
+ .sw-site-footer-brand {
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: var(--sw-space-3);
37
+ grid-column: 1 / -1;
38
+ max-width: 20rem;
39
+ }
40
+
41
+ .sw-site-footer-brand > :first-child {
42
+ align-self: flex-start;
43
+ }
44
+
45
+ .sw-site-footer-tagline {
46
+ color: var(--sw-color-muted);
47
+ margin: 0;
48
+ ${type(label, 400)}
49
+ }
50
+
51
+ .sw-site-footer-group {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: var(--sw-space-3);
55
+ min-width: 0;
56
+ }
57
+
58
+ .sw-site-footer-label {
59
+ color: var(--sw-color-text);
60
+ margin: 0;
61
+ ${type(caption, 600)}
62
+ }
63
+
64
+ .sw-site-footer-links {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: var(--sw-space-2);
68
+ list-style: none;
69
+ margin: 0;
70
+ padding: 0;
71
+ }
72
+
73
+ [data-theme] .sw-band-inverse a.sw-site-footer-link {
74
+ color: var(--sw-color-muted);
75
+ display: inline-flex;
76
+ ${type(label, 400)}
77
+ overflow-wrap: anywhere;
78
+ text-decoration-color: transparent;
79
+ }
80
+
81
+ [data-theme] .sw-band-inverse a.sw-site-footer-link:is(:hover, :focus-visible) {
82
+ color: var(--sw-color-text);
83
+ text-decoration-color: currentColor;
84
+ }
85
+
86
+ [data-theme] .sw-band-inverse a.sw-site-footer-link[aria-current='page'] {
87
+ color: var(--sw-color-text);
88
+ font-weight: ${label.fontWeight};
89
+ }
90
+
91
+ .sw-site-footer-bottom {
92
+ align-items: center;
93
+ border-top: 1px solid var(--sw-color-border);
94
+ color: var(--sw-color-muted);
95
+ display: flex;
96
+ flex-wrap: wrap;
97
+ gap: var(--sw-space-3);
98
+ justify-content: space-between;
99
+ margin-top: var(--sw-space-6);
100
+ padding-top: var(--sw-space-4);
101
+ ${type(caption)}
102
+ }
103
+
104
+ .sw-site-footer-end {
105
+ margin-inline-start: auto;
106
+ }
107
+
108
+ @media ${breakpointQuery('from', 'md')} {
109
+ .sw-site-footer-top {
110
+ display: flex;
111
+ flex-wrap: wrap;
112
+ gap: var(--sw-space-6) var(--sw-space-8);
113
+ justify-content: space-between;
114
+ }
115
+ }
116
+
117
+ @media ${coarsePointerQuery} {
118
+ [data-theme] .sw-band-inverse a.sw-site-footer-link {
119
+ align-items: center;
120
+ min-height: ${touchTarget};
121
+ }
122
+
123
+ .sw-site-footer-links {
124
+ gap: 0;
125
+ }
126
+ }
127
+
128
+ @media (forced-colors: active) {
129
+ [data-theme] .sw-band-inverse a.sw-site-footer-link {
130
+ color: LinkText;
131
+ }
132
+
133
+ .sw-site-footer-bottom {
134
+ border-top-color: CanvasText;
135
+ }
136
+ }`;
137
+ }
138
+ export function siteFooterClassCatalog() {
139
+ return [
140
+ 'sw-site-footer',
141
+ 'sw-site-footer-top',
142
+ 'sw-site-footer-brand',
143
+ 'sw-site-footer-tagline',
144
+ 'sw-site-footer-group',
145
+ 'sw-site-footer-label',
146
+ 'sw-site-footer-links',
147
+ 'sw-site-footer-link',
148
+ 'sw-site-footer-bottom',
149
+ 'sw-site-footer-legal',
150
+ 'sw-site-footer-end',
151
+ ];
152
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssSiteHeaderClasses(): string;
2
+ export declare function siteHeaderClassCatalog(): string[];
@@ -0,0 +1,262 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { quietFill } from './quiet-fill.js';
4
+ import { zIndex } from './stacking.js';
5
+ const label = typographyVariants.label;
6
+ const title = typographyVariants.title;
7
+ /** The bar's height: 56 px on a phone, 72 px from `lg`. */
8
+ const compactHeight = 'calc(var(--sw-control-md-min-height) + var(--sw-space-3))';
9
+ const wideHeight = 'calc(var(--sw-control-md-min-height) + var(--sw-space-5) + var(--sw-space-1))';
10
+ const wide = breakpointQuery('from', 'lg');
11
+ /*
12
+ * The bar: the solid surface with a hairline under it, its content in the
13
+ * page container with the page gutter. Below `lg` it holds the brand, the
14
+ * compact actions and the menu button; from `lg` the brand, the
15
+ * destinations (the current one at label weight over a bar in the words'
16
+ * colour, as tall as the bar) and the actions, spread apart.
17
+ */
18
+ function barRules() {
19
+ return `.sw-site-header {
20
+ --sw-site-header-height: ${compactHeight};
21
+ background: var(--sw-color-surface);
22
+ border-bottom: 1px solid var(--sw-color-border);
23
+ color: var(--sw-color-text);
24
+ font-family: var(--sw-font-sans);
25
+ position: relative;
26
+ }
27
+
28
+ .sw-site-header-sticky {
29
+ position: sticky;
30
+ top: 0;
31
+ ${zIndex('topChrome')}
32
+ }
33
+
34
+ .sw-site-header-bar {
35
+ align-items: center;
36
+ box-sizing: border-box;
37
+ display: flex;
38
+ gap: var(--sw-space-5);
39
+ justify-content: space-between;
40
+ margin-inline: auto;
41
+ max-width: calc(var(--sw-container-max) + 2 * var(--sw-space-5));
42
+ min-height: var(--sw-site-header-height);
43
+ padding-inline: var(--sw-space-4);
44
+ }
45
+
46
+ .sw-site-header-brand {
47
+ align-items: center;
48
+ display: flex;
49
+ flex: none;
50
+ }
51
+
52
+ .sw-site-header-nav,
53
+ .sw-site-header-actions {
54
+ display: none;
55
+ }
56
+
57
+ .sw-site-header-compact {
58
+ align-items: center;
59
+ display: flex;
60
+ gap: var(--sw-space-1);
61
+ }
62
+
63
+ .sw-site-header-list {
64
+ list-style: none;
65
+ margin: 0;
66
+ padding: 0;
67
+ }
68
+
69
+ .sw-site-header-nav .sw-site-header-list {
70
+ display: flex;
71
+ gap: var(--sw-space-5);
72
+ }
73
+
74
+ [data-theme] .sw-site-header-nav .sw-site-header-link {
75
+ align-items: center;
76
+ color: var(--sw-color-text);
77
+ display: inline-flex;
78
+ font-size: ${label.fontSize}px;
79
+ font-weight: 400;
80
+ letter-spacing: ${label.letterSpacing}px;
81
+ line-height: ${label.lineHeight}px;
82
+ min-height: var(--sw-site-header-height);
83
+ text-decoration: none;
84
+ }
85
+
86
+ [data-theme] .sw-site-header-nav .sw-site-header-link:hover {
87
+ box-shadow: inset 0 -2px 0 var(--sw-color-border);
88
+ }
89
+
90
+ [data-theme] .sw-site-header-nav .sw-site-header-link[aria-current] {
91
+ box-shadow: inset 0 -2px 0 currentColor;
92
+ font-weight: ${label.fontWeight};
93
+ }
94
+
95
+ [data-theme] .sw-site-header-nav .sw-site-header-link:focus-visible {
96
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
97
+ }
98
+
99
+ .sw-site-header-actions {
100
+ align-items: center;
101
+ gap: var(--sw-space-4);
102
+ }
103
+
104
+ @media ${breakpointQuery('from', 'md')} {
105
+ .sw-site-header-bar { padding-inline: var(--sw-space-5); }
106
+ }
107
+
108
+ @media ${wide} {
109
+ .sw-site-header { --sw-site-header-height: ${wideHeight}; }
110
+ .sw-site-header-nav { display: block; }
111
+ .sw-site-header-actions { display: flex; }
112
+ .sw-site-header-compact { display: none; }
113
+ }`;
114
+ }
115
+ /*
116
+ * The menu is a native \`details\`: its \`summary\` is the button (the menu
117
+ * glyph and its name while closed, the close glyph and its name while
118
+ * open, the other pair hidden so only one is read), and its panel lies
119
+ * under the bar over the rest of the window, the destinations as large
120
+ * rows with hairlines and the menu's actions at the panel's foot. It
121
+ * scrolls on its own when the window is short.
122
+ */
123
+ function menuRules() {
124
+ return `.sw-site-header-menu > summary {
125
+ list-style: none;
126
+ }
127
+
128
+ .sw-site-header-menu > summary::-webkit-details-marker {
129
+ display: none;
130
+ }
131
+
132
+ .sw-site-header-menu-button {
133
+ align-items: center;
134
+ border-radius: var(--sw-radius-pill);
135
+ box-sizing: border-box;
136
+ color: var(--sw-color-text);
137
+ cursor: pointer;
138
+ display: inline-flex;
139
+ height: var(--sw-control-md-min-height);
140
+ justify-content: center;
141
+ width: var(--sw-control-md-min-height);
142
+ }
143
+
144
+ .sw-site-header-menu-button:hover {
145
+ background: ${quietFill};
146
+ }
147
+
148
+ .sw-site-header-menu-button:focus-visible {
149
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
150
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
151
+ }
152
+
153
+ .sw-site-header-menu-open,
154
+ .sw-site-header-menu-close,
155
+ .sw-site-header-glyph {
156
+ align-items: center;
157
+ display: inline-flex;
158
+ }
159
+
160
+ .sw-site-header-menu[open] .sw-site-header-menu-open,
161
+ .sw-site-header-menu:not([open]) .sw-site-header-menu-close {
162
+ display: none;
163
+ }
164
+
165
+ .sw-site-header-panel {
166
+ background: var(--sw-color-surface);
167
+ box-sizing: border-box;
168
+ display: flex;
169
+ flex-direction: column;
170
+ height: calc(100dvh - var(--sw-site-header-height) - 1px);
171
+ inset-inline: 0;
172
+ overflow-y: auto;
173
+ padding: var(--sw-space-2) var(--sw-space-4) var(--sw-space-6);
174
+ position: absolute;
175
+ top: calc(100% + 1px);
176
+ }
177
+
178
+ .sw-site-header-menu-nav .sw-site-header-list {
179
+ display: flex;
180
+ flex-direction: column;
181
+ }
182
+
183
+ [data-theme] .sw-site-header-menu-nav .sw-site-header-link {
184
+ align-items: center;
185
+ border-bottom: 1px solid var(--sw-color-border);
186
+ color: var(--sw-color-text);
187
+ display: flex;
188
+ font-size: ${title.fontSize}px;
189
+ font-weight: ${title.fontWeight};
190
+ letter-spacing: ${title.letterSpacing}px;
191
+ line-height: ${title.lineHeight}px;
192
+ min-height: ${compactHeight};
193
+ text-decoration: none;
194
+ }
195
+
196
+ [data-theme] .sw-site-header-menu-nav .sw-site-header-link[aria-current] {
197
+ color: var(--sw-color-accent);
198
+ }
199
+
200
+ [data-theme] .sw-site-header-menu-nav .sw-site-header-link:focus-visible {
201
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
202
+ }
203
+
204
+ .sw-site-header-menu-actions {
205
+ display: flex;
206
+ flex-direction: column;
207
+ gap: var(--sw-space-3);
208
+ margin-top: auto;
209
+ padding-top: var(--sw-space-5);
210
+ }`;
211
+ }
212
+ export function cssSiteHeaderClasses() {
213
+ return `${barRules()}
214
+
215
+ ${menuRules()}
216
+
217
+ /*
218
+ * Forced colours drop box shadows, so the current destination's bar is a
219
+ * thick underline there.
220
+ */
221
+ @media (forced-colors: active) {
222
+ .sw-site-header,
223
+ [data-theme] .sw-site-header-menu-nav .sw-site-header-link {
224
+ border-color: CanvasText;
225
+ }
226
+
227
+ [data-theme] .sw-site-header-link[aria-current] {
228
+ color: Highlight;
229
+ }
230
+
231
+ [data-theme] .sw-site-header-nav .sw-site-header-link[aria-current] {
232
+ text-decoration: underline;
233
+ text-decoration-thickness: 2px;
234
+ text-underline-offset: var(--sw-space-2);
235
+ }
236
+
237
+ .sw-site-header-menu-button {
238
+ border: 1px solid ButtonText;
239
+ }
240
+ }`;
241
+ }
242
+ export function siteHeaderClassCatalog() {
243
+ return [
244
+ 'sw-site-header',
245
+ 'sw-site-header-sticky',
246
+ 'sw-site-header-bar',
247
+ 'sw-site-header-brand',
248
+ 'sw-site-header-nav',
249
+ 'sw-site-header-actions',
250
+ 'sw-site-header-compact',
251
+ 'sw-site-header-list',
252
+ 'sw-site-header-link',
253
+ 'sw-site-header-menu',
254
+ 'sw-site-header-menu-button',
255
+ 'sw-site-header-menu-open',
256
+ 'sw-site-header-menu-close',
257
+ 'sw-site-header-glyph',
258
+ 'sw-site-header-panel',
259
+ 'sw-site-header-menu-nav',
260
+ 'sw-site-header-menu-actions',
261
+ ];
262
+ }
@@ -0,0 +1,10 @@
1
+ export declare const skeletonVariants: readonly ["lines", "rows"];
2
+ /** `lines` is bars of text in a column; `rows` is list rows with hairlines. */
3
+ export type SkeletonVariant = (typeof skeletonVariants)[number];
4
+ /** How many bars or rows a Skeleton draws. */
5
+ export declare const skeletonLineRange: {
6
+ readonly min: 1;
7
+ readonly max: 6;
8
+ };
9
+ export declare function cssSkeletonClasses(): string;
10
+ export declare function skeletonClassCatalog(): string[];
@@ -0,0 +1,110 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { quietFill } from './quiet-fill.js';
3
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
4
+ const body = typographyVariants.body;
5
+ export const skeletonVariants = ['lines', 'rows'];
6
+ /** How many bars or rows a Skeleton draws. */
7
+ export const skeletonLineRange = { min: 1, max: 6 };
8
+ /*
9
+ * The widths the bars take in turn, so a column reads as text with ragged
10
+ * ends rather than a grid; the last bar of `lines` is shorter still.
11
+ */
12
+ const barWidths = ['100%', '92%', '84%', '96%', '88%', '80%'];
13
+ function barWidthRules(parent) {
14
+ return barWidths
15
+ .map((width, index) => `${parent} > .sw-skeleton-bar:nth-of-type(${index + 1}) { width: ${width}; }`)
16
+ .join('\n');
17
+ }
18
+ /*
19
+ * Skeleton: grey placeholder bars in the quiet fill, rounded, a text
20
+ * line's height apart. `rows` draws each as a list row: a mark (space-6,
21
+ * the small radius) and a bar at the control height (44 px on a coarse
22
+ * pointer, as a pressable row there), hairlines between. The bars pulse
23
+ * between full and 60 % opacity over the travel duration, and stay still
24
+ * under reduced motion. Forced colours, which drop fills, draw each bar's
25
+ * edge in GrayText so the shape stays.
26
+ */
27
+ export function cssSkeletonClasses() {
28
+ return `.sw-skeleton {
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: var(--sw-space-3);
32
+ width: 100%;
33
+ }
34
+
35
+ .sw-skeleton-bar,
36
+ .sw-skeleton-mark {
37
+ animation: sw-skeleton-pulse calc(2 * var(--sw-motion-travel)) var(--sw-motion-travel-easing) infinite alternate;
38
+ background: ${quietFill};
39
+ border-radius: var(--sw-radius-sm);
40
+ box-sizing: border-box;
41
+ display: block;
42
+ flex: none;
43
+ }
44
+
45
+ .sw-skeleton-bar {
46
+ height: calc(${body.fontSize}px * 0.8);
47
+ }
48
+
49
+ ${barWidthRules('.sw-skeleton-lines')}
50
+ .sw-skeleton-lines > .sw-skeleton-bar:last-of-type:not(:first-of-type) { width: 60%; }
51
+
52
+ .sw-skeleton-rows {
53
+ gap: 0;
54
+ }
55
+
56
+ .sw-skeleton-row {
57
+ align-items: center;
58
+ box-sizing: border-box;
59
+ display: flex;
60
+ gap: var(--sw-space-3);
61
+ min-height: var(--sw-control-md-min-height);
62
+ padding-block: var(--sw-space-2);
63
+ }
64
+
65
+ .sw-skeleton-row + .sw-skeleton-row {
66
+ border-top: 1px solid var(--sw-color-border);
67
+ }
68
+
69
+ .sw-skeleton-row .sw-skeleton-bar {
70
+ flex: 0 1 60%;
71
+ }
72
+
73
+ .sw-skeleton-row:nth-of-type(even) .sw-skeleton-bar {
74
+ flex-basis: 45%;
75
+ }
76
+
77
+ .sw-skeleton-mark {
78
+ height: var(--sw-space-6);
79
+ width: var(--sw-space-6);
80
+ }
81
+
82
+ @keyframes sw-skeleton-pulse {
83
+ from { opacity: 1; }
84
+ to { opacity: 0.6; }
85
+ }
86
+
87
+ @media ${coarsePointerQuery} {
88
+ .sw-skeleton-row { min-height: ${touchTarget}; }
89
+ }
90
+
91
+ @media (prefers-reduced-motion: reduce) {
92
+ .sw-skeleton-bar,
93
+ .sw-skeleton-mark { animation: none; }
94
+ }
95
+
96
+ @media (forced-colors: active) {
97
+ .sw-skeleton-bar,
98
+ .sw-skeleton-mark { border: 1px solid GrayText; }
99
+ .sw-skeleton-row + .sw-skeleton-row { border-color: GrayText; }
100
+ }`;
101
+ }
102
+ export function skeletonClassCatalog() {
103
+ return [
104
+ 'sw-skeleton',
105
+ ...skeletonVariants.map((variant) => `sw-skeleton-${variant}`),
106
+ 'sw-skeleton-bar',
107
+ 'sw-skeleton-row',
108
+ 'sw-skeleton-mark',
109
+ ];
110
+ }
@@ -13,15 +13,15 @@
13
13
  * its popup can only paint as high as the surface does; lifting the surface
14
14
  * over the ActionBar keeps an open list tappable where the bar would cover
15
15
  * it, and over the next surface in the flow.
16
- * 5. `topChrome`: the sticky AppHeader or sticky Tabs strip (and a banded
17
- * Dialog's title band, inside its dialog), over a lifted
16
+ * 5. `topChrome`: the sticky AppHeader, sticky Tabs strip or AppShell page
17
+ * bar (and a banded Dialog's title band, inside its dialog), over a lifted
18
18
  * surface that scrolls up under it (the header is the page's way back, the
19
19
  * strip its way between sections, and a popup opens downward).
20
20
  * 6. `sheetGrabber`: a bottom sheet's grabber, inside its dialog, over the
21
21
  * banded title band that sticks under it.
22
22
  * 7. `popup`: a popup outside any lifted surface (Select list, ActionMenu
23
- * list, MultiSelect panel, Combobox list, Tooltip, the DateField and CalendarButton
24
- * calendar) over everything in the page.
23
+ * list, ContextSwitcher popup, MultiSelect panel, Combobox list, Tooltip,
24
+ * the DateField and CalendarButton calendar) over everything in the page.
25
25
  *
26
26
  * Dialog and Toast sit on the browser's top layer, above all of these.
27
27
  */
@@ -60,6 +60,9 @@ const openPopups = [
60
60
  // Combobox's list. Rendered only while open; on the top layer where the
61
61
  // popover API exists.
62
62
  '.sw-combobox-popup',
63
+ // ContextSwitcher's popup. Rendered only while open; on the top layer
64
+ // where the popover API exists.
65
+ '.sw-context-switcher-popup',
63
66
  '.sw-tooltip:not([hidden])',
64
67
  // DateField, CalendarButton and DateRangeButton share it. Rendered only
65
68
  // while open; on the top layer where the popover API exists.
@@ -2,6 +2,8 @@ import { cardVariants, darkTheme, lightTheme, typographyVariants, } from '@scale
2
2
  import { chartToneClassCatalog } from './chart-tones.js';
3
3
  import { actionBarClassCatalog } from './css-action-bar.js';
4
4
  import { actionMenuClassCatalog } from './css-action-menu.js';
5
+ import { commandPaletteClassCatalog } from './css-command-palette.js';
6
+ import { contextSwitcherClassCatalog } from './css-context-switcher.js';
5
7
  import { accordionClassCatalog } from './css-accordion.js';
6
8
  import { switchClassCatalog } from './css-switch.js';
7
9
  import { dateFieldClassCatalog } from './css-date-field.js';
@@ -10,11 +12,13 @@ import { dateRangeClassCatalog } from './css-date-range.js';
10
12
  import { fileFieldClassCatalog } from './css-file-field.js';
11
13
  import { articleClassCatalog } from './css-article.js';
12
14
  import { documentViewerClassCatalog } from './css-document-viewer.js';
15
+ import { fixtureClassCatalog } from './css-fixture.js';
13
16
  import { listClassCatalog } from './css-list.js';
14
17
  import { markdownClassCatalog } from './css-markdown.js';
15
18
  import { countPillClassCatalog } from './css-count-pill.js';
16
19
  import { checkboxClassCatalog } from './css-checkbox.js';
17
20
  import { radioGroupClassCatalog } from './css-radio-group.js';
21
+ import { skeletonClassCatalog } from './css-skeleton.js';
18
22
  import { spinnerClassCatalog } from './css-spinner.js';
19
23
  import { progressClassCatalog } from './css-progress.js';
20
24
  import { noticeClassCatalog } from './css-notice.js';
@@ -22,6 +26,9 @@ import { glyphTileClassCatalog } from './css-glyph-tile.js';
22
26
  import { tooltipClassCatalog } from './css-tooltip.js';
23
27
  import { infoTipClassCatalog } from './css-info-tip.js';
24
28
  import { separatorClassCatalog } from './css-separator.js';
29
+ import { bandClassCatalog } from './css-band.js';
30
+ import { siteFooterClassCatalog } from './css-site-footer.js';
31
+ import { siteHeaderClassCatalog } from './css-site-header.js';
25
32
  import { statTileClassCatalog } from './css-stat-tile.js';
26
33
  import { descriptionListClassCatalog } from './css-description-list.js';
27
34
  import { tabsClassCatalog } from './css-tabs.js';
@@ -39,6 +46,7 @@ import { boxBorderClassCatalog } from './css-box-border.js';
39
46
  import { boxLinkClassCatalog } from './css-box-link.js';
40
47
  import { cssComponentClasses } from './css-components.js';
41
48
  import { buttonClassCatalog } from './css-button.js';
49
+ import { buttonLinkClassCatalog } from './css-button-link.js';
42
50
  import { barChartClassCatalog } from './css-bar-chart.js';
43
51
  import { chartLegendClassCatalog } from './css-chart-legend.js';
44
52
  import { chartPlotClassCatalog } from './css-chart-plot.js';
@@ -81,6 +89,7 @@ export function utilityClassCatalog() {
81
89
  'sw-tabular',
82
90
  ...Object.keys(typographyVariants).map((name) => `sw-text-${name}`),
83
91
  ...buttonClassCatalog(),
92
+ ...buttonLinkClassCatalog(),
84
93
  ...dataClassCatalog(),
85
94
  ...segmentedClassCatalog(),
86
95
  ...tableClassCatalog(),
@@ -103,6 +112,8 @@ export function utilityClassCatalog() {
103
112
  ...accordionClassCatalog(),
104
113
  ...selectClassCatalog(),
105
114
  ...actionMenuClassCatalog(),
115
+ ...contextSwitcherClassCatalog(),
116
+ ...commandPaletteClassCatalog(),
106
117
  ...multiSelectClassCatalog(),
107
118
  ...comboboxClassCatalog(),
108
119
  ...actionBarClassCatalog(),
@@ -111,6 +122,7 @@ export function utilityClassCatalog() {
111
122
  ...calendarButtonClassCatalog(),
112
123
  ...dateRangeClassCatalog(),
113
124
  ...listClassCatalog(),
125
+ ...fixtureClassCatalog(),
114
126
  ...fileFieldClassCatalog(),
115
127
  ...articleClassCatalog(),
116
128
  ...documentViewerClassCatalog(),
@@ -119,12 +131,16 @@ export function utilityClassCatalog() {
119
131
  ...checkboxClassCatalog(),
120
132
  ...radioGroupClassCatalog(),
121
133
  ...spinnerClassCatalog(),
134
+ ...skeletonClassCatalog(),
122
135
  ...progressClassCatalog(),
123
136
  ...noticeClassCatalog(),
124
137
  ...glyphTileClassCatalog(),
125
138
  ...tooltipClassCatalog(),
126
139
  ...infoTipClassCatalog(),
127
140
  ...separatorClassCatalog(),
141
+ ...bandClassCatalog(),
142
+ ...siteFooterClassCatalog(),
143
+ ...siteHeaderClassCatalog(),
128
144
  ...statTileClassCatalog(),
129
145
  ...descriptionListClassCatalog(),
130
146
  ...tabsClassCatalog(),
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ export { listGroupVariants, listRowTitleAppearances, listRowTones, listRowValueT
16
16
  export { listTileColumnCounts, type ListGroupTileColumns, } from './css/css-list-tiles.js';
17
17
  export { Checkbox, type CheckboxProps } from './components/Checkbox.js';
18
18
  export { Spinner, type SpinnerProps, type SpinnerSize, } from './components/Spinner.js';
19
+ export { Skeleton, skeletonVariants, type SkeletonProps, type SkeletonVariant, } from './components/Skeleton.js';
19
20
  export { GlyphTile, glyphTileSizes, type GlyphTileHue, type GlyphTileProps, type GlyphTileSize, } from './components/GlyphTile.js';
20
21
  export { Notice, noticeTones, noticeVariants, type NoticeProps, type NoticeRole, type NoticeTone, type NoticeVariant, } from './components/Notice.js';
21
22
  export { Progress, progressTones, type ProgressProps, type ProgressTone, } from './components/Progress.js';
@@ -31,6 +32,7 @@ export { LineChart, type LineChartCurve, type LineChartMarker, type LineChartPer
31
32
  export { DataBar, type DataBarProps, type DataBarTone, dataBarTones, } from './components/DataBar.js';
32
33
  export { Pagination, type PaginationLabels, type PaginationProps, } from './components/Pagination.js';
33
34
  export { Button, type ButtonDisabledAppearance, type ButtonProps, type ButtonSize, type ButtonVariant, type ButtonWidth, } from './components/Button.js';
35
+ export { ButtonLink, type ButtonLinkProps } from './components/ButtonLink.js';
34
36
  export { buttonWidths } from './css/css-button-width.js';
35
37
  export { buttonDisabledAppearances, webButtonVariants, } from './css/css-button.js';
36
38
  export { Box, type BoxBorder, type BoxElement, type BoxLink, type BoxProps, } from './components/Box.js';
@@ -52,6 +54,11 @@ export { Split } from './components/Split.js';
52
54
  export type { SplitProps } from './components/Split.js';
53
55
  export { Nav, navAppearances, type NavAppearance, type NavProps, } from './components/Nav.js';
54
56
  export { NavCount, navCountTones, type NavCountProps, type NavCountTone, } from './components/NavCount.js';
57
+ export { FixtureGroup, type FixtureGroupProps, } from './components/FixtureGroup.js';
58
+ export { FixtureRow, type FixtureRowProps, type FixtureSide, type FixtureStatus, } from './components/FixtureRow.js';
59
+ export { CommandPalette, commandPaletteShortcuts, type CommandPaletteGroup, type CommandPaletteItem, type CommandPaletteProps, type CommandPaletteShortcut, } from './components/CommandPalette.js';
60
+ export { CommandPaletteTrigger, type CommandPaletteTriggerProps, } from './components/CommandPaletteTrigger.js';
61
+ export { ContextSwitcher, contextSwitcherTriggerAppearances, contextSwitcherWidths, type ContextSwitcherAction, type ContextSwitcherBadge, type ContextSwitcherFilter, type ContextSwitcherGroup, type ContextSwitcherOption, type ContextSwitcherProps, type ContextSwitcherTriggerAppearance, type ContextSwitcherWidth, } from './components/ContextSwitcher.js';
55
62
  export { AppShell, type AppShellLabels, type AppShellNavGroup, type AppShellNavItem, type AppShellProps, } from './components/AppShell.js';
56
63
  export { SectionNav, sectionNavAppearances, type SectionNavAppearance, type SectionNavItem, type SectionNavProps, } from './components/SectionNav.js';
57
64
  export { SegmentedControl, type SegmentedControlProps, type SegmentedControlVariant, segmentedSizes, type SegmentedSize, type SegmentedItem, } from './components/SegmentedControl.js';
@@ -68,6 +75,12 @@ export { utilityClassCatalog } from './css/stylesheet.js';
68
75
  export type { VisibilityProps } from './visibility-classes.js';
69
76
  export type { Breakpoint, BreakpointDirection, VisibilityBreakpoint, } from './css/breakpoints.js';
70
77
  export { breakpointQuery } from './css/breakpoints.js';
78
+ export { Band, bandElements, bandTones, bandWidths, } from './components/Band.js';
79
+ export type { BandElement, BandProps, BandTone, BandWidth, } from './components/Band.js';
80
+ export { SiteHeader } from './components/SiteHeader.js';
81
+ export type { SiteHeaderItem, SiteHeaderLabels, SiteHeaderProps, } from './components/SiteHeader.js';
82
+ export { SiteFooter } from './components/SiteFooter.js';
83
+ export type { SiteFooterGroup, SiteFooterLink, SiteFooterProps, } from './components/SiteFooter.js';
71
84
  export { Separator } from './components/Separator.js';
72
85
  export type { SeparatorOrientation, SeparatorProps, } from './components/Separator.js';
73
86
  export { DescriptionItem, DescriptionList, descriptionListLayouts, } from './components/DescriptionList.js';