@gsa-tts/graymatter-ui 1.3.0 → 2.0.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/README.md +59 -91
- package/assets/uswds/components/usa-banner.js.mjs +805 -0
- package/assets/uswds/components/usa-banner.js.umd.js +127 -0
- package/package.json +18 -34
- package/src/components/UsaBanner.svelte +2 -2
- package/src/utils/index.ts +0 -4
- package/static/uswds/js/uswds.js +37 -3
- package/static/uswds/js/uswds.min.js +1 -1
- package/static/uswds/js/uswds.min.js.map +1 -1
- package/static/uswds/styles/styles.css +3 -3
- package/static/uswds/styles/styles.css.map +1 -1
- package/dist/graymatter-ui.js +0 -6211
- package/dist/graymatter-ui.umd.cjs +0 -88
- package/src/astro-config/index.ts +0 -139
- package/src/components/Button.webcomponent.svelte +0 -58
- package/src/components/DesktopHeader.webcomponent.svelte +0 -21
- package/src/components/DesktopSideNav.webcomponent.svelte +0 -42
- package/src/components/Logo.webcomponent.svelte +0 -29
- package/src/components/MobileBottomNav.webcomponent.svelte +0 -32
- package/src/components/MobileSideMenu.webcomponent.svelte +0 -13
- package/src/components/MobileTopNav.webcomponent.svelte +0 -20
- package/src/index.ts +0 -19
- package/src/layouts/BaseLayout.astro +0 -37
- package/src/layouts/DocsLayout.astro +0 -342
- package/src/layouts/GlobalAppLayout.astro +0 -536
- package/src/layouts/GoogleTagManager.astro +0 -17
- package/src/layouts/Head.astro +0 -101
- package/src/layouts/fonts.ts +0 -17
- package/src/pages/.gitkeep +0 -0
- package/src/utils/remarkPlugins.ts +0 -55
|
@@ -1,536 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import BaseLayout from '@gsa-tts/graymatter-ui/layouts/BaseLayout.astro';
|
|
3
|
-
import '../styles/base/global.css';
|
|
4
|
-
import DesktopSideNav from '../components/DesktopSideNav.svelte';
|
|
5
|
-
import DesktopHeader from '../components/DesktopHeader.svelte';
|
|
6
|
-
import MobileTopNav from '../components/MobileTopNav.svelte';
|
|
7
|
-
import MobileBottomNav from '../components/MobileBottomNav.svelte';
|
|
8
|
-
import MobileSideMenu from '../components/MobileSideMenu.svelte';
|
|
9
|
-
import Logo from '../components/Logo.svelte';
|
|
10
|
-
import { siteName } from '../constants';
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
componentOptions = {},
|
|
14
|
-
ssrSelectedItem,
|
|
15
|
-
gtmID,
|
|
16
|
-
showAppIcons = true,
|
|
17
|
-
profileMenuData = undefined,
|
|
18
|
-
supplementalMenuData = undefined,
|
|
19
|
-
logoLinkUrl,
|
|
20
|
-
logoLinkLabel,
|
|
21
|
-
title: pageTitle,
|
|
22
|
-
description,
|
|
23
|
-
openGraphImage,
|
|
24
|
-
hideDiscover = true,
|
|
25
|
-
className = '',
|
|
26
|
-
} = Astro.props;
|
|
27
|
-
|
|
28
|
-
// Read at build time for static output; prefer PUBLIC_* for safe exposure
|
|
29
|
-
const chatUrl =
|
|
30
|
-
import.meta.env.PUBLIC_CHAT_URL || process.env.PUBLIC_CHAT_URL || undefined;
|
|
31
|
-
const consoleUrl =
|
|
32
|
-
import.meta.env.PUBLIC_CONSOLE_URL ||
|
|
33
|
-
process.env.PUBLIC_CONSOLE_URL ||
|
|
34
|
-
undefined;
|
|
35
|
-
const apiDocsUrl =
|
|
36
|
-
import.meta.env.PUBLIC_API_DOCS_URL ||
|
|
37
|
-
process.env.PUBLIC_API_DOCS_URL ||
|
|
38
|
-
undefined;
|
|
39
|
-
const discoverUrl =
|
|
40
|
-
import.meta.env.PUBLIC_DISCOVER_URL ||
|
|
41
|
-
process.env.PUBLIC_DISCOVER_URL ||
|
|
42
|
-
undefined;
|
|
43
|
-
|
|
44
|
-
const chat = chatUrl || '#';
|
|
45
|
-
const consoleApp = consoleUrl || '#';
|
|
46
|
-
const api = apiDocsUrl || '#';
|
|
47
|
-
const discover = discoverUrl || '#';
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
<BaseLayout title={pageTitle} {gtmID} {description} {openGraphImage}>
|
|
51
|
-
<div class={`app ${className}`}>
|
|
52
|
-
<!-- Desktop Navigation -->
|
|
53
|
-
<div class="layout-container">
|
|
54
|
-
<nav aria-label="Main navigation">
|
|
55
|
-
<DesktopSideNav
|
|
56
|
-
client:load
|
|
57
|
-
ssrSelectedItem={ssrSelectedItem}
|
|
58
|
-
showAppIcons={showAppIcons}
|
|
59
|
-
hideDiscover={hideDiscover}
|
|
60
|
-
profileMenuData={profileMenuData}
|
|
61
|
-
apiUrl={api}
|
|
62
|
-
chatUrl={chat}
|
|
63
|
-
consoleUrl={consoleApp}
|
|
64
|
-
discoverUrl={discover}
|
|
65
|
-
{supplementalMenuData}
|
|
66
|
-
>
|
|
67
|
-
<slot name="sub-nav" />
|
|
68
|
-
</DesktopSideNav>
|
|
69
|
-
</nav>
|
|
70
|
-
<div class="main-container">
|
|
71
|
-
<header aria-label="Main site header">
|
|
72
|
-
<DesktopHeader client:load />
|
|
73
|
-
</header>
|
|
74
|
-
<main id="main-content" tabindex="0">
|
|
75
|
-
<slot />
|
|
76
|
-
</main>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
<!-- Mobile Navigation -->
|
|
81
|
-
<nav aria-label="Mobile navigation">
|
|
82
|
-
<MobileTopNav
|
|
83
|
-
client:load
|
|
84
|
-
profileMenuData={profileMenuData}
|
|
85
|
-
logoLinkUrl={logoLinkUrl}
|
|
86
|
-
logoLinkLabel={logoLinkLabel}
|
|
87
|
-
{supplementalMenuData}
|
|
88
|
-
/>
|
|
89
|
-
<MobileSideMenu client:load {logoLinkUrl} {logoLinkLabel}>
|
|
90
|
-
<slot name="sub-nav" />
|
|
91
|
-
</MobileSideMenu>
|
|
92
|
-
<MobileBottomNav
|
|
93
|
-
client:load
|
|
94
|
-
showAppIcons={showAppIcons}
|
|
95
|
-
hideDiscover={hideDiscover}
|
|
96
|
-
apiUrl={api}
|
|
97
|
-
chatUrl={chat}
|
|
98
|
-
consoleUrl={consoleApp}
|
|
99
|
-
discoverUrl={discover}
|
|
100
|
-
/>
|
|
101
|
-
</nav>
|
|
102
|
-
|
|
103
|
-
<!-- Fixed Logo (Desktop only) -->
|
|
104
|
-
<nav aria-label="Site logo">
|
|
105
|
-
<div class="logo-fixed-container display-none tablet:display-flex">
|
|
106
|
-
{
|
|
107
|
-
logoLinkUrl ? (
|
|
108
|
-
<a
|
|
109
|
-
class="display-flex"
|
|
110
|
-
href={logoLinkUrl}
|
|
111
|
-
aria-label={logoLinkLabel}
|
|
112
|
-
>
|
|
113
|
-
<Logo width={75} height={22} />
|
|
114
|
-
</a>
|
|
115
|
-
) : (
|
|
116
|
-
<Logo width={75} height={22} />
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
</div>
|
|
120
|
-
</nav>
|
|
121
|
-
|
|
122
|
-
<!-- Mobile Main Content -->
|
|
123
|
-
<main id="main-content-mobile" class="main-content-mobile">
|
|
124
|
-
<slot />
|
|
125
|
-
</main>
|
|
126
|
-
</div>
|
|
127
|
-
</BaseLayout>
|
|
128
|
-
|
|
129
|
-
<script is:inline>
|
|
130
|
-
if (typeof window !== 'undefined') {
|
|
131
|
-
// Handle URL hash scrolling immediately to prevent initial render at top
|
|
132
|
-
if (window.location.hash) {
|
|
133
|
-
const anchor = window.location.hash.substring(1);
|
|
134
|
-
|
|
135
|
-
// Set a flag to prevent SubNavMenu from also scrolling
|
|
136
|
-
sessionStorage.setItem('globalLayoutScrolling', 'true');
|
|
137
|
-
|
|
138
|
-
// Use requestAnimationFrame to scroll as early as possible
|
|
139
|
-
requestAnimationFrame(() => {
|
|
140
|
-
if (window.innerWidth <= 640) {
|
|
141
|
-
// Mobile: scroll in mobile content container
|
|
142
|
-
const mobileContent = document.getElementById('main-content-mobile');
|
|
143
|
-
if (mobileContent) {
|
|
144
|
-
const mobileAnchor = mobileContent.querySelector(
|
|
145
|
-
`[id="${anchor}"]`
|
|
146
|
-
);
|
|
147
|
-
if (mobileAnchor) {
|
|
148
|
-
const rect = mobileAnchor.getBoundingClientRect();
|
|
149
|
-
const mobileRect = mobileContent.getBoundingClientRect();
|
|
150
|
-
const scrollTop =
|
|
151
|
-
mobileContent.scrollTop + rect.top - mobileRect.top;
|
|
152
|
-
mobileContent.scrollTo({ top: scrollTop, behavior: 'auto' });
|
|
153
|
-
|
|
154
|
-
setTimeout(() => {
|
|
155
|
-
let topLevelTitle = null;
|
|
156
|
-
if (typeof window !== 'undefined') {
|
|
157
|
-
topLevelTitle = sessionStorage.getItem(
|
|
158
|
-
'subNav-topLevelTitle'
|
|
159
|
-
);
|
|
160
|
-
if (topLevelTitle) {
|
|
161
|
-
try {
|
|
162
|
-
topLevelTitle = JSON.parse(topLevelTitle);
|
|
163
|
-
} catch {}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
window.dispatchEvent(
|
|
167
|
-
new CustomEvent('sectionVisible', {
|
|
168
|
-
detail: {
|
|
169
|
-
title: topLevelTitle || mobileAnchor.textContent?.trim(),
|
|
170
|
-
id: mobileAnchor.id,
|
|
171
|
-
},
|
|
172
|
-
})
|
|
173
|
-
);
|
|
174
|
-
sessionStorage.removeItem('globalLayoutScrolling');
|
|
175
|
-
}, 100);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
// Desktop: scroll in desktop content container
|
|
180
|
-
const mainContent = document.getElementById('main-content');
|
|
181
|
-
if (mainContent) {
|
|
182
|
-
const desktopAnchor = mainContent.querySelector(`[id="${anchor}"]`);
|
|
183
|
-
if (desktopAnchor) {
|
|
184
|
-
try {
|
|
185
|
-
// Add temporary scroll margin to account for header
|
|
186
|
-
const originalScrollMargin =
|
|
187
|
-
desktopAnchor.style.scrollMarginTop;
|
|
188
|
-
desktopAnchor.style.scrollMarginTop = '56px';
|
|
189
|
-
|
|
190
|
-
desktopAnchor.scrollIntoView({
|
|
191
|
-
behavior: 'auto',
|
|
192
|
-
block: 'start',
|
|
193
|
-
inline: 'nearest',
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Restore original scroll margin after a delay
|
|
197
|
-
setTimeout(() => {
|
|
198
|
-
desktopAnchor.style.scrollMarginTop = originalScrollMargin;
|
|
199
|
-
}, 1000);
|
|
200
|
-
} catch (e) {
|
|
201
|
-
// Fallback to manual calculation
|
|
202
|
-
const headerHeight = 56;
|
|
203
|
-
const rect = desktopAnchor.getBoundingClientRect();
|
|
204
|
-
const scrollTop = window.scrollY + rect.top - headerHeight;
|
|
205
|
-
const finalScrollTop = Math.max(0, scrollTop);
|
|
206
|
-
window.scrollTo({ top: finalScrollTop, behavior: 'auto' });
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
setTimeout(() => {
|
|
210
|
-
let topLevelTitle = null;
|
|
211
|
-
if (typeof window !== 'undefined') {
|
|
212
|
-
topLevelTitle = sessionStorage.getItem(
|
|
213
|
-
'subNav-topLevelTitle'
|
|
214
|
-
);
|
|
215
|
-
if (topLevelTitle) {
|
|
216
|
-
try {
|
|
217
|
-
topLevelTitle = JSON.parse(topLevelTitle);
|
|
218
|
-
} catch {}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
window.dispatchEvent(
|
|
222
|
-
new CustomEvent('sectionVisible', {
|
|
223
|
-
detail: {
|
|
224
|
-
title: topLevelTitle || desktopAnchor.textContent?.trim(),
|
|
225
|
-
id: desktopAnchor.id,
|
|
226
|
-
},
|
|
227
|
-
})
|
|
228
|
-
);
|
|
229
|
-
sessionStorage.removeItem('globalLayoutScrolling');
|
|
230
|
-
}, 100);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
window.addEventListener('DOMContentLoaded', () => {
|
|
238
|
-
// Check navigation flag first, before any components mount
|
|
239
|
-
const isMainNavNavigation =
|
|
240
|
-
sessionStorage.getItem('navigatingToMainNav') === 'true';
|
|
241
|
-
|
|
242
|
-
if (isMainNavNavigation) {
|
|
243
|
-
// Clear the flag immediately
|
|
244
|
-
sessionStorage.removeItem('navigatingToMainNav');
|
|
245
|
-
// Don't clear header state here - let navigation components handle it
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Handle URL hash scrolling (fallback for cases where immediate scroll didn't work)
|
|
249
|
-
if (window.location.hash) {
|
|
250
|
-
const anchor = window.location.hash.substring(1);
|
|
251
|
-
|
|
252
|
-
// Only proceed if we haven't already scrolled
|
|
253
|
-
if (sessionStorage.getItem('globalLayoutScrolling') !== 'true') {
|
|
254
|
-
sessionStorage.setItem('globalLayoutScrolling', 'true');
|
|
255
|
-
|
|
256
|
-
if (window.innerWidth <= 640) {
|
|
257
|
-
// Mobile: scroll in mobile content container
|
|
258
|
-
const mobileContent = document.getElementById(
|
|
259
|
-
'main-content-mobile'
|
|
260
|
-
);
|
|
261
|
-
if (mobileContent) {
|
|
262
|
-
const mobileAnchor = mobileContent.querySelector(
|
|
263
|
-
`[id="${anchor}"]`
|
|
264
|
-
);
|
|
265
|
-
if (mobileAnchor) {
|
|
266
|
-
setTimeout(() => {
|
|
267
|
-
const rect = mobileAnchor.getBoundingClientRect();
|
|
268
|
-
const mobileRect = mobileContent.getBoundingClientRect();
|
|
269
|
-
const scrollTop =
|
|
270
|
-
mobileContent.scrollTop + rect.top - mobileRect.top;
|
|
271
|
-
mobileContent.scrollTo({ top: scrollTop, behavior: 'auto' });
|
|
272
|
-
|
|
273
|
-
setTimeout(() => {
|
|
274
|
-
let topLevelTitle = null;
|
|
275
|
-
if (typeof window !== 'undefined') {
|
|
276
|
-
topLevelTitle = sessionStorage.getItem(
|
|
277
|
-
'subNav-topLevelTitle'
|
|
278
|
-
);
|
|
279
|
-
if (topLevelTitle) {
|
|
280
|
-
try {
|
|
281
|
-
topLevelTitle = JSON.parse(topLevelTitle);
|
|
282
|
-
} catch {}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
window.dispatchEvent(
|
|
286
|
-
new CustomEvent('sectionVisible', {
|
|
287
|
-
detail: {
|
|
288
|
-
title:
|
|
289
|
-
topLevelTitle || mobileAnchor.textContent?.trim(),
|
|
290
|
-
id: mobileAnchor.id,
|
|
291
|
-
},
|
|
292
|
-
})
|
|
293
|
-
);
|
|
294
|
-
// Clear the flag after scrolling
|
|
295
|
-
if (typeof window !== 'undefined') {
|
|
296
|
-
sessionStorage.removeItem('globalLayoutScrolling');
|
|
297
|
-
}
|
|
298
|
-
}, 100);
|
|
299
|
-
}, 100);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
} else {
|
|
303
|
-
// Desktop: scroll in desktop content container
|
|
304
|
-
const mainContent = document.getElementById('main-content');
|
|
305
|
-
if (mainContent) {
|
|
306
|
-
const desktopAnchor = mainContent.querySelector(
|
|
307
|
-
`[id="${anchor}"]`
|
|
308
|
-
);
|
|
309
|
-
if (desktopAnchor) {
|
|
310
|
-
setTimeout(() => {
|
|
311
|
-
// Try scrollIntoView first with a temporary offset
|
|
312
|
-
try {
|
|
313
|
-
// Add temporary scroll margin to account for header
|
|
314
|
-
const originalScrollMargin =
|
|
315
|
-
desktopAnchor.style.scrollMarginTop;
|
|
316
|
-
desktopAnchor.style.scrollMarginTop = '56px';
|
|
317
|
-
|
|
318
|
-
desktopAnchor.scrollIntoView({
|
|
319
|
-
behavior: 'auto',
|
|
320
|
-
block: 'start',
|
|
321
|
-
inline: 'nearest',
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// Restore original scroll margin after a delay
|
|
325
|
-
setTimeout(() => {
|
|
326
|
-
desktopAnchor.style.scrollMarginTop =
|
|
327
|
-
originalScrollMargin;
|
|
328
|
-
}, 1000);
|
|
329
|
-
} catch (e) {
|
|
330
|
-
// Fallback to manual calculation
|
|
331
|
-
const headerHeight = 56;
|
|
332
|
-
const rect = desktopAnchor.getBoundingClientRect();
|
|
333
|
-
const scrollTop = window.scrollY + rect.top - headerHeight;
|
|
334
|
-
// Ensure scrollTop is not negative
|
|
335
|
-
const finalScrollTop = Math.max(0, scrollTop);
|
|
336
|
-
|
|
337
|
-
window.scrollTo({ top: finalScrollTop, behavior: 'auto' });
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
setTimeout(() => {
|
|
341
|
-
let topLevelTitle = null;
|
|
342
|
-
if (typeof window !== 'undefined') {
|
|
343
|
-
topLevelTitle = sessionStorage.getItem(
|
|
344
|
-
'subNav-topLevelTitle'
|
|
345
|
-
);
|
|
346
|
-
if (topLevelTitle) {
|
|
347
|
-
try {
|
|
348
|
-
topLevelTitle = JSON.parse(topLevelTitle);
|
|
349
|
-
} catch {}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
window.dispatchEvent(
|
|
353
|
-
new CustomEvent('sectionVisible', {
|
|
354
|
-
detail: {
|
|
355
|
-
title:
|
|
356
|
-
topLevelTitle || desktopAnchor.textContent?.trim(),
|
|
357
|
-
id: desktopAnchor.id,
|
|
358
|
-
},
|
|
359
|
-
})
|
|
360
|
-
);
|
|
361
|
-
// Clear the flag after scrolling
|
|
362
|
-
if (typeof window !== 'undefined') {
|
|
363
|
-
sessionStorage.removeItem('globalLayoutScrolling');
|
|
364
|
-
}
|
|
365
|
-
}, 100);
|
|
366
|
-
}, 100);
|
|
367
|
-
} else {
|
|
368
|
-
// Log all available anchors for debugging
|
|
369
|
-
const allAnchors = mainContent.querySelectorAll('[id]');
|
|
370
|
-
// Clear the flag if anchor not found
|
|
371
|
-
if (typeof window !== 'undefined') {
|
|
372
|
-
sessionStorage.removeItem('globalLayoutScrolling');
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
} else {
|
|
376
|
-
// Clear the flag if container not found
|
|
377
|
-
if (typeof window !== 'undefined') {
|
|
378
|
-
sessionStorage.removeItem('globalLayoutScrolling');
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Handle layout changes (mobile ↔ desktop)
|
|
388
|
-
if (typeof window !== 'undefined') {
|
|
389
|
-
let lastWidth = window.innerWidth;
|
|
390
|
-
let resizeTimeout = null;
|
|
391
|
-
|
|
392
|
-
window.addEventListener('resize', () => {
|
|
393
|
-
// Debounce resize events
|
|
394
|
-
if (resizeTimeout) {
|
|
395
|
-
clearTimeout(resizeTimeout);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
resizeTimeout = setTimeout(() => {
|
|
399
|
-
const currentWidth = window.innerWidth;
|
|
400
|
-
const wasMobile = lastWidth <= 640;
|
|
401
|
-
const isMobile = currentWidth <= 640;
|
|
402
|
-
|
|
403
|
-
if (wasMobile !== isMobile) {
|
|
404
|
-
// Handle anchor scrolling for layout changes
|
|
405
|
-
if (window.location.hash) {
|
|
406
|
-
const anchor = window.location.hash.substring(1);
|
|
407
|
-
|
|
408
|
-
// Wait for navigation state to update, then scroll
|
|
409
|
-
setTimeout(() => {
|
|
410
|
-
if (isMobile) {
|
|
411
|
-
// Mobile: scroll in mobile content container
|
|
412
|
-
const mobileContent = document.getElementById(
|
|
413
|
-
'main-content-mobile'
|
|
414
|
-
);
|
|
415
|
-
if (mobileContent) {
|
|
416
|
-
const mobileAnchor = mobileContent.querySelector(
|
|
417
|
-
`[id="${anchor}"]`
|
|
418
|
-
);
|
|
419
|
-
if (mobileAnchor) {
|
|
420
|
-
const rect = mobileAnchor.getBoundingClientRect();
|
|
421
|
-
const mobileRect = mobileContent.getBoundingClientRect();
|
|
422
|
-
const scrollTop =
|
|
423
|
-
mobileContent.scrollTop + rect.top - mobileRect.top;
|
|
424
|
-
mobileContent.scrollTo({
|
|
425
|
-
top: scrollTop,
|
|
426
|
-
behavior: 'auto',
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
setTimeout(() => {
|
|
430
|
-
let topLevelTitle = null;
|
|
431
|
-
if (typeof window !== 'undefined') {
|
|
432
|
-
topLevelTitle = sessionStorage.getItem(
|
|
433
|
-
'subNav-topLevelTitle'
|
|
434
|
-
);
|
|
435
|
-
if (topLevelTitle) {
|
|
436
|
-
try {
|
|
437
|
-
topLevelTitle = JSON.parse(topLevelTitle);
|
|
438
|
-
} catch {}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
window.dispatchEvent(
|
|
442
|
-
new CustomEvent('sectionVisible', {
|
|
443
|
-
detail: {
|
|
444
|
-
title:
|
|
445
|
-
topLevelTitle || mobileAnchor.textContent?.trim(),
|
|
446
|
-
id: mobileAnchor.id,
|
|
447
|
-
},
|
|
448
|
-
})
|
|
449
|
-
);
|
|
450
|
-
}, 500);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
} else {
|
|
454
|
-
// Desktop: scroll in desktop content container
|
|
455
|
-
const mainContent = document.getElementById('main-content');
|
|
456
|
-
if (mainContent) {
|
|
457
|
-
const desktopAnchor = mainContent.querySelector(
|
|
458
|
-
`[id="${anchor}"]`
|
|
459
|
-
);
|
|
460
|
-
if (desktopAnchor) {
|
|
461
|
-
// Try scrollIntoView first with a temporary offset
|
|
462
|
-
try {
|
|
463
|
-
// Add temporary scroll margin to account for header
|
|
464
|
-
const originalScrollMargin =
|
|
465
|
-
desktopAnchor.style.scrollMarginTop;
|
|
466
|
-
desktopAnchor.style.scrollMarginTop = '56px';
|
|
467
|
-
|
|
468
|
-
desktopAnchor.scrollIntoView({
|
|
469
|
-
behavior: 'auto',
|
|
470
|
-
block: 'start',
|
|
471
|
-
inline: 'nearest',
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// Restore original scroll margin after a delay
|
|
475
|
-
setTimeout(() => {
|
|
476
|
-
desktopAnchor.style.scrollMarginTop =
|
|
477
|
-
originalScrollMargin;
|
|
478
|
-
}, 1000);
|
|
479
|
-
} catch (e) {
|
|
480
|
-
// Fallback to manual calculation
|
|
481
|
-
const headerHeight = 56;
|
|
482
|
-
const rect = desktopAnchor.getBoundingClientRect();
|
|
483
|
-
const scrollTop =
|
|
484
|
-
window.scrollY + rect.top - headerHeight;
|
|
485
|
-
|
|
486
|
-
// Ensure scrollTop is not negative
|
|
487
|
-
const finalScrollTop = Math.max(0, scrollTop);
|
|
488
|
-
|
|
489
|
-
window.scrollTo({
|
|
490
|
-
top: finalScrollTop,
|
|
491
|
-
behavior: 'auto',
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
setTimeout(() => {
|
|
496
|
-
let topLevelTitle = null;
|
|
497
|
-
if (typeof window !== 'undefined') {
|
|
498
|
-
topLevelTitle = sessionStorage.getItem(
|
|
499
|
-
'subNav-topLevelTitle'
|
|
500
|
-
);
|
|
501
|
-
if (topLevelTitle) {
|
|
502
|
-
try {
|
|
503
|
-
topLevelTitle = JSON.parse(topLevelTitle);
|
|
504
|
-
} catch {}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
window.dispatchEvent(
|
|
508
|
-
new CustomEvent('sectionVisible', {
|
|
509
|
-
detail: {
|
|
510
|
-
title:
|
|
511
|
-
topLevelTitle ||
|
|
512
|
-
desktopAnchor.textContent?.trim(),
|
|
513
|
-
id: desktopAnchor.id,
|
|
514
|
-
},
|
|
515
|
-
})
|
|
516
|
-
);
|
|
517
|
-
}, 500);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}, 350); // Wait for navigation state to settle
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Dispatch custom event for layout transition
|
|
525
|
-
window.dispatchEvent(
|
|
526
|
-
new CustomEvent('layoutTransition', {
|
|
527
|
-
detail: { isMobile },
|
|
528
|
-
})
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
lastWidth = currentWidth;
|
|
533
|
-
}, 250); // Debounce delay
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
</script>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
let gtmCode = '';
|
|
3
|
-
if (Astro.props.gtmID) {
|
|
4
|
-
// Adds Google Tag Manager and DAP analytics for GSA/TTS (required for federal sites)
|
|
5
|
-
gtmCode = `<!-- Google tag (gtag.js) -->
|
|
6
|
-
<script async src="https://www.googletagmanager.com/gtag/js?id=${Astro.props.gtmID}"></script>
|
|
7
|
-
<script>
|
|
8
|
-
window.dataLayer = window.dataLayer || [];
|
|
9
|
-
function gtag(){dataLayer.push(arguments);}
|
|
10
|
-
gtag('js', new Date());
|
|
11
|
-
gtag('config', '${Astro.props.gtmID}');
|
|
12
|
-
</script>
|
|
13
|
-
<script id="_fed_an_ua_tag" async src="https://dap.digitalgov.gov/Universal-Federated-Analytics-Min.js?agency=GSA&subagency=TTS"></script>`;
|
|
14
|
-
}
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
<Fragment set:html={gtmCode} />
|
package/src/layouts/Head.astro
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { siteName } from '../constants';
|
|
3
|
-
const { title, description, openGraphImage, gtmID } = Astro.props;
|
|
4
|
-
import { getUrlFromBase } from '@gsa-tts/graymatter-ui/helpers';
|
|
5
|
-
import GoogleTagManager from './GoogleTagManager.astro';
|
|
6
|
-
import { fontPreloads } from './fonts';
|
|
7
|
-
|
|
8
|
-
// Default values for Open Graph
|
|
9
|
-
const defaultDescription =
|
|
10
|
-
'Accelerating trusted AI adoption across government. Three powerful AI tools. Industry-leading models. One integrated platform.';
|
|
11
|
-
const defaultOgImage = getUrlFromBase('/common/assets/images/og-preview.webp');
|
|
12
|
-
|
|
13
|
-
// Use computed variables instead of direct props to avoid Vite 7 import analysis issues
|
|
14
|
-
const pageTitle = title ? `${title} | USAi` : 'USAi';
|
|
15
|
-
const gtmIDValue = gtmID;
|
|
16
|
-
const ogTitle = title || siteName;
|
|
17
|
-
const ogDescription = description || defaultDescription;
|
|
18
|
-
const ogImage = openGraphImage || defaultOgImage;
|
|
19
|
-
const siteUrl = Astro.site || Astro.url.origin;
|
|
20
|
-
const canonicalUrl = new URL(Astro.url.pathname, siteUrl).href;
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
<meta name="viewport" content="width=device-width" />
|
|
24
|
-
<title>{pageTitle}</title>
|
|
25
|
-
<script is:inline>
|
|
26
|
-
if ('performance' in window) {
|
|
27
|
-
performance.mark('usai:documenthead:start');
|
|
28
|
-
}
|
|
29
|
-
</script>
|
|
30
|
-
{gtmIDValue && <GoogleTagManager gtmID={gtmIDValue} />}
|
|
31
|
-
<script is:inline src={`${getUrlFromBase('uswds/js/uswds-init.min.js')}`}
|
|
32
|
-
></script>
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
fontPreloads.map(href => (
|
|
36
|
-
<link rel="preload" href={href} as="font" type="font/woff2" crossorigin />
|
|
37
|
-
))
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
<style is:global>
|
|
41
|
-
:root {
|
|
42
|
-
--ai-font-family-sans: 'Archivo', sans-serif;
|
|
43
|
-
--ai-font-family-monospace: 'Space Mono', monospace;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
body {
|
|
47
|
-
font-family: var(--ai-font-family-sans);
|
|
48
|
-
}
|
|
49
|
-
</style>
|
|
50
|
-
<script>
|
|
51
|
-
import '../../static/uswds/js/uswds.js';
|
|
52
|
-
</script>
|
|
53
|
-
|
|
54
|
-
<!-- Open Graph -->
|
|
55
|
-
<meta property="og:type" content="website" />
|
|
56
|
-
<meta property="og:url" content={canonicalUrl} />
|
|
57
|
-
<meta property="og:title" content={ogTitle} />
|
|
58
|
-
<meta property="og:description" content={ogDescription} />
|
|
59
|
-
<meta property="og:image" content={ogImage} />
|
|
60
|
-
<meta property="og:site_name" content={ogTitle} />
|
|
61
|
-
|
|
62
|
-
<meta property="twitter:card" content="summary_large_image" />
|
|
63
|
-
<meta property="twitter:url" content={canonicalUrl} />
|
|
64
|
-
<meta property="twitter:title" content={ogTitle} />
|
|
65
|
-
<meta property="twitter:description" content={ogDescription} />
|
|
66
|
-
<meta property="twitter:image" content={ogImage} />
|
|
67
|
-
|
|
68
|
-
<!-- Additional meta tags -->
|
|
69
|
-
<meta name="description" content={ogDescription} />
|
|
70
|
-
<link rel="canonical" href={canonicalUrl} />
|
|
71
|
-
|
|
72
|
-
<link
|
|
73
|
-
rel="icon"
|
|
74
|
-
sizes="any"
|
|
75
|
-
href=`${getUrlFromBase('/common/assets/images/favicon.ico')}`
|
|
76
|
-
media="(prefers-color-scheme: light)"
|
|
77
|
-
/>
|
|
78
|
-
<link
|
|
79
|
-
rel="icon"
|
|
80
|
-
type="image/svg+xml"
|
|
81
|
-
href=`${getUrlFromBase('/common/assets/images/favicon-light.svg')}`
|
|
82
|
-
media="(prefers-color-scheme: light)"
|
|
83
|
-
/>
|
|
84
|
-
<link
|
|
85
|
-
rel="icon"
|
|
86
|
-
sizes="any"
|
|
87
|
-
href=`${getUrlFromBase('/common/assets/images/favicon-dark.ico')}`
|
|
88
|
-
media="(prefers-color-scheme: dark)"
|
|
89
|
-
/>
|
|
90
|
-
<link
|
|
91
|
-
rel="icon"
|
|
92
|
-
type="image/svg+xml"
|
|
93
|
-
href=`${getUrlFromBase('/common/assets/images/favicon-dark.svg')}`
|
|
94
|
-
media="(prefers-color-scheme: dark)"
|
|
95
|
-
/>
|
|
96
|
-
<!-- This belongs at the very bottom of the head -->
|
|
97
|
-
<script is:inline>
|
|
98
|
-
if ('performance' in window) {
|
|
99
|
-
performance.mark('usai:documenthead:end');
|
|
100
|
-
}
|
|
101
|
-
</script>
|
package/src/layouts/fonts.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// Centralized font imports and preload URLs.
|
|
2
|
-
import '@fontsource/archivo/400.css';
|
|
3
|
-
import '@fontsource/archivo/500.css';
|
|
4
|
-
import '@fontsource/archivo/600.css';
|
|
5
|
-
import '@fontsource/space-mono/400.css';
|
|
6
|
-
|
|
7
|
-
import archivo400Url from '@fontsource/archivo/files/archivo-latin-400-normal.woff2?url';
|
|
8
|
-
import archivo500Url from '@fontsource/archivo/files/archivo-latin-500-normal.woff2?url';
|
|
9
|
-
import archivo600Url from '@fontsource/archivo/files/archivo-latin-600-normal.woff2?url';
|
|
10
|
-
import spaceMono400Url from '@fontsource/space-mono/files/space-mono-latin-400-normal.woff2?url';
|
|
11
|
-
|
|
12
|
-
export const fontPreloads = [
|
|
13
|
-
archivo400Url,
|
|
14
|
-
archivo500Url,
|
|
15
|
-
archivo600Url,
|
|
16
|
-
spaceMono400Url,
|
|
17
|
-
];
|
package/src/pages/.gitkeep
DELETED
|
File without changes
|