@plutocms/core 0.46.0 → 0.47.0
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 +16 -0
- package/FEATURES.md +4 -0
- package/app/assets/css/pluto-base.css +228 -0
- package/app/components/Pluto/PlutoHtml.vue +6 -21
- package/app/components/Pluto/PlutoRoot.vue +19 -0
- package/app/composables/pluto-admin-theme.ts +21 -0
- package/modules/pluto-css/css-entries.ts +33 -8
- package/modules/pluto-css/index.ts +27 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.47.0](https://github.com/plutocms/pluto/compare/v0.46.0...v0.47.0) (2026-11-02)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### ⚠ BREAKING CHANGES
|
|
8
|
+
|
|
9
|
+
* PlutoHtml has no Tailwind prose classes. A site imports `@plutocms/css/base` for the prose look. A site that adds its own page offset for the admin bar removes it, or the page moves down twice.
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* the public components use plain CSS, with an optional @plutocms/css/base ([#353](https://github.com/plutocms/pluto/issues/353)) ([d0fa541](https://github.com/plutocms/pluto/commit/d0fa541b34a672370ebf2d95372a967a1aebde10))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* the admin has its styles in the first frame of a client navigation ([#354](https://github.com/plutocms/pluto/issues/354)) ([f7a4226](https://github.com/plutocms/pluto/commit/f7a4226966e73bf2a91ae599303c35a8daac61a2))
|
|
19
|
+
|
|
4
20
|
## [0.46.0](https://github.com/plutocms/pluto/compare/v0.45.0...v0.46.0) (2026-11-02)
|
|
5
21
|
|
|
6
22
|
|
package/FEATURES.md
CHANGED
|
@@ -72,6 +72,10 @@ policies shared by every project here.
|
|
|
72
72
|
layer's `pluto-layer.css` (imported as `@plutocms/css` by the admin build and by a Tailwind
|
|
73
73
|
site), and warns about a Tailwind build in a layer or more than one.
|
|
74
74
|
@.claude/skills/single-tailwind-build/SKILL.md
|
|
75
|
+
- Public components: the components for a public site (`PlutoHtml`, `PlutoImage`, `PlutoLogo`,
|
|
76
|
+
`PlutoRoot`, and those of the layers) use only `pluto-*` classes and work with no Tailwind. Their
|
|
77
|
+
look is plain CSS in each layer's `pluto-base.css`, which a site imports with the opt-in
|
|
78
|
+
`@import '@plutocms/css/base'`. @.claude/skills/public-components/SKILL.md
|
|
75
79
|
- Git CMS: a standalone CMS with Markdown posts in a Git repo. Out of the box it publishes to the
|
|
76
80
|
local Git repo (dev only: each save is a local commit, `commit.local`); a GitHub App publishes
|
|
77
81
|
from a deployed site. The module is `pluto-github` (config
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The look of the public components of core. Opt-in: a site imports it with
|
|
3
|
+
* `@import '@plutocms/css/base'`, with or without Tailwind. Plain CSS only:
|
|
4
|
+
* no Tailwind directive. See the public-components skill.
|
|
5
|
+
*
|
|
6
|
+
* Every selector is in `:where()` (zero specificity) and in the `components`
|
|
7
|
+
* layer. On a Tailwind site, the file joins Tailwind's own layer, so a utility
|
|
8
|
+
* wins. On a site with no Tailwind, any unlayered rule of the site wins.
|
|
9
|
+
*
|
|
10
|
+
* Colors read the Nuxt UI variables, with plain fallbacks: a Nuxt UI site
|
|
11
|
+
* gets its own theme. A site can also set the `--pluto-prose-*` variables.
|
|
12
|
+
*/
|
|
13
|
+
@layer theme, base, components, utilities;
|
|
14
|
+
|
|
15
|
+
@layer components {
|
|
16
|
+
/* PlutoHtml, PlutoMarkdown, and the PlutoProse of the blog layer. */
|
|
17
|
+
:where(.pluto-prose) {
|
|
18
|
+
--pluto-prose-heading: var(--ui-text-highlighted, currentColor);
|
|
19
|
+
--pluto-prose-muted: var(
|
|
20
|
+
--ui-text-muted,
|
|
21
|
+
color-mix(in oklab, currentColor 65%, transparent)
|
|
22
|
+
);
|
|
23
|
+
--pluto-prose-link: var(--ui-primary, LinkText);
|
|
24
|
+
--pluto-prose-border: var(
|
|
25
|
+
--ui-border,
|
|
26
|
+
color-mix(in oklab, currentColor 15%, transparent)
|
|
27
|
+
);
|
|
28
|
+
--pluto-prose-border-accented: var(
|
|
29
|
+
--ui-border-accented,
|
|
30
|
+
color-mix(in oklab, currentColor 30%, transparent)
|
|
31
|
+
);
|
|
32
|
+
--pluto-prose-code-bg: var(
|
|
33
|
+
--ui-bg-muted,
|
|
34
|
+
color-mix(in oklab, currentColor 6%, transparent)
|
|
35
|
+
);
|
|
36
|
+
--pluto-prose-th-bg: var(
|
|
37
|
+
--ui-bg-elevated,
|
|
38
|
+
color-mix(in oklab, currentColor 4%, transparent)
|
|
39
|
+
);
|
|
40
|
+
--pluto-prose-radius: calc(var(--ui-radius, 0.25rem) * 1.5);
|
|
41
|
+
--pluto-prose-mono: var(
|
|
42
|
+
--font-mono,
|
|
43
|
+
ui-monospace,
|
|
44
|
+
SFMono-Regular,
|
|
45
|
+
Menlo,
|
|
46
|
+
Consolas,
|
|
47
|
+
monospace
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
width: 100%;
|
|
51
|
+
overflow-wrap: break-word;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
:where(.pluto-prose) ::selection {
|
|
55
|
+
background-color: color-mix(
|
|
56
|
+
in oklab,
|
|
57
|
+
var(--pluto-prose-link) 20%,
|
|
58
|
+
transparent
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:where(.pluto-prose) > :where(*) {
|
|
63
|
+
margin-block: 1.25rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:where(.pluto-prose) > :where(:first-child) {
|
|
67
|
+
margin-block-start: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
:where(.pluto-prose) > :where(:last-child) {
|
|
71
|
+
margin-block-end: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:where(.pluto-prose) :where(p) {
|
|
75
|
+
line-height: 1.75rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:where(.pluto-prose) :where(a) {
|
|
79
|
+
color: var(--pluto-prose-link);
|
|
80
|
+
font-weight: 500;
|
|
81
|
+
text-decoration: none;
|
|
82
|
+
border-block-end: 1px solid transparent;
|
|
83
|
+
transition: border-color 0.15s;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:where(.pluto-prose) :where(a:hover) {
|
|
87
|
+
border-block-end-color: var(--pluto-prose-link);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:where(.pluto-prose) :where(a:focus-visible) {
|
|
91
|
+
border-block-end-color: transparent;
|
|
92
|
+
border-radius: 0.125rem;
|
|
93
|
+
outline: 3px solid
|
|
94
|
+
color-mix(in oklab, var(--pluto-prose-link) 25%, transparent);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
:where(.pluto-prose) :where(h1, h2, h3, h4, h5, h6) {
|
|
98
|
+
color: var(--pluto-prose-heading);
|
|
99
|
+
font-weight: 700;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
:where(.pluto-prose) :where(h1) {
|
|
103
|
+
font-size: 1.875rem;
|
|
104
|
+
line-height: 2.25rem;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:where(.pluto-prose) :where(h2) {
|
|
108
|
+
font-size: 1.5rem;
|
|
109
|
+
line-height: 2rem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
:where(.pluto-prose) :where(h3) {
|
|
113
|
+
font-size: 1.25rem;
|
|
114
|
+
line-height: 1.75rem;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
:where(.pluto-prose) :where(h4) {
|
|
118
|
+
font-size: 1.125rem;
|
|
119
|
+
line-height: 1.75rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
:where(.pluto-prose) :where(h5, h6) {
|
|
123
|
+
font-size: 1rem;
|
|
124
|
+
line-height: 1.5rem;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:where(.pluto-prose) :where(blockquote) {
|
|
128
|
+
margin-inline: 0;
|
|
129
|
+
padding-inline-start: 1rem;
|
|
130
|
+
border-inline-start: 4px solid var(--pluto-prose-border-accented);
|
|
131
|
+
font-style: italic;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
:where(.pluto-prose) :where(hr) {
|
|
135
|
+
border: 0;
|
|
136
|
+
border-block-start: 1px solid var(--pluto-prose-border);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
:where(.pluto-prose) :where(code) {
|
|
140
|
+
display: inline-block;
|
|
141
|
+
padding: 0.125rem 0.375rem;
|
|
142
|
+
border: 1px solid var(--pluto-prose-border);
|
|
143
|
+
border-radius: var(--pluto-prose-radius);
|
|
144
|
+
background-color: var(--pluto-prose-code-bg);
|
|
145
|
+
color: var(--pluto-prose-heading);
|
|
146
|
+
font-family: var(--pluto-prose-mono);
|
|
147
|
+
font-size: 0.875rem;
|
|
148
|
+
font-weight: 500;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:where(.pluto-prose) :where(pre) {
|
|
152
|
+
overflow-x: auto;
|
|
153
|
+
padding: 0.75rem 1rem;
|
|
154
|
+
border: 1px solid var(--pluto-prose-border);
|
|
155
|
+
border-radius: var(--pluto-prose-radius);
|
|
156
|
+
background-color: var(--pluto-prose-code-bg);
|
|
157
|
+
font-family: var(--pluto-prose-mono);
|
|
158
|
+
font-size: 0.875rem;
|
|
159
|
+
line-height: 1.5rem;
|
|
160
|
+
white-space: pre-wrap;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
:where(.pluto-prose) :where(pre code) {
|
|
164
|
+
display: inline;
|
|
165
|
+
padding: 0;
|
|
166
|
+
border: 0;
|
|
167
|
+
border-radius: 0;
|
|
168
|
+
background-color: transparent;
|
|
169
|
+
color: inherit;
|
|
170
|
+
font-size: inherit;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
:where(.pluto-prose) :where(ul, ol) {
|
|
174
|
+
padding-inline-start: 1.5rem;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
:where(.pluto-prose) :where(ul) {
|
|
178
|
+
list-style-type: disc;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
:where(.pluto-prose) :where(ol) {
|
|
182
|
+
list-style-type: decimal;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
:where(.pluto-prose) :where(ul > li)::marker {
|
|
186
|
+
color: var(--pluto-prose-border-accented);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
:where(.pluto-prose) :where(ol > li)::marker {
|
|
190
|
+
color: var(--pluto-prose-muted);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
:where(.pluto-prose) :where(li) {
|
|
194
|
+
margin-block: 0.375rem;
|
|
195
|
+
padding-inline-start: 0.375rem;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
:where(.pluto-prose) :where(img) {
|
|
199
|
+
display: block;
|
|
200
|
+
max-width: 100%;
|
|
201
|
+
height: auto;
|
|
202
|
+
border-radius: var(--pluto-prose-radius);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
:where(.pluto-prose) :where(table) {
|
|
206
|
+
display: block;
|
|
207
|
+
overflow-x: auto;
|
|
208
|
+
width: 100%;
|
|
209
|
+
border-collapse: collapse;
|
|
210
|
+
font-size: 0.875rem;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
:where(.pluto-prose) :where(th, td) {
|
|
214
|
+
padding: 0.5rem 0.75rem;
|
|
215
|
+
border: 1px solid var(--pluto-prose-border);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
:where(.pluto-prose) :where(th) {
|
|
219
|
+
background-color: var(--pluto-prose-th-bg);
|
|
220
|
+
color: var(--pluto-prose-heading);
|
|
221
|
+
font-weight: 600;
|
|
222
|
+
text-align: start;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
:where(.pluto-prose) :where(input[type='checkbox']) {
|
|
226
|
+
margin-inline-end: 0.5rem;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -1,36 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { tv } from '@nuxt/ui/utils/tv'
|
|
3
|
-
import theme from '#build/ui/editor'
|
|
4
|
-
|
|
5
2
|
// Shows the `html` of a public item (the public API of Pluto), with readable prose styles. It
|
|
6
3
|
// works with every backend: the Git CMS and `@plutocms/supabase-blog` both send the body as
|
|
7
4
|
// sanitized HTML, so the string is safe to set as `innerHTML`. This component never parses or
|
|
8
5
|
// sanitizes. Do not pass any other HTML to it.
|
|
9
|
-
|
|
6
|
+
//
|
|
7
|
+
// The prose look is `.pluto-prose` in app/assets/css/pluto-base.css: plain CSS that a site
|
|
8
|
+
// imports with `@plutocms/css/base`. No Tailwind class here, so the component works on a site
|
|
9
|
+
// with no Tailwind. See the public-components skill.
|
|
10
|
+
withDefaults(
|
|
10
11
|
defineProps<{
|
|
11
12
|
html: string
|
|
12
13
|
tag?: string
|
|
13
|
-
class?: any
|
|
14
14
|
}>(),
|
|
15
15
|
{
|
|
16
16
|
tag: 'article',
|
|
17
|
-
class: undefined,
|
|
18
17
|
}
|
|
19
18
|
)
|
|
20
|
-
|
|
21
|
-
// The base of the Nuxt UI editor theme gives readable prose with semantic
|
|
22
|
-
// color tokens. The editor adds side padding, so `sm:px-0` removes it.
|
|
23
|
-
// The table classes are here because the editor theme has none.
|
|
24
|
-
const ui = computed(() => tv({ extend: theme })())
|
|
25
|
-
|
|
26
|
-
const classes = computed(() => [
|
|
27
|
-
ui.value.base({ class: 'sm:px-0' }),
|
|
28
|
-
'[&_table]:block [&_table]:w-full [&_table]:overflow-x-auto [&_table]:text-sm',
|
|
29
|
-
'[&_:is(th,td)]:border [&_:is(th,td)]:border-default [&_:is(th,td)]:px-3 [&_:is(th,td)]:py-2',
|
|
30
|
-
'[&_th]:bg-elevated [&_th]:text-highlighted [&_th]:text-start [&_th]:font-semibold',
|
|
31
|
-
'[&_input[type=checkbox]]:me-2',
|
|
32
|
-
props.class,
|
|
33
|
-
])
|
|
34
19
|
</script>
|
|
35
20
|
|
|
36
21
|
<template>
|
|
@@ -38,5 +23,5 @@ const classes = computed(() => [
|
|
|
38
23
|
on a dynamic `<component :is>` drops the value in the SSR output. The
|
|
39
24
|
hyphenated spelling renders a literal `inner-h-t-m-l` attribute. -->
|
|
40
25
|
<!-- eslint-disable-next-line vue/attribute-hyphenation -- must stay camelCase, see comment above -->
|
|
41
|
-
<component :is="tag" v-if="html" :
|
|
26
|
+
<component :is="tag" v-if="html" :innerHTML="html" class="pluto-prose" />
|
|
42
27
|
</template>
|
|
@@ -162,6 +162,25 @@ useHead({
|
|
|
162
162
|
--pluto-admin-bar-height: 2.5rem;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/*
|
|
166
|
+
* The bar is fixed at the top, so a public page moves down by its height while
|
|
167
|
+
* it shows: the top of the page is never under the bar. A sticky or fixed
|
|
168
|
+
* header of the site still needs `top: var(--pluto-admin-bar-height)`. The
|
|
169
|
+
* admin pages (`data-pluto-admin`) have their own layout. Zero specificity
|
|
170
|
+
* (`:where`), so a rule of the site wins.
|
|
171
|
+
*/
|
|
172
|
+
:where(:root[data-pluto-admin-bar]:not([data-pluto-admin])) body {
|
|
173
|
+
padding-top: var(--pluto-admin-bar-height);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
:where(:root[data-pluto-admin-bar]:not([data-pluto-admin])) {
|
|
177
|
+
scroll-padding-top: var(--pluto-admin-bar-height);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
:where(:root[data-pluto-admin-bar]:not([data-pluto-admin])) #__nuxt {
|
|
181
|
+
min-height: calc(100dvh - var(--pluto-admin-bar-height));
|
|
182
|
+
}
|
|
183
|
+
|
|
165
184
|
/*
|
|
166
185
|
* The wrappers grow to fill #__nuxt. Plain CSS, not Tailwind classes: a site
|
|
167
186
|
* may have no Tailwind (the admin has a build of its own).
|
|
@@ -8,9 +8,30 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Call it from every admin layout. `useHead` removes the attribute when the
|
|
10
10
|
* layout unmounts, so the public site never gets it.
|
|
11
|
+
*
|
|
12
|
+
* `useHead` puts the attribute in the server HTML, but in the browser it
|
|
13
|
+
* writes <html> after the render, a frame late. The admin build is scoped to
|
|
14
|
+
* the attribute, so a client navigation from the public site showed one frame
|
|
15
|
+
* of the admin with no styles. `onMounted` runs in the same task as the DOM
|
|
16
|
+
* change, before the paint, so the attribute is set there too. A count keeps
|
|
17
|
+
* it while another admin layout is mounted.
|
|
11
18
|
*/
|
|
19
|
+
let mountedAdminLayouts = 0
|
|
20
|
+
|
|
12
21
|
export function usePlutoAdminTheme() {
|
|
13
22
|
useHead({ htmlAttrs: { 'data-pluto-admin': '' } })
|
|
23
|
+
if (import.meta.client) {
|
|
24
|
+
onMounted(() => {
|
|
25
|
+
mountedAdminLayouts++
|
|
26
|
+
document.documentElement.setAttribute('data-pluto-admin', '')
|
|
27
|
+
})
|
|
28
|
+
onUnmounted(() => {
|
|
29
|
+
mountedAdminLayouts--
|
|
30
|
+
if (mountedAdminLayouts === 0) {
|
|
31
|
+
document.documentElement.removeAttribute('data-pluto-admin')
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
}
|
|
14
35
|
}
|
|
15
36
|
|
|
16
37
|
/**
|
|
@@ -11,24 +11,30 @@ export const PLUTO_CSS_IMPORT = '@plutocms/css'
|
|
|
11
11
|
/** The opt-in Pluto site theme: an alias of `#build/pluto-site.css`. */
|
|
12
12
|
export const PLUTO_SITE_CSS_IMPORT = '@plutocms/css/site'
|
|
13
13
|
|
|
14
|
+
/** A layer's plain CSS for its public components. See the public-components skill. */
|
|
15
|
+
export const LAYER_BASE_CSS_FILE = 'assets/css/pluto-base.css'
|
|
16
|
+
|
|
17
|
+
/** The opt-in look of the public components: an alias of `#build/pluto-base.css`. */
|
|
18
|
+
export const PLUTO_BASE_CSS_IMPORT = '@plutocms/css/base'
|
|
19
|
+
|
|
14
20
|
function toPosix(path: string): string {
|
|
15
21
|
return path.replaceAll('\\', '/')
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
|
-
* Returns the `
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* later in the build and wins.
|
|
25
|
+
* Returns the `file` of every layer that has one, base layer first. Nuxt
|
|
26
|
+
* lists the layers top first (the app, then each layer it extends), so the
|
|
27
|
+
* list is reversed: a higher layer's rules then come later and win.
|
|
23
28
|
*/
|
|
24
29
|
export function findLayerCssFiles(
|
|
25
30
|
layerAppDirs: string[],
|
|
26
|
-
exists: (path: string) => boolean
|
|
31
|
+
exists: (path: string) => boolean,
|
|
32
|
+
file = LAYER_CSS_FILE
|
|
27
33
|
): string[] {
|
|
28
34
|
return [...layerAppDirs]
|
|
29
35
|
.reverse()
|
|
30
|
-
.map((appDir) => toPosix(join(appDir,
|
|
31
|
-
.filter((
|
|
36
|
+
.map((appDir) => toPosix(join(appDir, file)))
|
|
37
|
+
.filter((path) => exists(path))
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
/**
|
|
@@ -37,9 +43,26 @@ export function findLayerCssFiles(
|
|
|
37
43
|
* another drive (Windows) stays absolute.
|
|
38
44
|
*/
|
|
39
45
|
export function renderPlutoCss(files: string[], buildDir: string): string {
|
|
46
|
+
return renderLayerImports(files, buildDir, LAYER_CSS_FILE)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Renders `#build/pluto-base.css`: one `@import` for each layer's
|
|
51
|
+
* `pluto-base.css`. The plain CSS of the public components: an app imports
|
|
52
|
+
* it (`@plutocms/css/base`) with or without Tailwind.
|
|
53
|
+
*/
|
|
54
|
+
export function renderPlutoBaseCss(files: string[], buildDir: string): string {
|
|
55
|
+
return renderLayerImports(files, buildDir, LAYER_BASE_CSS_FILE)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function renderLayerImports(
|
|
59
|
+
files: string[],
|
|
60
|
+
buildDir: string,
|
|
61
|
+
name: string
|
|
62
|
+
): string {
|
|
40
63
|
return [
|
|
41
64
|
'/* Generated by @plutocms/core (modules/pluto-css). Do not edit. */',
|
|
42
|
-
|
|
65
|
+
`/* Each Pluto layer's app/${name}, base layer first. */`,
|
|
43
66
|
...files.map((file) => cssImport(file, buildDir)),
|
|
44
67
|
'',
|
|
45
68
|
].join('\n')
|
|
@@ -81,6 +104,8 @@ export function renderPlutoAdminCss(): string {
|
|
|
81
104
|
'@import "tailwindcss";',
|
|
82
105
|
'@import "@nuxt/ui";',
|
|
83
106
|
`@import "${PLUTO_CSS_IMPORT}";`,
|
|
107
|
+
// The admin shows public components too: the post preview, the 3D preview.
|
|
108
|
+
`@import "${PLUTO_BASE_CSS_IMPORT}";`,
|
|
84
109
|
'',
|
|
85
110
|
].join('\n')
|
|
86
111
|
}
|
|
@@ -13,9 +13,12 @@ import {
|
|
|
13
13
|
analyzeCssEntry,
|
|
14
14
|
checkCssSetup,
|
|
15
15
|
findLayerCssFiles,
|
|
16
|
+
LAYER_BASE_CSS_FILE,
|
|
17
|
+
PLUTO_BASE_CSS_IMPORT,
|
|
16
18
|
PLUTO_CSS_IMPORT,
|
|
17
19
|
PLUTO_SITE_CSS_IMPORT,
|
|
18
20
|
renderPlutoAdminCss,
|
|
21
|
+
renderPlutoBaseCss,
|
|
19
22
|
renderPlutoCss,
|
|
20
23
|
renderPlutoSiteCss,
|
|
21
24
|
resolveRelativeImport,
|
|
@@ -49,6 +52,9 @@ export interface PlutoCssOptions {
|
|
|
49
52
|
* - `@plutocms/css/site` (opt-in) is an alias of `#build/pluto-site.css`: the
|
|
50
53
|
* Pluto site theme of core (app/assets/css/pluto-site-theme.css), the look of
|
|
51
54
|
* the Pluto docs for a site.
|
|
55
|
+
* - `@plutocms/css/base` (opt-in) is an alias of `#build/pluto-base.css`: each
|
|
56
|
+
* layer's app/assets/css/pluto-base.css, the plain CSS look of its public
|
|
57
|
+
* components. It needs no Tailwind. The admin build imports it too.
|
|
52
58
|
* - The names are aliases, not npm packages, so they resolve with every package
|
|
53
59
|
* manager, also when the app installs only an outer layer.
|
|
54
60
|
* - The module warns about a layer that registers its own Tailwind build, and
|
|
@@ -84,13 +90,31 @@ export default defineNuxtModule<PlutoCssOptions>({
|
|
|
84
90
|
getContents: () => renderPlutoSiteCss(siteTheme, nuxt.options.buildDir),
|
|
85
91
|
})
|
|
86
92
|
|
|
87
|
-
// The
|
|
88
|
-
//
|
|
89
|
-
|
|
93
|
+
// The opt-in plain CSS of the public components, from every layer. An
|
|
94
|
+
// app imports `@plutocms/css/base`, with or without Tailwind.
|
|
95
|
+
const layerBaseCssFiles = findLayerCssFiles(
|
|
96
|
+
layers.map((layer) => layer.app),
|
|
97
|
+
existsSync,
|
|
98
|
+
LAYER_BASE_CSS_FILE
|
|
99
|
+
)
|
|
100
|
+
addTemplate({
|
|
101
|
+
filename: 'pluto-base.css',
|
|
102
|
+
write: true,
|
|
103
|
+
getContents: () =>
|
|
104
|
+
renderPlutoBaseCss(layerBaseCssFiles, nuxt.options.buildDir),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// The names that an app's CSS entry imports. Tailwind and Vite resolve
|
|
108
|
+
// `@import` through the aliases. `/site` and `/base` come first: an alias
|
|
109
|
+
// also matches the paths under its name, so `@plutocms/css` would take them.
|
|
90
110
|
nuxt.options.alias[PLUTO_SITE_CSS_IMPORT] = join(
|
|
91
111
|
nuxt.options.buildDir,
|
|
92
112
|
'pluto-site.css'
|
|
93
113
|
)
|
|
114
|
+
nuxt.options.alias[PLUTO_BASE_CSS_IMPORT] = join(
|
|
115
|
+
nuxt.options.buildDir,
|
|
116
|
+
'pluto-base.css'
|
|
117
|
+
)
|
|
94
118
|
nuxt.options.alias[PLUTO_CSS_IMPORT] = join(
|
|
95
119
|
nuxt.options.buildDir,
|
|
96
120
|
'pluto.css'
|
package/package.json
CHANGED