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