themes-y2k/manifest.yaml
Alex Dunmow b05ecf145d fix(y2k): remediate home whitespace + mobile responsiveness
- discography: kill the ballooning empty-media box — compact fixed-height
  chrome tile placeholder instead of a full aspect-square gray void, and
  a real CSS media-query grid (.y2k-grid-records: 1/2/4 cols) so column
  count no longer depends on the host Tailwind having scanned this persona
  template (it stuck at 2 cols at 1440px, making each empty square huge).
- landing: header absolute top-0 -> sticky top-0 (matches every other
  page template) so the audio bar no longer collides with / underlaps
  the navbar.
- waveform_player: constrain to a centered max-w-4xl column with section
  padding (was edge-to-edge full-bleed) and stack on phones.
- headings: remove the wide primary text-shadow halo that read as a stray
  render artifact behind every heading; tone the chrome shimmer to a faint
  glint. All colours stay semantic hsl(var(--token)).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:16:40 +08:00

524 lines
19 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;
/* Subtle chrome bevel only — the old wide primary halo read as a
stray rendering artifact behind every heading, so it is gone. */
text-shadow:
0 1px 0 hsl(var(--bevel-light) / 0.55),
0 1px 1px hsl(var(--bevel-dark) / 0.22);
}
/* Chrome shimmer sweep across big headlines (motion, reduced-motion-safe).
Toned to a faint glint so it never smears the letters in a still frame. */
.glitch {
position: relative;
background-image: linear-gradient(
100deg,
transparent 35%,
hsl(var(--bevel-light) / 0.3) 48%,
hsl(var(--accent) / 0.22) 50%,
transparent 62%);
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);
}
/* --- Responsive persona grids ----------------------------- */
/* Explicit media queries so column counts do NOT depend on the host
Tailwind build having scanned these theme templates (persona blocks
use responsive class combos no builtin emits, so the JIT drops them
and the grid collapses to its base count). 1 col on phones. */
.y2k-grid-records {
display: grid;
gap: 1.5rem;
grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 640px) {
.y2k-grid-records { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
.y2k-grid-records { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* Compact chrome tile for absent album/track art — a fixed, modest
height so an empty media field never balloons into a blank box. */
.y2k-media-empty {
display: flex;
align-items: center;
justify-content: center;
height: 11rem;
}
/* --- 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; }
}