colorizr 5.0.1 → 5.1.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.d.mts +70 -6
- package/dist/index.d.ts +70 -6
- package/dist/index.js +162 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -11
- package/src/index.ts +1 -1
- package/src/scale.ts +397 -70
package/dist/index.d.mts
CHANGED
|
@@ -921,6 +921,18 @@ declare function saturate(input: string, amount: number, format?: ColorType): st
|
|
|
921
921
|
|
|
922
922
|
type ScaleMode = 'light' | 'dark';
|
|
923
923
|
type ScaleVariant = 'deep' | 'neutral' | 'pastel' | 'subtle' | 'vibrant';
|
|
924
|
+
/**
|
|
925
|
+
* Parabolic chroma curve with a movable center.
|
|
926
|
+
*/
|
|
927
|
+
interface ScaleChromaPeak {
|
|
928
|
+
/** Blend between constant chroma (0) and the full parabola (1). */
|
|
929
|
+
amount: number;
|
|
930
|
+
/**
|
|
931
|
+
* Lightness (exclusive 0-1) where chroma stays at full base.
|
|
932
|
+
* @default 0.5
|
|
933
|
+
*/
|
|
934
|
+
peak?: number;
|
|
935
|
+
}
|
|
924
936
|
/**
|
|
925
937
|
* Options for generating a color scale.
|
|
926
938
|
*
|
|
@@ -931,14 +943,29 @@ type ScaleVariant = 'deep' | 'neutral' | 'pastel' | 'subtle' | 'vibrant';
|
|
|
931
943
|
*/
|
|
932
944
|
interface ScaleOptions {
|
|
933
945
|
/**
|
|
934
|
-
* Controls chroma
|
|
946
|
+
* Controls how chroma flows across the scale. Three forms:
|
|
947
|
+
*
|
|
948
|
+
* Scalar (0-1) — parabolic blend centered at mid-lightness:
|
|
935
949
|
* - 0: Constant chroma across all steps (default).
|
|
936
|
-
* - 1:
|
|
937
|
-
*
|
|
950
|
+
* - 1: Full parabola — peak chroma at mid-lightness, reduced toward extremes.
|
|
951
|
+
* Shorthand for `{ amount: x }`.
|
|
952
|
+
*
|
|
953
|
+
* `{ amount, peak }` — the same parabola with a movable center.
|
|
954
|
+
* `peak` is the lightness (exclusive 0-1) that keeps full base chroma;
|
|
955
|
+
* sides away from it are reduced. Defaults to 0.5.
|
|
956
|
+
*
|
|
957
|
+
* `{ low, high }` — fractions (0-1) of the maximum P3 chroma at each step,
|
|
958
|
+
* blended from `low` (50-end) through the input color's own fraction at the
|
|
959
|
+
* lock/middle step to `high` (950-end). NOT equivalent to the scalar form —
|
|
960
|
+
* it is a different model: values set chroma relative to the gamut ceiling
|
|
961
|
+
* instead of bending the input's chroma. Being gamut-relative, the input's
|
|
962
|
+
* chroma matters only at the anchor step; an achromatic input collapses the
|
|
963
|
+
* anchor fraction to 0, yielding a V-shaped, colored scale (full at the ends,
|
|
964
|
+
* gray at the anchor).
|
|
938
965
|
*
|
|
939
966
|
* @default 0
|
|
940
967
|
*/
|
|
941
|
-
chromaCurve?: number;
|
|
968
|
+
chromaCurve?: number | ScaleChromaPeak | ScaleRange;
|
|
942
969
|
/**
|
|
943
970
|
* Output color format.
|
|
944
971
|
*
|
|
@@ -947,14 +974,36 @@ interface ScaleOptions {
|
|
|
947
974
|
* If not specified, the output will match the format of the input color.
|
|
948
975
|
*/
|
|
949
976
|
format?: ColorType;
|
|
977
|
+
/**
|
|
978
|
+
* Hue rotation across the scale, in degrees.
|
|
979
|
+
*
|
|
980
|
+
* A scalar `x` is shorthand for `{ low: -x, high: x }`.
|
|
981
|
+
* `low` shifts the low-key end (50), `high` the high-key end (950); shifts
|
|
982
|
+
* blend to 0° at the lock step (or the middle step when not locked), so the
|
|
983
|
+
* input hue is preserved there. The per-step gamut clamp follows the
|
|
984
|
+
* shifted hue.
|
|
985
|
+
*
|
|
986
|
+
* Values must be within [-180, 180].
|
|
987
|
+
*
|
|
988
|
+
* @default 0
|
|
989
|
+
*/
|
|
990
|
+
hueShift?: number | ScaleRange;
|
|
950
991
|
/**
|
|
951
992
|
* The lightness tuning factor for the scale.
|
|
952
993
|
* - 1: Linear lightness distribution.
|
|
953
994
|
* - >1: Lighter tones are emphasized.
|
|
954
995
|
* - <1: Darker tones are emphasized.
|
|
996
|
+
*
|
|
997
|
+
* A scalar `x` is shorthand for `{ low: x, high: x }`. With `{ low, high }`
|
|
998
|
+
* each half of the scale gets its own exponent: `low` shapes the low-key
|
|
999
|
+
* half (toward 50), `high` the high-key half (toward 950), split at the
|
|
1000
|
+
* lock step when locked or blended continuously when not.
|
|
1001
|
+
* Reference palettes typically fit `{ low: 1.5, high: 1 }`.
|
|
1002
|
+
*
|
|
1003
|
+
* Values must be greater than 0.
|
|
955
1004
|
* @default 1.5
|
|
956
1005
|
*/
|
|
957
|
-
lightnessCurve?: number;
|
|
1006
|
+
lightnessCurve?: number | ScaleRange;
|
|
958
1007
|
/**
|
|
959
1008
|
* Lock input color's lightness at specific step position.
|
|
960
1009
|
*
|
|
@@ -965,6 +1014,11 @@ interface ScaleOptions {
|
|
|
965
1014
|
* Must be a valid step key for the current step count.
|
|
966
1015
|
* Default step keys (11 steps): 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950.
|
|
967
1016
|
* Step keys vary based on the `steps` option (3-20 steps supported).
|
|
1017
|
+
*
|
|
1018
|
+
* When locked at the first or last step, only one side of the scale remains,
|
|
1019
|
+
* so the unused-side value (`low`/`high`) of asymmetric options
|
|
1020
|
+
* (`hueShift`, `lightnessCurve`, endpoint `chromaCurve`) has no effect and
|
|
1021
|
+
* emits a warning.
|
|
968
1022
|
*/
|
|
969
1023
|
lock?: number;
|
|
970
1024
|
/**
|
|
@@ -1022,6 +1076,16 @@ interface ScaleOptions {
|
|
|
1022
1076
|
*/
|
|
1023
1077
|
variant?: ScaleVariant;
|
|
1024
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Endpoint values for an asymmetric scale option.
|
|
1081
|
+
*
|
|
1082
|
+
* `low` applies at the low-key end (50), `high` at the high-key end (950),
|
|
1083
|
+
* regardless of `mode` — keys, not lightness, define the ends.
|
|
1084
|
+
*/
|
|
1085
|
+
interface ScaleRange {
|
|
1086
|
+
high: number;
|
|
1087
|
+
low: number;
|
|
1088
|
+
}
|
|
1025
1089
|
/**
|
|
1026
1090
|
* Generate a scale of colors based on the input color.
|
|
1027
1091
|
*
|
|
@@ -1078,4 +1142,4 @@ declare function toGamut(input: string, format?: ColorType): string;
|
|
|
1078
1142
|
*/
|
|
1079
1143
|
declare function transparentize(input: string, alpha: number, format?: ColorType): string;
|
|
1080
1144
|
|
|
1081
|
-
export { APCA_VERSION, type Analysis, CLMS_TO_OKLAB, type ColorKeysTuple, type ColorModel, type ColorModelKey, type ColorModelKeys, type ColorReturn, type ColorTuple, type ColorType, type ColorTypeInput, type ColorValue, type ColorizrOptions, type ConverterParameters, DEG2RAD, DELTA_E_JND, type FormatCSSOptions, GAMUT_EPSILON, type HEX, type HSL, type HueMode, type LAB, type LCH, LMS_TO_LRGB, LRGB_TO_LMS, type MixOptions, OKLAB_TO_CLMS, P3_TO_SRGB, P3_TO_XYZ, PRECISION, type PaletteOptions, type PlainObject, RAD2DEG, type RGB, type ReadableColorMethod, type ReadableColorOptions, SRGB_TO_P3, type ScaleOptions, type ScaleVariant, type Scheme, type SchemeOptions, XYZ_TO_SRGB, addAlphaToHex, apcaContrast, brightnessDifference, chroma, colorDifference, compare, contrast, convertAlphaToHex, convertCSS, darken, Colorizr as default, deltaE, desaturate, extractAlphaFromHex, extractColorParts, formatCSS, formatHex, getColorType, getP3MaxChroma, getP3MaxColor, getScaleStepKeys, grayscale, hex2hsl, hex2oklab, hex2oklch, hex2rgb, hsl2hex, hsl2oklab, hsl2oklch, hsl2rgb, invert, isHSL, isHex, isInGamut, isLAB, isLCH, isRGB, isValidColor, lighten, luminance, mix, name, oklab2hex, oklab2hsl, oklab2oklch, oklab2rgb, oklabToLinearP3, oklabToLinearSRGB, oklch2hex, oklch2hsl, oklch2oklab, oklch2rgb, opacify, opacity, palette, parseCSS, random, readableColor, removeAlphaFromHex, rgb2hex, rgb2hsl, rgb2oklab, rgb2oklch, rotate, saturate, scale, scheme, srgbGammaDecode, srgbGammaEncode, toGamut, transparentize };
|
|
1145
|
+
export { APCA_VERSION, type Analysis, CLMS_TO_OKLAB, type ColorKeysTuple, type ColorModel, type ColorModelKey, type ColorModelKeys, type ColorReturn, type ColorTuple, type ColorType, type ColorTypeInput, type ColorValue, type ColorizrOptions, type ConverterParameters, DEG2RAD, DELTA_E_JND, type FormatCSSOptions, GAMUT_EPSILON, type HEX, type HSL, type HueMode, type LAB, type LCH, LMS_TO_LRGB, LRGB_TO_LMS, type MixOptions, OKLAB_TO_CLMS, P3_TO_SRGB, P3_TO_XYZ, PRECISION, type PaletteOptions, type PlainObject, RAD2DEG, type RGB, type ReadableColorMethod, type ReadableColorOptions, SRGB_TO_P3, type ScaleChromaPeak, type ScaleOptions, type ScaleRange, type ScaleVariant, type Scheme, type SchemeOptions, XYZ_TO_SRGB, addAlphaToHex, apcaContrast, brightnessDifference, chroma, colorDifference, compare, contrast, convertAlphaToHex, convertCSS, darken, Colorizr as default, deltaE, desaturate, extractAlphaFromHex, extractColorParts, formatCSS, formatHex, getColorType, getP3MaxChroma, getP3MaxColor, getScaleStepKeys, grayscale, hex2hsl, hex2oklab, hex2oklch, hex2rgb, hsl2hex, hsl2oklab, hsl2oklch, hsl2rgb, invert, isHSL, isHex, isInGamut, isLAB, isLCH, isRGB, isValidColor, lighten, luminance, mix, name, oklab2hex, oklab2hsl, oklab2oklch, oklab2rgb, oklabToLinearP3, oklabToLinearSRGB, oklch2hex, oklch2hsl, oklch2oklab, oklch2rgb, opacify, opacity, palette, parseCSS, random, readableColor, removeAlphaFromHex, rgb2hex, rgb2hsl, rgb2oklab, rgb2oklch, rotate, saturate, scale, scheme, srgbGammaDecode, srgbGammaEncode, toGamut, transparentize };
|
package/dist/index.d.ts
CHANGED
|
@@ -921,6 +921,18 @@ declare function saturate(input: string, amount: number, format?: ColorType): st
|
|
|
921
921
|
|
|
922
922
|
type ScaleMode = 'light' | 'dark';
|
|
923
923
|
type ScaleVariant = 'deep' | 'neutral' | 'pastel' | 'subtle' | 'vibrant';
|
|
924
|
+
/**
|
|
925
|
+
* Parabolic chroma curve with a movable center.
|
|
926
|
+
*/
|
|
927
|
+
interface ScaleChromaPeak {
|
|
928
|
+
/** Blend between constant chroma (0) and the full parabola (1). */
|
|
929
|
+
amount: number;
|
|
930
|
+
/**
|
|
931
|
+
* Lightness (exclusive 0-1) where chroma stays at full base.
|
|
932
|
+
* @default 0.5
|
|
933
|
+
*/
|
|
934
|
+
peak?: number;
|
|
935
|
+
}
|
|
924
936
|
/**
|
|
925
937
|
* Options for generating a color scale.
|
|
926
938
|
*
|
|
@@ -931,14 +943,29 @@ type ScaleVariant = 'deep' | 'neutral' | 'pastel' | 'subtle' | 'vibrant';
|
|
|
931
943
|
*/
|
|
932
944
|
interface ScaleOptions {
|
|
933
945
|
/**
|
|
934
|
-
* Controls chroma
|
|
946
|
+
* Controls how chroma flows across the scale. Three forms:
|
|
947
|
+
*
|
|
948
|
+
* Scalar (0-1) — parabolic blend centered at mid-lightness:
|
|
935
949
|
* - 0: Constant chroma across all steps (default).
|
|
936
|
-
* - 1:
|
|
937
|
-
*
|
|
950
|
+
* - 1: Full parabola — peak chroma at mid-lightness, reduced toward extremes.
|
|
951
|
+
* Shorthand for `{ amount: x }`.
|
|
952
|
+
*
|
|
953
|
+
* `{ amount, peak }` — the same parabola with a movable center.
|
|
954
|
+
* `peak` is the lightness (exclusive 0-1) that keeps full base chroma;
|
|
955
|
+
* sides away from it are reduced. Defaults to 0.5.
|
|
956
|
+
*
|
|
957
|
+
* `{ low, high }` — fractions (0-1) of the maximum P3 chroma at each step,
|
|
958
|
+
* blended from `low` (50-end) through the input color's own fraction at the
|
|
959
|
+
* lock/middle step to `high` (950-end). NOT equivalent to the scalar form —
|
|
960
|
+
* it is a different model: values set chroma relative to the gamut ceiling
|
|
961
|
+
* instead of bending the input's chroma. Being gamut-relative, the input's
|
|
962
|
+
* chroma matters only at the anchor step; an achromatic input collapses the
|
|
963
|
+
* anchor fraction to 0, yielding a V-shaped, colored scale (full at the ends,
|
|
964
|
+
* gray at the anchor).
|
|
938
965
|
*
|
|
939
966
|
* @default 0
|
|
940
967
|
*/
|
|
941
|
-
chromaCurve?: number;
|
|
968
|
+
chromaCurve?: number | ScaleChromaPeak | ScaleRange;
|
|
942
969
|
/**
|
|
943
970
|
* Output color format.
|
|
944
971
|
*
|
|
@@ -947,14 +974,36 @@ interface ScaleOptions {
|
|
|
947
974
|
* If not specified, the output will match the format of the input color.
|
|
948
975
|
*/
|
|
949
976
|
format?: ColorType;
|
|
977
|
+
/**
|
|
978
|
+
* Hue rotation across the scale, in degrees.
|
|
979
|
+
*
|
|
980
|
+
* A scalar `x` is shorthand for `{ low: -x, high: x }`.
|
|
981
|
+
* `low` shifts the low-key end (50), `high` the high-key end (950); shifts
|
|
982
|
+
* blend to 0° at the lock step (or the middle step when not locked), so the
|
|
983
|
+
* input hue is preserved there. The per-step gamut clamp follows the
|
|
984
|
+
* shifted hue.
|
|
985
|
+
*
|
|
986
|
+
* Values must be within [-180, 180].
|
|
987
|
+
*
|
|
988
|
+
* @default 0
|
|
989
|
+
*/
|
|
990
|
+
hueShift?: number | ScaleRange;
|
|
950
991
|
/**
|
|
951
992
|
* The lightness tuning factor for the scale.
|
|
952
993
|
* - 1: Linear lightness distribution.
|
|
953
994
|
* - >1: Lighter tones are emphasized.
|
|
954
995
|
* - <1: Darker tones are emphasized.
|
|
996
|
+
*
|
|
997
|
+
* A scalar `x` is shorthand for `{ low: x, high: x }`. With `{ low, high }`
|
|
998
|
+
* each half of the scale gets its own exponent: `low` shapes the low-key
|
|
999
|
+
* half (toward 50), `high` the high-key half (toward 950), split at the
|
|
1000
|
+
* lock step when locked or blended continuously when not.
|
|
1001
|
+
* Reference palettes typically fit `{ low: 1.5, high: 1 }`.
|
|
1002
|
+
*
|
|
1003
|
+
* Values must be greater than 0.
|
|
955
1004
|
* @default 1.5
|
|
956
1005
|
*/
|
|
957
|
-
lightnessCurve?: number;
|
|
1006
|
+
lightnessCurve?: number | ScaleRange;
|
|
958
1007
|
/**
|
|
959
1008
|
* Lock input color's lightness at specific step position.
|
|
960
1009
|
*
|
|
@@ -965,6 +1014,11 @@ interface ScaleOptions {
|
|
|
965
1014
|
* Must be a valid step key for the current step count.
|
|
966
1015
|
* Default step keys (11 steps): 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950.
|
|
967
1016
|
* Step keys vary based on the `steps` option (3-20 steps supported).
|
|
1017
|
+
*
|
|
1018
|
+
* When locked at the first or last step, only one side of the scale remains,
|
|
1019
|
+
* so the unused-side value (`low`/`high`) of asymmetric options
|
|
1020
|
+
* (`hueShift`, `lightnessCurve`, endpoint `chromaCurve`) has no effect and
|
|
1021
|
+
* emits a warning.
|
|
968
1022
|
*/
|
|
969
1023
|
lock?: number;
|
|
970
1024
|
/**
|
|
@@ -1022,6 +1076,16 @@ interface ScaleOptions {
|
|
|
1022
1076
|
*/
|
|
1023
1077
|
variant?: ScaleVariant;
|
|
1024
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Endpoint values for an asymmetric scale option.
|
|
1081
|
+
*
|
|
1082
|
+
* `low` applies at the low-key end (50), `high` at the high-key end (950),
|
|
1083
|
+
* regardless of `mode` — keys, not lightness, define the ends.
|
|
1084
|
+
*/
|
|
1085
|
+
interface ScaleRange {
|
|
1086
|
+
high: number;
|
|
1087
|
+
low: number;
|
|
1088
|
+
}
|
|
1025
1089
|
/**
|
|
1026
1090
|
* Generate a scale of colors based on the input color.
|
|
1027
1091
|
*
|
|
@@ -1078,4 +1142,4 @@ declare function toGamut(input: string, format?: ColorType): string;
|
|
|
1078
1142
|
*/
|
|
1079
1143
|
declare function transparentize(input: string, alpha: number, format?: ColorType): string;
|
|
1080
1144
|
|
|
1081
|
-
export { APCA_VERSION, type Analysis, CLMS_TO_OKLAB, type ColorKeysTuple, type ColorModel, type ColorModelKey, type ColorModelKeys, type ColorReturn, type ColorTuple, type ColorType, type ColorTypeInput, type ColorValue, type ColorizrOptions, type ConverterParameters, DEG2RAD, DELTA_E_JND, type FormatCSSOptions, GAMUT_EPSILON, type HEX, type HSL, type HueMode, type LAB, type LCH, LMS_TO_LRGB, LRGB_TO_LMS, type MixOptions, OKLAB_TO_CLMS, P3_TO_SRGB, P3_TO_XYZ, PRECISION, type PaletteOptions, type PlainObject, RAD2DEG, type RGB, type ReadableColorMethod, type ReadableColorOptions, SRGB_TO_P3, type ScaleOptions, type ScaleVariant, type Scheme, type SchemeOptions, XYZ_TO_SRGB, addAlphaToHex, apcaContrast, brightnessDifference, chroma, colorDifference, compare, contrast, convertAlphaToHex, convertCSS, darken, Colorizr as default, deltaE, desaturate, extractAlphaFromHex, extractColorParts, formatCSS, formatHex, getColorType, getP3MaxChroma, getP3MaxColor, getScaleStepKeys, grayscale, hex2hsl, hex2oklab, hex2oklch, hex2rgb, hsl2hex, hsl2oklab, hsl2oklch, hsl2rgb, invert, isHSL, isHex, isInGamut, isLAB, isLCH, isRGB, isValidColor, lighten, luminance, mix, name, oklab2hex, oklab2hsl, oklab2oklch, oklab2rgb, oklabToLinearP3, oklabToLinearSRGB, oklch2hex, oklch2hsl, oklch2oklab, oklch2rgb, opacify, opacity, palette, parseCSS, random, readableColor, removeAlphaFromHex, rgb2hex, rgb2hsl, rgb2oklab, rgb2oklch, rotate, saturate, scale, scheme, srgbGammaDecode, srgbGammaEncode, toGamut, transparentize };
|
|
1145
|
+
export { APCA_VERSION, type Analysis, CLMS_TO_OKLAB, type ColorKeysTuple, type ColorModel, type ColorModelKey, type ColorModelKeys, type ColorReturn, type ColorTuple, type ColorType, type ColorTypeInput, type ColorValue, type ColorizrOptions, type ConverterParameters, DEG2RAD, DELTA_E_JND, type FormatCSSOptions, GAMUT_EPSILON, type HEX, type HSL, type HueMode, type LAB, type LCH, LMS_TO_LRGB, LRGB_TO_LMS, type MixOptions, OKLAB_TO_CLMS, P3_TO_SRGB, P3_TO_XYZ, PRECISION, type PaletteOptions, type PlainObject, RAD2DEG, type RGB, type ReadableColorMethod, type ReadableColorOptions, SRGB_TO_P3, type ScaleChromaPeak, type ScaleOptions, type ScaleRange, type ScaleVariant, type Scheme, type SchemeOptions, XYZ_TO_SRGB, addAlphaToHex, apcaContrast, brightnessDifference, chroma, colorDifference, compare, contrast, convertAlphaToHex, convertCSS, darken, Colorizr as default, deltaE, desaturate, extractAlphaFromHex, extractColorParts, formatCSS, formatHex, getColorType, getP3MaxChroma, getP3MaxColor, getScaleStepKeys, grayscale, hex2hsl, hex2oklab, hex2oklch, hex2rgb, hsl2hex, hsl2oklab, hsl2oklch, hsl2rgb, invert, isHSL, isHex, isInGamut, isLAB, isLCH, isRGB, isValidColor, lighten, luminance, mix, name, oklab2hex, oklab2hsl, oklab2oklch, oklab2rgb, oklabToLinearP3, oklabToLinearSRGB, oklch2hex, oklch2hsl, oklch2oklab, oklch2rgb, opacify, opacity, palette, parseCSS, random, readableColor, removeAlphaFromHex, rgb2hex, rgb2hsl, rgb2oklab, rgb2oklch, rotate, saturate, scale, scheme, srgbGammaDecode, srgbGammaEncode, toGamut, transparentize };
|
package/dist/index.js
CHANGED
|
@@ -2177,11 +2177,53 @@ var chromaScale = {
|
|
|
2177
2177
|
subtle: 0.2,
|
|
2178
2178
|
vibrant: 1.25
|
|
2179
2179
|
};
|
|
2180
|
+
function assertScaleObject(value, label) {
|
|
2181
|
+
invariant(isPlainObject(value), `${label} must be a number or an object.`);
|
|
2182
|
+
}
|
|
2183
|
+
function computeLightnessMapWithLock(lock, inputLightness, keys, lightnessCurve, maxLightness, minLightness, mode) {
|
|
2184
|
+
const lightnessMap = {};
|
|
2185
|
+
const lockIndex = keys.indexOf(lock);
|
|
2186
|
+
lightnessMap[lock] = inputLightness;
|
|
2187
|
+
if (lockIndex > 0) {
|
|
2188
|
+
const target = mode === "light" ? maxLightness : minLightness;
|
|
2189
|
+
for (let index = 0; index < lockIndex; index++) {
|
|
2190
|
+
const t = index / lockIndex;
|
|
2191
|
+
lightnessMap[keys[index]] = target + (inputLightness - target) * t ** lightnessCurve.low;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
const remaining = keys.length - 1 - lockIndex;
|
|
2195
|
+
if (remaining > 0) {
|
|
2196
|
+
const target = mode === "light" ? minLightness : maxLightness;
|
|
2197
|
+
for (let index = lockIndex + 1; index < keys.length; index++) {
|
|
2198
|
+
const t = (index - lockIndex) / remaining;
|
|
2199
|
+
lightnessMap[keys[index]] = inputLightness + (target - inputLightness) * t ** lightnessCurve.high;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return lightnessMap;
|
|
2203
|
+
}
|
|
2204
|
+
function computeLightnessMapWithoutLock(keys, lightnessCurve, maxLightness, minLightness, mode) {
|
|
2205
|
+
const lightnessMap = {};
|
|
2206
|
+
for (let index = 0; index < keys.length; index++) {
|
|
2207
|
+
const tBase = index / (keys.length - 1);
|
|
2208
|
+
const exponent = lightnessCurve.low + (lightnessCurve.high - lightnessCurve.low) * tBase;
|
|
2209
|
+
const t = tBase ** exponent;
|
|
2210
|
+
lightnessMap[keys[index]] = mode === "light" ? maxLightness - (maxLightness - minLightness) * t : minLightness + (maxLightness - minLightness) * t;
|
|
2211
|
+
}
|
|
2212
|
+
return lightnessMap;
|
|
2213
|
+
}
|
|
2214
|
+
function computeStepHue(hue, hueShift, index, anchorIndex, keysLength) {
|
|
2215
|
+
if (hueShift.low === 0 && hueShift.high === 0 || index === anchorIndex) {
|
|
2216
|
+
return hue;
|
|
2217
|
+
}
|
|
2218
|
+
const shift = index < anchorIndex ? hueShift.low * (1 - index / anchorIndex) : hueShift.high * ((index - anchorIndex) / (keysLength - 1 - anchorIndex));
|
|
2219
|
+
return (hue + shift + 360) % 360;
|
|
2220
|
+
}
|
|
2180
2221
|
function generatePalette(options) {
|
|
2181
2222
|
const {
|
|
2182
2223
|
baseChroma,
|
|
2183
2224
|
chromaCurve,
|
|
2184
2225
|
hue,
|
|
2226
|
+
hueShift,
|
|
2185
2227
|
inputLightness,
|
|
2186
2228
|
keys,
|
|
2187
2229
|
lightnessCurve,
|
|
@@ -2191,52 +2233,113 @@ function generatePalette(options) {
|
|
|
2191
2233
|
mode
|
|
2192
2234
|
} = options;
|
|
2193
2235
|
const palette2 = {};
|
|
2194
|
-
const lightnessMap =
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
const
|
|
2206
|
-
if (remaining > 0) {
|
|
2207
|
-
const target = mode === "light" ? minLightness : maxLightness;
|
|
2208
|
-
for (let index = lockIndex + 1; index < keys.length; index++) {
|
|
2209
|
-
const t = (index - lockIndex) / remaining;
|
|
2210
|
-
lightnessMap[keys[index]] = inputLightness + (target - inputLightness) * t ** lightnessCurve;
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
} else {
|
|
2214
|
-
for (let index = 0; index < keys.length; index++) {
|
|
2215
|
-
const t = (index / (keys.length - 1)) ** lightnessCurve;
|
|
2216
|
-
lightnessMap[keys[index]] = mode === "light" ? maxLightness - (maxLightness - minLightness) * t : minLightness + (maxLightness - minLightness) * t;
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
for (const key of keys) {
|
|
2236
|
+
const lightnessMap = lock !== void 0 && inputLightness !== void 0 ? computeLightnessMapWithLock(
|
|
2237
|
+
lock,
|
|
2238
|
+
inputLightness,
|
|
2239
|
+
keys,
|
|
2240
|
+
lightnessCurve,
|
|
2241
|
+
maxLightness,
|
|
2242
|
+
minLightness,
|
|
2243
|
+
mode
|
|
2244
|
+
) : computeLightnessMapWithoutLock(keys, lightnessCurve, maxLightness, minLightness, mode);
|
|
2245
|
+
const anchorIndex = lock !== void 0 ? keys.indexOf(lock) : Math.floor((keys.length - 1) / 2);
|
|
2246
|
+
for (let index = 0; index < keys.length; index++) {
|
|
2247
|
+
const key = keys[index];
|
|
2220
2248
|
const lightness = lightnessMap[key];
|
|
2221
|
-
const
|
|
2222
|
-
const maxChroma = getP3MaxChroma({ l: lightness, c: 0, h:
|
|
2223
|
-
|
|
2249
|
+
const stepHue = computeStepHue(hue, hueShift, index, anchorIndex, keys.length);
|
|
2250
|
+
const maxChroma = getP3MaxChroma({ l: lightness, c: 0, h: stepHue });
|
|
2251
|
+
const chroma2 = chromaCurve.type === "endpoints" ? getStepRelativeChroma(chromaCurve, index, anchorIndex, keys.length) * maxChroma : getStepChroma(lightness, baseChroma, chromaCurve.amount, chromaCurve.peak);
|
|
2252
|
+
palette2[key] = { l: lightness, c: Math.min(chroma2, maxChroma), h: stepHue };
|
|
2224
2253
|
}
|
|
2225
2254
|
return palette2;
|
|
2226
2255
|
}
|
|
2227
|
-
function getStepChroma(lightness, baseChroma,
|
|
2228
|
-
if (
|
|
2256
|
+
function getStepChroma(lightness, baseChroma, amount, peak) {
|
|
2257
|
+
if (amount === 0) {
|
|
2229
2258
|
return baseChroma;
|
|
2230
2259
|
}
|
|
2231
|
-
const parabolic = 4 * lightness * (1 - lightness);
|
|
2232
|
-
const curveScale = 1 -
|
|
2260
|
+
const parabolic = peak === 0.5 ? 4 * lightness * (1 - lightness) : Math.max(0, 1 - ((lightness - peak) / Math.max(peak, 1 - peak)) ** 2);
|
|
2261
|
+
const curveScale = 1 - amount * (1 - parabolic);
|
|
2233
2262
|
return Math.max(0, baseChroma * curveScale);
|
|
2234
2263
|
}
|
|
2264
|
+
function getStepRelativeChroma(curve, index, anchorIndex, total) {
|
|
2265
|
+
const { high, low, mid } = curve;
|
|
2266
|
+
if (index === anchorIndex) {
|
|
2267
|
+
return mid;
|
|
2268
|
+
}
|
|
2269
|
+
if (index < anchorIndex) {
|
|
2270
|
+
return low + (mid - low) * (index / anchorIndex);
|
|
2271
|
+
}
|
|
2272
|
+
return mid + (high - mid) * ((index - anchorIndex) / (total - 1 - anchorIndex));
|
|
2273
|
+
}
|
|
2274
|
+
function normalizeChromaCurve(chromaCurve, lch, baseChroma) {
|
|
2275
|
+
if (isNumber(chromaCurve)) {
|
|
2276
|
+
invariant(isNumberInRange(chromaCurve, 0, 1), "chromaCurve must be within the range [0, 1].");
|
|
2277
|
+
return { amount: chromaCurve, peak: 0.5, type: "parabola" };
|
|
2278
|
+
}
|
|
2279
|
+
assertScaleObject(chromaCurve, "chromaCurve");
|
|
2280
|
+
if ("amount" in chromaCurve) {
|
|
2281
|
+
const { amount, peak = 0.5 } = chromaCurve;
|
|
2282
|
+
invariant(isNumberInRange(amount, 0, 1), "chromaCurve amount must be within the range [0, 1].");
|
|
2283
|
+
invariant(
|
|
2284
|
+
isNumber(peak) && peak > 0 && peak < 1,
|
|
2285
|
+
"chromaCurve peak must be within the range (0, 1)."
|
|
2286
|
+
);
|
|
2287
|
+
return { amount, peak, type: "parabola" };
|
|
2288
|
+
}
|
|
2289
|
+
const { high, low } = chromaCurve;
|
|
2290
|
+
invariant(
|
|
2291
|
+
isNumberInRange(low, 0, 1) && isNumberInRange(high, 0, 1),
|
|
2292
|
+
"chromaCurve low/high must be within the range [0, 1]."
|
|
2293
|
+
);
|
|
2294
|
+
const maxChromaAtInput = getP3MaxChroma(lch);
|
|
2295
|
+
return {
|
|
2296
|
+
high,
|
|
2297
|
+
low,
|
|
2298
|
+
mid: maxChromaAtInput > 0 ? clamp(baseChroma / maxChromaAtInput, 0, 1) : 0,
|
|
2299
|
+
type: "endpoints"
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
function resolveBaseChroma(saturation, variant, lch) {
|
|
2303
|
+
if (saturation !== void 0) {
|
|
2304
|
+
return clamp(saturation, 0, 100) / 100 * getP3MaxChroma(lch);
|
|
2305
|
+
}
|
|
2306
|
+
if (variant && chromaScale[variant]) {
|
|
2307
|
+
return lch.c * chromaScale[variant];
|
|
2308
|
+
}
|
|
2309
|
+
if (variant) {
|
|
2310
|
+
warn(`variant: ${variant} is not valid, ignoring`);
|
|
2311
|
+
}
|
|
2312
|
+
return lch.c;
|
|
2313
|
+
}
|
|
2314
|
+
function warnIgnoredLockEndpoints(lock, keys, options) {
|
|
2315
|
+
if (lock === void 0) {
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
const lockIndex = keys.indexOf(lock);
|
|
2319
|
+
if (lockIndex !== 0 && lockIndex !== keys.length - 1) {
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
const { chromaCurve, hueShift, lightnessCurve } = options;
|
|
2323
|
+
const ignored = lockIndex === 0 ? "low" : "high";
|
|
2324
|
+
const ranges = [
|
|
2325
|
+
["hueShift", hueShift],
|
|
2326
|
+
["lightnessCurve", lightnessCurve]
|
|
2327
|
+
];
|
|
2328
|
+
if (chromaCurve.type === "endpoints") {
|
|
2329
|
+
ranges.push(["chromaCurve", { high: chromaCurve.high, low: chromaCurve.low }]);
|
|
2330
|
+
}
|
|
2331
|
+
for (const [name2, range] of ranges) {
|
|
2332
|
+
if (range.low !== range.high) {
|
|
2333
|
+
warn(`lock at ${lock} ignores ${name2}.${ignored} (${range[ignored]})`);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2235
2337
|
function scale(input, options = {}) {
|
|
2236
2338
|
invariant(isString(input), MESSAGES.inputString);
|
|
2237
2339
|
const {
|
|
2238
2340
|
chromaCurve = 0,
|
|
2239
2341
|
format,
|
|
2342
|
+
hueShift = 0,
|
|
2240
2343
|
lightnessCurve = 1.5,
|
|
2241
2344
|
lock: lockOption,
|
|
2242
2345
|
maxLightness = 0.97,
|
|
@@ -2250,6 +2353,22 @@ function scale(input, options = {}) {
|
|
|
2250
2353
|
maxLightness > minLightness && maxLightness <= 1 && minLightness >= 0,
|
|
2251
2354
|
"maxLightness must be greater than minLightness and within the range [0, 1]."
|
|
2252
2355
|
);
|
|
2356
|
+
if (!isNumber(hueShift)) {
|
|
2357
|
+
assertScaleObject(hueShift, "hueShift");
|
|
2358
|
+
}
|
|
2359
|
+
const hueShiftRange = isNumber(hueShift) ? { low: -hueShift, high: hueShift } : hueShift;
|
|
2360
|
+
invariant(
|
|
2361
|
+
isNumberInRange(hueShiftRange.low, -180, 180) && isNumberInRange(hueShiftRange.high, -180, 180),
|
|
2362
|
+
"hueShift values must be within the range [-180, 180]."
|
|
2363
|
+
);
|
|
2364
|
+
if (!isNumber(lightnessCurve)) {
|
|
2365
|
+
assertScaleObject(lightnessCurve, "lightnessCurve");
|
|
2366
|
+
}
|
|
2367
|
+
const lightnessCurveRange = isNumber(lightnessCurve) ? { low: lightnessCurve, high: lightnessCurve } : lightnessCurve;
|
|
2368
|
+
invariant(
|
|
2369
|
+
isNumber(lightnessCurveRange.low) && lightnessCurveRange.low > 0 && isNumber(lightnessCurveRange.high) && lightnessCurveRange.high > 0,
|
|
2370
|
+
"lightnessCurve values must be greater than 0."
|
|
2371
|
+
);
|
|
2253
2372
|
const steps = stepsOption !== void 0 ? Math.round(stepsOption) : 11;
|
|
2254
2373
|
const keys = getScaleStepKeys(steps);
|
|
2255
2374
|
let lock = lockOption;
|
|
@@ -2259,23 +2378,22 @@ function scale(input, options = {}) {
|
|
|
2259
2378
|
}
|
|
2260
2379
|
const parsed = resolveColor(input);
|
|
2261
2380
|
const lch = parsed.oklch;
|
|
2262
|
-
|
|
2263
|
-
if (saturation !== void 0) {
|
|
2264
|
-
const maxChroma = getP3MaxChroma(lch);
|
|
2265
|
-
baseChroma = clamp(saturation, 0, 100) / 100 * maxChroma;
|
|
2266
|
-
} else if (variant && chromaScale[variant]) {
|
|
2267
|
-
baseChroma = lch.c * chromaScale[variant];
|
|
2268
|
-
} else {
|
|
2269
|
-
baseChroma = lch.c;
|
|
2270
|
-
}
|
|
2381
|
+
const baseChroma = resolveBaseChroma(saturation, variant, lch);
|
|
2271
2382
|
const colorFormat = format ?? parsed.type;
|
|
2383
|
+
const chromaCurveConfig = normalizeChromaCurve(chromaCurve, lch, baseChroma);
|
|
2384
|
+
warnIgnoredLockEndpoints(lock, keys, {
|
|
2385
|
+
chromaCurve: chromaCurveConfig,
|
|
2386
|
+
hueShift: hueShiftRange,
|
|
2387
|
+
lightnessCurve: lightnessCurveRange
|
|
2388
|
+
});
|
|
2272
2389
|
const palette2 = generatePalette({
|
|
2273
2390
|
baseChroma,
|
|
2274
|
-
chromaCurve,
|
|
2391
|
+
chromaCurve: chromaCurveConfig,
|
|
2275
2392
|
hue: lch.h,
|
|
2393
|
+
hueShift: hueShiftRange,
|
|
2276
2394
|
inputLightness: lock !== void 0 ? lch.l : void 0,
|
|
2277
2395
|
keys,
|
|
2278
|
-
lightnessCurve,
|
|
2396
|
+
lightnessCurve: lightnessCurveRange,
|
|
2279
2397
|
lock,
|
|
2280
2398
|
maxLightness,
|
|
2281
2399
|
minLightness,
|