srcdev-nuxt-components 9.1.56 → 9.1.57
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/.claude/settings.json +2 -1
- package/package.json +3 -1
- package/ramps.config.mjs +28 -0
- package/scripts/check-ramps.mjs +50 -0
- package/scripts/generate-consumer-ramps.mjs +100 -0
- package/scripts/generate-ramps.mjs +92 -0
package/.claude/settings.json
CHANGED
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
"Bash(rm -rf /Users/simoncornforth/websites/nuxt-components/.claude/projects/)",
|
|
54
54
|
"Bash(SRCDEV_STANDALONE=true npx vitest run app/components/01.atoms/toast)",
|
|
55
55
|
"Bash(SRCDEV_STANDALONE=true npx vitest run app/components/02.molecules/alert-content)",
|
|
56
|
-
"Bash(SRCDEV_STANDALONE=true npx vitest run app/components/01.atoms/prompt)"
|
|
56
|
+
"Bash(SRCDEV_STANDALONE=true npx vitest run app/components/01.atoms/prompt)",
|
|
57
|
+
"Bash(git fetch *)"
|
|
57
58
|
],
|
|
58
59
|
"additionalDirectories": [
|
|
59
60
|
"/Users/simoncornforth/websites/instepreflexology",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srcdev-nuxt-components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.1.
|
|
4
|
+
"version": "9.1.57",
|
|
5
5
|
"main": "nuxt.config.ts",
|
|
6
6
|
"types": "types.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
".claude/",
|
|
35
35
|
"app/",
|
|
36
36
|
"modules/",
|
|
37
|
+
"scripts/",
|
|
38
|
+
"ramps.config.mjs",
|
|
37
39
|
"nuxt.config.ts",
|
|
38
40
|
"types.d.ts"
|
|
39
41
|
],
|
package/ramps.config.mjs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// One source of truth for the oklch colour curve.
|
|
2
|
+
// Edit this file; run `npm run generate:ramps` to rebuild the CSS.
|
|
3
|
+
|
|
4
|
+
/** Lightness % at each step index 00–10 (light → dark) */
|
|
5
|
+
export const LIGHTNESS = [98, 94, 88, 80, 72, 64, 56, 48, 40, 32, 25];
|
|
6
|
+
|
|
7
|
+
/** Chroma multipliers at each step index 00–10.
|
|
8
|
+
* Actual chroma = ramp.chroma × multiplier.
|
|
9
|
+
* Ramps at mid-tone (06) reach full chroma; tapers toward both extremes. */
|
|
10
|
+
export const CHROMA_MULTIPLIERS = [0.045, 0.18, 0.32, 0.5, 0.68, 0.86, 1, 0.95, 0.86, 0.77, 0.64];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Named ramps.
|
|
14
|
+
* hue – oklch hue angle (degrees)
|
|
15
|
+
* chroma – maximum chroma (at the 06 step)
|
|
16
|
+
* drift – optional: rotates hue linearly across steps
|
|
17
|
+
* hue_i = hue + drift × (i / 10)
|
|
18
|
+
* e.g. drift -25 on hue 50 → step 00 = 50°, step 10 = 25°
|
|
19
|
+
*/
|
|
20
|
+
export const ramps = {
|
|
21
|
+
blue: { hue: 255, chroma: 0.22 },
|
|
22
|
+
red: { hue: 30, chroma: 0.24 },
|
|
23
|
+
green: { hue: 157, chroma: 0.19 },
|
|
24
|
+
amber: { hue: 75, chroma: 0.19 },
|
|
25
|
+
orange: { hue: 60, chroma: 0.15 },
|
|
26
|
+
sunset: { hue: 50, chroma: 0.22, drift: -25 },
|
|
27
|
+
slate: { hue: 260, chroma: 0.02 },
|
|
28
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CI check: runs the generator then fails if any generated file changed.
|
|
4
|
+
* Usage: node scripts/check-ramps.mjs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync } from "node:child_process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
|
|
11
|
+
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
12
|
+
|
|
13
|
+
const GENERATED = [
|
|
14
|
+
"app/assets/styles/setup/02.colours/_blue.css",
|
|
15
|
+
"app/assets/styles/setup/02.colours/_red.css",
|
|
16
|
+
"app/assets/styles/setup/02.colours/_green.css",
|
|
17
|
+
"app/assets/styles/setup/02.colours/_amber.css",
|
|
18
|
+
"app/assets/styles/setup/02.colours/_orange.css",
|
|
19
|
+
"app/assets/styles/setup/02.colours/_sunset.css",
|
|
20
|
+
"app/assets/styles/setup/02.colours/_slate.css",
|
|
21
|
+
"app/assets/styles/setup/02.colours/_theme-params.css",
|
|
22
|
+
"app/assets/styles/setup/03.theming/theme-ramp.css",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
console.log("Running generator…");
|
|
26
|
+
execSync("node scripts/generate-ramps.mjs", { cwd: ROOT, stdio: "inherit" });
|
|
27
|
+
|
|
28
|
+
console.log("Checking for diff…");
|
|
29
|
+
const diff = execSync(`git diff --name-only ${GENERATED.join(" ")}`, { cwd: ROOT })
|
|
30
|
+
.toString()
|
|
31
|
+
.trim();
|
|
32
|
+
|
|
33
|
+
if (diff) {
|
|
34
|
+
console.error("\n✗ Generated files are out of date. Run `npm run generate:ramps` and commit:\n");
|
|
35
|
+
console.error(diff);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Also check for untracked generated files
|
|
40
|
+
const untracked = execSync(`git ls-files --others --exclude-standard ${GENERATED.join(" ")}`, { cwd: ROOT })
|
|
41
|
+
.toString()
|
|
42
|
+
.trim();
|
|
43
|
+
|
|
44
|
+
if (untracked) {
|
|
45
|
+
console.error("\n✗ Generated files are new and untracked. Run `npm run generate:ramps` and commit:\n");
|
|
46
|
+
console.error(untracked);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log("✓ Generated files are up to date.");
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Consumer colour ramp generator.
|
|
4
|
+
*
|
|
5
|
+
* Run from your consuming app's project root:
|
|
6
|
+
* node node_modules/srcdev-nuxt-components/scripts/generate-consumer-ramps.mjs
|
|
7
|
+
*
|
|
8
|
+
* Reads: ramps.config.mjs in your project root
|
|
9
|
+
* Writes: app/assets/styles/setup/02.colours/ in your project root
|
|
10
|
+
*
|
|
11
|
+
* Your ramps.config.mjs can contain any combination of:
|
|
12
|
+
* - New palettes (e.g. gold, copper) — adds --gold-00..10 and --palette-gold-* vars
|
|
13
|
+
* - Built-in names (e.g. blue, red, green) — your generated file overrides the layer's
|
|
14
|
+
* values because consumer CSS loads after layer CSS in the cascade
|
|
15
|
+
*
|
|
16
|
+
* Example ramps.config.mjs:
|
|
17
|
+
* export const ramps = {
|
|
18
|
+
* gold: { hue: 85, chroma: 0.20 },
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* Produces:
|
|
22
|
+
* _gold.css — --gold-00 … --gold-10 (literal oklch values)
|
|
23
|
+
* _palette-params.css — --palette-gold-hue, --palette-gold-chroma
|
|
24
|
+
*
|
|
25
|
+
* Import both in your app/assets/styles/setup/02.colours/index.css, then
|
|
26
|
+
* reference --gold-09 etc. in your theme override files.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
30
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
31
|
+
import { dirname, join } from "node:path";
|
|
32
|
+
|
|
33
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const LAYER_ROOT = join(__dirname, "..");
|
|
35
|
+
const CONSUMER_ROOT = process.cwd();
|
|
36
|
+
|
|
37
|
+
// Lightness / chroma curves come from the layer so all ramps share the same curve shape
|
|
38
|
+
const { LIGHTNESS, CHROMA_MULTIPLIERS } = await import(pathToFileURL(join(LAYER_ROOT, "ramps.config.mjs")).href);
|
|
39
|
+
|
|
40
|
+
// Consumer's palette definitions
|
|
41
|
+
let consumerRamps;
|
|
42
|
+
try {
|
|
43
|
+
const config = await import(pathToFileURL(join(CONSUMER_ROOT, "ramps.config.mjs")).href);
|
|
44
|
+
consumerRamps = config.ramps;
|
|
45
|
+
if (!consumerRamps || typeof consumerRamps !== "object") throw new Error("ramps export missing");
|
|
46
|
+
} catch {
|
|
47
|
+
console.error("✗ Could not load ramps.config.mjs from your project root.");
|
|
48
|
+
console.error(" Create one exporting a `ramps` object, e.g.:");
|
|
49
|
+
console.error(" export const ramps = {");
|
|
50
|
+
console.error(" gold: { hue: 85, chroma: 0.20 },");
|
|
51
|
+
console.error(" };");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const COLOURS_DIR = join(CONSUMER_ROOT, "app/assets/styles/setup/02.colours");
|
|
56
|
+
mkdirSync(COLOURS_DIR, { recursive: true });
|
|
57
|
+
|
|
58
|
+
const HEADER = "/* GENERATED — edit your ramps.config.mjs, not this file */";
|
|
59
|
+
|
|
60
|
+
/** Round to at most `places` decimal places, stripping trailing zeros. */
|
|
61
|
+
function r(n, places = 4) {
|
|
62
|
+
return parseFloat(n.toFixed(places));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Individual colour files ──────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
for (const [name, cfg] of Object.entries(consumerRamps)) {
|
|
68
|
+
const { hue, chroma, drift = 0 } = cfg;
|
|
69
|
+
const lines = [HEADER, ":where(html) {"];
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i <= 10; i++) {
|
|
72
|
+
const step = String(i).padStart(2, "0");
|
|
73
|
+
const L = LIGHTNESS[i];
|
|
74
|
+
const C = r(chroma * CHROMA_MULTIPLIERS[i]);
|
|
75
|
+
const H = drift !== 0 ? r(hue + drift * (i / 10), 1) : hue;
|
|
76
|
+
lines.push(` --${name}-${step}: oklch(${L}% ${C} ${H});`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
lines.push("}");
|
|
80
|
+
writeFileSync(join(COLOURS_DIR, `_${name}.css`), lines.join("\n") + "\n");
|
|
81
|
+
console.log(` ✓ _${name}.css`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ─── _palette-params.css ─────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
const paramLines = [HEADER, ":where(html) {"];
|
|
87
|
+
for (const [name, { hue, chroma, drift }] of Object.entries(consumerRamps)) {
|
|
88
|
+
paramLines.push(` --palette-${name}-hue: ${hue};`);
|
|
89
|
+
paramLines.push(` --palette-${name}-chroma: ${chroma};`);
|
|
90
|
+
if (drift !== undefined) paramLines.push(` --palette-${name}-drift: ${drift};`);
|
|
91
|
+
}
|
|
92
|
+
paramLines.push("}");
|
|
93
|
+
writeFileSync(join(COLOURS_DIR, "_palette-params.css"), paramLines.join("\n") + "\n");
|
|
94
|
+
console.log(" ✓ _palette-params.css");
|
|
95
|
+
|
|
96
|
+
console.log("");
|
|
97
|
+
console.log("Done. Next steps:");
|
|
98
|
+
console.log(" 1. Import generated files in app/assets/styles/setup/02.colours/index.css");
|
|
99
|
+
console.log(" 2. Reference --palette-*-hue / --palette-*-chroma in your theme override");
|
|
100
|
+
console.log(" 3. Use named steps (e.g. var(--gold-09)) in page-level token overrides");
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generates colour ramp CSS from ramps.config.mjs.
|
|
4
|
+
* Outputs:
|
|
5
|
+
* app/assets/styles/setup/02.colours/_{name}.css — per-ramp literal values
|
|
6
|
+
* app/assets/styles/setup/02.colours/_theme-params.css — --palette-*-hue/chroma vars
|
|
7
|
+
* app/assets/styles/setup/03.theming/theme-ramp.css — parametric --colour-theme-* slot
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { writeFileSync } from "node:fs";
|
|
11
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const ROOT = join(__dirname, "..");
|
|
16
|
+
|
|
17
|
+
const { LIGHTNESS, CHROMA_MULTIPLIERS, ramps } = await import(pathToFileURL(join(ROOT, "ramps.config.mjs")).href);
|
|
18
|
+
|
|
19
|
+
const COLOURS_DIR = join(ROOT, "app/assets/styles/setup/02.colours");
|
|
20
|
+
const THEMING_DIR = join(ROOT, "app/assets/styles/setup/03.theming");
|
|
21
|
+
|
|
22
|
+
const HEADER = "/* GENERATED — edit ramps.config.mjs, not this file */";
|
|
23
|
+
|
|
24
|
+
/** Round to at most `places` decimal places, stripping trailing zeros. */
|
|
25
|
+
function r(n, places = 4) {
|
|
26
|
+
return parseFloat(n.toFixed(places));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Individual ramp files ───────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
for (const [name, cfg] of Object.entries(ramps)) {
|
|
32
|
+
const { hue, chroma, drift = 0 } = cfg;
|
|
33
|
+
const lines = [HEADER, ":where(html) {"];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i <= 10; i++) {
|
|
36
|
+
const step = String(i).padStart(2, "0");
|
|
37
|
+
const L = LIGHTNESS[i];
|
|
38
|
+
const C = r(chroma * CHROMA_MULTIPLIERS[i]);
|
|
39
|
+
const H = drift !== 0 ? r(hue + drift * (i / 10), 1) : hue;
|
|
40
|
+
lines.push(` --${name}-${step}: oklch(${L}% ${C} ${H});`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
lines.push("}");
|
|
44
|
+
writeFileSync(join(COLOURS_DIR, `_${name}.css`), lines.join("\n") + "\n");
|
|
45
|
+
console.log(` ✓ _${name}.css`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── _theme-params.css ────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
const paramLines = [HEADER, ":where(html) {"];
|
|
51
|
+
for (const [name, { hue, chroma, drift }] of Object.entries(ramps)) {
|
|
52
|
+
paramLines.push(` --palette-${name}-hue: ${hue};`);
|
|
53
|
+
paramLines.push(` --palette-${name}-chroma: ${chroma};`);
|
|
54
|
+
if (drift !== undefined) paramLines.push(` --palette-${name}-drift: ${drift};`);
|
|
55
|
+
}
|
|
56
|
+
paramLines.push("}");
|
|
57
|
+
writeFileSync(join(COLOURS_DIR, "_theme-params.css"), paramLines.join("\n") + "\n");
|
|
58
|
+
console.log(" ✓ _theme-params.css");
|
|
59
|
+
|
|
60
|
+
// ─── theme-ramp.css ───────────────────────────────────────────────────────────
|
|
61
|
+
// Declares --colour-theme-0..10 as oklch formulas on every element that can
|
|
62
|
+
// carry --theme-hue / --theme-chroma. The selector list must include every
|
|
63
|
+
// element that can redefine those vars so the formula re-evaluates there.
|
|
64
|
+
// --theme-hue-drift defaults to 0; set it (e.g. var(--palette-sunset-drift))
|
|
65
|
+
// to rotate hue linearly across steps, matching the ramp drift behaviour.
|
|
66
|
+
|
|
67
|
+
const rampLines = [
|
|
68
|
+
HEADER,
|
|
69
|
+
"/* Parametric theme-colour slot. Set --theme-hue and --theme-chroma on a",
|
|
70
|
+
" theme selector; these vars automatically pick up the right palette. */",
|
|
71
|
+
":where(html, [data-theme], [data-invalid]) {",
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i <= 10; i++) {
|
|
75
|
+
const L = LIGHTNESS[i];
|
|
76
|
+
const M = CHROMA_MULTIPLIERS[i];
|
|
77
|
+
const fraction = i === 0 ? "0" : i === 10 ? "1" : `${i} / 10`;
|
|
78
|
+
rampLines.push(
|
|
79
|
+
` --colour-theme-${i}: oklch(${L}% calc(var(--theme-chroma) * ${M}) calc(var(--theme-hue) + var(--theme-hue-drift, 0) * (${fraction})));`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Alias out-of-range indices used by older component code
|
|
84
|
+
rampLines.push(" /* Aliases for legacy --colour-theme-11 / -12 references */");
|
|
85
|
+
rampLines.push(" --colour-theme-11: var(--colour-theme-10);");
|
|
86
|
+
rampLines.push(" --colour-theme-12: var(--colour-theme-10);");
|
|
87
|
+
rampLines.push("}");
|
|
88
|
+
|
|
89
|
+
writeFileSync(join(THEMING_DIR, "theme-ramp.css"), rampLines.join("\n") + "\n");
|
|
90
|
+
console.log(" ✓ theme-ramp.css");
|
|
91
|
+
|
|
92
|
+
console.log("Done.");
|