@scalewing/react 1.47.0 → 1.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +171 -4
  3. package/dist/command-palette.test-support.d.ts +21 -0
  4. package/dist/command-palette.test-support.js +55 -0
  5. package/dist/components/AppHeader.d.ts +3 -0
  6. package/dist/components/AppShell.d.ts +4 -3
  7. package/dist/components/AppShell.js +24 -7
  8. package/dist/components/Band.d.ts +30 -0
  9. package/dist/components/Band.js +23 -0
  10. package/dist/components/ButtonLink.d.ts +33 -0
  11. package/dist/components/ButtonLink.js +27 -0
  12. package/dist/components/CommandPalette.d.ts +12 -0
  13. package/dist/components/CommandPalette.js +143 -0
  14. package/dist/components/CommandPaletteTrigger.d.ts +9 -0
  15. package/dist/components/CommandPaletteTrigger.js +16 -0
  16. package/dist/components/ContextSwitcher.d.ts +12 -0
  17. package/dist/components/ContextSwitcher.js +62 -0
  18. package/dist/components/FixtureGroup.d.ts +9 -0
  19. package/dist/components/FixtureGroup.js +16 -0
  20. package/dist/components/FixtureRow.d.ts +10 -0
  21. package/dist/components/FixtureRow.js +46 -0
  22. package/dist/components/GlyphTile.d.ts +9 -3
  23. package/dist/components/GlyphTile.js +15 -4
  24. package/dist/components/SiteFooter.d.ts +12 -0
  25. package/dist/components/SiteFooter.js +23 -0
  26. package/dist/components/SiteHeader.d.ts +14 -0
  27. package/dist/components/SiteHeader.js +24 -0
  28. package/dist/components/Skeleton.d.ts +22 -0
  29. package/dist/components/Skeleton.js +30 -0
  30. package/dist/components/app-shell/AppShellBar.d.ts +23 -0
  31. package/dist/components/app-shell/AppShellBar.js +25 -0
  32. package/dist/components/app-shell/types.d.ts +36 -1
  33. package/dist/components/app-shell/validate.d.ts +4 -3
  34. package/dist/components/app-shell/validate.js +21 -3
  35. package/dist/components/command-palette/PaletteList.d.ts +20 -0
  36. package/dist/components/command-palette/PaletteList.js +21 -0
  37. package/dist/components/command-palette/shortcut.d.ts +19 -0
  38. package/dist/components/command-palette/shortcut.js +75 -0
  39. package/dist/components/command-palette/types.d.ts +72 -0
  40. package/dist/components/command-palette/types.js +2 -0
  41. package/dist/components/command-palette/validate.d.ts +10 -0
  42. package/dist/components/command-palette/validate.js +50 -0
  43. package/dist/components/context-switcher/SwitcherOption.d.ts +17 -0
  44. package/dist/components/context-switcher/SwitcherOption.js +20 -0
  45. package/dist/components/context-switcher/SwitcherPopup.d.ts +31 -0
  46. package/dist/components/context-switcher/SwitcherPopup.js +111 -0
  47. package/dist/components/context-switcher/options.d.ts +33 -0
  48. package/dist/components/context-switcher/options.js +60 -0
  49. package/dist/components/context-switcher/types.d.ts +96 -0
  50. package/dist/components/context-switcher/types.js +1 -0
  51. package/dist/components/context-switcher/use-switcher-list.d.ts +29 -0
  52. package/dist/components/context-switcher/use-switcher-list.js +93 -0
  53. package/dist/components/context-switcher/validate.d.ts +8 -0
  54. package/dist/components/context-switcher/validate.js +94 -0
  55. package/dist/components/fixture/fixture-list-context.d.ts +4 -0
  56. package/dist/components/fixture/fixture-list-context.js +9 -0
  57. package/dist/components/fixture/types.d.ts +50 -0
  58. package/dist/components/fixture/types.js +1 -0
  59. package/dist/components/fixture/validate.d.ts +8 -0
  60. package/dist/components/fixture/validate.js +36 -0
  61. package/dist/components/site-footer/types.d.ts +31 -0
  62. package/dist/components/site-footer/types.js +1 -0
  63. package/dist/components/site-footer/validate.d.ts +7 -0
  64. package/dist/components/site-footer/validate.js +22 -0
  65. package/dist/components/site-header/SiteHeaderLinks.d.ts +7 -0
  66. package/dist/components/site-header/SiteHeaderLinks.js +7 -0
  67. package/dist/components/site-header/types.d.ts +38 -0
  68. package/dist/components/site-header/types.js +1 -0
  69. package/dist/components/site-header/use-menu-dismiss.d.ts +8 -0
  70. package/dist/components/site-header/use-menu-dismiss.js +62 -0
  71. package/dist/components/site-header/validate.d.ts +7 -0
  72. package/dist/components/site-header/validate.js +17 -0
  73. package/dist/components/use-element-height.d.ts +10 -0
  74. package/dist/components/use-element-height.js +35 -0
  75. package/dist/context-switcher.test-support.d.ts +9 -0
  76. package/dist/context-switcher.test-support.js +41 -0
  77. package/dist/css/css-app-shell-page-bar.d.ts +3 -0
  78. package/dist/css/css-app-shell-page-bar.js +81 -0
  79. package/dist/css/css-app-shell.js +6 -1
  80. package/dist/css/css-band.d.ts +10 -0
  81. package/dist/css/css-band.js +101 -0
  82. package/dist/css/css-button-link.d.ts +3 -0
  83. package/dist/css/css-button-link.js +48 -0
  84. package/dist/css/css-command-palette-key.d.ts +1 -0
  85. package/dist/css/css-command-palette-key.js +33 -0
  86. package/dist/css/css-command-palette-trigger.d.ts +2 -0
  87. package/dist/css/css-command-palette-trigger.js +88 -0
  88. package/dist/css/css-command-palette.d.ts +2 -0
  89. package/dist/css/css-command-palette.js +209 -0
  90. package/dist/css/css-components.js +24 -0
  91. package/dist/css/css-context-switcher-trigger.d.ts +15 -0
  92. package/dist/css/css-context-switcher-trigger.js +175 -0
  93. package/dist/css/css-context-switcher.d.ts +4 -0
  94. package/dist/css/css-context-switcher.js +219 -0
  95. package/dist/css/css-count-pill.d.ts +2 -2
  96. package/dist/css/css-count-pill.js +11 -3
  97. package/dist/css/css-fixture.d.ts +5 -0
  98. package/dist/css/css-fixture.js +260 -0
  99. package/dist/css/css-glyph-tile.d.ts +2 -0
  100. package/dist/css/css-glyph-tile.js +29 -1
  101. package/dist/css/css-site-footer.d.ts +2 -0
  102. package/dist/css/css-site-footer.js +152 -0
  103. package/dist/css/css-site-header.d.ts +2 -0
  104. package/dist/css/css-site-header.js +262 -0
  105. package/dist/css/css-skeleton.d.ts +10 -0
  106. package/dist/css/css-skeleton.js +110 -0
  107. package/dist/css/stacking.js +7 -4
  108. package/dist/css/stylesheet.js +16 -0
  109. package/dist/index.d.ts +13 -0
  110. package/dist/index.js +10 -0
  111. package/dist/styles.css +2008 -719
  112. package/package.json +2 -2
@@ -0,0 +1,219 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { checkStrokeIn } from './chevron.js';
3
+ import { contextSwitcherTriggerClassCatalog, cssContextSwitcherTriggerClasses, } from './css-context-switcher-trigger.js';
4
+ import { quietFill } from './quiet-fill.js';
5
+ import { zIndex } from './stacking.js';
6
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
7
+ const caption = typographyVariants.caption;
8
+ const label = typographyVariants.label;
9
+ /* The popup's least width, a named geometry of this control. */
10
+ const popupMinWidth = '15rem';
11
+ /** The trigger's width, set on the open popup so it is never narrower. */
12
+ export const contextSwitcherTriggerWidthVariable = '--sw-context-switcher-trigger-width';
13
+ const viewportCap = 'calc(100% - var(--sw-space-2) - var(--sw-space-2))';
14
+ /*
15
+ * The popup: a glass panel on the top layer beside the trigger (placed by
16
+ * script, as the menus are), at least 15rem and as wide as the trigger
17
+ * (its width set by the component), capped by the dialog width (or the
18
+ * trigger's, when wider) and the viewport. A column: the filter field,
19
+ * the listbox (the only part that scrolls), the empty words, the footer.
20
+ */
21
+ function popupRules() {
22
+ return `.sw-context-switcher-popup {
23
+ background: var(--sw-glass-fill);
24
+ border: 1px solid var(--sw-glass-border);
25
+ border-radius: var(--sw-radius-sm);
26
+ box-shadow: var(--sw-elevation-sm), inset 0 1px 0 var(--sw-glass-specular);
27
+ backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
28
+ -webkit-backdrop-filter: blur(var(--sw-glass-blur)) saturate(var(--sw-glass-saturate));
29
+ box-sizing: border-box;
30
+ color: var(--sw-color-text);
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: var(--sw-space-1);
34
+ inset: auto;
35
+ margin: 0;
36
+ max-height: ${viewportCap};
37
+ max-width: min(max(var(--sw-dialog-max), var(${contextSwitcherTriggerWidthVariable}, 0px)), ${viewportCap});
38
+ min-width: min(max(${popupMinWidth}, var(${contextSwitcherTriggerWidthVariable}, 0px)), ${viewportCap});
39
+ padding: var(--sw-space-1);
40
+ position: fixed;
41
+ width: max-content;
42
+ ${zIndex('popup')}
43
+ }
44
+
45
+ .sw-context-switcher-filter {
46
+ box-sizing: border-box;
47
+ flex: none;
48
+ margin: var(--sw-space-1);
49
+ width: calc(100% - var(--sw-space-2));
50
+ }
51
+
52
+ .sw-context-switcher-list {
53
+ flex: 1 1 auto;
54
+ max-height: var(--sw-select-max);
55
+ min-height: 0;
56
+ overflow-y: auto;
57
+ overscroll-behavior: contain;
58
+ }
59
+
60
+ .sw-context-switcher-list:focus {
61
+ outline: none;
62
+ }
63
+
64
+ .sw-context-switcher-heading {
65
+ color: var(--sw-color-muted);
66
+ font-family: var(--sw-font-sans);
67
+ font-size: ${caption.fontSize}px;
68
+ font-weight: 600;
69
+ letter-spacing: ${caption.letterSpacing}px;
70
+ line-height: ${caption.lineHeight}px;
71
+ padding: var(--sw-space-2) var(--sw-space-2) var(--sw-space-1);
72
+ }
73
+
74
+ .sw-context-switcher-empty {
75
+ color: var(--sw-color-muted);
76
+ font-family: var(--sw-font-sans);
77
+ font-size: ${caption.fontSize}px;
78
+ letter-spacing: ${caption.letterSpacing}px;
79
+ line-height: ${caption.lineHeight}px;
80
+ margin: 0;
81
+ padding: var(--sw-space-2);
82
+ }
83
+
84
+ .sw-context-switcher-footer {
85
+ border-top: 1px solid var(--sw-color-border);
86
+ display: flex;
87
+ flex: none;
88
+ flex-wrap: wrap;
89
+ gap: var(--sw-space-1);
90
+ padding-top: var(--sw-space-1);
91
+ }
92
+
93
+ .sw-context-switcher-action-icon {
94
+ display: inline-flex;
95
+ flex: none;
96
+ }`;
97
+ }
98
+ /*
99
+ * An option: the mark, the label over its muted detail, then the badge,
100
+ * the count pill and the current one's check at the inline end. The sm
101
+ * control height (44 px on a coarse pointer). The active row (the arrow
102
+ * keys' or the pointer's) takes the quiet fill, and an accent ring while
103
+ * the list itself has visible focus; the current one is at label weight
104
+ * with the accent check. Forced colours draw the active row in Highlight.
105
+ */
106
+ function optionRules() {
107
+ return `.sw-context-switcher-option {
108
+ align-items: center;
109
+ border-radius: var(--sw-radius-sm);
110
+ box-sizing: border-box;
111
+ cursor: pointer;
112
+ display: flex;
113
+ font-family: var(--sw-font-sans);
114
+ font-size: ${label.fontSize}px;
115
+ gap: var(--sw-space-2);
116
+ letter-spacing: ${label.letterSpacing}px;
117
+ line-height: ${label.lineHeight}px;
118
+ min-height: var(--sw-control-sm-min-height);
119
+ overflow-wrap: anywhere;
120
+ padding: var(--sw-space-1) var(--sw-space-2);
121
+ }
122
+
123
+ .sw-context-switcher-option[data-active='true'] {
124
+ background: ${quietFill};
125
+ }
126
+
127
+ /* On the quiet fill the muted detail moves toward the text colour, keeping 4.5:1. */
128
+ .sw-context-switcher-option[data-active='true'] .sw-context-switcher-detail {
129
+ color: color-mix(in srgb, var(--sw-color-muted) 70%, var(--sw-color-text));
130
+ }
131
+
132
+ .sw-context-switcher-list:focus-visible .sw-context-switcher-option[data-active='true'] {
133
+ outline: var(--sw-focus-ring-width) solid var(--sw-color-accent);
134
+ outline-offset: calc(-1 * var(--sw-focus-ring-width));
135
+ }
136
+
137
+ .sw-context-switcher-icon {
138
+ display: inline-flex;
139
+ flex: none;
140
+ }
141
+
142
+ .sw-context-switcher-words {
143
+ display: flex;
144
+ flex: 1 1 auto;
145
+ flex-direction: column;
146
+ min-width: 0;
147
+ }
148
+
149
+ .sw-context-switcher-option[aria-selected='true'] .sw-context-switcher-label {
150
+ font-weight: ${label.fontWeight};
151
+ }
152
+
153
+ .sw-context-switcher-detail {
154
+ color: var(--sw-color-muted);
155
+ font-size: ${caption.fontSize}px;
156
+ letter-spacing: ${caption.letterSpacing}px;
157
+ line-height: ${caption.lineHeight}px;
158
+ }
159
+
160
+ .sw-context-switcher-count {
161
+ flex: none;
162
+ }
163
+
164
+ .sw-context-switcher-check {
165
+ ${checkStrokeIn('var(--sw-color-accent)')}
166
+ box-sizing: border-box;
167
+ flex: none;
168
+ height: var(--sw-space-3);
169
+ margin-inline: var(--sw-space-1);
170
+ transform: rotate(45deg);
171
+ width: var(--sw-space-2);
172
+ }
173
+
174
+ @media ${coarsePointerQuery} {
175
+ .sw-context-switcher-option { min-height: ${touchTarget}; }
176
+ }
177
+
178
+ @media (forced-colors: active) {
179
+ .sw-context-switcher-popup { border-color: CanvasText; }
180
+ .sw-context-switcher-option[data-active='true'] { background: Highlight; color: HighlightText; forced-color-adjust: none; }
181
+ .sw-context-switcher-option[data-active='true'] .sw-context-switcher-detail { color: HighlightText; }
182
+ .sw-context-switcher-option[data-active='true'] .sw-badge,
183
+ .sw-context-switcher-option[data-active='true'] .sw-context-switcher-count { forced-color-adjust: auto; }
184
+ .sw-context-switcher-check { border-color: CanvasText; }
185
+ .sw-context-switcher-option[data-active='true'] .sw-context-switcher-check { border-color: HighlightText; }
186
+ .sw-context-switcher-footer { border-color: CanvasText; }
187
+ }`;
188
+ }
189
+ /*
190
+ * ContextSwitcher (see components/ContextSwitcher.tsx): the trigger is
191
+ * `css-context-switcher-trigger.ts`; its count joins the shared pill
192
+ * (`css-count-pill.ts`).
193
+ */
194
+ export function cssContextSwitcherClasses() {
195
+ return `${cssContextSwitcherTriggerClasses()}
196
+
197
+ ${popupRules()}
198
+
199
+ ${optionRules()}`;
200
+ }
201
+ export function contextSwitcherClassCatalog() {
202
+ return [
203
+ ...contextSwitcherTriggerClassCatalog(),
204
+ 'sw-context-switcher-popup',
205
+ 'sw-context-switcher-filter',
206
+ 'sw-context-switcher-list',
207
+ 'sw-context-switcher-heading',
208
+ 'sw-context-switcher-option',
209
+ 'sw-context-switcher-icon',
210
+ 'sw-context-switcher-words',
211
+ 'sw-context-switcher-label',
212
+ 'sw-context-switcher-detail',
213
+ 'sw-context-switcher-check',
214
+ 'sw-context-switcher-empty',
215
+ 'sw-context-switcher-footer',
216
+ 'sw-context-switcher-action-icon',
217
+ // sw-context-switcher-count is the shared pill's (css-count-pill.ts).
218
+ ];
219
+ }
@@ -1,5 +1,5 @@
1
- /** The pills that take a tone modifier: NavCount's and AppShell's item count. */
2
- declare const tonedPills: readonly ["sw-nav-count", "sw-app-shell-count"];
1
+ /** The pills that take a tone modifier: all but SectionNav's. */
2
+ declare const tonedPills: readonly ["sw-nav-count", "sw-app-shell-count", "sw-context-switcher-count"];
3
3
  /** The tones other than the pill's own danger. */
4
4
  declare const pillTones: readonly ["accent", "warning"];
5
5
  /** A toned pill's modifier class, such as `sw-nav-count-warning`. */
@@ -1,14 +1,22 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
2
  import { toneTintFill, toneTintInk } from './tone-tint.js';
3
3
  const caption = typographyVariants.caption;
4
- /** The counts drawn as the pill: SectionNav's, a Nav word's and AppShell's. */
4
+ /**
5
+ * The counts drawn as the pill: SectionNav's, a Nav word's, AppShell's and
6
+ * a ContextSwitcher option's.
7
+ */
5
8
  const pills = [
6
9
  'sw-section-nav-count',
7
10
  'sw-nav-count',
8
11
  'sw-app-shell-count',
12
+ 'sw-context-switcher-count',
13
+ ];
14
+ /** The pills that take a tone modifier: all but SectionNav's. */
15
+ const tonedPills = [
16
+ 'sw-nav-count',
17
+ 'sw-app-shell-count',
18
+ 'sw-context-switcher-count',
9
19
  ];
10
- /** The pills that take a tone modifier: NavCount's and AppShell's item count. */
11
- const tonedPills = ['sw-nav-count', 'sw-app-shell-count'];
12
20
  /** The tones other than the pill's own danger. */
13
21
  const pillTones = ['accent', 'warning'];
14
22
  /** A toned pill's modifier class, such as `sw-nav-count-warning`. */
@@ -0,0 +1,5 @@
1
+ export declare const fixtureCenterKinds: readonly ["time", "score"];
2
+ /** `time` is a kickoff time in a grey chip; `score` is bold figures. */
3
+ export type FixtureCenterKind = (typeof fixtureCenterKinds)[number];
4
+ export declare function cssFixtureClasses(): string;
5
+ export declare function fixtureClassCatalog(): string[];
@@ -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
  }
@@ -0,0 +1,2 @@
1
+ export declare function cssSiteFooterClasses(): string;
2
+ export declare function siteFooterClassCatalog(): string[];