@scalewing/react 1.46.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.
- package/CHANGELOG.md +74 -0
- package/README.md +228 -4
- package/dist/app-shell.test-support.d.ts +5 -0
- package/dist/app-shell.test-support.js +45 -0
- package/dist/command-palette.test-support.d.ts +21 -0
- package/dist/command-palette.test-support.js +55 -0
- package/dist/components/AppHeader.d.ts +3 -0
- package/dist/components/AppShell.d.ts +13 -0
- package/dist/components/AppShell.js +65 -0
- package/dist/components/Band.d.ts +30 -0
- package/dist/components/Band.js +23 -0
- package/dist/components/ButtonLink.d.ts +33 -0
- package/dist/components/ButtonLink.js +27 -0
- package/dist/components/CommandPalette.d.ts +12 -0
- package/dist/components/CommandPalette.js +143 -0
- package/dist/components/CommandPaletteTrigger.d.ts +9 -0
- package/dist/components/CommandPaletteTrigger.js +16 -0
- package/dist/components/ContextSwitcher.d.ts +12 -0
- package/dist/components/ContextSwitcher.js +62 -0
- package/dist/components/FixtureGroup.d.ts +9 -0
- package/dist/components/FixtureGroup.js +16 -0
- package/dist/components/FixtureRow.d.ts +10 -0
- package/dist/components/FixtureRow.js +46 -0
- package/dist/components/GlyphTile.d.ts +9 -3
- package/dist/components/GlyphTile.js +15 -4
- package/dist/components/NavCount.d.ts +12 -5
- package/dist/components/NavCount.js +9 -4
- package/dist/components/SectionNav.d.ts +2 -1
- package/dist/components/SectionNav.js +3 -19
- package/dist/components/SiteFooter.d.ts +12 -0
- package/dist/components/SiteFooter.js +23 -0
- package/dist/components/SiteHeader.d.ts +14 -0
- package/dist/components/SiteHeader.js +24 -0
- package/dist/components/Skeleton.d.ts +22 -0
- package/dist/components/Skeleton.js +30 -0
- package/dist/components/app-shell/AppShellBar.d.ts +23 -0
- package/dist/components/app-shell/AppShellBar.js +25 -0
- package/dist/components/app-shell/AppShellDrawer.d.ts +24 -0
- package/dist/components/app-shell/AppShellDrawer.js +17 -0
- package/dist/components/app-shell/AppShellLink.d.ts +14 -0
- package/dist/components/app-shell/AppShellLink.js +27 -0
- package/dist/components/app-shell/AppShellNav.d.ts +15 -0
- package/dist/components/app-shell/AppShellNav.js +29 -0
- package/dist/components/app-shell/AppShellSidebar.d.ts +18 -0
- package/dist/components/app-shell/AppShellSidebar.js +18 -0
- package/dist/components/app-shell/types.d.ts +122 -0
- package/dist/components/app-shell/types.js +1 -0
- package/dist/components/app-shell/validate.d.ts +7 -0
- package/dist/components/app-shell/validate.js +70 -0
- package/dist/components/command-palette/PaletteList.d.ts +20 -0
- package/dist/components/command-palette/PaletteList.js +21 -0
- package/dist/components/command-palette/shortcut.d.ts +19 -0
- package/dist/components/command-palette/shortcut.js +75 -0
- package/dist/components/command-palette/types.d.ts +72 -0
- package/dist/components/command-palette/types.js +2 -0
- package/dist/components/command-palette/validate.d.ts +10 -0
- package/dist/components/command-palette/validate.js +50 -0
- package/dist/components/context-switcher/SwitcherOption.d.ts +17 -0
- package/dist/components/context-switcher/SwitcherOption.js +20 -0
- package/dist/components/context-switcher/SwitcherPopup.d.ts +31 -0
- package/dist/components/context-switcher/SwitcherPopup.js +111 -0
- package/dist/components/context-switcher/options.d.ts +33 -0
- package/dist/components/context-switcher/options.js +60 -0
- package/dist/components/context-switcher/types.d.ts +96 -0
- package/dist/components/context-switcher/types.js +1 -0
- package/dist/components/context-switcher/use-switcher-list.d.ts +29 -0
- package/dist/components/context-switcher/use-switcher-list.js +93 -0
- package/dist/components/context-switcher/validate.d.ts +8 -0
- package/dist/components/context-switcher/validate.js +94 -0
- package/dist/components/fixture/fixture-list-context.d.ts +4 -0
- package/dist/components/fixture/fixture-list-context.js +9 -0
- package/dist/components/fixture/types.d.ts +50 -0
- package/dist/components/fixture/types.js +1 -0
- package/dist/components/fixture/validate.d.ts +8 -0
- package/dist/components/fixture/validate.js +36 -0
- package/dist/components/nav-items.d.ts +21 -0
- package/dist/components/nav-items.js +26 -0
- package/dist/components/site-footer/types.d.ts +31 -0
- package/dist/components/site-footer/types.js +1 -0
- package/dist/components/site-footer/validate.d.ts +7 -0
- package/dist/components/site-footer/validate.js +22 -0
- package/dist/components/site-header/SiteHeaderLinks.d.ts +7 -0
- package/dist/components/site-header/SiteHeaderLinks.js +7 -0
- package/dist/components/site-header/types.d.ts +38 -0
- package/dist/components/site-header/types.js +1 -0
- package/dist/components/site-header/use-menu-dismiss.d.ts +8 -0
- package/dist/components/site-header/use-menu-dismiss.js +62 -0
- package/dist/components/site-header/validate.d.ts +7 -0
- package/dist/components/site-header/validate.js +17 -0
- package/dist/components/use-element-height.d.ts +10 -0
- package/dist/components/use-element-height.js +35 -0
- package/dist/context-switcher.test-support.d.ts +9 -0
- package/dist/context-switcher.test-support.js +41 -0
- package/dist/css/css-app-shell-drawer.d.ts +2 -0
- package/dist/css/css-app-shell-drawer.js +91 -0
- package/dist/css/css-app-shell-nav.d.ts +3 -0
- package/dist/css/css-app-shell-nav.js +167 -0
- package/dist/css/css-app-shell-page-bar.d.ts +3 -0
- package/dist/css/css-app-shell-page-bar.js +81 -0
- package/dist/css/css-app-shell.d.ts +4 -0
- package/dist/css/css-app-shell.js +155 -0
- package/dist/css/css-band.d.ts +10 -0
- package/dist/css/css-band.js +101 -0
- package/dist/css/css-button-link.d.ts +3 -0
- package/dist/css/css-button-link.js +48 -0
- package/dist/css/css-command-palette-key.d.ts +1 -0
- package/dist/css/css-command-palette-key.js +33 -0
- package/dist/css/css-command-palette-trigger.d.ts +2 -0
- package/dist/css/css-command-palette-trigger.js +88 -0
- package/dist/css/css-command-palette.d.ts +2 -0
- package/dist/css/css-command-palette.js +209 -0
- package/dist/css/css-components.js +27 -0
- package/dist/css/css-context-switcher-trigger.d.ts +15 -0
- package/dist/css/css-context-switcher-trigger.js +175 -0
- package/dist/css/css-context-switcher.d.ts +4 -0
- package/dist/css/css-context-switcher.js +219 -0
- package/dist/css/css-count-pill.d.ts +7 -0
- package/dist/css/css-count-pill.js +50 -12
- package/dist/css/css-fixture.d.ts +5 -0
- package/dist/css/css-fixture.js +260 -0
- package/dist/css/css-glyph-tile.d.ts +2 -0
- package/dist/css/css-glyph-tile.js +29 -1
- package/dist/css/css-site-footer.d.ts +2 -0
- package/dist/css/css-site-footer.js +152 -0
- package/dist/css/css-site-header.d.ts +2 -0
- package/dist/css/css-site-header.js +262 -0
- package/dist/css/css-skeleton.d.ts +10 -0
- package/dist/css/css-skeleton.js +110 -0
- package/dist/css/stacking.js +7 -4
- package/dist/css/stylesheet.js +18 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +11 -0
- package/dist/styles.css +2122 -512
- package/package.json +2 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { breakpointQuery } from './breakpoints.js';
|
|
2
|
+
import { appShellDrawerClassCatalog, cssAppShellDrawerClasses, } from './css-app-shell-drawer.js';
|
|
3
|
+
import { appShellNavClassCatalog, cssAppShellNavClasses, cssAppShellRailNavRules, } from './css-app-shell-nav.js';
|
|
4
|
+
import { appShellPageBarClassCatalog, cssAppShellPageBarClasses, } from './css-app-shell-page-bar.js';
|
|
5
|
+
import { iconButtonRules } from './css-icon-button.js';
|
|
6
|
+
/*
|
|
7
|
+
* The width from which AppShell's sidebar is full rather than a rail: an
|
|
8
|
+
* internal media query of the component, not a public breakpoint (the
|
|
9
|
+
* `md` and `lg` tokens stay the only ones products use).
|
|
10
|
+
*/
|
|
11
|
+
const wideShellWidth = 75;
|
|
12
|
+
/** From this width up AppShell draws its full sidebar; script reads it too. */
|
|
13
|
+
export const appShellWideQuery = `(min-width: ${wideShellWidth}rem)`;
|
|
14
|
+
/* The full sidebar's width, a named geometry of this frame. */
|
|
15
|
+
const sidebarWidth = '15rem';
|
|
16
|
+
/* The rail: the 44 px target with a space-3 gutter each side, and its hairline. */
|
|
17
|
+
const railWidth = 'calc(var(--sw-control-md-min-height) + 2 * var(--sw-space-3) + 1px)';
|
|
18
|
+
const fromLg = breakpointQuery('from', 'lg');
|
|
19
|
+
/*
|
|
20
|
+
* Below `lg`: the page under a sticky top bar (an AppHeader `bar`: the menu
|
|
21
|
+
* button, the title, the end controls), no sidebar. The frame never makes
|
|
22
|
+
* `main` a scroll box: the page scrolls with the document.
|
|
23
|
+
*/
|
|
24
|
+
function barRules() {
|
|
25
|
+
return `.sw-app-shell {
|
|
26
|
+
min-height: 100vh;
|
|
27
|
+
min-height: 100dvh;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.sw-app-shell-bar {
|
|
31
|
+
align-items: center;
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: var(--sw-space-2);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.sw-app-shell-bar-title {
|
|
37
|
+
flex: 1 1 auto;
|
|
38
|
+
min-width: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.sw-app-shell-bar-end {
|
|
42
|
+
align-items: center;
|
|
43
|
+
display: flex;
|
|
44
|
+
flex: none;
|
|
45
|
+
gap: var(--sw-space-2);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
${iconButtonRules('sw-app-shell-menu')}
|
|
49
|
+
|
|
50
|
+
.sw-app-shell-main {
|
|
51
|
+
display: block;
|
|
52
|
+
min-width: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.sw-app-shell-sidebar,
|
|
56
|
+
.sw-app-shell-sidebar .sw-app-shell-rail-slot {
|
|
57
|
+
display: none;
|
|
58
|
+
}`;
|
|
59
|
+
}
|
|
60
|
+
/*
|
|
61
|
+
* From \`lg\`: no bar; the sidebar is a column of the frame's grid, sticky at
|
|
62
|
+
* the top at the viewport's height on the solid surface with a hairline at
|
|
63
|
+
* its inline end. Its header and footer keep their height and only the nav
|
|
64
|
+
* between them scrolls, when it is taller than the window. A rail to 75rem,
|
|
65
|
+
* full from there.
|
|
66
|
+
*/
|
|
67
|
+
function sidebarRules() {
|
|
68
|
+
return `@media ${fromLg} {
|
|
69
|
+
.sw-app-shell {
|
|
70
|
+
align-items: start;
|
|
71
|
+
display: grid;
|
|
72
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.sw-app-shell-bar {
|
|
76
|
+
display: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.sw-app-shell-sidebar {
|
|
80
|
+
background: var(--sw-color-surface);
|
|
81
|
+
border-inline-end: 1px solid var(--sw-color-border);
|
|
82
|
+
box-sizing: border-box;
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
height: 100vh;
|
|
86
|
+
height: 100dvh;
|
|
87
|
+
position: sticky;
|
|
88
|
+
top: 0;
|
|
89
|
+
width: ${railWidth};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@media not all and ${appShellWideQuery} {
|
|
93
|
+
.sw-app-shell-sidebar .sw-app-shell-slot {
|
|
94
|
+
display: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.sw-app-shell-sidebar .sw-app-shell-rail-slot {
|
|
98
|
+
display: block;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
${cssAppShellRailNavRules(' ')}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@media ${appShellWideQuery} {
|
|
106
|
+
.sw-app-shell-sidebar {
|
|
107
|
+
width: ${sidebarWidth};
|
|
108
|
+
}
|
|
109
|
+
}`;
|
|
110
|
+
}
|
|
111
|
+
/*
|
|
112
|
+
* AppShell: a workspace frame with a pinned side navigation (see
|
|
113
|
+
* components/AppShell.tsx). The links, groups and the rail's nav are
|
|
114
|
+
* `css-app-shell-nav.ts`; the page bar and the menu's count are
|
|
115
|
+
* `css-app-shell-page-bar.ts`; the menu drawer is `css-app-shell-drawer.ts`.
|
|
116
|
+
*/
|
|
117
|
+
export function cssAppShellClasses() {
|
|
118
|
+
return `${barRules()}
|
|
119
|
+
|
|
120
|
+
.sw-app-shell-header,
|
|
121
|
+
.sw-app-shell-footer {
|
|
122
|
+
flex: none;
|
|
123
|
+
padding: var(--sw-space-3);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.sw-app-shell-footer {
|
|
127
|
+
border-top: 1px solid var(--sw-color-border);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
${cssAppShellNavClasses()}
|
|
131
|
+
|
|
132
|
+
${cssAppShellPageBarClasses()}
|
|
133
|
+
|
|
134
|
+
${sidebarRules()}
|
|
135
|
+
|
|
136
|
+
${cssAppShellDrawerClasses()}`;
|
|
137
|
+
}
|
|
138
|
+
export function appShellClassCatalog() {
|
|
139
|
+
return [
|
|
140
|
+
'sw-app-shell',
|
|
141
|
+
'sw-app-shell-bar',
|
|
142
|
+
'sw-app-shell-menu',
|
|
143
|
+
'sw-app-shell-bar-title',
|
|
144
|
+
'sw-app-shell-bar-end',
|
|
145
|
+
'sw-app-shell-main',
|
|
146
|
+
'sw-app-shell-sidebar',
|
|
147
|
+
'sw-app-shell-slot',
|
|
148
|
+
'sw-app-shell-rail-slot',
|
|
149
|
+
'sw-app-shell-header',
|
|
150
|
+
'sw-app-shell-footer',
|
|
151
|
+
...appShellNavClassCatalog(),
|
|
152
|
+
...appShellPageBarClassCatalog(),
|
|
153
|
+
...appShellDrawerClassCatalog(),
|
|
154
|
+
];
|
|
155
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A band's fill: the page's surface, the quiet subtle fill, or the dark inverse band. */
|
|
2
|
+
export declare const bandTones: readonly ["surface", "subtle", "inverse"];
|
|
3
|
+
export type BandTone = (typeof bandTones)[number];
|
|
4
|
+
/** How wide a band's content column runs: the page container or the reading width. */
|
|
5
|
+
export declare const bandWidths: readonly ["container", "reading"];
|
|
6
|
+
export type BandWidth = (typeof bandWidths)[number];
|
|
7
|
+
export declare function bandToneClass(tone: BandTone): string;
|
|
8
|
+
export declare function bandWidthClass(width: BandWidth): string;
|
|
9
|
+
export declare function cssBandClasses(): string;
|
|
10
|
+
export declare function bandClassCatalog(): string[];
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { breakpointQuery } from './breakpoints.js';
|
|
2
|
+
/** A band's fill: the page's surface, the quiet subtle fill, or the dark inverse band. */
|
|
3
|
+
export const bandTones = ['surface', 'subtle', 'inverse'];
|
|
4
|
+
/** How wide a band's content column runs: the page container or the reading width. */
|
|
5
|
+
export const bandWidths = ['container', 'reading'];
|
|
6
|
+
export function bandToneClass(tone) {
|
|
7
|
+
return `sw-band-${tone}`;
|
|
8
|
+
}
|
|
9
|
+
export function bandWidthClass(width) {
|
|
10
|
+
return `sw-band-inner-${width}`;
|
|
11
|
+
}
|
|
12
|
+
/*
|
|
13
|
+
* The inverse band re-scopes the theme's colours for everything inside it:
|
|
14
|
+
* words, muted words, hairlines, the quiet subtle fill, links and the
|
|
15
|
+
* accent (a primary button, the focus ring) read on the dark fill, so
|
|
16
|
+
* Text, links and Buttons need no band-aware variant. Every plain link,
|
|
17
|
+
* a quiet one included, takes the band's words with a half-strength
|
|
18
|
+
* underline, since its colour is the words' own (WCAG 1.4.1). The muted
|
|
19
|
+
* words, hairline and subtle fill are mixes of the two band colours, so a
|
|
20
|
+
* brand overlay of `inverse` moves them too; the muted mix keeps 7:1 on
|
|
21
|
+
* the default bands and on a navy one.
|
|
22
|
+
*/
|
|
23
|
+
function inverseScope() {
|
|
24
|
+
return `.sw-band-inverse {
|
|
25
|
+
--sw-color-background: var(--sw-color-inverse);
|
|
26
|
+
--sw-color-surface: var(--sw-color-inverse);
|
|
27
|
+
--sw-color-text: var(--sw-color-onInverse);
|
|
28
|
+
--sw-color-muted: color-mix(in srgb, var(--sw-color-onInverse) 78%, var(--sw-color-inverse));
|
|
29
|
+
--sw-color-border: color-mix(in srgb, var(--sw-color-onInverse) 18%, var(--sw-color-inverse));
|
|
30
|
+
--sw-color-subtle: color-mix(in srgb, var(--sw-color-onInverse) 10%, var(--sw-color-inverse));
|
|
31
|
+
--sw-color-accent: var(--sw-color-onInverse);
|
|
32
|
+
--sw-color-onAccent: var(--sw-color-inverse);
|
|
33
|
+
background: var(--sw-color-inverse);
|
|
34
|
+
color: var(--sw-color-onInverse);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-theme] .sw-band-inverse a:not(.sw-button) {
|
|
38
|
+
color: var(--sw-color-onInverse);
|
|
39
|
+
text-decoration: underline;
|
|
40
|
+
text-decoration-color: color-mix(in srgb, currentColor 50%, transparent);
|
|
41
|
+
text-underline-offset: 0.2em;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
[data-theme] .sw-band-inverse a:not(.sw-button):hover {
|
|
45
|
+
text-decoration-color: currentColor;
|
|
46
|
+
}`;
|
|
47
|
+
}
|
|
48
|
+
/*
|
|
49
|
+
* The surface band is the cards' fill (white in light, a step above the
|
|
50
|
+
* black page in dark), so it stands apart from a grouped or dark page; the
|
|
51
|
+
* subtle band is the quiet grey.
|
|
52
|
+
*
|
|
53
|
+
* A band is a full-width stretch of the page in one fill with its content
|
|
54
|
+
* in a centred column, the page gutter at its sides (the 16 px phone
|
|
55
|
+
* gutter, 24 px from md).
|
|
56
|
+
*/
|
|
57
|
+
export function cssBandClasses() {
|
|
58
|
+
return `.sw-band {
|
|
59
|
+
box-sizing: border-box;
|
|
60
|
+
width: 100%;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.sw-band-surface {
|
|
64
|
+
background: var(--sw-color-surface);
|
|
65
|
+
color: var(--sw-color-text);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.sw-band-subtle {
|
|
69
|
+
background: var(--sw-color-subtle);
|
|
70
|
+
color: var(--sw-color-text);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
${inverseScope()}
|
|
74
|
+
|
|
75
|
+
.sw-band-inner {
|
|
76
|
+
box-sizing: border-box;
|
|
77
|
+
margin-inline: auto;
|
|
78
|
+
padding-inline: var(--sw-space-4);
|
|
79
|
+
width: 100%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.${bandWidthClass('container')} { max-width: calc(var(--sw-container-max) + 2 * var(--sw-space-5)); }
|
|
83
|
+
|
|
84
|
+
.${bandWidthClass('reading')} { max-width: calc(var(--sw-reading-max) + 2 * var(--sw-space-5)); }
|
|
85
|
+
|
|
86
|
+
@media ${breakpointQuery('from', 'md')} {
|
|
87
|
+
.sw-band-inner { padding-inline: var(--sw-space-5); }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@media (forced-colors: active) {
|
|
91
|
+
.sw-band-inverse { border-block: 1px solid CanvasText; }
|
|
92
|
+
}`;
|
|
93
|
+
}
|
|
94
|
+
export function bandClassCatalog() {
|
|
95
|
+
return [
|
|
96
|
+
'sw-band',
|
|
97
|
+
'sw-band-inner',
|
|
98
|
+
...bandTones.map(bandToneClass),
|
|
99
|
+
...bandWidths.map(bandWidthClass),
|
|
100
|
+
];
|
|
101
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { pressedRingGap, pressedRingWidth, webButtonVariants, } from './css-button.js';
|
|
2
|
+
/*
|
|
3
|
+
* A link drawn as a Button (`ButtonLink`) wears the Button's own classes.
|
|
4
|
+
* The canvas paints every link in the accent, with no underline, and in the
|
|
5
|
+
* text colour on hover, from `[data-theme] a`; these rules restore each
|
|
6
|
+
* variant's words over that, hover included, at one class more. A link
|
|
7
|
+
* that is the current page (`aria-current="page"`, such as a site's
|
|
8
|
+
* Contact us on Contact) takes the pressed button's accent ring past a gap
|
|
9
|
+
* in the page background, so the mark keeps the accent's contrast whatever
|
|
10
|
+
* the fill; forced colours drop box shadows, so there the same ring is a
|
|
11
|
+
* Highlight border on an out-of-flow ::after, as a pressed Button's is.
|
|
12
|
+
*/
|
|
13
|
+
const variantWords = {
|
|
14
|
+
primary: 'var(--sw-color-onAccent)',
|
|
15
|
+
secondary: 'var(--sw-color-onSecondary)',
|
|
16
|
+
tertiary: 'var(--sw-color-onTertiary)',
|
|
17
|
+
ghost: 'var(--sw-color-accent)',
|
|
18
|
+
danger: 'var(--sw-color-onDanger)',
|
|
19
|
+
neutral: 'var(--sw-color-text)',
|
|
20
|
+
};
|
|
21
|
+
export const buttonLinkClass = 'sw-button-link';
|
|
22
|
+
export function cssButtonLinkClasses() {
|
|
23
|
+
const words = webButtonVariants
|
|
24
|
+
.map((variant) => `[data-theme] a.${buttonLinkClass}.sw-button-${variant},\n[data-theme] a.${buttonLinkClass}.sw-button-${variant}:hover { color: ${variantWords[variant]}; }`)
|
|
25
|
+
.join('\n');
|
|
26
|
+
return `[data-theme] a.${buttonLinkClass} {
|
|
27
|
+
text-decoration: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
${words}
|
|
31
|
+
|
|
32
|
+
[data-theme] a.${buttonLinkClass}[aria-current='page'] {
|
|
33
|
+
box-shadow: 0 0 0 ${pressedRingGap}px var(--sw-color-background), 0 0 0 ${pressedRingWidth}px var(--sw-color-accent);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-theme] a.${buttonLinkClass}[aria-current='page']:focus-visible {
|
|
37
|
+
outline-offset: calc(var(--sw-focus-ring-offset) + ${pressedRingWidth}px);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media (forced-colors: active) {
|
|
41
|
+
[data-theme] a.${buttonLinkClass} { color: LinkText; }
|
|
42
|
+
[data-theme] a.${buttonLinkClass}[aria-current='page'] { border-color: Highlight; position: relative; }
|
|
43
|
+
[data-theme] a.${buttonLinkClass}[aria-current='page']::after { border: ${pressedRingWidth - pressedRingGap}px solid Highlight; border-radius: inherit; content: ''; inset: calc(-1px - ${pressedRingWidth}px); pointer-events: none; position: absolute; }
|
|
44
|
+
}`;
|
|
45
|
+
}
|
|
46
|
+
export function buttonLinkClassCatalog() {
|
|
47
|
+
return [buttonLinkClass];
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cssCommandPaletteKeyClasses(): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { typographyVariants } from '@scalewing/tokens';
|
|
2
|
+
const caption = typographyVariants.caption;
|
|
3
|
+
/*
|
|
4
|
+
* A key cap: a key's name or glyph ("⌘K", "↵") in a small rounded hairline
|
|
5
|
+
* box in the muted colour, caption size at weight 500 with tabular figures.
|
|
6
|
+
* CommandPaletteTrigger's shortcut hint and the palette's Enter hint share
|
|
7
|
+
* it. Forced colours keep its edge.
|
|
8
|
+
*/
|
|
9
|
+
export function cssCommandPaletteKeyClasses() {
|
|
10
|
+
return `.sw-command-palette-key {
|
|
11
|
+
align-items: center;
|
|
12
|
+
border: 1px solid var(--sw-color-border);
|
|
13
|
+
border-radius: var(--sw-radius-sm);
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
color: var(--sw-color-muted);
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
flex: none;
|
|
18
|
+
font-family: var(--sw-font-sans);
|
|
19
|
+
font-size: ${caption.fontSize}px;
|
|
20
|
+
font-variant-numeric: tabular-nums;
|
|
21
|
+
font-weight: 500;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
letter-spacing: ${caption.letterSpacing}px;
|
|
24
|
+
line-height: ${caption.lineHeight}px;
|
|
25
|
+
min-width: var(--sw-space-5);
|
|
26
|
+
padding-inline: var(--sw-space-1);
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@media (forced-colors: active) {
|
|
31
|
+
.sw-command-palette-key { border-color: CanvasText; color: CanvasText; }
|
|
32
|
+
}`;
|
|
33
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { typographyVariants } from '@scalewing/tokens';
|
|
2
|
+
import { quietFill } from './quiet-fill.js';
|
|
3
|
+
import { coarsePointerQuery, touchTarget } from './touch-target.js';
|
|
4
|
+
const label = typographyVariants.label;
|
|
5
|
+
/* The full trigger's least width, a named geometry of this control. */
|
|
6
|
+
const triggerMinWidth = '12rem';
|
|
7
|
+
const trigger = '.sw-command-palette-trigger';
|
|
8
|
+
const compact = `${trigger}.sw-command-palette-trigger-compact`;
|
|
9
|
+
/*
|
|
10
|
+
* The trigger: a search-looking pill (the surface inside the border
|
|
11
|
+
* hairline, fully rounded) holding the glyph, the placeholder words in the
|
|
12
|
+
* muted colour taking the free space, and the key hint at the end; the sm
|
|
13
|
+
* control height, the 44 px target on a coarse pointer. `compact` is the
|
|
14
|
+
* glyph alone in a round quiet button. Hover takes the quiet fill.
|
|
15
|
+
*/
|
|
16
|
+
export function cssCommandPaletteTriggerClasses() {
|
|
17
|
+
return `${trigger} {
|
|
18
|
+
align-items: center;
|
|
19
|
+
appearance: none;
|
|
20
|
+
background: var(--sw-color-surface);
|
|
21
|
+
border: 1px solid var(--sw-color-border);
|
|
22
|
+
border-radius: var(--sw-radius-pill);
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
color: var(--sw-color-muted);
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
font-family: var(--sw-font-sans);
|
|
28
|
+
font-size: ${label.fontSize}px;
|
|
29
|
+
font-weight: 400;
|
|
30
|
+
gap: var(--sw-space-2);
|
|
31
|
+
letter-spacing: ${label.letterSpacing}px;
|
|
32
|
+
line-height: ${label.lineHeight}px;
|
|
33
|
+
margin: 0;
|
|
34
|
+
max-width: 100%;
|
|
35
|
+
min-height: var(--sw-control-sm-min-height);
|
|
36
|
+
min-width: min(${triggerMinWidth}, 100%);
|
|
37
|
+
padding-inline: var(--sw-space-3) var(--sw-space-2);
|
|
38
|
+
text-align: start;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.sw-command-palette-trigger-icon {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
flex: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sw-command-palette-trigger-text {
|
|
47
|
+
flex: 1 1 auto;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
text-overflow: ellipsis;
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (hover: hover) {
|
|
55
|
+
${trigger}:hover { background: ${quietFill}; color: var(--sw-color-text); }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
${trigger}:focus-visible {
|
|
59
|
+
outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
|
|
60
|
+
outline-offset: var(--sw-focus-ring-offset);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
${compact} {
|
|
64
|
+
background: transparent;
|
|
65
|
+
border-color: transparent;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
min-width: var(--sw-control-sm-min-height);
|
|
68
|
+
padding: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@media ${coarsePointerQuery} {
|
|
72
|
+
${trigger} { min-height: ${touchTarget}; }
|
|
73
|
+
${compact} { min-width: ${touchTarget}; }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@media (forced-colors: active) {
|
|
77
|
+
${trigger} { color: ButtonText; }
|
|
78
|
+
}`;
|
|
79
|
+
}
|
|
80
|
+
export function commandPaletteTriggerClassCatalog() {
|
|
81
|
+
return [
|
|
82
|
+
'sw-command-palette-trigger',
|
|
83
|
+
'sw-command-palette-trigger-compact',
|
|
84
|
+
'sw-command-palette-trigger-icon',
|
|
85
|
+
'sw-command-palette-trigger-text',
|
|
86
|
+
'sw-command-palette-key',
|
|
87
|
+
];
|
|
88
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { typographyVariants } from '@scalewing/tokens';
|
|
2
|
+
import { breakpointQuery } from './breakpoints.js';
|
|
3
|
+
import { cssCommandPaletteKeyClasses } from './css-command-palette-key.js';
|
|
4
|
+
import { commandPaletteTriggerClassCatalog, cssCommandPaletteTriggerClasses, } from './css-command-palette-trigger.js';
|
|
5
|
+
import { quietFill } from './quiet-fill.js';
|
|
6
|
+
import { coarsePointerQuery, touchTarget } from './touch-target.js';
|
|
7
|
+
const body = typographyVariants.body;
|
|
8
|
+
const caption = typographyVariants.caption;
|
|
9
|
+
const label = typographyVariants.label;
|
|
10
|
+
/* The palette's width, a named geometry of this control. */
|
|
11
|
+
const paletteWidth = '40rem';
|
|
12
|
+
/* Its distance from the window's top: a tenth of the height, at least space-2. */
|
|
13
|
+
const paletteTop = 'max(var(--sw-space-2), 10vh)';
|
|
14
|
+
const fromMd = breakpointQuery('from', 'md');
|
|
15
|
+
/*
|
|
16
|
+
* The palette: a modal dialog on the top layer near the window's top (not
|
|
17
|
+
* centred, so results grow downward), glass like Dialog, 40rem wide (the
|
|
18
|
+
* window less space-2 each side on a phone). Its body is a column: the
|
|
19
|
+
* field, the listbox (the only part that scrolls), the message, the hint.
|
|
20
|
+
*/
|
|
21
|
+
function frameRules() {
|
|
22
|
+
return `.sw-command-palette {
|
|
23
|
+
background: var(--sw-glass-fill);
|
|
24
|
+
border: 1px solid var(--sw-glass-border);
|
|
25
|
+
border-radius: var(--sw-radius-lg);
|
|
26
|
+
box-shadow: var(--sw-elevation-sm), inset 0 1px 0 var(--sw-glass-specular);
|
|
27
|
+
backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
|
|
28
|
+
-webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
color: var(--sw-color-text);
|
|
31
|
+
margin: var(--sw-space-2) auto auto;
|
|
32
|
+
max-height: calc(100dvh - 2 * var(--sw-space-2));
|
|
33
|
+
max-width: calc(100% - 2 * var(--sw-space-2));
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
padding: 0;
|
|
36
|
+
width: ${paletteWidth};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@media ${fromMd} {
|
|
40
|
+
.sw-command-palette {
|
|
41
|
+
margin-top: ${paletteTop};
|
|
42
|
+
max-height: calc(100dvh - ${paletteTop} - var(--sw-space-8));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sw-command-palette::backdrop {
|
|
47
|
+
background: color-mix(in srgb, var(--sw-color-text) 28%, transparent);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.sw-command-palette-body {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
max-height: inherit;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[data-theme] input.sw-command-palette-field {
|
|
57
|
+
background: transparent;
|
|
58
|
+
border: 0;
|
|
59
|
+
border-bottom: 1px solid var(--sw-color-border);
|
|
60
|
+
border-radius: 0;
|
|
61
|
+
box-sizing: border-box;
|
|
62
|
+
flex: none;
|
|
63
|
+
font-size: ${body.fontSize}px;
|
|
64
|
+
letter-spacing: ${body.letterSpacing}px;
|
|
65
|
+
line-height: ${body.lineHeight}px;
|
|
66
|
+
min-height: ${touchTarget};
|
|
67
|
+
padding: var(--sw-space-3) var(--sw-space-4);
|
|
68
|
+
width: 100%;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[data-theme] input.sw-command-palette-field:focus,
|
|
72
|
+
[data-theme] input.sw-command-palette-field:focus-visible {
|
|
73
|
+
border-bottom-color: var(--sw-color-accent);
|
|
74
|
+
box-shadow: inset 0 -1px 0 var(--sw-color-accent);
|
|
75
|
+
outline: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.sw-command-palette-list {
|
|
79
|
+
flex: 1 1 auto;
|
|
80
|
+
min-height: 0;
|
|
81
|
+
overflow-y: auto;
|
|
82
|
+
overscroll-behavior: contain;
|
|
83
|
+
padding: var(--sw-space-1) var(--sw-space-2);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.sw-command-palette-heading {
|
|
87
|
+
color: var(--sw-color-muted);
|
|
88
|
+
font-family: var(--sw-font-sans);
|
|
89
|
+
font-size: ${caption.fontSize}px;
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
letter-spacing: ${caption.letterSpacing}px;
|
|
92
|
+
line-height: ${caption.lineHeight}px;
|
|
93
|
+
padding: var(--sw-space-2) var(--sw-space-2) var(--sw-space-1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.sw-command-palette-message,
|
|
97
|
+
.sw-command-palette-hint {
|
|
98
|
+
color: var(--sw-color-muted);
|
|
99
|
+
font-family: var(--sw-font-sans);
|
|
100
|
+
font-size: ${caption.fontSize}px;
|
|
101
|
+
letter-spacing: ${caption.letterSpacing}px;
|
|
102
|
+
line-height: ${caption.lineHeight}px;
|
|
103
|
+
margin: 0;
|
|
104
|
+
padding: var(--sw-space-3) var(--sw-space-4);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.sw-command-palette-hint {
|
|
108
|
+
border-top: 1px solid var(--sw-color-border);
|
|
109
|
+
flex: none;
|
|
110
|
+
}`;
|
|
111
|
+
}
|
|
112
|
+
/*
|
|
113
|
+
* A result: its mark, its label over its muted detail, and on the active
|
|
114
|
+
* row (`aria-selected`, the arrow keys' or the pointer's) the quiet fill
|
|
115
|
+
* and the Enter key cap at the end. The sm control height, 44 px on a
|
|
116
|
+
* coarse pointer. On the fill the detail moves toward the text colour to
|
|
117
|
+
* keep 4.5:1. Forced colours draw the active row in Highlight.
|
|
118
|
+
*/
|
|
119
|
+
function itemRules() {
|
|
120
|
+
return `.sw-command-palette-item {
|
|
121
|
+
align-items: center;
|
|
122
|
+
border-radius: var(--sw-radius-sm);
|
|
123
|
+
box-sizing: border-box;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
display: flex;
|
|
126
|
+
font-family: var(--sw-font-sans);
|
|
127
|
+
font-size: ${label.fontSize}px;
|
|
128
|
+
gap: var(--sw-space-3);
|
|
129
|
+
letter-spacing: ${label.letterSpacing}px;
|
|
130
|
+
line-height: ${label.lineHeight}px;
|
|
131
|
+
min-height: var(--sw-control-sm-min-height);
|
|
132
|
+
overflow-wrap: anywhere;
|
|
133
|
+
padding: var(--sw-space-1) var(--sw-space-2);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.sw-command-palette-item[aria-selected='true'] {
|
|
137
|
+
background: ${quietFill};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.sw-command-palette-icon {
|
|
141
|
+
display: inline-flex;
|
|
142
|
+
flex: none;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.sw-command-palette-words {
|
|
146
|
+
display: flex;
|
|
147
|
+
flex: 1 1 auto;
|
|
148
|
+
flex-direction: column;
|
|
149
|
+
min-width: 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.sw-command-palette-label {
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.sw-command-palette-detail {
|
|
157
|
+
color: var(--sw-color-muted);
|
|
158
|
+
font-size: ${caption.fontSize}px;
|
|
159
|
+
letter-spacing: ${caption.letterSpacing}px;
|
|
160
|
+
line-height: ${caption.lineHeight}px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.sw-command-palette-item[aria-selected='true'] .sw-command-palette-detail {
|
|
164
|
+
color: color-mix(in srgb, var(--sw-color-muted) 70%, var(--sw-color-text));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@media ${coarsePointerQuery} {
|
|
168
|
+
.sw-command-palette-item { min-height: ${touchTarget}; }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@media (forced-colors: active) {
|
|
172
|
+
.sw-command-palette { border-color: CanvasText; }
|
|
173
|
+
.sw-command-palette-item[aria-selected='true'] { background: Highlight; color: HighlightText; forced-color-adjust: none; }
|
|
174
|
+
.sw-command-palette-item[aria-selected='true'] .sw-command-palette-detail,
|
|
175
|
+
.sw-command-palette-item[aria-selected='true'] .sw-command-palette-key { border-color: HighlightText; color: HighlightText; }
|
|
176
|
+
[data-theme] input.sw-command-palette-field:focus-visible { border-bottom-color: Highlight; box-shadow: inset 0 -1px 0 Highlight; }
|
|
177
|
+
}`;
|
|
178
|
+
}
|
|
179
|
+
/*
|
|
180
|
+
* CommandPalette and CommandPaletteTrigger (see components/CommandPalette.tsx):
|
|
181
|
+
* the trigger is `css-command-palette-trigger.ts`, the shared key cap
|
|
182
|
+
* `css-command-palette-key.ts`.
|
|
183
|
+
*/
|
|
184
|
+
export function cssCommandPaletteClasses() {
|
|
185
|
+
return `${cssCommandPaletteKeyClasses()}
|
|
186
|
+
|
|
187
|
+
${cssCommandPaletteTriggerClasses()}
|
|
188
|
+
|
|
189
|
+
${frameRules()}
|
|
190
|
+
|
|
191
|
+
${itemRules()}`;
|
|
192
|
+
}
|
|
193
|
+
export function commandPaletteClassCatalog() {
|
|
194
|
+
return [
|
|
195
|
+
...commandPaletteTriggerClassCatalog(),
|
|
196
|
+
'sw-command-palette',
|
|
197
|
+
'sw-command-palette-body',
|
|
198
|
+
'sw-command-palette-field',
|
|
199
|
+
'sw-command-palette-list',
|
|
200
|
+
'sw-command-palette-heading',
|
|
201
|
+
'sw-command-palette-item',
|
|
202
|
+
'sw-command-palette-icon',
|
|
203
|
+
'sw-command-palette-words',
|
|
204
|
+
'sw-command-palette-label',
|
|
205
|
+
'sw-command-palette-detail',
|
|
206
|
+
'sw-command-palette-message',
|
|
207
|
+
'sw-command-palette-hint',
|
|
208
|
+
];
|
|
209
|
+
}
|