@ankhorage/color-theory 0.0.6 → 0.0.8
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 +12 -0
- package/README.md +1 -1
- package/dist/neutral.d.ts.map +1 -1
- package/dist/neutral.js +2 -2
- package/dist/neutral.js.map +1 -1
- package/dist/swatches.d.ts +1 -0
- package/dist/swatches.d.ts.map +1 -1
- package/dist/swatches.js +3 -2
- package/dist/swatches.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ankhorage/color-theory
|
|
2
2
|
|
|
3
|
+
## 0.0.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cd67ac5: Align generated neutral key colors with the swatch base-step lightness so adjacent light neutral steps remain distinct.
|
|
8
|
+
|
|
9
|
+
## 0.0.7
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- f3945f3: Update packages
|
|
14
|
+
|
|
3
15
|
## 0.0.6
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/color-theory
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Standalone color theory, harmony, swatch, contrast, and theme color generation utilities.
|
|
9
9
|
|
package/dist/neutral.d.ts.map
CHANGED
|
@@ -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,
|
|
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,EAEL,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAE5B,MAAM,YAAY,CAAC;AAEpB,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,5 +1,5 @@
|
|
|
1
1
|
import { normalizeHueDegrees, oklchToHex, parseHexToOklch } from './internal-culori';
|
|
2
|
-
import { generateColorSwatch } from './swatches';
|
|
2
|
+
import { COLOR_SWATCH_BASE_LIGHTNESS, generateColorSwatch, } from './swatches';
|
|
3
3
|
export const MIN_HUEFUL_CHROMA = 0.015;
|
|
4
4
|
/***
|
|
5
5
|
Pick the harmony color that should tint the generated neutral swatch.
|
|
@@ -40,7 +40,7 @@ export function generateNeutralSwatch(roleColors) {
|
|
|
40
40
|
const neutralChroma = preferGray ? 0 : clampNumber(sourceChroma * 0.06, 0.004, 0.012);
|
|
41
41
|
const neutralKeyColor = oklchToHex({
|
|
42
42
|
mode: 'oklch',
|
|
43
|
-
l:
|
|
43
|
+
l: COLOR_SWATCH_BASE_LIGHTNESS,
|
|
44
44
|
c: neutralChroma,
|
|
45
45
|
h: neutralHue,
|
|
46
46
|
});
|
package/dist/neutral.js.map
CHANGED
|
@@ -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,
|
|
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,EACL,2BAA2B,EAG3B,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,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,2BAA2B;QAC9B,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 {\n COLOR_SWATCH_BASE_LIGHTNESS,\n type ColorSwatch,\n type ColorSwatchDiagnostics,\n generateColorSwatch,\n} 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: COLOR_SWATCH_BASE_LIGHTNESS,\n c: neutralChroma,\n h: neutralHue,\n });\n\n const { swatch, diagnostics } = generateColorSwatch(neutralKeyColor);\n\n return { neutralKeyColor, neutral: swatch, diagnostics };\n}\n"]}
|
package/dist/swatches.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { HexColor } from './hex';
|
|
|
2
2
|
export declare const COLOR_SWATCH_STEPS: readonly [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
3
3
|
export type ColorSwatchStep = (typeof COLOR_SWATCH_STEPS)[number];
|
|
4
4
|
export declare const COLOR_SWATCH_BASE_STEP: 500;
|
|
5
|
+
export declare const COLOR_SWATCH_BASE_LIGHTNESS = 0.551;
|
|
5
6
|
export type ColorSwatch = Record<ColorSwatchStep, HexColor>;
|
|
6
7
|
export type ColorSwatchWarningCode = 'weak_step' | 'weak_adjacent_delta' | 'limited_lightness_range';
|
|
7
8
|
export interface ColorSwatchWarning {
|
package/dist/swatches.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AACnD,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AAEjD,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
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { deltaEoklch, oklchToHex, parseHexToOklch } from './internal-culori';
|
|
2
2
|
export const COLOR_SWATCH_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
|
|
3
3
|
export const COLOR_SWATCH_BASE_STEP = 500;
|
|
4
|
+
export const COLOR_SWATCH_BASE_LIGHTNESS = 0.551;
|
|
4
5
|
const BASELINE_LIGHTNESS_BY_STEP = {
|
|
5
6
|
50: 0.985,
|
|
6
7
|
100: 0.967,
|
|
7
8
|
200: 0.928,
|
|
8
9
|
300: 0.872,
|
|
9
10
|
400: 0.707,
|
|
10
|
-
|
|
11
|
+
[COLOR_SWATCH_BASE_STEP]: COLOR_SWATCH_BASE_LIGHTNESS,
|
|
11
12
|
600: 0.446,
|
|
12
13
|
700: 0.373,
|
|
13
14
|
800: 0.278,
|
|
@@ -43,7 +44,7 @@ function chromaMultiplierForStep(step) {
|
|
|
43
44
|
*/
|
|
44
45
|
export function generateColorSwatch(baseColor) {
|
|
45
46
|
const baseOklch = parseHexToOklch(baseColor);
|
|
46
|
-
const baseOffset = baseOklch.l -
|
|
47
|
+
const baseOffset = baseOklch.l - COLOR_SWATCH_BASE_LIGHTNESS;
|
|
47
48
|
const warnings = [];
|
|
48
49
|
const swatch = {};
|
|
49
50
|
const lightnessEntries = [];
|
package/dist/swatches.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swatches.js","sourceRoot":"","sources":["../src/swatches.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE7E,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAGlG,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAY,CAAC;
|
|
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;\nexport const COLOR_SWATCH_BASE_LIGHTNESS = 0.551;\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 [COLOR_SWATCH_BASE_STEP]: COLOR_SWATCH_BASE_LIGHTNESS,\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 - COLOR_SWATCH_BASE_LIGHTNESS;\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"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/color-theory",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.8",
|
|
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": {
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"version-packages": "changeset version"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@ankhorage/devtools": "^1.0.
|
|
76
|
+
"@ankhorage/devtools": "^1.0.6",
|
|
77
77
|
"@ankhorage/paradox": "^0.1.6",
|
|
78
78
|
"@changesets/cli": "^2.31.0",
|
|
79
79
|
"@types/bun": "^1.3.13",
|