@scalewing/react 1.47.0 → 1.49.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 (135) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +215 -6
  3. package/dist/command-palette.test-support.d.ts +21 -0
  4. package/dist/command-palette.test-support.js +55 -0
  5. package/dist/components/AppHeader.d.ts +3 -0
  6. package/dist/components/AppShell.d.ts +4 -3
  7. package/dist/components/AppShell.js +24 -7
  8. package/dist/components/Band.d.ts +30 -0
  9. package/dist/components/Band.js +23 -0
  10. package/dist/components/ButtonLink.d.ts +33 -0
  11. package/dist/components/ButtonLink.js +27 -0
  12. package/dist/components/CommandPalette.d.ts +12 -0
  13. package/dist/components/CommandPalette.js +143 -0
  14. package/dist/components/CommandPaletteTrigger.d.ts +9 -0
  15. package/dist/components/CommandPaletteTrigger.js +16 -0
  16. package/dist/components/ContextSwitcher.d.ts +12 -0
  17. package/dist/components/ContextSwitcher.js +62 -0
  18. package/dist/components/EmptyState.d.ts +23 -0
  19. package/dist/components/EmptyState.js +40 -0
  20. package/dist/components/FixtureGroup.d.ts +9 -0
  21. package/dist/components/FixtureGroup.js +16 -0
  22. package/dist/components/FixtureRow.d.ts +10 -0
  23. package/dist/components/FixtureRow.js +46 -0
  24. package/dist/components/GlyphTile.d.ts +9 -3
  25. package/dist/components/GlyphTile.js +15 -4
  26. package/dist/components/Grid.d.ts +11 -3
  27. package/dist/components/Grid.js +13 -4
  28. package/dist/components/Hero.d.ts +26 -0
  29. package/dist/components/Hero.js +23 -0
  30. package/dist/components/SiteFooter.d.ts +12 -0
  31. package/dist/components/SiteFooter.js +23 -0
  32. package/dist/components/SiteHeader.d.ts +14 -0
  33. package/dist/components/SiteHeader.js +24 -0
  34. package/dist/components/Skeleton.d.ts +22 -0
  35. package/dist/components/Skeleton.js +30 -0
  36. package/dist/components/Stepper.d.ts +11 -1
  37. package/dist/components/Stepper.js +10 -8
  38. package/dist/components/Text.js +3 -0
  39. package/dist/components/app-shell/AppShellBar.d.ts +23 -0
  40. package/dist/components/app-shell/AppShellBar.js +25 -0
  41. package/dist/components/app-shell/types.d.ts +36 -1
  42. package/dist/components/app-shell/validate.d.ts +4 -3
  43. package/dist/components/app-shell/validate.js +21 -3
  44. package/dist/components/command-palette/PaletteList.d.ts +20 -0
  45. package/dist/components/command-palette/PaletteList.js +21 -0
  46. package/dist/components/command-palette/shortcut.d.ts +19 -0
  47. package/dist/components/command-palette/shortcut.js +75 -0
  48. package/dist/components/command-palette/types.d.ts +72 -0
  49. package/dist/components/command-palette/types.js +2 -0
  50. package/dist/components/command-palette/validate.d.ts +10 -0
  51. package/dist/components/command-palette/validate.js +50 -0
  52. package/dist/components/context-switcher/SwitcherOption.d.ts +17 -0
  53. package/dist/components/context-switcher/SwitcherOption.js +20 -0
  54. package/dist/components/context-switcher/SwitcherPopup.d.ts +31 -0
  55. package/dist/components/context-switcher/SwitcherPopup.js +111 -0
  56. package/dist/components/context-switcher/options.d.ts +33 -0
  57. package/dist/components/context-switcher/options.js +60 -0
  58. package/dist/components/context-switcher/types.d.ts +96 -0
  59. package/dist/components/context-switcher/types.js +1 -0
  60. package/dist/components/context-switcher/use-switcher-list.d.ts +29 -0
  61. package/dist/components/context-switcher/use-switcher-list.js +93 -0
  62. package/dist/components/context-switcher/validate.d.ts +8 -0
  63. package/dist/components/context-switcher/validate.js +94 -0
  64. package/dist/components/fixture/fixture-list-context.d.ts +4 -0
  65. package/dist/components/fixture/fixture-list-context.js +9 -0
  66. package/dist/components/fixture/types.d.ts +50 -0
  67. package/dist/components/fixture/types.js +1 -0
  68. package/dist/components/fixture/validate.d.ts +8 -0
  69. package/dist/components/fixture/validate.js +36 -0
  70. package/dist/components/site-footer/types.d.ts +31 -0
  71. package/dist/components/site-footer/types.js +1 -0
  72. package/dist/components/site-footer/validate.d.ts +7 -0
  73. package/dist/components/site-footer/validate.js +22 -0
  74. package/dist/components/site-header/SiteHeaderLinks.d.ts +7 -0
  75. package/dist/components/site-header/SiteHeaderLinks.js +7 -0
  76. package/dist/components/site-header/types.d.ts +38 -0
  77. package/dist/components/site-header/types.js +1 -0
  78. package/dist/components/site-header/use-menu-dismiss.d.ts +8 -0
  79. package/dist/components/site-header/use-menu-dismiss.js +62 -0
  80. package/dist/components/site-header/validate.d.ts +7 -0
  81. package/dist/components/site-header/validate.js +17 -0
  82. package/dist/components/stepper/StepperCountFrame.d.ts +15 -0
  83. package/dist/components/stepper/StepperCountFrame.js +25 -0
  84. package/dist/components/use-element-height.d.ts +10 -0
  85. package/dist/components/use-element-height.js +35 -0
  86. package/dist/context-switcher.test-support.d.ts +9 -0
  87. package/dist/context-switcher.test-support.js +41 -0
  88. package/dist/css/css-app-shell-page-bar.d.ts +3 -0
  89. package/dist/css/css-app-shell-page-bar.js +81 -0
  90. package/dist/css/css-app-shell.js +6 -1
  91. package/dist/css/css-band.d.ts +10 -0
  92. package/dist/css/css-band.js +101 -0
  93. package/dist/css/css-button-link.d.ts +3 -0
  94. package/dist/css/css-button-link.js +48 -0
  95. package/dist/css/css-command-palette-key.d.ts +1 -0
  96. package/dist/css/css-command-palette-key.js +33 -0
  97. package/dist/css/css-command-palette-trigger.d.ts +2 -0
  98. package/dist/css/css-command-palette-trigger.js +88 -0
  99. package/dist/css/css-command-palette.d.ts +2 -0
  100. package/dist/css/css-command-palette.js +209 -0
  101. package/dist/css/css-components.js +30 -0
  102. package/dist/css/css-context-switcher-trigger.d.ts +15 -0
  103. package/dist/css/css-context-switcher-trigger.js +175 -0
  104. package/dist/css/css-context-switcher.d.ts +4 -0
  105. package/dist/css/css-context-switcher.js +219 -0
  106. package/dist/css/css-count-pill.d.ts +2 -2
  107. package/dist/css/css-count-pill.js +11 -3
  108. package/dist/css/css-empty-state.d.ts +2 -0
  109. package/dist/css/css-empty-state.js +77 -0
  110. package/dist/css/css-fixture.d.ts +5 -0
  111. package/dist/css/css-fixture.js +260 -0
  112. package/dist/css/css-glyph-tile.d.ts +2 -0
  113. package/dist/css/css-glyph-tile.js +29 -1
  114. package/dist/css/css-grid-span.js +5 -2
  115. package/dist/css/css-grid.d.ts +13 -0
  116. package/dist/css/css-grid.js +17 -1
  117. package/dist/css/css-hero.d.ts +2 -0
  118. package/dist/css/css-hero.js +62 -0
  119. package/dist/css/css-site-footer.d.ts +2 -0
  120. package/dist/css/css-site-footer.js +152 -0
  121. package/dist/css/css-site-header.d.ts +2 -0
  122. package/dist/css/css-site-header.js +262 -0
  123. package/dist/css/css-skeleton.d.ts +10 -0
  124. package/dist/css/css-skeleton.js +110 -0
  125. package/dist/css/css-stepper-unit.d.ts +9 -0
  126. package/dist/css/css-stepper-unit.js +102 -0
  127. package/dist/css/css-stepper.js +5 -1
  128. package/dist/css/stacking.js +7 -4
  129. package/dist/css/stylesheet.js +20 -0
  130. package/dist/index.d.ts +18 -2
  131. package/dist/index.js +13 -1
  132. package/dist/stepper-count.d.ts +8 -0
  133. package/dist/stepper-count.js +17 -0
  134. package/dist/styles.css +2202 -702
  135. package/package.json +2 -2
@@ -0,0 +1,260 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { chevronStrokeIn } from './chevron.js';
4
+ import { quietFill } from './quiet-fill.js';
5
+ import { touchTarget } from './touch-target.js';
6
+ const caption = typographyVariants.caption;
7
+ const label = typographyVariants.label;
8
+ export const fixtureCenterKinds = ['time', 'score'];
9
+ const belowMd = breakpointQuery('below', 'md');
10
+ /*
11
+ * The run: one grid whose columns are the home side, the centre, the away
12
+ * side, the end (details and status) and the chevron. Each item and its
13
+ * row are subgrids of it, so every row's centre and end line up down the
14
+ * run. The space between the columns is the centre's, the end's and the
15
+ * chevron's own margin, so a run with no end or chevron keeps no gap for
16
+ * them.
17
+ */
18
+ function listRules() {
19
+ return `.sw-fixture-group {
20
+ display: flex;
21
+ flex-direction: column;
22
+ }
23
+
24
+ .sw-fixture-group-title {
25
+ color: var(--sw-color-muted);
26
+ font-family: var(--sw-font-sans);
27
+ font-size: ${caption.fontSize}px;
28
+ font-weight: 600;
29
+ letter-spacing: ${caption.letterSpacing}px;
30
+ line-height: ${caption.lineHeight}px;
31
+ padding-block: var(--sw-space-2) var(--sw-space-1);
32
+ }
33
+
34
+ .sw-fixture-list {
35
+ display: grid;
36
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto auto;
37
+ list-style: none;
38
+ margin: 0;
39
+ padding: 0;
40
+ }
41
+
42
+ .sw-fixture-item,
43
+ .sw-fixture-row {
44
+ display: grid;
45
+ grid-column: 1 / -1;
46
+ grid-template-columns: subgrid;
47
+ }
48
+
49
+ .sw-fixture-item {
50
+ border-top: 1px solid var(--sw-color-border);
51
+ }`;
52
+ }
53
+ /*
54
+ * A row: the md control height (44 px), its parts centred on one line a
55
+ * space-3 apart; a link or button row is the whole target, the text
56
+ * colour, the quiet fill on hover and an inset accent ring, with the
57
+ * shared chevron at the end. The sides' names are label type at weight 500
58
+ * and wrap; the home side ends at the centre (its mark nearest it), the
59
+ * away side starts from it, mirrored in right-to-left.
60
+ */
61
+ function rowRules() {
62
+ return `.sw-fixture-row {
63
+ align-items: center;
64
+ appearance: none;
65
+ background: transparent;
66
+ border: 0;
67
+ box-sizing: border-box;
68
+ color: var(--sw-color-text);
69
+ font-family: var(--sw-font-sans);
70
+ font-size: ${label.fontSize}px;
71
+ letter-spacing: ${label.letterSpacing}px;
72
+ line-height: ${label.lineHeight}px;
73
+ margin: 0;
74
+ min-height: ${touchTarget};
75
+ padding: var(--sw-space-2);
76
+ row-gap: var(--sw-space-1);
77
+ text-align: start;
78
+ text-decoration: none;
79
+ width: 100%;
80
+ }
81
+
82
+ .sw-fixture-row-pressable { cursor: pointer; }
83
+
84
+ @media (hover: hover) {
85
+ .sw-fixture-row-pressable:hover { background: ${quietFill}; }
86
+ }
87
+
88
+ .sw-fixture-row-pressable:focus { outline: none; }
89
+
90
+ .sw-fixture-row-pressable:focus-visible {
91
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
92
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
93
+ }
94
+
95
+ [data-theme] a.sw-fixture-row,
96
+ [data-theme] a.sw-fixture-row:hover {
97
+ color: var(--sw-color-text);
98
+ }
99
+
100
+ [data-theme] a.sw-fixture-row:focus-visible {
101
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
102
+ }
103
+
104
+ .sw-fixture-team {
105
+ align-items: center;
106
+ display: flex;
107
+ gap: var(--sw-space-2);
108
+ min-width: 0;
109
+ }
110
+
111
+ .sw-fixture-team-home {
112
+ grid-column: 1;
113
+ justify-content: flex-end;
114
+ text-align: end;
115
+ }
116
+
117
+ .sw-fixture-team-away {
118
+ grid-column: 3;
119
+ }
120
+
121
+ .sw-fixture-name {
122
+ font-weight: 500;
123
+ min-width: 0;
124
+ overflow-wrap: anywhere;
125
+ }
126
+
127
+ .sw-fixture-mark {
128
+ display: inline-flex;
129
+ flex: none;
130
+ }
131
+
132
+ .sw-fixture-center {
133
+ font-variant-numeric: tabular-nums;
134
+ grid-column: 2;
135
+ margin-inline: var(--sw-space-3);
136
+ justify-self: center;
137
+ white-space: nowrap;
138
+ }
139
+
140
+ .sw-fixture-center-time {
141
+ background: var(--sw-color-subtle);
142
+ border-radius: var(--sw-radius-sm);
143
+ font-size: ${caption.fontSize}px;
144
+ font-weight: 600;
145
+ letter-spacing: ${caption.letterSpacing}px;
146
+ line-height: ${caption.lineHeight}px;
147
+ padding: calc(var(--sw-space-1) / 2) var(--sw-space-2);
148
+ }
149
+
150
+ .sw-fixture-center-score {
151
+ font-weight: 700;
152
+ }
153
+
154
+ .sw-fixture-end {
155
+ align-items: center;
156
+ color: var(--sw-color-muted);
157
+ display: flex;
158
+ flex-wrap: wrap;
159
+ font-size: ${caption.fontSize}px;
160
+ gap: var(--sw-space-2);
161
+ grid-column: 4;
162
+ justify-content: flex-end;
163
+ letter-spacing: ${caption.letterSpacing}px;
164
+ margin-inline-start: var(--sw-space-3);
165
+ line-height: ${caption.lineHeight}px;
166
+ }
167
+
168
+ .sw-fixture-chevron {
169
+ ${chevronStrokeIn('var(--sw-color-muted)')}
170
+ grid-column: 5;
171
+ justify-self: end;
172
+ margin-inline: var(--sw-space-3) var(--sw-space-1);
173
+ transform: rotate(-45deg);
174
+ }
175
+
176
+ .sw-fixture-chevron:dir(rtl) { transform: rotate(135deg); }
177
+
178
+ @media (forced-colors: active) {
179
+ .sw-fixture-row-pressable:hover {
180
+ outline: var(--sw-focus-ring-width) solid Highlight;
181
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
182
+ }
183
+ .sw-fixture-center-time { border: 1px solid CanvasText; }
184
+ .sw-fixture-chevron { border-color: CanvasText; }
185
+ }`;
186
+ }
187
+ /*
188
+ * Below `md`: three columns and the chevron, the end on a second line
189
+ * from the start. After the row rules, so it wins over their placement.
190
+ */
191
+ function narrowRules() {
192
+ return `@media ${belowMd} {
193
+ .sw-fixture-list {
194
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
195
+ }
196
+
197
+ .sw-fixture-end {
198
+ grid-column: 1 / 4;
199
+ grid-row: 2;
200
+ justify-content: flex-start;
201
+ margin-inline-start: 0;
202
+ }
203
+
204
+ .sw-fixture-chevron {
205
+ grid-column: 4;
206
+ grid-row: 1 / span 2;
207
+ }
208
+ }`;
209
+ }
210
+ /*
211
+ * Without subgrid (Chrome before 117, Safari before 16): each row is its
212
+ * own grid of the same columns, so a row still reads in order and the
213
+ * centres sit near the middle, but they do not line up down the run.
214
+ */
215
+ function fallbackRules() {
216
+ return `@supports not (grid-template-columns: subgrid) {
217
+ .sw-fixture-item {
218
+ display: block;
219
+ }
220
+
221
+ .sw-fixture-row {
222
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto auto;
223
+ }
224
+
225
+ @media ${belowMd} {
226
+ .sw-fixture-row {
227
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto;
228
+ }
229
+ }
230
+ }`;
231
+ }
232
+ /* FixtureGroup and FixtureRow (see components/FixtureRow.tsx). */
233
+ export function cssFixtureClasses() {
234
+ return `${listRules()}
235
+
236
+ ${rowRules()}
237
+
238
+ ${narrowRules()}
239
+
240
+ ${fallbackRules()}`;
241
+ }
242
+ export function fixtureClassCatalog() {
243
+ return [
244
+ 'sw-fixture-group',
245
+ 'sw-fixture-group-title',
246
+ 'sw-fixture-list',
247
+ 'sw-fixture-item',
248
+ 'sw-fixture-row',
249
+ 'sw-fixture-row-pressable',
250
+ 'sw-fixture-team',
251
+ 'sw-fixture-team-home',
252
+ 'sw-fixture-team-away',
253
+ 'sw-fixture-name',
254
+ 'sw-fixture-mark',
255
+ 'sw-fixture-center',
256
+ ...fixtureCenterKinds.map((kind) => `sw-fixture-center-${kind}`),
257
+ 'sw-fixture-end',
258
+ 'sw-fixture-chevron',
259
+ ];
260
+ }
@@ -1,5 +1,7 @@
1
1
  export declare const glyphTileSizes: readonly ["sm", "md", "lg"];
2
2
  /** `sm` 24, `md` 28 (the xs control height), `lg` 32. */
3
3
  export type GlyphTileSize = (typeof glyphTileSizes)[number];
4
+ /** The most letters a tile holds, such as a three-letter crest ("NSL"). */
5
+ export declare const glyphTileLetterLimit = 3;
4
6
  export declare function cssGlyphTileClasses(): string;
5
7
  export declare function glyphTileClassCatalog(): string[];
@@ -1,5 +1,7 @@
1
1
  import { categoricalColorKeys } from '@scalewing/tokens';
2
2
  export const glyphTileSizes = ['sm', 'md', 'lg'];
3
+ /** The most letters a tile holds, such as a three-letter crest ("NSL"). */
4
+ export const glyphTileLetterLimit = 3;
3
5
  const tileSide = {
4
6
  sm: 'var(--sw-space-5)',
5
7
  md: 'var(--sw-control-xs-min-height)',
@@ -12,7 +14,7 @@ function hueRules() {
12
14
  }
13
15
  function sizeRules() {
14
16
  return Object.entries(tileSide)
15
- .map(([size, side]) => `.sw-glyph-tile-${size} { height: ${side}; width: ${side}; }`)
17
+ .map(([size, side]) => `.sw-glyph-tile-${size} { --sw-glyph-tile-side: ${side}; height: ${side}; width: ${side}; }`)
16
18
  .join('\n');
17
19
  }
18
20
  /*
@@ -40,6 +42,28 @@ export function cssGlyphTileClasses() {
40
42
  width: var(--sw-space-4);
41
43
  }
42
44
 
45
+ /*
46
+ * Letters in place of a glyph: one line, centred, weight 600, the line as
47
+ * tall as the letters, sized from the tile's side rather than the text
48
+ * round it, so they fit at every size: one letter at half the side, two
49
+ * at 0.42 of it, three at a third, tightened a little, so even a wide
50
+ * trio ("WMW") fits inside the smallest tile.
51
+ */
52
+ .sw-glyph-tile-text {
53
+ font-weight: 600;
54
+ line-height: 1;
55
+ white-space: nowrap;
56
+ }
57
+
58
+ .sw-glyph-tile-text-1 { font-size: calc(var(--sw-glyph-tile-side) / 2); }
59
+
60
+ .sw-glyph-tile-text-2 { font-size: calc(var(--sw-glyph-tile-side) * 0.42); }
61
+
62
+ .sw-glyph-tile-text-3 {
63
+ font-size: calc(var(--sw-glyph-tile-side) / 3);
64
+ letter-spacing: -0.02em;
65
+ }
66
+
43
67
  ${hueRules()}
44
68
 
45
69
  ${sizeRules()}
@@ -53,5 +77,9 @@ export function glyphTileClassCatalog() {
53
77
  'sw-glyph-tile',
54
78
  ...categoricalColorKeys.map((key) => `sw-glyph-tile-${key}`),
55
79
  ...glyphTileSizes.map((size) => `sw-glyph-tile-${size}`),
80
+ 'sw-glyph-tile-text',
81
+ 'sw-glyph-tile-text-1',
82
+ 'sw-glyph-tile-text-2',
83
+ 'sw-glyph-tile-text-3',
56
84
  ];
57
85
  }
@@ -1,5 +1,5 @@
1
1
  import { breakpointQuery, breakpoints } from './breakpoints.js';
2
- import { gridColumnCounts, gridColumnsBelowClass, gridColumnsClass, } from './css-grid.js';
2
+ import { gridColumnCounts, gridColumnsBelowClass, gridColumnsClass, gridFitClass, gridFits, } from './css-grid.js';
3
3
  export const gridColumnSpans = gridColumnCounts;
4
4
  export function gridSpanClass(span) {
5
5
  return `sw-grid-span-${span}`;
@@ -21,6 +21,9 @@ function widerSpans(columns) {
21
21
  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
+ // 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
+ const fitted = gridFits.flatMap((fit) => widerSpans(1).map((span) => `:where(.${gridFitClass(fit)}) > .${gridSpanClass(span)} { grid-column: auto; }`));
24
27
  // Below a breakpoint every span wider than one is restated for the phone
25
28
  // column count, which also undoes a desktop cap when that count is larger.
26
29
  const below = breakpoints.map((breakpoint) => `@media ${breakpointQuery('below', breakpoint)} {
@@ -28,7 +31,7 @@ ${gridColumnCounts
28
31
  .flatMap((columns) => widerSpans(1).map((span) => ` ${capRule(gridColumnsBelowClass(breakpoint, columns), columns, span)}`))
29
32
  .join('\n')}
30
33
  }`);
31
- return [...spans, ...caps, ...below].join('\n');
34
+ return [...spans, ...caps, ...fitted, ...below].join('\n');
32
35
  }
33
36
  export function gridSpanClassCatalog() {
34
37
  return gridColumnSpans.map((span) => gridSpanClass(span));
@@ -1,6 +1,19 @@
1
1
  import { type Breakpoint } from './breakpoints.js';
2
2
  export declare const gridColumnCounts: readonly [1, 2, 3, 4, 6];
3
3
  export type GridColumns = (typeof gridColumnCounts)[number];
4
+ /**
5
+ * A fitted grid's narrowest column: as many columns as fit the grid's width
6
+ * at this minimum, each stretched to share the row, and one column when
7
+ * the grid is narrower than the minimum.
8
+ */
9
+ export declare const gridFitWidths: {
10
+ readonly narrow: "15rem";
11
+ readonly medium: "16.25rem";
12
+ readonly wide: "18.75rem";
13
+ };
14
+ export type GridFit = keyof typeof gridFitWidths;
15
+ export declare const gridFits: GridFit[];
16
+ export declare function gridFitClass(fit: GridFit): string;
4
17
  export declare function gridColumnsClass(columns: GridColumns): string;
5
18
  export declare function gridColumnsBelowClass(breakpoint: Breakpoint, columns: GridColumns): string;
6
19
  export declare function cssGridClasses(): string;
@@ -1,5 +1,19 @@
1
1
  import { breakpointQuery, breakpoints, } from './breakpoints.js';
2
2
  export const gridColumnCounts = [1, 2, 3, 4, 6];
3
+ /**
4
+ * A fitted grid's narrowest column: as many columns as fit the grid's width
5
+ * at this minimum, each stretched to share the row, and one column when
6
+ * the grid is narrower than the minimum.
7
+ */
8
+ export const gridFitWidths = {
9
+ narrow: '15rem',
10
+ medium: '16.25rem',
11
+ wide: '18.75rem',
12
+ };
13
+ export const gridFits = Object.keys(gridFitWidths);
14
+ export function gridFitClass(fit) {
15
+ return `sw-grid-fit-${fit}`;
16
+ }
3
17
  export function gridColumnsClass(columns) {
4
18
  return `sw-grid-cols-${columns}`;
5
19
  }
@@ -20,12 +34,14 @@ ${gridColumnCounts
20
34
  .map((count) => columnsRule(gridColumnsBelowClass(breakpoint, count), count))
21
35
  .join('\n')}
22
36
  }`);
23
- return [base, ...columns, ...below].join('\n');
37
+ const fitted = gridFits.map((fit) => `.${gridFitClass(fit)} { grid-template-columns: repeat(auto-fit, minmax(min(100%, ${gridFitWidths[fit]}), 1fr)); }`);
38
+ return [base, ...columns, ...below, ...fitted].join('\n');
24
39
  }
25
40
  export function gridClassCatalog() {
26
41
  return [
27
42
  'sw-grid',
28
43
  ...gridColumnCounts.map((count) => gridColumnsClass(count)),
29
44
  ...breakpoints.flatMap((breakpoint) => gridColumnCounts.map((count) => gridColumnsBelowClass(breakpoint, count))),
45
+ ...gridFits.map(gridFitClass),
30
46
  ];
31
47
  }
@@ -0,0 +1,2 @@
1
+ export declare function cssHeroClasses(): string;
2
+ export declare function heroClassCatalog(): string[];
@@ -0,0 +1,62 @@
1
+ import { breakpointQuery } from './breakpoints.js';
2
+ /*
3
+ * A public page's opening: the words in one column (an eyebrow, the hero
4
+ * title, the lead and the actions) and an optional picture beside them.
5
+ * From `md` the picture keeps its own width at the row's end and the words
6
+ * take the rest; below `md` the picture sits centred under the words and
7
+ * the actions stack to the full width, as a phone's thumb expects. With no
8
+ * picture the words keep the reading width, so a long title or lead does
9
+ * not run the page's full width. The actions sit one spacing step further
10
+ * from the lead than the text's gap, so the buttons read as their own row.
11
+ */
12
+ export function cssHeroClasses() {
13
+ return `.sw-hero {
14
+ align-items: center;
15
+ display: grid;
16
+ gap: var(--sw-space-8);
17
+ min-width: 0;
18
+ }
19
+
20
+ .sw-hero-text {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: var(--sw-space-4);
24
+ min-width: 0;
25
+ }
26
+
27
+ .sw-hero:not(.sw-hero-with-media) .sw-hero-text {
28
+ max-width: var(--sw-reading-max);
29
+ }
30
+
31
+ .sw-hero-actions {
32
+ display: flex;
33
+ flex-wrap: wrap;
34
+ gap: var(--sw-space-3);
35
+ padding-top: var(--sw-space-1);
36
+ }
37
+
38
+ .sw-hero-media {
39
+ justify-self: center;
40
+ max-width: 100%;
41
+ min-width: 0;
42
+ }
43
+
44
+ @media ${breakpointQuery('from', 'md')} {
45
+ .sw-hero-text { gap: var(--sw-space-5); }
46
+ .sw-hero-with-media { grid-template-columns: minmax(0, 1fr) auto; }
47
+ }
48
+
49
+ @media ${breakpointQuery('below', 'md')} {
50
+ .sw-hero { gap: var(--sw-space-6); }
51
+ .sw-hero-actions { align-items: stretch; flex-direction: column; }
52
+ }`;
53
+ }
54
+ export function heroClassCatalog() {
55
+ return [
56
+ 'sw-hero',
57
+ 'sw-hero-with-media',
58
+ 'sw-hero-text',
59
+ 'sw-hero-actions',
60
+ 'sw-hero-media',
61
+ ];
62
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssSiteFooterClasses(): string;
2
+ export declare function siteFooterClassCatalog(): string[];
@@ -0,0 +1,152 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { breakpointQuery } from './breakpoints.js';
3
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
4
+ const label = typographyVariants.label;
5
+ const caption = typographyVariants.caption;
6
+ /** The font declarations of a typography variant, weight included or not. */
7
+ function type(variant, weight = variant.fontWeight) {
8
+ return `font-size: ${variant.fontSize}px;
9
+ font-weight: ${weight};
10
+ letter-spacing: ${variant.letterSpacing}px;
11
+ line-height: ${variant.lineHeight}px;`;
12
+ }
13
+ /*
14
+ * The footer sits on the inverse band (`Band tone="inverse"`), which
15
+ * re-scopes the theme's words, muted words and hairline, so these rules
16
+ * read the ordinary variables. Group labels are the band's words at label
17
+ * weight; links are its muted words in label size, no underline (a list
18
+ * of links in a named `nav`), the band's words with an underline on hover
19
+ * and focus, and the current page in the band's words at label weight. The
20
+ * link rules match the band's running-text link rule's specificity and
21
+ * win by coming after it (`cssSiteFooterClasses` follows `cssBandClasses`;
22
+ * a test holds the order).
23
+ * Below `md` the brand spans two columns of groups; from `md` the brand
24
+ * and the groups share one wrapping row, spread apart.
25
+ */
26
+ export function cssSiteFooterClasses() {
27
+ return `.sw-site-footer-top {
28
+ display: grid;
29
+ gap: var(--sw-space-6) var(--sw-space-5);
30
+ grid-template-columns: repeat(2, minmax(0, 1fr));
31
+ }
32
+
33
+ .sw-site-footer-brand {
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: var(--sw-space-3);
37
+ grid-column: 1 / -1;
38
+ max-width: 20rem;
39
+ }
40
+
41
+ .sw-site-footer-brand > :first-child {
42
+ align-self: flex-start;
43
+ }
44
+
45
+ .sw-site-footer-tagline {
46
+ color: var(--sw-color-muted);
47
+ margin: 0;
48
+ ${type(label, 400)}
49
+ }
50
+
51
+ .sw-site-footer-group {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: var(--sw-space-3);
55
+ min-width: 0;
56
+ }
57
+
58
+ .sw-site-footer-label {
59
+ color: var(--sw-color-text);
60
+ margin: 0;
61
+ ${type(caption, 600)}
62
+ }
63
+
64
+ .sw-site-footer-links {
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: var(--sw-space-2);
68
+ list-style: none;
69
+ margin: 0;
70
+ padding: 0;
71
+ }
72
+
73
+ [data-theme] .sw-band-inverse a.sw-site-footer-link {
74
+ color: var(--sw-color-muted);
75
+ display: inline-flex;
76
+ ${type(label, 400)}
77
+ overflow-wrap: anywhere;
78
+ text-decoration-color: transparent;
79
+ }
80
+
81
+ [data-theme] .sw-band-inverse a.sw-site-footer-link:is(:hover, :focus-visible) {
82
+ color: var(--sw-color-text);
83
+ text-decoration-color: currentColor;
84
+ }
85
+
86
+ [data-theme] .sw-band-inverse a.sw-site-footer-link[aria-current='page'] {
87
+ color: var(--sw-color-text);
88
+ font-weight: ${label.fontWeight};
89
+ }
90
+
91
+ .sw-site-footer-bottom {
92
+ align-items: center;
93
+ border-top: 1px solid var(--sw-color-border);
94
+ color: var(--sw-color-muted);
95
+ display: flex;
96
+ flex-wrap: wrap;
97
+ gap: var(--sw-space-3);
98
+ justify-content: space-between;
99
+ margin-top: var(--sw-space-6);
100
+ padding-top: var(--sw-space-4);
101
+ ${type(caption)}
102
+ }
103
+
104
+ .sw-site-footer-end {
105
+ margin-inline-start: auto;
106
+ }
107
+
108
+ @media ${breakpointQuery('from', 'md')} {
109
+ .sw-site-footer-top {
110
+ display: flex;
111
+ flex-wrap: wrap;
112
+ gap: var(--sw-space-6) var(--sw-space-8);
113
+ justify-content: space-between;
114
+ }
115
+ }
116
+
117
+ @media ${coarsePointerQuery} {
118
+ [data-theme] .sw-band-inverse a.sw-site-footer-link {
119
+ align-items: center;
120
+ min-height: ${touchTarget};
121
+ }
122
+
123
+ .sw-site-footer-links {
124
+ gap: 0;
125
+ }
126
+ }
127
+
128
+ @media (forced-colors: active) {
129
+ [data-theme] .sw-band-inverse a.sw-site-footer-link {
130
+ color: LinkText;
131
+ }
132
+
133
+ .sw-site-footer-bottom {
134
+ border-top-color: CanvasText;
135
+ }
136
+ }`;
137
+ }
138
+ export function siteFooterClassCatalog() {
139
+ return [
140
+ 'sw-site-footer',
141
+ 'sw-site-footer-top',
142
+ 'sw-site-footer-brand',
143
+ 'sw-site-footer-tagline',
144
+ 'sw-site-footer-group',
145
+ 'sw-site-footer-label',
146
+ 'sw-site-footer-links',
147
+ 'sw-site-footer-link',
148
+ 'sw-site-footer-bottom',
149
+ 'sw-site-footer-legal',
150
+ 'sw-site-footer-end',
151
+ ];
152
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssSiteHeaderClasses(): string;
2
+ export declare function siteHeaderClassCatalog(): string[];