@nswds/app 1.99.0 → 1.101.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
  );
@@ -14919,7 +14919,10 @@ function ColorCard({ name, token, hex: hex2, rgb: rgb2, hsl, oklch: oklch2, form
14919
14919
  ] }) });
14920
14920
  }
14921
14921
  var recommendedBackgroundTones = [50, 100, 200, 400, 600, 800];
14922
+ var recommendedForegroundTones = [50, 200, 400, 600, 800];
14922
14923
  var AAA_NORMAL_TEXT_RATIO = 7;
14924
+ var DEFAULT_PRIMARY_FAMILY_KEY = "blue";
14925
+ var DEFAULT_ACCENT_FAMILY_KEY = "red";
14923
14926
  var MIN_DARK_BACKGROUND_TONE_FOR_WHITE = 600;
14924
14927
  var WHITE_FOREGROUND = {
14925
14928
  token: "white",
@@ -14928,6 +14931,8 @@ var WHITE_FOREGROUND = {
14928
14931
  rgb: "rgb(255, 255, 255)",
14929
14932
  hsl: "hsl(0, 0%, 100%)",
14930
14933
  oklch: "oklch(1 0 0)",
14934
+ familyKey: "white",
14935
+ familyLabel: "White",
14931
14936
  tone: 0
14932
14937
  };
14933
14938
  function extractTone(token) {
@@ -14938,23 +14943,47 @@ function formatFamilyLabel(name, key) {
14938
14943
  if (key === "grey") return "Grey";
14939
14944
  return name.replace(/^NSW Aboriginal\s+/i, "").replace(/^NSW\s+/i, "").trim();
14940
14945
  }
14941
- function toPairingColor(color2) {
14946
+ function toPairingColor(color2, familyKey, familyLabel) {
14942
14947
  return {
14943
14948
  ...color2,
14949
+ familyKey,
14950
+ familyLabel,
14944
14951
  tone: extractTone(color2.token)
14945
14952
  };
14946
14953
  }
14947
- function getPreferredForegroundTone(backgroundTone) {
14948
- if (backgroundTone <= 200) return 800;
14949
- if (backgroundTone <= 500) return 700;
14950
- if (backgroundTone <= 650) return 250;
14951
- return 200;
14954
+ function buildPairingCollections() {
14955
+ return {
14956
+ brand: Object.entries(colors.brand).map(([key, palette]) => {
14957
+ const familyLabel = formatFamilyLabel(palette.name, key);
14958
+ return {
14959
+ key,
14960
+ label: familyLabel,
14961
+ paletteName: palette.name,
14962
+ colors: palette.colors.map((color2) => toPairingColor(color2, key, familyLabel))
14963
+ };
14964
+ }),
14965
+ aboriginal: Object.entries(colors.aboriginal).map(([key, palette]) => {
14966
+ const familyLabel = formatFamilyLabel(palette.name, key);
14967
+ return {
14968
+ key,
14969
+ label: familyLabel,
14970
+ paletteName: palette.name,
14971
+ colors: palette.colors.map((color2) => toPairingColor(color2, key, familyLabel))
14972
+ };
14973
+ })
14974
+ };
14952
14975
  }
14976
+ var pairingFamilyCollections = buildPairingCollections();
14953
14977
  function getPairRating(contrastRatio) {
14954
14978
  if (contrastRatio >= 7) return "AAA";
14955
14979
  if (contrastRatio >= 4.5) return "AA";
14956
14980
  return "AA Large";
14957
14981
  }
14982
+ function getPreferredForegroundTone(backgroundTone) {
14983
+ if (backgroundTone <= 200) return 800;
14984
+ if (backgroundTone <= 500) return 600;
14985
+ return 200;
14986
+ }
14958
14987
  function isPreferredDirection(backgroundTone, foregroundTone, preferredForegroundTone) {
14959
14988
  if (preferredForegroundTone < backgroundTone) {
14960
14989
  return foregroundTone < backgroundTone;
@@ -14977,15 +15006,27 @@ function buildPair(background, foreground) {
14977
15006
  rating: getPairRating(contrastRatio)
14978
15007
  };
14979
15008
  }
14980
- function pickForeground(colorsToPair, background, minimumRatio) {
15009
+ function sortRecommendedPairs(background, pairs) {
14981
15010
  const preferredForegroundTone = getPreferredForegroundTone(background.tone);
14982
- const passingCandidates = colorsToPair.filter((color2) => color2.token !== background.token).map((color2) => buildPair(background, color2)).filter((pair) => pair.contrastRatio >= minimumRatio);
14983
- if (passingCandidates.length === 0) return null;
14984
- const preferredCandidates = passingCandidates.filter(
14985
- (pair) => isPreferredDirection(background.tone, pair.foreground.tone, preferredForegroundTone)
14986
- );
14987
- const candidates = preferredCandidates.length > 0 ? preferredCandidates : passingCandidates;
14988
- return candidates.sort((left, right) => {
15011
+ return [...pairs].sort((left, right) => {
15012
+ const leftPreferred = isPreferredDirection(
15013
+ background.tone,
15014
+ left.foreground.tone,
15015
+ preferredForegroundTone
15016
+ ) ? 0 : 1;
15017
+ const rightPreferred = isPreferredDirection(
15018
+ background.tone,
15019
+ right.foreground.tone,
15020
+ preferredForegroundTone
15021
+ ) ? 0 : 1;
15022
+ if (leftPreferred !== rightPreferred) {
15023
+ return leftPreferred - rightPreferred;
15024
+ }
15025
+ const leftWhitePenalty = left.foreground.token === WHITE_FOREGROUND.token ? 1 : 0;
15026
+ const rightWhitePenalty = right.foreground.token === WHITE_FOREGROUND.token ? 1 : 0;
15027
+ if (leftWhitePenalty !== rightWhitePenalty) {
15028
+ return leftWhitePenalty - rightWhitePenalty;
15029
+ }
14989
15030
  const leftTargetDelta = Math.abs(left.foreground.tone - preferredForegroundTone);
14990
15031
  const rightTargetDelta = Math.abs(right.foreground.tone - preferredForegroundTone);
14991
15032
  if (leftTargetDelta !== rightTargetDelta) {
@@ -14996,258 +15037,359 @@ function pickForeground(colorsToPair, background, minimumRatio) {
14996
15037
  if (leftToneGap !== rightToneGap) {
14997
15038
  return rightToneGap - leftToneGap;
14998
15039
  }
14999
- return right.contrastRatio - left.contrastRatio;
15000
- })[0];
15001
- }
15002
- function buildRecommendedPairs(colorsToPair, minimumRatio) {
15003
- const backgrounds = recommendedBackgroundTones.map((tone) => colorsToPair.find((color2) => color2.tone === tone)).filter((color2) => Boolean(color2));
15004
- const recommendedPairs = [];
15005
- for (const background of backgrounds) {
15006
- const pair = pickForeground(colorsToPair, background, minimumRatio);
15007
- const whitePair = background.tone >= MIN_DARK_BACKGROUND_TONE_FOR_WHITE ? buildPair(background, WHITE_FOREGROUND) : null;
15008
- if (pair && !recommendedPairs.some((item) => item.id === pair.id)) {
15009
- recommendedPairs.push(pair);
15040
+ if (left.foreground.familyKey === background.familyKey && right.foreground.familyKey !== background.familyKey) {
15041
+ return -1;
15010
15042
  }
15011
- if (whitePair && whitePair.contrastRatio >= minimumRatio && !recommendedPairs.some((item) => item.id === whitePair.id)) {
15012
- recommendedPairs.push(whitePair);
15043
+ if (right.foreground.familyKey === background.familyKey && left.foreground.familyKey !== background.familyKey) {
15044
+ return 1;
15013
15045
  }
15046
+ return right.contrastRatio - left.contrastRatio;
15047
+ });
15048
+ }
15049
+ function dedupeColors(colorsToDedupe) {
15050
+ return Array.from(new Map(colorsToDedupe.map((color2) => [color2.token, color2])).values());
15051
+ }
15052
+ function buildRecommendedPairsForBackground(background, foregrounds, minimumRatio) {
15053
+ const candidatePairs = foregrounds.filter((foreground) => foreground.token !== background.token).map((foreground) => buildPair(background, foreground)).filter((pair) => pair.contrastRatio >= minimumRatio);
15054
+ const whitePair = background.tone >= MIN_DARK_BACKGROUND_TONE_FOR_WHITE ? buildPair(background, WHITE_FOREGROUND) : null;
15055
+ if (whitePair && whitePair.contrastRatio >= minimumRatio) {
15056
+ candidatePairs.push(whitePair);
15014
15057
  }
15015
- return recommendedPairs;
15058
+ const dedupedPairs = Array.from(new Map(candidatePairs.map((pair) => [pair.id, pair])).values());
15059
+ return sortRecommendedPairs(background, dedupedPairs);
15060
+ }
15061
+ function findGreyFamily(families) {
15062
+ return families.find((family) => family.key.toLowerCase().includes("grey")) ?? families[0];
15063
+ }
15064
+ function getNonGreyFamilies(families) {
15065
+ const greyFamily = findGreyFamily(families);
15066
+ return families.filter((family) => family.key !== greyFamily.key);
15067
+ }
15068
+ function findFamilyByKey(families, key) {
15069
+ return key ? families.find((family) => family.key === key) : void 0;
15070
+ }
15071
+ function getDefaultPrimaryFamily(families) {
15072
+ const nonGreyFamilies = getNonGreyFamilies(families);
15073
+ return findFamilyByKey(nonGreyFamilies, DEFAULT_PRIMARY_FAMILY_KEY) ?? findFamilyByKey(nonGreyFamilies, "green") ?? nonGreyFamilies[0] ?? families[0];
15074
+ }
15075
+ function getDefaultAccentFamily(families, primaryKey) {
15076
+ const nonGreyFamilies = getNonGreyFamilies(families).filter((family) => family.key !== primaryKey);
15077
+ return findFamilyByKey(nonGreyFamilies, DEFAULT_ACCENT_FAMILY_KEY) ?? nonGreyFamilies[0] ?? getDefaultPrimaryFamily(families);
15078
+ }
15079
+ function isApprovedBackgroundTone(color2) {
15080
+ return recommendedBackgroundTones.includes(
15081
+ color2.tone
15082
+ );
15083
+ }
15084
+ function isApprovedForegroundTone(color2) {
15085
+ return recommendedForegroundTones.includes(
15086
+ color2.tone
15087
+ );
15088
+ }
15089
+ function getPairingFamilies(themeCategory) {
15090
+ return pairingFamilyCollections[themeCategory];
15016
15091
  }
15017
- function buildRecommendedCollections(minimumRatio) {
15092
+ function getDefaultAccentFamilyKey(themeCategory, primaryKey) {
15093
+ return getDefaultAccentFamily(getPairingFamilies(themeCategory), primaryKey).key;
15094
+ }
15095
+ function getPairingContext(themeCategory, primaryKey, accentKey) {
15096
+ const families = getPairingFamilies(themeCategory);
15097
+ const primary = findFamilyByKey(getNonGreyFamilies(families), primaryKey) ?? getDefaultPrimaryFamily(families);
15098
+ const accent = findFamilyByKey(
15099
+ getNonGreyFamilies(families).filter((family) => family.key !== primary.key),
15100
+ accentKey
15101
+ ) ?? getDefaultAccentFamily(families, primary.key);
15102
+ const grey = findGreyFamily(families);
15103
+ const allFamilies = [primary, accent, grey];
15104
+ const backgroundGroups = [
15105
+ {
15106
+ key: "primary",
15107
+ label: "Primary backgrounds",
15108
+ family: primary,
15109
+ backgrounds: primary.colors.filter(isApprovedBackgroundTone)
15110
+ },
15111
+ {
15112
+ key: "accent",
15113
+ label: "Accent backgrounds",
15114
+ family: accent,
15115
+ backgrounds: accent.colors.filter(isApprovedBackgroundTone)
15116
+ },
15117
+ {
15118
+ key: "grey",
15119
+ label: "Grey backgrounds",
15120
+ family: grey,
15121
+ backgrounds: grey.colors.filter(isApprovedBackgroundTone)
15122
+ }
15123
+ ];
15124
+ const backgrounds = backgroundGroups.flatMap((group) => group.backgrounds);
15125
+ const candidateForegrounds = dedupeColors(
15126
+ allFamilies.flatMap((family) => family.colors.filter(isApprovedForegroundTone))
15127
+ );
15128
+ const pairsByBackground = {};
15129
+ for (const background of backgrounds) {
15130
+ pairsByBackground[background.token] = buildRecommendedPairsForBackground(
15131
+ background,
15132
+ candidateForegrounds,
15133
+ AAA_NORMAL_TEXT_RATIO
15134
+ );
15135
+ }
15018
15136
  return {
15019
- brand: Object.entries(colors.brand).map(([key, palette]) => {
15020
- const scale2 = palette.colors.map(toPairingColor);
15021
- return {
15022
- key,
15023
- label: formatFamilyLabel(palette.name, key),
15024
- paletteName: palette.name,
15025
- colors: scale2,
15026
- recommendedPairs: buildRecommendedPairs(scale2, minimumRatio)
15027
- };
15028
- }),
15029
- aboriginal: Object.entries(colors.aboriginal).map(([key, palette]) => {
15030
- const scale2 = palette.colors.map(toPairingColor);
15031
- return {
15032
- key,
15033
- label: formatFamilyLabel(palette.name, key),
15034
- paletteName: palette.name,
15035
- colors: scale2,
15036
- recommendedPairs: buildRecommendedPairs(scale2, minimumRatio)
15037
- };
15038
- })
15137
+ accent,
15138
+ allFamilies,
15139
+ backgroundGroups,
15140
+ backgrounds,
15141
+ grey,
15142
+ pairsByBackground,
15143
+ primary,
15144
+ recommendedPairs: backgrounds.flatMap(
15145
+ (background) => pairsByBackground[background.token] ?? []
15146
+ ),
15147
+ themeCategory
15039
15148
  };
15040
15149
  }
15041
- var recommendedAaaPairingCollections = buildRecommendedCollections(AAA_NORMAL_TEXT_RATIO);
15042
15150
  function getWhiteForegroundPair(background) {
15043
15151
  return buildPair(background, WHITE_FOREGROUND);
15044
15152
  }
15045
15153
  function supportsWhiteForegroundPreview(background) {
15046
15154
  return background.tone >= MIN_DARK_BACKGROUND_TONE_FOR_WHITE;
15047
15155
  }
15048
- function getPairingColorValue(color2, format) {
15049
- return getColorValue(color2, format);
15050
- }
15051
- var styles3 = {
15052
- base: [
15053
- // Base
15054
- "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",
15055
- // States
15056
- "data-[state=on]:bg-grey-100 data-[state=on]:text-grey-850",
15057
- // Hover
15058
- "hover:bg-grey-100 hover:text-grey-850",
15059
- // Focus
15060
- "focus:outline focus:outline-2 focus:outline-offset-0 focus:outline-(--toggle-border)",
15061
- // Dark mode
15062
- "dark:text-white",
15063
- // Dark mode states
15064
- "dark:data-[state=on]:bg-white/10 dark:data-[state=on]:text-white",
15065
- // Dark mode hover
15066
- "dark:hover:bg-white/10 dark:hover:text-white",
15067
- // Disabled
15068
- "disabled:pointer-events-none disabled:opacity-50",
15069
- // Icon
15070
- '[&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 [&_svg]:shrink-0',
15071
- // Aria invalid
15072
- "aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
15073
- // Aria invalid dark mode
15074
- "dark:aria-invalid:ring-destructive/40"
15075
- ],
15076
- outline: [
15077
- // Base
15078
- "text-grey-800 border [--toggle-border:var(--color-grey-300)]",
15079
- // States
15080
- "hover:[--toggle-border:var(--color-grey-400)]",
15081
- // Dark mode
15082
- "dark:[--toggle-border:white]/40",
15083
- // Dark mode states
15084
- "dark:hover:[--toggle-border:white]/50",
15085
- // Data on
15086
- "data-[state=on]:bg-primary-800/10"
15087
- ]
15088
- };
15089
- var toggleVariants = cva(styles3.base, {
15090
- variants: {
15091
- variant: {
15092
- ghost: "",
15093
- outline: clsx12(styles3.outline)
15094
- },
15095
- size: {
15096
- default: "h-9 px-2 min-w-9",
15097
- sm: "h-8 px-1.5 min-w-8",
15098
- lg: "h-10 px-2.5 min-w-10"
15099
- }
15100
- },
15101
- defaultVariants: {
15102
- variant: "ghost",
15103
- 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);
15104
15164
  }
15105
- });
15106
- function Toggle({
15107
- className,
15108
- variant,
15109
- size,
15110
- ...props
15111
- }) {
15112
- return /* @__PURE__ */ jsx(
15113
- TogglePrimitive.Root,
15114
- {
15115
- "data-slot": "toggle",
15116
- className: cn(toggleVariants({ variant, size, className })),
15117
- ...props
15118
- }
15119
- );
15165
+ return "";
15120
15166
  }
15121
- var ToggleGroupContext = React5.createContext({
15122
- size: "default",
15123
- variant: "ghost"
15124
- });
15125
- function ToggleGroup({
15126
- className,
15127
- variant,
15128
- size,
15129
- children,
15130
- ...props
15131
- }) {
15132
- return /* @__PURE__ */ jsx(
15133
- ToggleGroupPrimitive.Root,
15134
- {
15135
- "data-slot": "toggle-group",
15136
- "data-variant": variant,
15137
- "data-size": size,
15138
- className: cn(
15139
- "group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
15140
- className
15141
- ),
15142
- ...props,
15143
- children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
15144
- }
15145
- );
15167
+ function baseSlug(input) {
15168
+ return input.toLowerCase().trim().replace(/[\s\W]+/g, "-").replace(/^-+|-+$/g, "");
15146
15169
  }
15147
- function ToggleGroupItem({
15170
+ function Heading({
15148
15171
  className,
15172
+ trim = "normal",
15173
+ size = 1,
15174
+ level = 1,
15175
+ display = false,
15176
+ id: idProp,
15149
15177
  children,
15150
- variant,
15151
- size,
15152
15178
  ...props
15153
15179
  }) {
15154
- 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]);
15155
15220
  return /* @__PURE__ */ jsx(
15156
- ToggleGroupPrimitive.Item,
15221
+ Tag,
15157
15222
  {
15158
- "data-slot": "toggle-group-item",
15159
- "data-variant": context.variant || variant,
15160
- "data-size": context.size || size,
15161
- className: cn(
15162
- toggleVariants({
15163
- variant: context.variant || variant,
15164
- size: context.size || size
15165
- }),
15166
- "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",
15167
- className
15168
- ),
15169
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
+ ),
15170
15235
  children
15171
15236
  }
15172
15237
  );
15173
15238
  }
15174
- function FormatToggle({ format, setFormat }) {
15175
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
15176
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Format:" }),
15177
- /* @__PURE__ */ jsxs(
15178
- ToggleGroup,
15179
- {
15180
- type: "single",
15181
- value: format,
15182
- onValueChange: (value) => value && setFormat(value),
15183
- children: [
15184
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hex", "aria-label": "HEX format", children: "HEX" }),
15185
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "rgb", "aria-label": "RGB format", children: "RGB" }),
15186
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hsl", "aria-label": "HSL format", children: "HSL" }),
15187
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "oklch", "aria-label": "OKLCH format", children: "OKLCH" })
15188
- ]
15189
- }
15190
- )
15191
- ] });
15192
- }
15193
- function getPreferredFamilyKey(families) {
15194
- return families.find((family) => family.key === "green")?.key ?? families[0]?.key ?? "";
15195
- }
15196
- function getDefaultPair(family) {
15197
- if (!family || family.recommendedPairs.length === 0) return null;
15198
- return family.recommendedPairs.reduce((bestPair, pair) => {
15199
- const bestDelta = Math.abs(bestPair.background.tone - 600);
15200
- const currentDelta = Math.abs(pair.background.tone - 600);
15201
- return currentDelta < bestDelta ? pair : bestPair;
15202
- }, family.recommendedPairs[0]);
15203
- }
15204
- function getFamilySwatchColor(family) {
15205
- return family.colors.find((color2) => color2.tone === 500)?.hex ?? family.colors.find((color2) => color2.tone === 400)?.hex ?? family.colors[Math.min(3, family.colors.length - 1)]?.hex ?? "transparent";
15206
- }
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";
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";
15207
15245
  function getToneFromToken(token) {
15208
15246
  if (!token) return null;
15209
15247
  const match = token.match(/-(\d+)$/);
15210
15248
  return match ? Number.parseInt(match[1], 10) : null;
15211
15249
  }
15212
- function isApprovedBackgroundTone(color2) {
15213
- return recommendedBackgroundTones.includes(
15214
- color2.tone
15215
- );
15250
+ function getFamilySwatchColor(family, preferredTone = 600) {
15251
+ const exactMatch = family.colors.find((color2) => color2.tone === preferredTone);
15252
+ if (exactMatch) {
15253
+ return exactMatch.hex;
15254
+ }
15255
+ const closestMatch = [...family.colors].sort(
15256
+ (left, right) => Math.abs(left.tone - preferredTone) - Math.abs(right.tone - preferredTone)
15257
+ )[0];
15258
+ return closestMatch?.hex ?? "transparent";
15216
15259
  }
15217
- function getBackgroundOptions(family) {
15218
- return family?.colors.filter(isApprovedBackgroundTone) ?? [];
15260
+ function getFamilySelectorLabel(family, themeCategory, selectionRole) {
15261
+ if (themeCategory !== "aboriginal") {
15262
+ return family.label;
15263
+ }
15264
+ const preferredTone = selectionRole === "primary colour" ? 800 : 600;
15265
+ return family.colors.find((color2) => color2.tone === preferredTone)?.name ?? family.label;
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;
15219
15273
  }
15220
15274
  function isWhiteForegroundPair(pair) {
15221
15275
  return pair.foreground.token === "white";
15222
15276
  }
15223
- function getRecommendedPairForBackground(family, backgroundToken, preferredPairId) {
15224
- if (!family) return null;
15225
- const backgroundPairs = family.recommendedPairs.filter(
15226
- (pair) => pair.background.token === backgroundToken
15227
- );
15228
- if (backgroundPairs.length === 0) return null;
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
+ }
15341
+ function getPreferredPairForBackground(pairs, preferredPairId) {
15229
15342
  if (preferredPairId) {
15230
- const preferredPair = backgroundPairs.find((pair) => pair.id === preferredPairId);
15343
+ const preferredPair = pairs.find((pair) => pair.id === preferredPairId);
15231
15344
  if (preferredPair) {
15232
15345
  return preferredPair;
15233
15346
  }
15234
15347
  }
15235
- return backgroundPairs.find((pair) => !isWhiteForegroundPair(pair)) ?? backgroundPairs[0];
15348
+ return pairs.find((pair) => !isWhiteForegroundPair(pair)) ?? pairs[0] ?? null;
15236
15349
  }
15237
- function getFallbackBackgroundToken(family, preferredTone) {
15238
- const backgroundOptions = getBackgroundOptions(family);
15239
- if (preferredTone) {
15240
- const preferredBackground = backgroundOptions.find((color2) => color2.tone === preferredTone);
15241
- if (preferredBackground) {
15242
- return preferredBackground.token;
15350
+ function getDefaultBackgroundToken(context) {
15351
+ for (const tone of PREFERRED_BACKGROUND_TONES) {
15352
+ for (const group of context.backgroundGroups) {
15353
+ const match = group.backgrounds.find(
15354
+ (background) => background.tone === tone && (context.pairsByBackground[background.token]?.length ?? 0) > 0
15355
+ );
15356
+ if (match) {
15357
+ return match.token;
15358
+ }
15359
+ }
15360
+ }
15361
+ for (const tone of PREFERRED_BACKGROUND_TONES) {
15362
+ for (const group of context.backgroundGroups) {
15363
+ const match = group.backgrounds.find((background) => background.tone === tone);
15364
+ if (match) {
15365
+ return match.token;
15366
+ }
15367
+ }
15368
+ }
15369
+ return context.backgrounds[0]?.token ?? "";
15370
+ }
15371
+ function resolveBackgroundToken(context, preferredToken, preferredTone) {
15372
+ if (preferredToken && context.backgrounds.some((background) => background.token === preferredToken)) {
15373
+ return preferredToken;
15374
+ }
15375
+ if (preferredTone !== null && preferredTone !== void 0) {
15376
+ for (const group of context.backgroundGroups) {
15377
+ const match = group.backgrounds.find((background) => background.tone === preferredTone);
15378
+ if (match) {
15379
+ return match.token;
15380
+ }
15243
15381
  }
15244
15382
  }
15245
- return getDefaultPair(family)?.background.token ?? backgroundOptions[0]?.token ?? "";
15383
+ return getDefaultBackgroundToken(context);
15246
15384
  }
15247
15385
  function ColorPairingToolLoading() {
15248
15386
  return /* @__PURE__ */ jsxs("div", { className: "animate-pulse space-y-6", children: [
15249
- /* @__PURE__ */ jsx("div", { className: "h-40 rounded-sm border border-grey-200 bg-grey-50 dark:border-grey-700 dark:bg-grey-900" }),
15250
- /* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1.3fr)_minmax(20rem,0.9fr)]", children: [
15387
+ /* @__PURE__ */ jsx("div", { className: "h-32 rounded-sm border border-grey-200 bg-grey-50 dark:border-grey-700 dark:bg-grey-900" }),
15388
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-2", children: [
15389
+ /* @__PURE__ */ jsx("div", { className: "h-48 rounded-sm border border-grey-200 bg-grey-50 dark:border-grey-700 dark:bg-grey-900" }),
15390
+ /* @__PURE__ */ jsx("div", { className: "h-48 rounded-sm border border-grey-200 bg-grey-50 dark:border-grey-700 dark:bg-grey-900" })
15391
+ ] }),
15392
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1.5fr)_minmax(18rem,0.72fr)]", children: [
15251
15393
  /* @__PURE__ */ jsx("div", { className: "h-96 rounded-sm border border-grey-200 bg-grey-50 dark:border-grey-700 dark:bg-grey-900" }),
15252
15394
  /* @__PURE__ */ jsx("div", { className: "h-96 rounded-sm border border-grey-200 bg-grey-50 dark:border-grey-700 dark:bg-grey-900" })
15253
15395
  ] })
@@ -15255,30 +15397,52 @@ function ColorPairingToolLoading() {
15255
15397
  }
15256
15398
  function getInitialPairingState(searchParams) {
15257
15399
  const paletteParam = searchParams.get("palette");
15258
- const familyParam = searchParams.get("family");
15400
+ const primaryParam = searchParams.get("primary");
15401
+ const accentParam = searchParams.get("accent");
15259
15402
  const pairParam = searchParams.get("pair");
15260
15403
  const backgroundParam = searchParams.get("background");
15261
15404
  const themeCategory = paletteParam === "brand" || paletteParam === "aboriginal" ? paletteParam : "brand";
15262
- const families = recommendedAaaPairingCollections[themeCategory];
15263
- const familyKey = families.some((family) => family.key === familyParam) ? familyParam : getPreferredFamilyKey(families);
15264
- const activeFamily = families.find((family) => family.key === familyKey) ?? families[0];
15265
- const backgroundOptions = getBackgroundOptions(activeFamily);
15266
- const pairBackgroundToken = activeFamily?.recommendedPairs.find((pair) => pair.id === pairParam)?.background.token ?? null;
15267
- const selectedBackgroundToken = backgroundOptions.some((color2) => color2.token === backgroundParam) ? backgroundParam : getFallbackBackgroundToken(
15268
- activeFamily,
15269
- getToneFromToken(backgroundParam ?? pairBackgroundToken)
15270
- );
15271
- const selectedPairId = getRecommendedPairForBackground(activeFamily, selectedBackgroundToken, pairParam)?.id ?? "";
15272
- return { familyKey, selectedBackgroundToken, selectedPairId, themeCategory };
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;
15413
+ const pairBackgroundToken = context.recommendedPairs.find((pair) => pair.id === pairParam)?.background.token ?? null;
15414
+ const selectedBackgroundToken = resolveBackgroundToken(
15415
+ context,
15416
+ backgroundParam ?? pairBackgroundToken ?? defaultBackgroundToken,
15417
+ getToneFromToken(backgroundParam ?? pairBackgroundToken ?? defaultBackgroundToken)
15418
+ );
15419
+ const selectedPairId = getPreferredPairForBackground(
15420
+ context.pairsByBackground[selectedBackgroundToken] ?? [],
15421
+ pairParam ?? defaultPairId
15422
+ )?.id ?? "";
15423
+ return {
15424
+ accentKey: context.accent.key,
15425
+ primaryKey: context.primary.key,
15426
+ selectedBackgroundToken,
15427
+ selectedPairId,
15428
+ themeCategory
15429
+ };
15273
15430
  }
15274
15431
  function PairPreview({
15275
- family,
15432
+ familySummary,
15276
15433
  isRecommended,
15277
15434
  pair
15278
15435
  }) {
15279
15436
  const whiteForeground = isWhiteForegroundPair(pair);
15280
15437
  const statusLabel = isRecommended ? "Pass" : pair.passes.aaaLarge ? "Large text only" : "Example only";
15281
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
+ };
15282
15446
  return /* @__PURE__ */ jsx(Card, { className: "gap-0 overflow-hidden py-0", children: /* @__PURE__ */ jsx(
15283
15447
  "div",
15284
15448
  {
@@ -15300,7 +15464,7 @@ function PairPreview({
15300
15464
  },
15301
15465
  children: [
15302
15466
  /* @__PURE__ */ jsx(Icons.palette, { "data-slot": "icon", className: "size-4" }),
15303
- family.label
15467
+ familySummary
15304
15468
  ]
15305
15469
  }
15306
15470
  ),
@@ -15321,10 +15485,23 @@ function PairPreview({
15321
15485
  )
15322
15486
  ] }),
15323
15487
  /* @__PURE__ */ jsxs("div", { className: "max-w-xl space-y-4 pb-12 sm:pb-16", children: [
15324
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.22em] uppercase", children: whiteForeground && !isRecommended ? "White on colour example" : whiteForeground ? "White on colour" : "Tone on tone" }),
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" }),
15325
15489
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15326
15490
  /* @__PURE__ */ jsx("h3", { className: "max-w-lg text-4xl leading-none font-bold text-current sm:text-5xl", children: "Pair colour with confidence." }),
15327
- /* @__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 tone-on-tone combinations for headings, body copy, and calls to action on colour." })
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
+ )
15328
15505
  ] })
15329
15506
  ] }),
15330
15507
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
@@ -15361,24 +15538,94 @@ function PairPreview({
15361
15538
  }
15362
15539
  ) });
15363
15540
  }
15541
+ function PreviewFallbackCard({
15542
+ familySummary,
15543
+ selectedBackground
15544
+ }) {
15545
+ return /* @__PURE__ */ jsx(Card, { className: "gap-0 overflow-hidden py-0", children: /* @__PURE__ */ jsx(
15546
+ "div",
15547
+ {
15548
+ className: "p-6 sm:min-h-[26rem] sm:p-8",
15549
+ style: {
15550
+ backgroundColor: selectedBackground.hex,
15551
+ color: selectedBackground.tone >= 600 ? "#ffffff" : "#002664"
15552
+ },
15553
+ children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-[22rem] flex-col justify-between gap-8", children: [
15554
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-full border border-current bg-transparent px-3 py-1 text-[0.72rem] font-semibold tracking-[0.16em] uppercase", children: [
15555
+ /* @__PURE__ */ jsx(Icons.palette, { "data-slot": "icon", className: "size-4" }),
15556
+ familySummary
15557
+ ] }) }),
15558
+ /* @__PURE__ */ jsxs("div", { className: "max-w-xl space-y-4 pb-12 sm:pb-16", children: [
15559
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.22em] uppercase", children: "Approved background" }),
15560
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15561
+ /* @__PURE__ */ jsx("h3", { className: "max-w-lg text-4xl leading-none font-bold text-current sm:text-5xl", children: "Pair colour with confidence." }),
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." })
15563
+ ] })
15564
+ ] }),
15565
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
15566
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-full bg-current/10 px-4 py-2 text-sm font-semibold", children: [
15567
+ "No recommendation",
15568
+ /* @__PURE__ */ jsx(Icons.info, { "data-slot": "icon", className: "size-4" })
15569
+ ] }),
15570
+ /* @__PURE__ */ jsx("span", { className: "inline-flex rounded-full border border-current px-4 py-2 text-sm", children: selectedBackground.token })
15571
+ ] })
15572
+ ] })
15573
+ }
15574
+ ) });
15575
+ }
15364
15576
  function PairDetailCard({
15365
15577
  color: color2,
15366
- format,
15367
- role
15578
+ role,
15579
+ visibleFormats
15368
15580
  }) {
15369
15581
  const [, copyToClipboardRaw] = useCopyToClipboard();
15370
15582
  const [copiedField, setCopiedField] = useState(null);
15371
- const valueLabel = format.toUpperCase();
15372
- 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
+ }, []);
15373
15610
  const copyField = (field) => {
15374
- 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;
15375
15616
  copyToClipboardRaw(fieldValue);
15376
15617
  setCopiedField(field);
15377
- const toastLabel = field === "token" ? "Token" : field === "tone" ? "Tone" : `${valueLabel} value`;
15618
+ const toastLabel = valueRows.find((row) => row.key === field)?.label ?? "Value";
15378
15619
  toast(`${toastLabel} copied to clipboard`, {
15379
15620
  duration: 2e3
15380
15621
  });
15381
- 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);
15382
15629
  };
15383
15630
  const renderCopyButton = (field, srLabel, className) => /* @__PURE__ */ jsxs(
15384
15631
  Button2,
@@ -15393,43 +15640,39 @@ function PairDetailCard({
15393
15640
  ]
15394
15641
  }
15395
15642
  );
15396
- return /* @__PURE__ */ jsxs(Card, { className: "gap-4", children: [
15397
- /* @__PURE__ */ jsx(CardHeader, { className: "gap-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
15643
+ return /* @__PURE__ */ jsxs(Card, { className: "gap-0", children: [
15644
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 border-b", children: [
15645
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15646
+ /* @__PURE__ */ jsx(CardTitle, { children: role }),
15647
+ /* @__PURE__ */ jsx(CardDescription, { children: color2.name ?? color2.token })
15648
+ ] }),
15398
15649
  /* @__PURE__ */ jsx(
15399
15650
  "div",
15400
15651
  {
15401
- className: "size-14 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
15652
+ className: "h-14 w-full rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
15402
15653
  style: { backgroundColor: color2.hex }
15403
15654
  }
15404
- ),
15405
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15406
- /* @__PURE__ */ jsx(CardTitle, { children: role }),
15407
- /* @__PURE__ */ jsx(CardDescription, { children: color2.name ?? color2.token })
15408
- ] })
15409
- ] }) }),
15410
- /* @__PURE__ */ jsxs(CardContent, { className: "grid gap-4", children: [
15411
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15412
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
15413
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: "Token" }),
15414
- renderCopyButton("token", `Copy ${role.toLowerCase()} token`)
15415
- ] }),
15416
- /* @__PURE__ */ jsx("p", { className: "font-mono text-base break-all", children: color2.token })
15417
- ] }),
15418
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15419
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
15420
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: "Tone" }),
15421
- renderCopyButton("tone", `Copy ${role.toLowerCase()} tone`)
15422
- ] }),
15423
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: color2.tone })
15424
- ] }),
15425
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15426
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
15427
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: valueLabel }),
15428
- renderCopyButton("value", `Copy ${role.toLowerCase()} ${valueLabel} value`)
15429
- ] }),
15430
- /* @__PURE__ */ jsx("p", { className: "font-mono text-sm break-all", children: formattedValue })
15655
+ )
15656
+ ] }),
15657
+ /* @__PURE__ */ jsx(CardContent, { className: "px-0", children: /* @__PURE__ */ jsx("dl", { className: "divide-y divide-grey-100 dark:divide-grey-800", children: valueRows.map((row) => /* @__PURE__ */ jsxs("div", { className: "space-y-2 px-6 py-3", children: [
15658
+ /* @__PURE__ */ jsx("dt", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: row.label }),
15659
+ /* @__PURE__ */ jsxs("dd", { className: "flex items-start justify-between gap-4", children: [
15660
+ /* @__PURE__ */ jsx(
15661
+ "span",
15662
+ {
15663
+ className: cn(
15664
+ "min-w-0 flex-1 text-base text-foreground",
15665
+ row.mono && "font-mono text-sm break-all sm:text-base"
15666
+ ),
15667
+ children: row.value
15668
+ }
15669
+ ),
15670
+ /* @__PURE__ */ jsx("span", { className: "flex shrink-0 justify-end", children: row.copyable ? renderCopyButton(
15671
+ row.key,
15672
+ `Copy ${role.toLowerCase()} ${row.label.toLowerCase()}`
15673
+ ) : null })
15431
15674
  ] })
15432
- ] })
15675
+ ] }, row.key)) }) })
15433
15676
  ] });
15434
15677
  }
15435
15678
  function ComplianceRow({
@@ -15437,31 +15680,40 @@ function ComplianceRow({
15437
15680
  passes,
15438
15681
  threshold
15439
15682
  }) {
15440
- 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: [
15441
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15442
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: label }),
15443
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: threshold })
15444
- ] }),
15445
- /* @__PURE__ */ jsxs(
15446
- "span",
15447
- {
15448
- className: cn(
15449
- "inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-semibold",
15450
- 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"
15451
- ),
15452
- children: [
15453
- passes ? /* @__PURE__ */ jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsx(Icons.close, { "data-slot": "icon", className: "size-4" }),
15454
- passes ? "Pass" : "Fail"
15455
- ]
15456
- }
15457
- )
15458
- ] });
15683
+ return /* @__PURE__ */ jsxs(
15684
+ "div",
15685
+ {
15686
+ className: cn(
15687
+ "flex items-center justify-between gap-4 rounded-sm border px-4 py-3",
15688
+ 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"
15689
+ ),
15690
+ children: [
15691
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15692
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: label }),
15693
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: threshold })
15694
+ ] }),
15695
+ /* @__PURE__ */ jsxs(
15696
+ "span",
15697
+ {
15698
+ className: cn(
15699
+ "inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-semibold",
15700
+ 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"
15701
+ ),
15702
+ children: [
15703
+ passes ? /* @__PURE__ */ jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsx(Icons.close, { "data-slot": "icon", className: "size-4" }),
15704
+ passes ? "Pass" : "Fail"
15705
+ ]
15706
+ }
15707
+ )
15708
+ ]
15709
+ }
15710
+ );
15459
15711
  }
15460
15712
  function PairComplianceCard({ pair }) {
15461
15713
  return /* @__PURE__ */ jsxs(Card, { children: [
15462
15714
  /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15463
15715
  /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "AAA compliance" }),
15464
- /* @__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." })
15716
+ /* @__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." })
15465
15717
  ] }),
15466
15718
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-3", children: [
15467
15719
  /* @__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: [
@@ -15476,7 +15728,7 @@ function PairComplianceCard({ pair }) {
15476
15728
  {
15477
15729
  label: "AAA normal text",
15478
15730
  passes: pair.passes.aaaText,
15479
- threshold: "7.0:1 or higher"
15731
+ threshold: AAA_NORMAL_TEXT_THRESHOLD
15480
15732
  }
15481
15733
  ),
15482
15734
  /* @__PURE__ */ jsx(
@@ -15484,105 +15736,115 @@ function PairComplianceCard({ pair }) {
15484
15736
  {
15485
15737
  label: "AAA large text",
15486
15738
  passes: pair.passes.aaaLarge,
15487
- threshold: "4.5:1 or higher"
15739
+ threshold: AAA_LARGE_TEXT_THRESHOLD
15488
15740
  }
15489
15741
  )
15490
15742
  ] })
15491
15743
  ] });
15492
15744
  }
15493
- function WhiteTextExampleCard({ pair }) {
15494
- return /* @__PURE__ */ jsxs(Card, { children: [
15495
- /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15496
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "White text example" }),
15497
- /* @__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." })
15498
- ] }),
15499
- /* @__PURE__ */ jsxs(CardContent, { className: "grid gap-6 xl:grid-cols-[minmax(0,1.1fr)_minmax(18rem,0.9fr)]", children: [
15500
- /* @__PURE__ */ jsx(
15501
- "div",
15745
+ function ColorFamilySelector({
15746
+ families,
15747
+ label,
15748
+ selectedKey,
15749
+ selectionRole,
15750
+ themeCategory,
15751
+ onSelect
15752
+ }) {
15753
+ const swatchTone = selectionRole === "primary colour" ? 800 : 600;
15754
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-0", children: [
15755
+ /* @__PURE__ */ jsx(Heading, { level: 3, size: 6, className: "mb-4 text-foreground", trim: "normal", children: label }),
15756
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 lg:grid-cols-4 xl:grid-cols-5", children: families.map((family) => {
15757
+ const isSelected = family.key === selectedKey;
15758
+ const familySelectorLabel = getFamilySelectorLabel(family, themeCategory, selectionRole);
15759
+ return /* @__PURE__ */ jsxs(
15760
+ "button",
15502
15761
  {
15503
- className: "rounded-sm border border-grey-200 p-6 dark:border-grey-700",
15504
- style: {
15505
- backgroundColor: pair.background.hex,
15506
- color: pair.foreground.hex
15507
- },
15508
- children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15509
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
15510
- /* @__PURE__ */ jsx("p", { className: "text-lg leading-tight font-semibold", children: "Large text example" }),
15511
- /* @__PURE__ */ jsxs("p", { className: "text-sm/6", children: [
15512
- "White text on ",
15513
- pair.background.token
15514
- ] })
15515
- ] }),
15516
- /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-sm/6", children: [
15517
- /* @__PURE__ */ jsxs("p", { children: [
15518
- "AAA normal text: ",
15519
- /* @__PURE__ */ jsx("strong", { children: pair.passes.aaaText ? "pass" : "fail" })
15520
- ] }),
15521
- /* @__PURE__ */ jsxs("p", { children: [
15522
- "AAA large text: ",
15523
- /* @__PURE__ */ jsx("strong", { children: pair.passes.aaaLarge ? "pass" : "fail" })
15524
- ] })
15525
- ] })
15526
- ] })
15527
- }
15528
- ),
15529
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
15530
- /* @__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: [
15531
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold tracking-[0.16em] text-muted-foreground uppercase", children: "Contrast ratio" }),
15532
- /* @__PURE__ */ jsxs("p", { className: "mt-1 text-2xl font-semibold", children: [
15533
- pair.contrastRatio.toFixed(2),
15534
- ":1"
15535
- ] })
15536
- ] }),
15537
- /* @__PURE__ */ jsx(
15538
- ComplianceRow,
15539
- {
15540
- label: "AAA normal text",
15541
- passes: pair.passes.aaaText,
15542
- threshold: "7.0:1 or higher"
15543
- }
15544
- ),
15545
- /* @__PURE__ */ jsx(
15546
- ComplianceRow,
15547
- {
15548
- label: "AAA large text",
15549
- passes: pair.passes.aaaLarge,
15550
- threshold: "4.5:1 or higher"
15551
- }
15552
- )
15553
- ] })
15554
- ] })
15762
+ type: "button",
15763
+ "aria-label": `Select ${familySelectorLabel} as ${selectionRole}`,
15764
+ "aria-pressed": isSelected,
15765
+ onClick: () => onSelect(family.key),
15766
+ className: cn(
15767
+ "group relative flex w-full items-center gap-3 rounded-sm border px-3 py-2 text-left transition-colors",
15768
+ "border-grey-200 bg-background hover:bg-primary-800/10 hover:text-foreground dark:hover:bg-white/10",
15769
+ isSelected && "border-grey-800 dark:border-grey-400"
15770
+ ),
15771
+ children: [
15772
+ /* @__PURE__ */ jsx(
15773
+ "span",
15774
+ {
15775
+ className: "size-4 shrink-0 rounded-full",
15776
+ style: { backgroundColor: getFamilySwatchColor(family, swatchTone) }
15777
+ }
15778
+ ),
15779
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: familySelectorLabel })
15780
+ ]
15781
+ },
15782
+ family.key
15783
+ );
15784
+ }) })
15555
15785
  ] });
15556
15786
  }
15557
- function ColorPairingToolContent() {
15787
+ function ColorPairingToolContent({ visibleFormats }) {
15558
15788
  const searchParams = useSearchParams();
15559
- const {
15560
- familyKey: initialFamilyKey,
15561
- selectedBackgroundToken: initialSelectedBackgroundToken,
15562
- selectedPairId: initialSelectedPairId,
15563
- themeCategory: initialThemeCategory
15564
- } = getInitialPairingState(searchParams);
15565
- const [themeCategory, setThemeCategory] = useState(initialThemeCategory);
15566
- const [format, setFormat] = useState("hex");
15567
- const [activeFamilyKey, setActiveFamilyKey] = useState(initialFamilyKey);
15789
+ const [initialState] = useState(() => getInitialPairingState(searchParams));
15790
+ const [themeCategory, setThemeCategory] = useState(initialState.themeCategory);
15791
+ const [primaryFamilyKey, setPrimaryFamilyKey] = useState(initialState.primaryKey);
15792
+ const [accentFamilyKey, setAccentFamilyKey] = useState(initialState.accentKey);
15793
+ const [, copyShareLinkRaw] = useCopyToClipboard();
15794
+ const [copiedShareLink, setCopiedShareLink] = useState(false);
15795
+ const copiedShareLinkTimeoutRef = useRef(null);
15568
15796
  const [selectedBackgroundToken, setSelectedBackgroundToken] = useState(
15569
- initialSelectedBackgroundToken
15570
- );
15571
- const [selectedPairId, setSelectedPairId] = useState(initialSelectedPairId);
15572
- const families = recommendedAaaPairingCollections[themeCategory];
15573
- const resolvedActiveFamilyKey = families.some((family) => family.key === activeFamilyKey) ? activeFamilyKey : getPreferredFamilyKey(families);
15574
- const activeFamily = families.find((family) => family.key === resolvedActiveFamilyKey) ?? families[0];
15575
- const backgroundOptions = getBackgroundOptions(activeFamily);
15576
- const selectedBackground = backgroundOptions.find((color2) => color2.token === selectedBackgroundToken) ?? backgroundOptions[0] ?? activeFamily?.colors[0] ?? null;
15577
- const selectedPair = selectedBackground ? getRecommendedPairForBackground(activeFamily, selectedBackground.token, selectedPairId) : null;
15578
- const whiteForegroundExample = selectedBackground && supportsWhiteForegroundPreview(selectedBackground) ? getWhiteForegroundPair(selectedBackground) : null;
15579
- const previewPair = selectedPair ?? whiteForegroundExample;
15580
- const showWhiteForegroundExample = Boolean(whiteForegroundExample) && (!selectedPair || selectedPair.foreground.token !== "white");
15797
+ initialState.selectedBackgroundToken
15798
+ );
15799
+ const [selectedPairId, setSelectedPairId] = useState(initialState.selectedPairId);
15800
+ const themeFamilies = useMemo(() => getPairingFamilies(themeCategory), [themeCategory]);
15801
+ const context = useMemo(
15802
+ () => getPairingContext(themeCategory, primaryFamilyKey, accentFamilyKey),
15803
+ [themeCategory, primaryFamilyKey, accentFamilyKey]
15804
+ );
15805
+ const selectableFamilies = useMemo(
15806
+ () => themeFamilies.filter((family) => family.key !== context.grey.key),
15807
+ [themeFamilies, context.grey.key]
15808
+ );
15809
+ const selectableAccentFamilies = useMemo(
15810
+ () => selectableFamilies.filter((family) => family.key !== context.primary.key),
15811
+ [selectableFamilies, context.primary.key]
15812
+ );
15813
+ const selectedBackground = useMemo(
15814
+ () => context.backgrounds.find((background) => background.token === selectedBackgroundToken) ?? context.backgrounds[0] ?? null,
15815
+ [context.backgrounds, selectedBackgroundToken]
15816
+ );
15817
+ const selectedBackgroundPairs = useMemo(
15818
+ () => selectedBackground ? context.pairsByBackground[selectedBackground.token] ?? [] : [],
15819
+ [context.pairsByBackground, selectedBackground]
15820
+ );
15821
+ const selectedPair = useMemo(
15822
+ () => getPreferredPairForBackground(selectedBackgroundPairs, selectedPairId),
15823
+ [selectedBackgroundPairs, selectedPairId]
15824
+ );
15825
+ const whiteForegroundExample = useMemo(
15826
+ () => selectedBackground && supportsWhiteForegroundPreview(selectedBackground) ? getWhiteForegroundPair(selectedBackground) : null,
15827
+ [selectedBackground]
15828
+ );
15829
+ const previewPair = selectedPair ?? whiteForegroundExample ?? null;
15581
15830
  const detailForeground = selectedPair?.foreground ?? whiteForegroundExample?.foreground ?? null;
15582
- const updateUrlParams = (nextThemeCategory, nextFamilyKey, nextSelectedBackgroundToken, nextSelectedPairId) => {
15831
+ const familySummary = useMemo(
15832
+ () => [context.primary.label, context.accent.label, context.grey.label].join(" + "),
15833
+ [context.primary.label, context.accent.label, context.grey.label]
15834
+ );
15835
+ useEffect(() => {
15836
+ return () => {
15837
+ if (copiedShareLinkTimeoutRef.current) {
15838
+ clearTimeout(copiedShareLinkTimeoutRef.current);
15839
+ }
15840
+ };
15841
+ }, []);
15842
+ const updateUrlParams = (nextThemeCategory, nextPrimaryKey, nextAccentKey, nextSelectedBackgroundToken, nextSelectedPairId) => {
15583
15843
  const params = new URLSearchParams(window.location.search);
15844
+ params.delete("family");
15584
15845
  params.set("palette", nextThemeCategory);
15585
- params.set("family", nextFamilyKey);
15846
+ params.set("primary", nextPrimaryKey);
15847
+ params.set("accent", nextAccentKey);
15586
15848
  params.set("background", nextSelectedBackgroundToken);
15587
15849
  if (nextSelectedPairId) {
15588
15850
  params.set("pair", nextSelectedPairId);
@@ -15595,294 +15857,450 @@ function ColorPairingToolContent() {
15595
15857
  `${window.location.pathname}?${params.toString()}${window.location.hash}`
15596
15858
  );
15597
15859
  };
15598
- const handleThemeCategoryChange = (nextThemeCategory) => {
15599
- const nextFamilies = recommendedAaaPairingCollections[nextThemeCategory];
15600
- const nextFamilyKey = nextFamilies.some((family) => family.key === activeFamilyKey) ? activeFamilyKey : getPreferredFamilyKey(nextFamilies);
15601
- const nextActiveFamily = nextFamilies.find((family) => family.key === nextFamilyKey) ?? nextFamilies[0];
15602
- const nextSelectedBackgroundToken = getFallbackBackgroundToken(
15603
- nextActiveFamily,
15604
- getToneFromToken(selectedBackgroundToken)
15605
- );
15606
- const nextSelectedPair = getRecommendedPairForBackground(
15607
- nextActiveFamily,
15608
- nextSelectedBackgroundToken,
15609
- selectedPairId
15860
+ const syncSelection = (nextThemeCategory, nextPrimaryKey, nextAccentKey, preferredBackgroundToken, preferredPairId) => {
15861
+ const nextContext = getPairingContext(nextThemeCategory, nextPrimaryKey, nextAccentKey);
15862
+ const nextSelectedBackgroundToken = resolveBackgroundToken(
15863
+ nextContext,
15864
+ preferredBackgroundToken,
15865
+ getToneFromToken(preferredBackgroundToken)
15610
15866
  );
15867
+ const nextSelectedPairId = getPreferredPairForBackground(
15868
+ nextContext.pairsByBackground[nextSelectedBackgroundToken] ?? [],
15869
+ preferredPairId
15870
+ )?.id ?? "";
15611
15871
  setThemeCategory(nextThemeCategory);
15612
- setActiveFamilyKey(nextFamilyKey);
15872
+ setPrimaryFamilyKey(nextContext.primary.key);
15873
+ setAccentFamilyKey(nextContext.accent.key);
15613
15874
  setSelectedBackgroundToken(nextSelectedBackgroundToken);
15614
- setSelectedPairId(nextSelectedPair?.id ?? "");
15875
+ setSelectedPairId(nextSelectedPairId);
15615
15876
  updateUrlParams(
15616
15877
  nextThemeCategory,
15617
- nextFamilyKey,
15878
+ nextContext.primary.key,
15879
+ nextContext.accent.key,
15618
15880
  nextSelectedBackgroundToken,
15619
- nextSelectedPair?.id ?? ""
15881
+ nextSelectedPairId
15620
15882
  );
15621
15883
  };
15622
- const handleFamilyChange = (nextFamilyKey) => {
15623
- const nextActiveFamily = families.find((family) => family.key === nextFamilyKey);
15624
- const nextSelectedBackgroundToken = getFallbackBackgroundToken(
15625
- nextActiveFamily,
15626
- getToneFromToken(selectedBackgroundToken)
15884
+ const handleThemeCategoryChange = (nextThemeCategory) => {
15885
+ syncSelection(
15886
+ nextThemeCategory,
15887
+ primaryFamilyKey,
15888
+ accentFamilyKey,
15889
+ selectedBackgroundToken,
15890
+ selectedPairId
15627
15891
  );
15628
- const nextSelectedPair = getRecommendedPairForBackground(
15629
- nextActiveFamily,
15630
- nextSelectedBackgroundToken,
15892
+ };
15893
+ const handlePrimaryColorChange = (nextPrimaryKey) => {
15894
+ const nextAccentKey = nextPrimaryKey === accentFamilyKey ? getDefaultAccentFamilyKey(themeCategory, nextPrimaryKey) : accentFamilyKey;
15895
+ syncSelection(
15896
+ themeCategory,
15897
+ nextPrimaryKey,
15898
+ nextAccentKey,
15899
+ selectedBackgroundToken,
15631
15900
  selectedPairId
15632
15901
  );
15633
- setActiveFamilyKey(nextFamilyKey);
15634
- setSelectedBackgroundToken(nextSelectedBackgroundToken);
15635
- setSelectedPairId(nextSelectedPair?.id ?? "");
15636
- updateUrlParams(
15902
+ };
15903
+ const handleAccentColorChange = (nextAccentKey) => {
15904
+ if (nextAccentKey === primaryFamilyKey) return;
15905
+ syncSelection(
15637
15906
  themeCategory,
15638
- nextFamilyKey,
15639
- nextSelectedBackgroundToken,
15640
- nextSelectedPair?.id ?? ""
15907
+ primaryFamilyKey,
15908
+ nextAccentKey,
15909
+ selectedBackgroundToken,
15910
+ selectedPairId
15641
15911
  );
15642
15912
  };
15643
15913
  const handleBackgroundChange = (nextSelectedBackgroundToken) => {
15644
- const nextSelectedPair = getRecommendedPairForBackground(
15645
- activeFamily,
15646
- nextSelectedBackgroundToken,
15914
+ const nextSelectedPairId = getPreferredPairForBackground(
15915
+ context.pairsByBackground[nextSelectedBackgroundToken] ?? [],
15647
15916
  selectedPairId
15648
- );
15917
+ )?.id ?? "";
15649
15918
  setSelectedBackgroundToken(nextSelectedBackgroundToken);
15650
- setSelectedPairId(nextSelectedPair?.id ?? "");
15919
+ setSelectedPairId(nextSelectedPairId);
15651
15920
  updateUrlParams(
15652
15921
  themeCategory,
15653
- resolvedActiveFamilyKey,
15922
+ context.primary.key,
15923
+ context.accent.key,
15654
15924
  nextSelectedBackgroundToken,
15655
- nextSelectedPair?.id ?? ""
15925
+ nextSelectedPairId
15656
15926
  );
15657
15927
  };
15658
15928
  const handlePairChange = (nextSelectedPairId) => {
15659
- const nextSelectedPair = activeFamily?.recommendedPairs.find((pair) => pair.id === nextSelectedPairId) ?? null;
15660
- if (!nextSelectedPair) return;
15661
- setSelectedBackgroundToken(nextSelectedPair.background.token);
15929
+ const nextSelectedPair = selectedBackgroundPairs.find((pair) => pair.id === nextSelectedPairId);
15930
+ if (!nextSelectedPair || !selectedBackground) return;
15662
15931
  setSelectedPairId(nextSelectedPairId);
15663
15932
  updateUrlParams(
15664
15933
  themeCategory,
15665
- resolvedActiveFamilyKey,
15666
- nextSelectedPair.background.token,
15934
+ context.primary.key,
15935
+ context.accent.key,
15936
+ selectedBackground.token,
15667
15937
  nextSelectedPairId
15668
15938
  );
15669
15939
  };
15670
- if (!activeFamily || !selectedBackground) {
15940
+ if (!selectedBackground) {
15671
15941
  return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardHeader, { children: [
15672
15942
  /* @__PURE__ */ jsx(CardTitle, { children: "No approved background tones available" }),
15673
15943
  /* @__PURE__ */ jsx(CardDescription, { children: "No approved tones are available for the current palette." })
15674
15944
  ] }) });
15675
15945
  }
15676
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
15677
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
15678
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
15679
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Palette:" }),
15946
+ const handleCopyShareLink = () => {
15947
+ copyShareLinkRaw(window.location.href);
15948
+ setCopiedShareLink(true);
15949
+ toast("Colour pairing link copied to clipboard", {
15950
+ duration: 2e3
15951
+ });
15952
+ if (copiedShareLinkTimeoutRef.current) {
15953
+ clearTimeout(copiedShareLinkTimeoutRef.current);
15954
+ }
15955
+ copiedShareLinkTimeoutRef.current = setTimeout(() => {
15956
+ setCopiedShareLink(false);
15957
+ copiedShareLinkTimeoutRef.current = null;
15958
+ }, 2e3);
15959
+ };
15960
+ const activePaletteEntries = [
15961
+ {
15962
+ key: `primary-${context.primary.key}`,
15963
+ label: "Primary",
15964
+ family: context.primary,
15965
+ familyLabel: getActivePaletteFamilyLabel(context.primary, themeCategory, "primary"),
15966
+ swatchTone: 800
15967
+ },
15968
+ {
15969
+ key: `accent-${context.accent.key}`,
15970
+ label: "Accent",
15971
+ family: context.accent,
15972
+ familyLabel: getActivePaletteFamilyLabel(context.accent, themeCategory, "accent"),
15973
+ swatchTone: 600
15974
+ },
15975
+ {
15976
+ key: `grey-${context.grey.key}`,
15977
+ label: "Grey",
15978
+ family: context.grey,
15979
+ familyLabel: getActivePaletteFamilyLabel(context.grey, themeCategory, "grey"),
15980
+ swatchTone: 800
15981
+ }
15982
+ ];
15983
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
15984
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15985
+ /* @__PURE__ */ jsx(
15986
+ WorkflowStep,
15987
+ {
15988
+ step: 1,
15989
+ title: "Select your palette",
15990
+ description: "Choose the palette family you want to work from.",
15991
+ level: 2
15992
+ }
15993
+ ),
15994
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
15995
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
15996
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Palette:" }),
15997
+ /* @__PURE__ */ jsx(
15998
+ SegmentedControl,
15999
+ {
16000
+ value: themeCategory,
16001
+ onValueChange: (value) => handleThemeCategoryChange(value),
16002
+ children: /* @__PURE__ */ jsxs(SegmentedControlList, { className: "w-full sm:w-fit", children: [
16003
+ /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "brand", children: "Brand palette" }),
16004
+ /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "aboriginal", children: "Aboriginal palette" })
16005
+ ] })
16006
+ }
16007
+ )
16008
+ ] }),
16009
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-3", children: /* @__PURE__ */ jsxs(Button2, { variant: "outline", size: "sm", onClick: handleCopyShareLink, children: [
16010
+ copiedShareLink ? /* @__PURE__ */ jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsx(Icons.content_copy, { "data-slot": "icon", className: "size-4" }),
16011
+ copiedShareLink ? "Link copied" : "Copy link"
16012
+ ] }) })
16013
+ ] })
16014
+ ] }),
16015
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
16016
+ /* @__PURE__ */ jsx(
16017
+ WorkflowStep,
16018
+ {
16019
+ step: 2,
16020
+ title: "Choose your colours",
16021
+ description: "Pick a primary and accent colour. Grey is added automatically.",
16022
+ level: 2
16023
+ }
16024
+ ),
16025
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
15680
16026
  /* @__PURE__ */ jsx(
15681
- SegmentedControl,
16027
+ ColorFamilySelector,
15682
16028
  {
15683
- value: themeCategory,
15684
- onValueChange: (value) => handleThemeCategoryChange(value),
15685
- children: /* @__PURE__ */ jsxs(SegmentedControlList, { className: "w-full sm:w-fit", children: [
15686
- /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "brand", children: "Brand palette" }),
15687
- /* @__PURE__ */ jsx(SegmentedControlTrigger, { value: "aboriginal", children: "Aboriginal palette" })
15688
- ] })
16029
+ label: "Primary colour",
16030
+ families: selectableFamilies,
16031
+ selectedKey: context.primary.key,
16032
+ selectionRole: "primary colour",
16033
+ themeCategory,
16034
+ onSelect: handlePrimaryColorChange
16035
+ }
16036
+ ),
16037
+ /* @__PURE__ */ jsx(
16038
+ ColorFamilySelector,
16039
+ {
16040
+ label: "Accent colour",
16041
+ families: selectableAccentFamilies,
16042
+ selectedKey: context.accent.key,
16043
+ selectionRole: "accent colour",
16044
+ themeCategory,
16045
+ onSelect: handleAccentColorChange
15689
16046
  }
15690
16047
  )
15691
- ] }),
15692
- /* @__PURE__ */ jsx(FormatToggle, { format, setFormat })
16048
+ ] })
15693
16049
  ] }),
15694
- /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 lg:grid-cols-4 xl:grid-cols-5", children: families.map((family) => {
15695
- const isActive = family.key === resolvedActiveFamilyKey;
15696
- return /* @__PURE__ */ jsxs(
15697
- "button",
16050
+ /* @__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: [
16051
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.14em] text-foreground uppercase", children: "Active palette" }),
16052
+ /* @__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(
16053
+ "span",
15698
16054
  {
15699
- type: "button",
15700
- onClick: () => handleFamilyChange(family.key),
15701
- className: cn(
15702
- "group relative flex w-full items-center gap-3 rounded-sm border px-3 py-2 text-left transition-colors",
15703
- "border-grey-200 bg-background hover:bg-primary-800/10 hover:text-foreground dark:hover:bg-white/10",
15704
- isActive && "border-grey-800 dark:border-grey-400"
15705
- ),
16055
+ className: "inline-flex items-center gap-2 rounded-sm border border-grey-200 bg-background px-3 py-2 dark:border-grey-700",
15706
16056
  children: [
15707
16057
  /* @__PURE__ */ jsx(
15708
16058
  "span",
15709
16059
  {
15710
- className: "size-4 shrink-0 rounded-full",
15711
- style: { backgroundColor: getFamilySwatchColor(family) }
16060
+ className: "size-2.5 rounded-full",
16061
+ style: { backgroundColor: getFamilySwatchColor(family, swatchTone) }
15712
16062
  }
15713
16063
  ),
15714
- /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: family.label })
16064
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
16065
+ label,
16066
+ ":"
16067
+ ] }),
16068
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: familyLabel })
15715
16069
  ]
15716
16070
  },
15717
- family.key
15718
- );
15719
- }) }) }),
15720
- /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
15721
- /* @__PURE__ */ jsxs("div", { className: "grid items-start gap-6 xl:grid-cols-[minmax(0,1.5fr)_minmax(17rem,0.68fr)]", children: [
15722
- /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
15723
- previewPair ? /* @__PURE__ */ jsx(
15724
- PairPreview,
15725
- {
15726
- family: activeFamily,
15727
- isRecommended: Boolean(selectedPair),
15728
- pair: previewPair
15729
- }
15730
- ) : /* @__PURE__ */ jsx(Card, { className: "gap-0 overflow-hidden py-0", children: /* @__PURE__ */ jsx(
15731
- "div",
15732
- {
15733
- className: "p-6 sm:min-h-[26rem] sm:p-8",
15734
- style: {
15735
- backgroundColor: selectedBackground.hex,
15736
- color: selectedBackground.tone >= 600 ? "#ffffff" : "#002664"
15737
- },
15738
- children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-[22rem] flex-col justify-between gap-8", children: [
15739
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-full border border-current bg-transparent px-3 py-1 text-[0.72rem] font-semibold tracking-[0.16em] uppercase", children: [
15740
- /* @__PURE__ */ jsx(Icons.palette, { "data-slot": "icon", className: "size-4" }),
15741
- activeFamily.label
15742
- ] }) }),
15743
- /* @__PURE__ */ jsxs("div", { className: "max-w-xl space-y-4 pb-12 sm:pb-16", children: [
15744
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold tracking-[0.22em] uppercase", children: "Approved background" }),
15745
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
15746
- /* @__PURE__ */ jsx("h3", { className: "max-w-lg text-4xl leading-none font-bold text-current sm:text-5xl", children: "Pair colour with confidence." }),
15747
- /* @__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." })
15748
- ] })
15749
- ] }),
15750
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
15751
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 rounded-full bg-current/10 px-4 py-2 text-sm font-semibold", children: [
15752
- "No recommendation",
15753
- /* @__PURE__ */ jsx(Icons.info, { "data-slot": "icon", className: "size-4" })
15754
- ] }),
15755
- /* @__PURE__ */ jsx("span", { className: "inline-flex rounded-full border border-current px-4 py-2 text-sm", children: selectedBackground.token })
16071
+ key
16072
+ )) })
16073
+ ] }) }),
16074
+ /* @__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: [
16075
+ /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
16076
+ /* @__PURE__ */ jsxs(Card, { children: [
16077
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 border-b", children: [
16078
+ /* @__PURE__ */ jsx(
16079
+ WorkflowStep,
16080
+ {
16081
+ step: 3,
16082
+ title: "Pick a background",
16083
+ description: "Select an approved background tone from your primary, accent, or grey families.",
16084
+ level: 3,
16085
+ variant: "card"
16086
+ }
16087
+ ),
16088
+ /* @__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." })
16089
+ ] }),
16090
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-5", children: [
16091
+ /* @__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: [
16092
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-sm border border-grey-200 bg-background px-3 py-3 dark:border-grey-700", children: [
16093
+ /* @__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" }) }),
16094
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
16095
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Recommended foreground available" }),
16096
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "The centre dot marks the recommended foreground colour." })
16097
+ ] })
16098
+ ] }),
16099
+ /* @__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: [
16100
+ /* @__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)]" }) }),
16101
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
16102
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "No recommended AAA foreground" }),
16103
+ /* @__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." })
15756
16104
  ] })
15757
16105
  ] })
15758
- }
15759
- ) }),
15760
- /* @__PURE__ */ jsxs(Card, { children: [
15761
- /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15762
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Background tone strip" }),
15763
- /* @__PURE__ */ jsx(CardDescription, { children: "Only approved background tones are shown. Filled chips have a recommended AAA foreground pair, and dark backgrounds may also offer white text where it passes AAA." })
15764
16106
  ] }),
15765
- /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
15766
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-5 gap-2 sm:grid-cols-7 lg:grid-cols-10 xl:grid-cols-[repeat(19,minmax(0,1fr))]", children: backgroundOptions.map((color2) => {
15767
- const pair = getRecommendedPairForBackground(activeFamily, color2.token);
15768
- const hasWhiteExample = supportsWhiteForegroundPreview(color2);
15769
- const isSelected = selectedBackground.token === color2.token;
15770
- return /* @__PURE__ */ jsxs(
15771
- "button",
15772
- {
15773
- type: "button",
15774
- onClick: () => handleBackgroundChange(color2.token),
15775
- className: cn(
15776
- "group relative h-12 rounded-sm border border-grey-200 transition-transform hover:scale-[1.03] dark:border-grey-700",
15777
- isSelected && "ring-2 ring-primary-500 ring-offset-2 ring-offset-background"
15778
- ),
15779
- style: { backgroundColor: color2.hex },
15780
- children: [
15781
- pair ? /* @__PURE__ */ jsx(
15782
- "span",
15783
- {
15784
- 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",
15785
- style: { backgroundColor: pair.foreground.hex }
15786
- }
15787
- ) : null,
15788
- /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
15789
- color2.token,
15790
- pair ? ` paired with ${pair.foreground.token}` : hasWhiteExample ? " has a white text example in this tool" : " has no recommended pair in this tool"
15791
- ] })
15792
- ]
15793
- },
15794
- color2.token
15795
- );
15796
- }) }),
15797
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4 text-sm text-muted-foreground", children: [
15798
- /* @__PURE__ */ jsxs("span", { children: [
15799
- "Selected background:",
15800
- " ",
15801
- /* @__PURE__ */ jsx("strong", { className: "text-foreground", children: selectedBackground.token })
16107
+ context.backgroundGroups.map((group) => {
16108
+ const groupFamilyLabel = selectedBackground.familyKey === group.family.key ? selectedBackground.name ?? selectedBackground.token : group.family.label;
16109
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
16110
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
16111
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: group.label }),
16112
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: groupFamilyLabel })
15802
16113
  ] }),
15803
- /* @__PURE__ */ jsxs("span", { children: [
15804
- selectedPair ? "Recommended foreground:" : "White text example:",
15805
- " ",
15806
- /* @__PURE__ */ jsx("strong", { className: "text-foreground", children: selectedPair?.foreground.token ?? whiteForegroundExample?.foreground.token ?? "None" })
15807
- ] })
16114
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 sm:grid-cols-6", children: group.backgrounds.map((background) => {
16115
+ const pairs = context.pairsByBackground[background.token] ?? [];
16116
+ const hasRecommendedForeground = pairs.length > 0;
16117
+ const preferredPair = selectedBackground.token === background.token ? selectedPair ?? getPreferredPairForBackground(pairs) : getPreferredPairForBackground(pairs);
16118
+ const hasWhiteExample = supportsWhiteForegroundPreview(background);
16119
+ const isSelected = selectedBackground.token === background.token;
16120
+ 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`;
16121
+ return /* @__PURE__ */ jsxs(
16122
+ "button",
16123
+ {
16124
+ type: "button",
16125
+ "aria-label": ariaLabel,
16126
+ "aria-pressed": isSelected,
16127
+ onClick: () => handleBackgroundChange(background.token),
16128
+ className: cn(
16129
+ "group relative h-12 rounded-sm border transition-all",
16130
+ hasRecommendedForeground && "border-grey-200 hover:scale-[1.03] hover:shadow-sm dark:border-grey-700",
16131
+ !hasRecommendedForeground && hasWhiteExample && "border-grey-300/80 hover:scale-[1.01] dark:border-grey-600/80",
16132
+ !hasRecommendedForeground && !hasWhiteExample && "border-dashed border-grey-300/70 hover:scale-[1.01] dark:border-grey-600/70",
16133
+ isSelected && "ring-2 ring-primary-500 ring-offset-2 ring-offset-background"
16134
+ ),
16135
+ style: { backgroundColor: background.hex },
16136
+ children: [
16137
+ preferredPair ? /* @__PURE__ */ jsx(
16138
+ "span",
16139
+ {
16140
+ 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",
16141
+ style: { backgroundColor: preferredPair.foreground.hex }
16142
+ }
16143
+ ) : null,
16144
+ !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
16145
+ ]
16146
+ },
16147
+ background.token
16148
+ );
16149
+ }) })
16150
+ ] }, group.key);
16151
+ }),
16152
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4 text-sm text-muted-foreground", children: [
16153
+ /* @__PURE__ */ jsxs("span", { children: [
16154
+ "Selected background:",
16155
+ " ",
16156
+ /* @__PURE__ */ jsx("strong", { className: "text-foreground", children: selectedBackground.token })
16157
+ ] }),
16158
+ /* @__PURE__ */ jsxs("span", { children: [
16159
+ selectedPair ? "Selected foreground:" : "White text example:",
16160
+ " ",
16161
+ /* @__PURE__ */ jsx("strong", { className: "text-foreground", children: selectedPair?.foreground.token ?? whiteForegroundExample?.foreground.token ?? "None" })
15808
16162
  ] })
15809
16163
  ] })
15810
- ] }),
15811
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
15812
- /* @__PURE__ */ jsx(PairDetailCard, { color: selectedBackground, format, role: "Background" }),
15813
- detailForeground ? /* @__PURE__ */ jsx(PairDetailCard, { color: detailForeground, format, role: "Foreground" }) : /* @__PURE__ */ jsxs(Card, { className: "gap-4", children: [
15814
- /* @__PURE__ */ jsx(CardHeader, { className: "gap-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
15815
- /* @__PURE__ */ jsx(CardTitle, { children: "Foreground" }),
15816
- /* @__PURE__ */ jsx(CardDescription, { children: "No recommended foreground available" })
15817
- ] }) }),
15818
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select another approved tone or review the recommended pairs list for the same family." }) })
15819
- ] })
15820
16164
  ] })
15821
16165
  ] }),
15822
- /* @__PURE__ */ jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsxs(Card, { className: "h-fit", children: [
15823
- /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 border-b", children: [
15824
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Recommended pairs" }),
15825
- /* @__PURE__ */ jsx(CardDescription, { children: "Recommended AAA combinations for this family, including white on eligible dark backgrounds." })
15826
- ] }),
15827
- /* @__PURE__ */ jsx(CardContent, { className: "grid gap-3", children: activeFamily.recommendedPairs.map((pair) => {
15828
- const isActive = selectedPair?.id === pair.id;
15829
- return /* @__PURE__ */ jsxs(
15830
- "button",
15831
- {
15832
- type: "button",
15833
- onClick: () => handlePairChange(pair.id),
15834
- className: cn(
15835
- "rounded-sm border p-4 text-left transition-colors",
15836
- isActive ? "border-primary-500 bg-primary-50/70 dark:bg-primary-950/30" : "border-grey-200 hover:border-grey-400 hover:bg-grey-50 dark:border-grey-700 dark:hover:border-grey-500 dark:hover:bg-grey-900/70"
15837
- ),
15838
- children: [
15839
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
15840
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
16166
+ previewPair ? /* @__PURE__ */ jsx(
16167
+ PairPreview,
16168
+ {
16169
+ familySummary,
16170
+ isRecommended: Boolean(selectedPair),
16171
+ pair: previewPair
16172
+ }
16173
+ ) : /* @__PURE__ */ jsx(
16174
+ PreviewFallbackCard,
16175
+ {
16176
+ familySummary,
16177
+ selectedBackground
16178
+ }
16179
+ ),
16180
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
16181
+ /* @__PURE__ */ jsx(
16182
+ PairDetailCard,
16183
+ {
16184
+ color: selectedBackground,
16185
+ role: "Background",
16186
+ visibleFormats
16187
+ }
16188
+ ),
16189
+ detailForeground ? /* @__PURE__ */ jsx(
16190
+ PairDetailCard,
16191
+ {
16192
+ color: detailForeground,
16193
+ role: "Foreground",
16194
+ visibleFormats
16195
+ }
16196
+ ) : /* @__PURE__ */ jsxs(Card, { className: "gap-4", children: [
16197
+ /* @__PURE__ */ jsx(CardHeader, { className: "gap-3 border-b", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
16198
+ /* @__PURE__ */ jsx(CardTitle, { children: "Foreground" }),
16199
+ /* @__PURE__ */ jsx(CardDescription, { children: "No recommended foreground available" })
16200
+ ] }) }),
16201
+ /* @__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." }) })
16202
+ ] })
16203
+ ] }),
16204
+ previewPair ? /* @__PURE__ */ jsx(PairComplianceCard, { pair: previewPair }) : null
16205
+ ] }),
16206
+ /* @__PURE__ */ jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsxs(Card, { className: "h-fit", children: [
16207
+ /* @__PURE__ */ jsxs(CardHeader, { className: "gap-4 border-b", children: [
16208
+ /* @__PURE__ */ jsx(
16209
+ WorkflowStep,
16210
+ {
16211
+ step: 4,
16212
+ title: "Choose a foreground",
16213
+ description: `Review the AAA combinations available for ${selectedBackground.token}.`,
16214
+ level: 3,
16215
+ variant: "card"
16216
+ }
16217
+ ),
16218
+ /* @__PURE__ */ jsxs(CardDescription, { children: [
16219
+ "Recommended foregrounds are drawn from ",
16220
+ context.primary.label,
16221
+ ",",
16222
+ " ",
16223
+ context.accent.label,
16224
+ ", Grey, and white where it meets AAA."
16225
+ ] })
16226
+ ] }),
16227
+ /* @__PURE__ */ jsx(CardContent, { className: "grid gap-3", children: selectedBackgroundPairs.length > 0 ? selectedBackgroundPairs.map((pair) => {
16228
+ const isActive = selectedPair?.id === pair.id;
16229
+ return /* @__PURE__ */ jsxs(
16230
+ "button",
16231
+ {
16232
+ type: "button",
16233
+ "aria-label": `Use ${getPairingColorDisplayName(pair.foreground)} on ${getPairingColorDisplayName(pair.background)}`,
16234
+ "aria-pressed": isActive,
16235
+ onClick: () => handlePairChange(pair.id),
16236
+ className: cn(
16237
+ "rounded-sm border p-4 text-left transition-colors",
16238
+ isActive ? "border-primary-500 bg-primary-50/70 dark:bg-primary-950/30" : "border-grey-200 hover:border-grey-400 hover:bg-grey-50 dark:border-grey-700 dark:hover:border-grey-500 dark:hover:bg-grey-900/70"
16239
+ ),
16240
+ children: [
16241
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
16242
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
16243
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-[0.65rem] font-semibold tracking-[0.14em] text-muted-foreground uppercase", children: [
16244
+ /* @__PURE__ */ jsx("span", { className: "w-11 text-center", children: "BG" }),
16245
+ /* @__PURE__ */ jsx("span", { className: "w-11 text-center", children: "FG" })
16246
+ ] }),
16247
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
15841
16248
  /* @__PURE__ */ jsx(
15842
16249
  "div",
15843
16250
  {
15844
16251
  className: "size-11 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
15845
- style: { backgroundColor: pair.background.hex }
16252
+ style: {
16253
+ backgroundColor: pair.background.hex
16254
+ }
15846
16255
  }
15847
16256
  ),
15848
16257
  /* @__PURE__ */ jsx(
15849
16258
  "div",
15850
16259
  {
15851
16260
  className: "size-11 rounded-sm border border-grey-200 shadow-sm dark:border-grey-700",
15852
- style: { backgroundColor: pair.foreground.hex }
16261
+ style: {
16262
+ backgroundColor: pair.foreground.hex
16263
+ }
15853
16264
  }
15854
16265
  )
15855
- ] }),
15856
- /* @__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: [
15857
- /* @__PURE__ */ jsx(Icons.contrast, { "data-slot": "icon", className: "size-4" }),
15858
- pair.rating
15859
16266
  ] })
15860
16267
  ] }),
15861
- /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-1", children: [
15862
- /* @__PURE__ */ jsxs("p", { className: "font-medium text-foreground", children: [
15863
- pair.background.token,
15864
- " / ",
15865
- pair.foreground.token
15866
- ] }),
15867
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
15868
- pair.contrastRatio.toFixed(2),
15869
- ":1 contrast ratio"
15870
- ] })
16268
+ /* @__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: [
16269
+ /* @__PURE__ */ jsx(Icons.contrast, { "data-slot": "icon", className: "size-4" }),
16270
+ pair.rating
15871
16271
  ] })
15872
- ]
15873
- },
15874
- pair.id
15875
- );
15876
- }) })
15877
- ] }) })
15878
- ] }),
15879
- selectedPair ? /* @__PURE__ */ jsx(PairComplianceCard, { pair: selectedPair }) : null,
15880
- showWhiteForegroundExample && whiteForegroundExample ? /* @__PURE__ */ jsx(WhiteTextExampleCard, { pair: whiteForegroundExample }) : null
15881
- ] })
16272
+ ] }),
16273
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-1", children: [
16274
+ /* @__PURE__ */ jsxs("p", { className: "font-medium text-foreground", children: [
16275
+ getPairingColorDisplayName(pair.background),
16276
+ " /",
16277
+ " ",
16278
+ getPairingColorDisplayName(pair.foreground)
16279
+ ] }),
16280
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
16281
+ pair.foreground.familyLabel,
16282
+ " on ",
16283
+ pair.background.familyLabel
16284
+ ] }),
16285
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
16286
+ pair.contrastRatio.toFixed(2),
16287
+ ":1 contrast ratio"
16288
+ ] })
16289
+ ] })
16290
+ ]
16291
+ },
16292
+ pair.id
16293
+ );
16294
+ }) : /* @__PURE__ */ jsx("div", { className: "rounded-sm border border-grey-200 p-4 text-sm text-muted-foreground dark:border-grey-700", children: "No recommended AAA foregrounds are available for this background." }) })
16295
+ ] }) })
16296
+ ] }) })
15882
16297
  ] });
15883
16298
  }
15884
- function ColorPairingTool() {
15885
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ColorPairingToolLoading, {}), children: /* @__PURE__ */ jsx(ColorPairingToolContent, {}) });
16299
+ function ColorPairingTool({
16300
+ visibleFormats = DEFAULT_VISIBLE_FORMATS
16301
+ } = {}) {
16302
+ const normalizedVisibleFormats = [...new Set(visibleFormats)];
16303
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ColorPairingToolLoading, {}), children: /* @__PURE__ */ jsx(ColorPairingToolContent, { visibleFormats: normalizedVisibleFormats }) });
15886
16304
  }
15887
16305
  function ColorSwatches({ theme: theme2, format, viewMode }) {
15888
16306
  return /* @__PURE__ */ jsx(
@@ -18363,93 +18781,152 @@ function FormMessage({ className, ...props }) {
18363
18781
  }
18364
18782
  );
18365
18783
  }
18366
-
18367
- // package.json
18368
- var package_default = {
18369
- version: "1.98.0"};
18370
- var SluggerContext = React5__default.createContext(null);
18371
- function flattenText(nodes) {
18372
- if (nodes == null || typeof nodes === "boolean") return "";
18373
- if (typeof nodes === "string" || typeof nodes === "number" || typeof nodes === "bigint")
18374
- return String(nodes);
18375
- if (Array.isArray(nodes)) return nodes.map(flattenText).join("");
18376
- if (React5__default.isValidElement(nodes)) {
18377
- return flattenText(nodes.props.children);
18784
+ var styles3 = {
18785
+ base: [
18786
+ // Base
18787
+ "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",
18788
+ // States
18789
+ "data-[state=on]:bg-grey-100 data-[state=on]:text-grey-850",
18790
+ // Hover
18791
+ "hover:bg-grey-100 hover:text-grey-850",
18792
+ // Focus
18793
+ "focus:outline focus:outline-2 focus:outline-offset-0 focus:outline-(--toggle-border)",
18794
+ // Dark mode
18795
+ "dark:text-white",
18796
+ // Dark mode states
18797
+ "dark:data-[state=on]:bg-white/10 dark:data-[state=on]:text-white",
18798
+ // Dark mode hover
18799
+ "dark:hover:bg-white/10 dark:hover:text-white",
18800
+ // Disabled
18801
+ "disabled:pointer-events-none disabled:opacity-50",
18802
+ // Icon
18803
+ '[&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 [&_svg]:shrink-0',
18804
+ // Aria invalid
18805
+ "aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
18806
+ // Aria invalid dark mode
18807
+ "dark:aria-invalid:ring-destructive/40"
18808
+ ],
18809
+ outline: [
18810
+ // Base
18811
+ "text-grey-800 border [--toggle-border:var(--color-grey-300)]",
18812
+ // States
18813
+ "hover:[--toggle-border:var(--color-grey-400)]",
18814
+ // Dark mode
18815
+ "dark:[--toggle-border:white]/40",
18816
+ // Dark mode states
18817
+ "dark:hover:[--toggle-border:white]/50",
18818
+ // Data on
18819
+ "data-[state=on]:bg-primary-800/10"
18820
+ ]
18821
+ };
18822
+ var toggleVariants = cva(styles3.base, {
18823
+ variants: {
18824
+ variant: {
18825
+ ghost: "",
18826
+ outline: clsx12(styles3.outline)
18827
+ },
18828
+ size: {
18829
+ default: "h-9 px-2 min-w-9",
18830
+ sm: "h-8 px-1.5 min-w-8",
18831
+ lg: "h-10 px-2.5 min-w-10"
18832
+ }
18833
+ },
18834
+ defaultVariants: {
18835
+ variant: "ghost",
18836
+ size: "default"
18378
18837
  }
18379
- return "";
18380
- }
18381
- function baseSlug(input) {
18382
- return input.toLowerCase().trim().replace(/[\s\W]+/g, "-").replace(/^-+|-+$/g, "");
18838
+ });
18839
+ function Toggle({
18840
+ className,
18841
+ variant,
18842
+ size,
18843
+ ...props
18844
+ }) {
18845
+ return /* @__PURE__ */ jsx(
18846
+ TogglePrimitive.Root,
18847
+ {
18848
+ "data-slot": "toggle",
18849
+ className: cn(toggleVariants({ variant, size, className })),
18850
+ ...props
18851
+ }
18852
+ );
18383
18853
  }
18384
- function Heading({
18854
+ var ToggleGroupContext = React5.createContext({
18855
+ size: "default",
18856
+ variant: "ghost"
18857
+ });
18858
+ function ToggleGroup({
18385
18859
  className,
18386
- trim = "normal",
18387
- size = 1,
18388
- level = 1,
18389
- display = false,
18390
- id: idProp,
18860
+ variant,
18861
+ size,
18391
18862
  children,
18392
18863
  ...props
18393
18864
  }) {
18394
- const Tag = `h${level}`;
18395
- const slugger = useContext(SluggerContext);
18396
- const headingSizeClasses = {
18397
- 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))]",
18398
- 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))]",
18399
- 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))]",
18400
- 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))]",
18401
- 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))]",
18402
- 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))]"
18403
- };
18404
- const displaySizeClasses = {
18405
- 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))]",
18406
- 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))]",
18407
- 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))]",
18408
- 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))]"
18409
- };
18410
- const sizeClass = display ? displaySizeClasses[size] : headingSizeClasses[size];
18411
- const trimClasses = {
18412
- normal: ["before:content-none after:content-none"],
18413
- start: [
18414
- 'before:content-[""] before:table after:content-none',
18415
- "before:mb-[calc(var(--leading-trim-start,var(--default-leading-trim-start))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
18416
- ],
18417
- end: [
18418
- 'before:content-none after:content-[""] after:table',
18419
- "after:mt-[calc(var(--leading-trim-end,var(--default-leading-trim-end))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
18420
- ],
18421
- both: [
18422
- 'before:content-[""] before:table after:content-[""] after:table',
18423
- "before:mb-[calc(var(--leading-trim-start,var(--default-leading-trim-start))-var(--line-height,calc(1em*var(--default-line-height)))/2)]",
18424
- "after:mt-[calc(var(--leading-trim-end,var(--default-leading-trim-end))-var(--line-height,calc(1em*var(--default-line-height)))/2)]"
18425
- ]
18426
- };
18427
- const computedId = useMemo(() => {
18428
- if (idProp) return idProp;
18429
- const text = flattenText(children);
18430
- if (!text) return void 0;
18431
- const base = baseSlug(text);
18432
- return slugger ? slugger.slug(base) : base;
18433
- }, [idProp, children, slugger]);
18434
18865
  return /* @__PURE__ */ jsx(
18435
- Tag,
18866
+ ToggleGroupPrimitive.Root,
18436
18867
  {
18868
+ "data-slot": "toggle-group",
18869
+ "data-variant": variant,
18870
+ "data-size": size,
18871
+ className: cn(
18872
+ "group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
18873
+ className
18874
+ ),
18437
18875
  ...props,
18438
- id: computedId,
18439
- "data-anchor": true,
18440
- className: clsx12(
18441
- className,
18442
- trimClasses[trim],
18443
- "m-0",
18444
- "leading-[var(--line-height)] font-[var(--heading-font-family)] font-bold",
18445
- "[--leading-trim-end:var(--heading-leading-trim-end)] [--leading-trim-start:var(--heading-leading-trim-start)]",
18446
- "text-primary-800 dark:text-white",
18447
- sizeClass
18876
+ children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
18877
+ }
18878
+ );
18879
+ }
18880
+ function ToggleGroupItem({
18881
+ className,
18882
+ children,
18883
+ variant,
18884
+ size,
18885
+ ...props
18886
+ }) {
18887
+ const context = React5.useContext(ToggleGroupContext);
18888
+ return /* @__PURE__ */ jsx(
18889
+ ToggleGroupPrimitive.Item,
18890
+ {
18891
+ "data-slot": "toggle-group-item",
18892
+ "data-variant": context.variant || variant,
18893
+ "data-size": context.size || size,
18894
+ className: cn(
18895
+ toggleVariants({
18896
+ variant: context.variant || variant,
18897
+ size: context.size || size
18898
+ }),
18899
+ "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",
18900
+ className
18448
18901
  ),
18902
+ ...props,
18449
18903
  children
18450
18904
  }
18451
18905
  );
18452
18906
  }
18907
+ function FormatToggle({ format, setFormat }) {
18908
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
18909
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Format:" }),
18910
+ /* @__PURE__ */ jsxs(
18911
+ ToggleGroup,
18912
+ {
18913
+ type: "single",
18914
+ value: format,
18915
+ onValueChange: (value) => value && setFormat(value),
18916
+ children: [
18917
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hex", "aria-label": "HEX format", children: "HEX" }),
18918
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "rgb", "aria-label": "RGB format", children: "RGB" }),
18919
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "hsl", "aria-label": "HSL format", children: "HSL" }),
18920
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "oklch", "aria-label": "OKLCH format", children: "OKLCH" })
18921
+ ]
18922
+ }
18923
+ )
18924
+ ] });
18925
+ }
18926
+
18927
+ // package.json
18928
+ var package_default = {
18929
+ version: "1.100.0"};
18453
18930
  function Logo(props) {
18454
18931
  return /* @__PURE__ */ jsxs(Fragment, { children: [
18455
18932
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: "NSW Government" }),