Add the summer theme: illustrated meadow banner, cohesive palette

A light, playful theme whose every color is sampled from its banner.svg
meadow scene (sky/grass/sun/flower pink): fixed daylit landscape page
background with a sun-glow echo, tilted gradient brand, flower bullets,
a dense grass strip (grass.svg, served via /_themes) under h1s, rounded
shadowed tables with a sunny hover sheen, and a layered-parallax banner
(sun rises, clouds drift, nearer hills move less) with idle animations
wrapped in prefers-reduced-motion.

The /_themes route now serves any theme asset (previously only
theme.css/banner.css) so CSS can reference sibling files like grass.svg.
This commit is contained in:
2026-08-18 21:54:44 +00:00
parent 2140f06501
commit e9297cac61
6 changed files with 869 additions and 8 deletions
+9 -2
View File
@@ -70,7 +70,8 @@ not for the public pages. See `docs/design-principles.md` for the design.
in the site editor via `/_api/settings`; empty = no header link and
no `<title>` suffix. `Data.theme` is the active theme name (empty =
none/base only); themes are folders in `pagerite/themes/{name}`
containing `theme.css` and/or `banner.css` (+ `banner.svg` artwork),
containing `theme.css` and/or `banner.css` (+ `banner.svg` artwork and
any extra assets the CSS references, like summer's `grass.svg`),
served by the backend at `/_themes/{name}/...` — read from disk per
request (etag by mtime), never built, so on-disk edits show on the
next page load even in prod. The theme selector and banner-design
@@ -154,7 +155,13 @@ not for the public pages. See `docs/design-principles.md` for the design.
brand; `nitro` = racing/HUD style following `prefers-color-scheme`
(warm light-grey page, deep violet in dark), Montserrat/Literata,
black as an accent only, a straight orange blade under the banner, and
an orange racing-tab nav clipped with a bezier `shape()`) and the
an orange racing-tab nav clipped with a bezier `shape()`; `summer` =
light playful meadow, one palette sampled from its illustrated
`banner.svg` (sky/grass/sun/flower pink), Fraunces/Literata, a tilted
gradient brand, flower bullets, and a layered-parallax banner (sun
rises, clouds drift, nearer hills move less) with idle animations
(swaying flowers, floating clouds, breathing sun glow) wrapped in
`prefers-reduced-motion: no-preference`) and the
companion `banner.css` banner designs are served by the backend.
- Vite builds ES-module `.js` outputs; the backend renders `<script
type="module">` for them (module scripts defer by default).
+11 -6
View File
@@ -412,14 +412,16 @@ async def delete_file(name: str) -> None:
@app.get("/_themes/{name}/{filename}")
async def theme_file(name: str, filename: str, request: Request) -> Response:
"""Serve a theme/banner-design stylesheet from pagerite/themes/{name}/.
"""Serve a theme/banner-design file from pagerite/themes/{name}/.
Read from disk on every request (etag by mtime+size): theme files are
never built or content-hashed, so edits on disk show on the next page
load, in prod as well as dev.
Stylesheets plus any extra assets the CSS references (like summer's
grass.svg). Read from disk on every request (etag by mtime+size):
theme files are never built or content-hashed, so edits on disk show
on the next page load, in prod as well as dev.
"""
if (
filename not in {"theme.css", "banner.css"}
"/" in filename
or filename.startswith(".")
or "/" in name
or name.startswith(".")
):
@@ -429,12 +431,15 @@ async def theme_file(name: str, filename: str, request: Request) -> Response:
stat = path.stat()
except FileNotFoundError:
raise HTTPException(404) from None
if not path.is_file():
raise HTTPException(404)
etag = f'"{stat.st_mtime_ns:x}-{stat.st_size:x}"'
if request.headers.get("if-none-match") == etag:
return Response(status_code=304)
mime = mimetypes.guess_type(filename)[0] or "application/octet-stream"
return Response(
path.read_bytes(),
media_type="text/css",
media_type=mime,
headers={"etag": etag, "cache-control": "no-cache"},
)
+75
View File
@@ -0,0 +1,75 @@
/* Summer banner design: a bright illustrated meadow (inlined by the
backend into #page-banner) — rolling hills, leafy bushes, swaying
flowers, drifting clouds and a sun that rises as you scroll. */
#banner {
min-height: 15rem;
}
/* The artwork fades into the page background at its bottom edge. */
.summer-fade {
stop-color: var(--bg);
}
/* Layered parallax on top of the whole-artwork drift (pagerite.js sets
--pry on <html>): the sun rises fastest, the clouds lag behind, and the
nearer a hill the less it moves — a cheap depth cue while scrolling. */
#page-banner .sun,
#page-banner .sun-glow {
transform-box: fill-box;
transform: translateY(calc(var(--pry, 0px) * -1.5));
}
#page-banner .clouds {
transform: translateY(calc(var(--pry, 0px) * -0.7));
}
#page-banner .hills-far {
transform: translateY(calc(var(--pry, 0px) * -0.3));
}
#page-banner .hills-near {
transform: translateY(calc(var(--pry, 0px) * -0.15));
}
#page-banner .meadow {
transform: translateY(calc(var(--pry, 0px) * -0.05));
}
/* Idle animations: the sun breathes, clouds float sideways, flowers sway.
Each animated group is a transform-less wrapper so the CSS transform
never clobbers the placement transforms inside. */
@media (prefers-reduced-motion: no-preference) {
#page-banner .sun-glow {
animation: summer-sun 9s ease-in-out infinite alternate;
}
#page-banner .cloud-a { animation: summer-drift 56s ease-in-out infinite alternate; }
#page-banner .cloud-b { animation: summer-drift 73s ease-in-out infinite alternate-reverse; }
#page-banner .cloud-c { animation: summer-drift 64s ease-in-out infinite alternate; }
#page-banner .flower {
transform-box: fill-box;
transform-origin: 50% 100%;
animation: summer-sway 5s ease-in-out infinite alternate;
}
/* Stagger the sway so the meadow doesn't move in lockstep. */
#page-banner .flower:nth-child(3n) { animation-delay: -1.7s; }
#page-banner .flower:nth-child(3n + 1) { animation-delay: -3.1s; animation-duration: 6s; }
}
@keyframes summer-sun {
from { opacity: 0.22; }
to { opacity: 0.38; }
}
@keyframes summer-drift {
from { transform: translateX(-1.6%); }
to { transform: translateX(1.6%); }
}
@keyframes summer-sway {
from { transform: rotate(-2.6deg); }
to { transform: rotate(2.6deg); }
}
+369
View File
@@ -0,0 +1,369 @@
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1600 320"
preserveAspectRatio="xMidYMid slice">
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#8ed0ff"/>
<stop offset=".64" stop-color="#d9f1ff"/>
<stop offset="1" stop-color="#eef9ff"/>
</linearGradient>
<linearGradient id="farHill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#b8dca7"/>
<stop offset="1" stop-color="#a4cf90"/>
</linearGradient>
<linearGradient id="nearHill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#91c97c"/>
<stop offset="1" stop-color="#74b65e"/>
</linearGradient>
<linearGradient id="grass" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#69ae4b"/>
<stop offset="1" stop-color="#4f913b"/>
</linearGradient>
<!-- Soft fade into the page background (stop-color from banner.css) -->
<linearGradient id="meadowFade" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" class="summer-fade" stop-color="#e6f4cf" stop-opacity="0"/>
<stop offset="1" class="summer-fade" stop-color="#e6f4cf"/>
</linearGradient>
<filter id="soft">
<feGaussianBlur stdDeviation="16"/>
</filter>
<filter id="cloudShadow" x="-20%" y="-40%" width="140%" height="180%">
<feDropShadow dx="0" dy="3" stdDeviation="4"
flood-color="#4f8fb0" flood-opacity=".12"/>
</filter>
</defs>
<!-- Sky -->
<rect width="1600" height="320" fill="url(#sky)"/>
<!-- Sun (rises on scroll, glow pulses) -->
<circle class="sun-glow" cx="1330" cy="72" r="82"
fill="#fff0a2"
opacity=".28"
filter="url(#soft)"/>
<circle class="sun" cx="1330" cy="72" r="42"
fill="#fff0a2"
opacity=".82"/>
<!-- Clouds (drift on scroll, lazily float sideways) -->
<g class="clouds" fill="#fff" opacity=".82" filter="url(#cloudShadow)">
<g class="cloud cloud-a">
<g transform="translate(160 72)">
<ellipse cx="0" cy="12" rx="54" ry="20"/>
<circle cx="-27" cy="4" r="26"/>
<circle cx="7" cy="-6" r="34"/>
<circle cx="40" cy="7" r="25"/>
</g>
</g>
<g class="cloud cloud-b" opacity=".72">
<g transform="translate(650 52)">
<ellipse cx="0" cy="10" rx="45" ry="16"/>
<circle cx="-21" cy="4" r="21"/>
<circle cx="7" cy="-5" r="27"/>
<circle cx="32" cy="6" r="20"/>
</g>
</g>
<g class="cloud cloud-c" opacity=".58">
<g transform="translate(1070 110)">
<ellipse cx="0" cy="8" rx="38" ry="14"/>
<circle cx="-18" cy="3" r="18"/>
<circle cx="6" cy="-4" r="23"/>
<circle cx="28" cy="5" r="17"/>
</g>
</g>
</g>
<!-- Far rolling hills (slow parallax); drawn past the bottom edge so the
drift never exposes sky underneath -->
<path class="hills-far" d="
M0 211
C145 170 263 173 390 211
C516 249 616 237 730 194
C846 151 945 168 1065 208
C1195 251 1334 214 1600 171
L1600 340
L0 340
Z"
fill="url(#farHill)"/>
<!-- Near rolling hills -->
<path class="hills-near" d="
M0 248
C120 217 228 217 342 247
C454 278 566 269 674 228
C789 184 901 202 1024 243
C1143 282 1278 271 1391 229
C1473 199 1538 196 1600 207
L1600 340
L0 340
Z"
fill="url(#nearHill)"/>
<!-- Proper leafy bushes (ride with the near hills) -->
<g class="hills-near">
<g transform="translate(130 236)">
<path d="
M-58 28
C-61 7 -46 -5 -28 0
C-28 -20 -4 -31 11 -16
C22 -36 54 -27 53 -5
C73 -10 90 6 85 27
Z"
fill="#5e9c4b"/>
<path d="
M-47 27
C-48 10 -37 1 -23 5
C-20 -10 -4 -17 8 -7
C19 -21 40 -17 43 -1
C60 -4 70 8 68 26
Z"
fill="#70ad58"/>
</g>
<g transform="translate(1240 225) scale(.9)">
<path d="
M-58 28
C-61 7 -46 -5 -28 0
C-28 -20 -4 -31 11 -16
C22 -36 54 -27 53 -5
C73 -10 90 6 85 27
Z"
fill="#5e9c4b"/>
<path d="
M-47 27
C-48 10 -37 1 -23 5
C-20 -10 -4 -17 8 -7
C19 -21 40 -17 43 -1
C60 -4 70 8 68 26
Z"
fill="#70ad58"/>
</g>
<g transform="translate(460 250) scale(.58)">
<path d="
M-58 28
C-61 7 -46 -5 -28 0
C-28 -20 -4 -31 11 -16
C22 -36 54 -27 53 -5
C73 -10 90 6 85 27
Z"
fill="#5a9748"/>
</g>
<g transform="translate(940 245) scale(.55)">
<path d="
M-58 28
C-61 7 -46 -5 -28 0
C-28 -20 -4 -31 11 -16
C22 -36 54 -27 53 -5
C73 -10 90 6 85 27
Z"
fill="#5a9748"/>
</g>
</g>
<!-- Foreground meadow (near-static parallax) -->
<path class="meadow" d="
M0 270
C95 260 175 267 260 273
C347 279 431 265 521 262
C620 258 705 275 808 272
C914 269 1004 251 1115 258
C1229 265 1324 279 1424 270
C1491 264 1547 258 1600 262
L1600 340
L0 340
Z"
fill="url(#grass)"/>
<!-- Dense grass blades -->
<g class="meadow" fill="none"
stroke="#4b8937"
stroke-width="2.5"
stroke-linecap="round">
<path d="M20 276 q-2 -19 4 -31 M28 276 q8 -18 11 -29"/>
<path d="M65 272 q-5 -18 1 -28 M74 272 q8 -16 11 -25"/>
<path d="M110 271 q-2 -23 5 -36 M121 272 q9 -18 12 -30"/>
<path d="M165 273 q-6 -16 -1 -28 M175 273 q7 -18 11 -27"/>
<path d="M225 274 q-3 -24 4 -37 M236 274 q8 -17 12 -29"/>
<path d="M286 273 q-6 -19 -1 -31 M296 274 q9 -18 12 -28"/>
<path d="M345 270 q-3 -18 3 -31 M355 270 q8 -17 12 -28"/>
<path d="M405 266 q-4 -22 2 -35 M416 266 q9 -17 13 -28"/>
<path d="M472 263 q-3 -18 3 -30 M482 263 q8 -19 13 -29"/>
<path d="M535 263 q-6 -19 -1 -31 M546 264 q9 -19 13 -30"/>
<path d="M598 266 q-3 -24 4 -37 M609 267 q8 -19 12 -31"/>
<path d="M665 271 q-6 -17 -1 -29 M676 271 q9 -17 13 -28"/>
<path d="M735 273 q-4 -22 3 -34 M746 273 q8 -18 12 -29"/>
<path d="M805 272 q-6 -18 -1 -30 M816 271 q9 -17 13 -27"/>
<path d="M875 265 q-3 -23 4 -35 M886 265 q8 -18 12 -28"/>
<path d="M945 258 q-5 -18 1 -30 M955 258 q9 -17 12 -28"/>
<path d="M1018 256 q-3 -22 4 -34 M1029 256 q8 -18 12 -28"/>
<path d="M1090 259 q-5 -20 1 -31 M1101 259 q8 -18 12 -29"/>
<path d="M1162 264 q-3 -24 4 -36 M1173 264 q8 -18 12 -30"/>
<path d="M1234 269 q-5 -19 1 -31 M1245 269 q8 -17 12 -28"/>
<path d="M1304 273 q-3 -23 4 -35 M1315 273 q8 -18 12 -29"/>
<path d="M1374 272 q-5 -18 1 -29 M1385 272 q8 -18 12 -28"/>
<path d="M1445 268 q-3 -22 4 -34 M1456 268 q8 -17 12 -28"/>
<path d="M1515 264 q-5 -20 1 -31 M1526 264 q8 -18 12 -29"/>
<path d="M1570 263 q-3 -21 4 -33 M1581 263 q8 -17 12 -28"/>
</g>
<!-- Extra foreground tufts -->
<g class="meadow" fill="#579a40">
<path d="M80 320 l15 -45 3 45 14 -57 0 57 20 -38 -7 38z"/>
<path d="M365 320 l10 -37 5 37 15 -49 -2 49 20 -34 -7 34z"/>
<path d="M710 320 l13 -42 3 42 15 -54 -1 54 20 -38 -8 38z"/>
<path d="M1040 320 l10 -35 5 35 14 -49 0 49 18 -32 -6 32z"/>
<path d="M1360 320 l13 -43 3 43 16 -55 -2 55 20 -37 -7 37z"/>
</g>
<!-- Flowers (sway gently; each wrapped in a classed group so the CSS
transform doesn't clobber the placement transform) -->
<g class="meadow">
<g class="flower">
<g transform="translate(190 270)">
<path d="M0 8 Q-2 25 -4 42" stroke="#42833a" stroke-width="2" fill="none"/>
<g fill="#ff8aa7">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ffe071"/>
</g>
</g>
<g class="flower">
<g transform="translate(310 280) scale(.8)">
<path d="M0 8 Q2 23 1 37" stroke="#42833a" stroke-width="2" fill="none"/>
<g fill="#fff0a0">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ef7191"/>
</g>
</g>
<g class="flower">
<g transform="translate(540 265)">
<path d="M0 8 Q-2 25 -2 42" stroke="#42833a" stroke-width="2" fill="none"/>
<g fill="#ff8aa7">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ffe071"/>
</g>
</g>
<g class="flower">
<g transform="translate(670 279) scale(.7)">
<g fill="#fff0a0">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ef7191"/>
</g>
</g>
<g class="flower">
<g transform="translate(845 272)">
<path d="M0 8 Q2 25 0 42" stroke="#42833a" stroke-width="2" fill="none"/>
<g fill="#ff8aa7">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ffe071"/>
</g>
</g>
<g class="flower">
<g transform="translate(1010 265) scale(.85)">
<path d="M0 8 Q-2 24 -1 38" stroke="#42833a" stroke-width="2" fill="none"/>
<g fill="#fff0a0">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ef7191"/>
</g>
</g>
<g class="flower">
<g transform="translate(1190 275)">
<g fill="#ff8aa7">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ffe071"/>
</g>
</g>
<g class="flower">
<g transform="translate(1410 269)">
<path d="M0 8 Q2 26 0 42" stroke="#42833a" stroke-width="2" fill="none"/>
<g fill="#fff0a0">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ef7191"/>
</g>
</g>
<g class="flower">
<g transform="translate(1510 282) scale(.72)">
<g fill="#ff8aa7">
<circle cx="-5" cy="0" r="5"/>
<circle cx="5" cy="0" r="5"/>
<circle cx="0" cy="-5" r="5"/>
<circle cx="0" cy="5" r="5"/>
</g>
<circle r="3" fill="#ffe071"/>
</g>
</g>
</g>
<!-- Small scattered meadow dots -->
<g class="meadow" fill="#ffe071" opacity=".9">
<circle cx="250" cy="287" r="2.5"/>
<circle cx="445" cy="281" r="2.5"/>
<circle cx="615" cy="289" r="2.5"/>
<circle cx="760" cy="283" r="2.5"/>
<circle cx="930" cy="279" r="2.5"/>
<circle cx="1125" cy="287" r="2.5"/>
<circle cx="1290" cy="281" r="2.5"/>
</g>
<g class="meadow" fill="#ff8aa7" opacity=".8">
<circle cx="120" cy="291" r="2.5"/>
<circle cx="395" cy="291" r="2.5"/>
<circle cx="730" cy="294" r="2.5"/>
<circle cx="1080" cy="294" r="2.5"/>
<circle cx="1460" cy="291" r="2.5"/>
</g>
<!-- Soft fade into the page background -->
<rect y="252" width="1600" height="68" fill="url(#meadowFade)"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+143
View File
@@ -0,0 +1,143 @@
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="9" viewBox="0 0 300 9">
<g fill="none" stroke-linecap="round">
<path d="M1.0 9 q2.0 -3.0 1.5 -5.5" stroke="#579a40" stroke-width="1.0"/>
<path d="M3.5 9 q1.7 -4.1 3.0 -7.5" stroke="#579a40" stroke-width="1.6"/>
<path d="M6.3 9 q-2.0 -3.8 -1.8 -6.9" stroke="#63ad4d" stroke-width="0.8"/>
<path d="M8.4 9 q-1.7 -2.3 -3.1 -4.2" stroke="#74b65e" stroke-width="1.2"/>
<path d="M11.0 9 q0.6 -3.5 1.8 -6.3" stroke="#4f913b" stroke-width="1.3"/>
<path d="M12.7 9 q-0.4 -2.6 -1.9 -4.8" stroke="#63ad4d" stroke-width="0.8"/>
<path d="M14.3 9 q-2.3 -2.9 2.5 -5.3" stroke="#4f913b" stroke-width="1.4"/>
<path d="M16.9 9 q1.8 -4.5 -2.9 -8.1" stroke="#74b65e" stroke-width="1.8"/>
<path d="M19.5 9 q0.5 -2.6 2.9 -4.7" stroke="#63ad4d" stroke-width="1.4"/>
<path d="M22.5 9 q2.3 -2.1 -1.7 -3.9" stroke="#579a40" stroke-width="1.7"/>
<path d="M25.2 9 q-2.1 -4.4 -3.8 -7.9" stroke="#42833a" stroke-width="1.0"/>
<path d="M27.7 9 q-1.9 -4.5 -3.8 -8.2" stroke="#74b65e" stroke-width="1.4"/>
<path d="M29.4 9 q-0.3 -3.7 -3.1 -6.7" stroke="#579a40" stroke-width="1.4"/>
<path d="M31.0 9 q-0.9 -3.5 0.1 -6.3" stroke="#579a40" stroke-width="0.9"/>
<path d="M32.6 9 q-0.9 -4.3 1.1 -7.8" stroke="#4f913b" stroke-width="1.4"/>
<path d="M35.7 9 q-1.4 -2.4 2.3 -4.3" stroke="#63ad4d" stroke-width="1.5"/>
<path d="M36.9 9 q-0.4 -3.2 0.0 -5.8" stroke="#63ad4d" stroke-width="1.6"/>
<path d="M38.5 9 q1.1 -4.6 0.6 -8.4" stroke="#63ad4d" stroke-width="1.2"/>
<path d="M41.3 9 q2.3 -2.9 4.0 -5.4" stroke="#63ad4d" stroke-width="1.8"/>
<path d="M43.3 9 q1.6 -2.6 4.5 -4.7" stroke="#4f913b" stroke-width="1.0"/>
<path d="M45.4 9 q-0.3 -2.6 -2.1 -4.8" stroke="#63ad4d" stroke-width="0.8"/>
<path d="M47.0 9 q0.0 -4.3 -3.8 -7.8" stroke="#42833a" stroke-width="1.6"/>
<path d="M49.6 9 q1.6 -3.1 0.3 -5.7" stroke="#4f913b" stroke-width="1.4"/>
<path d="M51.6 9 q-1.2 -2.7 -0.2 -4.9" stroke="#74b65e" stroke-width="1.2"/>
<path d="M53.7 9 q-1.0 -2.4 3.6 -4.3" stroke="#579a40" stroke-width="1.6"/>
<path d="M55.7 9 q1.4 -2.1 3.5 -3.9" stroke="#579a40" stroke-width="1.2"/>
<path d="M58.4 9 q-0.6 -2.6 0.7 -4.8" stroke="#579a40" stroke-width="1.2"/>
<path d="M60.9 9 q-2.3 -2.4 -3.7 -4.4" stroke="#579a40" stroke-width="1.0"/>
<path d="M62.7 9 q1.0 -2.3 -1.3 -4.1" stroke="#579a40" stroke-width="0.8"/>
<path d="M64.9 9 q-0.3 -4.0 -3.5 -7.2" stroke="#42833a" stroke-width="1.3"/>
<path d="M67.6 9 q1.7 -4.2 4.2 -7.6" stroke="#579a40" stroke-width="0.9"/>
<path d="M70.5 9 q-0.9 -2.3 1.3 -4.1" stroke="#42833a" stroke-width="1.7"/>
<path d="M72.8 9 q-0.2 -2.9 -2.9 -5.2" stroke="#42833a" stroke-width="1.0"/>
<path d="M74.3 9 q-0.5 -2.9 4.5 -5.2" stroke="#42833a" stroke-width="1.4"/>
<path d="M76.9 9 q-1.6 -2.3 0.5 -4.1" stroke="#42833a" stroke-width="1.7"/>
<path d="M78.4 9 q1.1 -5.1 -0.4 -9.2" stroke="#579a40" stroke-width="1.0"/>
<path d="M80.6 9 q-1.2 -3.8 2.1 -6.9" stroke="#42833a" stroke-width="1.8"/>
<path d="M81.9 9 q-1.7 -2.7 -2.8 -5.0" stroke="#42833a" stroke-width="1.0"/>
<path d="M83.8 9 q1.8 -3.1 4.5 -5.7" stroke="#74b65e" stroke-width="0.8"/>
<path d="M85.8 9 q-0.7 -4.0 -0.4 -7.3" stroke="#42833a" stroke-width="0.8"/>
<path d="M88.0 9 q-1.6 -5.0 -0.1 -9.2" stroke="#63ad4d" stroke-width="0.8"/>
<path d="M90.9 9 q1.5 -4.4 2.2 -7.9" stroke="#74b65e" stroke-width="1.5"/>
<path d="M92.3 9 q1.5 -3.3 1.5 -6.0" stroke="#579a40" stroke-width="1.2"/>
<path d="M94.7 9 q-0.4 -3.4 0.2 -6.2" stroke="#63ad4d" stroke-width="1.0"/>
<path d="M96.4 9 q-2.1 -4.8 -1.6 -8.7" stroke="#4f913b" stroke-width="1.7"/>
<path d="M99.3 9 q-0.1 -4.9 0.3 -8.9" stroke="#4f913b" stroke-width="1.7"/>
<path d="M101.1 9 q1.0 -4.6 1.4 -8.4" stroke="#4f913b" stroke-width="0.8"/>
<path d="M103.6 9 q-1.6 -4.8 -0.8 -8.8" stroke="#4f913b" stroke-width="1.7"/>
<path d="M105.1 9 q-2.5 -2.1 1.7 -3.9" stroke="#74b65e" stroke-width="1.5"/>
<path d="M108.0 9 q1.2 -2.2 -3.6 -3.9" stroke="#74b65e" stroke-width="1.8"/>
<path d="M110.5 9 q1.0 -4.2 2.3 -7.6" stroke="#579a40" stroke-width="1.6"/>
<path d="M113.6 9 q0.2 -2.4 2.7 -4.4" stroke="#42833a" stroke-width="1.5"/>
<path d="M115.8 9 q-0.7 -4.6 -1.1 -8.3" stroke="#579a40" stroke-width="1.0"/>
<path d="M117.8 9 q-2.0 -3.2 3.7 -5.8" stroke="#579a40" stroke-width="1.1"/>
<path d="M119.2 9 q-1.0 -4.8 1.2 -8.7" stroke="#4f913b" stroke-width="1.6"/>
<path d="M121.9 9 q-0.2 -4.7 -3.9 -8.5" stroke="#4f913b" stroke-width="1.7"/>
<path d="M125.0 9 q0.4 -2.2 -0.7 -3.9" stroke="#42833a" stroke-width="1.6"/>
<path d="M127.3 9 q-1.6 -5.0 -0.4 -9.0" stroke="#74b65e" stroke-width="1.4"/>
<path d="M129.3 9 q-0.2 -4.1 4.2 -7.4" stroke="#42833a" stroke-width="1.1"/>
<path d="M131.7 9 q-1.7 -2.6 -0.4 -4.7" stroke="#42833a" stroke-width="1.2"/>
<path d="M133.0 9 q2.2 -4.9 -0.0 -8.9" stroke="#579a40" stroke-width="1.2"/>
<path d="M134.8 9 q-1.2 -4.4 -0.5 -8.0" stroke="#42833a" stroke-width="1.6"/>
<path d="M137.2 9 q2.3 -4.9 1.1 -8.8" stroke="#42833a" stroke-width="1.3"/>
<path d="M139.4 9 q-2.1 -4.4 2.3 -7.9" stroke="#4f913b" stroke-width="0.9"/>
<path d="M141.6 9 q1.3 -2.4 2.5 -4.4" stroke="#74b65e" stroke-width="1.4"/>
<path d="M143.3 9 q1.8 -3.5 2.7 -6.4" stroke="#74b65e" stroke-width="1.4"/>
<path d="M146.0 9 q0.1 -4.9 0.9 -8.9" stroke="#579a40" stroke-width="0.8"/>
<path d="M147.2 9 q2.3 -3.6 -2.1 -6.5" stroke="#42833a" stroke-width="1.5"/>
<path d="M149.9 9 q0.8 -3.0 4.6 -5.4" stroke="#63ad4d" stroke-width="0.9"/>
<path d="M151.7 9 q2.5 -3.7 2.8 -6.8" stroke="#74b65e" stroke-width="1.7"/>
<path d="M154.4 9 q-1.8 -2.5 2.6 -4.5" stroke="#63ad4d" stroke-width="1.8"/>
<path d="M155.9 9 q2.4 -4.9 3.1 -8.9" stroke="#63ad4d" stroke-width="1.2"/>
<path d="M158.8 9 q2.4 -3.5 2.3 -6.3" stroke="#63ad4d" stroke-width="1.2"/>
<path d="M161.5 9 q-1.5 -4.1 2.1 -7.5" stroke="#63ad4d" stroke-width="1.7"/>
<path d="M164.2 9 q2.4 -3.4 -1.4 -6.1" stroke="#63ad4d" stroke-width="1.1"/>
<path d="M166.1 9 q1.0 -2.4 2.1 -4.3" stroke="#74b65e" stroke-width="1.1"/>
<path d="M167.7 9 q-1.2 -5.2 1.7 -9.4" stroke="#579a40" stroke-width="1.0"/>
<path d="M169.8 9 q0.9 -5.0 1.2 -9.1" stroke="#74b65e" stroke-width="1.7"/>
<path d="M171.9 9 q-2.3 -5.1 0.1 -9.2" stroke="#63ad4d" stroke-width="1.4"/>
<path d="M174.8 9 q-2.3 -4.0 -2.4 -7.4" stroke="#42833a" stroke-width="1.8"/>
<path d="M176.1 9 q2.3 -3.9 3.4 -7.1" stroke="#74b65e" stroke-width="1.6"/>
<path d="M178.2 9 q2.3 -2.2 2.4 -4.1" stroke="#579a40" stroke-width="1.7"/>
<path d="M179.6 9 q-0.5 -3.2 -3.3 -5.8" stroke="#4f913b" stroke-width="1.1"/>
<path d="M181.1 9 q-2.1 -3.9 1.2 -7.0" stroke="#4f913b" stroke-width="1.4"/>
<path d="M183.7 9 q-2.5 -3.2 0.2 -5.8" stroke="#4f913b" stroke-width="1.7"/>
<path d="M186.2 9 q0.9 -4.4 0.1 -8.1" stroke="#42833a" stroke-width="1.3"/>
<path d="M188.7 9 q-1.1 -2.8 1.0 -5.1" stroke="#579a40" stroke-width="1.1"/>
<path d="M190.3 9 q-0.9 -3.0 -3.0 -5.5" stroke="#579a40" stroke-width="1.8"/>
<path d="M193.2 9 q0.2 -5.1 0.9 -9.3" stroke="#4f913b" stroke-width="0.9"/>
<path d="M195.3 9 q-1.0 -2.3 -3.2 -4.2" stroke="#4f913b" stroke-width="1.7"/>
<path d="M196.9 9 q-1.2 -2.7 1.3 -5.0" stroke="#4f913b" stroke-width="0.9"/>
<path d="M199.2 9 q-1.5 -4.5 4.5 -8.1" stroke="#63ad4d" stroke-width="1.1"/>
<path d="M201.6 9 q1.9 -3.7 -0.1 -6.7" stroke="#63ad4d" stroke-width="1.8"/>
<path d="M202.8 9 q-0.3 -2.2 0.4 -3.9" stroke="#42833a" stroke-width="1.6"/>
<path d="M205.5 9 q0.7 -4.2 3.1 -7.6" stroke="#579a40" stroke-width="0.9"/>
<path d="M207.3 9 q0.4 -4.3 -2.1 -7.8" stroke="#63ad4d" stroke-width="0.9"/>
<path d="M209.9 9 q2.0 -3.4 -0.4 -6.3" stroke="#4f913b" stroke-width="1.6"/>
<path d="M211.1 9 q-1.7 -2.1 0.8 -3.8" stroke="#4f913b" stroke-width="1.5"/>
<path d="M214.0 9 q-2.0 -2.5 4.3 -4.6" stroke="#579a40" stroke-width="0.9"/>
<path d="M215.6 9 q-2.2 -2.2 -2.1 -4.0" stroke="#74b65e" stroke-width="1.0"/>
<path d="M217.1 9 q-1.8 -3.1 -0.9 -5.6" stroke="#63ad4d" stroke-width="1.3"/>
<path d="M220.0 9 q1.4 -4.7 -1.8 -8.5" stroke="#42833a" stroke-width="1.8"/>
<path d="M223.1 9 q0.9 -3.4 -1.5 -6.2" stroke="#42833a" stroke-width="1.7"/>
<path d="M225.3 9 q2.4 -3.5 -3.8 -6.3" stroke="#4f913b" stroke-width="0.9"/>
<path d="M227.1 9 q2.2 -3.8 -3.0 -7.0" stroke="#579a40" stroke-width="1.0"/>
<path d="M229.7 9 q2.1 -4.4 4.4 -8.0" stroke="#74b65e" stroke-width="1.3"/>
<path d="M231.0 9 q-1.2 -4.8 2.8 -8.7" stroke="#63ad4d" stroke-width="1.6"/>
<path d="M232.7 9 q-0.9 -3.1 -3.3 -5.6" stroke="#63ad4d" stroke-width="1.5"/>
<path d="M234.8 9 q2.1 -4.6 0.4 -8.4" stroke="#4f913b" stroke-width="1.0"/>
<path d="M237.9 9 q-0.9 -4.6 -0.1 -8.4" stroke="#74b65e" stroke-width="1.4"/>
<path d="M239.4 9 q1.6 -4.7 -1.0 -8.5" stroke="#579a40" stroke-width="1.6"/>
<path d="M241.3 9 q1.9 -5.1 0.9 -9.4" stroke="#579a40" stroke-width="1.4"/>
<path d="M244.3 9 q-2.2 -3.6 -1.9 -6.6" stroke="#42833a" stroke-width="1.1"/>
<path d="M245.6 9 q1.7 -4.2 -1.7 -7.6" stroke="#579a40" stroke-width="1.4"/>
<path d="M247.1 9 q0.1 -3.1 2.0 -5.7" stroke="#42833a" stroke-width="1.5"/>
<path d="M249.5 9 q-2.2 -3.8 -2.2 -6.9" stroke="#42833a" stroke-width="0.9"/>
<path d="M251.5 9 q-2.1 -4.4 3.5 -8.0" stroke="#579a40" stroke-width="0.9"/>
<path d="M253.2 9 q0.8 -4.3 0.7 -7.8" stroke="#4f913b" stroke-width="1.7"/>
<path d="M255.5 9 q1.4 -2.2 -2.9 -4.0" stroke="#4f913b" stroke-width="1.4"/>
<path d="M258.1 9 q1.9 -4.6 1.0 -8.3" stroke="#63ad4d" stroke-width="1.2"/>
<path d="M260.5 9 q-1.9 -4.8 -3.7 -8.6" stroke="#579a40" stroke-width="1.4"/>
<path d="M263.5 9 q1.4 -3.9 -1.3 -7.2" stroke="#4f913b" stroke-width="1.1"/>
<path d="M265.2 9 q-0.9 -4.8 2.7 -8.7" stroke="#42833a" stroke-width="0.9"/>
<path d="M267.4 9 q2.5 -2.2 3.0 -4.1" stroke="#4f913b" stroke-width="1.5"/>
<path d="M269.4 9 q-1.8 -1.9 -3.9 -3.5" stroke="#579a40" stroke-width="1.5"/>
<path d="M272.3 9 q-1.8 -4.5 2.9 -8.2" stroke="#74b65e" stroke-width="1.1"/>
<path d="M274.0 9 q-0.6 -2.2 1.8 -3.9" stroke="#4f913b" stroke-width="1.6"/>
<path d="M276.4 9 q1.2 -2.8 -3.9 -5.1" stroke="#63ad4d" stroke-width="1.5"/>
<path d="M277.7 9 q2.4 -4.2 3.0 -7.7" stroke="#4f913b" stroke-width="1.1"/>
<path d="M279.6 9 q0.6 -5.0 -3.3 -9.2" stroke="#4f913b" stroke-width="1.4"/>
<path d="M282.5 9 q0.6 -3.5 1.2 -6.3" stroke="#4f913b" stroke-width="1.2"/>
<path d="M284.0 9 q-2.4 -3.8 -2.5 -6.8" stroke="#74b65e" stroke-width="1.5"/>
<path d="M285.6 9 q-1.6 -5.2 -2.4 -9.4" stroke="#63ad4d" stroke-width="1.5"/>
<path d="M288.4 9 q1.6 -2.4 3.1 -4.4" stroke="#63ad4d" stroke-width="1.2"/>
<path d="M290.2 9 q-1.9 -3.0 2.9 -5.4" stroke="#63ad4d" stroke-width="0.8"/>
<path d="M291.6 9 q-0.5 -2.7 -2.1 -4.9" stroke="#579a40" stroke-width="1.8"/>
<path d="M294.2 9 q0.8 -3.7 4.4 -6.7" stroke="#63ad4d" stroke-width="0.9"/>
<path d="M296.6 9 q1.2 -3.7 4.2 -6.7" stroke="#74b65e" stroke-width="0.9"/>
<path d="M298.1 9 q1.0 -3.8 3.9 -7.0" stroke="#42833a" stroke-width="0.8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

+262
View File
@@ -0,0 +1,262 @@
/* Summer theme: the banner's illustrated meadow carried through the whole
page — one palette (sky blue, grass green, sun yellow, flower pink),
soft daylight surfaces, and playful touches like a tilted brand and
flower bullets. */
:root {
color-scheme: light;
/* Every color is sampled (or text-darkened) from banner.svg. */
--bg: #e6f4cf;
/* pale meadow — the banner fades into this */
--surface: #f8fbf0;
--text: #2c4a2f;
/* deep leaf */
--muted: #5c7a5e;
--accent: #2598d6;
/* sky, darkened for text contrast */
--accent2: #4f913b;
/* grass */
--accent3: #e95f83;
/* flower pink, darkened for text contrast */
--sun: #ffe071;
--line: #4f913b29;
--link: color-mix(in oklab, var(--text) 38%, var(--accent));
--font-body: var(--font-cause);
--font-heading: var(--font-new-rocker);
--font-brand: var(--font-cause);
}
/* The page is the same landscape the banner paints: hazy sky light up top
(with an echo of the banner's sun in the top-right corner) settling into
a pale meadow below. Fixed, so the banner parallax reads against a
still backdrop. */
body {
background:
radial-gradient(ellipse 36rem 24rem at 85% -6rem,
#fff0a299 0,
#fff0a63d 45%,
transparent 70%),
radial-gradient(ellipse 42rem 26rem at 6% 2rem,
#8ed0ff4d 0,
transparent 70%),
radial-gradient(ellipse 30rem 22rem at 96% 62%,
#ff8aa726 0,
transparent 70%),
linear-gradient(180deg,
var(--bg) 0,
#d9eebd 24rem,
#c6e5a8 55rem,
#b8dd9c 100%);
background-attachment: fixed;
color: var(--text);
}
::selection {
background: var(--sun);
color: #3d5223;
}
/* The banner frame ties into the meadow beneath it. */
#banner {
border-bottom-color: #4f913b30;
box-shadow: 0 0.3rem 1rem #4f913b22;
}
/* Cheerful oversized tilted brand in a sky→grass→flower gradient. */
#brand {
font-size: clamp(3.2rem, 9vw, 7.5rem);
line-height: 1;
padding-bottom: 0.3em;
margin-bottom: -0.3em;
transform: rotate(-2deg);
transform-origin: left bottom;
background: linear-gradient(100deg,
var(--accent) 0 30%,
var(--accent2) 55%,
var(--accent3) 95%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: none;
filter:
drop-shadow(0 1px 0 #fff) drop-shadow(0 0.15rem 0.15rem #ffffff99) drop-shadow(0 0.3rem 0.4rem #4f913b33);
}
/* Navigation floats over the artwork: dark leaf text with a daylight halo. */
#nav {
color: #24452d;
text-shadow:
0 1px #ffffff,
0 0 0.2em #ffffff,
0 0 0.5em #d9f1ff;
}
#nav a {
color: #2c4a2f;
}
#nav ul ul a,
#nav span {
color: #527048;
}
#nav a:hover,
#nav .current {
color: var(--accent);
}
/* Content sits in a soft wash of sunlit meadow rather than on a white
card. */
main {
background: linear-gradient(90deg,
transparent,
#f0f8dc80 15%,
#f0f8dc8c 50%,
#f0f8dc80 85%,
transparent);
}
/* The sidebar is a piece of the same meadow: glassy green with a light
top edge and a grassy shadow. */
#sidebar {
background: linear-gradient(160deg, #f4faddd9, #d9eec5cf);
border-right: 1px solid #ffffff80;
border-bottom: 1px solid var(--line);
box-shadow: 0 0.3rem 1rem #4f913b1f;
}
#sidebar a {
color: #527048;
}
#sidebar a:hover,
#sidebar .current {
color: var(--accent);
}
/* One accent per heading level: sky h1 growing a dense low strip of grass
right against its baseline (grass.svg, ~140 varied blades per tile,
served by the backend next to this stylesheet), grass h2, flower h3 in
a quieter weight. */
article h1 {
color: var(--accent);
width: fit-content;
padding-bottom: 0.12em;
text-shadow: 0 1px #ffffffaa;
background: url("grass.svg") bottom left / auto 0.35em repeat-x;
}
article h2 {
color: var(--accent2);
text-shadow: 0 1px #ffffff88;
}
article h3 {
color: var(--accent3);
font-weight: 550;
}
article a {
color: var(--link);
}
article a:hover {
color: var(--accent);
}
/* Flower bullets, one accent per nesting level. */
article ul li::before {
content: "❀";
color: var(--accent3);
font-size: 0.8em;
vertical-align: 0.1em;
}
article ul ul li::before {
content: "✿";
color: var(--accent);
}
article ul ul ul li::before {
content: "❁";
color: var(--accent2);
}
/* Quotes get a grassy edge and a wash of sunlight. */
blockquote {
color: #4d6849;
border-left-color: var(--accent2);
background: linear-gradient(90deg, #fff0a238, transparent 70%);
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
/* Code rests on a shaded leaf panel, keeping the base light syntax
palette. */
pre {
--code-bg: #e7f1d6;
background: linear-gradient(160deg, #f0f7e2, #ddebc9);
border: 1px solid var(--line);
box-shadow:
inset 0 1px #ffffff99,
0 0.2rem 0.6rem #4f913b1a;
}
.copy {
background: linear-gradient(180deg, #f6faec, #e2efcf);
border-color: var(--line);
}
/* Tables: a rounded piece of the landscape. The base's header-gradient
hooks get sky→grass colors and the cell wash tints green; soft corners
and a grassy shadow replace the old hard grid. */
table {
--table-head-a: #fdfccd88;
--table-head-b: #ece9ba88;
--table-tint: var(--accent2);
border-collapse: separate;
border-spacing: 0;
border-radius: 0.7rem;
overflow: clip;
box-shadow: 0 0.1rem 0.5rem #4f913b88;
}
th {
color: #2c4a2f;
}
/* A sun-kissed sheen on hover, fading in over the cell wash. */
tbody td {
transition: background-color 0.2s;
}
tbody tr:hover td {
background-color: #fff0a238;
}
figcaption,
.dateline,
.footnote {
color: var(--muted);
}
/* Editor chrome inherits the meadow rather than the base white. */
.editor-root.overlay {
background: linear-gradient(160deg, #eef7dd, #d9eec5);
}
/* The page transition exposes summer color around the rotating
snapshots. */
::view-transition {
background: var(--bg);
}