@stridge/noctis-theme-engine 1.0.0-beta.0 → 1.0.0-beta.2
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/README.md +2 -0
- package/dist/color/oklch.js +2 -0
- package/dist/generate/theme.js +8 -4
- package/dist/presets.d.ts +6 -2
- package/dist/presets.js +14 -10
- package/dist/react/provider.d.ts +9 -0
- package/dist/react/provider.js +4 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @stridge/noctis-theme-engine
|
|
2
2
|
|
|
3
|
+
 
|
|
4
|
+
|
|
3
5
|
The framework-agnostic OKLCH theme generator at the base of the design system. From a
|
|
4
6
|
`{ background, accent, contrast }` input it produces the complete private primitive ramp
|
|
5
7
|
(`--noctis-engine-*`) — plus the elevation-scope re-derivations — as CSS variables, with
|
package/dist/color/oklch.js
CHANGED
|
@@ -84,6 +84,7 @@ function mix(x, y, t) {
|
|
|
84
84
|
/** Reduce chroma until the color sits inside the sRGB gamut, preserving L, H and alpha. */
|
|
85
85
|
function toSrgbGamut(o) {
|
|
86
86
|
const mapped = clampChroma(toCulori(o), "oklch", "rgb");
|
|
87
|
+
/* v8 ignore next -- defensive: clampChroma never returns undefined for a valid input */
|
|
87
88
|
return mapped ? fromCulori(mapped, o.h) : o;
|
|
88
89
|
}
|
|
89
90
|
function round(value, places) {
|
|
@@ -100,6 +101,7 @@ function toCss(o) {
|
|
|
100
101
|
const l = round(g.l, 4);
|
|
101
102
|
const c = round(g.c, 4);
|
|
102
103
|
const h = c === 0 ? 0 : round(g.h, 2);
|
|
104
|
+
/* v8 ignore next -- defensive: g.a is always set by toSrgbGamut/clampOklch */
|
|
103
105
|
const a = g.a ?? 1;
|
|
104
106
|
const base = `oklch(${l} ${c} ${h}`;
|
|
105
107
|
return a < 1 ? `${base} / ${round(a, 3)})` : `${base})`;
|
package/dist/generate/theme.js
CHANGED
|
@@ -139,10 +139,14 @@ function generateTheme(input, options) {
|
|
|
139
139
|
const magnitude = Math.abs(gain);
|
|
140
140
|
const controlGain = (bright ? -.8 : 1) * (contrast / 70);
|
|
141
141
|
const mutedAmount = bright ? MUTED_MIX.light : MUTED_MIX.dark;
|
|
142
|
-
const bg = (bright ? BG_L_STEPS_LIGHT : BG_L_STEPS).map((lStep, i) =>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
const bg = (bright ? BG_L_STEPS_LIGHT : BG_L_STEPS).map((lStep, i) => {
|
|
143
|
+
/* v8 ignore next 2 -- defensive: BG_C_STEPS[i] is always defined for every bgSteps index */
|
|
144
|
+
const cStep = BG_C_STEPS[i] ?? 0;
|
|
145
|
+
return adjust(canvas, {
|
|
146
|
+
l: lStep * magnitude,
|
|
147
|
+
c: cStep * magnitude
|
|
148
|
+
});
|
|
149
|
+
});
|
|
146
150
|
const bgSunken = adjust(canvas, { l: -(bright ? SUNKEN_L_STEP_LIGHT : SUNKEN_L_STEP) * magnitude });
|
|
147
151
|
const bgSelected = overrideColor("bg-selected") ?? mix(canvas, accent, bright ? SELECTED_MIX.light : SELECTED_MIX.dark);
|
|
148
152
|
const bgSelectedHover = adjust(bgSelected, {
|
package/dist/presets.d.ts
CHANGED
|
@@ -15,8 +15,12 @@ interface ThemePreset extends ThemeInput {
|
|
|
15
15
|
*/
|
|
16
16
|
declare const defaultPreset: ThemePreset;
|
|
17
17
|
/**
|
|
18
|
-
* The
|
|
19
|
-
*
|
|
18
|
+
* The presets Noctis itself ships — the neutral core of the system, no chromatic identities. Dark is
|
|
19
|
+
* the default; Light is its inverse; the two High Contrast variants keep the same canvas and accent but
|
|
20
|
+
* push the contrast knob up for low-vision and bright-environment reading. Brand/showcase themes (a
|
|
21
|
+
* violet Midnight, a green Forest, a warm Sunset, …) are an application concern, not the package's — the
|
|
22
|
+
* engine generates any seed at runtime, so a consumer assembles its own palette and passes it to the
|
|
23
|
+
* provider's `presets`. Seeds are authored in OKLCH so the calibration is exact.
|
|
20
24
|
*/
|
|
21
25
|
declare const presets: readonly ThemePreset[];
|
|
22
26
|
//#endregion
|
package/dist/presets.js
CHANGED
|
@@ -14,8 +14,12 @@ const defaultPreset = {
|
|
|
14
14
|
contrast: 30
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
17
|
-
* The
|
|
18
|
-
*
|
|
17
|
+
* The presets Noctis itself ships — the neutral core of the system, no chromatic identities. Dark is
|
|
18
|
+
* the default; Light is its inverse; the two High Contrast variants keep the same canvas and accent but
|
|
19
|
+
* push the contrast knob up for low-vision and bright-environment reading. Brand/showcase themes (a
|
|
20
|
+
* violet Midnight, a green Forest, a warm Sunset, …) are an application concern, not the package's — the
|
|
21
|
+
* engine generates any seed at runtime, so a consumer assembles its own palette and passes it to the
|
|
22
|
+
* provider's `presets`. Seeds are authored in OKLCH so the calibration is exact.
|
|
19
23
|
*/
|
|
20
24
|
const presets = [
|
|
21
25
|
defaultPreset,
|
|
@@ -26,16 +30,16 @@ const presets = [
|
|
|
26
30
|
contrast: 30
|
|
27
31
|
},
|
|
28
32
|
{
|
|
29
|
-
name: "
|
|
30
|
-
background: "oklch(0.
|
|
31
|
-
accent: "oklch(0.
|
|
32
|
-
contrast:
|
|
33
|
+
name: "Dark High Contrast",
|
|
34
|
+
background: "oklch(0.1711 0.0011 271)",
|
|
35
|
+
accent: "oklch(0.5674 0.1585 275)",
|
|
36
|
+
contrast: 70
|
|
33
37
|
},
|
|
34
38
|
{
|
|
35
|
-
name: "
|
|
36
|
-
background: "oklch(0.
|
|
37
|
-
accent: "oklch(0.
|
|
38
|
-
contrast:
|
|
39
|
+
name: "Light High Contrast",
|
|
40
|
+
background: "oklch(0.991 0.002 271)",
|
|
41
|
+
accent: "oklch(0.5674 0.1585 275)",
|
|
42
|
+
contrast: 70
|
|
39
43
|
}
|
|
40
44
|
];
|
|
41
45
|
//#endregion
|
package/dist/react/provider.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ThemeInput, ThemeOverrides } from "../generate/tokens.js";
|
|
2
|
+
import { ThemePreset } from "../presets.js";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
|
|
4
5
|
//#region src/react/provider.d.ts
|
|
@@ -11,6 +12,13 @@ interface ThemeProviderProps {
|
|
|
11
12
|
* uncontrolled case. Overrides participate in derivation — see `ThemeOverrides`.
|
|
12
13
|
*/
|
|
13
14
|
overrides?: ThemeOverrides;
|
|
15
|
+
/**
|
|
16
|
+
* The selectable presets exposed to the tree via `useTheme().presets` — the named seeds a theme
|
|
17
|
+
* picker offers. Defaults to the presets Noctis ships (Dark, Light, and their High Contrast
|
|
18
|
+
* variants); pass your own to advertise a custom palette. Purely the picker's menu — it does not
|
|
19
|
+
* change the active theme, which is driven by `initialInput`/`setTheme`.
|
|
20
|
+
*/
|
|
21
|
+
presets?: readonly ThemePreset[];
|
|
14
22
|
children: ReactNode;
|
|
15
23
|
}
|
|
16
24
|
/**
|
|
@@ -22,6 +30,7 @@ interface ThemeProviderProps {
|
|
|
22
30
|
declare function ThemeProvider({
|
|
23
31
|
initialInput,
|
|
24
32
|
overrides: overridesProp,
|
|
33
|
+
presets,
|
|
25
34
|
children
|
|
26
35
|
}: ThemeProviderProps): ReactNode;
|
|
27
36
|
//#endregion
|
package/dist/react/provider.js
CHANGED
|
@@ -13,7 +13,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
13
13
|
* localStorage) so the next load's SSR seed and blocking script repaint correctly. Exposes
|
|
14
14
|
* {@link useTheme}.
|
|
15
15
|
*/
|
|
16
|
-
function ThemeProvider({ initialInput, overrides: overridesProp, children }) {
|
|
16
|
+
function ThemeProvider({ initialInput, overrides: overridesProp, presets: presets$1 = presets, children }) {
|
|
17
17
|
const [input, setInput] = useState(() => initialInput ?? readPersistedInput() ?? defaultPreset);
|
|
18
18
|
const [localOverrides, setLocalOverrides] = useState(() => initialInput ? void 0 : readPersistedOverrides() ?? void 0);
|
|
19
19
|
const overrides = overridesProp ?? localOverrides;
|
|
@@ -37,12 +37,13 @@ function ThemeProvider({ initialInput, overrides: overridesProp, children }) {
|
|
|
37
37
|
setTheme,
|
|
38
38
|
overrides,
|
|
39
39
|
setOverrides,
|
|
40
|
-
presets
|
|
40
|
+
presets: presets$1
|
|
41
41
|
}), [
|
|
42
42
|
input,
|
|
43
43
|
setTheme,
|
|
44
44
|
overrides,
|
|
45
|
-
setOverrides
|
|
45
|
+
setOverrides,
|
|
46
|
+
presets$1
|
|
46
47
|
]),
|
|
47
48
|
children
|
|
48
49
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stridge/noctis-theme-engine",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"tsdown": "0.21.10",
|
|
48
48
|
"typescript": "6.0.3",
|
|
49
49
|
"vitest": "4.1.8",
|
|
50
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
50
51
|
"@stridge/noctis-typescript": "0.0.0"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
@@ -54,6 +55,6 @@
|
|
|
54
55
|
"check:publint": "publint",
|
|
55
56
|
"check:publish": "pnpm run check:publint",
|
|
56
57
|
"check:types": "tsc --noEmit",
|
|
57
|
-
"test": "vitest --run"
|
|
58
|
+
"test": "vitest --run --coverage"
|
|
58
59
|
}
|
|
59
60
|
}
|