@plutocms/core 0.38.0 → 0.38.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/CHANGELOG.md +7 -0
- package/app/assets/css/admin-theme.css +1 -1
- package/package.json +1 -1
- package/scripts/generate-admin-theme.cjs +0 -263
- package/tailwind.lint.css +0 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.38.1](https://github.com/plutocms/pluto/compare/v0.38.0...v0.38.1) (2026-10-30)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* the core package leaves out its dev scripts ([#284](https://github.com/plutocms/pluto/issues/284)) ([c62763f](https://github.com/plutocms/pluto/commit/c62763f42cd0976514b2de515ea3e52971eef65a))
|
|
10
|
+
|
|
4
11
|
## [0.38.0](https://github.com/plutocms/pluto/compare/v0.37.0...v0.38.0) (2026-10-30)
|
|
5
12
|
|
|
6
13
|
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* Chart colors are a validated categorical palette: lightness band, chroma
|
|
33
33
|
* floor, colorblind separation, and 3:1 contrast pass on white (light) and
|
|
34
34
|
* on the dark card surface (dark). This file is generated; keep the values in
|
|
35
|
-
* sync with scripts/generate-admin-theme.cjs: run it from
|
|
35
|
+
* sync with scripts/generate-admin-theme.cjs: run it from packages/core, never
|
|
36
36
|
* edit this file by hand.
|
|
37
37
|
*/
|
|
38
38
|
:root[data-pluto-admin],
|
package/package.json
CHANGED
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
// Generates app/assets/css/admin-theme.css from the Pluto UI tokens
|
|
2
|
-
// (app/assets/css/pluto-tokens.css) and the installed Tailwind palette (the
|
|
3
|
-
// status colors). Run from the repo root: node scripts/generate-admin-theme.cjs
|
|
4
|
-
// See the admin-theme skill for the palette choices and how they were
|
|
5
|
-
// validated.
|
|
6
|
-
const fs = require('node:fs')
|
|
7
|
-
const path = require('node:path')
|
|
8
|
-
|
|
9
|
-
const theme = fs.readFileSync(
|
|
10
|
-
path.join(
|
|
11
|
-
path.dirname(
|
|
12
|
-
require.resolve('tailwindcss/package.json', { paths: [process.cwd()] })
|
|
13
|
-
),
|
|
14
|
-
'theme.css'
|
|
15
|
-
),
|
|
16
|
-
'utf8'
|
|
17
|
-
)
|
|
18
|
-
const tokens = fs.readFileSync('app/assets/css/pluto-tokens.css', 'utf8')
|
|
19
|
-
|
|
20
|
-
// sRGB hex to an OKLCH literal, so every value in the theme has one form.
|
|
21
|
-
const toLinear = (c) =>
|
|
22
|
-
c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4
|
|
23
|
-
function hexToOklch(hex) {
|
|
24
|
-
const n = Number.parseInt(hex.slice(1), 16)
|
|
25
|
-
const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255].map((value) =>
|
|
26
|
-
toLinear(value / 255)
|
|
27
|
-
)
|
|
28
|
-
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)
|
|
29
|
-
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)
|
|
30
|
-
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)
|
|
31
|
-
const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s
|
|
32
|
-
const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s
|
|
33
|
-
const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
34
|
-
const C = Math.hypot(A, B)
|
|
35
|
-
const H = ((Math.atan2(B, A) * 180) / Math.PI + 360) % 360
|
|
36
|
-
return `oklch(${(L * 100).toFixed(1)}% ${C.toFixed(3)} ${H.toFixed(2)})`
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// A Pluto token (tholin, haze, heart, kuiper) or a Tailwind color, as an OKLCH literal.
|
|
40
|
-
function v(name) {
|
|
41
|
-
const own = tokens.match(
|
|
42
|
-
new RegExp(String.raw`--color-${name}: (#[0-9A-Fa-f]{6})`)
|
|
43
|
-
)
|
|
44
|
-
if (own) {
|
|
45
|
-
return hexToOklch(own[1])
|
|
46
|
-
}
|
|
47
|
-
return theme.match(
|
|
48
|
-
new RegExp(String.raw`--color-${name}: (oklch\([^)]*\))`)
|
|
49
|
-
)[1]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Tholin is a red-orange (hue 37). Tailwind red (hue 27) looks like it next
|
|
53
|
-
// to a primary button, so errors use rose (hue 17). Info is haze, the blue of
|
|
54
|
-
// the Pluto palette.
|
|
55
|
-
const roles = {
|
|
56
|
-
primary: 'tholin',
|
|
57
|
-
secondary: 'haze',
|
|
58
|
-
success: 'green',
|
|
59
|
-
info: 'haze',
|
|
60
|
-
warning: 'amber',
|
|
61
|
-
error: 'rose',
|
|
62
|
-
neutral: 'kuiper',
|
|
63
|
-
}
|
|
64
|
-
const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
|
|
65
|
-
const scales = Object.entries(roles)
|
|
66
|
-
.map(
|
|
67
|
-
([role, hue]) =>
|
|
68
|
-
` /* ${role}: ${hue} */\n${shades.map((s) => ` --ui-color-${role}-${s}: ${v(`${hue}-${s}`)};`).join('\n')}`
|
|
69
|
-
)
|
|
70
|
-
.join('\n\n')
|
|
71
|
-
|
|
72
|
-
// The semantic step of each role. Light: tholin 600 and haze 700 for 4.5:1 on
|
|
73
|
-
// white (the docs site uses the same steps), the status colors at 500. Dark:
|
|
74
|
-
// 400 for every role.
|
|
75
|
-
const lightSteps = {
|
|
76
|
-
primary: 600,
|
|
77
|
-
secondary: 700,
|
|
78
|
-
success: 500,
|
|
79
|
-
info: 500,
|
|
80
|
-
warning: 500,
|
|
81
|
-
error: 500,
|
|
82
|
-
}
|
|
83
|
-
const semantic = (steps) =>
|
|
84
|
-
Object.entries(steps)
|
|
85
|
-
.map(([role, step]) => ` --ui-${role}: var(--ui-color-${role}-${step});`)
|
|
86
|
-
.join('\n')
|
|
87
|
-
const darkSteps = Object.fromEntries(
|
|
88
|
-
Object.keys(lightSteps).map((role) => [role, 400])
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
const n = (s) => `var(--ui-color-neutral-${s})`
|
|
92
|
-
const neutralLight = {
|
|
93
|
-
'text-dimmed': n(400),
|
|
94
|
-
'text-muted': n(500),
|
|
95
|
-
'text-toned': n(600),
|
|
96
|
-
text: n(700),
|
|
97
|
-
'text-highlighted': n(900),
|
|
98
|
-
'text-inverted': '#fff',
|
|
99
|
-
bg: '#fff',
|
|
100
|
-
'bg-muted': n(50),
|
|
101
|
-
'bg-elevated': n(100),
|
|
102
|
-
'bg-accented': n(200),
|
|
103
|
-
'bg-inverted': n(900),
|
|
104
|
-
border: n(200),
|
|
105
|
-
'border-muted': n(200),
|
|
106
|
-
'border-accented': n(300),
|
|
107
|
-
'border-inverted': n(900),
|
|
108
|
-
}
|
|
109
|
-
// Dark surfaces on the kuiper hue (273). Cards sit one step above the
|
|
110
|
-
// kuiper-900 canvas. The sidebar sits between the kuiper-950 navbar and the
|
|
111
|
-
// canvas. The chart palette passes the dataviz checks on the card surface.
|
|
112
|
-
const CARD_DARK = 'oklch(28% 0.042 273)'
|
|
113
|
-
const SIDEBAR_DARK = 'oklch(20.5% 0.040 273)'
|
|
114
|
-
const neutralDark = {
|
|
115
|
-
'text-dimmed': n(500),
|
|
116
|
-
'text-muted': n(400),
|
|
117
|
-
'text-toned': n(300),
|
|
118
|
-
text: n(200),
|
|
119
|
-
'text-highlighted': '#fff',
|
|
120
|
-
'text-inverted': n(900),
|
|
121
|
-
bg: CARD_DARK,
|
|
122
|
-
'bg-muted': n(800),
|
|
123
|
-
'bg-elevated': n(800),
|
|
124
|
-
'bg-accented': n(700),
|
|
125
|
-
'bg-inverted': '#fff',
|
|
126
|
-
border: n(800),
|
|
127
|
-
'border-muted': n(700),
|
|
128
|
-
'border-accented': n(700),
|
|
129
|
-
'border-inverted': '#fff',
|
|
130
|
-
}
|
|
131
|
-
const declare = (o) =>
|
|
132
|
-
Object.entries(o)
|
|
133
|
-
.map(([k, val]) => ` --ui-${k}: ${val};`)
|
|
134
|
-
.join('\n')
|
|
135
|
-
|
|
136
|
-
// The chart series, in a fixed order: tholin, haze, lime, violet, teal, amber.
|
|
137
|
-
// Validated with the dataviz palette checks (lightness band, chroma floor,
|
|
138
|
-
// colorblind and normal-vision separation of neighbors, 3:1 contrast) on white
|
|
139
|
-
// for light, and on the dark card surface for dark. Dark uses tholin 500, not
|
|
140
|
-
// the primary 400, which is too light for the dark band. Re-run the checks
|
|
141
|
-
// before you change a slot.
|
|
142
|
-
const chartLight = [
|
|
143
|
-
'tholin-600',
|
|
144
|
-
'haze-600',
|
|
145
|
-
'lime-700',
|
|
146
|
-
'violet-500',
|
|
147
|
-
'teal-600',
|
|
148
|
-
'amber-600',
|
|
149
|
-
]
|
|
150
|
-
const chartDark = [
|
|
151
|
-
'tholin-500',
|
|
152
|
-
'haze-500',
|
|
153
|
-
'lime-600',
|
|
154
|
-
'violet-500',
|
|
155
|
-
'teal-600',
|
|
156
|
-
'amber-600',
|
|
157
|
-
]
|
|
158
|
-
// Slot 1 reads the primary scale (tholin 600 in light, 500 in dark), so a
|
|
159
|
-
// brand primary (appConfig.pluto.brand) recolors it too.
|
|
160
|
-
function chart(list, primaryStep) {
|
|
161
|
-
return [
|
|
162
|
-
` --ui-chart-1: var(--ui-color-primary-${primaryStep}); /* ${list[0]} */`,
|
|
163
|
-
...list
|
|
164
|
-
.slice(1)
|
|
165
|
-
.map((c, i) => ` --ui-chart-${i + 2}: ${v(c)}; /* ${c} */`),
|
|
166
|
-
].join('\n')
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const css = `/*
|
|
170
|
-
* The Pluto admin theme.
|
|
171
|
-
*
|
|
172
|
-
* Two selectors carry it:
|
|
173
|
-
* - \`:root[data-pluto-admin]\`: usePlutoAdminTheme() sets this attribute on
|
|
174
|
-
* <html> while an admin layout is mounted, so the theme also covers
|
|
175
|
-
* overlays (modals, slideovers, menus, toasts, the command palette) that
|
|
176
|
-
* render at the end of <body>.
|
|
177
|
-
* - \`[data-pluto-admin-scope]\`: for admin chrome that also shows on the
|
|
178
|
-
* public site, such as the admin navbar for a logged-in admin.
|
|
179
|
-
*
|
|
180
|
-
* \`[data-pluto-admin-dark]\` takes the dark tokens in both color modes. The
|
|
181
|
-
* admin navbar is near black in light mode too, so its text and buttons need
|
|
182
|
-
* the dark values.
|
|
183
|
-
*
|
|
184
|
-
* The colors are the Pluto UI tokens (pluto-tokens.css): primary tholin,
|
|
185
|
-
* secondary and info haze, neutral kuiper. The status colors come from
|
|
186
|
-
* Tailwind: green, amber, rose. The fonts are Public Sans for text and
|
|
187
|
-
* Fredoka for page titles.
|
|
188
|
-
*
|
|
189
|
-
* Every value is a literal. The consumer app styles its public site through
|
|
190
|
-
* its own app.config colors and @theme. Both reach the admin unless the admin
|
|
191
|
-
* pins its own values: app.config colors set Nuxt UI's --ui-color-* scales on
|
|
192
|
-
* :root, and @theme can redefine a Tailwind color such as --color-red-400.
|
|
193
|
-
* These selectors are more specific than Nuxt UI's :root and .dark rules.
|
|
194
|
-
*
|
|
195
|
-
* The semantic tokens (--ui-primary, --ui-bg, --ui-text, ...) are declared
|
|
196
|
-
* again here, not only the color scales. A custom property inherits as a
|
|
197
|
-
* resolved value, so on a scoped element the :root tokens would still hold
|
|
198
|
-
* the app's colors.
|
|
199
|
-
*
|
|
200
|
-
* Chart colors are a validated categorical palette: lightness band, chroma
|
|
201
|
-
* floor, colorblind separation, and 3:1 contrast pass on white (light) and
|
|
202
|
-
* on the dark card surface (dark). This file is generated; keep the values in
|
|
203
|
-
* sync with scripts/generate-admin-theme.cjs: run it from the repo root, never
|
|
204
|
-
* edit this file by hand.
|
|
205
|
-
*/
|
|
206
|
-
:root[data-pluto-admin],
|
|
207
|
-
[data-pluto-admin-scope] {
|
|
208
|
-
${scales}
|
|
209
|
-
|
|
210
|
-
${semantic(lightSteps)}
|
|
211
|
-
|
|
212
|
-
${declare(neutralLight)}
|
|
213
|
-
|
|
214
|
-
${chart(chartLight, 600)}
|
|
215
|
-
|
|
216
|
-
/* Surfaces. The navbar is near black in both modes. */
|
|
217
|
-
--pluto-admin-canvas: #fff;
|
|
218
|
-
--pluto-admin-sidebar: ${v('kuiper-50')};
|
|
219
|
-
--pluto-admin-navbar: ${v('kuiper-950')};
|
|
220
|
-
|
|
221
|
-
/* The Pluto fonts, as literals like the colors (see pluto-tokens.css). */
|
|
222
|
-
--font-sans: "Public Sans", ui-sans-serif, system-ui, sans-serif;
|
|
223
|
-
--pluto-admin-font-display: "Fredoka", "Public Sans", ui-sans-serif, system-ui, sans-serif;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
:root[data-pluto-admin].dark,
|
|
227
|
-
.dark [data-pluto-admin-scope],
|
|
228
|
-
[data-pluto-admin-dark] {
|
|
229
|
-
${semantic(darkSteps)}
|
|
230
|
-
|
|
231
|
-
${declare(neutralDark)}
|
|
232
|
-
|
|
233
|
-
${chart(chartDark, 500)}
|
|
234
|
-
|
|
235
|
-
/*
|
|
236
|
-
* Four steps, darkest at the top: a near-black navbar, a darker sidebar,
|
|
237
|
-
* a dim dark canvas (not pitch black), and cards (--ui-bg) one step above
|
|
238
|
-
* the canvas.
|
|
239
|
-
*/
|
|
240
|
-
--pluto-admin-navbar: ${v('kuiper-950')};
|
|
241
|
-
--pluto-admin-sidebar: ${SIDEBAR_DARK};
|
|
242
|
-
--pluto-admin-canvas: ${v('kuiper-900')};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
:root[data-pluto-admin] body {
|
|
246
|
-
background-color: var(--pluto-admin-canvas);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
:root[data-pluto-admin] body,
|
|
250
|
-
[data-pluto-admin-scope] {
|
|
251
|
-
font-family: var(--font-sans);
|
|
252
|
-
color: var(--ui-text);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/* Page titles (h1 only) use the rounded face of the wordmark, as on the docs site. */
|
|
256
|
-
:root[data-pluto-admin] h1,
|
|
257
|
-
[data-pluto-admin-scope] h1 {
|
|
258
|
-
font-family: var(--pluto-admin-font-display);
|
|
259
|
-
letter-spacing: -0.01em;
|
|
260
|
-
}
|
|
261
|
-
`
|
|
262
|
-
fs.writeFileSync('app/assets/css/admin-theme.css', css)
|
|
263
|
-
console.log('written', css.split('\n').length, 'lines')
|
package/tailwind.lint.css
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* The Tailwind design system for ESLint (better-tailwindcss) and the editor
|
|
3
|
-
* (Tailwind IntelliSense). It is not part of the build: a Pluto layer has no
|
|
4
|
-
* Tailwind entry, the app owns it (see the single-tailwind-build skill). This
|
|
5
|
-
* file gives the lint the same theme as an app: Tailwind, Nuxt UI, and the
|
|
6
|
-
* Pluto layer CSS (the Pluto UI tokens and the admin theme).
|
|
7
|
-
*/
|
|
8
|
-
@import 'tailwindcss';
|
|
9
|
-
@import '@nuxt/ui';
|
|
10
|
-
@import './app/assets/css/pluto-layer.css';
|