themes-y2k/manifest.yaml
Alex Dunmow ed85f673f4 feat(y2k): Wave A theme-fleet build — 49 overrides, 7 templates, band persona (WO-TF-026)
Rebuild the y2k theme to the OVERRIDE-CONTRACT (frozen cms a0f07e2ae) with the
locked musician/band persona: liquid chrome, frosted-plastic bevels, gradient
mesh, marquees, glitter, a webring, and a dependency-free canvas metaball hero.

- All 49 mandatory builtin overrides in templates/overrides/y2k/ + manifest
  template_overrides; every content/provider field preserved from the frozen
  builtin default, only the chrome re-skinned (legacy button/card field reads
  already reconciled to link/style/label + text/media).
- 7 page templates: default, full-width, landing, article, blog-index, contact,
  auth — chrome header, glitter backdrop, chrome footer.
- Hero override ships a GO-BIG canvas liquid-chrome metaball showpiece
  (hero/landing only): lazy-init on view, prefers-reduced-motion honored, static
  chrome-mesh no-JS fallback, dependency-free.
- Persona blocks (5): waveform_player, tracklist, tour_dates, discography,
  webring_badge. Duplicating theme blocks became overrides; nft_gallery DELETED
  per WO.
- 5 dual-mode presets (frosted iMac plastic light + rave blacklight dark), all
  19 tokens, mode both, tuned dark.
- Bundled OFL fonts (Orbitron / Inter / Share Tech Mono) with licenses; all
  font-family via var(--font-heading|body|mono, fallback). Updated
  RECOMMENDED_FONTS/ICONS; required_icon_packs declared.
- Themed email-safe wrapper (tables, inline styles, chrome header, marquee
  footer). Master pages + demo seed (home/about/blog x3/contact/login) in band
  voice; contact form wired to seeded contact_submissions + row_inserted
  email-admins workflow.

Known platform gaps (OVERRIDE-CONTRACT §11/§12, unchanged): auth trio +
page-suggestions don't dispatch overrides yet; provider-backed overrides render
empty live lists until the codeless loader registers template sources. Files
authored faithful to builtin field/provider names; they light up when the cms
fix lands. Visual quality UNVERIFIED until Wave B.

Gates: `make` exit 0 (codeless y2k-0.2.0.bnp), `ninja plugin verify` OK,
check-safety exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:58:43 +08:00

496 lines
18 KiB
YAML

# Y2K theme — codeless .bnp manifest (WO-TF-026, Wave A).
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
# Persona: musician / band. Millennium-web nostalgia — liquid chrome, frosted
# iMac plastic (light) and rave blacklight (dark), marquees, glitter, a webring,
# and a dependency-free canvas metaball hero showpiece (hero/landing only).
theme_presets: presets.json
bundled_fonts: fonts.json
master_pages: master_pages.json
required_icon_packs: ["lucide", "simple-icons"]
system_templates:
- key: y2k
title: Y2K
description: "Millennium-web band theme — liquid-chrome bevels, frosted plastic, marquees, glitter, a webring, and a canvas metaball hero."
page_templates:
- system: y2k
key: default
title: Default
description: "Chrome header, sparkle backdrop, plastic content column, chrome footer"
slots: [header, main, footer]
- system: y2k
key: full-width
title: Full Width
description: "Edge-to-edge showcase layout with a mesh backdrop, no max-width"
slots: [header, main, footer]
- system: y2k
key: landing
title: Landing
description: "Canvas metaball hero, chrome feature grid, glitter CTA, chrome footer"
slots: [header, main, footer]
- system: y2k
key: article
title: Article / Liner Notes
description: "Prose column with a chrome sidebar aside — liner-notes layout"
slots: [header, main, aside, footer]
- system: y2k
key: blog-index
title: Blog Index
description: "Zine feed of chrome post cards"
slots: [header, main, footer]
- system: y2k
key: contact
title: Contact
description: "Chrome contact console — form panel plus a plastic contact card"
slots: [header, main, footer]
- system: y2k
key: auth
title: Auth
description: "Centered chrome auth panel for login / register / reset"
slots: [header, main, footer]
# All 49 mandatory-tier builtin overrides (OVERRIDE-CONTRACT §1), dashed keys.
template_overrides:
# Chrome (4)
- { template: y2k, block: navbar }
- { template: y2k, block: footer }
- { template: y2k, block: utility-bar }
- { template: y2k, block: announcement-bar }
# Marketing (12)
- { template: y2k, block: hero }
- { template: y2k, block: feature-grid }
- { template: y2k, block: rich-section }
- { template: y2k, block: stats }
- { template: y2k, block: testimonial }
- { template: y2k, block: logo-strip }
- { template: y2k, block: pricing }
- { template: y2k, block: team }
- { template: y2k, block: faq }
- { template: y2k, block: timeline }
- { template: y2k, block: cta }
- { template: y2k, block: countdown }
# Primitives (11)
- { template: y2k, block: heading }
- { template: y2k, block: text }
- { template: y2k, block: rich-text }
- { template: y2k, block: image }
- { template: y2k, block: quote }
- { template: y2k, block: divider }
- { template: y2k, block: button }
- { template: y2k, block: card }
- { template: y2k, block: gallery }
- { template: y2k, block: video-embed }
- { template: y2k, block: audio-embed }
# Commerce / local (6)
- { template: y2k, block: menu-list }
- { template: y2k, block: event-list }
- { template: y2k, block: contact-form }
- { template: y2k, block: contact-card }
- { template: y2k, block: hours-location }
- { template: y2k, block: social-links }
# Blog family (12)
- { template: y2k, block: blog-hero }
- { template: y2k, block: featured-posts }
- { template: y2k, block: related-posts }
- { template: y2k, block: popular-posts }
- { template: y2k, block: category-list }
- { template: y2k, block: post-hero }
- { template: y2k, block: post-metadata }
- { template: y2k, block: author-bio }
- { template: y2k, block: author-bio-hero }
- { template: y2k, block: newsletter-signup }
- { template: y2k, block: breadcrumbs }
- { template: y2k, block: sidebar-nav }
# Auth surface (3)
- { template: y2k, block: auth-form }
- { template: y2k, block: password-reset-form }
- { template: y2k, block: auth-status }
# System (1)
- { template: y2k, block: page-suggestions }
email_wrappers:
- y2k
css:
input_css_append: |
/* ============================================================
Y2K — millennium-web band theme (WO-TF-026).
Liquid chrome, frosted-plastic bevels, gradient mesh, marquee,
glitter/sparkle, webring badge, and a canvas metaball hero.
Light = frosted iMac plastic. Dark = rave blacklight.
Every colour resolves through the host shadcn HSL tokens via
hsl(var(--token)); the extra chrome/mesh/bevel custom properties
below are HSL triples consumed the same way. No literal hex/rgb.
============================================================ */
/* --- Chrome / mesh / bevel channel tokens ----------------- */
:root {
--chrome-1: 220 40% 96%;
--chrome-2: 205 45% 82%;
--chrome-3: 300 45% 78%;
--chrome-4: 240 35% 55%;
--mesh-a: 320 95% 62%;
--mesh-b: 190 90% 60%;
--mesh-c: 55 95% 68%;
--bevel-light: 0 0% 100%;
--bevel-dark: 250 45% 22%;
}
.dark {
--chrome-1: 280 35% 24%;
--chrome-2: 250 40% 18%;
--chrome-3: 315 55% 30%;
--chrome-4: 265 45% 10%;
--mesh-a: 320 100% 66%;
--mesh-b: 150 95% 58%;
--mesh-c: 275 95% 66%;
--bevel-light: 300 60% 78%;
--bevel-dark: 270 80% 4%;
}
/* --- Keyframes -------------------------------------------- */
@keyframes y2k-marquee-x {
from { transform: translate3d(0, 0, 0); }
to { transform: translate3d(-50%, 0, 0); }
}
@keyframes y2k-sparkle {
0%, 100% { opacity: 0; transform: scale(0.4) rotate(0deg); }
50% { opacity: 1; transform: scale(1) rotate(180deg); }
}
@keyframes y2k-shimmer-sweep {
0% { background-position: -150% 0; }
100% { background-position: 250% 0; }
}
@keyframes y2k-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
@keyframes y2k-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.45; transform: scale(0.82); }
}
/* --- Typography ------------------------------------------- */
/* Families flow through the admin-bound CSS variables; the fallback
leads with the bundled OFL family so the intended look survives
before the admin assigns a slot. @font-face for the bundled
families is auto-emitted from fonts.json (source=template rows). */
.y2k-body {
font-family: var(--font-body, "Inter", system-ui, -apple-system, "Segoe UI", sans-serif);
}
.y2k-mono,
.y2k-mono code,
pre.y2k-mono,
code.y2k-mono {
font-family: var(--font-mono, "Share Tech Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace);
}
.y2k-display {
font-family: var(--font-heading, "Orbitron", "Michroma", system-ui, sans-serif);
letter-spacing: 0.02em;
}
/* Beveled chrome headline — bright top rim, dark under-shadow, coloured halo. */
.y2k-heading,
.glitch {
font-family: var(--font-heading, "Orbitron", "Michroma", system-ui, sans-serif);
letter-spacing: 0.03em;
text-shadow:
0 1px 0 hsl(var(--bevel-light) / 0.7),
0 2px 0 hsl(var(--bevel-dark) / 0.35),
0 6px 22px hsl(var(--primary) / 0.4);
}
/* Chrome shimmer sweep across big headlines (motion, reduced-motion-safe). */
.glitch {
position: relative;
background-image: linear-gradient(
100deg,
transparent 20%,
hsl(var(--bevel-light) / 0.65) 45%,
hsl(var(--accent) / 0.55) 50%,
transparent 60%);
background-size: 220% 100%;
background-repeat: no-repeat;
-webkit-background-clip: text;
background-clip: text;
animation: y2k-shimmer-sweep 5.5s ease-in-out infinite;
}
.y2k-prose,
.y2k-prose p,
.y2k-prose li {
font-family: var(--font-body, "Inter", system-ui, sans-serif);
line-height: 1.75;
}
.y2k-prose code {
color: hsl(var(--primary));
background-color: hsl(var(--muted));
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
font-family: var(--font-mono, "Share Tech Mono", ui-monospace, monospace);
font-size: 0.875em;
}
/* Eyebrow / kicker: mono, uppercase, wide-tracked chrome readout. */
.y2k-eyebrow {
font-family: var(--font-mono, "Share Tech Mono", ui-monospace, monospace);
text-transform: uppercase;
letter-spacing: 0.22em;
font-size: 0.75rem;
color: hsl(var(--primary));
}
/* --- Chrome surfaces / backdrops -------------------------- */
.y2k-chrome-bg {
background-image: linear-gradient(180deg,
hsl(var(--chrome-1)) 0%,
hsl(var(--chrome-2)) 35%,
hsl(var(--chrome-3)) 70%,
hsl(var(--chrome-4)) 100%);
}
.y2k-mesh-bg {
background-image:
radial-gradient(at 18% 20%, hsl(var(--mesh-a) / 0.8) 0%, transparent 55%),
radial-gradient(at 82% 28%, hsl(var(--mesh-b) / 0.75) 0%, transparent 55%),
radial-gradient(at 50% 82%, hsl(var(--mesh-c) / 0.65) 0%, transparent 55%),
linear-gradient(135deg, hsl(var(--background)) 0%, hsl(var(--card)) 100%);
}
/* Sticky header chrome bar. */
.y2k-chrome {
background-image: linear-gradient(180deg,
hsl(var(--chrome-1) / 0.92) 0%,
hsl(var(--chrome-2) / 0.88) 100%);
backdrop-filter: saturate(1.3) blur(6px);
box-shadow: inset 0 1px 0 hsl(var(--bevel-light) / 0.7);
}
/* --- Bevel / frame / glow --------------------------------- */
.y2k-bevel,
.hud-frame {
position: relative;
border: 2px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 hsl(var(--bevel-light) / 0.8),
inset 0 -2px 0 hsl(var(--bevel-dark) / 0.3),
0 3px 10px hsl(var(--bevel-dark) / 0.28);
}
.y2k-image-frame {
border-radius: 0.75rem;
border: 2px solid hsl(var(--border));
box-shadow:
inset 0 1px 0 hsl(var(--bevel-light) / 0.5),
0 4px 16px hsl(var(--bevel-dark) / 0.35);
}
.y2k-glow,
.neon-edge-magenta {
box-shadow: 0 0 0 1px hsl(var(--primary) / 0.4), 0 0 22px hsl(var(--primary) / 0.28);
transition: box-shadow 180ms ease, transform 180ms ease;
}
.y2k-glow:hover, .y2k-glow:focus-within,
.neon-edge-magenta:hover, .neon-edge-magenta:focus-within {
box-shadow: 0 0 0 1px hsl(var(--primary) / 0.85), 0 0 32px hsl(var(--primary) / 0.48);
transform: translateY(-2px);
}
.neon-edge-cyan {
box-shadow: 0 0 0 1px hsl(var(--accent) / 0.4), 0 0 22px hsl(var(--accent) / 0.28);
transition: box-shadow 180ms ease, transform 180ms ease;
}
.neon-edge-cyan:hover, .neon-edge-cyan:focus-within {
box-shadow: 0 0 0 1px hsl(var(--accent) / 0.85), 0 0 32px hsl(var(--accent) / 0.48);
transform: translateY(-2px);
}
.neon-edge-lime {
box-shadow: 0 0 0 1px hsl(var(--ring) / 0.4), 0 0 22px hsl(var(--ring) / 0.28);
transition: box-shadow 180ms ease, transform 180ms ease;
}
.neon-edge-lime:hover, .neon-edge-lime:focus-within {
box-shadow: 0 0 0 1px hsl(var(--ring) / 0.85), 0 0 32px hsl(var(--ring) / 0.48);
transform: translateY(-2px);
}
/* clip-cut renders as soft rounded plastic corners in Y2K. */
.clip-cut { border-radius: 0.75rem; }
/* --- Buttons ---------------------------------------------- */
.y2k-button,
.y2k-btn,
.rgb-split {
transition: transform 90ms ease, box-shadow 90ms ease, filter 90ms ease;
}
.y2k-button {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.65rem 1.25rem;
font-weight: 600;
border: 2px solid hsl(var(--border));
border-radius: 0.75rem;
background-image: linear-gradient(180deg,
hsl(var(--primary) / 0.96) 0%,
hsl(var(--primary)) 50%,
hsl(var(--primary) / 0.82) 100%);
color: hsl(var(--primary-foreground));
box-shadow:
inset 0 1px 0 hsl(var(--bevel-light) / 0.7),
inset 0 -2px 0 hsl(var(--bevel-dark) / 0.3),
0 2px 8px hsl(var(--bevel-dark) / 0.25);
}
.y2k-btn { border-radius: 0.75rem; }
.y2k-button:hover, .y2k-btn:hover, .rgb-split:hover, .rgb-split:focus-visible {
transform: translateY(-1px);
filter: brightness(1.05);
}
.y2k-button:active, .y2k-btn:active, .rgb-split:active {
transform: translateY(1px);
box-shadow:
inset 0 2px 5px hsl(var(--bevel-dark) / 0.45),
inset 0 -1px 0 hsl(var(--bevel-light) / 0.3);
}
.y2k-button:focus-visible, .y2k-btn:focus-visible,
.rgb-split:focus-visible,
button:focus-visible, a:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
}
/* --- Cards ------------------------------------------------ */
.y2k-card {
border-radius: 0.75rem;
box-shadow:
inset 0 1px 0 hsl(var(--bevel-light) / 0.55),
0 4px 14px hsl(var(--bevel-dark) / 0.22);
}
/* --- Marquee ---------------------------------------------- */
.y2k-marquee {
overflow: hidden;
position: relative;
border-top: 2px solid hsl(var(--border));
border-bottom: 2px solid hsl(var(--border));
}
.y2k-marquee-track {
display: inline-flex;
gap: 3rem;
white-space: nowrap;
animation-name: y2k-marquee-x;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-duration: 28s;
}
.y2k-marquee-track[data-speed="slow"] { animation-duration: 60s; }
.y2k-marquee-track[data-speed="medium"] { animation-duration: 30s; }
.y2k-marquee-track[data-speed="fast"] { animation-duration: 12s; }
.y2k-marquee:hover .y2k-marquee-track,
.y2k-marquee:focus-within .y2k-marquee-track {
animation-play-state: paused;
}
/* --- Sparkle / glitter atmosphere ------------------------- */
.y2k-sparkle {
pointer-events: none;
animation: y2k-sparkle 1.6s ease-in-out infinite;
}
/* scanlines (ported name) is repurposed as a faint glitter dot field. */
.scanlines {
opacity: 0.4;
background-image:
radial-gradient(hsl(var(--primary) / 0.5) 1px, transparent 1.5px),
radial-gradient(hsl(var(--accent) / 0.4) 1px, transparent 1.5px);
background-size: 42px 42px, 68px 68px;
background-position: 0 0, 24px 30px;
}
/* --- Status / caret -------------------------------------- */
.y2k-status-dot {
animation: y2k-pulse 2.2s ease-in-out infinite;
}
.caret-blink {
display: inline-block;
animation: y2k-blink 1.05s steps(2, end) infinite;
}
/* --- Webring 88x31 badge ---------------------------------- */
.y2k-webring-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 88px;
height: 31px;
font-size: 11px;
letter-spacing: 0.05em;
text-transform: uppercase;
border: 1px solid hsl(var(--border));
background-image: linear-gradient(180deg, hsl(var(--chrome-1)) 0%, hsl(var(--chrome-3)) 100%);
color: hsl(var(--primary-foreground));
text-decoration: none;
box-shadow: inset 0 1px 0 hsl(var(--bevel-light) / 0.6);
}
/* --- Static waveform strip (no canvas — motion tier) ------ */
.y2k-waveform {
background-image: repeating-linear-gradient(
90deg,
hsl(var(--primary) / 0.65) 0 2px,
hsl(var(--accent) / 0.4) 2px 4px,
transparent 4px 8px);
-webkit-mask-image: linear-gradient(90deg, transparent, hsl(0 0% 0%) 8%, hsl(0 0% 0%) 92%, transparent);
mask-image: linear-gradient(90deg, transparent, hsl(0 0% 0%) 8%, hsl(0 0% 0%) 92%, transparent);
border-radius: 0.25rem;
}
/* --- Empty state / article aside -------------------------- */
.y2k-empty {
font-family: var(--font-mono, "Share Tech Mono", ui-monospace, monospace);
letter-spacing: 0.14em;
}
.y2k-aside { border-radius: 0.75rem; }
/* --- Canvas metaball showpiece (hero / landing only) ------ */
/* The <canvas> lazy-inits from the hero's inline script; this styles the
container plus the static no-JS chrome-mesh fallback. */
.y2k-orb-wrap {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.y2k-orb-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
opacity: 0.65;
filter: blur(2px) contrast(1.15);
}
.y2k-orb-fallback {
position: absolute;
inset: 0;
background-image:
radial-gradient(60% 55% at 30% 35%, hsl(var(--mesh-a) / 0.55), transparent 60%),
radial-gradient(55% 50% at 72% 45%, hsl(var(--mesh-b) / 0.5), transparent 60%),
radial-gradient(50% 45% at 50% 80%, hsl(var(--mesh-c) / 0.45), transparent 60%),
linear-gradient(to bottom, transparent, hsl(var(--background) / 0.7));
}
/* --- Reduced motion: kill all motion ---------------------- */
@media (prefers-reduced-motion: reduce) {
.y2k-marquee-track,
.y2k-sparkle,
.glitch,
.caret-blink,
.y2k-status-dot,
.y2k-orb-canvas {
animation: none !important;
animation-duration: 0.01ms !important;
}
.glitch {
background-image: none;
-webkit-background-clip: border-box;
background-clip: border-box;
}
.y2k-orb-canvas { display: none; }
.y2k-button:hover, .y2k-btn:hover, .rgb-split:hover { transform: none; }
}
/* --- High-contrast: drop the glitter + orbs --------------- */
@media (prefers-contrast: more) {
.scanlines,
.y2k-orb-canvas { display: none; }
}