@gsa-tts/graymatter-ui 0.3.0 → 0.3.2

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 (48) hide show
  1. package/README.md +33 -3
  2. package/assets/images/ai-icons/check-circle.svg +3 -0
  3. package/assets/images/ai-icons/console-icon.svg +6 -6
  4. package/assets/images/ai-icons/pause-button.svg +5 -0
  5. package/assets/images/ai-icons/play-button.svg +4 -0
  6. package/assets/images/api-hero-image.webp +0 -0
  7. package/assets/images/api-icon-w.svg +3 -0
  8. package/assets/images/chat-hero-image-mobile.webp +0 -0
  9. package/assets/images/chat-hero-image.webp +0 -0
  10. package/assets/images/chat-icon-b.svg +4 -0
  11. package/assets/images/console-hero-image.webp +0 -0
  12. package/assets/images/console-icon-w.svg +6 -0
  13. package/assets/images/og-preview.webp +0 -0
  14. package/assets/images/partners/gemini-government-logo.svg +129 -0
  15. package/assets/images/partners/microsoft-logo.svg +14 -0
  16. package/dist/graymatter-ui.js +1943 -1829
  17. package/dist/graymatter-ui.umd.cjs +37 -15
  18. package/package.json +6 -6
  19. package/src/astro-config/index.ts +10 -0
  20. package/src/component-options/componentOptions.ts +27 -13
  21. package/src/components/Button.svelte +168 -6
  22. package/src/components/DesktopSideNav.webcomponent.svelte +2 -0
  23. package/src/components/GoogleTagManager.astro +1 -1
  24. package/src/components/Head.astro +58 -24
  25. package/src/components/MobileSideMenu.svelte +2 -2
  26. package/src/components/MobileSideMenu.webcomponent.svelte +2 -4
  27. package/src/components/MobileTopNav.svelte +9 -2
  28. package/src/components/MobileTopNav.webcomponent.svelte +2 -2
  29. package/src/components/icons/ExclamationIcon.svelte +24 -0
  30. package/src/components/icons/index.ts +1 -0
  31. package/src/helpers/url.ts +13 -4
  32. package/src/layouts/BaseLayout.astro +2 -4
  33. package/src/layouts/GlobalAppLayout.astro +9 -128
  34. package/src/styles/base/theme.css +6 -1
  35. package/src/styles/base/typography.css +8 -1
  36. package/src/utils/getAppUrls.ts +27 -4
  37. package/static/uswds/styles/styles.css +1 -1
  38. package/static/uswds/styles/styles.css.map +1 -1
  39. package/assets/images/api-hero-image.png +0 -0
  40. package/assets/images/chat-hero-image.png +0 -0
  41. package/assets/images/console-hero-image.png +0 -0
  42. package/assets/images/lp-bg-hero-1024.webp +0 -0
  43. package/assets/images/lp-bg-hero-1920.webp +0 -0
  44. package/assets/images/lp-bg-hero-640.webp +0 -0
  45. package/assets/images/lp-bg-hero.png +0 -0
  46. /package/assets/images/{api-icon.svg → api-icon-b.svg} +0 -0
  47. /package/assets/images/{chat-icon.svg → chat-icon-w.svg} +0 -0
  48. /package/assets/images/{console-icon.svg → console-icon-b.svg} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsa-tts/graymatter-ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -44,7 +44,6 @@
44
44
  "@testing-library/jest-dom": "^6.6.3",
45
45
  "@testing-library/svelte": "^5.2.8",
46
46
  "@testing-library/user-event": "^14.6.1",
47
- "@turbo/gen": "^2.4.0",
48
47
  "@types/node": "^22.13.0",
49
48
  "@uswds/compile": "1.2.2",
50
49
  "@vitest/coverage-istanbul": "^3.0.7",
@@ -53,16 +52,17 @@
53
52
  "gulp": "^5.0.0",
54
53
  "svelte": "^5.35.4",
55
54
  "typescript": "5.7.3",
56
- "vitest": "^3.0.7",
57
55
  "vite": "^6.3.5",
56
+ "vitest": "^3.0.7",
57
+ "@gsa-tts/graymatter-typescript-config": "0.0.1",
58
58
  "@gsa-tts/graymatter-eslint": "0.0.1",
59
- "@gsa-tts/graymatter-vitest-config": "0.0.1",
60
- "@gsa-tts/graymatter-typescript-config": "0.0.1"
59
+ "@gsa-tts/graymatter-vitest-config": "0.0.1"
61
60
  },
62
61
  "dependencies": {
63
62
  "@fontsource-variable/archivo": "^5.2.6",
63
+ "@fontsource/space-mono": "^5.2.8",
64
64
  "@uswds/uswds": "3.12.0",
65
- "astro": "^5.11.0",
65
+ "astro": "^5.12.8",
66
66
  "slugify": "^1.6.6",
67
67
  "@gsa-tts/graymatter-style-tokens": "0.3.0"
68
68
  },
@@ -113,6 +113,16 @@ export function createAstroConfig({
113
113
  styles: ['normal'],
114
114
  optimizedFallbacks: true,
115
115
  },
116
+ {
117
+ name: 'Space Mono',
118
+ cssVariable: '--ai-font-family-monospace', // Scoped variable name
119
+ provider: fontProviders.fontsource(),
120
+ weights: [400],
121
+ fallbacks: ['monospace'],
122
+ subsets: ['latin'],
123
+ styles: ['normal'],
124
+ optimizedFallbacks: true,
125
+ },
116
126
  ],
117
127
  },
118
128
  image: {
@@ -2,38 +2,52 @@ interface WebsiteComponentOptions {
2
2
  usaBanner?: {
3
3
  theme?: 'inverse' | 'default';
4
4
  };
5
- agencyHeader?: {
5
+ header?: {
6
6
  theme?: 'inverse' | 'default';
7
7
  };
8
8
  }
9
9
 
10
- interface ApiComponentOptions {
11
- usaBanner?: {
12
- theme?: 'inverse' | 'default';
13
- };
14
- }
15
-
16
- interface ConsoleComponentOptions {
10
+ interface AppComponentOptions {
17
11
  usaBanner?: {
18
12
  theme?: 'inverse' | 'default';
19
13
  };
20
14
  }
21
15
 
22
16
  export interface ComponentOptions {
17
+ api?: AppComponentOptions;
18
+ apiSubpage?: WebsiteComponentOptions;
19
+ chatSubpage?: WebsiteComponentOptions;
20
+ console?: AppComponentOptions;
21
+ consoleSubpage?: WebsiteComponentOptions;
22
+ partnershipsSubpage?: WebsiteComponentOptions;
23
23
  website?: WebsiteComponentOptions;
24
- api?: ApiComponentOptions;
25
- console?: ConsoleComponentOptions;
26
24
  }
27
25
 
28
26
  export const componentOptions: ComponentOptions = {
29
- website: {
27
+ api: {
28
+ usaBanner: { theme: 'default' },
29
+ },
30
+ apiSubpage: {
30
31
  usaBanner: { theme: 'inverse' },
31
- agencyHeader: { theme: 'inverse' },
32
+ header: { theme: 'inverse' },
32
33
  },
33
- api: {
34
+ chatSubpage: {
34
35
  usaBanner: { theme: 'default' },
36
+ header: { theme: 'default' },
35
37
  },
36
38
  console: {
37
39
  usaBanner: { theme: 'default' },
38
40
  },
41
+ consoleSubpage: {
42
+ usaBanner: { theme: 'inverse' },
43
+ header: { theme: 'inverse' },
44
+ },
45
+ partnershipsSubpage: {
46
+ usaBanner: { theme: 'default' },
47
+ header: { theme: 'default' },
48
+ },
49
+ website: {
50
+ usaBanner: { theme: 'inverse' },
51
+ header: { theme: 'inverse' },
52
+ },
39
53
  };
@@ -1,13 +1,33 @@
1
1
  <script lang="ts">
2
+ /**
3
+ * Button Component
4
+ *
5
+ * Props:
6
+ * - variant: 'primary' | 'secondary' | 'unstyled' (default: 'primary')
7
+ * - theme: 'default' | 'inverted' (default: 'default')
8
+ * - shape: 'round' | 'square' (default: 'round')
9
+ * - size: 'small' | 'medium' | 'large' (default: 'medium')
10
+ * - href?: string (optional - renders as <a> if provided)
11
+ * - disabled?: boolean (default: false)
12
+ * - type?: 'button' | 'submit' | 'reset' (default: 'button')
13
+ *
14
+ * Examples:
15
+ * - Round primary button: <Button>Click me</Button>
16
+ * - Square secondary button: <Button variant="secondary" shape="square">Square</Button>
17
+ * - Small button (any shape): <Button size="small">Small Button</Button>
18
+ * - Small square button: <Button variant="secondary" shape="square" size="small">Small Square</Button>
19
+ * - Inverted square button: <Button theme="inverted" shape="square">Inverted Square</Button>
20
+ */
2
21
  interface Props {
3
- variant?: 'primary' | 'secondary';
22
+ variant?: 'primary' | 'secondary' | 'unstyled';
4
23
  theme?: 'default' | 'inverted';
24
+ shape?: 'round' | 'square';
5
25
  href?: string;
6
26
  disabled?: boolean;
7
27
  size: 'small' | 'medium' | 'large';
8
28
  type?: 'button' | 'submit' | 'reset';
9
29
  onclick?: (event: MouseEvent) => void;
10
- children?: string | null;
30
+ children?: import('svelte').Snippet | null;
11
31
  class?: string;
12
32
  [key: `data-${string}`]: string;
13
33
  [key: `aria-${string}`]: string;
@@ -16,7 +36,8 @@
16
36
  let {
17
37
  variant = 'primary',
18
38
  theme = 'default',
19
- href = null,
39
+ shape = 'round',
40
+ href = undefined,
20
41
  disabled = false,
21
42
  onclick,
22
43
  children = null,
@@ -41,6 +62,7 @@
41
62
  variant === 'primary' ? 'usa-button--primary' : '',
42
63
  variant === 'secondary' ? 'usa-button--outline ai-button--outline' : '',
43
64
  variant === 'unstyled' ? 'ai-button--unstyled' : '',
65
+ shape === 'square' ? 'ai-button--square' : '',
44
66
  ]
45
67
  .filter(Boolean)
46
68
  .join(' ');
@@ -60,6 +82,7 @@
60
82
  {/if}
61
83
 
62
84
  <style>
85
+ /*! purgecss start ignore */
63
86
  :root,
64
87
  :host {
65
88
  /* global button */
@@ -106,6 +129,37 @@
106
129
  --ai-button-border-outline-inverted-hover: var(
107
130
  --ai-button-border-outline-inverted-rest
108
131
  );
132
+
133
+ /* square button specific tokens */
134
+ --ai-button-radius-square: var(--ai-size-4);
135
+ --ai-button-padding-x-square: var(--ai-size-12);
136
+ --ai-button-padding-y-square: var(--ai-size-6);
137
+ --ai-button-height-square: var(--ai-size-40);
138
+ --ai-button-width-square: 100%;
139
+
140
+ /* small square button tokens */
141
+ --ai-button-padding-x-square-small: var(--ai-size-8);
142
+ --ai-button-padding-y-square-small: var(--ai-size-4);
143
+ --ai-button-height-square-small: var(--ai-size-24);
144
+
145
+ /* square button color tokens */
146
+ --ai-button-background-square-primary-rest: var(--ai-color-black);
147
+ --ai-button-background-square-primary-hover: var(--ai-color-neutral-900);
148
+ --ai-button-background-square-primary-disabled: var(--ai-color-neutral-200);
149
+ --ai-button-text-square-primary-rest: var(--ai-color-white);
150
+ --ai-button-text-square-primary-hover: var(--ai-color-white);
151
+ --ai-button-text-square-primary-disabled: var(--ai-color-neutral-800);
152
+
153
+ --ai-button-background-square-secondary-rest: var(--ai-color-white);
154
+ --ai-button-background-square-secondary-hover: var(--ai-color-neutral-200);
155
+ --ai-button-background-square-secondary-disabled: var(
156
+ --ai-color-neutral-500
157
+ );
158
+ --ai-button-text-square-secondary-rest: var(--ai-color-black);
159
+ --ai-button-text-square-secondary-hover: var(--ai-color-black);
160
+ --ai-button-text-square-secondary-disabled: var(--ai-color-neutral-900);
161
+ --ai-button-border-square-secondary-rest: var(--ai-color-black);
162
+ --ai-button-border-square-secondary-hover: var(--ai-color-black);
109
163
  }
110
164
 
111
165
  .ai-button {
@@ -116,6 +170,11 @@
116
170
  padding-inline: var(--ai-button-padding-x);
117
171
  padding-block: var(--ai-button-padding-y);
118
172
  margin: 0;
173
+ width: fit-content;
174
+ }
175
+
176
+ .ai-button--no-gap {
177
+ gap: 0;
119
178
  }
120
179
 
121
180
  .ai-button:is(:hover, :focus) {
@@ -169,6 +228,108 @@
169
228
  border: var(--ai-button-border-inverted-hover);
170
229
  }
171
230
 
231
+ /* Square button styles */
232
+ .ai-button--square {
233
+ border-radius: var(--ai-button-radius-square);
234
+ padding-inline: var(--ai-button-padding-x-square);
235
+ padding-block: var(--ai-button-padding-y-square);
236
+ height: var(--ai-button-height-square);
237
+ width: var(--ai-button-width-square);
238
+ width: 100%;
239
+ display: flex;
240
+ align-items: center;
241
+ justify-content: center;
242
+ border: 0.0625rem solid var(--ai-color-neutral-300);
243
+ box-shadow: 0 0.0625rem var(--ai-size-2) 0 rgba(0, 0, 0, 0.1);
244
+ transition: all var(--ai-transition-ease-fast);
245
+ }
246
+
247
+ /* Small square button specific adjustments */
248
+ .ai-button--small.ai-button--square {
249
+ padding-inline: var(--ai-button-padding-x-square-small);
250
+ padding-block: var(--ai-button-padding-y-square-small);
251
+ height: var(--ai-button-height-square-small);
252
+ }
253
+
254
+ .ai-button--square:is(:hover, :focus) {
255
+ border-color: var(--ai-color-neutral-400);
256
+ box-shadow: 0 var(--ai-size-2) var(--ai-size-4) 0 rgba(0, 0, 0, 0.3);
257
+ }
258
+
259
+ .ai-button--square:focus {
260
+ outline-offset: var(--ai-size-2);
261
+ }
262
+
263
+ /* Square button variants */
264
+ .ai-button--square.ai-button--primary {
265
+ background-color: var(--ai-button-background-square-primary-rest);
266
+ color: var(--ai-button-text-square-primary-rest);
267
+ border-color: transparent;
268
+ box-shadow: none;
269
+ }
270
+
271
+ .ai-button--square.ai-button--primary:is(:hover, :focus) {
272
+ background-color: var(--ai-button-background-square-primary-hover);
273
+ color: var(--ai-button-text-square-primary-hover);
274
+ border-color: transparent;
275
+ box-shadow: none;
276
+ }
277
+
278
+ .ai-button--square.ai-button--secondary {
279
+ background-color: var(--ai-button-background-square-secondary-rest);
280
+ color: var(--ai-button-text-square-secondary-rest);
281
+ border-color: var(--ai-button-border-square-secondary-rest);
282
+ }
283
+
284
+ .ai-button--square.ai-button--secondary:is(:hover, :focus) {
285
+ background-color: var(--ai-button-background-square-secondary-hover);
286
+ color: var(--ai-button-text-square-secondary-hover);
287
+ border-color: var(--ai-button-border-square-secondary-hover);
288
+ }
289
+
290
+ .ai-button--square.ai-button--outline {
291
+ background-color: var(--ai-color-white);
292
+ color: var(--ai-color-neutral-800);
293
+ border-color: var(--ai-color-neutral-300);
294
+ }
295
+
296
+ .ai-button--square.ai-button--outline:is(:hover, :focus) {
297
+ background-color: var(--ai-color-neutral-50);
298
+ color: var(--ai-color-black);
299
+ border-color: var(--ai-color-neutral-400);
300
+ }
301
+
302
+ /* Square button inverted themes */
303
+ .ai-button--square.ai-button--inverted {
304
+ background-color: var(--ai-button-background-inverted-rest);
305
+ color: var(--ai-button-text-inverted-rest);
306
+ border-color: transparent;
307
+ }
308
+
309
+ .ai-button--square.ai-button--inverted:is(:hover, :focus) {
310
+ background-color: var(--ai-button-background-inverted-hover);
311
+ color: var(--ai-button-text-inverted-hover);
312
+ border-color: transparent;
313
+ }
314
+
315
+ .ai-button--square.ai-button--outline-inverted {
316
+ background-color: transparent;
317
+ color: var(--ai-color-white);
318
+ border-color: var(--ai-color-neutral-300);
319
+ }
320
+
321
+ .ai-button--square.ai-button--outline-inverted:is(:hover, :focus) {
322
+ background-color: var(--ai-color-neutral-900);
323
+ color: var(--ai-color-white);
324
+ border-color: var(--ai-color-neutral-400);
325
+ }
326
+
327
+ .ai-menu-buttons {
328
+ display: flex;
329
+ gap: var(--ai-size-16);
330
+ }
331
+
332
+ /* Unstyled button styles */
172
333
  .ai-button--unstyled {
173
334
  background: none;
174
335
  color: inherit;
@@ -189,9 +350,9 @@
189
350
  text-decoration: underline;
190
351
  }
191
352
 
192
- .ai-menu-buttons {
193
- display: flex;
194
- gap: var(--ai-size-16);
353
+ /* Unstyled square button - just center the text, reuse existing unstyled styles */
354
+ .ai-button--unstyled.ai-button--square {
355
+ text-align: center;
195
356
  }
196
357
 
197
358
  /* disabled button styles */
@@ -226,4 +387,5 @@
226
387
  color: inherit;
227
388
  opacity: 0.75;
228
389
  }
390
+ /*! purgecss end ignore */
229
391
  </style>
@@ -18,6 +18,7 @@
18
18
  chatUrl,
19
19
  consoleUrl,
20
20
  discoverUrl,
21
+ children,
21
22
  } = $props();
22
23
  </script>
23
24
 
@@ -31,6 +32,7 @@
31
32
  {chatUrl}
32
33
  {consoleUrl}
33
34
  {discoverUrl}
35
+ {children}
34
36
  >
35
37
  <slot />
36
38
  </DesktopSideNav>
@@ -10,7 +10,7 @@ if (Astro.props.gtmID) {
10
10
  gtag('js', new Date());
11
11
  gtag('config', '${Astro.props.gtmID}');
12
12
  </script>
13
- <script id="_fed_an_ua_tag" async type="text/javascript" src="https://dap.digitalgov.gov/Universal-Federated-Analytics-Min.js?agency=GSA&subagency=TTS"></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
14
  }
15
15
  ---
16
16
 
@@ -1,55 +1,89 @@
1
1
  ---
2
- const { title } = Astro.props;
2
+ import { siteName } from '../constants';
3
+ const { title, description, openGraphImage, gtmID } = Astro.props;
3
4
  import { Font } from 'astro:assets';
4
5
  import { getUrlFromBase } from '@gsa-tts/graymatter-ui/helpers';
6
+ import GoogleTagManager from './GoogleTagManager.astro';
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
+ const pageTitle = `${title} | USAi`;
14
+ const ogTitle = title || siteName;
15
+ const ogDescription = description || defaultDescription;
16
+ const ogImage = openGraphImage || defaultOgImage;
17
+ const siteUrl = Astro.site || Astro.url.origin;
18
+ const canonicalUrl = new URL(Astro.url.pathname, siteUrl).href;
5
19
  ---
6
20
 
7
21
  <meta name="viewport" content="width=device-width" />
22
+ <title>{pageTitle}</title>
23
+ <script is:inline>
24
+ if ('performance' in window) {
25
+ performance.mark('usai:documenthead:start');
26
+ }
27
+ </script>
28
+ {gtmID && <GoogleTagManager {gtmID} />}
29
+ <script is:inline src=`${getUrlFromBase('uswds/js/uswds-init.min.js')}`
30
+ ></script>
31
+ <Font cssVariable="--ai-font-family-sans" preload />
32
+ <Font cssVariable="--ai-font-family-monospace" />
33
+ <style>
34
+ body {
35
+ font-family: var(--ai-font-family-sans);
36
+ }
37
+ </style>
38
+ <script>
39
+ import '../../static/uswds/js/uswds.js';
40
+ </script>
41
+
42
+ <!-- Open Graph -->
43
+ <meta property="og:type" content="website" />
44
+ <meta property="og:url" content={canonicalUrl} />
45
+ <meta property="og:title" content={ogTitle} />
46
+ <meta property="og:description" content={ogDescription} />
47
+ <meta property="og:image" content={ogImage} />
48
+ <meta property="og:site_name" content={ogTitle} />
49
+
50
+ <meta property="twitter:card" content="summary_large_image" />
51
+ <meta property="twitter:url" content={canonicalUrl} />
52
+ <meta property="twitter:title" content={ogTitle} />
53
+ <meta property="twitter:description" content={ogDescription} />
54
+ <meta property="twitter:image" content={ogImage} />
55
+
56
+ <!-- Additional meta tags -->
57
+ <meta name="description" content={ogDescription} />
58
+ <link rel="canonical" href={canonicalUrl} />
59
+
8
60
  <link
9
61
  rel="icon"
10
62
  sizes="any"
11
- href=`${getUrlFromBase('/assets/images/favicon-light.ico')}`
63
+ href=`${getUrlFromBase('/common/assets/images/favicon.ico')}`
12
64
  media="(prefers-color-scheme: light)"
13
65
  />
14
66
  <link
15
67
  rel="icon"
16
68
  type="image/svg+xml"
17
- href=`${getUrlFromBase('/assets/images/favicon-light.svg')}`
69
+ href=`${getUrlFromBase('/common/assets/images/favicon-light.svg')}`
18
70
  media="(prefers-color-scheme: light)"
19
71
  />
20
72
  <link
21
73
  rel="icon"
22
74
  sizes="any"
23
- href=`${getUrlFromBase('/assets/images/favicon-dark.ico')}`
75
+ href=`${getUrlFromBase('/common/assets/images/favicon-dark.ico')}`
24
76
  media="(prefers-color-scheme: dark)"
25
77
  />
26
78
  <link
27
79
  rel="icon"
28
80
  type="image/svg+xml"
29
- href=`${getUrlFromBase('/assets/images/favicon-dark.svg')}`
81
+ href=`${getUrlFromBase('/common/assets/images/favicon-dark.svg')}`
30
82
  media="(prefers-color-scheme: dark)"
31
83
  />
32
- <meta name="generator" content={Astro.generator} />
33
- <title>{title}</title>
34
- <script is:inline>
35
- if ('performance' in window) {
36
- performance.mark('gsa:documenthead:start');
37
- }
38
- </script>
39
- <script is:inline src=`${getUrlFromBase('uswds/js/uswds-init.min.js')}`
40
- ></script>
41
- <Font cssVariable="--ai-font-family-sans" preload />
42
- <style>
43
- body {
44
- font-family: var(--ai-font-family-sans);
45
- }
46
- </style>
47
- <script>
48
- import '../../static/uswds/js/uswds.js';
49
- </script>
50
84
  <!-- This belongs at the very bottom of the head -->
51
85
  <script is:inline>
52
86
  if ('performance' in window) {
53
- performance.mark('gsa:documenthead:end');
87
+ performance.mark('usai:documenthead:end');
54
88
  }
55
89
  </script>
@@ -11,7 +11,7 @@
11
11
  let sideMenuElement: HTMLDivElement;
12
12
  let pendingMobileHash: string | null = null;
13
13
 
14
- let { logoLinkUrl, logoLinkLabel } = $props();
14
+ let { logoLinkUrl, logoLinkLabel, children } = $props();
15
15
 
16
16
  function handleMobileAnchorNavigate(event: Event) {
17
17
  const customEvent = event as CustomEvent;
@@ -180,7 +180,7 @@
180
180
  <!-- Show submenu items if available and not on discover -->
181
181
  {#if $selectedItem && $selectedItem !== 'discover'}
182
182
  <div class="subnav-section" part="subnav-section">
183
- <slot />
183
+ {@render children?.()}
184
184
  </div>
185
185
  {:else}
186
186
  <!-- Show message when no submenu is available -->
@@ -7,9 +7,7 @@
7
7
  <script lang="ts">
8
8
  import MobileSideMenu from './MobileSideMenu.svelte';
9
9
 
10
- let { logoLinkUrl, logoLinkLabel } = $props();
10
+ let { logoLinkUrl, logoLinkLabel, children } = $props();
11
11
  </script>
12
12
 
13
- <MobileSideMenu {logoLinkUrl} {logoLinkLabel}>
14
- <slot />
15
- </MobileSideMenu>
13
+ <MobileSideMenu {logoLinkUrl} {logoLinkLabel} {children} />
@@ -8,7 +8,7 @@
8
8
  import MenuIcon from './icons/MenuIcon.svelte';
9
9
  import { onMount } from 'svelte';
10
10
 
11
- let { profileMenuData } = $props();
11
+ let { profileMenuData, logoLinkUrl, logoLinkLabel } = $props();
12
12
 
13
13
  import ProfileMenu from './ProfileMenu.svelte';
14
14
 
@@ -90,7 +90,14 @@
90
90
  </button>
91
91
 
92
92
  <div class="brand">
93
- <Logo width={75} height={22} />
93
+ {#if logoLinkUrl}
94
+ <a class="display-flex" href={logoLinkUrl} aria-label={logoLinkLabel}>
95
+ <Logo width={75} height={22} />
96
+ </a>
97
+ {:else}
98
+ <Logo width={75} height={22} />
99
+ {/if}
100
+
94
101
  <span class="brand-text">{$selectedSubNavItemTitle || ''}</span>
95
102
  </div>
96
103
 
@@ -8,7 +8,7 @@
8
8
  import MobileTopNav from './MobileTopNav.svelte';
9
9
 
10
10
  // Props for the web component
11
- let { profileMenuData } = $props();
11
+ let { profileMenuData, logoLinkUrl, logoLinkLabel } = $props();
12
12
  </script>
13
13
 
14
- <MobileTopNav {profileMenuData} />
14
+ <MobileTopNav {profileMenuData} {logoLinkUrl} {logoLinkLabel} />
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ class?: string;
4
+ size?: number;
5
+ }
6
+
7
+ let { class: className = '', size = 32 }: Props = $props();
8
+ </script>
9
+
10
+ <svg
11
+ class={className}
12
+ width={size}
13
+ height={size}
14
+ viewBox="0 0 32 33"
15
+ fill="none"
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ >
18
+ <path
19
+ fill-rule="evenodd"
20
+ clip-rule="evenodd"
21
+ d="M3 16.5028C3 9.32311 8.8203 3.50281 16 3.50281C23.1797 3.50281 29 9.32311 29 16.5028C29 23.6825 23.1797 29.5028 16 29.5028C8.8203 29.5028 3 23.6825 3 16.5028ZM16 11.5028C16.5523 11.5028 17 11.9505 17 12.5028V17.5028C17 18.0551 16.5523 18.5028 16 18.5028C15.4477 18.5028 15 18.0551 15 17.5028V12.5028C15 11.9505 15.4477 11.5028 16 11.5028ZM16 22.5028C16.5523 22.5028 17 22.0551 17 21.5028C17 20.9505 16.5523 20.5028 16 20.5028C15.4477 20.5028 15 20.9505 15 21.5028C15 22.0551 15.4477 22.5028 16 22.5028Z"
22
+ fill="currentColor"
23
+ />
24
+ </svg>
@@ -4,3 +4,4 @@ export { default as ConsoleIcon } from './ConsoleIcon.svelte';
4
4
  export { default as ApiIcon } from './ApiIcon.svelte';
5
5
  export { default as MenuIcon } from './MenuIcon.svelte';
6
6
  export { default as CloseIcon } from './CloseIcon.svelte';
7
+ export { default as ExclamationIcon } from './ExclamationIcon.svelte';
@@ -1,13 +1,22 @@
1
1
  import { normalizeTrailingSlash } from './string-formatters.js';
2
2
 
3
3
  export const getBaseUrl = () => {
4
- const { BASEURL } = process.env;
5
-
6
- if (!BASEURL) {
4
+ // Check if we're in a browser environment
5
+ if (typeof window !== 'undefined') {
7
6
  return '/';
8
7
  }
9
8
 
10
- return normalizeTrailingSlash(BASEURL);
9
+ // Node.js environment
10
+ if (typeof process !== 'undefined' && process.env) {
11
+ const { BASEURL } = process.env;
12
+ if (!BASEURL) {
13
+ return '/';
14
+ }
15
+ return normalizeTrailingSlash(BASEURL);
16
+ }
17
+
18
+ // Fallback
19
+ return '/';
11
20
  };
12
21
 
13
22
  export const getUrlFromBase = (assetPath?: string) => {
@@ -4,6 +4,7 @@ import Head from '../components/Head.astro';
4
4
  import UsaSkipNav from '../components/UsaSkipNav.svelte';
5
5
  import { getPageTitle } from '../helpers/meta';
6
6
  import GoogleTagManager from '../components/GoogleTagManager.astro';
7
+ const { description, openGraphImage, gtmID } = Astro.props;
7
8
 
8
9
  const title = getPageTitle(Astro);
9
10
  ---
@@ -12,10 +13,7 @@ const title = getPageTitle(Astro);
12
13
  <html lang="en">
13
14
  <head>
14
15
  <meta charset="UTF-8" />
15
- <Head title={title} />
16
- <slot name="analytics">
17
- <GoogleTagManager gtmID={Astro.props.gtmID} />
18
- </slot>
16
+ <Head {title} {gtmID} {openGraphImage} {description} />
19
17
  </head>
20
18
  <body>
21
19
  <slot name="skip-nav">