Add corporate and nitro themes, inline theme banners, code palette sets
- Two new themes (corporate light/dark, nitro racing) and a purple retune; theme banner SVGs moved to pagerite/themes/, inlined by the backend, recolored per scheme via CSS classes, with scroll parallax. - pygments.css maps tokens onto --code-* variables; light and dark palette sets in the base stylesheet resolve via light-dark() from the theme's color-scheme; themes just pick a set or retint --code-bg. - Sidebar omitted unless the section has at least two published items; fetch-navigation and the site editor handle it appearing/disappearing. - Floated figures keep captions below at 30% of the text column; h1/h2 clear floats so images never overflow into the next section. - Links use --link (color-mix of text and accent), weight 500, no underlines; hover goes full accent.
This commit is contained in:
@@ -105,11 +105,22 @@ function openPath(p) {
|
||||
|
||||
// --- In-place navigation (no transitions, replaceState) ------------------
|
||||
function swapRegions(doc) {
|
||||
for (const id of ['page-banner', 'nav', 'sidebar', 'main']) {
|
||||
for (const id of ['page-banner', 'nav', 'main']) {
|
||||
const fresh = doc.getElementById(id)
|
||||
const el = document.getElementById(id)
|
||||
if (fresh && el) el.replaceWith(document.importNode(fresh, true))
|
||||
}
|
||||
// #sidebar is omitted entirely when the section has no sub-navigation,
|
||||
// so it may be absent on either side: replace, insert, or remove.
|
||||
const freshSidebar = doc.getElementById('sidebar')
|
||||
const curSidebar = document.getElementById('sidebar')
|
||||
if (freshSidebar && curSidebar) {
|
||||
curSidebar.replaceWith(document.importNode(freshSidebar, true))
|
||||
} else if (freshSidebar) {
|
||||
document.getElementById('main')?.before(document.importNode(freshSidebar, true))
|
||||
} else if (curSidebar) {
|
||||
curSidebar.remove()
|
||||
}
|
||||
// The brand link lives in the header, outside the swappable regions,
|
||||
// and is absent entirely when no brand is configured.
|
||||
const freshBrand = doc.getElementById('brand')
|
||||
@@ -283,6 +294,8 @@ const theme = ref('purple')
|
||||
const THEME_OPTIONS = [
|
||||
{ value: '', label: 'none' },
|
||||
{ value: 'purple', label: 'purple' },
|
||||
{ value: 'corporate', label: 'corporate' },
|
||||
{ value: 'nitro', label: 'nitro' },
|
||||
]
|
||||
|
||||
async function loadSettings() {
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
@import url("./pygments.css");
|
||||
|
||||
:root {
|
||||
/* The base theme is light, period — this also resolves the light-dark()
|
||||
code palette below (themes declare their own color-scheme). */
|
||||
color-scheme: light;
|
||||
--bg: #ffffff;
|
||||
--surface: #f5f5f5;
|
||||
--text: #1a1a1a;
|
||||
@@ -12,6 +15,28 @@
|
||||
--accent: #0056b3;
|
||||
--accent2: #003d80;
|
||||
--line: #0000001a;
|
||||
/* Links stay quiet — mostly text color with a hint of the accent —
|
||||
and light up to the full accent on hover. */
|
||||
--link: color-mix(in oklab, var(--text) 50%, var(--accent));
|
||||
/* Code highlighting palette, consumed by pygments.css: complete light and
|
||||
dark sets (background included), resolved by light-dark() from the
|
||||
used color-scheme. A theme picks a set simply by declaring
|
||||
color-scheme; a fixed dark code block on a light page pins the dark
|
||||
set with `pre { color-scheme: dark }`. Only override individual
|
||||
--code-* variables for specific theming needs. */
|
||||
--code-bg: light-dark(var(--surface), #0d1117);
|
||||
--code-text: light-dark(#24292f, #e6edf3);
|
||||
--code-muted: light-dark(#6e7781, #6e7681);
|
||||
--code-comment: light-dark(#59636e, #8b949e);
|
||||
--code-keyword: light-dark(#cf222e, #ff7b72);
|
||||
--code-builtin: light-dark(#0550ae, #79c0ff);
|
||||
--code-string: light-dark(#0a3069, #a5d6ff);
|
||||
--code-function: light-dark(#8250df, #d2a8ff);
|
||||
--code-type: light-dark(#953800, #f0883e);
|
||||
--code-tag: light-dark(#116329, #7ee787);
|
||||
--code-error: light-dark(#d1242f, #f85149);
|
||||
--code-added: light-dark(#1a7f37, #56d364);
|
||||
--code-hl: light-dark(#fff8c5, #6e7681);
|
||||
/* One variable per available font family (faces in fonts/fonts.css);
|
||||
everything else references these, so font stacks live here only. */
|
||||
--font-source-sans: "Source Sans 3", system-ui, sans-serif;
|
||||
@@ -77,8 +102,10 @@ body {
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* Per-page banner content (img, styled div, canvas...) overlays the
|
||||
default artwork; swapped along with #nav/#main on fetch-navigation. */
|
||||
/* Per-page banner content (img, styled div, inline SVG...) fills the
|
||||
banner; swapped along with #nav/#main on fetch-navigation. When the page
|
||||
sets no banner of its own, the backend inlines the active theme's SVG
|
||||
artwork here instead (pagerite/themes/{theme}/banner.svg). */
|
||||
#page-banner {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -90,16 +117,11 @@ body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Legibility scrim: fade banner artwork into the page background behind
|
||||
the overlaid brand/nav. Text shadows are not used for this — on light
|
||||
backgrounds they just look like blurry text. */
|
||||
#page-banner::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 30% 0 0;
|
||||
background: linear-gradient(transparent, var(--bg));
|
||||
/* Scroll parallax: pagerite.js sets --pry on <html>; the banner stays
|
||||
windowed in place while the artwork drifts inside it. The scale
|
||||
provides overscan so the drift never reveals an edge. */
|
||||
transform: scale(1.25) translateY(var(--pry, 0px));
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
#brand,
|
||||
@@ -247,10 +269,6 @@ button.banner-edit-link {
|
||||
backdrop-filter: blur(0.5rem);
|
||||
}
|
||||
|
||||
#sidebar:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
@@ -436,12 +454,13 @@ article h2 {
|
||||
}
|
||||
|
||||
article a {
|
||||
color: var(--accent);
|
||||
color: var(--link);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
article a:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Blockquotes: inner paragraphs carry no margins (spacing comes from the
|
||||
@@ -463,9 +482,11 @@ pre {
|
||||
overflow-x: auto;
|
||||
padding: 0.5rem 0.8rem;
|
||||
/* Code text aligns with the surrounding paragraphs: the box extends
|
||||
past them by its own padding. */
|
||||
past them by its own padding. Themes that add a left border must
|
||||
extend margin-left by the border width to keep this alignment. */
|
||||
margin-left: -0.8rem;
|
||||
margin-right: -0.8rem;
|
||||
background: var(--code-bg);
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
@@ -531,7 +552,6 @@ figure {
|
||||
figcaption {
|
||||
color: var(--muted);
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
/* Captions of full-bleed images: centered and kept to a readable width. */
|
||||
@@ -546,14 +566,12 @@ figure:has(.right),
|
||||
img.right {
|
||||
float: right;
|
||||
margin: 0.3rem 0 1rem 1.5rem;
|
||||
max-width: 45%;
|
||||
}
|
||||
|
||||
figure:has(.left),
|
||||
img.left {
|
||||
float: left;
|
||||
margin: 0.3rem 1.5rem 1rem 0;
|
||||
max-width: 45%;
|
||||
}
|
||||
|
||||
/* .wide is full bleed: edge to edge of the viewport (or of the space left
|
||||
@@ -577,6 +595,44 @@ p:has(> img.wide:only-child) {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
/* A captioned image is a <figure>, and the positioning class lives on the
|
||||
img inside it: only the figure may float/shift, never the img itself,
|
||||
or the caption would wrap beside the image instead of sitting below.
|
||||
The image fills the figure's width. */
|
||||
figure:has(.right) img,
|
||||
figure:has(.left) img {
|
||||
float: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
figure:has(.wide) img {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Floats take a fixed share of the text column rather than sizing by the
|
||||
image's intrinsic width, which varies wildly (SVGs have none, and
|
||||
min-content collapses them). A percentage also scales correctly inside
|
||||
two-column segments, where the column is the containing block. The
|
||||
caption wraps within that width. A width attribute ({.right width=300})
|
||||
overrides the default on uncaptioned images. */
|
||||
figure:has(.right),
|
||||
figure:has(.left),
|
||||
img.right:not([width]),
|
||||
img.left:not([width]) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
/* Section headings start below any floated figures of the previous
|
||||
section, so images never overflow into the next one. */
|
||||
article h1,
|
||||
article h2 {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
body.editing figure:has(.wide),
|
||||
body.editing img.wide {
|
||||
width: calc(100vw - var(--editor-w) - 1rem);
|
||||
|
||||
@@ -1,86 +1,90 @@
|
||||
/* Pygments token styling, mapped 1:1 onto the --code-* palette variables
|
||||
defined in pagerite.css (light and dark sets resolved there via
|
||||
light-dark() from the theme's color-scheme). No literal colors in this
|
||||
file: recoloring code happens through the variables only. */
|
||||
pre { line-height: 125%; }
|
||||
td.linenos .normal { color: #6e7681; background-color: #0d1117; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: #6e7681; background-color: #0d1117; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .special { color: #e6edf3; background-color: #6e7681; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos.special { color: #e6edf3; background-color: #6e7681; padding-left: 5px; padding-right: 5px; }
|
||||
pre code .hll { background-color: #6e7681 }
|
||||
pre code { color: #E6EDF3 }
|
||||
pre code .c { color: #8B949E; font-style: italic } /* Comment */
|
||||
pre code .err { color: #F85149 } /* Error */
|
||||
pre code .esc { color: #E6EDF3 } /* Escape */
|
||||
pre code .g { color: #E6EDF3 } /* Generic */
|
||||
pre code .k { color: #FF7B72 } /* Keyword */
|
||||
pre code .l { color: #A5D6FF } /* Literal */
|
||||
pre code .n { color: #E6EDF3 } /* Name */
|
||||
pre code .o { color: #FF7B72; font-weight: bold } /* Operator */
|
||||
pre code .x { color: #E6EDF3 } /* Other */
|
||||
pre code .p { color: #E6EDF3 } /* Punctuation */
|
||||
pre code .ch { color: #8B949E; font-style: italic } /* Comment.Hashbang */
|
||||
pre code .cm { color: #8B949E; font-style: italic } /* Comment.Multiline */
|
||||
pre code .cp { color: #8B949E; font-weight: bold; font-style: italic } /* Comment.Preproc */
|
||||
pre code .cpf { color: #8B949E; font-style: italic } /* Comment.PreprocFile */
|
||||
pre code .c1 { color: #8B949E; font-style: italic } /* Comment.Single */
|
||||
pre code .cs { color: #8B949E; font-weight: bold; font-style: italic } /* Comment.Special */
|
||||
pre code .gd { color: #FFA198; background-color: #490202 } /* Generic.Deleted */
|
||||
pre code .ge { color: #E6EDF3; font-style: italic } /* Generic.Emph */
|
||||
pre code .ges { color: #E6EDF3; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||
pre code .gr { color: #FFA198 } /* Generic.Error */
|
||||
pre code .gh { color: #79C0FF; font-weight: bold } /* Generic.Heading */
|
||||
pre code .gi { color: #56D364; background-color: #0F5323 } /* Generic.Inserted */
|
||||
pre code .go { color: #8B949E } /* Generic.Output */
|
||||
pre code .gp { color: #8B949E } /* Generic.Prompt */
|
||||
pre code .gs { color: #E6EDF3; font-weight: bold } /* Generic.Strong */
|
||||
pre code .gu { color: #79C0FF } /* Generic.Subheading */
|
||||
pre code .gt { color: #FF7B72 } /* Generic.Traceback */
|
||||
pre code .g-Underline { color: #E6EDF3; text-decoration: underline } /* Generic.Underline */
|
||||
pre code .kc { color: #79C0FF } /* Keyword.Constant */
|
||||
pre code .kd { color: #FF7B72 } /* Keyword.Declaration */
|
||||
pre code .kn { color: #FF7B72 } /* Keyword.Namespace */
|
||||
pre code .kp { color: #79C0FF } /* Keyword.Pseudo */
|
||||
pre code .kr { color: #FF7B72 } /* Keyword.Reserved */
|
||||
pre code .kt { color: #FF7B72 } /* Keyword.Type */
|
||||
pre code .ld { color: #79C0FF } /* Literal.Date */
|
||||
pre code .m { color: #A5D6FF } /* Literal.Number */
|
||||
pre code .s { color: #A5D6FF } /* Literal.String */
|
||||
pre code .na { color: #E6EDF3 } /* Name.Attribute */
|
||||
pre code .nb { color: #E6EDF3 } /* Name.Builtin */
|
||||
pre code .nc { color: #F0883E; font-weight: bold } /* Name.Class */
|
||||
pre code .no { color: #79C0FF; font-weight: bold } /* Name.Constant */
|
||||
pre code .nd { color: #D2A8FF; font-weight: bold } /* Name.Decorator */
|
||||
pre code .ni { color: #FFA657 } /* Name.Entity */
|
||||
pre code .ne { color: #F0883E; font-weight: bold } /* Name.Exception */
|
||||
pre code .nf { color: #D2A8FF; font-weight: bold } /* Name.Function */
|
||||
pre code .nl { color: #79C0FF; font-weight: bold } /* Name.Label */
|
||||
pre code .nn { color: #FF7B72 } /* Name.Namespace */
|
||||
pre code .nx { color: #E6EDF3 } /* Name.Other */
|
||||
pre code .py { color: #79C0FF } /* Name.Property */
|
||||
pre code .nt { color: #7EE787 } /* Name.Tag */
|
||||
pre code .nv { color: #79C0FF } /* Name.Variable */
|
||||
pre code .ow { color: #FF7B72; font-weight: bold } /* Operator.Word */
|
||||
pre code .pm { color: #E6EDF3 } /* Punctuation.Marker */
|
||||
pre code .w { color: #6E7681 } /* Text.Whitespace */
|
||||
pre code .mb { color: #A5D6FF } /* Literal.Number.Bin */
|
||||
pre code .mf { color: #A5D6FF } /* Literal.Number.Float */
|
||||
pre code .mh { color: #A5D6FF } /* Literal.Number.Hex */
|
||||
pre code .mi { color: #A5D6FF } /* Literal.Number.Integer */
|
||||
pre code .mo { color: #A5D6FF } /* Literal.Number.Oct */
|
||||
pre code .sa { color: #79C0FF } /* Literal.String.Affix */
|
||||
pre code .sb { color: #A5D6FF } /* Literal.String.Backtick */
|
||||
pre code .sc { color: #A5D6FF } /* Literal.String.Char */
|
||||
pre code .dl { color: #79C0FF } /* Literal.String.Delimiter */
|
||||
pre code .sd { color: #A5D6FF } /* Literal.String.Doc */
|
||||
pre code .s2 { color: #A5D6FF } /* Literal.String.Double */
|
||||
pre code .se { color: #79C0FF } /* Literal.String.Escape */
|
||||
pre code .sh { color: #79C0FF } /* Literal.String.Heredoc */
|
||||
pre code .si { color: #A5D6FF } /* Literal.String.Interpol */
|
||||
pre code .sx { color: #A5D6FF } /* Literal.String.Other */
|
||||
pre code .sr { color: #79C0FF } /* Literal.String.Regex */
|
||||
pre code .s1 { color: #A5D6FF } /* Literal.String.Single */
|
||||
pre code .ss { color: #A5D6FF } /* Literal.String.Symbol */
|
||||
pre code .bp { color: #E6EDF3 } /* Name.Builtin.Pseudo */
|
||||
pre code .fm { color: #D2A8FF; font-weight: bold } /* Name.Function.Magic */
|
||||
pre code .vc { color: #79C0FF } /* Name.Variable.Class */
|
||||
pre code .vg { color: #79C0FF } /* Name.Variable.Global */
|
||||
pre code .vi { color: #79C0FF } /* Name.Variable.Instance */
|
||||
pre code .vm { color: #79C0FF } /* Name.Variable.Magic */
|
||||
pre code .il { color: #A5D6FF } /* Literal.Number.Integer.Long */
|
||||
td.linenos .normal { color: var(--code-muted); background-color: var(--code-bg); padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: var(--code-muted); background-color: var(--code-bg); padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .special { color: var(--code-text); background-color: var(--code-muted); padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos.special { color: var(--code-text); background-color: var(--code-muted); padding-left: 5px; padding-right: 5px; }
|
||||
pre code .hll { background-color: var(--code-hl) }
|
||||
pre code { color: var(--code-text) }
|
||||
pre code .c { color: var(--code-comment); font-style: italic } /* Comment */
|
||||
pre code .err { color: var(--code-error) } /* Error */
|
||||
pre code .esc { color: var(--code-text) } /* Escape */
|
||||
pre code .g { color: var(--code-text) } /* Generic */
|
||||
pre code .k { color: var(--code-keyword) } /* Keyword */
|
||||
pre code .l { color: var(--code-string) } /* Literal */
|
||||
pre code .n { color: var(--code-text) } /* Name */
|
||||
pre code .o { color: var(--code-keyword); font-weight: bold } /* Operator */
|
||||
pre code .x { color: var(--code-text) } /* Other */
|
||||
pre code .p { color: var(--code-text) } /* Punctuation */
|
||||
pre code .ch { color: var(--code-comment); font-style: italic } /* Comment.Hashbang */
|
||||
pre code .cm { color: var(--code-comment); font-style: italic } /* Comment.Multiline */
|
||||
pre code .cp { color: var(--code-comment); font-weight: bold; font-style: italic } /* Comment.Preproc */
|
||||
pre code .cpf { color: var(--code-comment); font-style: italic } /* Comment.PreprocFile */
|
||||
pre code .c1 { color: var(--code-comment); font-style: italic } /* Comment.Single */
|
||||
pre code .cs { color: var(--code-comment); font-weight: bold; font-style: italic } /* Comment.Special */
|
||||
pre code .gd { color: var(--code-error); background-color: color-mix(in oklab, var(--code-error) 25%, var(--code-bg)) } /* Generic.Deleted */
|
||||
pre code .ge { color: var(--code-text); font-style: italic } /* Generic.Emph */
|
||||
pre code .ges { color: var(--code-text); font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||
pre code .gr { color: var(--code-error) } /* Generic.Error */
|
||||
pre code .gh { color: var(--code-builtin); font-weight: bold } /* Generic.Heading */
|
||||
pre code .gi { color: var(--code-added); background-color: color-mix(in oklab, var(--code-added) 25%, var(--code-bg)) } /* Generic.Inserted */
|
||||
pre code .go { color: var(--code-muted) } /* Generic.Output */
|
||||
pre code .gp { color: var(--code-muted) } /* Generic.Prompt */
|
||||
pre code .gs { color: var(--code-text); font-weight: bold } /* Generic.Strong */
|
||||
pre code .gu { color: var(--code-builtin) } /* Generic.Subheading */
|
||||
pre code .gt { color: var(--code-error) } /* Generic.Traceback */
|
||||
pre code .g-Underline { color: var(--code-text); text-decoration: underline } /* Generic.Underline */
|
||||
pre code .kc { color: var(--code-builtin) } /* Keyword.Constant */
|
||||
pre code .kd { color: var(--code-keyword) } /* Keyword.Declaration */
|
||||
pre code .kn { color: var(--code-keyword) } /* Keyword.Namespace */
|
||||
pre code .kp { color: var(--code-builtin) } /* Keyword.Pseudo */
|
||||
pre code .kr { color: var(--code-keyword) } /* Keyword.Reserved */
|
||||
pre code .kt { color: var(--code-builtin) } /* Keyword.Type */
|
||||
pre code .ld { color: var(--code-builtin) } /* Literal.Date */
|
||||
pre code .m { color: var(--code-string) } /* Literal.Number */
|
||||
pre code .s { color: var(--code-string) } /* Literal.String */
|
||||
pre code .na { color: var(--code-text) } /* Name.Attribute */
|
||||
pre code .nb { color: var(--code-text) } /* Name.Builtin */
|
||||
pre code .nc { color: var(--code-type); font-weight: bold } /* Name.Class */
|
||||
pre code .no { color: var(--code-builtin); font-weight: bold } /* Name.Constant */
|
||||
pre code .nd { color: var(--code-function); font-weight: bold } /* Name.Decorator */
|
||||
pre code .ni { color: var(--code-type) } /* Name.Entity */
|
||||
pre code .ne { color: var(--code-type); font-weight: bold } /* Name.Exception */
|
||||
pre code .nf { color: var(--code-function); font-weight: bold } /* Name.Function */
|
||||
pre code .nl { color: var(--code-builtin); font-weight: bold } /* Name.Label */
|
||||
pre code .nn { color: var(--code-keyword) } /* Name.Namespace */
|
||||
pre code .nx { color: var(--code-text) } /* Name.Other */
|
||||
pre code .py { color: var(--code-builtin) } /* Name.Property */
|
||||
pre code .nt { color: var(--code-tag) } /* Name.Tag */
|
||||
pre code .nv { color: var(--code-builtin) } /* Name.Variable */
|
||||
pre code .ow { color: var(--code-keyword); font-weight: bold } /* Operator.Word */
|
||||
pre code .pm { color: var(--code-text) } /* Punctuation.Marker */
|
||||
pre code .w { color: var(--code-muted) } /* Text.Whitespace */
|
||||
pre code .mb { color: var(--code-string) } /* Literal.Number.Bin */
|
||||
pre code .mf { color: var(--code-string) } /* Literal.Number.Float */
|
||||
pre code .mh { color: var(--code-string) } /* Literal.Number.Hex */
|
||||
pre code .mi { color: var(--code-string) } /* Literal.Number.Integer */
|
||||
pre code .mo { color: var(--code-string) } /* Literal.Number.Oct */
|
||||
pre code .sa { color: var(--code-builtin) } /* Literal.String.Affix */
|
||||
pre code .sb { color: var(--code-string) } /* Literal.String.Backtick */
|
||||
pre code .sc { color: var(--code-string) } /* Literal.String.Char */
|
||||
pre code .dl { color: var(--code-builtin) } /* Literal.String.Delimiter */
|
||||
pre code .sd { color: var(--code-string) } /* Literal.String.Doc */
|
||||
pre code .s2 { color: var(--code-string) } /* Literal.String.Double */
|
||||
pre code .se { color: var(--code-builtin) } /* Literal.String.Escape */
|
||||
pre code .sh { color: var(--code-builtin) } /* Literal.String.Heredoc */
|
||||
pre code .si { color: var(--code-builtin) } /* Literal.String.Interpol */
|
||||
pre code .sx { color: var(--code-string) } /* Literal.String.Other */
|
||||
pre code .sr { color: var(--code-builtin) } /* Literal.String.Regex */
|
||||
pre code .s1 { color: var(--code-string) } /* Literal.String.Single */
|
||||
pre code .ss { color: var(--code-string) } /* Literal.String.Symbol */
|
||||
pre code .bp { color: var(--code-text) } /* Name.Builtin.Pseudo */
|
||||
pre code .fm { color: var(--code-function); font-weight: bold } /* Name.Function.Magic */
|
||||
pre code .vc { color: var(--code-builtin) } /* Name.Variable.Class */
|
||||
pre code .vg { color: var(--code-builtin) } /* Name.Variable.Global */
|
||||
pre code .vi { color: var(--code-builtin) } /* Name.Variable.Instance */
|
||||
pre code .vm { color: var(--code-builtin) } /* Name.Variable.Magic */
|
||||
pre code .il { color: var(--code-string) } /* Literal.Number.Integer.Long */
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/* Corporate theme: bright and bold professional. Saturated royal-blue
|
||||
gradients on white, geometric Montserrat display type over Inter body,
|
||||
and a genuinely large brand with a soft blue overlap shadow. Automatic
|
||||
dark mode keeps the same saturated blue identity on deep navy; the
|
||||
banner artwork (inlined by the backend) is recolored from here via the
|
||||
cb-* classes, so one SVG serves both modes. */
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
--bg: #ffffff;
|
||||
--surface: #eef3fd;
|
||||
--text: #12203f;
|
||||
--muted: #4d5f83;
|
||||
--accent: #0a5cff;
|
||||
--accent2: #0933a0;
|
||||
--line: #12203f14;
|
||||
--font-body: var(--font-inter);
|
||||
--font-heading: var(--font-montserrat);
|
||||
}
|
||||
|
||||
/* Banner artwork colors, light mode */
|
||||
.cb-bg0 {
|
||||
stop-color: #ffffff;
|
||||
}
|
||||
|
||||
.cb-bg1 {
|
||||
stop-color: #e6eefe;
|
||||
}
|
||||
|
||||
.cb-r0 {
|
||||
stop-color: var(--accent);
|
||||
}
|
||||
|
||||
.cb-r1 {
|
||||
stop-color: #00b3ff;
|
||||
}
|
||||
|
||||
.cb-g0,
|
||||
.cb-g1 {
|
||||
stop-color: var(--accent);
|
||||
}
|
||||
|
||||
.cb-dot {
|
||||
fill: var(--accent);
|
||||
}
|
||||
|
||||
.cb-orbit {
|
||||
stroke: var(--accent);
|
||||
}
|
||||
|
||||
.cb-spark {
|
||||
fill: var(--accent);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #0b1428;
|
||||
--surface: #142446;
|
||||
--text: #e9eefe;
|
||||
--muted: #93a7d0;
|
||||
--accent: #4d8dff;
|
||||
--accent2: #8ab6ff;
|
||||
--line: #ffffff17;
|
||||
/* Code wells stay navy in dark mode (light mode uses --surface). */
|
||||
--code-bg: #0d1b3e;
|
||||
}
|
||||
|
||||
/* Banner artwork colors, dark mode */
|
||||
.cb-bg0 {
|
||||
stop-color: #0d1830;
|
||||
}
|
||||
|
||||
.cb-bg1 {
|
||||
stop-color: #0a1122;
|
||||
}
|
||||
|
||||
.cb-r0 {
|
||||
stop-color: #2f7bff;
|
||||
}
|
||||
|
||||
.cb-r1 {
|
||||
stop-color: #00d0ff;
|
||||
}
|
||||
|
||||
.cb-g0,
|
||||
.cb-g1 {
|
||||
stop-color: #2f7bff;
|
||||
}
|
||||
|
||||
.cb-dot {
|
||||
fill: #4d8dff;
|
||||
}
|
||||
|
||||
.cb-orbit {
|
||||
stroke: #4d8dff;
|
||||
}
|
||||
|
||||
.cb-spark {
|
||||
fill: #6ea8ff;
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Genuinely large solid brand with a soft blue shadow overlapping the
|
||||
artwork — conservative, but unmissable. */
|
||||
#brand {
|
||||
font-size: clamp(4rem, 11vw, 8.5rem);
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.04em;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
color: var(--accent2);
|
||||
filter: drop-shadow(0 0.4rem 1.4rem rgb(10 92 255 / 0.3));
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#brand {
|
||||
color: #eaf1ff;
|
||||
filter: drop-shadow(0 0.4rem 1.4rem rgb(0 0 0 / 0.6));
|
||||
}
|
||||
}
|
||||
|
||||
#banner {
|
||||
min-height: 15rem;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#nav {
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
#nav .current {
|
||||
color: #fff;
|
||||
background: linear-gradient(120deg, var(--accent), var(--accent2));
|
||||
border-radius: 999px;
|
||||
padding: 0.15rem 0.85rem;
|
||||
margin: -0.15rem -0.85rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#nav {
|
||||
text-shadow: 0 0 0.15em #000;
|
||||
}
|
||||
}
|
||||
|
||||
/* Heading hierarchy: h1 navy with a short gradient bar, h2 in accent
|
||||
blue, h3 as an uppercase kicker. */
|
||||
article h1 {
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--accent2);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
article h1 {
|
||||
color: var(--text);
|
||||
}
|
||||
}
|
||||
|
||||
article h1::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 3.6rem;
|
||||
height: 0.32rem;
|
||||
margin-top: 0.5rem;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(90deg, var(--accent), #00b3ff);
|
||||
}
|
||||
|
||||
article h2 {
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.015em;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
article h3 {
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left-color: var(--accent);
|
||||
background: color-mix(in oklab, var(--accent) 6%, transparent);
|
||||
padding: 0.4rem 0.9rem;
|
||||
/* Keep the quoted text on the paragraph edge: the tinted box extends
|
||||
past it by its own border/padding, like code blocks. */
|
||||
margin: 0 -0.9rem 1rem calc(-0.25rem - 0.9rem);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Code follows the color scheme: the light set on a faint-blue surface in
|
||||
light mode, a navy well in dark mode (--code-bg above). The accent side
|
||||
bar stays in both. */
|
||||
pre {
|
||||
border: 1px solid transparent;
|
||||
border-left: 0.25rem solid var(--accent);
|
||||
/* Text on the paragraph edge: the box extends by padding + border. */
|
||||
margin-left: calc(-0.8rem - 0.25rem);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::view-transition {
|
||||
background: var(--bg);
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/* Nitro theme: racing/HUD style — saturated orange + cyan, console/HUD-style
|
||||
monospace display type, an orange racing-tab nav with a bezier clip-path,
|
||||
and a dark bezier banner. Follows prefers-color-scheme: the light scheme
|
||||
is a warm light-grey page, the dark scheme a deep violet page; the banner
|
||||
and other dark/bright elements carry over unchanged.
|
||||
|
||||
Color model: the base stylesheet's variables carry the page; nitro adds
|
||||
a small set of theme-specific colors below, and everything else in the
|
||||
file references variables only, so recoloring happens here. */
|
||||
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
|
||||
/* Page palette (base variables) */
|
||||
--bg: #eee;
|
||||
/* warm light grey page background */
|
||||
--surface: #fff;
|
||||
--text: #1a1a1a;
|
||||
--muted: #655f52;
|
||||
/* warm grey secondary text */
|
||||
--line: #1a1a1a1f;
|
||||
/* hairlines: tables, column rules */
|
||||
--accent: #ff6a00;
|
||||
/* racing orange: decorations, nav tab, markers */
|
||||
--accent2: #008ba3;
|
||||
/* deep cyan: h3, blockquote bar, nested markers */
|
||||
|
||||
/* Nitro-specific colors */
|
||||
--orange-deep: #d95a00;
|
||||
/* orange dark enough for body text (h2, links) */
|
||||
--ink: #000;
|
||||
/* pure black details: tab text, brand shadow */
|
||||
--neon: #59ecff;
|
||||
/* glowing cyan: current page on the orange tab */
|
||||
--tab-current: #eee;
|
||||
--tab-hover: #fff;
|
||||
/* glowing white: nav hover (kept bright: the
|
||||
nav glow is text-shadow, which fades dimmer) */
|
||||
--tab-muted: #0000008c;
|
||||
/* secondary labels on the orange tab */
|
||||
|
||||
--font-body: var(--font-montserrat);
|
||||
--font-heading: var(--font-literata);
|
||||
}
|
||||
|
||||
/* Dark scheme: same identity, but the page goes deep violet (never muddy
|
||||
near-black); the cyan and orange accents brighten to keep their punch. */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #17141f;
|
||||
--surface: #231f2e;
|
||||
--text: #f0ede8;
|
||||
--muted: #a89f8f;
|
||||
--line: #ffffff22;
|
||||
--accent2: #2fc3dd;
|
||||
/* brighter cyan, readable on dark */
|
||||
--orange-deep: #ff8a3d;
|
||||
/* brighter orange, readable on dark */
|
||||
--code-bg: #12101b;
|
||||
/* code wells join the violet family */
|
||||
}
|
||||
|
||||
/* Banner dark tones tinted to the same violet family as the page. */
|
||||
.nb-base {
|
||||
fill: #100d18;
|
||||
}
|
||||
|
||||
.nb-s1a {
|
||||
stop-color: #292536;
|
||||
}
|
||||
|
||||
.nb-s1b {
|
||||
stop-color: #100d18;
|
||||
}
|
||||
|
||||
.nb-s2a {
|
||||
stop-color: #1e1a2b;
|
||||
}
|
||||
|
||||
.nb-s2b {
|
||||
stop-color: #090811;
|
||||
}
|
||||
|
||||
.nb-c0 {
|
||||
stop-color: #322d44;
|
||||
}
|
||||
|
||||
.nb-c1 {
|
||||
stop-color: #171422;
|
||||
}
|
||||
|
||||
.nb-c2 {
|
||||
stop-color: #100d18;
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--accent);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Oversized outlined brand, spilling off the banner edge: orange stroke,
|
||||
solid black fill. */
|
||||
#brand {
|
||||
font-size: 10rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
color: var(--orange-deep);
|
||||
text-shadow: 0 0 0.1em black;
|
||||
}
|
||||
|
||||
/* Bezier-swept banner with wide orange stripes (inlined SVG), separated
|
||||
from the page by a straight orange blade. */
|
||||
#banner {
|
||||
height: 13rem;
|
||||
border-bottom: 4px solid var(--accent);
|
||||
}
|
||||
|
||||
/* Banner artwork dark tones: neutral greys in light mode (retinted to the
|
||||
page's violet family by the dark-scheme block above). */
|
||||
.nb-base {
|
||||
fill: #0b0b0d;
|
||||
}
|
||||
|
||||
.nb-s1a {
|
||||
stop-color: #242428;
|
||||
}
|
||||
|
||||
.nb-s1b {
|
||||
stop-color: #0b0b0d;
|
||||
}
|
||||
|
||||
.nb-s2a {
|
||||
stop-color: #19191d;
|
||||
}
|
||||
|
||||
.nb-s2b {
|
||||
stop-color: #060607;
|
||||
}
|
||||
|
||||
.nb-c0 {
|
||||
stop-color: #2a2a2f;
|
||||
}
|
||||
|
||||
.nb-c1 {
|
||||
stop-color: #131315;
|
||||
}
|
||||
|
||||
.nb-c2 {
|
||||
stop-color: #0b0b0d;
|
||||
}
|
||||
|
||||
#nav {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 0.95em;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
/* Racing tab: sitting at the bottom of the banner, with a bezier clip —
|
||||
slanted sides that join top and bottom horizontally, and a shallow
|
||||
wide notch at the top middle. Generous side padding so the em-based
|
||||
slants fit. The border-radius is the fallback for browsers without
|
||||
shape(). */
|
||||
background: var(--accent);
|
||||
width: fit-content;
|
||||
padding: 0.3em 2.5em 0;
|
||||
border-radius: 1.2em 1.2em 0 0;
|
||||
clip-path: shape(
|
||||
/* left side */
|
||||
from 0% 100%,
|
||||
curve to 2.5em 0 with 2em 100% / 1em 0,
|
||||
/* notch: ≈ golden-ratio width of the available top edge */
|
||||
hline to calc(19.1% + 1.7em),
|
||||
line to calc(19.1% + 2.1em) 0.2em,
|
||||
hline to calc(80.9% - 2.1em),
|
||||
line to calc(80.9% - 1.7em) 0,
|
||||
/* right side, bottom */
|
||||
hline to calc(100% - 2.5em),
|
||||
curve to 100% 100% with calc(100% - 1em) 0 / calc(100% - 2em) 100%,
|
||||
close);
|
||||
}
|
||||
|
||||
#nav a {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Current page and hover glow on the orange tab; both stay bright colors
|
||||
because the glow is text-shadow. */
|
||||
#nav .current {
|
||||
color: var(--tab-current);
|
||||
text-shadow: 0 0 0.1em;
|
||||
}
|
||||
|
||||
#nav a:hover {
|
||||
color: var(--tab-hover);
|
||||
text-shadow: 0 0 0.1em;
|
||||
}
|
||||
|
||||
#nav span {
|
||||
color: var(--tab-muted);
|
||||
}
|
||||
|
||||
/* Console-style headings: uppercase monospace. h1 in the page text color
|
||||
with a hazard-stripe underline, h2 deep orange, h3 cyan. */
|
||||
article h1,
|
||||
article h2,
|
||||
article h3 {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
article h1 {
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
padding-bottom: 0.5rem;
|
||||
/* The hazard-stripe underline breaks out of the page box: the negative
|
||||
right margin extends the h1's box (and thus its background) all the
|
||||
way to the viewport's right edge. */
|
||||
margin-right: calc((100% - 100vw) / 2);
|
||||
background:
|
||||
linear-gradient(-55deg,
|
||||
transparent 0 0.2rem,
|
||||
var(--accent) 0.2rem 0.9rem,
|
||||
transparent 0.9rem 1.4rem) -0.2rem bottom / 1.4rem 4px repeat-x;
|
||||
}
|
||||
|
||||
article h2 {
|
||||
color: var(--orange-deep);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
article h3 {
|
||||
color: var(--accent2);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
article a:hover {
|
||||
color: var(--accent2);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Chevron markers instead of the base emoji diamonds. */
|
||||
article ul li::before {
|
||||
content: "»";
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
article ul ul li::before {
|
||||
content: "›";
|
||||
color: var(--accent2);
|
||||
}
|
||||
|
||||
article ul ul ul li::before {
|
||||
content: "»";
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left-color: var(--accent2);
|
||||
background: color-mix(in oklab, var(--accent2) 6%, transparent);
|
||||
padding: 0.25rem 0.75rem;
|
||||
/* Keep the quoted text on the paragraph edge: the tinted box extends
|
||||
past it by its own border/padding, like code blocks. */
|
||||
margin: 0 -0.75rem 1rem -1rem;
|
||||
}
|
||||
|
||||
/* Code follows the color scheme; the dark-scheme well joins the violet
|
||||
family (--code-bg above). The orange side bar stays in both. */
|
||||
pre {
|
||||
border-left: 0.25rem solid var(--accent);
|
||||
/* Text on the paragraph edge: the box extends by padding + border. */
|
||||
margin-left: calc(-0.8rem - 0.25rem);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::view-transition {
|
||||
background: var(--bg);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 360">
|
||||
<rect width="1600" height="360" fill="#1b1830"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 122 B |
@@ -1,38 +1,74 @@
|
||||
/* Purple theme: overrides the conservative base variables and adds the
|
||||
more elaborate visual styling (dark palette, gradient brand, etc.). */
|
||||
/* Purple theme: bold dusk palette (sky/violet/pink on deep indigo),
|
||||
editorial typography (Fraunces display over Literata body), the sunrise
|
||||
banner artwork (inlined by the backend into #page-banner) and a
|
||||
playfully oversized tilted brand. */
|
||||
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--bg: #12101c;
|
||||
--surface: #1b1830;
|
||||
--text: #e8e6f2;
|
||||
--muted: #9a94b8;
|
||||
--accent: #00d4c8;
|
||||
--accent2: #7c5cff;
|
||||
--line: #ffffff1a;
|
||||
--bg: #131022;
|
||||
--surface: #1e1a36;
|
||||
--text: #ece9f7;
|
||||
--muted: #a79ecb;
|
||||
/* Icy sky-cyan: the cool counterweight to violet/pink (green-leaning
|
||||
teal clashed with them), and complementary to the sunrise's sun. */
|
||||
--accent: #5ad1f5;
|
||||
--accent2: #9b6bff;
|
||||
--accent3: #ff6b9d;
|
||||
--line: #ffffff1c;
|
||||
--font-body: var(--font-literata);
|
||||
--font-heading: var(--font-fraunces);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--accent2);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Oversized tilted brand in the sky→violet gradient. */
|
||||
#brand {
|
||||
font-size: clamp(3.2rem, 9vw, 7.5rem);
|
||||
line-height: 1;
|
||||
margin-bottom: -0.28em;
|
||||
transform: rotate(-2deg);
|
||||
transform-origin: left bottom;
|
||||
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);
|
||||
filter: drop-shadow(0 0.15rem 0.6rem #9b6bff55);
|
||||
}
|
||||
|
||||
/* Sunrise parallax: the sun and its glow rise faster than the artwork
|
||||
drift (pagerite.js sets --pry on <html>), so scrolling the page makes
|
||||
the sun come up. */
|
||||
#page-banner .sun,
|
||||
#page-banner .sun-glow {
|
||||
transform-box: fill-box;
|
||||
transform: translateY(calc(var(--pry, 0px) * -2));
|
||||
}
|
||||
|
||||
/* The banner artwork fades into the page background at its bottom edge
|
||||
(baked into the SVG, so a user banner replaces it cleanly). */
|
||||
.banner-fade {
|
||||
stop-color: var(--bg);
|
||||
}
|
||||
|
||||
#banner {
|
||||
background: url("./banner.svg") center 40% / cover;
|
||||
min-height: 13rem;
|
||||
}
|
||||
|
||||
/* Colored headings are part of the theme's more elaborate styling; the
|
||||
base keeps headings in plain text color. */
|
||||
/* Dark artwork: keep the nav readable with a shadow. */
|
||||
#nav {
|
||||
text-shadow: 0 0 0.15em black;
|
||||
}
|
||||
|
||||
/* Colored headings, one accent per level: teal h1 with a gradient
|
||||
underline, violet h2, pink h3 in a quieter weight. */
|
||||
article h1 {
|
||||
color: var(--accent);
|
||||
padding-bottom: 0.35rem;
|
||||
background: linear-gradient(90deg, var(--accent), var(--accent2) 45%, transparent) bottom left / 100% 2px no-repeat;
|
||||
}
|
||||
|
||||
article h2 {
|
||||
@@ -40,17 +76,37 @@ article h2 {
|
||||
}
|
||||
|
||||
article h3 {
|
||||
color: color-mix(in oklab, var(--accent2) 60%, var(--muted));
|
||||
color: var(--accent3);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Dark artwork: keep the nav readable with a shadow (the base style uses
|
||||
a scrim instead — shadows only work on dark backgrounds). */
|
||||
#nav {
|
||||
text-shadow: 0 0 0.15em black;
|
||||
/* Theme-colored diamond markers instead of the base emoji (blue/orange
|
||||
clashes with this palette). */
|
||||
article ul li::before {
|
||||
content: "◆";
|
||||
color: var(--accent);
|
||||
font-size: 0.7em;
|
||||
vertical-align: 0.15em;
|
||||
}
|
||||
|
||||
article ul ul li::before {
|
||||
content: "◆";
|
||||
color: var(--accent2);
|
||||
}
|
||||
|
||||
article ul ul ul li::before {
|
||||
content: "◆";
|
||||
color: var(--accent3);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left-color: var(--accent2);
|
||||
}
|
||||
|
||||
/* Code panels sit slightly lighter than the page; the token colors come
|
||||
from the base dark set (this theme declares color-scheme: dark). */
|
||||
pre {
|
||||
background: #ffffff09;
|
||||
--code-bg: var(--surface);
|
||||
}
|
||||
|
||||
::view-transition {
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
const REGIONS = ["page-banner", "nav", "sidebar", "main"];
|
||||
// Regions every page has. #sidebar is NOT among them: it is omitted
|
||||
// entirely when the section has no sub-navigation, and handled below.
|
||||
const REGIONS = ["page-banner", "nav", "main"];
|
||||
const reduceMotion = matchMedia("(prefers-reduced-motion: reduce)");
|
||||
let editorModule = null;
|
||||
|
||||
@@ -235,6 +237,27 @@
|
||||
currentPath = location.pathname;
|
||||
});
|
||||
|
||||
// --- Banner parallax ----------------------------------------------------
|
||||
// The banner artwork stays windowed in place while its contents drift
|
||||
// against the scroll. The --pry scroll parameter is also available to
|
||||
// themes for their own effects (e.g. the purple sun rising faster than
|
||||
// the drift). Event-driven only: perfectly still when the page is idle.
|
||||
if (!reduceMotion.matches) {
|
||||
let ticking = false;
|
||||
const drift = () => {
|
||||
ticking = false;
|
||||
document.documentElement.style.setProperty(
|
||||
"--pry", `${Math.min(scrollY * 0.1, 30)}px`,
|
||||
);
|
||||
};
|
||||
addEventListener("scroll", () => {
|
||||
if (!ticking) {
|
||||
ticking = true;
|
||||
requestAnimationFrame(drift);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// --- Fetch navigation ------------------------------------------------
|
||||
async function load(url, push = true, back = false) {
|
||||
// Navigating with the editor open closes it; unsaved edits are lost
|
||||
@@ -264,6 +287,18 @@
|
||||
const el = document.getElementById(id);
|
||||
el.replaceWith(document.importNode(doc.getElementById(id), true));
|
||||
}
|
||||
// #sidebar is omitted entirely when the section has no
|
||||
// sub-navigation, so it may be absent on either side of the swap:
|
||||
// replace, insert (as #main's preceding sibling), or remove.
|
||||
const oldSidebar = document.getElementById("sidebar");
|
||||
const newSidebar = doc.getElementById("sidebar");
|
||||
if (oldSidebar && newSidebar) {
|
||||
oldSidebar.replaceWith(document.importNode(newSidebar, true));
|
||||
} else if (newSidebar) {
|
||||
document.getElementById("main").before(document.importNode(newSidebar, true));
|
||||
} else if (oldSidebar) {
|
||||
oldSidebar.remove();
|
||||
}
|
||||
// Site-wide custom CSS lives in <head id="pagerite-user"> and must be
|
||||
// kept in sync across fetch-navigations. It is kept last in <head>:
|
||||
// in dev Vite injects the base stylesheet after the server-rendered
|
||||
|
||||
+11
-1
@@ -1,4 +1,5 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
import { readdirSync } from 'node:fs'
|
||||
import fastapiVue from './vite-plugin-fastapi.js'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
@@ -7,6 +8,15 @@ import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
const backendUrl = process.env.PAGERITE_BACKEND_URL || 'http://localhost:3200'
|
||||
|
||||
// Every theme directory ships its theme.css as a separate build entry, so
|
||||
// the backend can link base and theme stylesheets independently.
|
||||
const themesDir = fileURLToPath(new URL('./src/assets/themes', import.meta.url))
|
||||
const themeInputs = Object.fromEntries(
|
||||
readdirSync(themesDir, { withFileTypes: true })
|
||||
.filter((d) => d.isDirectory())
|
||||
.map((d) => [`theme_${d.name}`, `${themesDir}/${d.name}/theme.css`]),
|
||||
)
|
||||
|
||||
// Proxy content pages (/slug, /path/to/slug) to the FastAPI backend in dev.
|
||||
// Excludes Vite internals (/@..., /src, /node_modules, /__...) and the
|
||||
// backend's /_ prefix. /_api and /_f are handled by the fastapi-vue plugin.
|
||||
@@ -40,7 +50,7 @@ export default defineConfig({
|
||||
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)),
|
||||
...themeInputs,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user