@ankhorage/color-theory 0.0.1 → 0.0.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # @ankhorage/color-theory
2
+
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - b769031: Point exports to dist/
8
+
9
+ ## 0.0.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 971b60c: Add the initial standalone color theory API with harmony role colors, swatch generation, neutral color generation, semantic color references, theme color helpers, explicit Culori subpath exports, and test coverage.
14
+ - a133273: fix knip & run paradox
15
+ - e1c392a: fix paradox
package/README.md CHANGED
@@ -1 +1,3 @@
1
1
  # @ankhorage/color-theory
2
+
3
+ Standalone color theory, harmony, swatch, contrast, and theme color generation utilities.
@@ -3,5 +3,8 @@ export interface ReadableForegroundResult {
3
3
  foreground: HexColor;
4
4
  contrast: number;
5
5
  }
6
+ /***
7
+ Return the readable black or white foreground color with the stronger contrast against a background color.
8
+ */
6
9
  export declare function getReadableForeground(background: HexColor): ReadableForegroundResult;
7
10
  //# sourceMappingURL=contrast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contrast.d.ts","sourceRoot":"","sources":["../src/contrast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAItC,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,QAAQ,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,QAAQ,GAAG,wBAAwB,CASpF"}
1
+ {"version":3,"file":"contrast.d.ts","sourceRoot":"","sources":["../src/contrast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAItC,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,QAAQ,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD;;EAEE;AACF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,QAAQ,GAAG,wBAAwB,CASpF"}
package/dist/contrast.js CHANGED
@@ -2,6 +2,9 @@ import { parseHexColorOrThrow } from './hex';
2
2
  import { contrastRatio } from './internal-culori';
3
3
  const BLACK = parseHexColorOrThrow('#000000');
4
4
  const WHITE = parseHexColorOrThrow('#FFFFFF');
5
+ /***
6
+ Return the readable black or white foreground color with the stronger contrast against a background color.
7
+ */
5
8
  export function getReadableForeground(background) {
6
9
  const contrastOnBlack = contrastRatio(background, BLACK);
7
10
  const contrastOnWhite = contrastRatio(background, WHITE);
@@ -1 +1 @@
1
- {"version":3,"file":"contrast.js","sourceRoot":"","sources":["../src/contrast.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOlD,MAAM,KAAK,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAC9C,MAAM,KAAK,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAE9C,MAAM,UAAU,qBAAqB,CAAC,UAAoB;IACxD,MAAM,eAAe,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACzD,MAAM,eAAe,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAEzD,IAAI,eAAe,IAAI,eAAe,EAAE,CAAC;QACvC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC1D,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;AAC1D,CAAC","sourcesContent":["import type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\nimport { contrastRatio } from './internal-culori';\n\nexport interface ReadableForegroundResult {\n foreground: HexColor;\n contrast: number;\n}\n\nconst BLACK = parseHexColorOrThrow('#000000');\nconst WHITE = parseHexColorOrThrow('#FFFFFF');\n\nexport function getReadableForeground(background: HexColor): ReadableForegroundResult {\n const contrastOnBlack = contrastRatio(background, BLACK);\n const contrastOnWhite = contrastRatio(background, WHITE);\n\n if (contrastOnBlack >= contrastOnWhite) {\n return { foreground: BLACK, contrast: contrastOnBlack };\n }\n\n return { foreground: WHITE, contrast: contrastOnWhite };\n}\n"]}
1
+ {"version":3,"file":"contrast.js","sourceRoot":"","sources":["../src/contrast.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOlD,MAAM,KAAK,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAC9C,MAAM,KAAK,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAE9C;;EAEE;AACF,MAAM,UAAU,qBAAqB,CAAC,UAAoB;IACxD,MAAM,eAAe,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACzD,MAAM,eAAe,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAEzD,IAAI,eAAe,IAAI,eAAe,EAAE,CAAC;QACvC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC1D,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;AAC1D,CAAC","sourcesContent":["import type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\nimport { contrastRatio } from './internal-culori';\n\nexport interface ReadableForegroundResult {\n foreground: HexColor;\n contrast: number;\n}\n\nconst BLACK = parseHexColorOrThrow('#000000');\nconst WHITE = parseHexColorOrThrow('#FFFFFF');\n\n/***\n Return the readable black or white foreground color with the stronger contrast against a background color.\n*/\nexport function getReadableForeground(background: HexColor): ReadableForegroundResult {\n const contrastOnBlack = contrastRatio(background, BLACK);\n const contrastOnWhite = contrastRatio(background, WHITE);\n\n if (contrastOnBlack >= contrastOnWhite) {\n return { foreground: BLACK, contrast: contrastOnBlack };\n }\n\n return { foreground: WHITE, contrast: contrastOnWhite };\n}\n"]}
package/dist/harmony.d.ts CHANGED
@@ -16,5 +16,8 @@ export interface GeneratedHarmonyRoleColors {
16
16
  tertiary?: GeneratedHarmonyRoleColor;
17
17
  quaternary?: GeneratedHarmonyRoleColor;
18
18
  }
19
+ /***
20
+ Generate role-based harmony colors from a primary color and harmony strategy.
21
+ */
19
22
  export declare function generateHarmonyRoleColors(primaryColor: HexColor, harmony: ColorHarmony): GeneratedHarmonyRoleColors;
20
23
  //# sourceMappingURL=harmony.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"harmony.d.ts","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,eAAO,MAAM,eAAe,uGAOlB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAErF,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,kBAAkB,CAAC;IACzB,GAAG,EAAE,QAAQ,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7C,OAAO,EAAE,yBAAyB,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,UAAU,CAAC,EAAE,yBAAyB,CAAC;CACxC;AAoBD,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,QAAQ,EACtB,OAAO,EAAE,YAAY,GACpB,0BAA0B,CAsC5B"}
1
+ {"version":3,"file":"harmony.d.ts","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,eAAO,MAAM,eAAe,uGAOlB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAErF,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,kBAAkB,CAAC;IACzB,GAAG,EAAE,QAAQ,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,WAAW,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7C,OAAO,EAAE,yBAAyB,CAAC;IACnC,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,QAAQ,CAAC,EAAE,yBAAyB,CAAC;IACrC,UAAU,CAAC,EAAE,yBAAyB,CAAC;CACxC;AAoBD;;EAEE;AACF,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,QAAQ,EACtB,OAAO,EAAE,YAAY,GACpB,0BAA0B,CAsC5B"}
package/dist/harmony.js CHANGED
@@ -23,6 +23,9 @@ const OFFSETS_BY_HARMONY = {
23
23
  triadic: [0, 120, 240],
24
24
  tetradic: [0, 90, 180, 270],
25
25
  };
26
+ /***
27
+ Generate role-based harmony colors from a primary color and harmony strategy.
28
+ */
26
29
  export function generateHarmonyRoleColors(primaryColor, harmony) {
27
30
  const base = parseHexToOklch(primaryColor);
28
31
  const roles = ROLE_ORDER_BY_HARMONY[harmony];
@@ -1 +1 @@
1
- {"version":3,"file":"harmony.js","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\n\nexport const COLOR_HARMONIES = [\n 'monochromatic',\n 'analogous',\n 'complementary',\n 'triadic',\n 'tetradic',\n 'splitComplementary',\n] as const;\n\nexport type ColorHarmony = (typeof COLOR_HARMONIES)[number];\n\nexport type GeneratedColorRole = 'primary' | 'secondary' | 'tertiary' | 'quaternary';\n\nexport interface GeneratedHarmonyRoleColor {\n role: GeneratedColorRole;\n hex: HexColor;\n hueDegrees: number;\n source: 'selected' | 'generated';\n}\n\nexport interface GeneratedHarmonyRoleColors {\n harmony: ColorHarmony;\n colors: readonly GeneratedHarmonyRoleColor[];\n primary: GeneratedHarmonyRoleColor;\n secondary?: GeneratedHarmonyRoleColor;\n tertiary?: GeneratedHarmonyRoleColor;\n quaternary?: GeneratedHarmonyRoleColor;\n}\n\nconst ROLE_ORDER_BY_HARMONY: Record<ColorHarmony, readonly GeneratedColorRole[]> = {\n monochromatic: ['primary'],\n complementary: ['primary', 'secondary'],\n analogous: ['primary', 'secondary', 'tertiary'],\n splitComplementary: ['primary', 'secondary', 'tertiary'],\n triadic: ['primary', 'secondary', 'tertiary'],\n tetradic: ['primary', 'secondary', 'tertiary', 'quaternary'],\n};\n\nconst OFFSETS_BY_HARMONY: Record<ColorHarmony, readonly number[]> = {\n monochromatic: [0],\n analogous: [0, -30, 30],\n complementary: [0, 180],\n splitComplementary: [0, 150, 210],\n triadic: [0, 120, 240],\n tetradic: [0, 90, 180, 270],\n};\n\nexport function generateHarmonyRoleColors(\n primaryColor: HexColor,\n harmony: ColorHarmony,\n): GeneratedHarmonyRoleColors {\n const base = parseHexToOklch(primaryColor);\n const roles = ROLE_ORDER_BY_HARMONY[harmony];\n const offsets = OFFSETS_BY_HARMONY[harmony];\n const baseHue = normalizeHueDegrees(base.h);\n\n const colors: GeneratedHarmonyRoleColor[] = [];\n\n for (let index = 0; index < roles.length; index++) {\n const role = roles[index];\n if (!role) continue;\n\n const hueDegrees = normalizeHueDegrees(baseHue + (offsets[index] ?? 0));\n colors.push({\n role,\n hex: role === 'primary' ? primaryColor : oklchToHex({ ...base, h: hueDegrees }),\n hueDegrees,\n source: role === 'primary' ? 'selected' : 'generated',\n });\n }\n\n const primary = colors.find((color) => color.role === 'primary');\n if (!primary) {\n throw new Error('[color-theory] Expected generated harmony role colors to include primary.');\n }\n\n const secondary = colors.find((color) => color.role === 'secondary');\n const tertiary = colors.find((color) => color.role === 'tertiary');\n const quaternary = colors.find((color) => color.role === 'quaternary');\n\n return {\n harmony,\n colors,\n primary,\n ...(secondary ? { secondary } : {}),\n ...(tertiary ? { tertiary } : {}),\n ...(quaternary ? { quaternary } : {}),\n };\n}\n"]}
1
+ {"version":3,"file":"harmony.js","sourceRoot":"","sources":["../src/harmony.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\n\nexport const COLOR_HARMONIES = [\n 'monochromatic',\n 'analogous',\n 'complementary',\n 'triadic',\n 'tetradic',\n 'splitComplementary',\n] as const;\n\nexport type ColorHarmony = (typeof COLOR_HARMONIES)[number];\n\nexport type GeneratedColorRole = 'primary' | 'secondary' | 'tertiary' | 'quaternary';\n\nexport interface GeneratedHarmonyRoleColor {\n role: GeneratedColorRole;\n hex: HexColor;\n hueDegrees: number;\n source: 'selected' | 'generated';\n}\n\nexport interface GeneratedHarmonyRoleColors {\n harmony: ColorHarmony;\n colors: readonly GeneratedHarmonyRoleColor[];\n primary: GeneratedHarmonyRoleColor;\n secondary?: GeneratedHarmonyRoleColor;\n tertiary?: GeneratedHarmonyRoleColor;\n quaternary?: GeneratedHarmonyRoleColor;\n}\n\nconst ROLE_ORDER_BY_HARMONY: Record<ColorHarmony, readonly GeneratedColorRole[]> = {\n monochromatic: ['primary'],\n complementary: ['primary', 'secondary'],\n analogous: ['primary', 'secondary', 'tertiary'],\n splitComplementary: ['primary', 'secondary', 'tertiary'],\n triadic: ['primary', 'secondary', 'tertiary'],\n tetradic: ['primary', 'secondary', 'tertiary', 'quaternary'],\n};\n\nconst OFFSETS_BY_HARMONY: Record<ColorHarmony, readonly number[]> = {\n monochromatic: [0],\n analogous: [0, -30, 30],\n complementary: [0, 180],\n splitComplementary: [0, 150, 210],\n triadic: [0, 120, 240],\n tetradic: [0, 90, 180, 270],\n};\n\n/***\n Generate role-based harmony colors from a primary color and harmony strategy.\n*/\nexport function generateHarmonyRoleColors(\n primaryColor: HexColor,\n harmony: ColorHarmony,\n): GeneratedHarmonyRoleColors {\n const base = parseHexToOklch(primaryColor);\n const roles = ROLE_ORDER_BY_HARMONY[harmony];\n const offsets = OFFSETS_BY_HARMONY[harmony];\n const baseHue = normalizeHueDegrees(base.h);\n\n const colors: GeneratedHarmonyRoleColor[] = [];\n\n for (let index = 0; index < roles.length; index++) {\n const role = roles[index];\n if (!role) continue;\n\n const hueDegrees = normalizeHueDegrees(baseHue + (offsets[index] ?? 0));\n colors.push({\n role,\n hex: role === 'primary' ? primaryColor : oklchToHex({ ...base, h: hueDegrees }),\n hueDegrees,\n source: role === 'primary' ? 'selected' : 'generated',\n });\n }\n\n const primary = colors.find((color) => color.role === 'primary');\n if (!primary) {\n throw new Error('[color-theory] Expected generated harmony role colors to include primary.');\n }\n\n const secondary = colors.find((color) => color.role === 'secondary');\n const tertiary = colors.find((color) => color.role === 'tertiary');\n const quaternary = colors.find((color) => color.role === 'quaternary');\n\n return {\n harmony,\n colors,\n primary,\n ...(secondary ? { secondary } : {}),\n ...(tertiary ? { tertiary } : {}),\n ...(quaternary ? { quaternary } : {}),\n };\n}\n"]}
package/dist/hex.d.ts CHANGED
@@ -1,10 +1,20 @@
1
1
  export type HexColor = string & {
2
2
  readonly __hexColorBrand: unique symbol;
3
3
  };
4
+ /***
5
+ Check whether a string is a valid six-digit hex color.
6
+ */
4
7
  export declare function isHexColor(value: string): value is HexColor;
8
+ /***
9
+ Parse a string as a six-digit hex color and return null when it is invalid.
10
+ */
5
11
  export declare function parseHexColor(value: string): HexColor | null;
12
+ /***
13
+ Parse a string as a six-digit hex color or throw when it is invalid.
14
+ */
6
15
  export declare function parseHexColorOrThrow(value: string): HexColor;
7
- export declare const normalizeHexColor: typeof parseHexColor;
8
- export declare const normalizeHexColorOrThrow: typeof parseHexColorOrThrow;
16
+ /***
17
+ Assert that a string is a valid six-digit hex color.
18
+ */
9
19
  export declare function assertHexColor(value: string): asserts value is HexColor;
10
20
  //# sourceMappingURL=hex.d.ts.map
package/dist/hex.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hex.d.ts","sourceRoot":"","sources":["../src/hex.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAI5E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAG5D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAM5D;AAED,eAAO,MAAM,iBAAiB,sBAAgB,CAAC;AAC/C,eAAO,MAAM,wBAAwB,6BAAuB,CAAC;AAE7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAIvE"}
1
+ {"version":3,"file":"hex.d.ts","sourceRoot":"","sources":["../src/hex.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAI5E;;EAEE;AACF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAED;;EAEE;AACF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAG5D;AAED;;EAEE;AACF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAM5D;AAED;;EAEE;AACF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAIvE"}
package/dist/hex.js CHANGED
@@ -1,12 +1,21 @@
1
1
  const HEX6_CASE_INSENSITIVE_REGEX = /^#[0-9a-fA-F]{6}$/;
2
+ /***
3
+ Check whether a string is a valid six-digit hex color.
4
+ */
2
5
  export function isHexColor(value) {
3
6
  return HEX6_CASE_INSENSITIVE_REGEX.test(value);
4
7
  }
8
+ /***
9
+ Parse a string as a six-digit hex color and return null when it is invalid.
10
+ */
5
11
  export function parseHexColor(value) {
6
12
  if (!HEX6_CASE_INSENSITIVE_REGEX.test(value))
7
13
  return null;
8
14
  return value;
9
15
  }
16
+ /***
17
+ Parse a string as a six-digit hex color or throw when it is invalid.
18
+ */
10
19
  export function parseHexColorOrThrow(value) {
11
20
  const parsed = parseHexColor(value);
12
21
  if (!parsed) {
@@ -14,8 +23,9 @@ export function parseHexColorOrThrow(value) {
14
23
  }
15
24
  return parsed;
16
25
  }
17
- export const normalizeHexColor = parseHexColor;
18
- export const normalizeHexColorOrThrow = parseHexColorOrThrow;
26
+ /***
27
+ Assert that a string is a valid six-digit hex color.
28
+ */
19
29
  export function assertHexColor(value) {
20
30
  if (!isHexColor(value)) {
21
31
  throw new Error(`Invalid hex color (expected #RRGGBB or #rrggbb): ${JSON.stringify(value)}`);
package/dist/hex.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hex.js","sourceRoot":"","sources":["../src/hex.ts"],"names":[],"mappings":"AAEA,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAExD,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,OAAO,KAAiB,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC;AAC/C,MAAM,CAAC,MAAM,wBAAwB,GAAG,oBAAoB,CAAC;AAE7D,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC","sourcesContent":["export type HexColor = string & { readonly __hexColorBrand: unique symbol };\n\nconst HEX6_CASE_INSENSITIVE_REGEX = /^#[0-9a-fA-F]{6}$/;\n\nexport function isHexColor(value: string): value is HexColor {\n return HEX6_CASE_INSENSITIVE_REGEX.test(value);\n}\n\nexport function parseHexColor(value: string): HexColor | null {\n if (!HEX6_CASE_INSENSITIVE_REGEX.test(value)) return null;\n return value as HexColor;\n}\n\nexport function parseHexColorOrThrow(value: string): HexColor {\n const parsed = parseHexColor(value);\n if (!parsed) {\n throw new Error(`Invalid hex color (expected #RRGGBB or #rrggbb): ${JSON.stringify(value)}`);\n }\n return parsed;\n}\n\nexport const normalizeHexColor = parseHexColor;\nexport const normalizeHexColorOrThrow = parseHexColorOrThrow;\n\nexport function assertHexColor(value: string): asserts value is HexColor {\n if (!isHexColor(value)) {\n throw new Error(`Invalid hex color (expected #RRGGBB or #rrggbb): ${JSON.stringify(value)}`);\n }\n}\n"]}
1
+ {"version":3,"file":"hex.js","sourceRoot":"","sources":["../src/hex.ts"],"names":[],"mappings":"AAEA,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAExD;;EAEE;AACF,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,OAAO,KAAiB,CAAC;AAC3B,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC","sourcesContent":["export type HexColor = string & { readonly __hexColorBrand: unique symbol };\n\nconst HEX6_CASE_INSENSITIVE_REGEX = /^#[0-9a-fA-F]{6}$/;\n\n/***\n Check whether a string is a valid six-digit hex color.\n*/\nexport function isHexColor(value: string): value is HexColor {\n return HEX6_CASE_INSENSITIVE_REGEX.test(value);\n}\n\n/***\n Parse a string as a six-digit hex color and return null when it is invalid.\n*/\nexport function parseHexColor(value: string): HexColor | null {\n if (!HEX6_CASE_INSENSITIVE_REGEX.test(value)) return null;\n return value as HexColor;\n}\n\n/***\n Parse a string as a six-digit hex color or throw when it is invalid.\n*/\nexport function parseHexColorOrThrow(value: string): HexColor {\n const parsed = parseHexColor(value);\n if (!parsed) {\n throw new Error(`Invalid hex color (expected #RRGGBB or #rrggbb): ${JSON.stringify(value)}`);\n }\n return parsed;\n}\n\n/***\n Assert that a string is a valid six-digit hex color.\n*/\nexport function assertHexColor(value: string): asserts value is HexColor {\n if (!isHexColor(value)) {\n throw new Error(`Invalid hex color (expected #RRGGBB or #rrggbb): ${JSON.stringify(value)}`);\n }\n}\n"]}
@@ -1,25 +1,30 @@
1
1
  import type { HexColor } from './hex';
2
- export interface OklchColor {
2
+ interface OklchColor {
3
3
  mode: 'oklch';
4
4
  l: number;
5
5
  c: number;
6
6
  h: number;
7
7
  alpha?: number;
8
8
  }
9
- export interface RgbColor {
10
- mode: 'rgb';
11
- r: number;
12
- g: number;
13
- b: number;
14
- alpha?: number;
15
- }
9
+ /***
10
+ Normalize hue degrees into the zero-to-three-sixty range.
11
+ */
16
12
  export declare function normalizeHueDegrees(hueDegrees: number): number;
13
+ /***
14
+ Convert a hex color into a normalized OKLCH color object.
15
+ */
17
16
  export declare function parseHexToOklch(hex: HexColor): OklchColor;
17
+ /***
18
+ Convert an OKLCH color object into an in-gamut hex color.
19
+ */
18
20
  export declare function oklchToHex(oklch: OklchColor): HexColor;
19
- export declare function toOklchColor(input: string | object): OklchColor;
20
- export declare function toHexColor(input: string | object): HexColor;
21
+ /***
22
+ Calculate the perceptual OKLCH distance between two OKLCH colors.
23
+ */
21
24
  export declare function deltaEoklch(a: OklchColor, b: OklchColor): number;
25
+ /***
26
+ Calculate the WCAG contrast ratio between two color inputs.
27
+ */
22
28
  export declare function contrastRatio(colorA: string | object, colorB: string | object): number;
23
- export declare function relativeLuminance(color: string | object): number;
24
- export declare function rgbFrom(input: string | object): RgbColor;
29
+ export {};
25
30
  //# sourceMappingURL=internal-culori.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-culori.d.ts","sourceRoot":"","sources":["../src/internal-culori.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,KAAK,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAYD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,UAAU,CAWzD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAItD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAW/D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAI3D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAEhE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEtF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAMxD"}
1
+ {"version":3,"file":"internal-culori.d.ts","sourceRoot":"","sources":["../src/internal-culori.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,UAAU,UAAU;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsBD;;EAEE;AACF,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;EAEE;AACF,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,UAAU,CAWzD;AAED;;EAEE;AACF,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,CAItD;AAED;;EAEE;AACF,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,MAAM,CAEhE;AAED;;EAEE;AACF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEtF"}
@@ -1,18 +1,26 @@
1
- import { converter, differenceEuclidean, formatHex, toGamut, wcagContrast, wcagLuminance, } from 'culori';
1
+ import { converter, differenceEuclidean, formatHex, toGamut, wcagContrast } from 'culori';
2
2
  import { parseHexColorOrThrow } from './hex';
3
3
  const toOklch = converter('oklch');
4
- const toRgb = converter('rgb');
5
4
  const toRgbGamut = toGamut('rgb', 'oklch');
6
5
  const deltaE = differenceEuclidean('oklch');
6
+ /***
7
+ Clamp a number to a finite minimum and maximum range.
8
+ */
7
9
  function clampNumber(value, min, max) {
8
10
  if (!Number.isFinite(value))
9
11
  return min;
10
12
  return Math.min(max, Math.max(min, value));
11
13
  }
14
+ /***
15
+ Normalize hue degrees into the zero-to-three-sixty range.
16
+ */
12
17
  export function normalizeHueDegrees(hueDegrees) {
13
18
  const hue = ((hueDegrees % 360) + 360) % 360;
14
19
  return hue === 360 ? 0 : hue;
15
20
  }
21
+ /***
22
+ Convert a hex color into a normalized OKLCH color object.
23
+ */
16
24
  export function parseHexToOklch(hex) {
17
25
  const color = toOklch(hex);
18
26
  if (color?.mode !== 'oklch') {
@@ -23,40 +31,24 @@ export function parseHexToOklch(hex) {
23
31
  const h = normalizeHueDegrees(Number.isFinite(color.h) ? color.h : 0);
24
32
  return { mode: 'oklch', l, c, h, alpha: color.alpha };
25
33
  }
34
+ /***
35
+ Convert an OKLCH color object into an in-gamut hex color.
36
+ */
26
37
  export function oklchToHex(oklch) {
27
38
  const gamutRgb = toRgbGamut(oklch);
28
39
  const hex = formatHex(gamutRgb ?? oklch);
29
40
  return parseHexColorOrThrow(hex);
30
41
  }
31
- export function toOklchColor(input) {
32
- const color = toOklch(input);
33
- if (color?.mode !== 'oklch') {
34
- throw new Error(`Failed to parse/convert to OKLCH: ${JSON.stringify(input)}`);
35
- }
36
- const l = clampNumber(color.l, 0, 1);
37
- const c = clampNumber(color.c, 0, 0.4);
38
- const h = normalizeHueDegrees(Number.isFinite(color.h) ? color.h : 0);
39
- return { mode: 'oklch', l, c, h, alpha: color.alpha };
40
- }
41
- export function toHexColor(input) {
42
- const gamutRgb = toRgbGamut(input);
43
- const hex = formatHex(gamutRgb ?? input);
44
- return parseHexColorOrThrow(hex);
45
- }
42
+ /***
43
+ Calculate the perceptual OKLCH distance between two OKLCH colors.
44
+ */
46
45
  export function deltaEoklch(a, b) {
47
46
  return deltaE(a, b);
48
47
  }
48
+ /***
49
+ Calculate the WCAG contrast ratio between two color inputs.
50
+ */
49
51
  export function contrastRatio(colorA, colorB) {
50
52
  return wcagContrast(colorA, colorB);
51
53
  }
52
- export function relativeLuminance(color) {
53
- return wcagLuminance(color);
54
- }
55
- export function rgbFrom(input) {
56
- const rgb = toRgb(input);
57
- if (rgb?.mode !== 'rgb') {
58
- throw new Error(`Failed to convert to rgb: ${JSON.stringify(input)}`);
59
- }
60
- return rgb;
61
- }
62
54
  //# sourceMappingURL=internal-culori.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-culori.js","sourceRoot":"","sources":["../src/internal-culori.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n converter,\n differenceEuclidean,\n formatHex,\n toGamut,\n wcagContrast,\n wcagLuminance,\n} from 'culori';\n\nimport type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\n\nexport interface OklchColor {\n mode: 'oklch';\n l: number;\n c: number;\n h: number;\n alpha?: number;\n}\n\nexport interface RgbColor {\n mode: 'rgb';\n r: number;\n g: number;\n b: number;\n alpha?: number;\n}\n\nconst toOklch = converter('oklch');\nconst toRgb = converter('rgb');\nconst toRgbGamut = toGamut('rgb', 'oklch');\nconst deltaE = differenceEuclidean('oklch');\n\nfunction clampNumber(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return min;\n return Math.min(max, Math.max(min, value));\n}\n\nexport function normalizeHueDegrees(hueDegrees: number): number {\n const hue = ((hueDegrees % 360) + 360) % 360;\n return hue === 360 ? 0 : hue;\n}\n\nexport function parseHexToOklch(hex: HexColor): OklchColor {\n const color = toOklch(hex) as unknown as OklchColor | undefined;\n if (color?.mode !== 'oklch') {\n throw new Error(`Failed to convert hex to OKLCH: ${hex}`);\n }\n\n const l = clampNumber(color.l, 0, 1);\n const c = clampNumber(color.c, 0, 0.4);\n const h = normalizeHueDegrees(Number.isFinite(color.h) ? color.h : 0);\n\n return { mode: 'oklch', l, c, h, alpha: color.alpha };\n}\n\nexport function oklchToHex(oklch: OklchColor): HexColor {\n const gamutRgb = toRgbGamut(oklch) as unknown as RgbColor | undefined;\n const hex = formatHex(gamutRgb ?? oklch);\n return parseHexColorOrThrow(hex);\n}\n\nexport function toOklchColor(input: string | object): OklchColor {\n const color = toOklch(input as never) as unknown as OklchColor | undefined;\n if (color?.mode !== 'oklch') {\n throw new Error(`Failed to parse/convert to OKLCH: ${JSON.stringify(input)}`);\n }\n\n const l = clampNumber(color.l, 0, 1);\n const c = clampNumber(color.c, 0, 0.4);\n const h = normalizeHueDegrees(Number.isFinite(color.h) ? color.h : 0);\n\n return { mode: 'oklch', l, c, h, alpha: color.alpha };\n}\n\nexport function toHexColor(input: string | object): HexColor {\n const gamutRgb = toRgbGamut(input as never) as unknown as RgbColor | undefined;\n const hex = formatHex(gamutRgb ?? (input as never));\n return parseHexColorOrThrow(hex);\n}\n\nexport function deltaEoklch(a: OklchColor, b: OklchColor): number {\n return deltaE(a, b);\n}\n\nexport function contrastRatio(colorA: string | object, colorB: string | object): number {\n return wcagContrast(colorA as never, colorB as never);\n}\n\nexport function relativeLuminance(color: string | object): number {\n return wcagLuminance(color as never);\n}\n\nexport function rgbFrom(input: string | object): RgbColor {\n const rgb = toRgb(input as never) as unknown as RgbColor | undefined;\n if (rgb?.mode !== 'rgb') {\n throw new Error(`Failed to convert to rgb: ${JSON.stringify(input)}`);\n }\n return rgb;\n}\n"]}
1
+ {"version":3,"file":"internal-culori.js","sourceRoot":"","sources":["../src/internal-culori.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAG1F,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAkB7C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;AACnC,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC3C,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE5C;;EAEE;AACF,SAAS,WAAW,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC7C,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/B,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,eAAe,CAAC,GAAa;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAsC,CAAC;IAChE,IAAI,KAAK,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;AACxD,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAoC,CAAC;IACtE,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC;IACzC,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,WAAW,CAAC,CAAa,EAAE,CAAa;IACtD,OAAO,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtB,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,aAAa,CAAC,MAAuB,EAAE,MAAuB;IAC5E,OAAO,YAAY,CAAC,MAAe,EAAE,MAAe,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import { converter, differenceEuclidean, formatHex, toGamut, wcagContrast } from 'culori';\n\nimport type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\n\ninterface OklchColor {\n mode: 'oklch';\n l: number;\n c: number;\n h: number;\n alpha?: number;\n}\n\ninterface RgbColor {\n mode: 'rgb';\n r: number;\n g: number;\n b: number;\n alpha?: number;\n}\n\nconst toOklch = converter('oklch');\nconst toRgbGamut = toGamut('rgb', 'oklch');\nconst deltaE = differenceEuclidean('oklch');\n\n/***\n Clamp a number to a finite minimum and maximum range.\n*/\nfunction clampNumber(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return min;\n return Math.min(max, Math.max(min, value));\n}\n\n/***\n Normalize hue degrees into the zero-to-three-sixty range.\n*/\nexport function normalizeHueDegrees(hueDegrees: number): number {\n const hue = ((hueDegrees % 360) + 360) % 360;\n return hue === 360 ? 0 : hue;\n}\n\n/***\n Convert a hex color into a normalized OKLCH color object.\n*/\nexport function parseHexToOklch(hex: HexColor): OklchColor {\n const color = toOklch(hex) as unknown as OklchColor | undefined;\n if (color?.mode !== 'oklch') {\n throw new Error(`Failed to convert hex to OKLCH: ${hex}`);\n }\n\n const l = clampNumber(color.l, 0, 1);\n const c = clampNumber(color.c, 0, 0.4);\n const h = normalizeHueDegrees(Number.isFinite(color.h) ? color.h : 0);\n\n return { mode: 'oklch', l, c, h, alpha: color.alpha };\n}\n\n/***\n Convert an OKLCH color object into an in-gamut hex color.\n*/\nexport function oklchToHex(oklch: OklchColor): HexColor {\n const gamutRgb = toRgbGamut(oklch) as unknown as RgbColor | undefined;\n const hex = formatHex(gamutRgb ?? oklch);\n return parseHexColorOrThrow(hex);\n}\n\n/***\n Calculate the perceptual OKLCH distance between two OKLCH colors.\n*/\nexport function deltaEoklch(a: OklchColor, b: OklchColor): number {\n return deltaE(a, b);\n}\n\n/***\n Calculate the WCAG contrast ratio between two color inputs.\n*/\nexport function contrastRatio(colorA: string | object, colorB: string | object): number {\n return wcagContrast(colorA as never, colorB as never);\n}\n"]}
package/dist/neutral.d.ts CHANGED
@@ -11,5 +11,8 @@ export interface GeneratedNeutralMetadata {
11
11
  neutralKeyColor: HexColor;
12
12
  diagnostics: ColorSwatchDiagnostics;
13
13
  }
14
+ /***
15
+ Generate a softly tinted neutral swatch from generated harmony role colors.
16
+ */
14
17
  export declare function generateNeutralSwatch(roleColors: GeneratedHarmonyRoleColors): NeutralSwatchResult;
15
18
  //# sourceMappingURL=neutral.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"neutral.d.ts","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAEhG,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,QAAQ,CAAC;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,QAAQ,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;CACrC;AA4BD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,0BAA0B,GAAG,mBAAmB,CAuBjG"}
1
+ {"version":3,"file":"neutral.d.ts","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAEhG,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,QAAQ,CAAC;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,QAAQ,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAkCD;;EAEE;AACF,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,0BAA0B,GAAG,mBAAmB,CAuBjG"}
package/dist/neutral.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';
2
2
  import { generateColorSwatch } from './swatches';
3
3
  export const MIN_HUEFUL_CHROMA = 0.015;
4
+ /***
5
+ Pick the harmony color that should tint the generated neutral swatch.
6
+ */
4
7
  function pickTintSourceHex(roleColors, harmony) {
5
8
  const byMapping = {
6
9
  monochromatic: roleColors.primary.hex,
@@ -15,11 +18,17 @@ function pickTintSourceHex(roleColors, harmony) {
15
18
  roleColors.secondary?.hex ??
16
19
  roleColors.primary.hex);
17
20
  }
21
+ /***
22
+ Clamp a number to a finite minimum and maximum range.
23
+ */
18
24
  function clampNumber(value, min, max) {
19
25
  if (!Number.isFinite(value))
20
26
  return min;
21
27
  return Math.min(max, Math.max(min, value));
22
28
  }
29
+ /***
30
+ Generate a softly tinted neutral swatch from generated harmony role colors.
31
+ */
23
32
  export function generateNeutralSwatch(roleColors) {
24
33
  const tintSourceHex = pickTintSourceHex(roleColors, roleColors.harmony);
25
34
  const tintSource = parseHexToOklch(tintSourceHex);
@@ -1 +1 @@
1
- {"version":3,"file":"neutral.js","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAiD,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEhG,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAavC,SAAS,iBAAiB,CACxB,UAAsC,EACtC,OAAqB;IAErB,MAAM,SAAS,GAAwD;QACrE,aAAa,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG;QACrC,aAAa,EAAE,UAAU,CAAC,SAAS,EAAE,GAAG;QACxC,SAAS,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;QACnC,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;QAC5C,OAAO,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;QACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;KACnC,CAAC;IAEF,OAAO,CACL,SAAS,CAAC,OAAO,CAAC;QAClB,UAAU,CAAC,QAAQ,EAAE,GAAG;QACxB,UAAU,CAAC,SAAS,EAAE,GAAG;QACzB,UAAU,CAAC,OAAO,CAAC,GAAG,CACvB,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,UAAsC;IAC1E,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAElD,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExD,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,GAAG,iBAAiB,CAAC;IAEpD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACvF,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtF,MAAM,eAAe,GAAG,UAAU,CAAC;QACjC,IAAI,EAAE,OAAO;QACb,CAAC,EAAE,GAAG;QACN,CAAC,EAAE,aAAa;QAChB,CAAC,EAAE,UAAU;KACd,CAAC,CAAC;IAEH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAErE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["import type { ColorHarmony, GeneratedHarmonyRoleColors } from './harmony';\nimport type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\nimport { type ColorSwatch, type ColorSwatchDiagnostics, generateColorSwatch } from './swatches';\n\nexport const MIN_HUEFUL_CHROMA = 0.015;\n\nexport interface NeutralSwatchResult {\n neutralKeyColor: HexColor;\n neutral: ColorSwatch;\n diagnostics: ColorSwatchDiagnostics;\n}\n\nexport interface GeneratedNeutralMetadata {\n neutralKeyColor: HexColor;\n diagnostics: ColorSwatchDiagnostics;\n}\n\nfunction pickTintSourceHex(\n roleColors: GeneratedHarmonyRoleColors,\n harmony: ColorHarmony,\n): HexColor {\n const byMapping: Partial<Record<ColorHarmony, HexColor | undefined>> = {\n monochromatic: roleColors.primary.hex,\n complementary: roleColors.secondary?.hex,\n analogous: roleColors.tertiary?.hex,\n splitComplementary: roleColors.tertiary?.hex,\n triadic: roleColors.tertiary?.hex,\n tetradic: roleColors.tertiary?.hex,\n };\n\n return (\n byMapping[harmony] ??\n roleColors.tertiary?.hex ??\n roleColors.secondary?.hex ??\n roleColors.primary.hex\n );\n}\n\nfunction clampNumber(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return min;\n return Math.min(max, Math.max(min, value));\n}\n\nexport function generateNeutralSwatch(roleColors: GeneratedHarmonyRoleColors): NeutralSwatchResult {\n const tintSourceHex = pickTintSourceHex(roleColors, roleColors.harmony);\n const tintSource = parseHexToOklch(tintSourceHex);\n\n const primary = parseHexToOklch(roleColors.primary.hex);\n\n const preferGray = tintSource.c < MIN_HUEFUL_CHROMA;\n\n const hue = roleColors.harmony === 'monochromatic' ? primary.h : tintSource.h;\n const sourceChroma = roleColors.harmony === 'monochromatic' ? primary.c : tintSource.c;\n const neutralHue = preferGray ? 0 : normalizeHueDegrees(hue);\n const neutralChroma = preferGray ? 0 : clampNumber(sourceChroma * 0.06, 0.004, 0.012);\n\n const neutralKeyColor = oklchToHex({\n mode: 'oklch',\n l: 0.6,\n c: neutralChroma,\n h: neutralHue,\n });\n\n const { swatch, diagnostics } = generateColorSwatch(neutralKeyColor);\n\n return { neutralKeyColor, neutral: swatch, diagnostics };\n}\n"]}
1
+ {"version":3,"file":"neutral.js","sourceRoot":"","sources":["../src/neutral.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAiD,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEhG,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAavC;;EAEE;AACF,SAAS,iBAAiB,CACxB,UAAsC,EACtC,OAAqB;IAErB,MAAM,SAAS,GAAwD;QACrE,aAAa,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG;QACrC,aAAa,EAAE,UAAU,CAAC,SAAS,EAAE,GAAG;QACxC,SAAS,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;QACnC,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;QAC5C,OAAO,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;QACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,GAAG;KACnC,CAAC;IAEF,OAAO,CACL,SAAS,CAAC,OAAO,CAAC;QAClB,UAAU,CAAC,QAAQ,EAAE,GAAG;QACxB,UAAU,CAAC,SAAS,EAAE,GAAG;QACzB,UAAU,CAAC,OAAO,CAAC,GAAG,CACvB,CAAC;AACJ,CAAC;AAED;;EAEE;AACF,SAAS,WAAW,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,qBAAqB,CAAC,UAAsC;IAC1E,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;IAElD,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExD,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,GAAG,iBAAiB,CAAC;IAEpD,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,KAAK,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACvF,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,aAAa,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtF,MAAM,eAAe,GAAG,UAAU,CAAC;QACjC,IAAI,EAAE,OAAO;QACb,CAAC,EAAE,GAAG;QACN,CAAC,EAAE,aAAa;QAChB,CAAC,EAAE,UAAU;KACd,CAAC,CAAC;IAEH,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAErE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["import type { ColorHarmony, GeneratedHarmonyRoleColors } from './harmony';\nimport type { HexColor } from './hex';\nimport { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';\nimport { type ColorSwatch, type ColorSwatchDiagnostics, generateColorSwatch } from './swatches';\n\nexport const MIN_HUEFUL_CHROMA = 0.015;\n\nexport interface NeutralSwatchResult {\n neutralKeyColor: HexColor;\n neutral: ColorSwatch;\n diagnostics: ColorSwatchDiagnostics;\n}\n\nexport interface GeneratedNeutralMetadata {\n neutralKeyColor: HexColor;\n diagnostics: ColorSwatchDiagnostics;\n}\n\n/***\n Pick the harmony color that should tint the generated neutral swatch.\n*/\nfunction pickTintSourceHex(\n roleColors: GeneratedHarmonyRoleColors,\n harmony: ColorHarmony,\n): HexColor {\n const byMapping: Partial<Record<ColorHarmony, HexColor | undefined>> = {\n monochromatic: roleColors.primary.hex,\n complementary: roleColors.secondary?.hex,\n analogous: roleColors.tertiary?.hex,\n splitComplementary: roleColors.tertiary?.hex,\n triadic: roleColors.tertiary?.hex,\n tetradic: roleColors.tertiary?.hex,\n };\n\n return (\n byMapping[harmony] ??\n roleColors.tertiary?.hex ??\n roleColors.secondary?.hex ??\n roleColors.primary.hex\n );\n}\n\n/***\n Clamp a number to a finite minimum and maximum range.\n*/\nfunction clampNumber(value: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return min;\n return Math.min(max, Math.max(min, value));\n}\n\n/***\n Generate a softly tinted neutral swatch from generated harmony role colors.\n*/\nexport function generateNeutralSwatch(roleColors: GeneratedHarmonyRoleColors): NeutralSwatchResult {\n const tintSourceHex = pickTintSourceHex(roleColors, roleColors.harmony);\n const tintSource = parseHexToOklch(tintSourceHex);\n\n const primary = parseHexToOklch(roleColors.primary.hex);\n\n const preferGray = tintSource.c < MIN_HUEFUL_CHROMA;\n\n const hue = roleColors.harmony === 'monochromatic' ? primary.h : tintSource.h;\n const sourceChroma = roleColors.harmony === 'monochromatic' ? primary.c : tintSource.c;\n const neutralHue = preferGray ? 0 : normalizeHueDegrees(hue);\n const neutralChroma = preferGray ? 0 : clampNumber(sourceChroma * 0.06, 0.004, 0.012);\n\n const neutralKeyColor = oklchToHex({\n mode: 'oklch',\n l: 0.6,\n c: neutralChroma,\n h: neutralHue,\n });\n\n const { swatch, diagnostics } = generateColorSwatch(neutralKeyColor);\n\n return { neutralKeyColor, neutral: swatch, diagnostics };\n}\n"]}
@@ -20,6 +20,9 @@ export interface ColorSwatchDiagnostics {
20
20
  max: number;
21
21
  };
22
22
  }
23
+ /***
24
+ Generate a full color swatch and diagnostics from a base color.
25
+ */
23
26
  export declare function generateColorSwatch(baseColor: HexColor): {
24
27
  swatch: ColorSwatch;
25
28
  diagnostics: ColorSwatchDiagnostics;
@@ -1 +1 @@
1
- {"version":3,"file":"swatches.d.ts","sourceRoot":"","sources":["../src/swatches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,eAAO,MAAM,kBAAkB,iEAAkE,CAAC;AAClG,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AAE5D,MAAM,MAAM,sBAAsB,GAC9B,WAAW,GACX,qBAAqB,GACrB,yBAAyB,CAAC;AAE9B,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACxC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAgCD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,QAAQ,GAAG;IACxD,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,sBAAsB,CAAC;CACrC,CAoFA"}
1
+ {"version":3,"file":"swatches.d.ts","sourceRoot":"","sources":["../src/swatches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,eAAO,MAAM,kBAAkB,iEAAkE,CAAC;AAClG,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AAE5D,MAAM,MAAM,sBAAsB,GAC9B,WAAW,GACX,qBAAqB,GACrB,yBAAyB,CAAC;AAE9B,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACxC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE;QACd,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAsCD;;EAEE;AACF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,QAAQ,GAAG;IACxD,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,sBAAsB,CAAC;CACrC,CAoFA"}
package/dist/swatches.js CHANGED
@@ -16,11 +16,17 @@ const BASELINE_LIGHTNESS_BY_STEP = {
16
16
  };
17
17
  const MIN_USABLE_ADJACENT_DELTA = 0.012;
18
18
  const MIN_USABLE_LIGHTNESS_RANGE = 0.35;
19
+ /***
20
+ Clamp a number into the normalized zero-to-one range.
21
+ */
19
22
  function clamp01(value) {
20
23
  if (!Number.isFinite(value))
21
24
  return 0;
22
25
  return Math.min(1, Math.max(0, value));
23
26
  }
27
+ /***
28
+ Return the chroma multiplier used for a swatch step.
29
+ */
24
30
  function chromaMultiplierForStep(step) {
25
31
  if (step <= 200)
26
32
  return 0.55;
@@ -32,6 +38,9 @@ function chromaMultiplierForStep(step) {
32
38
  return 0.9;
33
39
  return 0.8;
34
40
  }
41
+ /***
42
+ Generate a full color swatch and diagnostics from a base color.
43
+ */
35
44
  export function generateColorSwatch(baseColor) {
36
45
  const baseOklch = parseHexToOklch(baseColor);
37
46
  const baseOffset = baseOklch.l - BASELINE_LIGHTNESS_BY_STEP[COLOR_SWATCH_BASE_STEP];
@@ -1 +1 @@
1
- {"version":3,"file":"swatches.js","sourceRoot":"","sources":["../src/swatches.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HexColor } from './hex';\nimport { deltaEoklch, oklchToHex, parseHexToOklch } from './internal-culori';\n\nexport const COLOR_SWATCH_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const;\nexport type ColorSwatchStep = (typeof COLOR_SWATCH_STEPS)[number];\n\nexport const COLOR_SWATCH_BASE_STEP = 500 as const;\n\nexport type ColorSwatch = Record<ColorSwatchStep, HexColor>;\n\nexport type ColorSwatchWarningCode =\n | 'weak_step'\n | 'weak_adjacent_delta'\n | 'limited_lightness_range';\n\nexport interface ColorSwatchWarning {\n code: ColorSwatchWarningCode;\n step?: ColorSwatchStep;\n message: string;\n deltaEFromBase?: number;\n}\n\nexport interface ColorSwatchDiagnostics {\n isUsable: boolean;\n warnings: readonly ColorSwatchWarning[];\n minAdjacentDelta: number;\n maxAdjacentDelta: number;\n lightnessRange: {\n min: number;\n max: number;\n };\n}\n\nconst BASELINE_LIGHTNESS_BY_STEP: Record<ColorSwatchStep, number> = {\n 50: 0.985,\n 100: 0.967,\n 200: 0.928,\n 300: 0.872,\n 400: 0.707,\n 500: 0.551,\n 600: 0.446,\n 700: 0.373,\n 800: 0.278,\n 900: 0.21,\n 950: 0.13,\n};\n\nconst MIN_USABLE_ADJACENT_DELTA = 0.012;\nconst MIN_USABLE_LIGHTNESS_RANGE = 0.35;\n\nfunction clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.min(1, Math.max(0, value));\n}\n\nfunction chromaMultiplierForStep(step: ColorSwatchStep): number {\n if (step <= 200) return 0.55;\n if (step <= 400) return 0.75;\n if (step === COLOR_SWATCH_BASE_STEP) return 1;\n if (step <= 700) return 0.9;\n return 0.8;\n}\n\nexport function generateColorSwatch(baseColor: HexColor): {\n swatch: ColorSwatch;\n diagnostics: ColorSwatchDiagnostics;\n} {\n const baseOklch = parseHexToOklch(baseColor);\n const baseOffset = baseOklch.l - BASELINE_LIGHTNESS_BY_STEP[COLOR_SWATCH_BASE_STEP];\n\n const warnings: ColorSwatchWarning[] = [];\n const swatch = {} as ColorSwatch;\n const lightnessEntries: number[] = [];\n const adjacentDeltas: number[] = [];\n\n for (const step of COLOR_SWATCH_STEPS) {\n if (step === COLOR_SWATCH_BASE_STEP) {\n swatch[step] = baseColor;\n lightnessEntries.push(baseOklch.l);\n continue;\n }\n\n const targetL = clamp01(BASELINE_LIGHTNESS_BY_STEP[step] + baseOffset);\n const targetC = clamp01(baseOklch.c * chromaMultiplierForStep(step));\n const target = { ...baseOklch, l: targetL, c: targetC };\n const hex = oklchToHex(target);\n swatch[step] = hex;\n\n const candidateOklch = parseHexToOklch(hex);\n lightnessEntries.push(candidateOklch.l);\n\n const deltaEFromBase = deltaEoklch(baseOklch, candidateOklch);\n if (deltaEFromBase < 0.02) {\n warnings.push({\n code: 'weak_step',\n step,\n deltaEFromBase,\n message: `Swatch step ${step} is visually close to the base color.`,\n });\n }\n }\n\n for (let index = 1; index < COLOR_SWATCH_STEPS.length; index++) {\n const previousStep = COLOR_SWATCH_STEPS[index - 1];\n const currentStep = COLOR_SWATCH_STEPS[index];\n if (previousStep === undefined || currentStep === undefined) continue;\n\n const previous = parseHexToOklch(swatch[previousStep]);\n const current = parseHexToOklch(swatch[currentStep]);\n const adjacentDelta = deltaEoklch(previous, current);\n adjacentDeltas.push(adjacentDelta);\n\n if (adjacentDelta < MIN_USABLE_ADJACENT_DELTA) {\n warnings.push({\n code: 'weak_adjacent_delta',\n step: currentStep,\n deltaEFromBase: adjacentDelta,\n message: `Swatch step ${currentStep} is visually close to adjacent step ${previousStep}.`,\n });\n }\n }\n\n const minAdjacentDelta = Math.min(...adjacentDeltas);\n const maxAdjacentDelta = Math.max(...adjacentDeltas);\n const minLightness = Math.min(...lightnessEntries);\n const maxLightness = Math.max(...lightnessEntries);\n const lightnessRange = maxLightness - minLightness;\n\n if (lightnessRange < MIN_USABLE_LIGHTNESS_RANGE) {\n warnings.push({\n code: 'limited_lightness_range',\n message: 'Generated swatch has a limited lightness range.',\n });\n }\n\n return {\n swatch,\n diagnostics: {\n isUsable:\n minAdjacentDelta >= MIN_USABLE_ADJACENT_DELTA &&\n lightnessRange >= MIN_USABLE_LIGHTNESS_RANGE,\n warnings,\n minAdjacentDelta,\n maxAdjacentDelta,\n lightnessRange: {\n min: minLightness,\n max: maxLightness,\n },\n },\n };\n}\n"]}
1
+ {"version":3,"file":"swatches.js","sourceRoot":"","sources":["../src/swatches.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HexColor } from './hex';\nimport { deltaEoklch, oklchToHex, parseHexToOklch } from './internal-culori';\n\nexport const COLOR_SWATCH_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const;\nexport type ColorSwatchStep = (typeof COLOR_SWATCH_STEPS)[number];\n\nexport const COLOR_SWATCH_BASE_STEP = 500 as const;\n\nexport type ColorSwatch = Record<ColorSwatchStep, HexColor>;\n\nexport type ColorSwatchWarningCode =\n | 'weak_step'\n | 'weak_adjacent_delta'\n | 'limited_lightness_range';\n\nexport interface ColorSwatchWarning {\n code: ColorSwatchWarningCode;\n step?: ColorSwatchStep;\n message: string;\n deltaEFromBase?: number;\n}\n\nexport interface ColorSwatchDiagnostics {\n isUsable: boolean;\n warnings: readonly ColorSwatchWarning[];\n minAdjacentDelta: number;\n maxAdjacentDelta: number;\n lightnessRange: {\n min: number;\n max: number;\n };\n}\n\nconst BASELINE_LIGHTNESS_BY_STEP: Record<ColorSwatchStep, number> = {\n 50: 0.985,\n 100: 0.967,\n 200: 0.928,\n 300: 0.872,\n 400: 0.707,\n 500: 0.551,\n 600: 0.446,\n 700: 0.373,\n 800: 0.278,\n 900: 0.21,\n 950: 0.13,\n};\n\nconst MIN_USABLE_ADJACENT_DELTA = 0.012;\nconst MIN_USABLE_LIGHTNESS_RANGE = 0.35;\n\n/***\n Clamp a number into the normalized zero-to-one range.\n*/\nfunction clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.min(1, Math.max(0, value));\n}\n\n/***\n Return the chroma multiplier used for a swatch step.\n*/\nfunction chromaMultiplierForStep(step: ColorSwatchStep): number {\n if (step <= 200) return 0.55;\n if (step <= 400) return 0.75;\n if (step === COLOR_SWATCH_BASE_STEP) return 1;\n if (step <= 700) return 0.9;\n return 0.8;\n}\n\n/***\n Generate a full color swatch and diagnostics from a base color.\n*/\nexport function generateColorSwatch(baseColor: HexColor): {\n swatch: ColorSwatch;\n diagnostics: ColorSwatchDiagnostics;\n} {\n const baseOklch = parseHexToOklch(baseColor);\n const baseOffset = baseOklch.l - BASELINE_LIGHTNESS_BY_STEP[COLOR_SWATCH_BASE_STEP];\n\n const warnings: ColorSwatchWarning[] = [];\n const swatch = {} as ColorSwatch;\n const lightnessEntries: number[] = [];\n const adjacentDeltas: number[] = [];\n\n for (const step of COLOR_SWATCH_STEPS) {\n if (step === COLOR_SWATCH_BASE_STEP) {\n swatch[step] = baseColor;\n lightnessEntries.push(baseOklch.l);\n continue;\n }\n\n const targetL = clamp01(BASELINE_LIGHTNESS_BY_STEP[step] + baseOffset);\n const targetC = clamp01(baseOklch.c * chromaMultiplierForStep(step));\n const target = { ...baseOklch, l: targetL, c: targetC };\n const hex = oklchToHex(target);\n swatch[step] = hex;\n\n const candidateOklch = parseHexToOklch(hex);\n lightnessEntries.push(candidateOklch.l);\n\n const deltaEFromBase = deltaEoklch(baseOklch, candidateOklch);\n if (deltaEFromBase < 0.02) {\n warnings.push({\n code: 'weak_step',\n step,\n deltaEFromBase,\n message: `Swatch step ${step} is visually close to the base color.`,\n });\n }\n }\n\n for (let index = 1; index < COLOR_SWATCH_STEPS.length; index++) {\n const previousStep = COLOR_SWATCH_STEPS[index - 1];\n const currentStep = COLOR_SWATCH_STEPS[index];\n if (previousStep === undefined || currentStep === undefined) continue;\n\n const previous = parseHexToOklch(swatch[previousStep]);\n const current = parseHexToOklch(swatch[currentStep]);\n const adjacentDelta = deltaEoklch(previous, current);\n adjacentDeltas.push(adjacentDelta);\n\n if (adjacentDelta < MIN_USABLE_ADJACENT_DELTA) {\n warnings.push({\n code: 'weak_adjacent_delta',\n step: currentStep,\n deltaEFromBase: adjacentDelta,\n message: `Swatch step ${currentStep} is visually close to adjacent step ${previousStep}.`,\n });\n }\n }\n\n const minAdjacentDelta = Math.min(...adjacentDeltas);\n const maxAdjacentDelta = Math.max(...adjacentDeltas);\n const minLightness = Math.min(...lightnessEntries);\n const maxLightness = Math.max(...lightnessEntries);\n const lightnessRange = maxLightness - minLightness;\n\n if (lightnessRange < MIN_USABLE_LIGHTNESS_RANGE) {\n warnings.push({\n code: 'limited_lightness_range',\n message: 'Generated swatch has a limited lightness range.',\n });\n }\n\n return {\n swatch,\n diagnostics: {\n isUsable:\n minAdjacentDelta >= MIN_USABLE_ADJACENT_DELTA &&\n lightnessRange >= MIN_USABLE_LIGHTNESS_RANGE,\n warnings,\n minAdjacentDelta,\n maxAdjacentDelta,\n lightnessRange: {\n min: minLightness,\n max: maxLightness,\n },\n },\n };\n}\n"]}
@@ -22,8 +22,17 @@ export interface GeneratedThemeModeColors {
22
22
  swatches: GeneratedThemeSwatches;
23
23
  neutral: GeneratedNeutralMetadata;
24
24
  }
25
+ /***
26
+ Parse the primary color configured for a theme mode.
27
+ */
25
28
  export declare function getThemeModePrimaryHex(mode: ThemeModeColorInput): HexColor;
29
+ /***
30
+ Generate harmony role colors, swatches, and neutral metadata for a theme mode.
31
+ */
26
32
  export declare function generateThemeModeColors(mode: ThemeModeColorInput): GeneratedThemeModeColors;
33
+ /***
34
+ Generate light and dark theme color outputs from theme color input.
35
+ */
27
36
  export declare function generateThemeColors(theme: ThemeColorInput): {
28
37
  light: GeneratedThemeModeColors;
29
38
  dark: GeneratedThemeModeColors;
@@ -1 +1 @@
1
- {"version":3,"file":"theme-colors.d.ts","sourceRoot":"","sources":["../src/theme-colors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAA6B,MAAM,WAAW,CAAC;AACvF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,KAAK,wBAAwB,EAAyB,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,IAAI,EAAE,mBAAmB,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,iBAAiB,EAAE,0BAA0B,CAAC;IAC9C,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,QAAQ,CAE1E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,GAAG,wBAAwB,CAgC3F;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG;IAC3D,KAAK,EAAE,wBAAwB,CAAC;IAChC,IAAI,EAAE,wBAAwB,CAAC;CAChC,CAKA"}
1
+ {"version":3,"file":"theme-colors.d.ts","sourceRoot":"","sources":["../src/theme-colors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAA6B,MAAM,WAAW,CAAC;AACvF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,KAAK,wBAAwB,EAAyB,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,IAAI,EAAE,mBAAmB,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,iBAAiB,EAAE,0BAA0B,CAAC;IAC9C,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAED;;EAEE;AACF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,QAAQ,CAE1E;AAED;;EAEE;AACF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,mBAAmB,GAAG,wBAAwB,CAgC3F;AAED;;EAEE;AACF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG;IAC3D,KAAK,EAAE,wBAAwB,CAAC;IAChC,IAAI,EAAE,wBAAwB,CAAC;CAChC,CAKA"}
@@ -2,9 +2,15 @@ import { generateHarmonyRoleColors } from './harmony';
2
2
  import { parseHexColorOrThrow } from './hex';
3
3
  import { generateNeutralSwatch } from './neutral';
4
4
  import { generateColorSwatch } from './swatches';
5
+ /***
6
+ Parse the primary color configured for a theme mode.
7
+ */
5
8
  export function getThemeModePrimaryHex(mode) {
6
9
  return parseHexColorOrThrow(mode.primaryColor);
7
10
  }
11
+ /***
12
+ Generate harmony role colors, swatches, and neutral metadata for a theme mode.
13
+ */
8
14
  export function generateThemeModeColors(mode) {
9
15
  const primaryHex = getThemeModePrimaryHex(mode);
10
16
  const harmonyRoleColors = generateHarmonyRoleColors(primaryHex, mode.harmony);
@@ -35,6 +41,9 @@ export function generateThemeModeColors(mode) {
35
41
  },
36
42
  };
37
43
  }
44
+ /***
45
+ Generate light and dark theme color outputs from theme color input.
46
+ */
38
47
  export function generateThemeColors(theme) {
39
48
  return {
40
49
  light: generateThemeModeColors(theme.light),
@@ -1 +1 @@
1
- {"version":3,"file":"theme-colors.js","sourceRoot":"","sources":["../src/theme-colors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAiC,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA0BnE,MAAM,UAAU,sBAAsB,CAAC,IAAyB;IAC9D,OAAO,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAAyB;IAC/D,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAE9E,MAAM,aAAa,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAChF,MAAM,eAAe,GAAG,iBAAiB,CAAC,SAAS;QACjD,CAAC,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,MAAM;QAC7D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,cAAc,GAAG,iBAAiB,CAAC,QAAQ;QAC/C,CAAC,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM;QAC5D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,UAAU;QACnD,CAAC,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM;QAC9D,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,OAAO,GAAG,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;IACzD,MAAM,QAAQ,GAA2B;QACvC,OAAO,EAAE,aAAa;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC;IAEF,OAAO;QACL,iBAAiB;QACjB,QAAQ;QACR,OAAO,EAAE;YACP,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAsB;IAIxD,OAAO;QACL,KAAK,EAAE,uBAAuB,CAAC,KAAK,CAAC,KAAK,CAAC;QAC3C,IAAI,EAAE,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC;KAC1C,CAAC;AACJ,CAAC","sourcesContent":["import { type GeneratedHarmonyRoleColors, generateHarmonyRoleColors } from './harmony';\nimport type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\nimport { type GeneratedNeutralMetadata, generateNeutralSwatch } from './neutral';\nimport { type ColorSwatch, generateColorSwatch } from './swatches';\n\nexport interface ThemeModeColorInput {\n primaryColor: string;\n harmony: GeneratedHarmonyRoleColors['harmony'];\n}\n\nexport interface ThemeColorInput {\n light: ThemeModeColorInput;\n dark: ThemeModeColorInput;\n}\n\nexport interface GeneratedThemeSwatches {\n primary: ColorSwatch;\n secondary?: ColorSwatch;\n tertiary?: ColorSwatch;\n quaternary?: ColorSwatch;\n neutral: ColorSwatch;\n}\n\nexport interface GeneratedThemeModeColors {\n harmonyRoleColors: GeneratedHarmonyRoleColors;\n swatches: GeneratedThemeSwatches;\n neutral: GeneratedNeutralMetadata;\n}\n\nexport function getThemeModePrimaryHex(mode: ThemeModeColorInput): HexColor {\n return parseHexColorOrThrow(mode.primaryColor);\n}\n\nexport function generateThemeModeColors(mode: ThemeModeColorInput): GeneratedThemeModeColors {\n const primaryHex = getThemeModePrimaryHex(mode);\n const harmonyRoleColors = generateHarmonyRoleColors(primaryHex, mode.harmony);\n\n const primarySwatch = generateColorSwatch(harmonyRoleColors.primary.hex).swatch;\n const secondarySwatch = harmonyRoleColors.secondary\n ? generateColorSwatch(harmonyRoleColors.secondary.hex).swatch\n : undefined;\n const tertiarySwatch = harmonyRoleColors.tertiary\n ? generateColorSwatch(harmonyRoleColors.tertiary.hex).swatch\n : undefined;\n const quaternarySwatch = harmonyRoleColors.quaternary\n ? generateColorSwatch(harmonyRoleColors.quaternary.hex).swatch\n : undefined;\n\n const neutral = generateNeutralSwatch(harmonyRoleColors);\n const swatches: GeneratedThemeSwatches = {\n primary: primarySwatch,\n neutral: neutral.neutral,\n ...(secondarySwatch ? { secondary: secondarySwatch } : {}),\n ...(tertiarySwatch ? { tertiary: tertiarySwatch } : {}),\n ...(quaternarySwatch ? { quaternary: quaternarySwatch } : {}),\n };\n\n return {\n harmonyRoleColors,\n swatches,\n neutral: {\n neutralKeyColor: neutral.neutralKeyColor,\n diagnostics: neutral.diagnostics,\n },\n };\n}\n\nexport function generateThemeColors(theme: ThemeColorInput): {\n light: GeneratedThemeModeColors;\n dark: GeneratedThemeModeColors;\n} {\n return {\n light: generateThemeModeColors(theme.light),\n dark: generateThemeModeColors(theme.dark),\n };\n}\n"]}
1
+ {"version":3,"file":"theme-colors.js","sourceRoot":"","sources":["../src/theme-colors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAiC,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA0BnE;;EAEE;AACF,MAAM,UAAU,sBAAsB,CAAC,IAAyB;IAC9D,OAAO,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AACjD,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,uBAAuB,CAAC,IAAyB;IAC/D,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAE9E,MAAM,aAAa,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAChF,MAAM,eAAe,GAAG,iBAAiB,CAAC,SAAS;QACjD,CAAC,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,MAAM;QAC7D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,cAAc,GAAG,iBAAiB,CAAC,QAAQ;QAC/C,CAAC,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM;QAC5D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,UAAU;QACnD,CAAC,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM;QAC9D,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,OAAO,GAAG,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;IACzD,MAAM,QAAQ,GAA2B;QACvC,OAAO,EAAE,aAAa;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAC;IAEF,OAAO;QACL,iBAAiB;QACjB,QAAQ;QACR,OAAO,EAAE;YACP,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC;KACF,CAAC;AACJ,CAAC;AAED;;EAEE;AACF,MAAM,UAAU,mBAAmB,CAAC,KAAsB;IAIxD,OAAO;QACL,KAAK,EAAE,uBAAuB,CAAC,KAAK,CAAC,KAAK,CAAC;QAC3C,IAAI,EAAE,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC;KAC1C,CAAC;AACJ,CAAC","sourcesContent":["import { type GeneratedHarmonyRoleColors, generateHarmonyRoleColors } from './harmony';\nimport type { HexColor } from './hex';\nimport { parseHexColorOrThrow } from './hex';\nimport { type GeneratedNeutralMetadata, generateNeutralSwatch } from './neutral';\nimport { type ColorSwatch, generateColorSwatch } from './swatches';\n\nexport interface ThemeModeColorInput {\n primaryColor: string;\n harmony: GeneratedHarmonyRoleColors['harmony'];\n}\n\nexport interface ThemeColorInput {\n light: ThemeModeColorInput;\n dark: ThemeModeColorInput;\n}\n\nexport interface GeneratedThemeSwatches {\n primary: ColorSwatch;\n secondary?: ColorSwatch;\n tertiary?: ColorSwatch;\n quaternary?: ColorSwatch;\n neutral: ColorSwatch;\n}\n\nexport interface GeneratedThemeModeColors {\n harmonyRoleColors: GeneratedHarmonyRoleColors;\n swatches: GeneratedThemeSwatches;\n neutral: GeneratedNeutralMetadata;\n}\n\n/***\n Parse the primary color configured for a theme mode.\n*/\nexport function getThemeModePrimaryHex(mode: ThemeModeColorInput): HexColor {\n return parseHexColorOrThrow(mode.primaryColor);\n}\n\n/***\n Generate harmony role colors, swatches, and neutral metadata for a theme mode.\n*/\nexport function generateThemeModeColors(mode: ThemeModeColorInput): GeneratedThemeModeColors {\n const primaryHex = getThemeModePrimaryHex(mode);\n const harmonyRoleColors = generateHarmonyRoleColors(primaryHex, mode.harmony);\n\n const primarySwatch = generateColorSwatch(harmonyRoleColors.primary.hex).swatch;\n const secondarySwatch = harmonyRoleColors.secondary\n ? generateColorSwatch(harmonyRoleColors.secondary.hex).swatch\n : undefined;\n const tertiarySwatch = harmonyRoleColors.tertiary\n ? generateColorSwatch(harmonyRoleColors.tertiary.hex).swatch\n : undefined;\n const quaternarySwatch = harmonyRoleColors.quaternary\n ? generateColorSwatch(harmonyRoleColors.quaternary.hex).swatch\n : undefined;\n\n const neutral = generateNeutralSwatch(harmonyRoleColors);\n const swatches: GeneratedThemeSwatches = {\n primary: primarySwatch,\n neutral: neutral.neutral,\n ...(secondarySwatch ? { secondary: secondarySwatch } : {}),\n ...(tertiarySwatch ? { tertiary: tertiarySwatch } : {}),\n ...(quaternarySwatch ? { quaternary: quaternarySwatch } : {}),\n };\n\n return {\n harmonyRoleColors,\n swatches,\n neutral: {\n neutralKeyColor: neutral.neutralKeyColor,\n diagnostics: neutral.diagnostics,\n },\n };\n}\n\n/***\n Generate light and dark theme color outputs from theme color input.\n*/\nexport function generateThemeColors(theme: ThemeColorInput): {\n light: GeneratedThemeModeColors;\n dark: GeneratedThemeModeColors;\n} {\n return {\n light: generateThemeModeColors(theme.light),\n dark: generateThemeModeColors(theme.dark),\n };\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/color-theory",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.3",
5
5
  "description": "Standalone color theory, harmony, swatch, contrast, and theme color generation utilities.",
6
6
  "homepage": "https://github.com/ankhorage/color-theory#readme",
7
7
  "bugs": {
@@ -29,7 +29,6 @@
29
29
  },
30
30
  "files": [
31
31
  "dist",
32
- "src",
33
32
  "package.json",
34
33
  "README.md",
35
34
  "CHANGELOG.md",
@@ -37,8 +36,8 @@
37
36
  ],
38
37
  "exports": {
39
38
  ".": {
40
- "react-native": "./src/index.ts",
41
- "browser": "./src/index.ts",
39
+ "react-native": "./dist/index.js",
40
+ "browser": "./dist/index.js",
42
41
  "types": "./dist/index.d.ts",
43
42
  "import": "./dist/index.js",
44
43
  "default": "./dist/index.js"