srcdev-nuxt-components 9.1.55 → 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 +30 -2
- package/.claude/skills/components/alert-content-inner.md +60 -0
- package/.claude/skills/components/display-dialog.md +80 -0
- package/.claude/skills/components/display-prompt.md +163 -0
- package/.claude/skills/components/display-toast.md +235 -0
- package/.claude/skills/index.md +7 -3
- package/.claude/skills/theming-colour-ramps.md +328 -0
- package/.claude/skills/theming-override-default.md +115 -186
- package/.claude/skills/theming-partial-override.md +88 -75
- package/README.md +166 -0
- package/app/app.config.ts +44 -0
- package/app/assets/styles/setup/01.config/_head.css +3 -3
- package/app/assets/styles/setup/02.colours/_amber.css +12 -10
- package/app/assets/styles/setup/02.colours/_blue.css +10 -9
- package/app/assets/styles/setup/02.colours/_green.css +12 -11
- package/app/assets/styles/setup/02.colours/_orange.css +12 -10
- package/app/assets/styles/setup/02.colours/_red.css +10 -9
- package/app/assets/styles/setup/02.colours/_slate.css +12 -11
- package/app/assets/styles/setup/02.colours/_sunset.css +12 -10
- package/app/assets/styles/setup/02.colours/_theme-params.css +18 -0
- package/app/assets/styles/setup/02.colours/index.css +1 -0
- package/app/assets/styles/setup/03.theming/_default.css +87 -0
- package/app/assets/styles/setup/03.theming/_error.css +14 -0
- package/app/assets/styles/setup/03.theming/_success.css +4 -0
- package/app/assets/styles/setup/03.theming/_theme-slots.css +16 -0
- package/app/assets/styles/setup/03.theming/_warning.css +13 -0
- package/app/assets/styles/setup/03.theming/index.css +6 -4
- package/app/assets/styles/setup/03.theming/theme-ramp.css +19 -0
- package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +0 -7
- package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
- package/app/assets/styles/setup/index.css +1 -1
- package/app/components/01.atoms/display-dialog/DisplayDialog.vue +74 -24
- package/app/components/01.atoms/display-dialog/tests/DisplayDialog.spec.ts +148 -14
- package/app/components/01.atoms/display-dialog/tests/__snapshots__/DisplayDialog.spec.ts.snap +3 -3
- package/app/components/01.atoms/prompt/DisplayPrompt.vue +113 -0
- package/app/components/01.atoms/prompt/stories/DisplayPrompt.stories.ts +112 -0
- package/app/components/01.atoms/prompt/tests/DisplayPrompt.spec.ts +205 -0
- package/app/components/01.atoms/toast/DisplayToast.vue +327 -0
- package/app/components/01.atoms/toast/DisplayToastProvider.vue +362 -0
- package/app/components/01.atoms/toast/stories/DisplayToast.stories.ts +195 -0
- package/app/components/01.atoms/toast/stories/DisplayToastProvider.stories.ts +142 -0
- package/app/components/01.atoms/toast/tests/DisplayToast.spec.ts +266 -0
- package/app/components/01.atoms/toast/tests/DisplayToastProvider.spec.ts +256 -0
- package/app/components/02.molecules/action-menu/ActionMenu.vue +8 -1
- package/app/components/02.molecules/action-menu/ActionMenuItemCore.vue +11 -3
- package/app/components/02.molecules/alert-content/AlertContent.vue +67 -0
- package/app/components/02.molecules/alert-content/AlertContentInner.vue +153 -0
- package/app/components/02.molecules/alert-content/stories/AlertContent.stories.ts +131 -0
- package/app/components/02.molecules/alert-content/tests/AlertContent.spec.ts +135 -0
- package/app/components/02.molecules/alert-content/tests/AlertContentInner.spec.ts +135 -0
- package/app/components/02.molecules/alert-masked-content/AlertMaskedContent.vue +78 -0
- package/app/components/02.molecules/alert-masked-content/stories/AlertMaskedContent.stories.ts +132 -0
- package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +149 -0
- package/app/components/05.forms/input-button/InputButtonCore.vue +35 -39
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +8 -8
- package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioCore.vue +5 -5
- package/app/components/05.forms/input-copy/InputCopyCore.vue +7 -7
- package/app/components/05.forms/input-number/InputNumberCore.vue +5 -5
- package/app/components/05.forms/input-select/InputSelectCore.vue +6 -6
- package/app/components/05.forms/input-text/InputTextCore.vue +6 -6
- package/app/components/05.forms/input-text/variants/InputTextAsNumberWithLabel.vue +2 -2
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +5 -5
- package/app/components/05.forms/pending-effect/PendingEffect.vue +60 -60
- package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +10 -10
- package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +4 -4
- package/app/components/display-theme-switch/DisplayThemeSwitch.vue +4 -4
- package/app/components/display-tooltip/DisplayTooltip.vue +4 -4
- package/app/composables/useBodyLock.ts +21 -0
- package/app/composables/useDialogControls.ts +11 -11
- package/app/composables/useToastQueue.ts +39 -0
- package/app/layouts/default.vue +1 -1
- package/app/pages/ui/display-dialog.vue +275 -127
- package/app/pages/ui/display-prompt.vue +255 -52
- package/app/pages/ui/display-toast.vue +278 -299
- package/app/pages/ui/mask-element.vue +25 -3
- package/app/pages/ui/settings.vue +9 -35
- package/app/types/app-config.d.ts +41 -0
- package/app/types/components/display-prompt.d.ts +11 -0
- package/app/types/components/display-toast.d.ts +11 -9
- package/app/types/components/index.ts +2 -0
- package/app/types/components/semantic-theme.d.ts +1 -0
- package/nuxt.config.ts +16 -0
- package/package.json +8 -4
- 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/app/assets/styles/setup/03.theming/default/_dark.css +0 -156
- package/app/assets/styles/setup/03.theming/default/_light.css +0 -166
- package/app/assets/styles/setup/03.theming/default/index.css +0 -2
- package/app/assets/styles/setup/03.theming/error/_dark.css +0 -56
- package/app/assets/styles/setup/03.theming/error/_light.css +0 -74
- package/app/assets/styles/setup/03.theming/error/index.css +0 -2
- package/app/assets/styles/setup/03.theming/success/_dark.css +0 -50
- package/app/assets/styles/setup/03.theming/success/_light.css +0 -65
- package/app/assets/styles/setup/03.theming/success/index.css +0 -2
- package/app/assets/styles/setup/03.theming/warning/_dark.css +0 -50
- package/app/assets/styles/setup/03.theming/warning/_light.css +0 -64
- package/app/assets/styles/setup/03.theming/warning/index.css +0 -2
- package/app/components/display-prompt/DisplayPromptCore.vue +0 -228
- package/app/components/display-prompt/variants/DisplayPromptError.vue +0 -53
- package/app/components/display-toast/DisplayToast.vue +0 -447
- package/app/components/display-toast/molecules/DefaultToastContent.vue +0 -163
- package/app/components/display-toast/stories/DisplayToast.stories.ts +0 -380
|
@@ -10,38 +10,12 @@
|
|
|
10
10
|
</PageRow>
|
|
11
11
|
|
|
12
12
|
<PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
|
|
13
|
-
<
|
|
13
|
+
<div class="theme-test" data-theme="error">
|
|
14
|
+
<h2 class="page-heading-2 mbe-18">Theme test (error)</h2>
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
</div>
|
|
19
|
-
</PageRow>
|
|
20
|
-
|
|
21
|
-
<PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
|
|
22
|
-
<h1 class="page-heading-1 mbe-18">Primary button elements</h1>
|
|
23
|
-
|
|
24
|
-
<div class="mb-12" data-theme="default">
|
|
25
|
-
<p class="page-body-normal mbe-12"><code>class="test-button" data-theme="default"</code></p>
|
|
26
|
-
<button class="input-button-core" type="button">This is a button</button>
|
|
27
|
-
</div>
|
|
28
|
-
</PageRow>
|
|
29
|
-
|
|
30
|
-
<PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
|
|
31
|
-
<h1 class="page-heading-1 mbe-18">Error button elements</h1>
|
|
32
|
-
|
|
33
|
-
<div class="mb-12" data-theme="default" data-error>
|
|
34
|
-
<p class="page-body-normal mbe-12"><code>class="test-button" data-theme="error"</code></p>
|
|
35
|
-
<button class="input-button-core" type="button">This is a button</button>
|
|
36
|
-
</div>
|
|
37
|
-
</PageRow>
|
|
38
|
-
|
|
39
|
-
<PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
|
|
40
|
-
<h1 class="page-heading-1 mbe-18">Success button elements</h1>
|
|
41
|
-
|
|
42
|
-
<div class="mb-12" data-theme="success">
|
|
43
|
-
<p class="page-body-normal mbe-12"><code>class="test-button" data-theme="success"</code></p>
|
|
44
|
-
<button class="input-button-core" type="button">This is a button</button>
|
|
16
|
+
<div class="mb-12">
|
|
17
|
+
<p class="page-body-normal mbe-12">This is test text</p>
|
|
18
|
+
</div>
|
|
45
19
|
</div>
|
|
46
20
|
</PageRow>
|
|
47
21
|
</template>
|
|
@@ -65,16 +39,16 @@ useHead({
|
|
|
65
39
|
|
|
66
40
|
<style lang="css">
|
|
67
41
|
.settings-page {
|
|
68
|
-
.test
|
|
69
|
-
|
|
42
|
+
.theme-test {
|
|
43
|
+
margin: 2rem;
|
|
70
44
|
padding: 0.6rem 1.2rem;
|
|
71
45
|
|
|
72
46
|
border: var(--colour-theme-10) solid 1px;
|
|
73
47
|
outline: var(--colour-theme-10) solid 1px;
|
|
74
48
|
outline-offset: 3px;
|
|
75
49
|
background-color: var(--colour-theme-1);
|
|
76
|
-
color: var(--colour-theme-
|
|
77
|
-
border-radius:
|
|
50
|
+
color: var(--colour-theme-7);
|
|
51
|
+
border-radius: 0.4rem;
|
|
78
52
|
}
|
|
79
53
|
}
|
|
80
54
|
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SemanticTheme, DisplayPromptTheme, DisplayToastTheme, DisplayToastPosition, DisplayToastAlignment } from "./components"
|
|
2
|
+
|
|
3
|
+
declare module "@nuxt/schema" {
|
|
4
|
+
interface AppConfigInput {
|
|
5
|
+
srcdev?: {
|
|
6
|
+
alertContent?: {
|
|
7
|
+
icons?: Partial<Record<SemanticTheme, string>>
|
|
8
|
+
dismissIcon?: string
|
|
9
|
+
}
|
|
10
|
+
displayPrompt?: {
|
|
11
|
+
theme?: DisplayPromptTheme
|
|
12
|
+
dismissible?: boolean
|
|
13
|
+
useAutoFocus?: boolean
|
|
14
|
+
masked?: boolean
|
|
15
|
+
}
|
|
16
|
+
displayToast?: {
|
|
17
|
+
appearance?: {
|
|
18
|
+
theme?: DisplayToastTheme
|
|
19
|
+
position?: DisplayToastPosition
|
|
20
|
+
alignment?: DisplayToastAlignment
|
|
21
|
+
fullWidth?: boolean
|
|
22
|
+
masked?: boolean
|
|
23
|
+
}
|
|
24
|
+
behavior?: {
|
|
25
|
+
autoDismiss?: boolean
|
|
26
|
+
duration?: number
|
|
27
|
+
revealDuration?: number
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
displayDialog?: {
|
|
31
|
+
variant?: "dialog" | "modal" | "confirm" | "alert" | "fullscreen"
|
|
32
|
+
justifyDialog?: "start" | "center" | "end"
|
|
33
|
+
alignDialog?: "start" | "center" | "end"
|
|
34
|
+
lockViewport?: boolean
|
|
35
|
+
allowContentScroll?: boolean
|
|
36
|
+
theme?: SemanticTheme
|
|
37
|
+
closeIcon?: string
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SemanticTheme } from "./semantic-theme.d"
|
|
2
|
+
|
|
3
|
+
export type DisplayPromptTheme = SemanticTheme
|
|
4
|
+
|
|
5
|
+
export interface DisplayPromptProps {
|
|
6
|
+
theme?: DisplayPromptTheme
|
|
7
|
+
dismissible?: boolean
|
|
8
|
+
useAutoFocus?: boolean
|
|
9
|
+
masked?: boolean
|
|
10
|
+
styleClassPassthrough?: string | string[]
|
|
11
|
+
}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { VNode, ComponentPublicInstance } from "vue"
|
|
2
|
+
import type { SemanticTheme } from "./semantic-theme.d"
|
|
2
3
|
|
|
3
|
-
export type DisplayToastTheme =
|
|
4
|
-
| "primary"
|
|
5
|
-
| "secondary"
|
|
6
|
-
| "tertiary"
|
|
7
|
-
| "ghost"
|
|
8
|
-
| "error"
|
|
9
|
-
| "info"
|
|
10
|
-
| "success"
|
|
11
|
-
| "warning"
|
|
4
|
+
export type DisplayToastTheme = SemanticTheme
|
|
12
5
|
|
|
13
6
|
export type DisplayToastPosition = "top" | "bottom"
|
|
14
7
|
export type DisplayToastAlignment = "left" | "center" | "right"
|
|
@@ -18,6 +11,7 @@ export interface DisplayToastAppearanceConfig {
|
|
|
18
11
|
position?: DisplayToastPosition
|
|
19
12
|
alignment?: DisplayToastAlignment
|
|
20
13
|
fullWidth?: boolean
|
|
14
|
+
masked?: boolean
|
|
21
15
|
}
|
|
22
16
|
|
|
23
17
|
export interface DisplayToastBehaviorConfig {
|
|
@@ -45,6 +39,14 @@ export interface DisplayToastProps {
|
|
|
45
39
|
styleClassPassthrough?: string | string[]
|
|
46
40
|
}
|
|
47
41
|
|
|
42
|
+
export type ToastQueueStatus = "pending" | "visible"
|
|
43
|
+
|
|
44
|
+
export interface ToastQueueEntry {
|
|
45
|
+
id: string
|
|
46
|
+
config: DisplayToastConfig
|
|
47
|
+
status: ToastQueueStatus
|
|
48
|
+
}
|
|
49
|
+
|
|
48
50
|
export interface ToastSlots {
|
|
49
51
|
default?(props?: Record<string, never>): VNode[]
|
|
50
52
|
customToastIcon?(props?: Record<string, never>): VNode[]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Export types from the components directory
|
|
2
|
+
export * from "./semantic-theme.d"
|
|
2
3
|
export * from "./carousel-basic.d"
|
|
3
4
|
export * from "./slider-gallery.d"
|
|
4
5
|
export * from "./canvas-switcher.d"
|
|
@@ -6,6 +7,7 @@ export * from "./display-chip.d"
|
|
|
6
7
|
export * from "./tabs.d"
|
|
7
8
|
export * from "./responsive-header.d"
|
|
8
9
|
export * from "./display-toast.d"
|
|
10
|
+
export * from "./display-prompt.d"
|
|
9
11
|
export * from "./qr-code.d"
|
|
10
12
|
export * from "./alert-mask-core.d"
|
|
11
13
|
export * from "./hero-text"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type SemanticTheme = "info" | "success" | "warning" | "error"
|
package/nuxt.config.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
2
2
|
import { createResolver } from "@nuxt/kit";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
3
5
|
|
|
4
6
|
const { resolve } = createResolver(import.meta.url);
|
|
5
7
|
const isStandalone = !!process.env.SRCDEV_STANDALONE;
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
6
9
|
|
|
7
10
|
export default defineNuxtConfig({
|
|
8
11
|
// debug: !isProduction,
|
|
@@ -126,6 +129,19 @@ export default defineNuxtConfig({
|
|
|
126
129
|
vue: {
|
|
127
130
|
runtimeCompiler: true,
|
|
128
131
|
},
|
|
132
|
+
// Re-generate ramps on config change during local dev.
|
|
133
|
+
// Guarded by isStandalone so consumers extending this layer never trigger
|
|
134
|
+
// generation inside node_modules (they never set SRCDEV_STANDALONE).
|
|
135
|
+
hooks: isStandalone
|
|
136
|
+
? {
|
|
137
|
+
async "builder:watch"(_event, path) {
|
|
138
|
+
if (!path.endsWith("ramps.config.mjs")) return;
|
|
139
|
+
console.log("[ramps] ramps.config.mjs changed, regenerating…");
|
|
140
|
+
await execFileAsync("node", ["scripts/generate-ramps.mjs"], { cwd: resolve("./") });
|
|
141
|
+
console.log("[ramps] done.");
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
: {},
|
|
129
145
|
compatibilityDate: "2026-05-20",
|
|
130
146
|
typescript: {
|
|
131
147
|
includeWorkspace: true,
|
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",
|
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
"cleanall": "rm -rf node_modules && rm -rf .nuxt && rm -rf .output && rm package-lock.json",
|
|
11
11
|
"reinstall": "rm -rf node_modules && npm install",
|
|
12
12
|
"cleaninstall": "npm run clean && npm run reinstall",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
13
|
+
"generate:ramps": "node scripts/generate-ramps.mjs",
|
|
14
|
+
"check:ramps": "node scripts/check-ramps.mjs",
|
|
15
|
+
"dev": "npm run generate:ramps && SRCDEV_STANDALONE=true nuxt dev",
|
|
16
|
+
"build": "npm run generate:ramps && SRCDEV_STANDALONE=true nuxt build",
|
|
17
|
+
"generate": "npm run generate:ramps && SRCDEV_STANDALONE=true nuxt generate",
|
|
16
18
|
"preview": "SRCDEV_STANDALONE=true nuxt preview",
|
|
17
19
|
"lint": "eslint .",
|
|
18
20
|
"prepare": "SRCDEV_STANDALONE=true nuxt prepare",
|
|
@@ -32,6 +34,8 @@
|
|
|
32
34
|
".claude/",
|
|
33
35
|
"app/",
|
|
34
36
|
"modules/",
|
|
37
|
+
"scripts/",
|
|
38
|
+
"ramps.config.mjs",
|
|
35
39
|
"nuxt.config.ts",
|
|
36
40
|
"types.d.ts"
|
|
37
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.");
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
:where(html.dark) {
|
|
2
|
-
/* ===========================================
|
|
3
|
-
COLOR SCALE VARIABLES
|
|
4
|
-
Base color tokens for the default theme
|
|
5
|
-
=========================================== */
|
|
6
|
-
--colour-theme-1: var(--blue-01);
|
|
7
|
-
--colour-theme-2: var(--blue-02);
|
|
8
|
-
--colour-theme-3: var(--blue-03);
|
|
9
|
-
--colour-theme-4: var(--blue-04);
|
|
10
|
-
--colour-theme-5: var(--blue-05);
|
|
11
|
-
--colour-theme-6: var(--blue-06);
|
|
12
|
-
--colour-theme-7: var(--blue-07);
|
|
13
|
-
--colour-theme-8: var(--blue-08);
|
|
14
|
-
--colour-theme-9: var(--blue-09);
|
|
15
|
-
--colour-theme-10: var(--blue-10);
|
|
16
|
-
--colour-theme-11: var(--blue-10);
|
|
17
|
-
--colour-theme-12: var(--blue-10);
|
|
18
|
-
|
|
19
|
-
/* ===========================================
|
|
20
|
-
GENERIC BODY VARIABLES
|
|
21
|
-
=========================================== */
|
|
22
|
-
--page-bg: var(--slate-08);
|
|
23
|
-
--colour-text-default: var(--slate-01);
|
|
24
|
-
--colour-text-accent: var(--blue-05);
|
|
25
|
-
--colour-text-eyebrow: var(--blue-05);
|
|
26
|
-
|
|
27
|
-
/* ===========================================
|
|
28
|
-
GLASS PANEL VARIABLES
|
|
29
|
-
=========================================== */
|
|
30
|
-
--glass-panel-bg: rgba(12, 12, 20, 0.45);
|
|
31
|
-
--glass-panel-border-color: rgba(255, 255, 255, 0.07);
|
|
32
|
-
--glass-panel-shadow: 0 8px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
33
|
-
--glass-panel-highlight: rgba(255, 255, 255, 0.04);
|
|
34
|
-
|
|
35
|
-
/* ===========================================
|
|
36
|
-
HERO TEXT VARIABLES
|
|
37
|
-
=========================================== */
|
|
38
|
-
--hero-text-bg-img: linear-gradient(135deg, #c2a770, #b4747e, #d1bd94);
|
|
39
|
-
|
|
40
|
-
/* ===========================================
|
|
41
|
-
EYEBROW TEXT VARIABLES
|
|
42
|
-
=========================================== */
|
|
43
|
-
--eyebrow-text-bg-img: linear-gradient(135deg, #c2a770, #b4747e, #d1bd94);
|
|
44
|
-
|
|
45
|
-
/* ===========================================
|
|
46
|
-
STEPPER LIST VARIABLES
|
|
47
|
-
=========================================== */
|
|
48
|
-
--stepper-list-counter-circle-background: transparent;
|
|
49
|
-
--stepper-list-counter-circle-text: var(--blue-02);
|
|
50
|
-
--stepper-list-counter-circle-border: var(--blue-02);
|
|
51
|
-
|
|
52
|
-
--stepper-list-counter-disc-background: var(--blue-02);
|
|
53
|
-
--stepper-list-counter-disc-text: var(--blue-09);
|
|
54
|
-
--stepper-list-counter-disc-border: var(--blue-02);
|
|
55
|
-
|
|
56
|
-
--stepper-list-counter-square-background: transparent;
|
|
57
|
-
--stepper-list-counter-square-text: var(--blue-02);
|
|
58
|
-
--stepper-list-counter-square-border: var(--blue-02);
|
|
59
|
-
|
|
60
|
-
--stepper-list-connector-color: var(--blue-02);
|
|
61
|
-
|
|
62
|
-
--stepper-list-icon: var(--blue-02);
|
|
63
|
-
|
|
64
|
-
/* ===========================================
|
|
65
|
-
COLOUR FINDER VARIABLES
|
|
66
|
-
=========================================== */
|
|
67
|
-
--colour-finder-primary-colour: var(--blue-03);
|
|
68
|
-
--colour-finder-border-colour: var(--blue-03);
|
|
69
|
-
|
|
70
|
-
--colour-finder-checked-surface-colour: var(--green-10);
|
|
71
|
-
--colour-finder-checked-stroke-colour: var(--green-03);
|
|
72
|
-
|
|
73
|
-
--colour-finder-conflict-surface-colour: var(--sunset-09);
|
|
74
|
-
--colour-finder-conflict-stroke-colour: var(--sunset-03);
|
|
75
|
-
|
|
76
|
-
/* ===========================================
|
|
77
|
-
TREATMENT CONSULTANT VARIABLES
|
|
78
|
-
=========================================== */
|
|
79
|
-
--treatment-consultant-primary-colour: var(--amber-02);
|
|
80
|
-
--treatment-consultant-primary-foreground: var(--amber-02);
|
|
81
|
-
|
|
82
|
-
--treatment-consultant-muted-colour: var(--amber-05);
|
|
83
|
-
--treatment-consultant-muted-foreground: var(--amber-05);
|
|
84
|
-
|
|
85
|
-
--treatment-consultant-background: var(--amber-10);
|
|
86
|
-
--treatment-consultant-foreground: var(--amber-02);
|
|
87
|
-
|
|
88
|
-
--treatment-consultant-border-colour: var(--amber-03);
|
|
89
|
-
|
|
90
|
-
--treatment-consultant-checked-surface-colour: var(--green-10);
|
|
91
|
-
--treatment-consultant-checked-stroke-colour: var(--green-03);
|
|
92
|
-
|
|
93
|
-
--treatment-consultant-conflict-surface-colour: var(--sunset-09);
|
|
94
|
-
--treatment-consultant-conflict-stroke-colour: var(--sunset-03);
|
|
95
|
-
|
|
96
|
-
/* ===========================================
|
|
97
|
-
ANCHOR LINK VARIABLES
|
|
98
|
-
=========================================== */
|
|
99
|
-
--colour-link-default: var(--blue-02);
|
|
100
|
-
--colour-link-hover: var(--blue-03);
|
|
101
|
-
|
|
102
|
-
/* ===========================================
|
|
103
|
-
FORM INPUT VARIABLES
|
|
104
|
-
Styling for form inputs and controls
|
|
105
|
-
=========================================== */
|
|
106
|
-
|
|
107
|
-
/* Input surfaces and backgrounds */
|
|
108
|
-
--theme-input-surface: var(--slate-10);
|
|
109
|
-
--theme-input-surface-hover: var(--slate-09);
|
|
110
|
-
|
|
111
|
-
/* Input borders */
|
|
112
|
-
--theme-input-border: var(--blue-06);
|
|
113
|
-
--theme-input-border-hover: var(--blue-05);
|
|
114
|
-
--theme-input-border-focus: var(--blue-04);
|
|
115
|
-
|
|
116
|
-
/* Input outlines and focus */
|
|
117
|
-
--theme-input-outline: var(--blue-06);
|
|
118
|
-
--theme-input-outline-focus: var(--blue-04);
|
|
119
|
-
--theme-input-visible-outline: var(--blue-04);
|
|
120
|
-
--theme-focus-visible-shadow: 0 0 0 2px var(--blue-02);
|
|
121
|
-
|
|
122
|
-
/* ===========================================
|
|
123
|
-
INPUT TEXT VARIABLES
|
|
124
|
-
=========================================== */
|
|
125
|
-
--theme-input-placeholder: var(--slate-04);
|
|
126
|
-
--theme-input-text-color-normal: var(--slate-01);
|
|
127
|
-
|
|
128
|
-
/* ===========================================
|
|
129
|
-
CHECKBOX AND RADIO BUTTON VARIABLES
|
|
130
|
-
=========================================== */
|
|
131
|
-
--theme-checkbox-symbol-surface: var(--theme-input-surface);
|
|
132
|
-
--theme-checkbox-symbol-color: var(--blue-02);
|
|
133
|
-
--theme-checkbox-decorator-color: var(--blue-02);
|
|
134
|
-
|
|
135
|
-
/* ===========================================
|
|
136
|
-
FORM BUTTON VARIABLES
|
|
137
|
-
Styling for form buttons and controls
|
|
138
|
-
=========================================== */
|
|
139
|
-
|
|
140
|
-
/* Variant Primary */
|
|
141
|
-
--theme-button-primary-border: var(--blue-07);
|
|
142
|
-
--theme-button-primary-border-active: var(--blue-07);
|
|
143
|
-
--theme-button-primary-text: var(--blue-00);
|
|
144
|
-
--theme-button-primary-text-hover: var(--blue-00);
|
|
145
|
-
|
|
146
|
-
/* Variant Secondary */
|
|
147
|
-
--theme-button-secondary-surface: var(--blue-01);
|
|
148
|
-
--theme-button-secondary-border-active: var(--blue-01);
|
|
149
|
-
--theme-button-secondary-text: var(--blue-09);
|
|
150
|
-
|
|
151
|
-
/* Variant Tertiary */
|
|
152
|
-
--theme-button-tertiary-surface: transparent;
|
|
153
|
-
--theme-button-tertiary-text: var(--blue-01);
|
|
154
|
-
--theme-button-tertiary-border-active: var(--blue-01);
|
|
155
|
-
--theme-button-tertiary-outline-active: var(--blue-09);
|
|
156
|
-
}
|