@clhaas/palette-kit 0.4.0 → 0.4.1
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/engine/relation/relation.d.ts +5 -3
- package/dist/engine/relation/relation.js +80 -5
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/errors/errors.js +1 -1
- package/docs/API.md +5 -0
- package/docs/Text.md +4 -0
- package/docs/Usage-ReactNative.md +11 -0
- package/docs/Validation.md +3 -1
- package/docs/spec.md +2 -0
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type OklchColor } from '../../core/oklch.js';
|
|
2
|
+
import type { ColorOutput, ResolveOutput } from '../../export/types.js';
|
|
2
3
|
import { type ResolverConfig } from '../../presets/presets.js';
|
|
3
4
|
import type { Context } from '../context/context.js';
|
|
4
5
|
import type { Level } from '../level/level.js';
|
|
@@ -6,11 +7,12 @@ import { type Usage } from '../usage/strategy.js';
|
|
|
6
7
|
export declare const RELATIONS: readonly ["on", "over", "under"];
|
|
7
8
|
export type Relation = (typeof RELATIONS)[number];
|
|
8
9
|
export type RelationAvailability = 'required' | 'optional' | 'forbidden';
|
|
10
|
+
export type RelationInputTarget = ResolveOutput<ColorOutput>;
|
|
9
11
|
export type RelationTarget = Readonly<OklchColor>;
|
|
10
12
|
export type RelationOptions = Readonly<{
|
|
11
|
-
on?:
|
|
12
|
-
over?:
|
|
13
|
-
under?:
|
|
13
|
+
on?: RelationInputTarget;
|
|
14
|
+
over?: RelationInputTarget;
|
|
15
|
+
under?: RelationInputTarget;
|
|
14
16
|
}>;
|
|
15
17
|
export type ResolvedRelation<R extends Relation = Relation> = Readonly<{
|
|
16
18
|
relation: R;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveOnContrast } from '../../contrast/contrast.js';
|
|
2
2
|
import { isOklchColor, normalizeOklch, } from '../../core/oklch.js';
|
|
3
|
+
import { linearRgbToOklab, oklabToOklch } from '../../operators/convert.js';
|
|
3
4
|
import { defaultResolverConfig, } from '../../presets/presets.js';
|
|
4
5
|
import { createForbiddenAxisCombinationError, createInvalidRelationTargetError, createMissingRequiredAxisError, createMultipleRelationsError, } from '../../utils/errors/errors.js';
|
|
5
6
|
import { assertUsage } from '../usage/strategy.js';
|
|
@@ -32,6 +33,57 @@ export const relationCompatibility = Object.freeze({
|
|
|
32
33
|
});
|
|
33
34
|
const relationList = RELATIONS.join(', ');
|
|
34
35
|
const formatInvalidRelationError = (value) => `Invalid relation "${String(value)}". Expected one of: ${relationList}.`;
|
|
36
|
+
const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
37
|
+
const isEncodedRgbChannel = (value) => typeof value === 'number' &&
|
|
38
|
+
Number.isInteger(value) &&
|
|
39
|
+
value >= 0 &&
|
|
40
|
+
value <= 255;
|
|
41
|
+
const isAlphaChannel = (value) => isFiniteNumber(value) && value >= 0 && value <= 1;
|
|
42
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
43
|
+
const isOklabRelationTarget = (value) => {
|
|
44
|
+
if (!isRecord(value)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return (value.space === 'oklab' &&
|
|
48
|
+
isFiniteNumber(value.l) &&
|
|
49
|
+
value.l >= 0 &&
|
|
50
|
+
value.l <= 1 &&
|
|
51
|
+
isFiniteNumber(value.a) &&
|
|
52
|
+
isFiniteNumber(value.b) &&
|
|
53
|
+
isAlphaChannel(value.alpha));
|
|
54
|
+
};
|
|
55
|
+
const isRgbRelationTarget = (value) => {
|
|
56
|
+
if (!isRecord(value)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return (isEncodedRgbChannel(value.r) &&
|
|
60
|
+
isEncodedRgbChannel(value.g) &&
|
|
61
|
+
isEncodedRgbChannel(value.b) &&
|
|
62
|
+
isAlphaChannel(value.alpha));
|
|
63
|
+
};
|
|
64
|
+
const isRgbaRelationTarget = (value) => {
|
|
65
|
+
if (!isRecord(value)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return (isEncodedRgbChannel(value.r) &&
|
|
69
|
+
isEncodedRgbChannel(value.g) &&
|
|
70
|
+
isEncodedRgbChannel(value.b) &&
|
|
71
|
+
isAlphaChannel(value.a));
|
|
72
|
+
};
|
|
73
|
+
const isHexRelationTarget = (value) => typeof value === 'string' && /^#[0-9a-f]{6}$/.test(value);
|
|
74
|
+
const parseHexChannel = (hex, start) => Number.parseInt(hex.slice(start, start + 2), 16);
|
|
75
|
+
const decodeSrgbChannel = (channel) => {
|
|
76
|
+
const encoded = channel / 255;
|
|
77
|
+
return encoded <= 0.04045
|
|
78
|
+
? encoded / 12.92
|
|
79
|
+
: ((encoded + 0.055) / 1.055) ** 2.4;
|
|
80
|
+
};
|
|
81
|
+
const rgbTargetToOklch = (target) => oklabToOklch(linearRgbToOklab({
|
|
82
|
+
alpha: target.alpha,
|
|
83
|
+
b: decodeSrgbChannel(target.b),
|
|
84
|
+
g: decodeSrgbChannel(target.g),
|
|
85
|
+
r: decodeSrgbChannel(target.r),
|
|
86
|
+
}));
|
|
35
87
|
export function isRelation(value) {
|
|
36
88
|
return (typeof value === 'string' &&
|
|
37
89
|
RELATIONS.includes(value));
|
|
@@ -41,10 +93,33 @@ export function assertRelation(value) {
|
|
|
41
93
|
throw new Error(formatInvalidRelationError(value));
|
|
42
94
|
}
|
|
43
95
|
}
|
|
44
|
-
function
|
|
45
|
-
if (
|
|
46
|
-
|
|
96
|
+
function normalizeRelationTarget(relation, target) {
|
|
97
|
+
if (isOklchColor(target)) {
|
|
98
|
+
return target;
|
|
99
|
+
}
|
|
100
|
+
if (isOklabRelationTarget(target)) {
|
|
101
|
+
return oklabToOklch(target);
|
|
102
|
+
}
|
|
103
|
+
if (isHexRelationTarget(target)) {
|
|
104
|
+
return rgbTargetToOklch({
|
|
105
|
+
alpha: 1,
|
|
106
|
+
b: parseHexChannel(target, 5),
|
|
107
|
+
g: parseHexChannel(target, 3),
|
|
108
|
+
r: parseHexChannel(target, 1),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (isRgbaRelationTarget(target)) {
|
|
112
|
+
return rgbTargetToOklch({
|
|
113
|
+
alpha: target.a,
|
|
114
|
+
b: target.b,
|
|
115
|
+
g: target.g,
|
|
116
|
+
r: target.r,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (isRgbRelationTarget(target)) {
|
|
120
|
+
return rgbTargetToOklch(target);
|
|
47
121
|
}
|
|
122
|
+
throw createInvalidRelationTargetError(relation);
|
|
48
123
|
}
|
|
49
124
|
const getProvidedRelations = (relations) => RELATIONS.filter((relation) => relations[relation] !== undefined);
|
|
50
125
|
export function validateRelationOptions(usage, relations = {}) {
|
|
@@ -69,10 +144,10 @@ export function validateRelationOptions(usage, relations = {}) {
|
|
|
69
144
|
throw createForbiddenAxisCombinationError(`Relation "${relation}"`, usage);
|
|
70
145
|
}
|
|
71
146
|
const target = relations[relation];
|
|
72
|
-
|
|
147
|
+
const normalizedTarget = normalizeRelationTarget(relation, target);
|
|
73
148
|
return Object.freeze({
|
|
74
149
|
relation,
|
|
75
|
-
target,
|
|
150
|
+
target: normalizedTarget,
|
|
76
151
|
});
|
|
77
152
|
}
|
|
78
153
|
export const relationApplicationHooks = Object.freeze({
|
package/dist/types/index.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export type PaletteKitConfig<I extends string = string, PaletteOutput extends Co
|
|
|
47
47
|
resolverConfig?: ResolverConfigOverrides;
|
|
48
48
|
}>;
|
|
49
49
|
export type PaletteDefaultOutput<PaletteOutput extends ColorOutput | undefined, SystemDefaultOutput extends ColorOutput | undefined> = PaletteOutput extends ColorOutput ? PaletteOutput : SystemDefaultOutput extends ColorOutput ? SystemDefaultOutput : 'oklch';
|
|
50
|
-
type RelationTarget =
|
|
50
|
+
type RelationTarget = PaletteResolveOutput<ColorOutput>;
|
|
51
51
|
type DefaultStateOptions = Readonly<{
|
|
52
52
|
state?: 'default';
|
|
53
53
|
stateDirection?: StateDeltaDirection;
|
|
@@ -15,7 +15,7 @@ export const createUnknownIntentError = (intent) => new PaletteKitError('UNKNOWN
|
|
|
15
15
|
export const createMissingRequiredAxisError = (axis, usage, message) => new PaletteKitError('MISSING_REQUIRED_AXIS', 'resolver-input', message ?? `${axis} is required for usage "${usage}".`, { axis, usage });
|
|
16
16
|
export const createForbiddenAxisCombinationError = (axis, usage, message) => new PaletteKitError('FORBIDDEN_AXIS_COMBINATION', 'resolver-input', message ?? `${axis} is not allowed for usage "${usage}".`, { axis, usage });
|
|
17
17
|
export const createInvalidRelationTargetError = (relation, message) => new PaletteKitError('INVALID_RELATION_TARGET', 'resolver-input', message ??
|
|
18
|
-
`Relation "${relation}" target must be a normalized OKLCH color.`, { relation });
|
|
18
|
+
`Relation "${relation}" target must be a normalized OKLCH color or a serialized Palette Kit color output.`, { relation });
|
|
19
19
|
export const createMultipleRelationsError = (relations) => new PaletteKitError('MULTIPLE_RELATIONS', 'resolver-input', `Only one relation may be provided. Received: ${relations.join(', ')}.`, { relations: [...relations] });
|
|
20
20
|
export const createUnresolvedContextError = () => new PaletteKitError('UNRESOLVED_CONTEXT', 'resolver-input', 'Context could not be resolved. Provide resolverContext, paletteContext, or systemDefaultContext.');
|
|
21
21
|
export const createUnsupportedOutputError = (output, message) => new PaletteKitError('UNSUPPORTED_OUTPUT', 'serialization', message ?? `Unsupported color output "${output}" in Phase 10 serializer.`, { output });
|
package/docs/API.md
CHANGED
|
@@ -80,6 +80,11 @@ const text = palette.resolve({
|
|
|
80
80
|
meet the target after the configured luminance shift and chroma reduction, it
|
|
81
81
|
throws.
|
|
82
82
|
|
|
83
|
+
Relation targets may be any color returned by Palette Kit outputs: `oklch`,
|
|
84
|
+
`oklab`, `srgb`, `p3`, `hex`, or `rgba`. Serialized targets are normalized back
|
|
85
|
+
to OKLCH internally before contrast or layering logic runs. CSS/RN strings such
|
|
86
|
+
as `rgba(...)` are not relation targets.
|
|
87
|
+
|
|
83
88
|
## State Rules
|
|
84
89
|
|
|
85
90
|
`state` defaults to `"default"`.
|
package/docs/Text.md
CHANGED
|
@@ -28,6 +28,10 @@ const text = palette.resolve({
|
|
|
28
28
|
});
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
`on` may receive any color returned by Palette Kit outputs. This supports
|
|
32
|
+
palettes configured with `output: "rgba"` while keeping contrast resolution
|
|
33
|
+
internal to OKLCH.
|
|
34
|
+
|
|
31
35
|
## Current Behavior
|
|
32
36
|
|
|
33
37
|
`on` enforces APCA contrast. The default target is Lc 60. APCA is the primary
|
|
@@ -43,6 +43,17 @@ Convert it to a React Native string if needed:
|
|
|
43
43
|
const rnColor = `rgba(${background.r}, ${background.g}, ${background.b}, ${background.a})`;
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
Use the Palette Kit RGBA object itself for relation targets before converting it
|
|
47
|
+
to a React Native string:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const text = palette.resolve({
|
|
51
|
+
usage: "visualVocabulary",
|
|
52
|
+
intent: "brand",
|
|
53
|
+
on: background,
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
46
57
|
## Resolver Override
|
|
47
58
|
|
|
48
59
|
You can override output per call:
|
package/docs/Validation.md
CHANGED
|
@@ -53,7 +53,9 @@ Relation rules:
|
|
|
53
53
|
- `overlays` forbids `on`.
|
|
54
54
|
- `fill` and `lines` allow `on`.
|
|
55
55
|
|
|
56
|
-
Relation targets
|
|
56
|
+
Relation targets may be normalized OKLCH colors or serialized Palette Kit
|
|
57
|
+
outputs: `oklab`, `srgb`, `p3`, `hex`, or `rgba`. CSS/RN strings such as
|
|
58
|
+
`rgba(...)` are not accepted.
|
|
57
59
|
|
|
58
60
|
## State
|
|
59
61
|
|
package/docs/spec.md
CHANGED
|
@@ -81,6 +81,8 @@ and the current explicit clip gamut strategy.
|
|
|
81
81
|
- Forbidden axis combinations throw.
|
|
82
82
|
- `on` enforces APCA contrast with a default Lc 60 target and exposes WCAG as a
|
|
83
83
|
fallback diagnostic.
|
|
84
|
+
- Relation targets accept normalized OKLCH or any serialized Palette Kit output
|
|
85
|
+
and are normalized back to OKLCH before relation logic runs.
|
|
84
86
|
- `over` applies configured alpha by level.
|
|
85
87
|
- `under` applies configured alpha and luminance reduction by level.
|
|
86
88
|
- State alpha deltas apply only where alpha is allowed, currently `overlays`.
|
package/package.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"repository": {
|
|
58
58
|
"type": "git",
|
|
59
|
-
"url": "https://github.com/claushaas/palette-kit"
|
|
59
|
+
"url": "git+https://github.com/claushaas/palette-kit.git"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
},
|
|
73
73
|
"type": "module",
|
|
74
74
|
"types": "dist/index.d.ts",
|
|
75
|
-
"version": "0.4.
|
|
75
|
+
"version": "0.4.1"
|
|
76
76
|
}
|