@doneisbetter/gds-core 2.6.6 → 3.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.
@@ -7,7 +7,7 @@ import {
7
7
  StateBlock,
8
8
  getSemanticActionLabel,
9
9
  resolveSemanticActionConfig
10
- } from "./chunk-IAP3JU55.mjs";
10
+ } from "./chunk-P7ICTEEB.mjs";
11
11
 
12
12
  // src/SemanticButton.tsx
13
13
  import { useEffect, useState } from "react";
@@ -141,42 +141,77 @@ import {
141
141
  gdsTheme
142
142
  } from "@doneisbetter/gds-theme";
143
143
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
144
+ function resolvePreviewColorScheme(presetId, requestedScheme) {
145
+ if (presetId === "dark-public") {
146
+ return "dark";
147
+ }
148
+ return requestedScheme;
149
+ }
144
150
  var themePresetCatalog = {
145
151
  default: {
146
152
  label: "Default runtime theme",
147
153
  bestFor: "Balanced multi-surface products that need the baseline GDS system.",
148
154
  summary: "The default shared runtime lane for most adopters.",
149
- themeKey: "gdsTheme"
155
+ themeKey: "gdsTheme",
156
+ supportedUse: "Starter products, hybrid public/admin apps, and teams adopting all canonical contracts.",
157
+ avoidFor: "Avoid for products requiring a distinct editorial voice or guaranteed dark-first brand contrast."
150
158
  },
151
159
  "dark-public": {
152
160
  label: "Dark public theme",
153
161
  bestFor: "Dark-first public experiences and campaign-style landing surfaces.",
154
162
  summary: "A darker public presentation lane with the shipped runtime rhythm intact.",
155
- themeKey: "gdsDarkPublicTheme"
163
+ themeKey: "gdsDarkPublicTheme",
164
+ supportedUse: "Products with a deliberate dark visual baseline, low-light UX, or premium media-first surfaces.",
165
+ avoidFor: "Avoid when mixed mode and editorial readability are core requirements."
156
166
  },
157
167
  "flat-surface": {
158
168
  label: "Flat surface theme",
159
169
  bestFor: "Operational products that prefer quieter elevation and flatter surface contrast.",
160
170
  summary: "Removes some visual weight without creating a second token authority.",
161
- themeKey: "gdsFlatSurfaceTheme"
171
+ themeKey: "gdsFlatSurfaceTheme",
172
+ supportedUse: "Dashboards, admin surfaces, and dense content where elevation becomes distracting.",
173
+ avoidFor: "Avoid for brand-first storytelling or high-expressiveness hero-first pages."
162
174
  },
163
175
  editorial: {
164
176
  label: "Editorial serif theme",
165
177
  bestFor: "Documentation, editorial, and content-led experiences.",
166
178
  summary: "Adds a more expressive public reading tone while staying inside GDS contracts.",
167
- themeKey: "gdsEditorialPublicTheme"
179
+ themeKey: "gdsEditorialPublicTheme",
180
+ supportedUse: "Guides, docs, and catalog content where reading comfort matters more than impact.",
181
+ avoidFor: "Avoid for dense transactional or data-first workflows with strict minimal contrast."
168
182
  },
169
183
  brand: {
170
184
  label: "Brand theme generator",
171
185
  bestFor: "Consumer teams that need controlled brand expression without forking the system.",
172
186
  summary: "Composes the shipped helpers into a governed product-authored theme lane.",
173
- themeKey: "createPublicBrandTheme(...)"
187
+ themeKey: "createPublicBrandTheme(...)",
188
+ supportedUse: "Whitelisted public branding programs with narrow product-authored intent and policy guardrails.",
189
+ avoidFor: "Avoid for local style experiments or temporary visual fixes without compliance approval."
174
190
  }
175
191
  };
192
+ var colorSchemeProof = [
193
+ {
194
+ id: "light",
195
+ label: "Light",
196
+ description: "Validates readable default surfaces, controls, badges, and focus states against light backgrounds."
197
+ },
198
+ {
199
+ id: "dark",
200
+ label: "Dark",
201
+ description: "Validates contrast for public, operational, and feedback surfaces when dark mode is active."
202
+ },
203
+ {
204
+ id: "auto",
205
+ label: "Auto",
206
+ description: "Documents the adopter path for OS-controlled schemes while keeping the provider contract unchanged."
207
+ }
208
+ ];
176
209
  function ThemePreviewSurface({
177
210
  preset,
178
- colorScheme
211
+ colorScheme,
212
+ requestedColorScheme
179
213
  }) {
214
+ const forcedScheme = requestedColorScheme && requestedColorScheme !== colorScheme;
180
215
  return /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "lg", children: [
181
216
  /* @__PURE__ */ jsxs2(Group2, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
182
217
  /* @__PURE__ */ jsxs2(Stack, { gap: 4, children: [
@@ -191,7 +226,12 @@ function ThemePreviewSurface({
191
226
  /* @__PURE__ */ jsx4("strong", { children: "Color scheme:" }),
192
227
  " ",
193
228
  colorScheme
194
- ] })
229
+ ] }),
230
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
231
+ /* @__PURE__ */ jsx4("strong", { children: "Accessibility proof:" }),
232
+ " status uses text, badge label, and placement, not color alone."
233
+ ] }),
234
+ forcedScheme ? /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "This lane always previews in dark mode so the runtime stays inside its sanctioned contrast contract." }) : null
195
235
  ] }),
196
236
  /* @__PURE__ */ jsx4(ThemeToggle, {})
197
237
  ] }),
@@ -220,7 +260,8 @@ function ThemePreviewSurface({
220
260
  description: "This preview uses the real shipped design-system runtime rather than a docs-only styling lane.",
221
261
  metadata: [
222
262
  { id: "runtime", label: "Runtime lane", value: preset.themeKey },
223
- { id: "scheme", label: "Color scheme", value: colorScheme }
263
+ { id: "scheme", label: "Color scheme", value: colorScheme },
264
+ { id: "focus", label: "A11y proof", value: "Keyboard + readable states" }
224
265
  ],
225
266
  primaryAction: /* @__PURE__ */ jsx4(Button2, { size: "sm", children: "Inspect route" })
226
267
  }
@@ -258,8 +299,10 @@ function ReferenceThemeExplorer() {
258
299
  const comparisonSummary = themePresetCatalog[comparisonPreset];
259
300
  const selectedTheme = useMemo(() => resolveTheme(preset), [preset, brandPrimary, brandFlatSurfaces, brandEditorialSerif]);
260
301
  const comparedTheme = useMemo(() => resolveTheme(comparisonPreset), [comparisonPreset, brandPrimary, brandFlatSurfaces, brandEditorialSerif]);
261
- const previewKey = `${preset}-${colorScheme}-${brandPrimary}-${brandFlatSurfaces}-${brandEditorialSerif}`;
262
- const comparisonPreviewKey = `${comparisonPreset}-${colorScheme}-${brandPrimary}-${brandFlatSurfaces}-${brandEditorialSerif}`;
302
+ const effectiveColorScheme = resolvePreviewColorScheme(preset, colorScheme);
303
+ const effectiveComparisonScheme = resolvePreviewColorScheme(comparisonPreset, colorScheme);
304
+ const previewKey = `${preset}-${effectiveColorScheme}-${brandPrimary}-${brandFlatSurfaces}-${brandEditorialSerif}`;
305
+ const comparisonPreviewKey = `${comparisonPreset}-${effectiveComparisonScheme}-${brandPrimary}-${brandFlatSurfaces}-${brandEditorialSerif}`;
263
306
  const reset = () => {
264
307
  setPreset("default");
265
308
  setColorScheme("light");
@@ -335,7 +378,7 @@ function ReferenceThemeExplorer() {
335
378
  ),
336
379
  /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "The generator composes shipped helpers instead of creating a second theme authority inside the website." })
337
380
  ] }) }),
338
- /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
381
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", role: "status", "aria-live": "polite", children: [
339
382
  /* @__PURE__ */ jsx4(Title, { order: 4, children: "Current selection summary" }),
340
383
  /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
341
384
  /* @__PURE__ */ jsx4(Text2, { fw: 700, children: selectionSummary.label }),
@@ -354,7 +397,8 @@ function ReferenceThemeExplorer() {
354
397
  /* @__PURE__ */ jsx4("strong", { children: "Color scheme:" }),
355
398
  " ",
356
399
  colorScheme
357
- ] })
400
+ ] }),
401
+ preset === "dark-public" && colorScheme !== effectiveColorScheme ? /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "gdsDarkPublicTheme always renders in dark mode inside the live preview." }) : null
358
402
  ] }),
359
403
  /* @__PURE__ */ jsx4(
360
404
  Checkbox,
@@ -387,18 +431,47 @@ function ReferenceThemeExplorer() {
387
431
  children: /* @__PURE__ */ jsx4(SimpleGrid, { cols: { base: 1, md: 2, xl: 5 }, spacing: "md", children: Object.values(themePresetCatalog).map((lane) => /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
388
432
  /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: lane.label }),
389
433
  /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: lane.summary }),
390
- /* @__PURE__ */ jsx4(Code, { block: true, fz: "10px", children: lane.themeKey })
434
+ /* @__PURE__ */ jsxs2(Text2, { size: "xs", children: [
435
+ /* @__PURE__ */ jsx4("strong", { children: "Best for:" }),
436
+ " ",
437
+ lane.supportedUse
438
+ ] }),
439
+ /* @__PURE__ */ jsx4(Code, { block: true, fz: "10px", children: lane.themeKey }),
440
+ /* @__PURE__ */ jsxs2(Text2, { size: "xs", c: "dimmed", children: [
441
+ /* @__PURE__ */ jsx4("strong", { children: "Avoid for:" }),
442
+ " ",
443
+ lane.avoidFor ?? "No special exclusion noted for this lane."
444
+ ] })
391
445
  ] }) }, lane.themeKey)) })
392
446
  }
393
447
  ),
448
+ /* @__PURE__ */ jsx4(
449
+ ReferenceSection,
450
+ {
451
+ title: "Light, dark, and auto proof",
452
+ description: "Every official lane must remain usable across explicit light, explicit dark, and OS-controlled auto modes. The dark-public lane is intentionally forced to dark in preview to preserve its contrast contract.",
453
+ children: /* @__PURE__ */ jsx4(SimpleGrid, { cols: { base: 1, md: 3 }, spacing: "md", children: colorSchemeProof.map((item) => /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
454
+ /* @__PURE__ */ jsx4(Badge, { variant: "light", color: item.id === "dark" ? "violet" : item.id === "auto" ? "teal" : "blue", w: "fit-content", children: item.label }),
455
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", children: item.description }),
456
+ /* @__PURE__ */ jsx4(Text2, { size: "xs", c: "dimmed", children: "Required proof: semantic text, visible focus, and contrast-safe state treatment." })
457
+ ] }) }, item.id)) })
458
+ }
459
+ ),
394
460
  /* @__PURE__ */ jsx4(
395
461
  ReferenceSection,
396
462
  {
397
463
  title: "Live Theme Preview",
398
464
  description: "Visitors can test the shipped presets, compare lanes, and inspect actual GDS surfaces under each theme.",
399
465
  children: /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, xl: comparisonEnabled ? 2 : 1 }, spacing: "lg", children: [
400
- /* @__PURE__ */ jsx4(GdsProvider, { theme: selectedTheme, defaultColorScheme: colorScheme, children: /* @__PURE__ */ jsx4(ThemePreviewSurface, { preset: selectionSummary, colorScheme }) }, previewKey),
401
- comparisonEnabled ? /* @__PURE__ */ jsx4(GdsProvider, { theme: comparedTheme, defaultColorScheme: colorScheme, children: /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
466
+ /* @__PURE__ */ jsx4(GdsProvider, { theme: selectedTheme, defaultColorScheme: effectiveColorScheme, children: /* @__PURE__ */ jsx4(
467
+ ThemePreviewSurface,
468
+ {
469
+ preset: selectionSummary,
470
+ colorScheme: effectiveColorScheme,
471
+ requestedColorScheme: colorScheme
472
+ }
473
+ ) }, previewKey),
474
+ comparisonEnabled ? /* @__PURE__ */ jsx4(GdsProvider, { theme: comparedTheme, defaultColorScheme: effectiveComparisonScheme, children: /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
402
475
  /* @__PURE__ */ jsxs2(Group2, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
403
476
  /* @__PURE__ */ jsxs2(Stack, { gap: 4, children: [
404
477
  /* @__PURE__ */ jsx4(Text2, { fw: 700, children: "Comparison Preview Surface" }),
@@ -406,7 +479,14 @@ function ReferenceThemeExplorer() {
406
479
  ] }),
407
480
  /* @__PURE__ */ jsx4(Badge, { color: "violet", variant: "light", children: comparisonSummary.label })
408
481
  ] }),
409
- /* @__PURE__ */ jsx4(ThemePreviewSurface, { preset: comparisonSummary, colorScheme })
482
+ /* @__PURE__ */ jsx4(
483
+ ThemePreviewSurface,
484
+ {
485
+ preset: comparisonSummary,
486
+ colorScheme: effectiveComparisonScheme,
487
+ requestedColorScheme: colorScheme
488
+ }
489
+ )
410
490
  ] }) }) }, comparisonPreviewKey) : null
411
491
  ] })
412
492
  }
@@ -414,18 +494,34 @@ function ReferenceThemeExplorer() {
414
494
  /* @__PURE__ */ jsx4(
415
495
  ReferenceSection,
416
496
  {
417
- title: "Creator-Authored Experience Boundary",
418
- description: "Creator-authored expression is allowed only through the sanctioned theme helpers and narrow exception process.",
497
+ title: "Unsupported lane boundary",
498
+ description: "Unsupported local theme lanes are blocked by policy and compliance because they create parallel design-system authority.",
419
499
  tone: "supporting",
420
- children: /* @__PURE__ */ jsx4(
421
- StateBlock,
422
- {
423
- variant: "info",
424
- title: "Shipped first, custom second",
425
- description: "The official site uses shipped presets and the public brand generator first. Product-authored overrides must stay reviewable, testable, and scoped.",
426
- compact: true
427
- }
428
- )
500
+ children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
501
+ /* @__PURE__ */ jsx4(
502
+ StateBlock,
503
+ {
504
+ variant: "permission",
505
+ title: "Do not create local branding-layer helpers",
506
+ description: "If a consumer needs brand expression, use createPublicBrandTheme(...). If a lane is missing, request it for GDS instead of building extendGdsTheme(...), createTheme(...), or mergeMantineTheme(...) ownership locally.",
507
+ compact: true
508
+ }
509
+ ),
510
+ /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
511
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
512
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: "Approved remediation" }),
513
+ /* @__PURE__ */ jsxs2(Code, { block: true, children: [
514
+ "createPublicBrandTheme(",
515
+ `{ flatSurfaces: true, overrides: { primaryColor: 'blue' } }`,
516
+ ")"
517
+ ] })
518
+ ] }) }),
519
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
520
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: "Prohibited ownership" }),
521
+ /* @__PURE__ */ jsx4(Code, { block: true, children: "extendGdsTheme(...) / createTheme(...) / mergeMantineTheme(...)" })
522
+ ] }) })
523
+ ] })
524
+ ] })
429
525
  }
430
526
  )
431
527
  ] });
@@ -529,18 +625,94 @@ function DiscoveryShell({
529
625
  );
530
626
  }
531
627
 
628
+ // src/DocsShell.tsx
629
+ import { useState as useState3 } from "react";
630
+ import { Box as Box2, Burger as Burger2, Container, Divider, Group as Group4, Stack as Stack2, Text as Text4, Transition } from "@mantine/core";
631
+ import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
632
+ function DocsShellSidebar({ primaryNavigation, secondaryNavigation }) {
633
+ return /* @__PURE__ */ jsxs4(Stack2, { gap: "md", h: "100%", children: [
634
+ primaryNavigation ? /* @__PURE__ */ jsxs4(Stack2, { gap: "xs", children: [
635
+ /* @__PURE__ */ jsx7(Text4, { size: "xs", fw: 700, c: "dimmed", children: "Primary" }),
636
+ primaryNavigation
637
+ ] }) : null,
638
+ secondaryNavigation ? /* @__PURE__ */ jsxs4(Fragment, { children: [
639
+ /* @__PURE__ */ jsx7(Divider, {}),
640
+ /* @__PURE__ */ jsxs4(Stack2, { gap: "xs", children: [
641
+ /* @__PURE__ */ jsx7(Text4, { size: "xs", fw: 700, c: "dimmed", children: "More" }),
642
+ secondaryNavigation
643
+ ] })
644
+ ] }) : null
645
+ ] });
646
+ }
647
+ function resolveContentContainerSize(value) {
648
+ if (value === "full") {
649
+ return "100%";
650
+ }
651
+ return value ?? "lg";
652
+ }
653
+ function DocsShell({
654
+ brand,
655
+ primaryNavigation,
656
+ secondaryNavigation,
657
+ headerContext,
658
+ actions,
659
+ mobileNavigation,
660
+ mobileNavigationMode = "drawer",
661
+ footer,
662
+ children,
663
+ contentWidth = "full"
664
+ }) {
665
+ const [mobileNavOpen, setMobileNavOpen] = useState3(false);
666
+ return /* @__PURE__ */ jsx7(
667
+ DiscoveryShell,
668
+ {
669
+ header: /* @__PURE__ */ jsxs4(Group4, { h: "100%", justify: "space-between", align: "center", wrap: "nowrap", gap: "md", children: [
670
+ /* @__PURE__ */ jsxs4(Group4, { gap: "sm", align: "center", wrap: "nowrap", style: { minWidth: 0 }, children: [
671
+ mobileNavigationMode === "inline-collapse" && mobileNavigation ? /* @__PURE__ */ jsxs4(Fragment, { children: [
672
+ /* @__PURE__ */ jsx7(
673
+ Burger2,
674
+ {
675
+ opened: mobileNavOpen,
676
+ onClick: () => setMobileNavOpen((value) => !value),
677
+ hiddenFrom: "sm",
678
+ size: "sm",
679
+ "aria-label": "Toggle docs navigation"
680
+ }
681
+ ),
682
+ /* @__PURE__ */ jsx7(Transition, { mounted: mobileNavOpen, transition: "pop", duration: 120, children: (styles) => /* @__PURE__ */ jsx7(Box2, { style: styles, children: /* @__PURE__ */ jsx7(Box2, { mt: "xs", p: "sm", style: { borderRadius: 8, border: "1px solid var(--mantine-color-default-border)" }, children: mobileNavigation }) }) })
683
+ ] }) : null,
684
+ /* @__PURE__ */ jsx7(Box2, { style: { minWidth: 0 }, children: brand }),
685
+ headerContext ? /* @__PURE__ */ jsx7(Text4, { size: "sm", c: "dimmed", lineClamp: 1, children: headerContext }) : null
686
+ ] }),
687
+ /* @__PURE__ */ jsx7(Group4, { gap: "sm", wrap: "nowrap", style: { minWidth: 0 }, children: actions })
688
+ ] }),
689
+ sidebar: /* @__PURE__ */ jsx7(
690
+ DocsShellSidebar,
691
+ {
692
+ primaryNavigation,
693
+ secondaryNavigation
694
+ }
695
+ ),
696
+ footer,
697
+ mobileNavigationLabel: "Open docs navigation",
698
+ headerHeight: 72,
699
+ children: /* @__PURE__ */ jsx7(Container, { size: resolveContentContainerSize(contentWidth), px: "md", py: "lg", w: "100%", children })
700
+ }
701
+ );
702
+ }
703
+
532
704
  // src/SidebarNav.tsx
533
705
  import { forwardRef } from "react";
534
- import { Box as Box2, NavLink, Stack as Stack2, Text as Text4, createPolymorphicComponent } from "@mantine/core";
706
+ import { Box as Box3, NavLink, Stack as Stack3, Text as Text5, createPolymorphicComponent } from "@mantine/core";
535
707
  import { useGdsTranslation as useGdsTranslation3 } from "@doneisbetter/gds-theme";
536
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
708
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
537
709
  function SidebarNav({ children, ariaLabel = "Primary navigation", gap = "md" }) {
538
- return /* @__PURE__ */ jsx7(Stack2, { component: "nav", "aria-label": ariaLabel, gap, h: "100%", children });
710
+ return /* @__PURE__ */ jsx8(Stack3, { component: "nav", "aria-label": ariaLabel, gap, h: "100%", children });
539
711
  }
540
712
  function SidebarNavSection({ label, children, pushToBottom = false }) {
541
- return /* @__PURE__ */ jsxs4(Stack2, { gap: "xs", mt: pushToBottom ? "auto" : void 0, children: [
542
- label ? /* @__PURE__ */ jsx7(Text4, { size: "xs", fw: 700, c: "dimmed", children: label }) : null,
543
- /* @__PURE__ */ jsx7(Stack2, { gap: 4, children })
713
+ return /* @__PURE__ */ jsxs5(Stack3, { gap: "xs", mt: pushToBottom ? "auto" : void 0, children: [
714
+ label ? /* @__PURE__ */ jsx8(Text5, { size: "xs", fw: 700, c: "dimmed", children: label }) : null,
715
+ /* @__PURE__ */ jsx8(Stack3, { gap: 4, children })
544
716
  ] });
545
717
  }
546
718
  var _SidebarNavItem = forwardRef(
@@ -559,14 +731,14 @@ var _SidebarNavItem = forwardRef(
559
731
  const config = action ? resolveSemanticActionConfig(action, vocabularyPacks) : null;
560
732
  const Icon = config?.icon;
561
733
  const resolvedLabel = label ?? (action ? getSemanticActionLabel(action, t, vocabularyPacks) : void 0);
562
- return /* @__PURE__ */ jsx7(
734
+ return /* @__PURE__ */ jsx8(
563
735
  NavLink,
564
736
  {
565
737
  ref,
566
738
  label: resolvedLabel,
567
739
  description,
568
- leftSection: icon ?? (Icon ? /* @__PURE__ */ jsx7(Icon, { size: "1rem", stroke: 1.5 }) : void 0),
569
- rightSection: badge ? /* @__PURE__ */ jsx7(Box2, { children: badge }) : props.rightSection,
740
+ leftSection: icon ?? (Icon ? /* @__PURE__ */ jsx8(Icon, { size: "1rem", stroke: 1.5 }) : void 0),
741
+ rightSection: badge ? /* @__PURE__ */ jsx8(Box3, { children: badge }) : props.rightSection,
570
742
  "aria-label": ariaLabel ?? (typeof resolvedLabel === "string" ? resolvedLabel : void 0),
571
743
  "aria-current": props.active ? "page" : ariaCurrent,
572
744
  ...props
@@ -577,11 +749,11 @@ var _SidebarNavItem = forwardRef(
577
749
  var SidebarNavItem = createPolymorphicComponent(_SidebarNavItem);
578
750
 
579
751
  // src/DocsCodeBlock.tsx
580
- import { useState as useState3 } from "react";
581
- import { ActionIcon as ActionIcon2, Code as Code2, Group as Group4, Paper as Paper3, Stack as Stack3, Text as Text5 } from "@mantine/core";
582
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
752
+ import { useState as useState4 } from "react";
753
+ import { ActionIcon as ActionIcon2, Code as Code2, Group as Group5, Paper as Paper3, Stack as Stack4, Text as Text6 } from "@mantine/core";
754
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
583
755
  function DocsCodeBlock({ code, language, title, withCopy = true }) {
584
- const [copied, setCopied] = useState3(false);
756
+ const [copied, setCopied] = useState4(false);
585
757
  const handleCopy = async () => {
586
758
  if (!navigator?.clipboard) {
587
759
  return;
@@ -590,13 +762,13 @@ function DocsCodeBlock({ code, language, title, withCopy = true }) {
590
762
  setCopied(true);
591
763
  window.setTimeout(() => setCopied(false), 1200);
592
764
  };
593
- return /* @__PURE__ */ jsx8(Paper3, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs5(Stack3, { gap: "sm", children: [
594
- title || withCopy ? /* @__PURE__ */ jsxs5(Group4, { justify: "space-between", align: "center", children: [
595
- /* @__PURE__ */ jsxs5(Stack3, { gap: 0, children: [
596
- title ? /* @__PURE__ */ jsx8(Text5, { fw: 600, children: title }) : null,
597
- language ? /* @__PURE__ */ jsx8(Text5, { size: "xs", c: "dimmed", children: language }) : null
765
+ return /* @__PURE__ */ jsx9(Paper3, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs6(Stack4, { gap: "sm", children: [
766
+ title || withCopy ? /* @__PURE__ */ jsxs6(Group5, { justify: "space-between", align: "center", children: [
767
+ /* @__PURE__ */ jsxs6(Stack4, { gap: 0, children: [
768
+ title ? /* @__PURE__ */ jsx9(Text6, { fw: 600, children: title }) : null,
769
+ language ? /* @__PURE__ */ jsx9(Text6, { size: "xs", c: "dimmed", children: language }) : null
598
770
  ] }),
599
- withCopy ? /* @__PURE__ */ jsx8(
771
+ withCopy ? /* @__PURE__ */ jsx9(
600
772
  ActionIcon2,
601
773
  {
602
774
  variant: "subtle",
@@ -604,18 +776,18 @@ function DocsCodeBlock({ code, language, title, withCopy = true }) {
604
776
  onClick: () => {
605
777
  void handleCopy();
606
778
  },
607
- children: /* @__PURE__ */ jsx8(GdsIcons.Copy, { size: "1rem" })
779
+ children: /* @__PURE__ */ jsx9(GdsIcons.Copy, { size: "1rem" })
608
780
  }
609
781
  ) : null
610
782
  ] }) : null,
611
- /* @__PURE__ */ jsx8(Code2, { block: true, children: code })
783
+ /* @__PURE__ */ jsx9(Code2, { block: true, children: code })
612
784
  ] }) });
613
785
  }
614
786
 
615
787
  // src/ShareButtonGroup.tsx
616
- import { useState as useState4 } from "react";
617
- import { ActionIcon as ActionIcon3, Button as Button3, Group as Group5, Stack as Stack4, Text as Text6 } from "@mantine/core";
618
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
788
+ import { useState as useState5 } from "react";
789
+ import { ActionIcon as ActionIcon3, Button as Button3, Group as Group6, Stack as Stack5, Text as Text7 } from "@mantine/core";
790
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
619
791
  var channelLabels = {
620
792
  native: "Share",
621
793
  copy: "Copy link",
@@ -650,9 +822,9 @@ function ShareAction({
650
822
  const label = channelLabels[channel];
651
823
  const Icon = channel === "copy" ? GdsIcons.Copy : channel === "mail" ? GdsIcons.Mail : channel === "message" ? GdsIcons.Message : GdsIcons.Refer;
652
824
  if (compact) {
653
- return href ? /* @__PURE__ */ jsx9(ActionIcon3, { component: "a", href, target: "_blank", rel: "noreferrer", variant: "subtle", size: "lg", "aria-label": label, children: /* @__PURE__ */ jsx9(Icon, { size: "1rem", stroke: 1.75 }) }) : /* @__PURE__ */ jsx9(ActionIcon3, { variant: "subtle", size: "lg", "aria-label": label, onClick, children: /* @__PURE__ */ jsx9(Icon, { size: "1rem", stroke: 1.75 }) });
825
+ return href ? /* @__PURE__ */ jsx10(ActionIcon3, { component: "a", href, target: "_blank", rel: "noreferrer", variant: "subtle", size: "lg", "aria-label": label, children: /* @__PURE__ */ jsx10(Icon, { size: "1rem", stroke: 1.75 }) }) : /* @__PURE__ */ jsx10(ActionIcon3, { variant: "subtle", size: "lg", "aria-label": label, onClick, children: /* @__PURE__ */ jsx10(Icon, { size: "1rem", stroke: 1.75 }) });
654
826
  }
655
- return href ? /* @__PURE__ */ jsx9(Button3, { component: "a", href, target: "_blank", rel: "noreferrer", variant: "default", leftSection: /* @__PURE__ */ jsx9(Icon, { size: "1rem", stroke: 1.75 }), children: label }) : /* @__PURE__ */ jsx9(Button3, { variant: "default", leftSection: /* @__PURE__ */ jsx9(Icon, { size: "1rem", stroke: 1.75 }), onClick, children: label });
827
+ return href ? /* @__PURE__ */ jsx10(Button3, { component: "a", href, target: "_blank", rel: "noreferrer", variant: "default", leftSection: /* @__PURE__ */ jsx10(Icon, { size: "1rem", stroke: 1.75 }), children: label }) : /* @__PURE__ */ jsx10(Button3, { variant: "default", leftSection: /* @__PURE__ */ jsx10(Icon, { size: "1rem", stroke: 1.75 }), onClick, children: label });
656
828
  }
657
829
  function ShareButtonGroup({
658
830
  url,
@@ -663,8 +835,8 @@ function ShareButtonGroup({
663
835
  label = "Share this",
664
836
  description
665
837
  }) {
666
- const [copied, setCopied] = useState4(false);
667
- const [shared, setShared] = useState4(false);
838
+ const [copied, setCopied] = useState5(false);
839
+ const [shared, setShared] = useState5(false);
668
840
  const hrefs = encodeShare(url, title, text);
669
841
  async function handleCopy() {
670
842
  await navigator.clipboard?.writeText?.(url);
@@ -680,52 +852,67 @@ function ShareButtonGroup({
680
852
  }
681
853
  await handleCopy();
682
854
  }
683
- return /* @__PURE__ */ jsxs6(Stack4, { gap: "sm", children: [
684
- /* @__PURE__ */ jsxs6(Stack4, { gap: 2, children: [
685
- /* @__PURE__ */ jsx9(Text6, { fw: 600, children: label }),
686
- description ? /* @__PURE__ */ jsx9(Text6, { size: "sm", c: "dimmed", children: description }) : null
855
+ return /* @__PURE__ */ jsxs7(Stack5, { gap: "sm", children: [
856
+ /* @__PURE__ */ jsxs7(Stack5, { gap: 2, children: [
857
+ /* @__PURE__ */ jsx10(Text7, { fw: 600, children: label }),
858
+ description ? /* @__PURE__ */ jsx10(Text7, { size: "sm", c: "dimmed", children: description }) : null
687
859
  ] }),
688
- /* @__PURE__ */ jsx9(Group5, { gap: "sm", wrap: "wrap", children: channels.map((channel) => {
860
+ /* @__PURE__ */ jsx10(Group6, { gap: "sm", wrap: "wrap", children: channels.map((channel) => {
689
861
  if (channel === "copy") {
690
- return /* @__PURE__ */ jsx9(ShareAction, { channel, compact, onClick: () => void handleCopy() }, channel);
862
+ return /* @__PURE__ */ jsx10(ShareAction, { channel, compact, onClick: () => void handleCopy() }, channel);
691
863
  }
692
864
  if (channel === "native") {
693
- return /* @__PURE__ */ jsx9(ShareAction, { channel, compact, onClick: () => void handleNativeShare() }, channel);
865
+ return /* @__PURE__ */ jsx10(ShareAction, { channel, compact, onClick: () => void handleNativeShare() }, channel);
694
866
  }
695
- return /* @__PURE__ */ jsx9(ShareAction, { channel, compact, href: hrefs[channel] }, channel);
867
+ return /* @__PURE__ */ jsx10(ShareAction, { channel, compact, href: hrefs[channel] }, channel);
696
868
  }) }),
697
- copied ? /* @__PURE__ */ jsx9(Text6, { size: "sm", c: "teal", children: "Link copied." }) : null,
698
- shared ? /* @__PURE__ */ jsx9(Text6, { size: "sm", c: "teal", children: "Share sheet opened." }) : null
869
+ copied ? /* @__PURE__ */ jsx10(Text7, { size: "sm", c: "teal", children: "Link copied." }) : null,
870
+ shared ? /* @__PURE__ */ jsx10(Text7, { size: "sm", c: "teal", children: "Share sheet opened." }) : null
699
871
  ] });
700
872
  }
701
873
 
702
874
  // src/UploadDropzone.tsx
703
- import { useRef, useState as useState5 } from "react";
704
- import { Box as Box3, Button as Button4, Group as Group6, Stack as Stack5, Text as Text7 } from "@mantine/core";
705
- import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
875
+ import { useRef, useState as useState6 } from "react";
876
+ import { Badge as Badge2, Box as Box4, Button as Button4, Group as Group7, Stack as Stack6, Text as Text8 } from "@mantine/core";
877
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
706
878
  function UploadDropzone({
707
879
  title,
708
880
  description,
709
881
  onFilesSelected,
710
882
  accept,
883
+ acceptedTypesLabel,
884
+ maxSizeLabel,
711
885
  multiple = true,
712
886
  actionLabel = "Choose files",
713
- mode = "panel"
887
+ mode = "panel",
888
+ state = "idle",
889
+ selectedFiles = [],
890
+ error,
891
+ policyText,
892
+ retryAction,
893
+ removeAction,
894
+ readonly = false
714
895
  }) {
715
896
  const inputRef = useRef(null);
716
- const [dragging, setDragging] = useState5(false);
897
+ const [dragging, setDragging] = useState6(false);
717
898
  const UploadIcon = GdsIcons.Upload;
899
+ const effectiveState = readonly ? "readonly" : dragging ? "drag-active" : state;
900
+ const isDisabled = readonly || effectiveState === "upload-pending";
901
+ const isError = ["upload-failed", "unsupported-type", "too-large"].includes(effectiveState);
718
902
  const forwardFiles = (files) => {
719
- if (!files?.length || !onFilesSelected) {
903
+ if (isDisabled || !files?.length || !onFilesSelected) {
720
904
  return;
721
905
  }
722
906
  onFilesSelected(Array.from(files));
723
907
  };
724
- return /* @__PURE__ */ jsxs7(
725
- Box3,
908
+ return /* @__PURE__ */ jsxs8(
909
+ Box4,
726
910
  {
727
911
  onDragOver: (event) => {
728
912
  event.preventDefault();
913
+ if (isDisabled) {
914
+ return;
915
+ }
729
916
  setDragging(true);
730
917
  },
731
918
  onDragLeave: () => setDragging(false),
@@ -736,12 +923,13 @@ function UploadDropzone({
736
923
  },
737
924
  p: mode === "inline" ? "md" : "xl",
738
925
  style: {
739
- border: `1px dashed var(${dragging ? "--mantine-color-violet-6" : "--mantine-color-default-border"})`,
926
+ border: `1px dashed var(${effectiveState === "drag-active" ? "--mantine-color-violet-6" : isError ? "--mantine-color-red-6" : "--mantine-color-default-border"})`,
740
927
  borderRadius: "var(--mantine-radius-lg)",
741
- background: dragging ? "var(--mantine-color-violet-light)" : "transparent"
928
+ background: effectiveState === "drag-active" ? "var(--mantine-color-violet-light)" : "transparent"
742
929
  },
930
+ "aria-invalid": isError || void 0,
743
931
  children: [
744
- /* @__PURE__ */ jsx10(
932
+ /* @__PURE__ */ jsx11(
745
933
  "input",
746
934
  {
747
935
  ref: inputRef,
@@ -749,14 +937,30 @@ function UploadDropzone({
749
937
  hidden: true,
750
938
  accept,
751
939
  multiple,
940
+ disabled: isDisabled,
752
941
  onChange: (event) => forwardFiles(event.currentTarget.files)
753
942
  }
754
943
  ),
755
- /* @__PURE__ */ jsxs7(Stack5, { align: mode === "inline" ? "flex-start" : "center", ta: mode === "inline" ? "left" : "center", gap: "sm", children: [
756
- /* @__PURE__ */ jsx10(UploadIcon, { size: "1.5rem" }),
757
- /* @__PURE__ */ jsx10(Text7, { fw: 600, children: title }),
758
- description ? /* @__PURE__ */ jsx10(Text7, { size: "sm", c: "dimmed", children: description }) : null,
759
- /* @__PURE__ */ jsx10(Group6, { children: /* @__PURE__ */ jsx10(Button4, { variant: "light", onClick: () => inputRef.current?.click(), children: actionLabel }) })
944
+ /* @__PURE__ */ jsxs8(Stack6, { align: mode === "inline" ? "flex-start" : "center", ta: mode === "inline" ? "left" : "center", gap: "sm", children: [
945
+ /* @__PURE__ */ jsx11(UploadIcon, { size: "1.5rem" }),
946
+ /* @__PURE__ */ jsx11(Badge2, { variant: "light", color: isError ? "red" : effectiveState === "selected" ? "blue" : effectiveState === "upload-pending" ? "violet" : "gray", children: effectiveState.replace("-", " ") }),
947
+ /* @__PURE__ */ jsx11(Text8, { fw: 600, children: title }),
948
+ description ? /* @__PURE__ */ jsx11(Text8, { size: "sm", c: "dimmed", children: description }) : null,
949
+ acceptedTypesLabel || maxSizeLabel ? /* @__PURE__ */ jsxs8(Group7, { gap: "xs", justify: mode === "inline" ? "flex-start" : "center", children: [
950
+ acceptedTypesLabel ? /* @__PURE__ */ jsx11(Badge2, { variant: "outline", color: "gray", children: acceptedTypesLabel }) : null,
951
+ maxSizeLabel ? /* @__PURE__ */ jsx11(Badge2, { variant: "outline", color: "gray", children: maxSizeLabel }) : null
952
+ ] }) : null,
953
+ selectedFiles.length ? /* @__PURE__ */ jsxs8(Text8, { size: "sm", children: [
954
+ "Selected: ",
955
+ selectedFiles.join(", ")
956
+ ] }) : null,
957
+ policyText ? /* @__PURE__ */ jsx11(Text8, { size: "sm", c: isError ? "red.7" : "dimmed", children: policyText }) : null,
958
+ error ? /* @__PURE__ */ jsx11(Text8, { size: "sm", c: "red.7", role: "alert", children: error }) : null,
959
+ /* @__PURE__ */ jsxs8(Group7, { children: [
960
+ /* @__PURE__ */ jsx11(Button4, { variant: "light", onClick: () => inputRef.current?.click(), disabled: isDisabled, children: actionLabel }),
961
+ retryAction,
962
+ removeAction
963
+ ] })
760
964
  ] })
761
965
  ]
762
966
  }
@@ -764,12 +968,13 @@ function UploadDropzone({
764
968
  }
765
969
 
766
970
  // src/AccessRecoveryPanel.tsx
767
- import { Group as Group7 } from "@mantine/core";
971
+ import { Group as Group8 } from "@mantine/core";
768
972
  import { useGdsTranslation as useGdsTranslation4 } from "@doneisbetter/gds-theme";
769
- import { jsx as jsx11 } from "react/jsx-runtime";
973
+ import { jsx as jsx12 } from "react/jsx-runtime";
770
974
  var stateBlockVariantByState = {
771
975
  unauthenticated: "permission",
772
976
  "expired-session": "info",
977
+ timeout: "error",
773
978
  forbidden: "permission",
774
979
  missing: "error",
775
980
  unavailable: "error"
@@ -783,6 +988,10 @@ var defaultCopyByState = {
783
988
  title: "Session expired",
784
989
  description: "Sign in again or retry to continue where you left off."
785
990
  },
991
+ timeout: {
992
+ title: "Request timed out",
993
+ description: "The recovery action took too long. Retry or choose a safe destination."
994
+ },
786
995
  forbidden: {
787
996
  title: "You do not have access",
788
997
  description: "This content is outside your current permissions or scope."
@@ -814,6 +1023,12 @@ function defaultActionsForState(state, {
814
1023
  secondary: retryAction && signInAction ? retryAction : backAction,
815
1024
  tertiary: supportAction ?? null
816
1025
  };
1026
+ case "timeout":
1027
+ return {
1028
+ primary: retryAction ?? backAction,
1029
+ secondary: retryAction && backAction ? backAction : supportAction ?? null,
1030
+ tertiary: retryAction && backAction ? supportAction ?? null : null
1031
+ };
817
1032
  case "forbidden":
818
1033
  return { primary: backAction, secondary: supportAction ?? null, tertiary: null };
819
1034
  case "missing":
@@ -835,7 +1050,7 @@ function ActionGroup({
835
1050
  if (actions.length === 0) {
836
1051
  return null;
837
1052
  }
838
- return /* @__PURE__ */ jsx11(Group7, { gap: "sm", justify: "center", wrap: "wrap", children: actions.map((actionConfig, index) => /* @__PURE__ */ jsx11(
1053
+ return /* @__PURE__ */ jsx12(Group8, { gap: "sm", justify: "center", wrap: "wrap", children: actions.map((actionConfig, index) => /* @__PURE__ */ jsx12(
839
1054
  SemanticButton,
840
1055
  {
841
1056
  action: actionConfig.action,
@@ -869,14 +1084,14 @@ function AccessRecoveryPanel({
869
1084
  onBack,
870
1085
  supportAction
871
1086
  });
872
- return /* @__PURE__ */ jsx11(
1087
+ return /* @__PURE__ */ jsx12(
873
1088
  StateBlock,
874
1089
  {
875
1090
  variant: stateBlockVariantByState[state],
876
1091
  compact,
877
1092
  title: title ?? t(`gds.accessRecovery.${state}.title`, defaultCopy.title),
878
1093
  description: description ?? t(`gds.accessRecovery.${state}.description`, defaultCopy.description),
879
- action: /* @__PURE__ */ jsx11(
1094
+ action: /* @__PURE__ */ jsx12(
880
1095
  ActionGroup,
881
1096
  {
882
1097
  primaryAction: primaryAction ?? defaults.primary,
@@ -895,6 +1110,7 @@ export {
895
1110
  ReferenceThemeExplorer,
896
1111
  GameBoardTile,
897
1112
  DiscoveryShell,
1113
+ DocsShell,
898
1114
  SidebarNav,
899
1115
  SidebarNavSection,
900
1116
  SidebarNavItem,