@nswds/app 1.100.0 → 1.102.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/dist/index.js CHANGED
@@ -42,8 +42,6 @@ import * as LabelPrimitive from '@radix-ui/react-label';
42
42
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
43
43
  import * as TabsPrimitives from '@radix-ui/react-tabs';
44
44
  import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
45
- import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
46
- import * as TogglePrimitive from '@radix-ui/react-toggle';
47
45
  import { Command as Command$1 } from 'cmdk';
48
46
  import * as DialogPrimitive from '@radix-ui/react-dialog';
49
47
  import * as ContextMenuPrimitive from '@radix-ui/react-context-menu';
@@ -51,6 +49,8 @@ import { useReactTable, getFacetedUniqueValues, getFacetedRowModel, getSortedRow
51
49
  import * as SeparatorPrimitive from '@radix-ui/react-separator';
52
50
  import { Drawer as Drawer$1 } from 'vaul';
53
51
  import { FormProvider, Controller, useFormContext, useFormState } from 'react-hook-form';
52
+ import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
53
+ import * as TogglePrimitive from '@radix-ui/react-toggle';
54
54
  import Image2 from 'next/image';
55
55
  import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
56
56
  import { OTPInput, OTPInputContext } from 'input-otp';
@@ -7707,7 +7707,7 @@ function CardDescription({ className, ...props }) {
7707
7707
  "div",
7708
7708
  {
7709
7709
  "data-slot": "card-description",
7710
- className: cn("text-sm text-muted-foreground", className),
7710
+ className: cn("text-base text-muted-foreground", className),
7711
7711
  ...props
7712
7712
  }
7713
7713
  );
@@ -14981,8 +14981,7 @@ function getPairRating(contrastRatio) {
14981
14981
  }
14982
14982
  function getPreferredForegroundTone(backgroundTone) {
14983
14983
  if (backgroundTone <= 200) return 800;
14984
- if (backgroundTone <= 500) return 700;
14985
- if (backgroundTone <= 650) return 250;
14984
+ if (backgroundTone <= 500) return 600;
14986
14985
  return 200;
14987
14986
  }
14988
14987
  function isPreferredDirection(backgroundTone, foregroundTone, preferredForegroundTone) {
@@ -15154,154 +15153,95 @@ function getWhiteForegroundPair(background) {
15154
15153
  function supportsWhiteForegroundPreview(background) {
15155
15154
  return background.tone >= MIN_DARK_BACKGROUND_TONE_FOR_WHITE;
15156
15155
  }
15157
- function getPairingColorValue(color2, format) {
15158
- return getColorValue(color2, format);
15159
- }
15160
- var styles3 = {
15161
- base: [
15162
- // Base
15163
- "inline-flex items-center justify-center gap-2 rounded-sm text-sm font-medium bg-transparent transition-all whitespace-nowrap cursor-pointer border-(--toggle-border) [--toggle-border:var(--color-grey-200)] text-grey-800",
15164
- // States
15165
- "data-[state=on]:bg-grey-100 data-[state=on]:text-grey-850",
15166
- // Hover
15167
- "hover:bg-grey-100 hover:text-grey-850",
15168
- // Focus
15169
- "focus:outline focus:outline-2 focus:outline-offset-0 focus:outline-(--toggle-border)",
15170
- // Dark mode
15171
- "dark:text-white",
15172
- // Dark mode states
15173
- "dark:data-[state=on]:bg-white/10 dark:data-[state=on]:text-white",
15174
- // Dark mode hover
15175
- "dark:hover:bg-white/10 dark:hover:text-white",
15176
- // Disabled
15177
- "disabled:pointer-events-none disabled:opacity-50",
15178
- // Icon
15179
- '[&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 [&_svg]:shrink-0',
15180
- // Aria invalid
15181
- "aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
15182
- // Aria invalid dark mode
15183
- "dark:aria-invalid:ring-destructive/40"
15184
- ],
15185
- outline: [
15186
- // Base
15187
- "text-grey-800 border [--toggle-border:var(--color-grey-300)]",
15188
- // States
15189
- "hover:[--toggle-border:var(--color-grey-400)]",
15190
- // Dark mode
15191
- "dark:[--toggle-border:white]/40",
15192
- // Dark mode states
15193
- "dark:hover:[--toggle-border:white]/50",
15194
- // Data on
15195
- "data-[state=on]:bg-primary-800/10"
15196
- ]
15197
- };
15198
- var toggleVariants = cva(styles3.base, {
15199
- variants: {
15200
- variant: {
15201
- ghost: "",
15202
- outline: clsx12(styles3.outline)
15203
- },
15204
- size: {
15205
- default: "h-9 px-2 min-w-9",
15206
- sm: "h-8 px-1.5 min-w-8",
15207
- lg: "h-10 px-2.5 min-w-10"
15208
- }
15209
- },
15210
- defaultVariants: {
15211
- variant: "ghost",
15212
- size: "default"
15156
+ var SluggerContext = React5__default.createContext(null);
15157
+ function flattenText(nodes) {
15158
+ if (nodes == null || typeof nodes === "boolean") return "";
15159
+ if (typeof nodes === "string" || typeof nodes === "number" || typeof nodes === "bigint")
15160
+ return String(nodes);
15161
+ if (Array.isArray(nodes)) return nodes.map(flattenText).join("");
15162
+ if (React5__default.isValidElement(nodes)) {
15163
+ return flattenText(nodes.props.children);
15213
15164
  }
15214
- });
15215
- function Toggle({
15216
- className,
15217
- variant,
15218
- size,
15219
- ...props
15220
- }) {
15221
- return /* @__PURE__ */ jsx(
15222
- TogglePrimitive.Root,
15223
- {
15224
- "data-slot": "toggle",
15225
- className: cn(toggleVariants({ variant, size, className })),
15226
- ...props
15227
- }
15228
- );
15165
+ return "";
15229
15166
  }
15230
- var ToggleGroupContext = React5.createContext({
15231
- size: "default",
15232
- variant: "ghost"
15233
- });
15234
- function ToggleGroup({
15235
- className,
15236
- variant,
15237
- size,
15238
- children,
15239
- ...props
15240
- }) {
15241
- return /* @__PURE__ */ jsx(
15242
- ToggleGroupPrimitive.Root,
15243
- {
15244
- "data-slot": "toggle-group",
15245
- "data-variant": variant,
15246
- "data-size": size,
15247
- className: cn(
15248
- "group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
15249
- className
15250
- ),
15251
- ...props,
15252
- children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
15253
- }
15254
- );
15167
+ function baseSlug(input) {
15168
+ return input.toLowerCase().trim().replace(/[\s\W]+/g, "-").replace(/^-+|-+$/g, "");
15255
15169
  }
15256
- function ToggleGroupItem({
15170
+ function Heading({
15257
15171
  className,
15172
+ trim = "normal",
15173
+ size = 1,
15174
+ level = 1,
15175
+ display = false,
15176
+ id: idProp,
15258
15177
  children,
15259
- variant,
15260
- size,
15261
15178
  ...props
15262
15179
  }) {
15263
- const context = React5.useContext(ToggleGroupContext);
15180
+ const Tag = `h${level}`;
15181
+ const slugger = useContext(SluggerContext);
15182
+ const headingSizeClasses = {
15183
+ 1: "text-[calc(var(--heading-font-size-1)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-52)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
15184
+ 2: "text-[calc(var(--heading-font-size-2)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-44)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
15185
+ 3: "text-[calc(var(--heading-font-size-3)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-40)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
15186
+ 4: "text-[calc(var(--heading-font-size-4)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-36)] tracking-[calc(var(--heading-letter-spacing-1)_+_var(--heading-letter-spacing))]",
15187
+ 5: "text-[calc(var(--heading-font-size-5)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-32)] tracking-[calc(var(--heading-letter-spacing-1)_+_var(--heading-letter-spacing))]",
15188
+ 6: "text-[calc(var(--heading-font-size-6)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-28)] tracking-[calc(var(--letter-spacing-0)_+_var(--heading-letter-spacing))]"
15189
+ };
15190
+ const displaySizeClasses = {
15191
+ 1: "text-[calc(var(--display-font-size-1)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-96)] tracking-[calc(var(--heading-letter-spacing-3)_+_var(--heading-letter-spacing))]",
15192
+ 2: "text-[calc(var(--display-font-size-2)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-60)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
15193
+ 3: "text-[calc(var(--display-font-size-3)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-52)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
15194
+ 4: "text-[calc(var(--display-font-size-4)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-44)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]"
15195
+ };
15196
+ const sizeClass = display ? displaySizeClasses[size] : headingSizeClasses[size];
15197
+ const trimClasses = {
15198
+ normal: ["before:content-none after:content-none"],
15199
+ start: [
15200
+ 'before:content-[""] before:table after:content-none',
15201
+ "before:mb-[calc(var(--leading-trim-start,var(--default-leading-trim-start))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
15202
+ ],
15203
+ end: [
15204
+ 'before:content-none after:content-[""] after:table',
15205
+ "after:mt-[calc(var(--leading-trim-end,var(--default-leading-trim-end))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
15206
+ ],
15207
+ both: [
15208
+ 'before:content-[""] before:table after:content-[""] after:table',
15209
+ "before:mb-[calc(var(--leading-trim-start,var(--default-leading-trim-start))-var(--line-height,calc(1em*var(--default-line-height)))/2)]",
15210
+ "after:mt-[calc(var(--leading-trim-end,var(--default-leading-trim-end))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
15211
+ ]
15212
+ };
15213
+ const computedId = useMemo(() => {
15214
+ if (idProp) return idProp;
15215
+ const text = flattenText(children);
15216
+ if (!text) return void 0;
15217
+ const base = baseSlug(text);
15218
+ return slugger ? slugger.slug(base) : base;
15219
+ }, [idProp, children, slugger]);
15264
15220
  return /* @__PURE__ */ jsx(
15265
- ToggleGroupPrimitive.Item,
15221
+ Tag,
15266
15222
  {
15267
- "data-slot": "toggle-group-item",
15268
- "data-variant": context.variant || variant,
15269
- "data-size": context.size || size,
15270
- className: cn(
15271
- toggleVariants({
15272
- variant: context.variant || variant,
15273
- size: context.size || size
15274
- }),
15275
- "min-w-0 shrink-0 rounded-none shadow-none first:rounded-l-sm last:rounded-r-sm focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
15276
- className
15277
- ),
15278
15223
  ...props,
15224
+ id: computedId,
15225
+ "data-anchor": true,
15226
+ className: clsx12(
15227
+ className,
15228
+ trimClasses[trim],
15229
+ "m-0",
15230
+ "leading-[var(--line-height)] font-[var(--heading-font-family)] font-bold",
15231
+ "[--leading-trim-end:var(--heading-leading-trim-end)] [--leading-trim-start:var(--heading-leading-trim-start)]",
15232
+ "text-primary-800 dark:text-white",
15233
+ sizeClass
15234
+ ),
15279
15235
  children
15280
15236
  }
15281
15237
  );
15282
15238
  }
15283
- function FormatToggle({ format, setFormat }) {
15284
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
15285
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Format:" }),
15286
- /* @__PURE__ */ jsxs(
15287
- ToggleGroup,
15288
- {
15289
- type: "single",
15290
- value: format,
15291
- onValueChange: (value) => value && setFormat(value),
15292
- children: [
15293
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hex", "aria-label": "HEX format", children: "HEX" }),
15294
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "rgb", "aria-label": "RGB format", children: "RGB" }),
15295
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hsl", "aria-label": "HSL format", children: "HSL" }),
15296
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "oklch", "aria-label": "OKLCH format", children: "OKLCH" })
15297
- ]
15298
- }
15299
- )
15300
- ] });
15301
- }
15302
- var AAA_NORMAL_TEXT_THRESHOLD = "7.0:1 or higher for text below 18pt, or below 14pt bold";
15303
- var AAA_LARGE_TEXT_THRESHOLD = "4.5:1 or higher for text at 18pt and above, or 14pt and above bold";
15239
+ var AAA_NORMAL_TEXT_THRESHOLD = "7.0:1 for text below the WCAG large-text threshold. Sentence-case body copy should generally stay at 16px+ unless it is microcopy.";
15240
+ var AAA_LARGE_TEXT_THRESHOLD = "4.5:1 for WCAG large text: 24px+, or 18.5px+ bold";
15304
15241
  var PREFERRED_BACKGROUND_TONES = [400, 600, 200, 800, 100, 50];
15242
+ var DEFAULT_VISIBLE_FORMATS = ["hex", "rgb", "hsl", "oklch"];
15243
+ var DEFAULT_INITIAL_BACKGROUND_TOKEN = "nsw-blue-800";
15244
+ var DEFAULT_INITIAL_PAIR_ID = "nsw-blue-800:nsw-blue-200";
15305
15245
  function getToneFromToken(token) {
15306
15246
  if (!token) return null;
15307
15247
  const match = token.match(/-(\d+)$/);
@@ -15324,9 +15264,80 @@ function getFamilySelectorLabel(family, themeCategory, selectionRole) {
15324
15264
  const preferredTone = selectionRole === "primary colour" ? 800 : 600;
15325
15265
  return family.colors.find((color2) => color2.tone === preferredTone)?.name ?? family.label;
15326
15266
  }
15267
+ function getActivePaletteFamilyLabel(family, themeCategory, paletteRole) {
15268
+ if (themeCategory !== "aboriginal") {
15269
+ return family.label;
15270
+ }
15271
+ const preferredTone = paletteRole === "primary" ? 800 : paletteRole === "accent" ? 600 : 800;
15272
+ return family.colors.find((color2) => color2.tone === preferredTone)?.name ?? family.label;
15273
+ }
15327
15274
  function isWhiteForegroundPair(pair) {
15328
15275
  return pair.foreground.token === "white";
15329
15276
  }
15277
+ function getWhiteForegroundGuidance(pair) {
15278
+ if (pair.passes.aaaText) {
15279
+ return "White is approved for headings, body copy, and calls to action on this background.";
15280
+ }
15281
+ if (pair.passes.aaaLarge) {
15282
+ return "Use white only for WCAG large text on this background, such as headings at 24px+ or bold text at 18.5px+. Keep sentence-case body copy at 16px+ and use a darker recommended foreground instead.";
15283
+ }
15284
+ return "Do not use white on this background. Choose one of the recommended foregrounds below instead.";
15285
+ }
15286
+ function getPreviewGuidance(pair, isRecommended) {
15287
+ if (!isWhiteForegroundPair(pair)) {
15288
+ return "Use only AAA-recommended combinations across your selected primary, accent, and grey families.";
15289
+ }
15290
+ if (isRecommended) {
15291
+ return "Use white text on dark colour only when it meets AAA for headings, body copy, and calls to action.";
15292
+ }
15293
+ return getWhiteForegroundGuidance(pair);
15294
+ }
15295
+ function getPairingColorDisplayName(color2) {
15296
+ return color2.name ?? color2.token;
15297
+ }
15298
+ function WorkflowStep({
15299
+ step,
15300
+ title,
15301
+ description,
15302
+ level = 2,
15303
+ variant = "section",
15304
+ className
15305
+ }) {
15306
+ if (variant === "card") {
15307
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
15308
+ /* @__PURE__ */ jsx("span", { className: "flex size-9 items-center justify-center rounded-full bg-primary-800 text-sm font-semibold text-white dark:bg-primary-700", children: step }),
15309
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15310
+ /* @__PURE__ */ jsx(
15311
+ Heading,
15312
+ {
15313
+ level,
15314
+ size: 5,
15315
+ className: "text-primary-800 dark:text-white",
15316
+ trim: "normal",
15317
+ children: title
15318
+ }
15319
+ ),
15320
+ description ? /* @__PURE__ */ jsx("p", { className: "text-base/6 text-muted-foreground", children: description }) : null
15321
+ ] })
15322
+ ] });
15323
+ }
15324
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-4", className), children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-4", children: [
15325
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-full bg-primary-800 text-sm font-semibold text-white dark:bg-primary-700", children: step }),
15326
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-3", children: [
15327
+ /* @__PURE__ */ jsx(
15328
+ Heading,
15329
+ {
15330
+ level,
15331
+ size: 4,
15332
+ className: "text-primary-800 dark:text-white",
15333
+ trim: "normal",
15334
+ children: title
15335
+ }
15336
+ ),
15337
+ description ? /* @__PURE__ */ jsx("p", { className: "text-base/6 text-muted-foreground", children: description }) : null
15338
+ ] })
15339
+ ] }) });
15340
+ }
15330
15341
  function getPreferredPairForBackground(pairs, preferredPairId) {
15331
15342
  if (preferredPairId) {
15332
15343
  const preferredPair = pairs.find((pair) => pair.id === preferredPairId);
@@ -15392,15 +15403,22 @@ function getInitialPairingState(searchParams) {
15392
15403
  const backgroundParam = searchParams.get("background");
15393
15404
  const themeCategory = paletteParam === "brand" || paletteParam === "aboriginal" ? paletteParam : "brand";
15394
15405
  const context = getPairingContext(themeCategory, primaryParam, accentParam);
15406
+ const shouldUseDefaultBrandExample = !backgroundParam && !pairParam && themeCategory === "brand" && context.primary.key === "blue" && context.accent.key === "red";
15407
+ const defaultBackgroundToken = shouldUseDefaultBrandExample ? context.backgrounds.some(
15408
+ (background) => background.token === DEFAULT_INITIAL_BACKGROUND_TOKEN
15409
+ ) ? DEFAULT_INITIAL_BACKGROUND_TOKEN : null : null;
15410
+ const defaultPairId = shouldUseDefaultBrandExample && defaultBackgroundToken && context.pairsByBackground[defaultBackgroundToken]?.some(
15411
+ (pair) => pair.id === DEFAULT_INITIAL_PAIR_ID
15412
+ ) ? DEFAULT_INITIAL_PAIR_ID : null;
15395
15413
  const pairBackgroundToken = context.recommendedPairs.find((pair) => pair.id === pairParam)?.background.token ?? null;
15396
15414
  const selectedBackgroundToken = resolveBackgroundToken(
15397
15415
  context,
15398
- backgroundParam ?? pairBackgroundToken,
15399
- getToneFromToken(backgroundParam ?? pairBackgroundToken)
15416
+ backgroundParam ?? pairBackgroundToken ?? defaultBackgroundToken,
15417
+ getToneFromToken(backgroundParam ?? pairBackgroundToken ?? defaultBackgroundToken)
15400
15418
  );
15401
15419
  const selectedPairId = getPreferredPairForBackground(
15402
15420
  context.pairsByBackground[selectedBackgroundToken] ?? [],
15403
- pairParam
15421
+ pairParam ?? defaultPairId
15404
15422
  )?.id ?? "";
15405
15423
  return {
15406
15424
  accentKey: context.accent.key,
@@ -15418,6 +15436,13 @@ function PairPreview({
15418
15436
  const whiteForeground = isWhiteForegroundPair(pair);
15419
15437
  const statusLabel = isRecommended ? "Pass" : pair.passes.aaaLarge ? "Large text only" : "Example only";
15420
15438
  const StatusIcon = isRecommended ? Icons.check : Icons.info;
15439
+ const fauxButtonStyle = {
15440
+ "--btn-bg": pair.foreground.hex,
15441
+ "--btn-border": pair.foreground.hex,
15442
+ "--btn-text": pair.background.hex,
15443
+ "--btn-icon": pair.background.hex,
15444
+ "--btn-hover-overlay": pair.background.hex
15445
+ };
15421
15446
  return /* @__PURE__ */ jsx(Card, { className: "gap-0 overflow-hidden py-0", children: /* @__PURE__ */ jsx(
15422
15447
  "div",
15423
15448
  {
@@ -15463,7 +15488,20 @@ function PairPreview({
15463
15488
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.22em] uppercase", children: whiteForeground && !isRecommended ? "White on colour example" : whiteForeground ? "White on colour" : "Colour on colour" }),
15464
15489
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15465
15490
  /* @__PURE__ */ jsx("h3", { className: "max-w-lg text-4xl leading-none font-bold text-current sm:text-5xl", children: "Pair colour with confidence." }),
15466
- /* @__PURE__ */ jsx("p", { className: "max-w-md text-sm/6 sm:text-base/7", children: whiteForeground ? isRecommended ? "Use white text on dark colour only when it meets AAA for headings, body copy, and calls to action." : "This white text example is included for reference on approved dark backgrounds. Check the result card below before using it for normal or large text." : "Use only AAA-recommended combinations across your selected primary, accent, and grey families." })
15491
+ /* @__PURE__ */ jsx("p", { className: "max-w-md text-base/6 sm:text-base/7", children: getPreviewGuidance(pair, isRecommended) }),
15492
+ /* @__PURE__ */ jsx(
15493
+ "span",
15494
+ {
15495
+ "aria-hidden": "true",
15496
+ "data-variant": "solid",
15497
+ className: cn(
15498
+ buttonVariants({ variant: "solid", size: "default" }),
15499
+ "pointer-events-none cursor-default px-6 text-[16px] font-[700] select-none sm:px-6 sm:text-[16px] sm:font-[700]"
15500
+ ),
15501
+ style: fauxButtonStyle,
15502
+ children: "Get started"
15503
+ }
15504
+ )
15467
15505
  ] })
15468
15506
  ] }),
15469
15507
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
@@ -15521,7 +15559,7 @@ function PreviewFallbackCard({
15521
15559
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.22em] uppercase", children: "Approved background" }),
15522
15560
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15523
15561
  /* @__PURE__ */ jsx("h3", { className: "max-w-lg text-4xl leading-none font-bold text-current sm:text-5xl", children: "Pair colour with confidence." }),
15524
- /* @__PURE__ */ jsx("p", { className: "max-w-md text-sm/6 sm:text-base/7", children: "This approved background tone does not currently have a recommended AAA foreground in this tool." })
15562
+ /* @__PURE__ */ jsx("p", { className: "max-w-md text-base/6 sm:text-base/7", children: "This approved background tone does not currently have a recommended AAA foreground in this tool. Choose another approved background tone to continue." })
15525
15563
  ] })
15526
15564
  ] }),
15527
15565
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
@@ -15537,22 +15575,57 @@ function PreviewFallbackCard({
15537
15575
  }
15538
15576
  function PairDetailCard({
15539
15577
  color: color2,
15540
- format,
15541
- role
15578
+ role,
15579
+ visibleFormats
15542
15580
  }) {
15543
15581
  const [, copyToClipboardRaw] = useCopyToClipboard();
15544
15582
  const [copiedField, setCopiedField] = useState(null);
15545
- const valueLabel = format.toUpperCase();
15546
- const formattedValue = getPairingColorValue(color2, format);
15583
+ const copiedFieldTimeoutRef = useRef(null);
15584
+ const hasDisplayTone = color2.token !== "white";
15585
+ const formatRows = visibleFormats.map((format) => ({
15586
+ key: format,
15587
+ label: format.toUpperCase(),
15588
+ value: color2[format],
15589
+ mono: true,
15590
+ copyable: true
15591
+ }));
15592
+ const valueRows = [
15593
+ { key: "token", label: "Token", value: color2.token, mono: true, copyable: true },
15594
+ {
15595
+ key: "tone",
15596
+ label: "Tone",
15597
+ value: hasDisplayTone ? String(color2.tone) : "Not applicable",
15598
+ mono: false,
15599
+ copyable: hasDisplayTone
15600
+ },
15601
+ ...formatRows
15602
+ ];
15603
+ useEffect(() => {
15604
+ return () => {
15605
+ if (copiedFieldTimeoutRef.current) {
15606
+ clearTimeout(copiedFieldTimeoutRef.current);
15607
+ }
15608
+ };
15609
+ }, []);
15547
15610
  const copyField = (field) => {
15548
- const fieldValue = field === "token" ? color2.token : field === "tone" ? String(color2.tone) : formattedValue;
15611
+ if (field === "tone" && !hasDisplayTone) {
15612
+ return;
15613
+ }
15614
+ const fieldValue = valueRows.find((row) => row.key === field)?.value;
15615
+ if (!fieldValue) return;
15549
15616
  copyToClipboardRaw(fieldValue);
15550
15617
  setCopiedField(field);
15551
- const toastLabel = field === "token" ? "Token" : field === "tone" ? "Tone" : `${valueLabel} value`;
15618
+ const toastLabel = valueRows.find((row) => row.key === field)?.label ?? "Value";
15552
15619
  toast(`${toastLabel} copied to clipboard`, {
15553
15620
  duration: 2e3
15554
15621
  });
15555
- setTimeout(() => setCopiedField(null), 2e3);
15622
+ if (copiedFieldTimeoutRef.current) {
15623
+ clearTimeout(copiedFieldTimeoutRef.current);
15624
+ }
15625
+ copiedFieldTimeoutRef.current = setTimeout(() => {
15626
+ setCopiedField(null);
15627
+ copiedFieldTimeoutRef.current = null;
15628
+ }, 2e3);
15556
15629
  };
15557
15630
  const renderCopyButton = (field, srLabel, className) => /* @__PURE__ */ jsxs(
15558
15631
  Button2,
@@ -15567,43 +15640,40 @@ function PairDetailCard({
15567
15640
  ]
15568
15641
  }
15569
15642
  );
15570
- return /* @__PURE__ */ jsxs(Card, { className: "gap-4", children: [
15571
- /* @__PURE__ */ jsx(CardHeader, { className: "gap-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
15572
- /* @__PURE__ */ jsx(
15573
- "div",
15574
- {
15575
- className: "size-14 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
15576
- style: { backgroundColor: color2.hex }
15577
- }
15578
- ),
15643
+ return /* @__PURE__ */ jsxs(Card, { className: "gap-0 pb-0", children: [
15644
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 border-b", children: [
15579
15645
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15580
15646
  /* @__PURE__ */ jsx(CardTitle, { children: role }),
15581
15647
  /* @__PURE__ */ jsx(CardDescription, { children: color2.name ?? color2.token })
15582
- ] })
15583
- ] }) }),
15584
- /* @__PURE__ */ jsxs(CardContent, { className: "grid gap-4", children: [
15585
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15586
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
15587
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: "Token" }),
15588
- renderCopyButton("token", `Copy ${role.toLowerCase()} token`)
15589
- ] }),
15590
- /* @__PURE__ */ jsx("p", { className: "font-mono text-base break-all", children: color2.token })
15591
15648
  ] }),
15592
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15593
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
15594
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: "Tone" }),
15595
- renderCopyButton("tone", `Copy ${role.toLowerCase()} tone`)
15596
- ] }),
15597
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: color2.tone })
15649
+ /* @__PURE__ */ jsx(
15650
+ "div",
15651
+ {
15652
+ className: "h-14 w-full rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
15653
+ style: { backgroundColor: color2.hex }
15654
+ }
15655
+ )
15656
+ ] }),
15657
+ /* @__PURE__ */ jsx(CardContent, { className: "px-0 pb-3", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-grey-100 dark:divide-grey-800", children: valueRows.map((row) => /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 px-6 py-3", children: [
15658
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
15659
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: row.label }),
15660
+ /* @__PURE__ */ jsx(
15661
+ "p",
15662
+ {
15663
+ className: cn(
15664
+ "mt-2 min-w-0 text-base text-foreground",
15665
+ row.mono && "font-mono text-sm break-all sm:text-base"
15666
+ ),
15667
+ children: row.value
15668
+ }
15669
+ )
15598
15670
  ] }),
15599
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15600
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
15601
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: valueLabel }),
15602
- renderCopyButton("value", `Copy ${role.toLowerCase()} ${valueLabel} value`)
15603
- ] }),
15604
- /* @__PURE__ */ jsx("p", { className: "font-mono text-sm break-all", children: formattedValue })
15605
- ] })
15606
- ] })
15671
+ row.copyable ? renderCopyButton(
15672
+ row.key,
15673
+ `Copy ${role.toLowerCase()} ${row.label.toLowerCase()}`,
15674
+ "mt-0.5"
15675
+ ) : null
15676
+ ] }, row.key)) }) })
15607
15677
  ] });
15608
15678
  }
15609
15679
  function ComplianceRow({
@@ -15611,31 +15681,40 @@ function ComplianceRow({
15611
15681
  passes,
15612
15682
  threshold
15613
15683
  }) {
15614
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 rounded-sm border border-grey-200 px-4 py-3 dark:border-grey-700", children: [
15615
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15616
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: label }),
15617
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: threshold })
15618
- ] }),
15619
- /* @__PURE__ */ jsxs(
15620
- "span",
15621
- {
15622
- className: cn(
15623
- "inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-semibold",
15624
- passes ? "bg-success-50 text-success-800 dark:bg-success-950/30 dark:text-success-200" : "bg-danger-50 text-danger-800 dark:bg-danger-950/30 dark:text-danger-200"
15625
- ),
15626
- children: [
15627
- passes ? /* @__PURE__ */ jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsx(Icons.close, { "data-slot": "icon", className: "size-4" }),
15628
- passes ? "Pass" : "Fail"
15629
- ]
15630
- }
15631
- )
15632
- ] });
15684
+ return /* @__PURE__ */ jsxs(
15685
+ "div",
15686
+ {
15687
+ className: cn(
15688
+ "flex items-center justify-between gap-4 rounded-sm border px-4 py-3",
15689
+ passes ? "border-success-200 bg-success-50 dark:border-success-900/60 dark:bg-success-950/20" : "border-danger-200 bg-danger-50 dark:border-danger-900/60 dark:bg-danger-950/20"
15690
+ ),
15691
+ children: [
15692
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15693
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: label }),
15694
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: threshold })
15695
+ ] }),
15696
+ /* @__PURE__ */ jsxs(
15697
+ "span",
15698
+ {
15699
+ className: cn(
15700
+ "inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-semibold",
15701
+ passes ? "bg-success-50 text-success-800 dark:bg-success-950/30 dark:text-success-200" : "bg-danger-50 text-danger-800 dark:bg-danger-950/30 dark:text-danger-200"
15702
+ ),
15703
+ children: [
15704
+ passes ? /* @__PURE__ */ jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsx(Icons.close, { "data-slot": "icon", className: "size-4" }),
15705
+ passes ? "Pass" : "Fail"
15706
+ ]
15707
+ }
15708
+ )
15709
+ ]
15710
+ }
15711
+ );
15633
15712
  }
15634
15713
  function PairComplianceCard({ pair }) {
15635
15714
  return /* @__PURE__ */ jsxs(Card, { children: [
15636
15715
  /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15637
15716
  /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "AAA compliance" }),
15638
- /* @__PURE__ */ jsx(CardDescription, { children: "Only pairs that pass AAA normal-text contrast are suggested here. Evaluation uses the raw contrast ratio, not the rounded display value." })
15717
+ /* @__PURE__ */ jsx(CardDescription, { children: "This card shows the contrast result for the example above. Evaluation uses the raw contrast ratio, not the rounded display value." })
15639
15718
  ] }),
15640
15719
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3", children: [
15641
15720
  /* @__PURE__ */ jsxs("div", { className: "rounded-sm border border-grey-200 bg-grey-50 px-4 py-3 dark:border-grey-700 dark:bg-grey-900/60", children: [
@@ -15664,71 +15743,6 @@ function PairComplianceCard({ pair }) {
15664
15743
  ] })
15665
15744
  ] });
15666
15745
  }
15667
- function WhiteTextExampleCard({ pair }) {
15668
- return /* @__PURE__ */ jsxs(Card, { children: [
15669
- /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15670
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "White text example" }),
15671
- /* @__PURE__ */ jsx(CardDescription, { children: "For approved dark backgrounds, white text may be suitable for large text. It is only recommended in this tool when it also passes AAA normal text." })
15672
- ] }),
15673
- /* @__PURE__ */ jsxs(CardContent, { className: "grid gap-6 xl:grid-cols-[minmax(0,1.1fr)_minmax(18rem,0.9fr)]", children: [
15674
- /* @__PURE__ */ jsx(
15675
- "div",
15676
- {
15677
- className: "rounded-sm border border-grey-200 p-6 dark:border-grey-700",
15678
- style: {
15679
- backgroundColor: pair.background.hex,
15680
- color: pair.foreground.hex
15681
- },
15682
- children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15683
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15684
- /* @__PURE__ */ jsx("p", { className: "text-lg leading-tight font-semibold", children: "Large text example" }),
15685
- /* @__PURE__ */ jsxs("p", { className: "text-sm/6", children: [
15686
- "White text on ",
15687
- pair.background.token
15688
- ] })
15689
- ] }),
15690
- /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm/6", children: [
15691
- /* @__PURE__ */ jsxs("p", { children: [
15692
- "AAA normal text: ",
15693
- /* @__PURE__ */ jsx("strong", { children: pair.passes.aaaText ? "pass" : "fail" })
15694
- ] }),
15695
- /* @__PURE__ */ jsxs("p", { children: [
15696
- "AAA large text: ",
15697
- /* @__PURE__ */ jsx("strong", { children: pair.passes.aaaLarge ? "pass" : "fail" })
15698
- ] })
15699
- ] }),
15700
- /* @__PURE__ */ jsx("p", { className: "text-sm/6", children: "Normal text means body text and smaller headings below 18pt, or below 14pt when bold. Large text means 18pt and above, or 14pt and above when bold." })
15701
- ] })
15702
- }
15703
- ),
15704
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15705
- /* @__PURE__ */ jsxs("div", { className: "rounded-sm border border-grey-200 bg-grey-50 px-4 py-3 dark:border-grey-700 dark:bg-grey-900/60", children: [
15706
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: "Contrast ratio" }),
15707
- /* @__PURE__ */ jsxs("p", { className: "mt-1 text-2xl font-semibold", children: [
15708
- pair.contrastRatio.toFixed(2),
15709
- ":1"
15710
- ] })
15711
- ] }),
15712
- /* @__PURE__ */ jsx(
15713
- ComplianceRow,
15714
- {
15715
- label: "AAA normal text",
15716
- passes: pair.passes.aaaText,
15717
- threshold: AAA_NORMAL_TEXT_THRESHOLD
15718
- }
15719
- ),
15720
- /* @__PURE__ */ jsx(
15721
- ComplianceRow,
15722
- {
15723
- label: "AAA large text",
15724
- passes: pair.passes.aaaLarge,
15725
- threshold: AAA_LARGE_TEXT_THRESHOLD
15726
- }
15727
- )
15728
- ] })
15729
- ] })
15730
- ] });
15731
- }
15732
15746
  function ColorFamilySelector({
15733
15747
  families,
15734
15748
  label,
@@ -15738,8 +15752,8 @@ function ColorFamilySelector({
15738
15752
  onSelect
15739
15753
  }) {
15740
15754
  const swatchTone = selectionRole === "primary colour" ? 800 : 600;
15741
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15742
- /* @__PURE__ */ jsx("h3", { className: "font-medium", children: label }),
15755
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-0", children: [
15756
+ /* @__PURE__ */ jsx(Heading, { level: 3, size: 6, className: "mb-4 text-foreground", trim: "normal", children: label }),
15743
15757
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 lg:grid-cols-4 xl:grid-cols-5", children: families.map((family) => {
15744
15758
  const isSelected = family.key === selectedKey;
15745
15759
  const familySelectorLabel = getFamilySelectorLabel(family, themeCategory, selectionRole);
@@ -15771,38 +15785,64 @@ function ColorFamilySelector({
15771
15785
  }) })
15772
15786
  ] });
15773
15787
  }
15774
- function ColorPairingToolContent() {
15788
+ function ColorPairingToolContent({ visibleFormats }) {
15775
15789
  const searchParams = useSearchParams();
15776
- const {
15777
- accentKey: initialAccentKey,
15778
- primaryKey: initialPrimaryKey,
15779
- selectedBackgroundToken: initialSelectedBackgroundToken,
15780
- selectedPairId: initialSelectedPairId,
15781
- themeCategory: initialThemeCategory
15782
- } = getInitialPairingState(searchParams);
15783
- const [themeCategory, setThemeCategory] = useState(initialThemeCategory);
15784
- const [format, setFormat] = useState("hex");
15785
- const [primaryFamilyKey, setPrimaryFamilyKey] = useState(initialPrimaryKey);
15786
- const [accentFamilyKey, setAccentFamilyKey] = useState(initialAccentKey);
15790
+ const [initialState] = useState(() => getInitialPairingState(searchParams));
15791
+ const [themeCategory, setThemeCategory] = useState(initialState.themeCategory);
15792
+ const [primaryFamilyKey, setPrimaryFamilyKey] = useState(initialState.primaryKey);
15793
+ const [accentFamilyKey, setAccentFamilyKey] = useState(initialState.accentKey);
15794
+ const [, copyShareLinkRaw] = useCopyToClipboard();
15795
+ const [copiedShareLink, setCopiedShareLink] = useState(false);
15796
+ const copiedShareLinkTimeoutRef = useRef(null);
15787
15797
  const [selectedBackgroundToken, setSelectedBackgroundToken] = useState(
15788
- initialSelectedBackgroundToken
15789
- );
15790
- const [selectedPairId, setSelectedPairId] = useState(initialSelectedPairId);
15791
- const themeFamilies = getPairingFamilies(themeCategory);
15792
- const context = getPairingContext(themeCategory, primaryFamilyKey, accentFamilyKey);
15793
- const selectableFamilies = themeFamilies.filter((family) => family.key !== context.grey.key);
15794
- const selectedBackground = context.backgrounds.find((background) => background.token === selectedBackgroundToken) ?? context.backgrounds[0] ?? null;
15795
- const selectedBackgroundPairs = selectedBackground ? context.pairsByBackground[selectedBackground.token] ?? [] : [];
15796
- const selectedPair = getPreferredPairForBackground(selectedBackgroundPairs, selectedPairId);
15797
- const whiteForegroundExample = selectedBackground && supportsWhiteForegroundPreview(selectedBackground) ? getWhiteForegroundPair(selectedBackground) : null;
15798
+ initialState.selectedBackgroundToken
15799
+ );
15800
+ const [selectedPairId, setSelectedPairId] = useState(initialState.selectedPairId);
15801
+ const themeFamilies = useMemo(() => getPairingFamilies(themeCategory), [themeCategory]);
15802
+ const context = useMemo(
15803
+ () => getPairingContext(themeCategory, primaryFamilyKey, accentFamilyKey),
15804
+ [themeCategory, primaryFamilyKey, accentFamilyKey]
15805
+ );
15806
+ const selectableFamilies = useMemo(
15807
+ () => themeFamilies.filter((family) => family.key !== context.grey.key),
15808
+ [themeFamilies, context.grey.key]
15809
+ );
15810
+ const selectableAccentFamilies = useMemo(
15811
+ () => selectableFamilies.filter((family) => family.key !== context.primary.key),
15812
+ [selectableFamilies, context.primary.key]
15813
+ );
15814
+ const selectedBackground = useMemo(
15815
+ () => context.backgrounds.find((background) => background.token === selectedBackgroundToken) ?? context.backgrounds[0] ?? null,
15816
+ [context.backgrounds, selectedBackgroundToken]
15817
+ );
15818
+ const selectedBackgroundPairs = useMemo(
15819
+ () => selectedBackground ? context.pairsByBackground[selectedBackground.token] ?? [] : [],
15820
+ [context.pairsByBackground, selectedBackground]
15821
+ );
15822
+ const selectedPair = useMemo(
15823
+ () => getPreferredPairForBackground(selectedBackgroundPairs, selectedPairId),
15824
+ [selectedBackgroundPairs, selectedPairId]
15825
+ );
15826
+ const whiteForegroundExample = useMemo(
15827
+ () => selectedBackground && supportsWhiteForegroundPreview(selectedBackground) ? getWhiteForegroundPair(selectedBackground) : null,
15828
+ [selectedBackground]
15829
+ );
15798
15830
  const previewPair = selectedPair ?? whiteForegroundExample ?? null;
15799
- const showWhiteForegroundExample = Boolean(whiteForegroundExample) && (!selectedPair || selectedPair.foreground.token !== "white");
15800
15831
  const detailForeground = selectedPair?.foreground ?? whiteForegroundExample?.foreground ?? null;
15801
- const familySummary = [context.primary.label, context.accent.label, context.grey.label].join(
15802
- " + "
15832
+ const familySummary = useMemo(
15833
+ () => [context.primary.label, context.accent.label, context.grey.label].join(" + "),
15834
+ [context.primary.label, context.accent.label, context.grey.label]
15803
15835
  );
15836
+ useEffect(() => {
15837
+ return () => {
15838
+ if (copiedShareLinkTimeoutRef.current) {
15839
+ clearTimeout(copiedShareLinkTimeoutRef.current);
15840
+ }
15841
+ };
15842
+ }, []);
15804
15843
  const updateUrlParams = (nextThemeCategory, nextPrimaryKey, nextAccentKey, nextSelectedBackgroundToken, nextSelectedPairId) => {
15805
15844
  const params = new URLSearchParams(window.location.search);
15845
+ params.delete("family");
15806
15846
  params.set("palette", nextThemeCategory);
15807
15847
  params.set("primary", nextPrimaryKey);
15808
15848
  params.set("accent", nextAccentKey);
@@ -15904,125 +15944,212 @@ function ColorPairingToolContent() {
15904
15944
  /* @__PURE__ */ jsx(CardDescription, { children: "No approved tones are available for the current palette." })
15905
15945
  ] }) });
15906
15946
  }
15907
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
15908
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
15909
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
15910
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Palette:" }),
15911
- /* @__PURE__ */ jsx(
15912
- SegmentedControl,
15913
- {
15914
- value: themeCategory,
15915
- onValueChange: (value) => handleThemeCategoryChange(value),
15916
- children: /* @__PURE__ */ jsxs(SegmentedControlList, { className: "w-full sm:w-fit", children: [
15917
- /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "brand", children: "Brand palette" }),
15918
- /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "aboriginal", children: "Aboriginal palette" })
15919
- ] })
15920
- }
15921
- )
15922
- ] }),
15923
- /* @__PURE__ */ jsx(FormatToggle, { format, setFormat })
15924
- ] }),
15925
- /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
15947
+ const handleCopyShareLink = () => {
15948
+ copyShareLinkRaw(window.location.href);
15949
+ setCopiedShareLink(true);
15950
+ toast("Colour pairing link copied to clipboard", {
15951
+ duration: 2e3
15952
+ });
15953
+ if (copiedShareLinkTimeoutRef.current) {
15954
+ clearTimeout(copiedShareLinkTimeoutRef.current);
15955
+ }
15956
+ copiedShareLinkTimeoutRef.current = setTimeout(() => {
15957
+ setCopiedShareLink(false);
15958
+ copiedShareLinkTimeoutRef.current = null;
15959
+ }, 2e3);
15960
+ };
15961
+ const activePaletteEntries = [
15962
+ {
15963
+ key: `primary-${context.primary.key}`,
15964
+ label: "Primary",
15965
+ family: context.primary,
15966
+ familyLabel: getActivePaletteFamilyLabel(context.primary, themeCategory, "primary"),
15967
+ swatchTone: 800
15968
+ },
15969
+ {
15970
+ key: `accent-${context.accent.key}`,
15971
+ label: "Accent",
15972
+ family: context.accent,
15973
+ familyLabel: getActivePaletteFamilyLabel(context.accent, themeCategory, "accent"),
15974
+ swatchTone: 600
15975
+ },
15976
+ {
15977
+ key: `grey-${context.grey.key}`,
15978
+ label: "Grey",
15979
+ family: context.grey,
15980
+ familyLabel: getActivePaletteFamilyLabel(context.grey, themeCategory, "grey"),
15981
+ swatchTone: 800
15982
+ }
15983
+ ];
15984
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
15985
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15926
15986
  /* @__PURE__ */ jsx(
15927
- ColorFamilySelector,
15928
- {
15929
- label: "Primary colour",
15930
- families: selectableFamilies,
15931
- selectedKey: context.primary.key,
15932
- selectionRole: "primary colour",
15933
- themeCategory,
15934
- onSelect: handlePrimaryColorChange
15987
+ WorkflowStep,
15988
+ {
15989
+ step: 1,
15990
+ title: "Select your palette",
15991
+ description: "Choose the palette family you want to work from.",
15992
+ level: 2
15935
15993
  }
15936
15994
  ),
15995
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
15996
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
15997
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Palette:" }),
15998
+ /* @__PURE__ */ jsx(
15999
+ SegmentedControl,
16000
+ {
16001
+ value: themeCategory,
16002
+ onValueChange: (value) => handleThemeCategoryChange(value),
16003
+ children: /* @__PURE__ */ jsxs(SegmentedControlList, { className: "w-full sm:w-fit", children: [
16004
+ /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "brand", children: "Brand palette" }),
16005
+ /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "aboriginal", children: "Aboriginal palette" })
16006
+ ] })
16007
+ }
16008
+ )
16009
+ ] }),
16010
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-3", children: /* @__PURE__ */ jsxs(Button2, { variant: "outline", size: "sm", onClick: handleCopyShareLink, children: [
16011
+ copiedShareLink ? /* @__PURE__ */ jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsx(Icons.content_copy, { "data-slot": "icon", className: "size-4" }),
16012
+ copiedShareLink ? "Link copied" : "Copy link"
16013
+ ] }) })
16014
+ ] })
16015
+ ] }),
16016
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15937
16017
  /* @__PURE__ */ jsx(
15938
- ColorFamilySelector,
15939
- {
15940
- label: "Accent colour",
15941
- families: selectableFamilies.filter((family) => family.key !== context.primary.key),
15942
- selectedKey: context.accent.key,
15943
- selectionRole: "accent colour",
15944
- themeCategory,
15945
- onSelect: handleAccentColorChange
16018
+ WorkflowStep,
16019
+ {
16020
+ step: 2,
16021
+ title: "Choose your colours",
16022
+ description: "Pick a primary and accent colour. Grey is added automatically.",
16023
+ level: 2
15946
16024
  }
15947
- )
16025
+ ),
16026
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
16027
+ /* @__PURE__ */ jsx(
16028
+ ColorFamilySelector,
16029
+ {
16030
+ label: "Primary colour",
16031
+ families: selectableFamilies,
16032
+ selectedKey: context.primary.key,
16033
+ selectionRole: "primary colour",
16034
+ themeCategory,
16035
+ onSelect: handlePrimaryColorChange
16036
+ }
16037
+ ),
16038
+ /* @__PURE__ */ jsx(
16039
+ ColorFamilySelector,
16040
+ {
16041
+ label: "Accent colour",
16042
+ families: selectableAccentFamilies,
16043
+ selectedKey: context.accent.key,
16044
+ selectionRole: "accent colour",
16045
+ themeCategory,
16046
+ onSelect: handleAccentColorChange
16047
+ }
16048
+ )
16049
+ ] })
15948
16050
  ] }),
15949
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
15950
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Included families:" }),
15951
- context.allFamilies.map((family) => /* @__PURE__ */ jsxs(
16051
+ /* @__PURE__ */ jsx("div", { className: "rounded-sm border border-grey-200 bg-grey-50 px-4 py-4 dark:border-grey-700 dark:bg-grey-900/40", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
16052
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.14em] text-foreground uppercase", children: "Active palette" }),
16053
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground", children: activePaletteEntries.map(({ family, familyLabel, key, label, swatchTone }) => /* @__PURE__ */ jsxs(
15952
16054
  "span",
15953
16055
  {
15954
- className: "inline-flex items-center gap-2 rounded-full border border-grey-200 px-3 py-1 text-muted-foreground dark:border-grey-700",
16056
+ className: "inline-flex items-center gap-2 rounded-sm border border-grey-200 bg-background px-3 py-2 dark:border-grey-700",
15955
16057
  children: [
15956
16058
  /* @__PURE__ */ jsx(
15957
16059
  "span",
15958
16060
  {
15959
16061
  className: "size-2.5 rounded-full",
15960
- style: { backgroundColor: getFamilySwatchColor(family) }
16062
+ style: { backgroundColor: getFamilySwatchColor(family, swatchTone) }
15961
16063
  }
15962
16064
  ),
15963
- family.label
16065
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
16066
+ label,
16067
+ ":"
16068
+ ] }),
16069
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: familyLabel })
15964
16070
  ]
15965
16071
  },
15966
- family.key
15967
- ))
15968
- ] }),
16072
+ key
16073
+ )) })
16074
+ ] }) }),
15969
16075
  /* @__PURE__ */ jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsxs("div", { className: "grid items-start gap-6 xl:grid-cols-[minmax(0,1.5fr)_minmax(18rem,0.72fr)]", children: [
15970
16076
  /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
15971
- previewPair ? /* @__PURE__ */ jsx(
15972
- PairPreview,
15973
- {
15974
- familySummary,
15975
- isRecommended: Boolean(selectedPair),
15976
- pair: previewPair
15977
- }
15978
- ) : /* @__PURE__ */ jsx(
15979
- PreviewFallbackCard,
15980
- {
15981
- familySummary,
15982
- selectedBackground
15983
- }
15984
- ),
15985
16077
  /* @__PURE__ */ jsxs(Card, { children: [
15986
- /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15987
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Approved backgrounds" }),
15988
- /* @__PURE__ */ jsx(CardDescription, { children: "Choose from your selected primary, accent, and grey background tones. Filled chips have at least one recommended AAA foreground. Foregrounds are limited to white and tones 50, 200, 400, 600, and 800." })
16078
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 border-b", children: [
16079
+ /* @__PURE__ */ jsx(
16080
+ WorkflowStep,
16081
+ {
16082
+ step: 3,
16083
+ title: "Pick a background",
16084
+ description: "Select an approved background tone from your primary, accent, or grey families.",
16085
+ level: 3,
16086
+ variant: "card"
16087
+ }
16088
+ ),
16089
+ /* @__PURE__ */ jsx(CardDescription, { children: "The centre dot shows the recommended foreground colour. A white slash means there is no recommended AAA foreground in this tool." })
15989
16090
  ] }),
15990
16091
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-5", children: [
15991
- context.backgroundGroups.map((group) => /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15992
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
15993
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: group.label }),
15994
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: group.family.label })
16092
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 rounded-sm border border-grey-200 bg-grey-50 p-4 sm:grid-cols-2 dark:border-grey-700 dark:bg-grey-900/40", children: [
16093
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-sm border border-grey-200 bg-background px-3 py-3 dark:border-grey-700", children: [
16094
+ /* @__PURE__ */ jsx("span", { className: "relative block size-8 rounded-[4px] border border-grey-400 bg-background dark:border-grey-500", children: /* @__PURE__ */ jsx("span", { className: "absolute top-1/2 left-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-grey-800 dark:bg-grey-100" }) }),
16095
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
16096
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Recommended foreground available" }),
16097
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "The centre dot marks the recommended foreground colour." })
16098
+ ] })
15995
16099
  ] }),
15996
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 sm:grid-cols-6", children: group.backgrounds.map((background) => {
15997
- const pairs = context.pairsByBackground[background.token] ?? [];
15998
- const preferredPair = selectedBackground.token === background.token ? selectedPair ?? getPreferredPairForBackground(pairs) : getPreferredPairForBackground(pairs);
15999
- const hasWhiteExample = supportsWhiteForegroundPreview(background);
16000
- const isSelected = selectedBackground.token === background.token;
16001
- const ariaLabel = pairs.length > 0 ? `Select ${background.token} background, recommended with ${preferredPair?.foreground.token ?? "an AAA foreground"}` : hasWhiteExample ? `Select ${background.token} background, white text example available` : `Select ${background.token} background, no recommended foreground in this tool`;
16002
- return /* @__PURE__ */ jsx(
16003
- "button",
16004
- {
16005
- type: "button",
16006
- "aria-label": ariaLabel,
16007
- "aria-pressed": isSelected,
16008
- onClick: () => handleBackgroundChange(background.token),
16009
- className: cn(
16010
- "group relative h-12 rounded-sm border border-grey-200 transition-transform hover:scale-[1.03] dark:border-grey-700",
16011
- isSelected && "ring-2 ring-primary-500 ring-offset-2 ring-offset-background"
16012
- ),
16013
- style: { backgroundColor: background.hex },
16014
- children: preferredPair ? /* @__PURE__ */ jsx(
16015
- "span",
16016
- {
16017
- className: "absolute top-1/2 left-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border border-white/30 shadow-sm",
16018
- style: { backgroundColor: preferredPair.foreground.hex }
16019
- }
16020
- ) : null
16021
- },
16022
- background.token
16023
- );
16024
- }) })
16025
- ] }, group.key)),
16100
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-sm border border-dashed border-grey-300 bg-background px-3 py-3 dark:border-grey-600", children: [
16101
+ /* @__PURE__ */ jsx("span", { className: "relative block size-8 rounded-[4px] border-2 border-dashed border-grey-400 bg-grey-500 dark:border-grey-500 dark:bg-grey-700", children: /* @__PURE__ */ jsx("span", { className: "absolute top-1/2 left-1/2 h-0.5 w-5 -translate-x-1/2 -translate-y-1/2 rotate-45 rounded-full bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.28)]" }) }),
16102
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
16103
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "No recommended AAA foreground" }),
16104
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "A white slash marks swatches that do not currently have a recommended AAA foreground in this tool." })
16105
+ ] })
16106
+ ] })
16107
+ ] }),
16108
+ context.backgroundGroups.map((group) => {
16109
+ const groupFamilyLabel = selectedBackground.familyKey === group.family.key ? selectedBackground.name ?? selectedBackground.token : group.family.label;
16110
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
16111
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
16112
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: group.label }),
16113
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: groupFamilyLabel })
16114
+ ] }),
16115
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 sm:grid-cols-6", children: group.backgrounds.map((background) => {
16116
+ const pairs = context.pairsByBackground[background.token] ?? [];
16117
+ const hasRecommendedForeground = pairs.length > 0;
16118
+ const preferredPair = selectedBackground.token === background.token ? selectedPair ?? getPreferredPairForBackground(pairs) : getPreferredPairForBackground(pairs);
16119
+ const hasWhiteExample = supportsWhiteForegroundPreview(background);
16120
+ const isSelected = selectedBackground.token === background.token;
16121
+ const ariaLabel = pairs.length > 0 ? `Select ${background.token} background, recommended with ${preferredPair?.foreground.token ?? "an AAA foreground"}` : hasWhiteExample ? `Select ${background.token} background, white text example available` : `Select ${background.token} background, no recommended foreground in this tool`;
16122
+ return /* @__PURE__ */ jsxs(
16123
+ "button",
16124
+ {
16125
+ type: "button",
16126
+ "aria-label": ariaLabel,
16127
+ "aria-pressed": isSelected,
16128
+ onClick: () => handleBackgroundChange(background.token),
16129
+ className: cn(
16130
+ "group relative h-12 rounded-sm border transition-all",
16131
+ hasRecommendedForeground && "border-grey-200 hover:scale-[1.03] hover:shadow-sm dark:border-grey-700",
16132
+ !hasRecommendedForeground && hasWhiteExample && "border-grey-300/80 hover:scale-[1.01] dark:border-grey-600/80",
16133
+ !hasRecommendedForeground && !hasWhiteExample && "border-dashed border-grey-300/70 hover:scale-[1.01] dark:border-grey-600/70",
16134
+ isSelected && "ring-2 ring-primary-500 ring-offset-2 ring-offset-background"
16135
+ ),
16136
+ style: { backgroundColor: background.hex },
16137
+ children: [
16138
+ preferredPair ? /* @__PURE__ */ jsx(
16139
+ "span",
16140
+ {
16141
+ className: "absolute top-1/2 left-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border border-white/30 shadow-sm",
16142
+ style: { backgroundColor: preferredPair.foreground.hex }
16143
+ }
16144
+ ) : null,
16145
+ !hasRecommendedForeground ? /* @__PURE__ */ jsx("span", { className: "absolute top-1/2 left-1/2 h-0.5 w-6 -translate-x-1/2 -translate-y-1/2 rotate-45 rounded-full bg-white shadow-[0_0_0_1px_rgba(0,0,0,0.28)]" }) : null
16146
+ ]
16147
+ },
16148
+ background.token
16149
+ );
16150
+ }) })
16151
+ ] }, group.key);
16152
+ }),
16026
16153
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4 text-sm text-muted-foreground", children: [
16027
16154
  /* @__PURE__ */ jsxs("span", { children: [
16028
16155
  "Selected background:",
@@ -16037,9 +16164,37 @@ function ColorPairingToolContent() {
16037
16164
  ] })
16038
16165
  ] })
16039
16166
  ] }),
16167
+ previewPair ? /* @__PURE__ */ jsx(
16168
+ PairPreview,
16169
+ {
16170
+ familySummary,
16171
+ isRecommended: Boolean(selectedPair),
16172
+ pair: previewPair
16173
+ }
16174
+ ) : /* @__PURE__ */ jsx(
16175
+ PreviewFallbackCard,
16176
+ {
16177
+ familySummary,
16178
+ selectedBackground
16179
+ }
16180
+ ),
16040
16181
  /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
16041
- /* @__PURE__ */ jsx(PairDetailCard, { color: selectedBackground, format, role: "Background" }),
16042
- detailForeground ? /* @__PURE__ */ jsx(PairDetailCard, { color: detailForeground, format, role: "Foreground" }) : /* @__PURE__ */ jsxs(Card, { className: "gap-4", children: [
16182
+ /* @__PURE__ */ jsx(
16183
+ PairDetailCard,
16184
+ {
16185
+ color: selectedBackground,
16186
+ role: "Background",
16187
+ visibleFormats
16188
+ }
16189
+ ),
16190
+ detailForeground ? /* @__PURE__ */ jsx(
16191
+ PairDetailCard,
16192
+ {
16193
+ color: detailForeground,
16194
+ role: "Foreground",
16195
+ visibleFormats
16196
+ }
16197
+ ) : /* @__PURE__ */ jsxs(Card, { className: "gap-4", children: [
16043
16198
  /* @__PURE__ */ jsx(CardHeader, { className: "gap-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
16044
16199
  /* @__PURE__ */ jsx(CardTitle, { children: "Foreground" }),
16045
16200
  /* @__PURE__ */ jsx(CardDescription, { children: "No recommended foreground available" })
@@ -16047,21 +16202,27 @@ function ColorPairingToolContent() {
16047
16202
  /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select another approved tone or review the recommended foregrounds for the same background." }) })
16048
16203
  ] })
16049
16204
  ] }),
16050
- selectedPair ? /* @__PURE__ */ jsx(PairComplianceCard, { pair: selectedPair }) : null,
16051
- showWhiteForegroundExample && whiteForegroundExample ? /* @__PURE__ */ jsx(WhiteTextExampleCard, { pair: whiteForegroundExample }) : null
16205
+ previewPair ? /* @__PURE__ */ jsx(PairComplianceCard, { pair: previewPair }) : null
16052
16206
  ] }),
16053
16207
  /* @__PURE__ */ jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsxs(Card, { className: "h-fit", children: [
16054
- /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
16055
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Recommended foregrounds" }),
16208
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 border-b", children: [
16209
+ /* @__PURE__ */ jsx(
16210
+ WorkflowStep,
16211
+ {
16212
+ step: 4,
16213
+ title: "Choose a foreground",
16214
+ description: `Review the AAA combinations available for ${selectedBackground.token}.`,
16215
+ level: 3,
16216
+ variant: "card"
16217
+ }
16218
+ ),
16056
16219
  /* @__PURE__ */ jsxs(CardDescription, { children: [
16057
- "AAA combinations for ",
16058
- selectedBackground.token,
16059
- ", drawn from",
16060
- " ",
16220
+ "Recommended foregrounds are drawn from ",
16061
16221
  context.primary.label,
16062
- ", ",
16222
+ ",",
16223
+ " ",
16063
16224
  context.accent.label,
16064
- ", Grey, and white using only foreground tones 50, 200, 400, 600, and 800."
16225
+ ", Grey, and white where it meets AAA."
16065
16226
  ] })
16066
16227
  ] }),
16067
16228
  /* @__PURE__ */ jsx(CardContent, { className: "grid gap-3", children: selectedBackgroundPairs.length > 0 ? selectedBackgroundPairs.map((pair) => {
@@ -16070,7 +16231,7 @@ function ColorPairingToolContent() {
16070
16231
  "button",
16071
16232
  {
16072
16233
  type: "button",
16073
- "aria-label": `Use ${pair.foreground.token} on ${pair.background.token}`,
16234
+ "aria-label": `Use ${getPairingColorDisplayName(pair.foreground)} on ${getPairingColorDisplayName(pair.background)}`,
16074
16235
  "aria-pressed": isActive,
16075
16236
  onClick: () => handlePairChange(pair.id),
16076
16237
  className: cn(
@@ -16079,32 +16240,43 @@ function ColorPairingToolContent() {
16079
16240
  ),
16080
16241
  children: [
16081
16242
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
16082
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
16083
- /* @__PURE__ */ jsx(
16084
- "div",
16085
- {
16086
- className: "size-11 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
16087
- style: { backgroundColor: pair.background.hex }
16088
- }
16089
- ),
16090
- /* @__PURE__ */ jsx(
16091
- "div",
16092
- {
16093
- className: "size-11 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
16094
- style: { backgroundColor: pair.foreground.hex }
16095
- }
16096
- )
16243
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
16244
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-[0.65rem] font-semibold tracking-[0.14em] text-muted-foreground uppercase", children: [
16245
+ /* @__PURE__ */ jsx("span", { className: "w-11 text-center", children: "BG" }),
16246
+ /* @__PURE__ */ jsx("span", { className: "w-11 text-center", children: "FG" })
16247
+ ] }),
16248
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
16249
+ /* @__PURE__ */ jsx(
16250
+ "div",
16251
+ {
16252
+ className: "size-11 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
16253
+ style: {
16254
+ backgroundColor: pair.background.hex
16255
+ }
16256
+ }
16257
+ ),
16258
+ /* @__PURE__ */ jsx(
16259
+ "div",
16260
+ {
16261
+ className: "size-11 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
16262
+ style: {
16263
+ backgroundColor: pair.foreground.hex
16264
+ }
16265
+ }
16266
+ )
16267
+ ] })
16097
16268
  ] }),
16098
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-grey-200 px-2.5 py-1 text-xs font-semibold text-muted-foreground dark:border-grey-700", children: [
16269
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-success-200 bg-success-50 px-2.5 py-1 text-xs font-semibold text-success-800 dark:border-success-900/60 dark:bg-success-950/20 dark:text-success-200", children: [
16099
16270
  /* @__PURE__ */ jsx(Icons.contrast, { "data-slot": "icon", className: "size-4" }),
16100
16271
  pair.rating
16101
16272
  ] })
16102
16273
  ] }),
16103
16274
  /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-1", children: [
16104
16275
  /* @__PURE__ */ jsxs("p", { className: "font-medium text-foreground", children: [
16105
- pair.background.token,
16106
- " / ",
16107
- pair.foreground.token
16276
+ getPairingColorDisplayName(pair.background),
16277
+ " /",
16278
+ " ",
16279
+ getPairingColorDisplayName(pair.foreground)
16108
16280
  ] }),
16109
16281
  /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
16110
16282
  pair.foreground.familyLabel,
@@ -16125,8 +16297,11 @@ function ColorPairingToolContent() {
16125
16297
  ] }) })
16126
16298
  ] });
16127
16299
  }
16128
- function ColorPairingTool() {
16129
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ColorPairingToolLoading, {}), children: /* @__PURE__ */ jsx(ColorPairingToolContent, {}) });
16300
+ function ColorPairingTool({
16301
+ visibleFormats = DEFAULT_VISIBLE_FORMATS
16302
+ } = {}) {
16303
+ const normalizedVisibleFormats = [...new Set(visibleFormats)];
16304
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ColorPairingToolLoading, {}), children: /* @__PURE__ */ jsx(ColorPairingToolContent, { visibleFormats: normalizedVisibleFormats }) });
16130
16305
  }
16131
16306
  function ColorSwatches({ theme: theme2, format, viewMode }) {
16132
16307
  return /* @__PURE__ */ jsx(
@@ -18607,93 +18782,152 @@ function FormMessage({ className, ...props }) {
18607
18782
  }
18608
18783
  );
18609
18784
  }
18610
-
18611
- // package.json
18612
- var package_default = {
18613
- version: "1.99.0"};
18614
- var SluggerContext = React5__default.createContext(null);
18615
- function flattenText(nodes) {
18616
- if (nodes == null || typeof nodes === "boolean") return "";
18617
- if (typeof nodes === "string" || typeof nodes === "number" || typeof nodes === "bigint")
18618
- return String(nodes);
18619
- if (Array.isArray(nodes)) return nodes.map(flattenText).join("");
18620
- if (React5__default.isValidElement(nodes)) {
18621
- return flattenText(nodes.props.children);
18785
+ var styles3 = {
18786
+ base: [
18787
+ // Base
18788
+ "inline-flex items-center justify-center gap-2 rounded-sm text-sm font-medium bg-transparent transition-all whitespace-nowrap cursor-pointer border-(--toggle-border) [--toggle-border:var(--color-grey-200)] text-grey-800",
18789
+ // States
18790
+ "data-[state=on]:bg-grey-100 data-[state=on]:text-grey-850",
18791
+ // Hover
18792
+ "hover:bg-grey-100 hover:text-grey-850",
18793
+ // Focus
18794
+ "focus:outline focus:outline-2 focus:outline-offset-0 focus:outline-(--toggle-border)",
18795
+ // Dark mode
18796
+ "dark:text-white",
18797
+ // Dark mode states
18798
+ "dark:data-[state=on]:bg-white/10 dark:data-[state=on]:text-white",
18799
+ // Dark mode hover
18800
+ "dark:hover:bg-white/10 dark:hover:text-white",
18801
+ // Disabled
18802
+ "disabled:pointer-events-none disabled:opacity-50",
18803
+ // Icon
18804
+ '[&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 [&_svg]:shrink-0',
18805
+ // Aria invalid
18806
+ "aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
18807
+ // Aria invalid dark mode
18808
+ "dark:aria-invalid:ring-destructive/40"
18809
+ ],
18810
+ outline: [
18811
+ // Base
18812
+ "text-grey-800 border [--toggle-border:var(--color-grey-300)]",
18813
+ // States
18814
+ "hover:[--toggle-border:var(--color-grey-400)]",
18815
+ // Dark mode
18816
+ "dark:[--toggle-border:white]/40",
18817
+ // Dark mode states
18818
+ "dark:hover:[--toggle-border:white]/50",
18819
+ // Data on
18820
+ "data-[state=on]:bg-primary-800/10"
18821
+ ]
18822
+ };
18823
+ var toggleVariants = cva(styles3.base, {
18824
+ variants: {
18825
+ variant: {
18826
+ ghost: "",
18827
+ outline: clsx12(styles3.outline)
18828
+ },
18829
+ size: {
18830
+ default: "h-9 px-2 min-w-9",
18831
+ sm: "h-8 px-1.5 min-w-8",
18832
+ lg: "h-10 px-2.5 min-w-10"
18833
+ }
18834
+ },
18835
+ defaultVariants: {
18836
+ variant: "ghost",
18837
+ size: "default"
18622
18838
  }
18623
- return "";
18624
- }
18625
- function baseSlug(input) {
18626
- return input.toLowerCase().trim().replace(/[\s\W]+/g, "-").replace(/^-+|-+$/g, "");
18839
+ });
18840
+ function Toggle({
18841
+ className,
18842
+ variant,
18843
+ size,
18844
+ ...props
18845
+ }) {
18846
+ return /* @__PURE__ */ jsx(
18847
+ TogglePrimitive.Root,
18848
+ {
18849
+ "data-slot": "toggle",
18850
+ className: cn(toggleVariants({ variant, size, className })),
18851
+ ...props
18852
+ }
18853
+ );
18627
18854
  }
18628
- function Heading({
18855
+ var ToggleGroupContext = React5.createContext({
18856
+ size: "default",
18857
+ variant: "ghost"
18858
+ });
18859
+ function ToggleGroup({
18629
18860
  className,
18630
- trim = "normal",
18631
- size = 1,
18632
- level = 1,
18633
- display = false,
18634
- id: idProp,
18861
+ variant,
18862
+ size,
18635
18863
  children,
18636
18864
  ...props
18637
18865
  }) {
18638
- const Tag = `h${level}`;
18639
- const slugger = useContext(SluggerContext);
18640
- const headingSizeClasses = {
18641
- 1: "text-[calc(var(--heading-font-size-1)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-52)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
18642
- 2: "text-[calc(var(--heading-font-size-2)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-44)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
18643
- 3: "text-[calc(var(--heading-font-size-3)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-40)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
18644
- 4: "text-[calc(var(--heading-font-size-4)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-36)] tracking-[calc(var(--heading-letter-spacing-1)_+_var(--heading-letter-spacing))]",
18645
- 5: "text-[calc(var(--heading-font-size-5)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-32)] tracking-[calc(var(--heading-letter-spacing-1)_+_var(--heading-letter-spacing))]",
18646
- 6: "text-[calc(var(--heading-font-size-6)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-28)] tracking-[calc(var(--letter-spacing-0)_+_var(--heading-letter-spacing))]"
18647
- };
18648
- const displaySizeClasses = {
18649
- 1: "text-[calc(var(--display-font-size-1)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-96)] tracking-[calc(var(--heading-letter-spacing-3)_+_var(--heading-letter-spacing))]",
18650
- 2: "text-[calc(var(--display-font-size-2)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-60)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
18651
- 3: "text-[calc(var(--display-font-size-3)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-52)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]",
18652
- 4: "text-[calc(var(--display-font-size-4)_*_var(--heading-font-size-adjust))] leading-[var(--line-height-44)] tracking-[calc(var(--heading-letter-spacing-2)_+_var(--heading-letter-spacing))]"
18653
- };
18654
- const sizeClass = display ? displaySizeClasses[size] : headingSizeClasses[size];
18655
- const trimClasses = {
18656
- normal: ["before:content-none after:content-none"],
18657
- start: [
18658
- 'before:content-[""] before:table after:content-none',
18659
- "before:mb-[calc(var(--leading-trim-start,var(--default-leading-trim-start))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
18660
- ],
18661
- end: [
18662
- 'before:content-none after:content-[""] after:table',
18663
- "after:mt-[calc(var(--leading-trim-end,var(--default-leading-trim-end))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
18664
- ],
18665
- both: [
18666
- 'before:content-[""] before:table after:content-[""] after:table',
18667
- "before:mb-[calc(var(--leading-trim-start,var(--default-leading-trim-start))-var(--line-height,calc(1em*var(--default-line-height)))/2)]",
18668
- "after:mt-[calc(var(--leading-trim-end,var(--default-leading-trim-end))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
18669
- ]
18670
- };
18671
- const computedId = useMemo(() => {
18672
- if (idProp) return idProp;
18673
- const text = flattenText(children);
18674
- if (!text) return void 0;
18675
- const base = baseSlug(text);
18676
- return slugger ? slugger.slug(base) : base;
18677
- }, [idProp, children, slugger]);
18678
18866
  return /* @__PURE__ */ jsx(
18679
- Tag,
18867
+ ToggleGroupPrimitive.Root,
18680
18868
  {
18869
+ "data-slot": "toggle-group",
18870
+ "data-variant": variant,
18871
+ "data-size": size,
18872
+ className: cn(
18873
+ "group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
18874
+ className
18875
+ ),
18681
18876
  ...props,
18682
- id: computedId,
18683
- "data-anchor": true,
18684
- className: clsx12(
18685
- className,
18686
- trimClasses[trim],
18687
- "m-0",
18688
- "leading-[var(--line-height)] font-[var(--heading-font-family)] font-bold",
18689
- "[--leading-trim-end:var(--heading-leading-trim-end)] [--leading-trim-start:var(--heading-leading-trim-start)]",
18690
- "text-primary-800 dark:text-white",
18691
- sizeClass
18877
+ children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
18878
+ }
18879
+ );
18880
+ }
18881
+ function ToggleGroupItem({
18882
+ className,
18883
+ children,
18884
+ variant,
18885
+ size,
18886
+ ...props
18887
+ }) {
18888
+ const context = React5.useContext(ToggleGroupContext);
18889
+ return /* @__PURE__ */ jsx(
18890
+ ToggleGroupPrimitive.Item,
18891
+ {
18892
+ "data-slot": "toggle-group-item",
18893
+ "data-variant": context.variant || variant,
18894
+ "data-size": context.size || size,
18895
+ className: cn(
18896
+ toggleVariants({
18897
+ variant: context.variant || variant,
18898
+ size: context.size || size
18899
+ }),
18900
+ "min-w-0 shrink-0 rounded-none shadow-none first:rounded-l-sm last:rounded-r-sm focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
18901
+ className
18692
18902
  ),
18903
+ ...props,
18693
18904
  children
18694
18905
  }
18695
18906
  );
18696
18907
  }
18908
+ function FormatToggle({ format, setFormat }) {
18909
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
18910
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Format:" }),
18911
+ /* @__PURE__ */ jsxs(
18912
+ ToggleGroup,
18913
+ {
18914
+ type: "single",
18915
+ value: format,
18916
+ onValueChange: (value) => value && setFormat(value),
18917
+ children: [
18918
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hex", "aria-label": "HEX format", children: "HEX" }),
18919
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "rgb", "aria-label": "RGB format", children: "RGB" }),
18920
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hsl", "aria-label": "HSL format", children: "HSL" }),
18921
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "oklch", "aria-label": "OKLCH format", children: "OKLCH" })
18922
+ ]
18923
+ }
18924
+ )
18925
+ ] });
18926
+ }
18927
+
18928
+ // package.json
18929
+ var package_default = {
18930
+ version: "1.101.0"};
18697
18931
  function Logo(props) {
18698
18932
  return /* @__PURE__ */ jsxs(Fragment, { children: [
18699
18933
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "NSW Government" }),