@auaust/jaune 0.0.3 → 0.0.5

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.
Files changed (66) hide show
  1. package/dist/chunk-Q3GK6DZM.js +2 -0
  2. package/dist/chunk-Q3GK6DZM.js.map +1 -0
  3. package/dist/chunk-X6GMJCOG.cjs +2 -0
  4. package/dist/chunk-X6GMJCOG.cjs.map +1 -0
  5. package/dist/{index-VyiURM-J.d.cts → index-7Xs589Bo.d.cts} +37 -90
  6. package/dist/{index-VyiURM-J.d.ts → index-7Xs589Bo.d.ts} +37 -90
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/utils.cjs +1 -1
  14. package/dist/utils.cjs.map +1 -1
  15. package/dist/utils.d.cts +115 -26
  16. package/dist/utils.d.ts +115 -26
  17. package/dist/utils.js +1 -1
  18. package/dist/utils.js.map +1 -1
  19. package/package.json +14 -10
  20. package/src/classes/Color.ts +26 -14
  21. package/src/data/fallbackColor.ts +10 -0
  22. package/src/{utils → data}/namedColors.ts +5 -109
  23. package/src/types/index.ts +2 -0
  24. package/src/utils/brightness.ts +17 -0
  25. package/src/utils/closestNamedColor.ts +25 -0
  26. package/src/utils/contrast.ts +17 -0
  27. package/src/utils/couldBeRgb.ts +13 -0
  28. package/src/utils/grayscale.ts +12 -0
  29. package/src/utils/index.ts +45 -34
  30. package/src/utils/invert.ts +11 -0
  31. package/src/utils/isAliasToNamedColor.ts +12 -0
  32. package/src/utils/isAlphaChannel.ts +5 -0
  33. package/src/utils/isBright.ts +7 -0
  34. package/src/utils/isColor.ts +7 -0
  35. package/src/utils/isColorChannels.ts +13 -0
  36. package/src/utils/isDark.ts +7 -0
  37. package/src/utils/isHex.ts +27 -0
  38. package/src/utils/isNamedColor.ts +12 -0
  39. package/src/utils/isOpaque.ts +6 -0
  40. package/src/utils/isRgb.ts +15 -0
  41. package/src/utils/isRgbChannel.ts +5 -0
  42. package/src/utils/isTranslucent.ts +7 -0
  43. package/src/utils/isTransparent.ts +6 -0
  44. package/src/utils/linearTosRGB.ts +10 -0
  45. package/src/utils/luminance.ts +1 -77
  46. package/src/utils/namedColorAliases.ts +33 -0
  47. package/src/utils/namedColorChannels.ts +13 -0
  48. package/src/utils/namedColorToHex.ts +11 -0
  49. package/src/utils/parseColor.ts +44 -0
  50. package/src/utils/parseHex.ts +23 -0
  51. package/src/utils/parseNamedColor.ts +13 -0
  52. package/src/utils/parseRgb.ts +14 -0
  53. package/src/utils/random.ts +2 -29
  54. package/src/utils/randomAlphaChannel.ts +15 -0
  55. package/src/utils/randomRgbChannel.ts +15 -0
  56. package/src/utils/sRGBtoLinear.ts +12 -0
  57. package/src/utils/toAlphaChannel.ts +5 -0
  58. package/src/utils/{channels.ts → toColorChannels.ts} +4 -31
  59. package/src/utils/toHex.ts +29 -0
  60. package/src/utils/toRgb.ts +13 -0
  61. package/src/utils/toRgbChannel.ts +5 -0
  62. package/src/utils/type.ts +32 -0
  63. package/src/utils/colors.ts +0 -72
  64. package/src/utils/hex.ts +0 -75
  65. package/src/utils/opacity.ts +0 -16
  66. package/src/utils/rgb.ts +0 -51
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@auaust/jaune",
3
3
  "license": "GPL-3.0-only",
4
4
  "type": "module",
5
- "version": "0.0.3",
5
+ "version": "0.0.5",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -13,13 +13,13 @@
13
13
  "url": "https://auaust.ch"
14
14
  },
15
15
  "devDependencies": {
16
- "@changesets/cli": "^2.28.1",
17
- "@vitest/coverage-istanbul": "^2.1.9",
18
- "rimraf": "^6.0.1",
19
- "tsup": "^8.4.0",
20
- "typescript": "^5.8.2",
21
- "type-fest": "^4.37.0",
22
- "vitest": "^2.1.9"
16
+ "@changesets/cli": "^2.29.8",
17
+ "@vitest/coverage-istanbul": "^4.0.16",
18
+ "rimraf": "^6.1.2",
19
+ "tsup": "^8.5.1",
20
+ "type-fest": "^5.3.1",
21
+ "typescript": "^5.9.3",
22
+ "vitest": "^4.0.16"
23
23
  },
24
24
  "files": [
25
25
  "dist",
@@ -30,23 +30,27 @@
30
30
  "types": "./dist/types/index.d.ts",
31
31
  "exports": {
32
32
  ".": {
33
+ "types": "./dist/index.d.ts",
33
34
  "import": "./dist/index.js",
34
35
  "require": "./dist/index.cjs"
35
36
  },
36
37
  "./utils": {
38
+ "types": "./dist/utils.d.ts",
37
39
  "import": "./dist/utils.js",
38
40
  "require": "./dist/utils.cjs"
39
41
  }
40
42
  },
41
43
  "dependencies": {
42
- "@auaust/primitive-kit": "^0.36.1"
44
+ "@auaust/primitive-kit": "^0.41.9"
43
45
  },
44
46
  "scripts": {
45
47
  "preinstall": "npx only-allow pnpm",
46
- "build": "pnpm test && pnpm run build:clean && pnpm run build:js",
48
+ "build": "pnpm test && pnpm build:notest",
49
+ "build:notest": "pnpm run build:clean && pnpm run build:js",
47
50
  "build:clean": "rimraf dist",
48
51
  "build:js": "tsup",
49
52
  "test": "vitest run --config vitest.config.ts --typecheck -- && pnpm typecheck",
53
+ "test:dist": "pnpm build:notest && vitest run --config vitest.config.ts --typecheck --mode build --",
50
54
  "watch": "vitest --config vitest.config.ts --typecheck --",
51
55
  "coverage": "vitest run --coverage",
52
56
  "typecheck": "echo 'Typechecking...' && tsc --noEmit && echo 'All good!'",
@@ -1,4 +1,4 @@
1
- import { N } from "@auaust/primitive-kit";
1
+ import { isNumber } from "@auaust/primitive-kit/numbers";
2
2
  import type { Writable } from "type-fest";
3
3
  import type {
4
4
  ColorChannels,
@@ -6,14 +6,17 @@ import type {
6
6
  MaybeNamedColor,
7
7
  NamedColor,
8
8
  Rgb,
9
- } from "~";
9
+ } from "~/index";
10
10
  import {
11
11
  brightness,
12
+ cache,
13
+ channels,
12
14
  closestNamedColor,
13
15
  contrast,
14
16
  distance,
15
17
  fallbackColor,
16
18
  grayscale,
19
+ invert,
17
20
  isAliasToNamedColor,
18
21
  isBright,
19
22
  isColor,
@@ -31,6 +34,7 @@ import {
31
34
  parseHex,
32
35
  parseNamedColor,
33
36
  parseRgb,
37
+ random,
34
38
  toAlphaChannel,
35
39
  toColorChannels,
36
40
  toHex,
@@ -38,8 +42,6 @@ import {
38
42
  toRgbChannel,
39
43
  type,
40
44
  } from "~/utils";
41
- import { random } from "~/utils/random";
42
- import { cache, channels } from "~/utils/symbols";
43
45
 
44
46
  export class Color {
45
47
  protected [channels]: Required<Writable<ColorChannels>> = undefined!;
@@ -52,31 +54,37 @@ export class Color {
52
54
  static from(value: ColorValue): Color;
53
55
  static from(red: number, green: number, blue: number, alpha?: number): Color;
54
56
  static from(value: ColorValue | number, ...rest: number[]): Color {
55
- if (N.is(value)) {
56
- return new this({ r: value, g: rest[0], b: rest[1], a: rest[2] });
57
+ if (isNumber(value)) {
58
+ return new Color({ r: value, g: rest[0], b: rest[1], a: rest[2] });
57
59
  }
58
60
 
59
- return new this(parseColor(value ?? fallbackColor));
61
+ return new Color(parseColor(value ?? fallbackColor));
60
62
  }
61
63
 
62
64
  static fromChannels(channels: ColorChannels): Color {
63
- return new this(channels);
65
+ return new Color(channels);
64
66
  }
65
67
 
66
68
  static fromRgb(rgb: Rgb): Color {
67
- return new this(parseRgb(rgb));
69
+ return new Color(parseRgb(rgb));
68
70
  }
69
71
 
70
72
  static fromHex(hex: string): Color {
71
- return new this(parseHex(hex));
73
+ return new Color(parseHex(hex));
72
74
  }
73
75
 
74
76
  static fromName(name: MaybeNamedColor): Color {
75
- return new this(parseNamedColor(name));
77
+ return new Color(parseNamedColor(name));
78
+ }
79
+
80
+ static parse(value: ColorValue): ColorChannels | undefined {
81
+ const color = parseColor(value);
82
+
83
+ return color.isFallback ? undefined : color;
76
84
  }
77
85
 
78
86
  static random(presets: Parameters<typeof random>[0] = {}): Color {
79
- return new this(random(presets));
87
+ return new Color(random(presets));
80
88
  }
81
89
 
82
90
  /**
@@ -324,12 +332,12 @@ export class Color {
324
332
 
325
333
  /** Returns the contrast ratio between this color and another. */
326
334
  contrast(color: ColorValue): number {
327
- return contrast(this[channels], Color.from(color)[channels]);
335
+ return contrast(this[channels], parseColor(color));
328
336
  }
329
337
 
330
338
  /** Returns the distance between this color and another. If `alpha` is `true`, the alpha channel is included in the calculation. */
331
339
  distance(color: ColorValue, alpha = false): number {
332
- return distance(this[channels], Color.from(color)[channels], alpha);
340
+ return distance(this[channels], parseColor(color), alpha);
333
341
  }
334
342
 
335
343
  /** Returns a new color with the grayscale equivalent of the current color, preserving the alpha channel. */
@@ -337,6 +345,10 @@ export class Color {
337
345
  return Color.fromChannels(grayscale(this[channels]));
338
346
  }
339
347
 
348
+ toInvert(): Color {
349
+ return Color.fromChannels(invert(this[channels]));
350
+ }
351
+
340
352
  toHex(): string {
341
353
  return this.memoize(toHex);
342
354
  }
@@ -0,0 +1,10 @@
1
+ import type { ColorChannels } from "~/types";
2
+
3
+ export const fallbackColor: Required<ColorChannels> = Object.freeze({
4
+ r: 0,
5
+ g: 0,
6
+ b: 0,
7
+ a: 1,
8
+ isTransformed: false,
9
+ isFallback: true,
10
+ });
@@ -1,13 +1,11 @@
1
- import { O, S } from "@auaust/primitive-kit";
2
- import type { ColorChannels, MaybeNamedColor, NamedColor } from "~/types";
3
- import { distance, fallbackColor, parseHex } from "~/utils";
1
+ import type { NamedColor } from "~/types";
4
2
 
5
3
  /**
6
4
  * A map of named colors and their HEX values.
7
5
  *
8
6
  * @see https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
9
7
  */
10
- export const namedColorsMap = O.freeze({
8
+ export const namedColorsMap = Object.freeze({
11
9
  aliceblue: "#f0f8ffff",
12
10
  antiquewhite: "#faebd7ff",
13
11
  aqua: "#00ffffff",
@@ -159,108 +157,6 @@ export const namedColorsMap = O.freeze({
159
157
  yellowgreen: "#9acd32ff",
160
158
  });
161
159
 
162
- const namedColors = new Set<NamedColor>(O.keys(namedColorsMap));
163
-
164
- const namedColorsChannelsCache: Partial<Record<NamedColor, ColorChannels>> = {};
165
-
166
- const namedColorsAliasesCache: Partial<
167
- Record<NamedColor, readonly NamedColor[]>
168
- > = {};
169
-
170
- /**
171
- * Whether the input is a valid named color.
172
- *
173
- * The check is case-sensitive.
174
- */
175
- export function isNamedColor(value: unknown): value is NamedColor {
176
- return S.is(value) && namedColors.has(<NamedColor>value.toLowerCase());
177
- }
178
-
179
- /**
180
- * Returns the color channels from a named color. Must be a valid named color, already lowercased.
181
- *
182
- * @internal
183
- */
184
- export function namedColorChannels(name: NamedColor): ColorChannels {
185
- return (namedColorsChannelsCache[name] ??= parseHex(namedColorToHex(name)!));
186
- }
187
-
188
- /**
189
- * Returns the color channels from a named color.
190
- */
191
- export function parseNamedColor(name: MaybeNamedColor): ColorChannels {
192
- if (!isNamedColor(name)) {
193
- return fallbackColor;
194
- }
195
-
196
- return namedColorChannels(<NamedColor>name.toLowerCase());
197
- }
198
-
199
- /**
200
- * Returns the corresponding HEX value of a named color.
201
- */
202
- export function namedColorToHex(name: NamedColor): string;
203
- export function namedColorToHex(name: MaybeNamedColor): string | undefined;
204
- export function namedColorToHex(name: MaybeNamedColor): string | undefined {
205
- return (
206
- namedColorsMap[<keyof typeof namedColorsMap>name.toLowerCase()] || undefined
207
- );
208
- }
209
-
210
- /** Returns the closest named color to the passed color channels. */
211
- export function closestNamedColor(channels: ColorChannels): NamedColor {
212
- let closest: NamedColor | undefined;
213
- let smallestDistance = Infinity;
214
-
215
- for (const name of namedColors) {
216
- const value = namedColorChannels(name);
217
- const d = distance(
218
- value,
219
- channels,
220
- value.a === 1 // Ignore alpha channel only if it's 1 -> allows to match transparent/black correctly
221
- );
222
-
223
- if (d < smallestDistance) {
224
- closest = name;
225
- smallestDistance = d;
226
- }
227
- }
228
-
229
- return closest!;
230
- }
231
-
232
- /**
233
- * Returns all the aliases of a named color.
234
- */
235
- export function namedColorAliases(name: NamedColor): readonly NamedColor[] {
236
- name = <NamedColor>name.toLowerCase();
237
-
238
- if (!isNamedColor(name)) {
239
- return [];
240
- }
241
-
242
- if (namedColorsAliasesCache[name]) {
243
- return namedColorsAliasesCache[name]!;
244
- }
245
-
246
- const aliases: NamedColor[] = [];
247
- const targetHex = namedColorToHex(name);
248
-
249
- for (const [name, hex] of O.entries(namedColorsMap)) {
250
- if (hex === targetHex) {
251
- aliases.push(name);
252
- }
253
- }
254
-
255
- return (namedColorsAliasesCache[name] = Object.freeze(aliases));
256
- }
257
-
258
- /**
259
- * Returns a boolean indicating whether two named colors are aliases.
260
- */
261
- export function isAliasToNamedColor(
262
- name: NamedColor,
263
- alias: NamedColor
264
- ): boolean {
265
- return namedColorAliases(name).includes(<NamedColor>alias.toLowerCase());
266
- }
160
+ export const namedColors = new Set<NamedColor>(
161
+ Object.keys(namedColorsMap) as NamedColor[]
162
+ );
@@ -82,3 +82,5 @@ export type ColorChannels = {
82
82
  */
83
83
  readonly isFallback?: boolean;
84
84
  };
85
+
86
+ export type ChannelRange = number | [min: number, max: number];
@@ -0,0 +1,17 @@
1
+ import type { ColorChannels } from "~/types";
2
+ import { luminance } from "~/utils";
3
+
4
+ /**
5
+ * Returns the perceived brightness of a color normalized to the range [0, 1].
6
+ *
7
+ * @see https://stackoverflow.com/a/56678483
8
+ */
9
+ export function brightness(channels: ColorChannels): number {
10
+ const l = luminance(channels);
11
+
12
+ if (l <= 216 / 24389) {
13
+ return (l * (24389 / 27)) / 100;
14
+ }
15
+
16
+ return (Math.pow(l, 1 / 3) * 116 - 16) / 100;
17
+ }
@@ -0,0 +1,25 @@
1
+ import type { ColorChannels, NamedColor } from "~/types";
2
+ import { distance, namedColorChannels, namedColors } from "~/utils";
3
+
4
+ /** Returns the closest named color to the passed color channels. */
5
+ export function closestNamedColor(channels: ColorChannels): NamedColor {
6
+ let closest: NamedColor | undefined;
7
+ let smallestDistance = Infinity;
8
+
9
+ for (const name of namedColors) {
10
+ const value = namedColorChannels(name);
11
+
12
+ const d = distance(
13
+ value,
14
+ channels,
15
+ value.a === 1 // Ignore alpha channel only if it's 1 -> allows to match transparent/black correctly
16
+ );
17
+
18
+ if (d < smallestDistance) {
19
+ closest = name;
20
+ smallestDistance = d;
21
+ }
22
+ }
23
+
24
+ return closest!;
25
+ }
@@ -0,0 +1,17 @@
1
+ import { minMax } from "@auaust/primitive-kit/numbers";
2
+ import type { ColorChannels } from "~/types";
3
+ import { luminance } from "~/utils";
4
+
5
+ /**
6
+ * Returns the contrast ratio between two colors.
7
+ *
8
+ * @see https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
9
+ */
10
+ export function contrast(channelsA: ColorChannels, channelsB: ColorChannels) {
11
+ const [darkest, brightest] = minMax(
12
+ luminance(channelsA),
13
+ luminance(channelsB)
14
+ );
15
+
16
+ return (brightest + 0.05) / (darkest + 0.05);
17
+ }
@@ -0,0 +1,13 @@
1
+ import { isArray } from "@auaust/primitive-kit/arrays";
2
+ import { isBetween, isNumber } from "@auaust/primitive-kit/numbers";
3
+ import type { Rgb } from "~/types";
4
+
5
+ /**
6
+ * Whether the input could be a valid RGB color.
7
+ * As in, it checks if the input is an array of numbers without validating the values range.
8
+ */
9
+ export function couldBeRgb(value: unknown): value is Rgb {
10
+ return (
11
+ isArray(value) && isBetween(value.length, 3, 4) && value.every(isNumber)
12
+ );
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { ColorChannels } from "~/types";
2
+ import { linearTosRGB, luminance, toColorChannels } from "~/utils";
3
+
4
+ /**
5
+ * Returns the grayscale equivalent of a color as a new color channels object.
6
+ */
7
+ export function grayscale(channels: ColorChannels): ColorChannels {
8
+ // Reverse gamma correction from luminance
9
+ const gray = linearTosRGB(luminance(channels)) * 255;
10
+
11
+ return toColorChannels(gray, gray, gray, channels.a);
12
+ }
@@ -1,35 +1,46 @@
1
- export {
2
- fallbackColor,
3
- isAlphaChannel,
4
- isColorChannels,
5
- isRgbChannel,
6
- toAlphaChannel,
7
- toColorChannels,
8
- toRgbChannel,
9
- } from "~/utils/channels";
10
- export { isColor, parseColor, type } from "~/utils/colors";
1
+ import { cache, channels } from "~/utils/symbols";
2
+
3
+ export { cache, channels }; // Not explicitly importing and exporting these symbols causes issues with TSUP when testing against the dist folder
4
+
5
+ export { fallbackColor } from "~/data/fallbackColor";
6
+ export { namedColors, namedColorsMap } from "~/data/namedColors";
7
+
8
+ export { brightness } from "~/utils/brightness";
9
+ export { closestNamedColor } from "~/utils/closestNamedColor";
10
+ export { contrast } from "~/utils/contrast";
11
+ export { couldBeRgb } from "~/utils/couldBeRgb";
11
12
  export { distance } from "~/utils/distance";
12
- export { isHex, parseHex, toHex } from "~/utils/hex";
13
- export {
14
- brightness,
15
- contrast,
16
- grayscale,
17
- isBright,
18
- isDark,
19
- linearTosRGB,
20
- luminance,
21
- sRGBtoLinear,
22
- } from "~/utils/luminance";
23
- export {
24
- closestNamedColor,
25
- isAliasToNamedColor,
26
- isNamedColor,
27
- namedColorAliases,
28
- namedColorChannels,
29
- namedColorsMap,
30
- namedColorToHex,
31
- parseNamedColor,
32
- } from "~/utils/namedColors";
33
- export { isOpaque, isTranslucent, isTransparent } from "~/utils/opacity";
34
- export { random, type ChannelRange } from "~/utils/random";
35
- export { couldBeRgb, isRgb, parseRgb, toRgb } from "~/utils/rgb";
13
+ export { grayscale } from "~/utils/grayscale";
14
+ export { invert } from "~/utils/invert";
15
+ export { isAliasToNamedColor } from "~/utils/isAliasToNamedColor";
16
+ export { isAlphaChannel } from "~/utils/isAlphaChannel";
17
+ export { isBright } from "~/utils/isBright";
18
+ export { isColor } from "~/utils/isColor";
19
+ export { isColorChannels } from "~/utils/isColorChannels";
20
+ export { isDark } from "~/utils/isDark";
21
+ export { isHex } from "~/utils/isHex";
22
+ export { isNamedColor } from "~/utils/isNamedColor";
23
+ export { isOpaque } from "~/utils/isOpaque";
24
+ export { isRgb } from "~/utils/isRgb";
25
+ export { isRgbChannel } from "~/utils/isRgbChannel";
26
+ export { isTranslucent } from "~/utils/isTranslucent";
27
+ export { isTransparent } from "~/utils/isTransparent";
28
+ export { linearTosRGB } from "~/utils/linearTosRGB";
29
+ export { luminance } from "~/utils/luminance";
30
+ export { namedColorAliases } from "~/utils/namedColorAliases";
31
+ export { namedColorChannels } from "~/utils/namedColorChannels";
32
+ export { namedColorToHex } from "~/utils/namedColorToHex";
33
+ export { parseColor } from "~/utils/parseColor";
34
+ export { parseHex } from "~/utils/parseHex";
35
+ export { parseNamedColor } from "~/utils/parseNamedColor";
36
+ export { parseRgb } from "~/utils/parseRgb";
37
+ export { random } from "~/utils/random";
38
+ export { randomAlphaChannel } from "~/utils/randomAlphaChannel";
39
+ export { randomRgbChannel } from "~/utils/randomRgbChannel";
40
+ export { sRGBtoLinear } from "~/utils/sRGBtoLinear";
41
+ export { toAlphaChannel } from "~/utils/toAlphaChannel";
42
+ export { toColorChannels } from "~/utils/toColorChannels";
43
+ export { toHex } from "~/utils/toHex";
44
+ export { toRgb } from "~/utils/toRgb";
45
+ export { toRgbChannel } from "~/utils/toRgbChannel";
46
+ export { type } from "~/utils/type";
@@ -0,0 +1,11 @@
1
+ import type { ColorChannels } from "~/types";
2
+ import { toColorChannels } from "~/utils/toColorChannels";
3
+
4
+ export function invert(channels: ColorChannels): ColorChannels {
5
+ return toColorChannels(
6
+ 255 - channels.r,
7
+ 255 - channels.g,
8
+ 255 - channels.b,
9
+ channels.a
10
+ );
11
+ }
@@ -0,0 +1,12 @@
1
+ import type { NamedColor } from "~/types";
2
+ import { namedColorAliases } from "~/utils";
3
+
4
+ /**
5
+ * Returns a boolean indicating whether two named colors are aliases.
6
+ */
7
+ export function isAliasToNamedColor(
8
+ name: NamedColor,
9
+ alias: NamedColor
10
+ ): boolean {
11
+ return namedColorAliases(name).includes(<NamedColor>alias.toLowerCase());
12
+ }
@@ -0,0 +1,5 @@
1
+ import { isBetween, isNumber } from "@auaust/primitive-kit/numbers";
2
+
3
+ export function isAlphaChannel(value: unknown): value is number {
4
+ return isNumber(value) && isBetween(value, 0, 1);
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { ColorChannels } from "~/types";
2
+ import { brightness } from "~/utils";
3
+
4
+ /** Returns whether the color is bright, as in its brightness is greater than 0.5 or the provided threshold. */
5
+ export function isBright(channels: ColorChannels, threshold = 0.5): boolean {
6
+ return brightness(channels) > threshold;
7
+ }
@@ -0,0 +1,7 @@
1
+ import type { ColorValue } from "~/types";
2
+ import { type } from "~/utils";
3
+
4
+ /** Returns true if the value is any format of supported color. */
5
+ export function isColor(value: unknown): value is ColorValue {
6
+ return type(value) !== undefined;
7
+ }
@@ -0,0 +1,13 @@
1
+ import { isObject } from "@auaust/primitive-kit/objects";
2
+ import type { ColorChannels } from "~/types";
3
+ import { isAlphaChannel, isRgbChannel } from "~/utils";
4
+
5
+ export function isColorChannels(value: unknown): value is ColorChannels {
6
+ return (
7
+ isObject(value, false) &&
8
+ isRgbChannel(value.r) &&
9
+ isRgbChannel(value.g) &&
10
+ isRgbChannel(value.b) &&
11
+ isAlphaChannel(value.a ?? 1)
12
+ );
13
+ }
@@ -0,0 +1,7 @@
1
+ import type { ColorChannels } from "~/types";
2
+ import { brightness } from "~/utils";
3
+
4
+ /** Returns whether the color is dark, as in its brightness is less than or equal to 0.5 or the provided threshold. */
5
+ export function isDark(channels: ColorChannels, threshold = 0.5): boolean {
6
+ return brightness(channels) <= threshold;
7
+ }
@@ -0,0 +1,27 @@
1
+ import { isStrictString, isString } from "@auaust/primitive-kit/strings";
2
+ import type { Hex } from "~/types";
3
+
4
+ const hexadecimalRegex = /^[0-9A-Fa-f]+$/;
5
+
6
+ /**
7
+ * Whether the input is a valid HEX color. With or without the alpha channel, and with single or double digits.
8
+ *
9
+ * It may or may not start with a hash character, which will be ignored.
10
+ */
11
+ export function isHex(value: unknown): value is Hex {
12
+ value = isString(value) && (value.startsWith("#") ? value.slice(1) : value);
13
+
14
+ if (!isStrictString(value)) {
15
+ return false;
16
+ }
17
+
18
+ switch (value.length) {
19
+ case 3: // RGB
20
+ case 4: // RGBA
21
+ case 6: // RRGGBB
22
+ case 8: // RRGGBBAA
23
+ return hexadecimalRegex.test(value);
24
+ default:
25
+ return false;
26
+ }
27
+ }
@@ -0,0 +1,12 @@
1
+ import { isString } from "@auaust/primitive-kit/strings";
2
+ import { namedColors } from "~/data/namedColors";
3
+ import type { NamedColor } from "~/types";
4
+
5
+ /**
6
+ * Whether the input is a valid named color.
7
+ *
8
+ * The check is case-insensitive.
9
+ */
10
+ export function isNamedColor(value: unknown): value is NamedColor {
11
+ return isString(value) && namedColors.has(<NamedColor>value.toLowerCase());
12
+ }
@@ -0,0 +1,6 @@
1
+ import type { ColorChannels } from "~/types";
2
+
3
+ /** Checks if the color is fully opaque. */
4
+ export function isOpaque(channels: ColorChannels) {
5
+ return channels.a === 1 || channels.a == undefined;
6
+ }
@@ -0,0 +1,15 @@
1
+ import { isArray } from "@auaust/primitive-kit/arrays";
2
+ import { isBetween } from "@auaust/primitive-kit/numbers";
3
+ import type { Rgb } from "~/types";
4
+ import { isAlphaChannel, isRgbChannel } from "~/utils";
5
+
6
+ /**
7
+ * Whether the input is a valid RGB color.
8
+ */
9
+ export function isRgb(value: unknown): value is Rgb {
10
+ return (
11
+ isArray(value) &&
12
+ isBetween(value.length, 3, 4) &&
13
+ value.every((n, i) => (i === 3 ? isAlphaChannel(n) : isRgbChannel(n)))
14
+ );
15
+ }
@@ -0,0 +1,5 @@
1
+ import { isBetween, isNumber } from "@auaust/primitive-kit/numbers";
2
+
3
+ export function isRgbChannel(value: unknown): value is number {
4
+ return isNumber(value) && isBetween(value, 0, 255);
5
+ }
@@ -0,0 +1,7 @@
1
+ import type { ColorChannels } from "~/types";
2
+ import { isOpaque } from "~/utils";
3
+
4
+ /** Checks if the color is at least partially transparent. */
5
+ export function isTranslucent(channels: ColorChannels) {
6
+ return !isOpaque(channels);
7
+ }
@@ -0,0 +1,6 @@
1
+ import type { ColorChannels } from "~/types";
2
+
3
+ /** Checks if the color is fully transparent. */
4
+ export function isTransparent(channels: ColorChannels) {
5
+ return channels.a === 0;
6
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns the gamma corrected sRGB value of a linear channel.
3
+ */
4
+ export function linearTosRGB(channel: number) {
5
+ if (channel <= 0.04045 / 12.92) {
6
+ return channel * 12.92;
7
+ }
8
+
9
+ return 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;
10
+ }