@scalewing/react 1.60.1 → 1.61.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 +23 -0
- package/dist/components/AppShell.d.ts +5 -3
- package/dist/components/AppShell.js +9 -4
- package/dist/components/DescriptionList.d.ts +11 -3
- package/dist/components/DescriptionList.js +5 -4
- package/dist/components/FilterChips.d.ts +1 -1
- package/dist/components/FilterChips.js +3 -3
- package/dist/components/FixtureGroup.d.ts +7 -3
- package/dist/components/FixtureGroup.js +11 -4
- package/dist/components/Grid.d.ts +11 -3
- package/dist/components/Grid.js +24 -6
- package/dist/components/PageHeader.d.ts +40 -0
- package/dist/components/PageHeader.js +36 -0
- package/dist/components/Tabs.d.ts +19 -0
- package/dist/components/Tabs.js +13 -3
- package/dist/components/Text.d.ts +20 -0
- package/dist/components/Text.js +9 -2
- package/dist/components/app-shell/types.d.ts +15 -0
- package/dist/components/app-shell/validate.d.ts +3 -3
- package/dist/components/app-shell/validate.js +9 -3
- package/dist/components/description-list-layout.d.ts +9 -0
- package/dist/components/description-list-layout.js +15 -0
- package/dist/components/filter-chips/options.d.ts +10 -1
- package/dist/components/filter-chips/options.js +2 -0
- package/dist/components/filter-chips/validate.d.ts +2 -2
- package/dist/components/filter-chips/validate.js +6 -3
- package/dist/components/fixture/types.d.ts +10 -0
- package/dist/css/breakpoints.d.ts +2 -1
- package/dist/css/breakpoints.js +2 -1
- package/dist/css/css-app-shell.d.ts +6 -0
- package/dist/css/css-app-shell.js +20 -1
- package/dist/css/css-button.js +9 -11
- package/dist/css/css-components.js +6 -0
- package/dist/css/css-density.d.ts +9 -0
- package/dist/css/css-density.js +95 -0
- package/dist/css/css-description-list.js +21 -0
- package/dist/css/css-filter-chips.d.ts +8 -0
- package/dist/css/css-filter-chips.js +35 -1
- package/dist/css/css-fixture-list.d.ts +10 -0
- package/dist/css/css-fixture-list.js +38 -0
- package/dist/css/css-fixture.js +4 -1
- package/dist/css/css-glyph-tile.js +6 -2
- package/dist/css/css-grid-span.js +8 -3
- package/dist/css/css-grid.d.ts +8 -1
- package/dist/css/css-grid.js +21 -2
- package/dist/css/css-page-header.d.ts +2 -0
- package/dist/css/css-page-header.js +106 -0
- package/dist/css/css-table-bounded.js +3 -2
- package/dist/css/css-table-metrics.d.ts +5 -0
- package/dist/css/css-table-metrics.js +5 -0
- package/dist/css/css-tabs.js +8 -0
- package/dist/css/css-text.d.ts +5 -0
- package/dist/css/css-text.js +26 -1
- package/dist/css/stylesheet.js +4 -0
- package/dist/css/touch-target.d.ts +1 -0
- package/dist/css/touch-target.js +4 -0
- package/dist/index.d.ts +12 -9
- package/dist/index.js +8 -6
- package/dist/styles.css +186 -7
- package/dist/theme/ThemeProvider.d.ts +13 -1
- package/dist/theme/ThemeProvider.js +7 -2
- package/package.json +2 -2
|
@@ -8,3 +8,12 @@ export declare const descriptionListLayouts: readonly ["columns", "spread"];
|
|
|
8
8
|
export type DescriptionListLayout = (typeof descriptionListLayouts)[number];
|
|
9
9
|
/** Fails closed on a layout the list cannot draw. */
|
|
10
10
|
export declare function assertDescriptionListLayout(layout: unknown): void;
|
|
11
|
+
/**
|
|
12
|
+
* A fixed term column, so details line up across several lists on a page:
|
|
13
|
+
* `sm` 8rem, `md` 12rem, `lg` 16rem, from `md` up and never more than half
|
|
14
|
+
* the list. Only with the `columns` layout.
|
|
15
|
+
*/
|
|
16
|
+
export declare const descriptionListTermWidths: readonly ["sm", "md", "lg"];
|
|
17
|
+
export type DescriptionListTermWidth = (typeof descriptionListTermWidths)[number];
|
|
18
|
+
/** Fails closed on a term width the list cannot draw. */
|
|
19
|
+
export declare function assertDescriptionListTermWidth(termWidth: unknown, layout: DescriptionListLayout): void;
|
|
@@ -11,3 +11,18 @@ export function assertDescriptionListLayout(layout) {
|
|
|
11
11
|
if (!isOneOf(descriptionListLayouts, layout))
|
|
12
12
|
throw new RangeError(`Unknown DescriptionList layout: ${String(layout)}`);
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* A fixed term column, so details line up across several lists on a page:
|
|
16
|
+
* `sm` 8rem, `md` 12rem, `lg` 16rem, from `md` up and never more than half
|
|
17
|
+
* the list. Only with the `columns` layout.
|
|
18
|
+
*/
|
|
19
|
+
export const descriptionListTermWidths = ['sm', 'md', 'lg'];
|
|
20
|
+
/** Fails closed on a term width the list cannot draw. */
|
|
21
|
+
export function assertDescriptionListTermWidth(termWidth, layout) {
|
|
22
|
+
if (termWidth === undefined)
|
|
23
|
+
return;
|
|
24
|
+
if (!isOneOf(descriptionListTermWidths, termWidth))
|
|
25
|
+
throw new RangeError(`Unknown DescriptionList termWidth: ${String(termWidth)}`);
|
|
26
|
+
if (layout !== 'columns')
|
|
27
|
+
throw new RangeError('DescriptionList termWidth needs the columns layout');
|
|
28
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { filterChipsSelectedStyles, type FilterChipsSelectedStyle } from '../../css/css-filter-chips.js';
|
|
1
2
|
import { type RequiredMark } from '../FieldLabelText.js';
|
|
3
|
+
export { filterChipsSelectedStyles, type FilterChipsSelectedStyle };
|
|
2
4
|
/**
|
|
3
5
|
* A chip's colour while it is not chosen: `neutral` (the default), the
|
|
4
6
|
* glass chip; `warning`, the warning tint with its words in the warning
|
|
@@ -72,6 +74,14 @@ export type FilterChipsProps = FilterChipsChoice & {
|
|
|
72
74
|
requiredMark?: RequiredMark;
|
|
73
75
|
countAppearance?: FilterChipCountAppearance;
|
|
74
76
|
overflow?: FilterChipsOverflow;
|
|
77
|
+
/**
|
|
78
|
+
* How a chosen chip looks: `solid` (the default) is the accent fill;
|
|
79
|
+
* `subtle` is the accent's quiet tint with the accent's ink inside an accent
|
|
80
|
+
* edge (4.5:1 in every palette), for a page where a chosen filter should
|
|
81
|
+
* not outweigh the page's primary action. Forced colours draw either in
|
|
82
|
+
* Highlight.
|
|
83
|
+
*/
|
|
84
|
+
selectedStyle?: FilterChipsSelectedStyle;
|
|
75
85
|
/**
|
|
76
86
|
* A message under the chips, such as "Pick a reason": a polite live
|
|
77
87
|
* region linked to the group by `aria-describedby`; the group is marked
|
|
@@ -82,4 +92,3 @@ export type FilterChipsProps = FilterChipsChoice & {
|
|
|
82
92
|
/** More ids that describe the group, such as a hint beside it. */
|
|
83
93
|
'aria-describedby'?: string;
|
|
84
94
|
};
|
|
85
|
-
export {};
|
|
@@ -2,8 +2,8 @@ import { type FilterChipsProps } from './options.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Fails closed on chips it cannot draw: an empty label, no options, blank
|
|
4
4
|
* or repeated values, a chosen value that is no option, a count that is
|
|
5
|
-
* not a whole number from 0, an unknown chip tone, count appearance
|
|
6
|
-
* overflow, and a multiple choice asked to be required or given a value
|
|
5
|
+
* not a whole number from 0, an unknown chip tone, count appearance,
|
|
6
|
+
* overflow or selected style, and a multiple choice asked to be required or given a value
|
|
7
7
|
* that is not a list.
|
|
8
8
|
*/
|
|
9
9
|
export declare function assertFilterChips(props: FilterChipsProps): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertRequiredMark } from '../FieldLabelText.js';
|
|
2
|
-
import { filterChipCountAppearances, filterChipsOverflows, filterChipTones, } from './options.js';
|
|
2
|
+
import { filterChipCountAppearances, filterChipsOverflows, filterChipsSelectedStyles, filterChipTones, } from './options.js';
|
|
3
3
|
function isCount(count) {
|
|
4
4
|
return count === undefined || (Number.isInteger(count) && count >= 0);
|
|
5
5
|
}
|
|
@@ -11,8 +11,8 @@ function chosenValues(props) {
|
|
|
11
11
|
/**
|
|
12
12
|
* Fails closed on chips it cannot draw: an empty label, no options, blank
|
|
13
13
|
* or repeated values, a chosen value that is no option, a count that is
|
|
14
|
-
* not a whole number from 0, an unknown chip tone, count appearance
|
|
15
|
-
* overflow, and a multiple choice asked to be required or given a value
|
|
14
|
+
* not a whole number from 0, an unknown chip tone, count appearance,
|
|
15
|
+
* overflow or selected style, and a multiple choice asked to be required or given a value
|
|
16
16
|
* that is not a list.
|
|
17
17
|
*/
|
|
18
18
|
export function assertFilterChips(props) {
|
|
@@ -49,4 +49,7 @@ export function assertFilterChips(props) {
|
|
|
49
49
|
const overflow = props.overflow ?? 'wrap';
|
|
50
50
|
if (!filterChipsOverflows.includes(overflow))
|
|
51
51
|
throw new RangeError(`Unknown FilterChips overflow: ${String(overflow)}`);
|
|
52
|
+
const selectedStyle = props.selectedStyle ?? 'solid';
|
|
53
|
+
if (!filterChipsSelectedStyles.includes(selectedStyle))
|
|
54
|
+
throw new RangeError(`Unknown FilterChips selectedStyle: ${String(selectedStyle)}`);
|
|
52
55
|
}
|
|
@@ -2,6 +2,7 @@ import { type MouseEvent, type ReactNode, type Ref } from 'react';
|
|
|
2
2
|
import { type VisibilityBreakpoint } from '../../css/breakpoints.js';
|
|
3
3
|
import { type BadgeTone } from '../../css/css-data.js';
|
|
4
4
|
import { type FixtureCenterKind, type FixtureFlagTone } from '../../css/css-fixture.js';
|
|
5
|
+
import { type FixtureGroupSides } from '../../css/css-fixture-list.js';
|
|
5
6
|
/** One side of a fixture: its name and an optional decorative mark (a crest). */
|
|
6
7
|
export type FixtureSide = {
|
|
7
8
|
name: string;
|
|
@@ -146,6 +147,15 @@ export type FixtureGroupProps = {
|
|
|
146
147
|
* the game and its day. Default `false`.
|
|
147
148
|
*/
|
|
148
149
|
titleHidden?: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* `fluid` (the default): the sides share the row, so the centre moves with
|
|
152
|
+
* the run's width. `fixed`: from `md` the sides are at most 16rem each and
|
|
153
|
+
* the end column takes the rest, its details and action at the row's end,
|
|
154
|
+
* so the score sits at the same place in every run of the same width on a
|
|
155
|
+
* page (a run with a `select` starts one box later). Below `md` both are
|
|
156
|
+
* the same.
|
|
157
|
+
*/
|
|
158
|
+
sides?: FixtureGroupSides;
|
|
149
159
|
/** The run's FixtureRows. */
|
|
150
160
|
children: ReactNode;
|
|
151
161
|
};
|
|
@@ -7,7 +7,8 @@ export declare const breakpoints: Breakpoint[];
|
|
|
7
7
|
/**
|
|
8
8
|
* The breakpoints `hideBelow` and `hideFrom` take: `md`, and `lg` (64rem)
|
|
9
9
|
* for content that fits only from a laptop up, such as a row of labelled
|
|
10
|
-
* destinations a tablet shows as glyphs
|
|
10
|
+
* destinations a tablet shows as glyphs, and `Grid rail` takes them for a rail
|
|
11
|
+
* that needs a laptop's room. The other layout props (`columnsBelow`,
|
|
11
12
|
* `stickyBelow`, `verticalFrom`, …) keep the one layout breakpoint.
|
|
12
13
|
*/
|
|
13
14
|
export declare const visibilityBreakpointScale: {
|
package/dist/css/breakpoints.js
CHANGED
|
@@ -6,7 +6,8 @@ export const breakpoints = Object.keys(breakpointScale);
|
|
|
6
6
|
/**
|
|
7
7
|
* The breakpoints `hideBelow` and `hideFrom` take: `md`, and `lg` (64rem)
|
|
8
8
|
* for content that fits only from a laptop up, such as a row of labelled
|
|
9
|
-
* destinations a tablet shows as glyphs
|
|
9
|
+
* destinations a tablet shows as glyphs, and `Grid rail` takes them for a rail
|
|
10
|
+
* that needs a laptop's room. The other layout props (`columnsBelow`,
|
|
10
11
|
* `stickyBelow`, `verticalFrom`, …) keep the one layout breakpoint.
|
|
11
12
|
*/
|
|
12
13
|
export const visibilityBreakpointScale = {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** `container` centres the page beside the sidebar at the container width. */
|
|
2
|
+
export declare const appShellContentMaxWidths: readonly ["none", "container"];
|
|
3
|
+
export type AppShellContentMaxWidth = (typeof appShellContentMaxWidths)[number];
|
|
4
|
+
/** The sidebar's and the drawer's fill. */
|
|
5
|
+
export declare const appShellSidebarTones: readonly ["surface", "subtle"];
|
|
6
|
+
export type AppShellSidebarTone = (typeof appShellSidebarTones)[number];
|
|
1
7
|
/** From this width up AppShell draws its full sidebar; script reads it too. */
|
|
2
8
|
export declare const appShellWideQuery = "(min-width: 75rem)";
|
|
3
9
|
export declare function cssAppShellClasses(): string;
|
|
@@ -4,6 +4,10 @@ import { appShellNavClassCatalog, cssAppShellNavClasses, cssAppShellRailNavRules
|
|
|
4
4
|
import { appShellPageBarClassCatalog, cssAppShellPageBarClasses, } from './css-app-shell-page-bar.js';
|
|
5
5
|
import { appShellSkipLinkClassCatalog, cssAppShellSkipLinkRules, } from './css-app-shell-skip-link.js';
|
|
6
6
|
import { iconButtonRules } from './css-icon-button.js';
|
|
7
|
+
/** `container` centres the page beside the sidebar at the container width. */
|
|
8
|
+
export const appShellContentMaxWidths = ['none', 'container'];
|
|
9
|
+
/** The sidebar's and the drawer's fill. */
|
|
10
|
+
export const appShellSidebarTones = ['surface', 'subtle'];
|
|
7
11
|
/*
|
|
8
12
|
* The width from which AppShell's sidebar is full rather than a rail: an
|
|
9
13
|
* internal media query of the component, not a public breakpoint (the
|
|
@@ -109,6 +113,18 @@ ${cssAppShellRailNavRules(' ')}
|
|
|
109
113
|
}
|
|
110
114
|
}`;
|
|
111
115
|
}
|
|
116
|
+
/*
|
|
117
|
+
* `sidebarTone="subtle"`: the sidebar and the drawer on the subtle fill, a
|
|
118
|
+
* class more than their own surface rules. The words keep 4.5:1 there in
|
|
119
|
+
* every palette: the text colour, a group's muted caption, and the text
|
|
120
|
+
* colour on the current link's fill over the grey (tested in every theme).
|
|
121
|
+
* Forced colours replace the fill.
|
|
122
|
+
*/
|
|
123
|
+
function sidebarToneRules() {
|
|
124
|
+
return `.sw-app-shell-sidebar-subtle :is(.sw-app-shell-sidebar, .sw-app-shell-drawer) {
|
|
125
|
+
background: var(--sw-color-subtle);
|
|
126
|
+
}`;
|
|
127
|
+
}
|
|
112
128
|
/*
|
|
113
129
|
* AppShell: a workspace frame with a pinned side navigation (see
|
|
114
130
|
* components/AppShell.tsx). The links, groups and the rail's nav are
|
|
@@ -137,7 +153,9 @@ ${sidebarRules()}
|
|
|
137
153
|
|
|
138
154
|
${cssAppShellDrawerClasses()}
|
|
139
155
|
|
|
140
|
-
${cssAppShellSkipLinkRules()}
|
|
156
|
+
${cssAppShellSkipLinkRules()}
|
|
157
|
+
|
|
158
|
+
${sidebarToneRules()}`;
|
|
141
159
|
}
|
|
142
160
|
export function appShellClassCatalog() {
|
|
143
161
|
return [
|
|
@@ -148,6 +166,7 @@ export function appShellClassCatalog() {
|
|
|
148
166
|
'sw-app-shell-bar-end',
|
|
149
167
|
'sw-app-shell-main',
|
|
150
168
|
'sw-app-shell-sidebar',
|
|
169
|
+
'sw-app-shell-sidebar-subtle',
|
|
151
170
|
'sw-app-shell-slot',
|
|
152
171
|
'sw-app-shell-rail-slot',
|
|
153
172
|
'sw-app-shell-header',
|
package/dist/css/css-button.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buttonSizes, buttonVariants,
|
|
1
|
+
import { buttonSizes, buttonVariants, typographyVariants, } from '@scalewing/tokens';
|
|
2
2
|
import { breakpointQuery } from './breakpoints.js';
|
|
3
3
|
import { buttonGroupAttachedClassCatalog, cssButtonGroupAttachedClasses, } from './css-button-group-attached.js';
|
|
4
4
|
import { buttonWidthClassCatalog, cssButtonWidthClasses, } from './css-button-width.js';
|
|
@@ -84,19 +84,17 @@ function ariaDisabledForcedRules() {
|
|
|
84
84
|
}`;
|
|
85
85
|
}
|
|
86
86
|
/*
|
|
87
|
-
* Each size's height and side padding for a fine pointer,
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* which it outranks
|
|
87
|
+
* Each size's height and side padding for a fine pointer, from the control
|
|
88
|
+
* variables (which the compact density restates), then the comfortable
|
|
89
|
+
* touch target (44 px both ways, the md control height) for every size on a
|
|
90
|
+
* coarse pointer, so an xs or sm Button in a table row or a checklist is
|
|
91
|
+
* still easy to tap. Its look stays the size's (padding and type); only the
|
|
92
|
+
* minimum box grows. The coarse rule follows the sizes, which it outranks
|
|
93
|
+
* only by order.
|
|
93
94
|
*/
|
|
94
95
|
function sizeRules() {
|
|
95
96
|
const sizes = buttonSizes
|
|
96
|
-
.map((size) => {
|
|
97
|
-
const control = controlScale[size];
|
|
98
|
-
return `.sw-button-${size} { min-height: ${control.minHeight}px; padding-inline: ${control.paddingInline}px; }`;
|
|
99
|
-
})
|
|
97
|
+
.map((size) => `.sw-button-${size} { min-height: var(--sw-control-${size}-min-height); padding-inline: var(--sw-control-${size}-padding-inline); }`)
|
|
100
98
|
.join('\n');
|
|
101
99
|
return `${sizes}
|
|
102
100
|
@media ${coarsePointerQuery} {
|
|
@@ -57,6 +57,7 @@ import { cssSeparatorClasses } from './css-separator.js';
|
|
|
57
57
|
import { cssBandClasses } from './css-band.js';
|
|
58
58
|
import { cssScreenPreviewClasses } from './css-screen-preview.js';
|
|
59
59
|
import { cssHeroClasses } from './css-hero.js';
|
|
60
|
+
import { cssPageHeaderClasses } from './css-page-header.js';
|
|
60
61
|
import { cssSiteFooterClasses } from './css-site-footer.js';
|
|
61
62
|
import { cssSiteHeaderClasses } from './css-site-header.js';
|
|
62
63
|
import { cssStatTileClasses } from './css-stat-tile.js';
|
|
@@ -74,6 +75,7 @@ import { cssTaskListClasses } from './css-task-list.js';
|
|
|
74
75
|
import { cssToastClasses } from './css-toast.js';
|
|
75
76
|
import { cssToastCardClasses } from './css-toast-card.js';
|
|
76
77
|
import { cssBoxBorderClasses } from './css-box-border.js';
|
|
78
|
+
import { cssDensityClasses } from './css-density.js';
|
|
77
79
|
import { cssPopupHostRules } from './stacking.js';
|
|
78
80
|
/*
|
|
79
81
|
* Reduce Transparency swaps every glass surface for the solid surface. It is
|
|
@@ -215,6 +217,8 @@ ${cssScreenPreviewClasses()}
|
|
|
215
217
|
|
|
216
218
|
${cssHeroClasses()}
|
|
217
219
|
|
|
220
|
+
${cssPageHeaderClasses()}
|
|
221
|
+
|
|
218
222
|
${cssSiteFooterClasses()}
|
|
219
223
|
|
|
220
224
|
${cssSiteHeaderClasses()}
|
|
@@ -298,5 +302,7 @@ ${cssResponsiveClasses()}
|
|
|
298
302
|
|
|
299
303
|
${cssBoxBorderClasses()}
|
|
300
304
|
|
|
305
|
+
${cssDensityClasses()}
|
|
306
|
+
|
|
301
307
|
${reducedTransparencyRules()}`;
|
|
302
308
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How tightly the themed root draws its controls: `comfortable` (the
|
|
3
|
+
* default) is the touch-sized scale; `compact` (opt-in, on a fine pointer
|
|
4
|
+
* only) is a desktop data tool's smaller controls and reading text.
|
|
5
|
+
*/
|
|
6
|
+
export declare const themeDensities: readonly ["comfortable", "compact"];
|
|
7
|
+
export type ThemeDensity = (typeof themeDensities)[number];
|
|
8
|
+
export declare function assertThemeDensity(density: unknown): asserts density is ThemeDensity;
|
|
9
|
+
export declare function cssDensityClasses(): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { compactControlScale, compactFilterChipMinHeight, typographyVariants, } from '@scalewing/tokens';
|
|
2
|
+
import { tableRowLineVariable } from './css-table-metrics.js';
|
|
3
|
+
import { finePointerQuery } from './touch-target.js';
|
|
4
|
+
/**
|
|
5
|
+
* How tightly the themed root draws its controls: `comfortable` (the
|
|
6
|
+
* default) is the touch-sized scale; `compact` (opt-in, on a fine pointer
|
|
7
|
+
* only) is a desktop data tool's smaller controls and reading text.
|
|
8
|
+
*/
|
|
9
|
+
export const themeDensities = ['comfortable', 'compact'];
|
|
10
|
+
export function assertThemeDensity(density) {
|
|
11
|
+
if (!themeDensities.includes(density))
|
|
12
|
+
throw new RangeError(`Unknown ThemeProvider density: ${String(density)}`);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The compact themed root. Two attributes outrank the `[data-theme]` block
|
|
16
|
+
* that declares the comfortable values, in any order.
|
|
17
|
+
*/
|
|
18
|
+
const compactRoot = "[data-theme][data-density='compact']";
|
|
19
|
+
/*
|
|
20
|
+
* The controls that state the body size for their own words rather than
|
|
21
|
+
* inherit it, which would otherwise stand 2 px over a compact Field beside
|
|
22
|
+
* them. An xs segmented track states its own caption size and stays.
|
|
23
|
+
*/
|
|
24
|
+
const statedBodyControls = [
|
|
25
|
+
'.sw-date-field-input',
|
|
26
|
+
'.sw-fixture-score-box',
|
|
27
|
+
'.sw-segmented-filled .sw-segmented-item',
|
|
28
|
+
'.sw-stepper-input',
|
|
29
|
+
'.sw-stepper-unit',
|
|
30
|
+
].join(', ');
|
|
31
|
+
function compactControlDeclarations() {
|
|
32
|
+
return Object.entries(compactControlScale)
|
|
33
|
+
.flatMap(([size, value]) => [
|
|
34
|
+
` --sw-control-${size}-min-height: ${value.minHeight}px;`,
|
|
35
|
+
` --sw-control-${size}-padding-inline: ${value.paddingInline}px;`,
|
|
36
|
+
])
|
|
37
|
+
.join('\n');
|
|
38
|
+
}
|
|
39
|
+
function bodySmDeclarations(indent) {
|
|
40
|
+
const { fontSize, letterSpacing, lineHeight } = typographyVariants.bodySm;
|
|
41
|
+
return [
|
|
42
|
+
`font-size: ${fontSize}px;`,
|
|
43
|
+
`letter-spacing: ${letterSpacing}px;`,
|
|
44
|
+
`line-height: ${lineHeight}px;`,
|
|
45
|
+
]
|
|
46
|
+
.map((line) => `${indent}${line}`)
|
|
47
|
+
.join('\n');
|
|
48
|
+
}
|
|
49
|
+
/*
|
|
50
|
+
* `ThemeProvider density="compact"`, and everything it changes, on a fine
|
|
51
|
+
* pointer only: a touch screen keeps the comfortable scale, its 44 px
|
|
52
|
+
* targets (`touchTarget` reads the md variable) included, and a coarse rule
|
|
53
|
+
* never runs where these do.
|
|
54
|
+
*
|
|
55
|
+
* Every size-aware rule reads the control variables, so restating them here
|
|
56
|
+
* moves them all, and geometry derived from them (the rail's width, a page
|
|
57
|
+
* bar's clearance, a calendar's days) moves in step. The themed root's own
|
|
58
|
+
* type steps to `bodySm`, so what inherits it follows: a table's cells, a
|
|
59
|
+
* field's label and its control's words (the same in a Card and outside
|
|
60
|
+
* one). `Text`'s default `body` variant states its own size, so it follows
|
|
61
|
+
* only where the reading text is asked for: in a Card, a Table and a
|
|
62
|
+
* DescriptionList. A rail's smaller link would bury its glyph under the count
|
|
63
|
+
* pill, so the pill moves out into the gutter (the full sidebar's is in flow,
|
|
64
|
+
* where this has no effect).
|
|
65
|
+
*/
|
|
66
|
+
export function cssDensityClasses() {
|
|
67
|
+
return `@media ${finePointerQuery} {
|
|
68
|
+
${compactRoot} {
|
|
69
|
+
${compactControlDeclarations()}
|
|
70
|
+
--sw-filter-chip-min-height: ${compactFilterChipMinHeight}px;
|
|
71
|
+
${bodySmDeclarations(' ')}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
${compactRoot} :is(.sw-card, .sw-table, .sw-description-list) .sw-text-body {
|
|
75
|
+
${bodySmDeclarations(' ')}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
${compactRoot} :is(${statedBodyControls}) {
|
|
79
|
+
${bodySmDeclarations(' ')}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
${compactRoot} .sw-table-bounded {
|
|
83
|
+
${tableRowLineVariable}: ${typographyVariants.bodySm.lineHeight}px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
${compactRoot} .sw-card {
|
|
87
|
+
border-radius: var(--sw-radius-md);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
${compactRoot} .sw-app-shell-sidebar .sw-app-shell-count {
|
|
91
|
+
inset-block-start: calc(-1 * var(--sw-space-1));
|
|
92
|
+
inset-inline-end: calc(-1 * var(--sw-space-2));
|
|
93
|
+
}
|
|
94
|
+
}`;
|
|
95
|
+
}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { breakpointQuery } from './breakpoints.js';
|
|
2
|
+
/** The fixed term columns of `termWidth`, a named geometry of this list. */
|
|
3
|
+
const termWidths = { sm: '8rem', md: '12rem', lg: '16rem' };
|
|
4
|
+
function termWidthClass(width) {
|
|
5
|
+
return `sw-description-list-term-${width}`;
|
|
6
|
+
}
|
|
7
|
+
/*
|
|
8
|
+
* From `md` only, where the list has a term column at all; below it the
|
|
9
|
+
* base rule stacks each term over its detail. The column never takes more
|
|
10
|
+
* than half the list, so a narrow card keeps room for its details.
|
|
11
|
+
*/
|
|
12
|
+
function termWidthRules() {
|
|
13
|
+
const widths = Object.entries(termWidths);
|
|
14
|
+
return `@media ${breakpointQuery('from', 'md')} {
|
|
15
|
+
${widths
|
|
16
|
+
.map(([width, size]) => ` .${termWidthClass(width)} { grid-template-columns: min(${size}, 50%) minmax(0, 1fr); }`)
|
|
17
|
+
.join('\n')}
|
|
18
|
+
}`;
|
|
19
|
+
}
|
|
2
20
|
/*
|
|
3
21
|
* A spread list sets each row on its own two columns, at every width: the
|
|
4
22
|
* term fits its words up to half the row at the start, and the detail
|
|
@@ -72,6 +90,8 @@ export function cssDescriptionListClasses() {
|
|
|
72
90
|
|
|
73
91
|
${spreadRules()}
|
|
74
92
|
|
|
93
|
+
${termWidthRules()}
|
|
94
|
+
|
|
75
95
|
@media (forced-colors: active) {
|
|
76
96
|
.sw-description-item + .sw-description-item { border-top-color: CanvasText; }
|
|
77
97
|
}`;
|
|
@@ -80,6 +100,7 @@ export function descriptionListClassCatalog() {
|
|
|
80
100
|
return [
|
|
81
101
|
'sw-description-list',
|
|
82
102
|
'sw-description-list-spread',
|
|
103
|
+
...Object.keys(termWidths).map(termWidthClass),
|
|
83
104
|
'sw-description-item',
|
|
84
105
|
'sw-description-term',
|
|
85
106
|
'sw-description-detail',
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a chosen chip looks: `solid` (the default) is the accent fill with its
|
|
3
|
+
* on-colour words; `subtle` is the accent's quiet tint with the accent's
|
|
4
|
+
* words inside an accent edge, for a page where the chosen chip should not
|
|
5
|
+
* outweigh the page's primary action.
|
|
6
|
+
*/
|
|
7
|
+
export declare const filterChipsSelectedStyles: readonly ["solid", "subtle"];
|
|
8
|
+
export type FilterChipsSelectedStyle = (typeof filterChipsSelectedStyles)[number];
|
|
1
9
|
export declare function cssFilterChipsClasses(): string;
|
|
2
10
|
export declare function filterChipsClassCatalog(): string[];
|
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
import { typographyVariants } from '@scalewing/tokens';
|
|
2
|
+
import { toneTintFill, toneTintInk } from './tone-tint.js';
|
|
2
3
|
import { cssFilterChipToneClasses, filterChipToneClassCatalog, } from './css-filter-chip-tones.js';
|
|
3
4
|
import { cssFilterChipsLayoutClasses, filterChipsLayoutClassCatalog, } from './css-filter-chips-layout.js';
|
|
4
5
|
import { cssFilterChipsRowClasses, filterChipsRowClassCatalog, } from './css-filter-chips-row.js';
|
|
5
6
|
const label = typographyVariants.label;
|
|
6
7
|
const data = typographyVariants.data;
|
|
7
8
|
const caption = typographyVariants.caption;
|
|
9
|
+
/**
|
|
10
|
+
* How a chosen chip looks: `solid` (the default) is the accent fill with its
|
|
11
|
+
* on-colour words; `subtle` is the accent's quiet tint with the accent's
|
|
12
|
+
* words inside an accent edge, for a page where the chosen chip should not
|
|
13
|
+
* outweigh the page's primary action.
|
|
14
|
+
*/
|
|
15
|
+
export const filterChipsSelectedStyles = ['solid', 'subtle'];
|
|
16
|
+
/*
|
|
17
|
+
* The compact density's chip height (`--sw-filter-chip-min-height`, set only
|
|
18
|
+
* there), else the md control's.
|
|
19
|
+
*/
|
|
20
|
+
const chipMinHeight = 'var(--sw-filter-chip-min-height, var(--sw-control-md-min-height))';
|
|
21
|
+
/*
|
|
22
|
+
* `selectedStyle="subtle"`. The words are the accent's ink, the accent moved
|
|
23
|
+
* toward the text colour, a Notice's way with a tint: the bare accent falls
|
|
24
|
+
* to 4.24:1 on the tint in harvest dark, and the ink keeps 4.5:1 in every
|
|
25
|
+
* palette. `:where()` holds the rule at the chosen chip's own specificity, so
|
|
26
|
+
* what follows it wins as it does over the accent fill: an error's danger
|
|
27
|
+
* edge and the forced colours' Highlight.
|
|
28
|
+
*/
|
|
29
|
+
function selectedSubtleRules() {
|
|
30
|
+
return `:where(.sw-filter-chips-selected-subtle) .sw-filter-chip-input:checked + .sw-filter-chip-face {
|
|
31
|
+
background: ${toneTintFill('accent')};
|
|
32
|
+
border-color: var(--sw-color-accent);
|
|
33
|
+
color: ${toneTintInk('accent')};
|
|
34
|
+
}`;
|
|
35
|
+
}
|
|
8
36
|
export function cssFilterChipsClasses() {
|
|
9
37
|
return `.sw-filter-chips {
|
|
10
38
|
border: 0;
|
|
@@ -52,13 +80,14 @@ export function cssFilterChipsClasses() {
|
|
|
52
80
|
background: var(--sw-glass-fill);
|
|
53
81
|
border: 1px solid var(--sw-color-border);
|
|
54
82
|
border-radius: var(--sw-radius-pill);
|
|
83
|
+
box-sizing: border-box;
|
|
55
84
|
color: var(--sw-color-text);
|
|
56
85
|
display: inline-flex;
|
|
57
86
|
font-family: var(--sw-font-sans);
|
|
58
87
|
font-size: ${label.fontSize}px;
|
|
59
88
|
gap: var(--sw-space-2);
|
|
60
89
|
line-height: ${label.lineHeight}px;
|
|
61
|
-
min-height:
|
|
90
|
+
min-height: ${chipMinHeight};
|
|
62
91
|
min-width: 0;
|
|
63
92
|
overflow-wrap: anywhere;
|
|
64
93
|
padding: var(--sw-space-1) var(--sw-space-3);
|
|
@@ -91,6 +120,8 @@ export function cssFilterChipsClasses() {
|
|
|
91
120
|
color: var(--sw-color-onAccent);
|
|
92
121
|
}
|
|
93
122
|
|
|
123
|
+
${selectedSubtleRules()}
|
|
124
|
+
|
|
94
125
|
.sw-filter-chip-input:checked + .sw-filter-chip-face .sw-filter-chip-count {
|
|
95
126
|
color: inherit;
|
|
96
127
|
}
|
|
@@ -153,6 +184,9 @@ export function filterChipsClassCatalog() {
|
|
|
153
184
|
'sw-filter-chip-face',
|
|
154
185
|
'sw-filter-chip-count',
|
|
155
186
|
'sw-filter-chip-quiet',
|
|
187
|
+
...filterChipsSelectedStyles
|
|
188
|
+
.filter((style) => style !== 'solid')
|
|
189
|
+
.map((style) => `sw-filter-chips-selected-${style}`),
|
|
156
190
|
'sw-filter-chips-error',
|
|
157
191
|
...filterChipsLayoutClassCatalog(),
|
|
158
192
|
...filterChipToneClassCatalog(),
|
|
@@ -1 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a FixtureGroup's sides take the row: `fluid` (the default) share all
|
|
3
|
+
* of it, so the centre sits in the middle of whatever is left; `fixed` are
|
|
4
|
+
* at most 16rem each, the end column taking the rest, so the centre sits at
|
|
5
|
+
* the same place in every run on a page.
|
|
6
|
+
*/
|
|
7
|
+
export declare const fixtureGroupSides: readonly ["fluid", "fixed"];
|
|
8
|
+
export type FixtureGroupSides = (typeof fixtureGroupSides)[number];
|
|
1
9
|
export declare function cssFixtureListRules(): string;
|
|
10
|
+
/** After the row's narrow and fallback rules, which `fixed` outranks only from md. */
|
|
11
|
+
export declare function cssFixtureListSidesRules(): string;
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import { typographyVariants } from '@scalewing/tokens';
|
|
2
|
+
import { breakpointQuery } from './breakpoints.js';
|
|
2
3
|
const caption = typographyVariants.caption;
|
|
4
|
+
/**
|
|
5
|
+
* How a FixtureGroup's sides take the row: `fluid` (the default) share all
|
|
6
|
+
* of it, so the centre sits in the middle of whatever is left; `fixed` are
|
|
7
|
+
* at most 16rem each, the end column taking the rest, so the centre sits at
|
|
8
|
+
* the same place in every run on a page.
|
|
9
|
+
*/
|
|
10
|
+
export const fixtureGroupSides = ['fluid', 'fixed'];
|
|
11
|
+
const fixedSide = 'minmax(0, 16rem)';
|
|
12
|
+
/*
|
|
13
|
+
* The centre's least width in a `fixed` run: an `auto` column alone is as
|
|
14
|
+
* wide as its widest centre, which differs between a run of scores and a run
|
|
15
|
+
* of kickoff times and would move the centres apart. This holds either, and
|
|
16
|
+
* two score boxes, with its margins.
|
|
17
|
+
*/
|
|
18
|
+
const fixedCentre = 'minmax(7rem, auto)';
|
|
19
|
+
/*
|
|
20
|
+
* From \`md\` only: below it the narrow rules' columns stand, the end on a
|
|
21
|
+
* second line. Without subgrid each row is its own grid of the same fixed
|
|
22
|
+
* columns, which line up on their own.
|
|
23
|
+
*/
|
|
24
|
+
function fixedSidesRules() {
|
|
25
|
+
return `@media ${breakpointQuery('from', 'md')} {
|
|
26
|
+
.sw-fixture-list-sides-fixed {
|
|
27
|
+
grid-template-columns: auto ${fixedSide} ${fixedCentre} ${fixedSide} 1fr auto auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@supports not (grid-template-columns: subgrid) {
|
|
31
|
+
.sw-fixture-list-sides-fixed .sw-fixture-row {
|
|
32
|
+
grid-template-columns: ${fixedSide} ${fixedCentre} ${fixedSide} 1fr auto;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}`;
|
|
36
|
+
}
|
|
3
37
|
/*
|
|
4
38
|
* The run: one grid whose columns are the select, the home side, the
|
|
5
39
|
* centre, the away side, the end (details and status), the chevron and the
|
|
@@ -48,3 +82,7 @@ export function cssFixtureListRules() {
|
|
|
48
82
|
border-top: 1px solid var(--sw-color-border);
|
|
49
83
|
}`;
|
|
50
84
|
}
|
|
85
|
+
/** After the row's narrow and fallback rules, which `fixed` outranks only from md. */
|
|
86
|
+
export function cssFixtureListSidesRules() {
|
|
87
|
+
return fixedSidesRules();
|
|
88
|
+
}
|
package/dist/css/css-fixture.js
CHANGED
|
@@ -3,7 +3,7 @@ import { breakpointQuery } from './breakpoints.js';
|
|
|
3
3
|
import { chevronStrokeIn } from './chevron.js';
|
|
4
4
|
import { mutedReadsAsText, quietFill } from './quiet-fill.js';
|
|
5
5
|
import { cssFixtureActionClasses, fixtureActionClassCatalog, } from './css-fixture-action.js';
|
|
6
|
-
import { cssFixtureListRules } from './css-fixture-list.js';
|
|
6
|
+
import { cssFixtureListRules, cssFixtureListSidesRules, } from './css-fixture-list.js';
|
|
7
7
|
import { cssFixtureDetailClasses, fixtureDetailClassCatalog, } from './css-fixture-detail.js';
|
|
8
8
|
import { cssFixtureFlagClasses, fixtureFlagClassCatalog, } from './css-fixture-flag.js';
|
|
9
9
|
import { cssFixtureScoreClasses, fixtureScoreClassCatalog, } from './css-fixture-score.js';
|
|
@@ -209,6 +209,8 @@ ${narrowRules()}
|
|
|
209
209
|
|
|
210
210
|
${fallbackRules()}
|
|
211
211
|
|
|
212
|
+
${cssFixtureListSidesRules()}
|
|
213
|
+
|
|
212
214
|
${cssFixtureFlagClasses()}
|
|
213
215
|
|
|
214
216
|
${cssFixtureSelectClasses()}
|
|
@@ -224,6 +226,7 @@ export function fixtureClassCatalog() {
|
|
|
224
226
|
'sw-fixture-group',
|
|
225
227
|
'sw-fixture-group-title',
|
|
226
228
|
'sw-fixture-list',
|
|
229
|
+
'sw-fixture-list-sides-fixed',
|
|
227
230
|
'sw-fixture-item',
|
|
228
231
|
'sw-fixture-row',
|
|
229
232
|
'sw-fixture-row-pressable',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { categoricalColorKeys } from '@scalewing/tokens';
|
|
1
|
+
import { categoricalColorKeys, controlScale } from '@scalewing/tokens';
|
|
2
2
|
export const glyphTileSizes = ['sm', 'md', 'lg'];
|
|
3
3
|
/** The most letters a tile holds, such as a three-letter crest ("NSL"). */
|
|
4
4
|
export const glyphTileLetterLimit = 3;
|
|
@@ -10,9 +10,13 @@ export const glyphTileLetterLimit = 3;
|
|
|
10
10
|
* the mix, 5.08:1). A glyph keeps the fill as it is.
|
|
11
11
|
*/
|
|
12
12
|
export const glyphTileLetterDarken = 0.25;
|
|
13
|
+
/*
|
|
14
|
+
* `md` reads the token, not the variable: a tile holds up to three letters,
|
|
15
|
+
* which a compact density's smaller xs control would crowd.
|
|
16
|
+
*/
|
|
13
17
|
const tileSide = {
|
|
14
18
|
sm: 'var(--sw-space-5)',
|
|
15
|
-
md:
|
|
19
|
+
md: `${controlScale.xs.minHeight}px`,
|
|
16
20
|
lg: 'var(--sw-space-6)',
|
|
17
21
|
};
|
|
18
22
|
function hueRules() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { breakpointQuery, breakpoints } from './breakpoints.js';
|
|
2
|
-
import { gridColumnCounts, gridColumnsBelowClass, gridColumnsClass, gridFitClass, gridFits, } from './css-grid.js';
|
|
2
|
+
import { gridColumnCounts, gridColumnsBelowClass, gridColumnsClass, gridFitClass, gridFits, gridRailClass, gridRails, } from './css-grid.js';
|
|
3
3
|
export const gridColumnSpans = gridColumnCounts;
|
|
4
4
|
export function gridSpanClass(span) {
|
|
5
5
|
return `sw-grid-span-${span}`;
|
|
@@ -22,8 +22,13 @@ export function cssGridSpanClasses() {
|
|
|
22
22
|
const spans = gridColumnSpans.map((span) => spanRule(`.${gridSpanClass(span)}`, span));
|
|
23
23
|
const caps = gridColumnCounts.flatMap((columns) => widerSpans(columns).map((span) => capRule(gridColumnsClass(columns), columns, span)));
|
|
24
24
|
// A fitted grid's column count follows its width, so a span there would
|
|
25
|
-
// add implicit columns: its children each take one column.
|
|
26
|
-
|
|
25
|
+
// add implicit columns: its children each take one column. A rail grid's
|
|
26
|
+
// two columns are not interchangeable, so the same holds there.
|
|
27
|
+
const oneColumnEach = [
|
|
28
|
+
...gridFits.map(gridFitClass),
|
|
29
|
+
...gridRails.map(gridRailClass),
|
|
30
|
+
];
|
|
31
|
+
const fitted = oneColumnEach.flatMap((gridClass) => widerSpans(1).map((span) => `:where(.${gridClass}) > .${gridSpanClass(span)} { grid-column: auto; }`));
|
|
27
32
|
// Below a breakpoint every span wider than one is restated for the phone
|
|
28
33
|
// column count, which also undoes a desktop cap when that count is larger.
|
|
29
34
|
const below = breakpoints.map((breakpoint) => `@media ${breakpointQuery('below', breakpoint)} {
|