Split CSS into base and theme, link them separately

- Move shared structural styles to frontend/src/assets/pagerite.css
  with conservative variables, and purple theme overrides to
  frontend/src/assets/themes/purple/theme.css.
- Backend now links the base and theme stylesheets as separate files
  so they can be swapped or augmented.
- Add blocking="render" to stylesheet links to avoid FOUC.
- Drop redundant defer from module script tags.
- Update related docs and comments.
This commit is contained in:
2026-08-16 19:51:17 +00:00
parent 840f15ee59
commit 6de0307856
10 changed files with 173 additions and 65 deletions
+1 -1
View File
@@ -335,7 +335,7 @@ onUnmounted(() => {
height: 100vh;
}
/* Docked-overlay positioning lives in the global style.css (.editor-host /
/* Docked-overlay positioning lives in the global pagerite.css (.editor-host /
.editor-root.overlay) since the host element is created by main.js. */
.toolbar {
+1 -1
View File
@@ -640,7 +640,7 @@ onUnmounted(() => {
height: 100vh;
}
/* Docked-overlay positioning lives in the global style.css (.editor-host /
/* Docked-overlay positioning lives in the global pagerite.css (.editor-host /
.editor-root.overlay) since the host element is created by main.js. */
.toolbar {
@@ -1,16 +1,17 @@
/* Shared styles for server-rendered pages and Vue components. */
/* Base structural and functional styles for Pagerite pages and editors.
Color choices are intentionally conservative CSS variables so themes can
override them without fighting layout or interaction rules. */
@import url("./fonts/fonts.css");
@import url("./pygments.css");
:root {
color-scheme: dark;
--bg: #12101c;
--surface: #1b1830;
--text: #e8e6f2;
--muted: #9a94b8;
--accent: #00d4c8;
--accent2: #7c5cff;
--line: #ffffff1a;
--bg: #ffffff;
--surface: #f5f5f5;
--text: #1a1a1a;
--muted: #666666;
--accent: #0056b3;
--accent2: #003d80;
--line: #0000001a;
/* Width of the docked editor panel (used both here for shifting the page
and in the Vue editor's own styles). */
--editor-w: min(46rem, 50vw);
@@ -45,10 +46,6 @@ body {
flex: 1;
}
::selection {
background: var(--accent2);
}
/* Full-width banner: image header with the brand and nav overlaid. */
#banner {
position: relative;
@@ -87,12 +84,7 @@ body {
text-decoration: none;
margin: auto 1.25rem 0;
padding-top: 1.5rem;
background: linear-gradient(90deg, var(--accent), var(--accent2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: none;
filter: drop-shadow(0 0 0.1rem #000);
color: var(--text);
}
/* Nav overlaid at the bottom of the banner */
@@ -433,7 +425,6 @@ pre {
past them by its own padding. */
margin-left: -0.8rem;
margin-right: -0.8rem;
background: #ffffff09;
border-radius: 4px;
position: relative;
}
@@ -595,7 +586,6 @@ footer {
FRAGILE: do not tweak; the view-transition pseudo-tree is picky. */
::view-transition {
perspective: 1000px;
background: #000;
inset: 0;
}
@@ -0,0 +1,34 @@
/* Purple theme: overrides the conservative base variables and adds the
more elaborate visual styling (dark palette, gradient brand, etc.). */
:root {
color-scheme: dark;
--bg: #12101c;
--surface: #1b1830;
--text: #e8e6f2;
--muted: #9a94b8;
--accent: #00d4c8;
--accent2: #7c5cff;
--line: #ffffff1a;
}
::selection {
background: var(--accent2);
}
#brand {
background: linear-gradient(90deg, var(--accent), var(--accent2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: none;
filter: drop-shadow(0 0 0.1rem #000);
}
pre {
background: #ffffff09;
}
::view-transition {
background: #000;
}
+5 -2
View File
@@ -1,5 +1,3 @@
import "./assets/style.css";
// Pagerite editor entries. Two separate apps, mounted in their own
// dynamically created host divs inside the static document:
// - PageEditor ("page" mode): pen next to an article heading — Markdown
@@ -8,6 +6,11 @@ import "./assets/style.css";
// (previewed into the real banner) and the site structure tree.
// The standalone /_/admin shell (#app in the DOM) mounts PageEditor with the
// page selected by location hash, as a no-dynamic-import fallback.
if (import.meta.env.DEV) {
import("./assets/pagerite.css");
import("./assets/themes/purple/theme.css");
}
import { createApp } from 'vue'
import PageEditor from './PageEditor.vue'
import SiteEditor from './SiteEditor.vue'
+6 -3
View File
@@ -1,5 +1,3 @@
import "./assets/style.css";
// Fetch-navigation: swap dynamic regions (#nav, #main) instead of full
// page loads. Real <a href> links are used throughout, so this is pure
// progressive enhancement - without JS every link does a normal load.
@@ -7,6 +5,11 @@ import "./assets/style.css";
// Also: scroll-reveal effects and code copy buttons. These need no
// support from the article itself and are re-applied after each swap.
(() => {
if (import.meta.env.DEV) {
import("./assets/pagerite.css");
import("./assets/themes/purple/theme.css");
}
const REGIONS = ["page-banner", "nav", "sidebar", "main"];
const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)");
let editorModule = null;
@@ -191,7 +194,7 @@ import "./assets/style.css";
runScripts(document.getElementById("main"));
applyEffects();
};
// Rotating cube page transition (see the FRAGILE block in style.css);
// Rotating cube page transition (see the FRAGILE block in pagerite.css);
// mirrored when navigating back through history. Navigation within the
// same top-level section crossfades instead, in either direction.
if (document.startViewTransition && !reduceMotion.matches) {
+2
View File
@@ -36,6 +36,8 @@ export default defineConfig({
input: {
main: fileURLToPath(new URL('./src/main.js', import.meta.url)),
pagerite: fileURLToPath(new URL('./src/pagerite.js', import.meta.url)),
pagerite_base: fileURLToPath(new URL('./src/assets/pagerite.css', import.meta.url)),
pagerite_theme: fileURLToPath(new URL('./src/assets/themes/purple/theme.css', import.meta.url)),
},
},
},