@scalewing/react 1.39.0 → 1.40.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 (95) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +102 -0
  3. package/dist/calendar-month.d.ts +6 -0
  4. package/dist/calendar-month.js +15 -0
  5. package/dist/components/DateRangeButton.d.ts +65 -0
  6. package/dist/components/DateRangeButton.js +54 -0
  7. package/dist/components/ListGroup.d.ts +33 -0
  8. package/dist/components/ListGroup.js +19 -0
  9. package/dist/components/ListRow.d.ts +51 -0
  10. package/dist/components/ListRow.js +59 -0
  11. package/dist/components/NavCount.d.ts +21 -0
  12. package/dist/components/NavCount.js +23 -0
  13. package/dist/components/SectionNav.js +1 -8
  14. package/dist/components/StatTile.d.ts +11 -4
  15. package/dist/components/StatTile.js +11 -4
  16. package/dist/components/{date-field → calendar}/CalendarGrid.d.ts +17 -2
  17. package/dist/components/{date-field → calendar}/CalendarGrid.js +11 -3
  18. package/dist/components/calendar/CalendarPopover.d.ts +25 -0
  19. package/dist/components/calendar/CalendarPopover.js +22 -0
  20. package/dist/components/calendar/MonthStep.d.ts +16 -0
  21. package/dist/components/calendar/MonthStep.js +17 -0
  22. package/dist/components/calendar/use-grid-focus.d.ts +18 -0
  23. package/dist/components/calendar/use-grid-focus.js +35 -0
  24. package/dist/components/calendar/wrap-tab.d.ts +7 -0
  25. package/dist/components/calendar/wrap-tab.js +29 -0
  26. package/dist/components/date-field/CalendarDialog.js +9 -67
  27. package/dist/components/date-field/CalendarHeader.js +2 -11
  28. package/dist/components/date-range/DateRangePopover.d.ts +7 -0
  29. package/dist/components/date-range/DateRangePopover.js +17 -0
  30. package/dist/components/date-range/DateRangeSheet.d.ts +7 -0
  31. package/dist/components/date-range/DateRangeSheet.js +15 -0
  32. package/dist/components/date-range/RangeMonths.d.ts +28 -0
  33. package/dist/components/date-range/RangeMonths.js +50 -0
  34. package/dist/components/date-range/RangePresets.d.ts +16 -0
  35. package/dist/components/date-range/RangePresets.js +10 -0
  36. package/dist/components/date-range/RangeSummary.d.ts +15 -0
  37. package/dist/components/date-range/RangeSummary.js +11 -0
  38. package/dist/components/date-range/range-panel-props.d.ts +31 -0
  39. package/dist/components/date-range/range-panel-props.js +1 -0
  40. package/dist/components/date-range/use-range-popup.d.ts +21 -0
  41. package/dist/components/date-range/use-range-popup.js +53 -0
  42. package/dist/components/link-press.d.ts +7 -0
  43. package/dist/components/link-press.js +13 -0
  44. package/dist/components/list/ListRowAccessoryMark.d.ts +5 -0
  45. package/dist/components/list/ListRowAccessoryMark.js +7 -0
  46. package/dist/components/list/ListRowContent.d.ts +20 -0
  47. package/dist/components/list/ListRowContent.js +10 -0
  48. package/dist/components/list/list-row-container.d.ts +5 -0
  49. package/dist/components/list/list-row-container.js +6 -0
  50. package/dist/components/use-media-query.d.ts +5 -0
  51. package/dist/components/use-media-query.js +18 -0
  52. package/dist/css/chevron.d.ts +6 -0
  53. package/dist/css/chevron.js +14 -0
  54. package/dist/css/css-button-group-attached.js +8 -4
  55. package/dist/css/css-button-group-track.js +1 -1
  56. package/dist/css/css-checkbox.js +2 -2
  57. package/dist/css/css-components.js +9 -0
  58. package/dist/css/css-count-pill.d.ts +2 -0
  59. package/dist/css/css-count-pill.js +54 -0
  60. package/dist/css/css-date-field.js +3 -2
  61. package/dist/css/css-date-range-presets.d.ts +2 -0
  62. package/dist/css/css-date-range-presets.js +109 -0
  63. package/dist/css/css-date-range.d.ts +2 -0
  64. package/dist/css/css-date-range.js +197 -0
  65. package/dist/css/css-list-row.d.ts +2 -0
  66. package/dist/css/css-list-row.js +193 -0
  67. package/dist/css/css-list.d.ts +3 -0
  68. package/dist/css/css-list.js +85 -0
  69. package/dist/css/css-scroll-region.d.ts +18 -0
  70. package/dist/css/css-scroll-region.js +41 -0
  71. package/dist/css/css-section-nav-filled.js +2 -25
  72. package/dist/css/css-stat-tile.d.ts +7 -0
  73. package/dist/css/css-stat-tile.js +16 -0
  74. package/dist/css/css-steps.js +2 -2
  75. package/dist/css/css-table-bounded-edges.js +4 -27
  76. package/dist/css/range-band.d.ts +2 -0
  77. package/dist/css/range-band.js +7 -0
  78. package/dist/css/stacking.js +2 -2
  79. package/dist/css/stylesheet.js +6 -0
  80. package/dist/date-range-draft.d.ts +26 -0
  81. package/dist/date-range-draft.js +36 -0
  82. package/dist/date-range-input.d.ts +26 -0
  83. package/dist/date-range-input.js +58 -0
  84. package/dist/date-range-labels.d.ts +22 -0
  85. package/dist/date-range-labels.js +26 -0
  86. package/dist/date-range-view.d.ts +12 -0
  87. package/dist/date-range-view.js +29 -0
  88. package/dist/date-range-words.d.ts +11 -0
  89. package/dist/date-range-words.js +40 -0
  90. package/dist/index.d.ts +6 -1
  91. package/dist/index.js +6 -1
  92. package/dist/list-options.d.ts +70 -0
  93. package/dist/list-options.js +77 -0
  94. package/dist/styles.css +507 -26
  95. package/package.json +1 -1
@@ -0,0 +1,197 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { rangeBand, rangeBandHover } from './range-band.js';
4
+ import { cssDateRangePresetsClasses, dateRangePresetsClassCatalog, } from './css-date-range-presets.js';
5
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
6
+ const label = typographyVariants.label;
7
+ const caption = typographyVariants.caption;
8
+ /* The steps and days are one square control wide, as in DateField's calendar. */
9
+ const square = 'var(--sw-control-md-min-height)';
10
+ /* A day between a picked range's ends: selected, but drawn as the band. */
11
+ const between = ".sw-date-range-months .sw-date-field-day-in-range[aria-selected='true']:not(.sw-date-field-day-range-start, .sw-date-field-day-range-end)";
12
+ function buttonRules() {
13
+ return `.sw-date-range-button {
14
+ font-variant-numeric: tabular-nums;
15
+ white-space: nowrap;
16
+ }
17
+
18
+ .sw-date-range-button:not(.sw-button-md) .sw-date-field-glyph {
19
+ height: var(--sw-space-4);
20
+ width: var(--sw-space-4);
21
+ }
22
+
23
+ @media ${coarsePointerQuery} {
24
+ .sw-button.sw-date-range-button,
25
+ .sw-date-range-actions > .sw-button { min-height: ${touchTarget}; }
26
+ }`;
27
+ }
28
+ /*
29
+ * The popover is DateField's calendar surface with no padding of its own:
30
+ * the presets, then the months, then a footer under a hairline. Below
31
+ * `lg` the presets sit above the months; from `lg` beside them.
32
+ */
33
+ function popoverRules() {
34
+ return `.sw-date-field-calendar.sw-date-range-popover {
35
+ gap: 0;
36
+ padding: 0;
37
+ }
38
+
39
+ .sw-date-range-body {
40
+ display: flex;
41
+ flex-direction: column;
42
+ }
43
+
44
+ @media ${breakpointQuery('from', 'lg')} {
45
+ .sw-date-range-body { flex-direction: row; }
46
+ }
47
+
48
+ .sw-date-range-footer {
49
+ align-items: center;
50
+ border-top: 1px solid var(--sw-color-border);
51
+ contain: inline-size;
52
+ display: flex;
53
+ flex-wrap: wrap;
54
+ gap: var(--sw-space-2) var(--sw-space-4);
55
+ padding: var(--sw-space-3);
56
+ }
57
+
58
+ .sw-date-range-summary {
59
+ color: var(--sw-color-muted);
60
+ flex: 1 1 auto;
61
+ font-size: ${caption.fontSize}px;
62
+ line-height: ${caption.lineHeight}px;
63
+ min-width: 0;
64
+ }
65
+
66
+ .sw-date-range-status { display: inline; }
67
+
68
+ .sw-date-range-line {
69
+ color: var(--sw-color-text);
70
+ display: block;
71
+ font-size: ${label.fontSize}px;
72
+ font-weight: ${label.fontWeight};
73
+ line-height: ${label.lineHeight}px;
74
+ }
75
+
76
+ .sw-date-range-actions {
77
+ display: flex;
78
+ gap: var(--sw-space-2);
79
+ margin-inline-start: auto;
80
+ }`;
81
+ }
82
+ function monthRules() {
83
+ return `.sw-date-range-months {
84
+ display: flex;
85
+ gap: var(--sw-space-5);
86
+ justify-content: center;
87
+ padding: var(--sw-space-3);
88
+ }
89
+
90
+ .sw-date-range-month {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: var(--sw-space-1);
94
+ }
95
+
96
+ .sw-date-range-month-header {
97
+ align-items: center;
98
+ display: grid;
99
+ grid-template-columns: ${square} 1fr ${square};
100
+ min-height: ${square};
101
+ }
102
+
103
+ .sw-date-range-month-title {
104
+ font-size: ${label.fontSize}px;
105
+ font-weight: ${label.fontWeight};
106
+ grid-column: 2;
107
+ line-height: ${label.lineHeight}px;
108
+ text-align: center;
109
+ }
110
+
111
+ ${between} {
112
+ background: ${rangeBand};
113
+ border-color: transparent;
114
+ border-radius: 0;
115
+ color: var(--sw-color-text);
116
+ font-weight: inherit;
117
+ }
118
+
119
+ ${between}:hover:not([aria-disabled='true']) { background: ${rangeBandHover}; }
120
+
121
+ ${between}[aria-current='date'] { border-color: var(--sw-color-accent); }`;
122
+ }
123
+ /*
124
+ * Below \`md\` the range is a Dialog bottom sheet: the presets in a
125
+ * scrolling row, one month, and a footer with the hint over Cancel and
126
+ * Apply, the primary twice as wide.
127
+ */
128
+ function sheetRules() {
129
+ return `.sw-date-range-sheet .sw-date-range-months { padding: 0; }
130
+
131
+ .sw-date-range-sheet .sw-dialog-footer-actions {
132
+ flex: 1 1 100%;
133
+ margin-inline-start: 0;
134
+ }
135
+
136
+ .sw-date-range-sheet-footer {
137
+ display: flex;
138
+ flex: 1 1 100%;
139
+ flex-direction: column;
140
+ gap: var(--sw-space-2);
141
+ }
142
+
143
+ .sw-date-range-sheet-footer .sw-date-range-hint {
144
+ color: var(--sw-color-muted);
145
+ font-size: ${caption.fontSize}px;
146
+ line-height: ${caption.lineHeight}px;
147
+ }
148
+
149
+ .sw-date-range-sheet-footer .sw-date-range-actions {
150
+ display: grid;
151
+ grid-template-columns: 1fr 2fr;
152
+ margin-inline-start: 0;
153
+ }`;
154
+ }
155
+ function forcedColorRules() {
156
+ return `@media (forced-colors: active) {
157
+ .sw-date-range-footer { border-top-color: CanvasText; }
158
+ ${between},
159
+ ${between}:hover:not([aria-disabled='true']) {
160
+ background: Mark;
161
+ color: MarkText;
162
+ forced-color-adjust: none;
163
+ }
164
+ }`;
165
+ }
166
+ export function cssDateRangeClasses() {
167
+ return [
168
+ buttonRules(),
169
+ popoverRules(),
170
+ monthRules(),
171
+ sheetRules(),
172
+ cssDateRangePresetsClasses(),
173
+ forcedColorRules(),
174
+ ].join('\n\n');
175
+ }
176
+ export function dateRangeClassCatalog() {
177
+ return [
178
+ 'sw-date-range-button',
179
+ 'sw-date-range-popover',
180
+ 'sw-date-range-body',
181
+ 'sw-date-range-months',
182
+ 'sw-date-range-month',
183
+ 'sw-date-range-month-header',
184
+ 'sw-date-range-month-title',
185
+ 'sw-date-range-footer',
186
+ 'sw-date-range-summary',
187
+ 'sw-date-range-status',
188
+ 'sw-date-range-line',
189
+ 'sw-date-range-count',
190
+ 'sw-date-range-separator',
191
+ 'sw-date-range-hint',
192
+ 'sw-date-range-actions',
193
+ 'sw-date-range-sheet',
194
+ 'sw-date-range-sheet-footer',
195
+ ...dateRangePresetsClassCatalog(),
196
+ ];
197
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssListRowClasses(): string;
2
+ export declare function listRowClassCatalog(): string[];
@@ -0,0 +1,193 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { checkStrokeIn, chevronStrokeIn } from './chevron.js';
3
+ import { tableCellTones as listRowValueTones } from './css-table-ledger.js';
4
+ import { quietFill } from './quiet-fill.js';
5
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
6
+ const label = typographyVariants.label;
7
+ const body = typographyVariants.body;
8
+ const caption = typographyVariants.caption;
9
+ /* Centres a mark of `size` on the title's first line. */
10
+ function onTitleLine(size) {
11
+ return `calc((${label.lineHeight}px - ${size}) / 2)`;
12
+ }
13
+ /*
14
+ * A row: the leading mark, the title over its detail, the value over its
15
+ * detail at the end, then the chevron or check, each lined up with the
16
+ * title's first line. A pressable row is the whole button or link: a
17
+ * quiet fill under the pointer, the focus ring inside its edge, 44 px on a
18
+ * coarse pointer. Plain rows (inside a Card) have no inline padding.
19
+ */
20
+ function rowRules() {
21
+ return `.sw-list-row {
22
+ align-items: flex-start;
23
+ appearance: none;
24
+ background: transparent;
25
+ border: 0;
26
+ box-sizing: border-box;
27
+ color: var(--sw-color-text);
28
+ display: flex;
29
+ font-family: var(--sw-font-sans);
30
+ font-size: ${label.fontSize}px;
31
+ gap: var(--sw-space-3);
32
+ line-height: ${label.lineHeight}px;
33
+ margin: 0;
34
+ padding: var(--sw-space-3) var(--sw-space-4);
35
+ text-align: start;
36
+ text-decoration: none;
37
+ width: 100%;
38
+ }
39
+
40
+ .sw-list-group-plain .sw-list-row { padding-inline: 0; }
41
+
42
+ .sw-list-row-pressable { cursor: pointer; }
43
+
44
+ @media (hover: hover) {
45
+ .sw-list-row-pressable:hover:not(.sw-list-row-disabled) { background: ${quietFill}; }
46
+ }
47
+
48
+ .sw-list-row-pressable:active:not(.sw-list-row-disabled) { background: ${quietFill}; }
49
+
50
+ .sw-list-row-pressable:focus { outline: none; }
51
+
52
+ .sw-list-row-pressable:focus-visible {
53
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
54
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
55
+ }
56
+
57
+ /* A link row keeps the text colour and the inset ring over the canvas link rules. */
58
+ [data-theme] a.sw-list-row,
59
+ [data-theme] a.sw-list-row:hover {
60
+ color: var(--sw-color-text);
61
+ }
62
+
63
+ [data-theme] a.sw-list-row:focus-visible {
64
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
65
+ }
66
+
67
+ .sw-list-row-disabled {
68
+ cursor: not-allowed;
69
+ opacity: var(--sw-disabled-opacity);
70
+ }
71
+
72
+ @media ${coarsePointerQuery} {
73
+ .sw-list-row-pressable { min-height: ${touchTarget}; }
74
+ }
75
+
76
+ /* Forced colours drop the fill, so a hovered row is outlined instead, as a pressable table row is. */
77
+ @media (forced-colors: active) {
78
+ .sw-list-row-pressable:hover:not(.sw-list-row-disabled) {
79
+ outline: var(--sw-focus-ring-width) solid Highlight;
80
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
81
+ }
82
+ }`;
83
+ }
84
+ function partRules() {
85
+ const tones = listRowValueTones
86
+ .map((tone) => `.sw-list-row-value-${tone} { color: var(--sw-color-${tone}); }`)
87
+ .join('\n');
88
+ return `.sw-list-row-leading {
89
+ align-items: center;
90
+ display: flex;
91
+ flex: none;
92
+ height: ${label.lineHeight}px;
93
+ }
94
+
95
+ .sw-list-row-label,
96
+ .sw-list-row-end {
97
+ display: flex;
98
+ flex-direction: column;
99
+ min-width: 0;
100
+ }
101
+
102
+ .sw-list-row-label { flex: 1 1 auto; }
103
+
104
+ .sw-list-row-end {
105
+ align-items: flex-end;
106
+ flex: none;
107
+ max-width: 50%;
108
+ text-align: end;
109
+ }
110
+
111
+ .sw-list-row-title {
112
+ font-weight: ${label.fontWeight};
113
+ overflow-wrap: anywhere;
114
+ }
115
+
116
+ .sw-list-row-title-plain { font-weight: ${body.fontWeight}; }
117
+
118
+ .sw-list-row-title-danger { color: var(--sw-color-danger); }
119
+
120
+ .sw-list-row-detail,
121
+ .sw-list-row-value-detail {
122
+ color: var(--sw-color-muted);
123
+ font-size: ${caption.fontSize}px;
124
+ line-height: ${caption.lineHeight}px;
125
+ }
126
+
127
+ /* The label takes the wrap; the end keeps its words whole up to half the row. */
128
+ .sw-list-row-detail { overflow-wrap: anywhere; }
129
+
130
+ .sw-list-row-value,
131
+ .sw-list-row-value-detail { font-variant-numeric: tabular-nums; }
132
+
133
+ .sw-list-row-value { font-weight: ${label.fontWeight}; }
134
+
135
+ ${tones}`;
136
+ }
137
+ /*
138
+ * The chevron is the stroke Accordion and the menus share, pointing to the
139
+ * inline end; the check is the same stroke in the accent, a short side and
140
+ * a long one. Both are borders, so forced colours keep them.
141
+ */
142
+ function accessoryRules() {
143
+ return `.sw-list-row-chevron {
144
+ ${chevronStrokeIn('var(--sw-color-muted)')}
145
+ flex: none;
146
+ margin-block-start: ${onTitleLine('var(--sw-space-2)')};
147
+ margin-inline-end: var(--sw-space-1);
148
+ transform: rotate(-45deg);
149
+ }
150
+
151
+ .sw-list-row-chevron:dir(rtl) { transform: rotate(135deg); }
152
+
153
+ .sw-list-row-check {
154
+ ${checkStrokeIn('var(--sw-color-accent)')}
155
+ box-sizing: border-box;
156
+ flex: none;
157
+ height: var(--sw-space-3);
158
+ margin-block-start: ${onTitleLine('var(--sw-space-3)')};
159
+ margin-inline-end: var(--sw-space-1);
160
+ transform: rotate(45deg);
161
+ width: var(--sw-space-2);
162
+ }
163
+
164
+ @media (forced-colors: active) {
165
+ .sw-list-row-check { border-color: CanvasText; }
166
+ }`;
167
+ }
168
+ export function cssListRowClasses() {
169
+ return `${rowRules()}
170
+
171
+ ${partRules()}
172
+
173
+ ${accessoryRules()}`;
174
+ }
175
+ export function listRowClassCatalog() {
176
+ return [
177
+ 'sw-list-row',
178
+ 'sw-list-row-pressable',
179
+ 'sw-list-row-disabled',
180
+ 'sw-list-row-leading',
181
+ 'sw-list-row-label',
182
+ 'sw-list-row-title',
183
+ 'sw-list-row-title-plain',
184
+ 'sw-list-row-title-danger',
185
+ 'sw-list-row-detail',
186
+ 'sw-list-row-end',
187
+ 'sw-list-row-value',
188
+ ...listRowValueTones.map((tone) => `sw-list-row-value-${tone}`),
189
+ 'sw-list-row-value-detail',
190
+ 'sw-list-row-chevron',
191
+ 'sw-list-row-check',
192
+ ];
193
+ }
@@ -0,0 +1,3 @@
1
+ export declare const listRowPadBlock = "var(--sw-space-3)";
2
+ export declare function cssListClasses(): string;
3
+ export declare function listClassCatalog(): string[];
@@ -0,0 +1,85 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { cssListRowClasses, listRowClassCatalog } from './css-list-row.js';
3
+ import { blockEdgeShadeRules, scrollRegionRules } from './css-scroll-region.js';
4
+ const label = typographyVariants.label;
5
+ const caption = typographyVariants.caption;
6
+ /* A row's padding, shared by the row rules and the bounded list's height. */
7
+ export const listRowPadBlock = 'var(--sw-space-3)';
8
+ const scroll = '.sw-list-group-scroll';
9
+ /*
10
+ * A ListGroup is one list (`sw-list`) with a hairline between its rows:
11
+ * a bordered panel on the surface, or plain rows inside a surface that
12
+ * already has one, with a hairline above the first row too. A footer sits
13
+ * under the rows, outside any scroll, under a hairline.
14
+ */
15
+ function groupRules() {
16
+ return `.sw-list {
17
+ list-style: none;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+
22
+ .sw-list > .sw-list-item + .sw-list-item,
23
+ .sw-list-group-plain .sw-list > .sw-list-item:first-child,
24
+ .sw-list-group-footer {
25
+ border-top: 1px solid var(--sw-color-border);
26
+ }
27
+
28
+ .sw-list-group-panel {
29
+ background: var(--sw-color-surface);
30
+ border: 1px solid var(--sw-color-border);
31
+ border-radius: var(--sw-radius-md);
32
+ overflow: clip;
33
+ }`;
34
+ }
35
+ /*
36
+ * A bounded list (\`maxHeight\`) is as tall as its row count
37
+ * (\`--sw-list-max-rows\`) of rows with a title and one detail line: the
38
+ * padding twice, the title's line, the detail's line and the hairline.
39
+ * Taller rows show fewer at a time. Its region is the shared scroll
40
+ * region (a named keyboard stop); while rows are past its top or bottom,
41
+ * that edge draws the scroll shade a spacing step 5 deep, a background
42
+ * layer under the rows. Forced colours draw no shade; printed, every row
43
+ * shows.
44
+ */
45
+ function boundedRules() {
46
+ const top = `${scroll}.sw-scroll-more-top`;
47
+ const bottom = `${scroll}.sw-scroll-more-bottom`;
48
+ return `${scrollRegionRules(scroll)}
49
+
50
+ ${scroll} {
51
+ max-height: calc(var(--sw-list-max-rows) * (2 * ${listRowPadBlock} + ${label.lineHeight}px + ${caption.lineHeight}px + 1px));
52
+ }
53
+
54
+ ${blockEdgeShadeRules({ top, bottom, both: `${top}.sw-scroll-more-bottom` })}
55
+
56
+ @media (forced-colors: active) {
57
+ .sw-list > .sw-list-item + .sw-list-item,
58
+ .sw-list-group-plain .sw-list > .sw-list-item:first-child,
59
+ .sw-list-group-footer,
60
+ .sw-list-group-panel { border-color: CanvasText; }
61
+ }
62
+
63
+ @media print {
64
+ ${scroll} { max-height: none; }
65
+ }`;
66
+ }
67
+ export function cssListClasses() {
68
+ return `${groupRules()}
69
+
70
+ ${boundedRules()}
71
+
72
+ ${cssListRowClasses()}`;
73
+ }
74
+ export function listClassCatalog() {
75
+ return [
76
+ 'sw-list',
77
+ 'sw-list-item',
78
+ 'sw-list-group',
79
+ 'sw-list-group-panel',
80
+ 'sw-list-group-plain',
81
+ 'sw-list-group-scroll',
82
+ 'sw-list-group-footer',
83
+ ...listRowClassCatalog(),
84
+ ];
85
+ }
@@ -23,6 +23,24 @@ export declare function scrollShadeRules(selector: string): string;
23
23
  * and the DenominationGrid strip share it.
24
24
  */
25
25
  export declare function scrollRegionRules(selector: string): string;
26
+ /** The scroll shade fading away from an edge, a gradient toward `direction`. */
27
+ export declare function blockShade(direction: 'bottom' | 'top'): string;
28
+ /** The selectors of a region scrolled past its top, its bottom, or both. */
29
+ export type BlockEdgeSelectors = {
30
+ top: string;
31
+ bottom: string;
32
+ both: string;
33
+ };
34
+ /**
35
+ * The shade a region of bounded height draws on its own top or bottom
36
+ * edge while `ScrollRegion` marks rows past it (`sw-scroll-more-top`,
37
+ * `sw-scroll-more-bottom`): a background layer a spacing step 5 deep,
38
+ * fixed to the box and under the rows. A caller narrows the selectors to
39
+ * the edges nothing pinned covers. Forced colours draw no shade; the
40
+ * system's scrollbar is the cue. A bounded Table and a bounded ListGroup
41
+ * share it.
42
+ */
43
+ export declare function blockEdgeShadeRules({ top, bottom, both, }: BlockEdgeSelectors): string;
26
44
  export declare function scrollRegionClassCatalog(): string[];
27
45
  /**
28
46
  * A region whose start column is pinned (sticky, on an opaque fill) hides
@@ -60,6 +60,47 @@ ${selector}:focus-visible {
60
60
 
61
61
  ${scrollShadeRules(selector)}`;
62
62
  }
63
+ /** The scroll shade fading away from an edge, a gradient toward `direction`. */
64
+ export function blockShade(direction) {
65
+ return `linear-gradient(to ${direction}, ${scrollShadeColor}, transparent)`;
66
+ }
67
+ /**
68
+ * The shade a region of bounded height draws on its own top or bottom
69
+ * edge while `ScrollRegion` marks rows past it (`sw-scroll-more-top`,
70
+ * `sw-scroll-more-bottom`): a background layer a spacing step 5 deep,
71
+ * fixed to the box and under the rows. A caller narrows the selectors to
72
+ * the edges nothing pinned covers. Forced colours draw no shade; the
73
+ * system's scrollbar is the cue. A bounded Table and a bounded ListGroup
74
+ * share it.
75
+ */
76
+ export function blockEdgeShadeRules({ top, bottom, both, }) {
77
+ return `${top},
78
+ ${bottom} {
79
+ background-repeat: no-repeat;
80
+ background-size: 100% var(--sw-space-5);
81
+ }
82
+
83
+ ${top} {
84
+ background-image: ${blockShade('bottom')};
85
+ background-position: top;
86
+ }
87
+
88
+ ${bottom} {
89
+ background-image: ${blockShade('top')};
90
+ background-position: bottom;
91
+ }
92
+
93
+ ${both} {
94
+ background-image: ${blockShade('bottom')}, ${blockShade('top')};
95
+ background-position: top, bottom;
96
+ }
97
+
98
+ @media (forced-colors: active) {
99
+ ${top},
100
+ ${bottom},
101
+ ${both} { background-image: none; }
102
+ }`;
103
+ }
63
104
  export function scrollRegionClassCatalog() {
64
105
  return [
65
106
  'sw-scroll-more-start',
@@ -1,6 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
2
  import { coarsePointerQuery, touchTarget } from './touch-target.js';
3
- const caption = typographyVariants.caption;
4
3
  const label = typographyVariants.label;
5
4
  /** How a section navigation marks its current item. */
6
5
  export const sectionNavAppearances = ['underline', 'filled'];
@@ -18,10 +17,7 @@ export function assertSectionNavAppearance(appearance) {
18
17
  * hovered link. A 2 px transparent border keeps room for forced colours,
19
18
  * where the current link's border turns Highlight.
20
19
  *
21
- * A count after a label (a number waiting there) is a filled pill: the
22
- * danger colour with its on-colour figures (4.5:1 in every palette, as a
23
- * danger Button's label), caption size at weight 700, a space-5 less
24
- * space-1 (20) high and at least as wide. Forced colours draw its edge.
20
+ * A count after a label is the shared count pill (`css-count-pill.ts`).
25
21
  */
26
22
  export function cssSectionNavFilledClasses() {
27
23
  // Two classes, so every filled rule outranks the side list's (one class
@@ -48,33 +44,14 @@ export function cssSectionNavFilledClasses() {
48
44
  font-weight: ${label.fontWeight};
49
45
  }
50
46
 
51
- [data-theme] .sw-section-nav-count {
52
- align-items: center;
53
- background: var(--sw-color-danger);
54
- border-radius: var(--sw-radius-pill);
55
- box-sizing: border-box;
56
- color: var(--sw-color-onDanger);
57
- display: inline-flex;
58
- font-size: ${caption.fontSize}px;
59
- font-variant-numeric: tabular-nums;
60
- font-weight: 700;
61
- justify-content: center;
62
- letter-spacing: ${caption.letterSpacing}px;
63
- line-height: 1;
64
- min-height: calc(var(--sw-space-5) - var(--sw-space-1));
65
- min-width: calc(var(--sw-space-5) - var(--sw-space-1));
66
- padding-inline: var(--sw-space-1);
67
- }
68
-
69
47
  @media ${coarsePointerQuery} {
70
48
  [data-theme] ${filled} .sw-section-nav-link { min-height: ${touchTarget}; }
71
49
  }
72
50
 
73
51
  @media (forced-colors: active) {
74
52
  [data-theme] ${filled} .sw-section-nav-link[aria-current] { border-color: Highlight; }
75
- [data-theme] .sw-section-nav-count { border: 1px solid CanvasText; }
76
53
  }`;
77
54
  }
78
55
  export function sectionNavFilledClassCatalog() {
79
- return ['sw-section-nav-filled', 'sw-section-nav-count'];
56
+ return ['sw-section-nav-filled'];
80
57
  }
@@ -1,5 +1,12 @@
1
1
  export declare const statTileTones: readonly ["default", "accent", "success", "danger", "warning"];
2
2
  export type StatTileTone = (typeof statTileTones)[number];
3
+ /**
4
+ * `card` (the default) is a tile on the surface with its own hairline and
5
+ * radius; `plain` has no surface, edge or padding, for figures inside a
6
+ * card that already has them, such as a row of answers divided by hairlines.
7
+ */
8
+ export declare const statTileAppearances: readonly ["card", "plain"];
9
+ export type StatTileAppearance = (typeof statTileAppearances)[number];
3
10
  /** The figure tile: a glyph circle before a label, a tabular value and a caption. Text colours come from `Text color`. */
4
11
  export declare function cssStatTileClasses(): string;
5
12
  export declare function statTileClassCatalog(): string[];
@@ -5,6 +5,12 @@ export const statTileTones = [
5
5
  'danger',
6
6
  'warning',
7
7
  ];
8
+ /**
9
+ * `card` (the default) is a tile on the surface with its own hairline and
10
+ * radius; `plain` has no surface, edge or padding, for figures inside a
11
+ * card that already has them, such as a row of answers divided by hairlines.
12
+ */
13
+ export const statTileAppearances = ['card', 'plain'];
8
14
  /** The figure tile: a glyph circle before a label, a tabular value and a caption. Text colours come from `Text color`. */
9
15
  export function cssStatTileClasses() {
10
16
  return `.sw-stat-tile {
@@ -44,6 +50,15 @@ export function cssStatTileClasses() {
44
50
  overflow-wrap: anywhere;
45
51
  }
46
52
 
53
+ /* A plain tile is its figure alone, top-aligned, inside the card around it. */
54
+ .sw-stat-tile-plain {
55
+ align-items: flex-start;
56
+ background: transparent;
57
+ border: 0;
58
+ border-radius: 0;
59
+ padding: 0;
60
+ }
61
+
47
62
  .sw-stat-tile-primary {
48
63
  background: var(--sw-color-accent);
49
64
  border-color: transparent;
@@ -63,6 +78,7 @@ export function cssStatTileClasses() {
63
78
  export function statTileClassCatalog() {
64
79
  return [
65
80
  'sw-stat-tile',
81
+ 'sw-stat-tile-plain',
66
82
  'sw-stat-tile-primary',
67
83
  'sw-stat-tile-glyph',
68
84
  'sw-stat-tile-body',
@@ -1,4 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { checkStrokeIn } from './chevron.js';
2
3
  const body = typographyVariants.body;
3
4
  const caption = typographyVariants.caption;
4
5
  const label = typographyVariants.label;
@@ -29,8 +30,7 @@ function markerRules() {
29
30
 
30
31
  /* Private chrome: a check drawn with two borders, as Checkbox's. */
31
32
  .sw-steps-item-done .sw-steps-marker::after {
32
- border-bottom: 2px solid var(--sw-color-surface);
33
- border-right: 2px solid var(--sw-color-surface);
33
+ ${checkStrokeIn('var(--sw-color-surface)')}
34
34
  content: '';
35
35
  height: var(--sw-space-3);
36
36
  margin-top: calc(-1 * var(--sw-space-1) / 2);