@commonpub/layer 0.69.1 → 0.70.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.
|
@@ -20,7 +20,10 @@ import {
|
|
|
20
20
|
randomizeRecipe,
|
|
21
21
|
randomName,
|
|
22
22
|
buildPalette,
|
|
23
|
-
|
|
23
|
+
suggestPalettes,
|
|
24
|
+
type PaletteSuggestion,
|
|
25
|
+
hexToHsl,
|
|
26
|
+
hslToHex,
|
|
24
27
|
contrast,
|
|
25
28
|
wcag,
|
|
26
29
|
COLOR_VIBES,
|
|
@@ -150,11 +153,35 @@ function palStrip(accent: string, scheme: HarmonyScheme, mode: 'light' | 'dark')
|
|
|
150
153
|
const p = buildPalette({ accent, scheme, mode }).sem;
|
|
151
154
|
return [p.bg, p.surface, p.accent, p.secondary, p.text];
|
|
152
155
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
]);
|
|
156
|
+
// --- Palette options + HSL accent control (Phase 4) -------------------
|
|
157
|
+
// From the chosen accent, offer several ready-to-apply harmonized palettes
|
|
158
|
+
// (choice over abstract knobs) + an HSL slider control (less janky than the
|
|
159
|
+
// bare native swatch).
|
|
160
|
+
const palettes = computed<PaletteSuggestion[]>(() => suggestPalettes(recipe.value.accent, recipe.value.mode));
|
|
161
|
+
const activePalette = computed<string>(() => {
|
|
162
|
+
const r = recipe.value;
|
|
163
|
+
return (
|
|
164
|
+
palettes.value.find(
|
|
165
|
+
(p) =>
|
|
166
|
+
p.scheme === r.scheme &&
|
|
167
|
+
p.neutralHue === r.neutralHue &&
|
|
168
|
+
p.neutralSat === r.neutralSat &&
|
|
169
|
+
(p.secondary ?? undefined) === (r.secondary ?? undefined),
|
|
170
|
+
)?.k ?? ''
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
function applyPaletteSuggestion(p: PaletteSuggestion): void {
|
|
174
|
+
recipe.value.scheme = p.scheme;
|
|
175
|
+
recipe.value.neutralHue = p.neutralHue;
|
|
176
|
+
recipe.value.neutralSat = p.neutralSat;
|
|
177
|
+
recipe.value.secondary = p.secondary;
|
|
178
|
+
useSecondary.value = Boolean(p.secondary);
|
|
179
|
+
}
|
|
180
|
+
const accentHsl = computed(() => hexToHsl(recipe.value.accent));
|
|
181
|
+
function setAccentHsl(part: 'h' | 's' | 'l', v: number): void {
|
|
182
|
+
const c = accentHsl.value;
|
|
183
|
+
recipe.value.accent = hslToHex(part === 'h' ? v : c.h, part === 's' ? v : c.s, part === 'l' ? v : c.l);
|
|
184
|
+
}
|
|
158
185
|
|
|
159
186
|
// --- Color actions -----------------------------------------------------
|
|
160
187
|
|
|
@@ -347,7 +374,31 @@ function finishWith(apply: boolean): void {
|
|
|
347
374
|
<input type="file" accept="image/*" hidden @change="onImagePick" />
|
|
348
375
|
</label>
|
|
349
376
|
</span>
|
|
377
|
+
<span class="cpub-studio-hslrow">
|
|
378
|
+
<span class="cpub-studio-hsl"><span>H</span><input type="range" min="0" max="360" :value="Math.round(accentHsl.h)" aria-label="Hue" @input="setAccentHsl('h', +($event.target as HTMLInputElement).value)" /></span>
|
|
379
|
+
<span class="cpub-studio-hsl"><span>S</span><input type="range" min="0" max="100" :value="Math.round(accentHsl.s)" aria-label="Saturation" @input="setAccentHsl('s', +($event.target as HTMLInputElement).value)" /></span>
|
|
380
|
+
<span class="cpub-studio-hsl"><span>L</span><input type="range" min="0" max="100" :value="Math.round(accentHsl.l)" aria-label="Lightness" @input="setAccentHsl('l', +($event.target as HTMLInputElement).value)" /></span>
|
|
381
|
+
</span>
|
|
350
382
|
</label>
|
|
383
|
+
<div class="cpub-studio-field">
|
|
384
|
+
<span class="cpub-studio-lbl">Palette options <span class="cpub-studio-hint">from your accent</span></span>
|
|
385
|
+
<div class="cpub-studio-palopts">
|
|
386
|
+
<button
|
|
387
|
+
v-for="p in palettes"
|
|
388
|
+
:key="p.k"
|
|
389
|
+
type="button"
|
|
390
|
+
class="cpub-studio-palopt"
|
|
391
|
+
:class="{ on: activePalette === p.k }"
|
|
392
|
+
:title="p.label"
|
|
393
|
+
@click="applyPaletteSuggestion(p)"
|
|
394
|
+
>
|
|
395
|
+
<span class="cpub-studio-palopt-strip">
|
|
396
|
+
<span v-for="(c, ci) in p.preview" :key="ci" :style="{ background: c }" />
|
|
397
|
+
</span>
|
|
398
|
+
<span class="cpub-studio-palopt-name">{{ p.label }}</span>
|
|
399
|
+
</button>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
351
402
|
<label class="cpub-studio-field">
|
|
352
403
|
<span class="cpub-studio-lbl">Color family <span class="cpub-studio-hint">harmony</span></span>
|
|
353
404
|
<span class="cpub-studio-seg cpub-studio-seg-wrap">
|
|
@@ -360,12 +411,6 @@ function finishWith(apply: boolean): void {
|
|
|
360
411
|
<button v-for="n in NEUTRALS" :key="n.k" type="button" :class="{ on: neutralMode === n.k }" @click="setNeutral(n.k)">{{ n.label }}</button>
|
|
361
412
|
</span>
|
|
362
413
|
</label>
|
|
363
|
-
<div class="cpub-studio-field">
|
|
364
|
-
<span class="cpub-studio-lbl">Suggested family</span>
|
|
365
|
-
<span class="cpub-studio-family">
|
|
366
|
-
<span v-for="(c, i) in familyStrip" :key="i" :style="{ background: c }" :title="c" />
|
|
367
|
-
</span>
|
|
368
|
-
</div>
|
|
369
414
|
<div class="cpub-studio-toggle-line">
|
|
370
415
|
<span class="cpub-studio-lbl">Hand-pick secondary</span>
|
|
371
416
|
<button type="button" class="cpub-studio-switch" :class="{ on: useSecondary }" :aria-pressed="useSecondary" @click="toggleSecondary" />
|
|
@@ -602,6 +647,19 @@ function finishWith(apply: boolean): void {
|
|
|
602
647
|
.cpub-studio-colorrow { display: flex; gap: var(--space-2); align-items: center; }
|
|
603
648
|
.cpub-studio-colorpick { width: 40px; height: 36px; padding: 0; border: var(--border-width-thin) solid var(--border2); background: none; cursor: pointer; flex-shrink: 0; }
|
|
604
649
|
|
|
650
|
+
.cpub-studio-hslrow { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2); margin-top: 6px; }
|
|
651
|
+
.cpub-studio-hsl { display: flex; align-items: center; gap: 4px; }
|
|
652
|
+
.cpub-studio-hsl > span { font-family: var(--font-mono); font-size: var(--text-label); color: var(--text-faint); width: 10px; }
|
|
653
|
+
.cpub-studio-hsl input[type="range"] { flex: 1; min-width: 0; accent-color: var(--accent); }
|
|
654
|
+
|
|
655
|
+
.cpub-studio-palopts { display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 6px; }
|
|
656
|
+
.cpub-studio-palopt { display: flex; flex-direction: column; gap: 4px; background: var(--surface2); border: var(--border-width-thin) solid var(--border2); padding: 5px; cursor: pointer; }
|
|
657
|
+
.cpub-studio-palopt:hover { border-color: var(--text-faint); }
|
|
658
|
+
.cpub-studio-palopt.on { border-color: var(--accent); background: var(--accent-bg); }
|
|
659
|
+
.cpub-studio-palopt-strip { display: flex; height: 22px; overflow: hidden; }
|
|
660
|
+
.cpub-studio-palopt-strip > span { flex: 1; }
|
|
661
|
+
.cpub-studio-palopt-name { font-family: var(--font-mono); font-size: var(--text-label); color: var(--text-dim); text-align: center; }
|
|
662
|
+
|
|
605
663
|
.cpub-studio-seg { display: grid; gap: 4px; grid-auto-flow: column; grid-auto-columns: 1fr; }
|
|
606
664
|
.cpub-studio-seg-wrap { grid-auto-flow: row; grid-template-columns: repeat(auto-fit, minmax(64px, 1fr)); }
|
|
607
665
|
.cpub-studio-seg button { background: var(--surface2); border: var(--border-width-thin) solid var(--border2); color: var(--text-dim); font-family: var(--font-mono); font-size: var(--text-label); font-weight: var(--font-weight-semibold); padding: 7px 4px; cursor: pointer; text-align: center; line-height: 1.2; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commonpub/layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.70.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"files": [
|
|
@@ -53,17 +53,17 @@
|
|
|
53
53
|
"vue": "^3.4.0",
|
|
54
54
|
"vue-router": "^4.3.0",
|
|
55
55
|
"zod": "^4.3.6",
|
|
56
|
-
"@commonpub/auth": "0.8.0",
|
|
57
|
-
"@commonpub/config": "0.20.0",
|
|
58
56
|
"@commonpub/editor": "0.7.11",
|
|
59
|
-
"@commonpub/schema": "0.38.0",
|
|
60
57
|
"@commonpub/docs": "0.6.3",
|
|
61
|
-
"@commonpub/
|
|
58
|
+
"@commonpub/config": "0.20.0",
|
|
62
59
|
"@commonpub/learning": "0.5.2",
|
|
60
|
+
"@commonpub/auth": "0.8.0",
|
|
61
|
+
"@commonpub/protocol": "0.13.0",
|
|
63
62
|
"@commonpub/server": "2.83.0",
|
|
64
|
-
"@commonpub/theme-studio": "0.
|
|
65
|
-
"@commonpub/ui": "0.12.
|
|
66
|
-
"@commonpub/explainer": "0.7.15"
|
|
63
|
+
"@commonpub/theme-studio": "0.5.1",
|
|
64
|
+
"@commonpub/ui": "0.12.2",
|
|
65
|
+
"@commonpub/explainer": "0.7.15",
|
|
66
|
+
"@commonpub/schema": "0.38.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@testing-library/jest-dom": "^6.9.1",
|
package/theme/base.css
CHANGED
|
@@ -209,6 +209,12 @@
|
|
|
209
209
|
--shadow-lg: 6px 6px 0 var(--border);
|
|
210
210
|
--shadow-xl: 8px 8px 0 var(--border);
|
|
211
211
|
--shadow-accent: 4px 4px 0 var(--accent);
|
|
212
|
+
/* Component surface shadow. Built-in themes deliberately do NOT override these
|
|
213
|
+
* (only --shadow-* above), so buttons/cards keep the offset-block signature.
|
|
214
|
+
* Theme Studio emits them per the recipe's shadowStyle so a custom theme's
|
|
215
|
+
* buttons/cards reflect its archetype (neumorphic relief, soft blur, etc.). */
|
|
216
|
+
--shadow-block: 4px 4px 0 var(--border);
|
|
217
|
+
--shadow-block-sm: 2px 2px 0 var(--border);
|
|
212
218
|
|
|
213
219
|
/* === TRANSITIONS === */
|
|
214
220
|
--transition-fast: 0.1s ease;
|
package/theme/components.css
CHANGED
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
.cpub-btn-primary {
|
|
28
28
|
background: var(--accent);
|
|
29
29
|
color: var(--color-text-inverse);
|
|
30
|
-
box-shadow:
|
|
30
|
+
box-shadow: var(--shadow-block);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
.cpub-btn-primary:hover {
|
|
34
|
-
box-shadow:
|
|
34
|
+
box-shadow: var(--shadow-block-sm);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.cpub-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
background: var(--secondary);
|
|
41
41
|
color: var(--color-on-secondary);
|
|
42
42
|
border-color: var(--secondary);
|
|
43
|
-
box-shadow:
|
|
43
|
+
box-shadow: var(--shadow-block);
|
|
44
44
|
}
|
|
45
|
-
.cpub-btn-secondary:hover { background: var(--secondary-hover); box-shadow:
|
|
45
|
+
.cpub-btn-secondary:hover { background: var(--secondary-hover); box-shadow: var(--shadow-block-sm); }
|
|
46
46
|
.cpub-btn-secondary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
47
47
|
|
|
48
48
|
.cpub-btn-sm { padding: 4px 10px; font-size: 11px; min-height: 44px; }
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
border: var(--border-width-default) solid var(--border);
|
|
90
90
|
padding: 16px;
|
|
91
91
|
margin-bottom: 12px;
|
|
92
|
-
box-shadow:
|
|
92
|
+
box-shadow: var(--shadow-block);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
.cpub-sb-title {
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
border-color: var(--accent);
|
|
165
165
|
background: var(--accent-bg);
|
|
166
166
|
color: var(--accent);
|
|
167
|
-
box-shadow:
|
|
167
|
+
box-shadow: var(--shadow-block-sm);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
.cpub-page-ellipsis {
|