themes-terminal/manifest.yaml
Alex Dunmow 1bab4171a9 feat(fonts): bundle JetBrains Mono + IBM Plex Mono via @font-face + preset typography refs
The host does not consume bundled_fonts (LoadedPlugin.BundledFonts is dead
code), so declare @font-face against the served /templates/terminal/fonts/
paths and point each preset's typography.fontHeading/body/mono at
template:terminal:<Family>. Mono faces now render instead of fallback sans.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:23:53 +08:00

484 lines
16 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Terminal theme — codeless .bnp manifest (WO-TF-025, Wave A).
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
theme_presets: presets.json
bundled_fonts: fonts.json
master_pages: master_pages.json
required_icon_packs: ["lucide", "simple-icons"]
system_templates:
- key: terminal
title: Terminal
description: "Phosphor-on-black TTY theme for dev tools and OSS docs — window chrome, man-page article layouts, keybind tables, boot logs, monospace type, and a typed-terminal canvas hero. Paper-teletype light, phosphor dark."
page_templates:
- system: terminal
key: default
title: Default
description: "Centered 80-col TTY page with window chrome and a sticky header"
slots: [header, main, footer]
- system: terminal
key: full-width
title: Full Width
description: "Edge-to-edge layout for tool dashboards and status pages"
slots: [header, main, footer]
- system: terminal
key: landing
title: Landing
description: "Typed-terminal canvas hero, feature grid, boot log, and CTA for README-style project sites"
slots: [header, main, footer]
- system: terminal
key: article
title: Article (man-page)
description: "Man-page two-column layout with a section rail, ideal for docs and writeups"
slots: [header, main, aside, footer]
- system: terminal
key: blog-index
title: Blog Index
description: "Changelog-style post feed with a sidebar"
slots: [header, main, footer]
- system: terminal
key: contact
title: Contact
description: "Terminal contact console — form panel plus a contact card"
slots: [header, main, footer]
- system: terminal
key: auth
title: Auth
description: "Centered login/register/reset console in a TTY window"
slots: [header, main, footer]
template_overrides:
# Chrome (4)
- { template: terminal, block: navbar }
- { template: terminal, block: footer }
- { template: terminal, block: utility-bar }
- { template: terminal, block: announcement-bar }
# Marketing (12)
- { template: terminal, block: hero }
- { template: terminal, block: feature-grid }
- { template: terminal, block: rich-section }
- { template: terminal, block: stats }
- { template: terminal, block: testimonial }
- { template: terminal, block: logo-strip }
- { template: terminal, block: pricing }
- { template: terminal, block: team }
- { template: terminal, block: faq }
- { template: terminal, block: timeline }
- { template: terminal, block: cta }
- { template: terminal, block: countdown }
# Primitives (11)
- { template: terminal, block: heading }
- { template: terminal, block: text }
- { template: terminal, block: rich-text }
- { template: terminal, block: image }
- { template: terminal, block: quote }
- { template: terminal, block: divider }
- { template: terminal, block: button }
- { template: terminal, block: card }
- { template: terminal, block: gallery }
- { template: terminal, block: video-embed }
- { template: terminal, block: audio-embed }
# Commerce / local (6)
- { template: terminal, block: menu-list }
- { template: terminal, block: event-list }
- { template: terminal, block: contact-form }
- { template: terminal, block: contact-card }
- { template: terminal, block: hours-location }
- { template: terminal, block: social-links }
# Blog family (12)
- { template: terminal, block: blog-hero }
- { template: terminal, block: featured-posts }
- { template: terminal, block: related-posts }
- { template: terminal, block: popular-posts }
- { template: terminal, block: category-list }
- { template: terminal, block: post-hero }
- { template: terminal, block: post-metadata }
- { template: terminal, block: author-bio }
- { template: terminal, block: author-bio-hero }
- { template: terminal, block: newsletter-signup }
- { template: terminal, block: breadcrumbs }
- { template: terminal, block: sidebar-nav }
# Auth surface (3)
- { template: terminal, block: auth-form }
- { template: terminal, block: password-reset-form }
- { template: terminal, block: auth-status }
# System (1)
- { template: terminal, block: page-suggestions }
email_wrappers:
- terminal
css:
input_css_append: |
/* ============================================================
Terminal — Phosphor-on-black TTY theme (WO-TF-025).
All colours consume the host shadcn HSL tokens via
hsl(var(--token)). No hardcoded hex / rgb / named colours.
Dual-mode: light = paper teletype, dark = phosphor CRT.
============================================================ */
/* Bundled mono faces. The host does not yet emit @font-face for
* bundled_fonts (LoadedPlugin.BundledFonts is unconsumed), so the theme
* declares its own faces against the served artifact paths
* (/templates/terminal/fonts/...). Presets set typography.fontHeading/
* body/mono to template:terminal:<Family> so --font-heading/body/mono
* resolve to these families. JetBrains Mono is one variable-weight file
* (400700); IBM Plex Mono ships 400 + 500. */
@font-face { font-family: 'JetBrains Mono'; src: url('/templates/terminal/fonts/jetbrains-mono-latin.woff2') format('woff2'); font-weight: 400 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url('/templates/terminal/fonts/ibm-plex-mono-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url('/templates/terminal/fonts/ibm-plex-mono-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
/* --- Globals: square corners, mono stack ------------------ */
:root { --radius: 0; }
.terminal-page {
font-family: var(--font-body, "IBM Plex Mono", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
letter-spacing: 0.01em;
}
.terminal-page * { border-radius: 0 !important; }
/* --- Typography helpers ----------------------------------- */
.terminal-heading {
font-family: var(--font-heading, "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
text-transform: uppercase;
letter-spacing: 0.06em;
}
.terminal-mono {
font-family: var(--font-mono, "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
}
.terminal-eyebrow {
font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
text-transform: uppercase;
letter-spacing: 0.2em;
font-size: 0.75rem;
color: hsl(var(--accent));
}
.terminal-prose,
.terminal-prose p,
.terminal-prose li {
font-family: var(--font-body, "IBM Plex Mono", ui-monospace, monospace);
line-height: 1.7;
}
.terminal-prose a {
color: hsl(var(--accent));
text-decoration: underline;
text-underline-offset: 0.2em;
}
.terminal-prose code,
code.terminal-mono {
color: hsl(var(--accent));
background-color: hsl(var(--muted));
padding: 0.1rem 0.35rem;
border: 1px solid hsl(var(--border));
font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
font-size: 0.875em;
}
/* --- Caret blink (element opacity; used as <span class="caret-blink">_</span>) --- */
@keyframes caret-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
.caret-blink {
display: inline-block;
animation: caret-blink 1s step-end infinite;
}
/* --- TTY window chrome (signature) ------------------------ */
.tty-window {
border: 1px solid hsl(var(--border));
background-color: hsl(var(--card));
color: hsl(var(--card-foreground));
}
.tty-titlebar {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.75rem;
border-bottom: 1px solid hsl(var(--border));
background-color: hsl(var(--muted));
color: hsl(var(--muted-foreground));
font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
font-size: 0.75rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.tty-dot {
display: inline-block;
width: 0.6rem;
height: 0.6rem;
border: 1px solid hsl(var(--border));
background-color: hsl(var(--secondary));
}
/* --- TTY frame: bordered panel with hover glow ------------ */
.tty-frame {
border: 1px solid hsl(var(--border));
background-color: hsl(var(--card));
transition: box-shadow 180ms ease, border-color 180ms ease;
}
.tty-frame:hover,
.tty-frame:focus-within {
border-color: hsl(var(--primary));
box-shadow: 0 0 0 1px hsl(var(--primary) / 0.5), 0 0 18px hsl(var(--primary) / 0.18);
}
/* --- ASCII corner brackets (accent + on the corners) ------ */
.tty-corners { position: relative; }
.tty-corners::before,
.tty-corners::after {
content: "+";
position: absolute;
color: hsl(var(--primary));
font-family: var(--font-mono, ui-monospace, monospace);
font-weight: 700;
line-height: 1;
pointer-events: none;
}
.tty-corners::before { top: -0.55em; left: -0.35em; }
.tty-corners::after { bottom: -0.55em; right: -0.35em; }
/* --- ASCII figure frame (image override) ------------------ */
.ascii-frame {
display: inline-block;
padding: 0.5rem;
border: 1px solid hsl(var(--border));
background-color: hsl(var(--card));
position: relative;
}
.ascii-frame figcaption {
display: block;
margin-top: 0.5rem;
color: hsl(var(--muted-foreground));
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 0.875rem;
text-align: center;
}
/* --- TTY scanlines overlay element (hero/landing) --------- */
.tty-scanlines {
opacity: 0.06;
background-image: linear-gradient(
to bottom,
hsl(var(--foreground)) 0px,
hsl(var(--foreground)) 1px,
transparent 1px,
transparent 3px
);
background-size: 100% 3px;
animation: tty-scan-drift 6s linear infinite;
}
@keyframes tty-scan-drift { 0% { background-position: 0 0; } 100% { background-position: 0 3px; } }
/* --- Page-level CRT scanline wash (subtle, whole page) ---- */
.crt-scanlines { position: relative; }
.crt-scanlines::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 1000;
background: repeating-linear-gradient(
to bottom,
transparent 0,
transparent 2px,
hsl(var(--foreground) / 0.04) 2px,
hsl(var(--foreground) / 0.04) 3px
);
}
/* --- Status dot pulse ------------------------------------- */
.terminal-status-dot { animation: terminal-pulse 2.2s ease-in-out infinite; }
@keyframes terminal-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
/* --- Typed-terminal canvas showpiece (hero override only) - */
.tty-rain-wrap {
position: absolute;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.tty-rain-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
opacity: 0.45;
}
.tty-rain-fallback {
position: absolute;
inset: 0;
background:
radial-gradient(120% 80% at 50% 0%, hsl(var(--primary) / 0.16), transparent 60%),
linear-gradient(to bottom, transparent, hsl(var(--background) / 0.7));
}
/* --- Man-page header block -------------------------------- */
.manpage-header {
font-family: var(--font-mono, ui-monospace, monospace);
border-top: 1px solid hsl(var(--border));
border-bottom: 1px solid hsl(var(--border));
padding: 0.6rem 0;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
gap: 1rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: hsl(var(--foreground));
}
.manpage-header .left,
.manpage-header .center,
.manpage-header .right { flex: 1; }
.manpage-header .center { text-align: center; }
.manpage-header .right { text-align: right; }
/* --- Section rail (article aside furniture) --------------- */
.terminal-toc {
font-family: var(--font-mono, ui-monospace, monospace);
color: hsl(var(--muted-foreground));
padding: 1rem;
border: 1px solid hsl(var(--border));
background: hsl(var(--card));
position: sticky;
top: 1rem;
}
.terminal-toc h4 {
text-transform: uppercase;
letter-spacing: 0.1em;
color: hsl(var(--foreground));
margin: 0 0 0.5rem;
font-size: 0.875rem;
}
.terminal-toc ul { list-style: none; margin: 0; padding: 0; }
.terminal-toc li { margin: 0.2rem 0; }
.terminal-toc a { color: hsl(var(--muted-foreground)); text-decoration: none; }
.terminal-toc a:hover { color: hsl(var(--foreground)); }
@media (max-width: 768px) { .terminal-toc { display: none; } }
/* --- Code console block ----------------------------------- */
.code-console {
font-family: var(--font-mono, ui-monospace, monospace);
background: hsl(var(--card));
border: 1px solid hsl(var(--border));
padding: 0.75rem 1rem;
margin: 1rem 0;
overflow-x: auto;
color: hsl(var(--card-foreground));
}
.code-console .cmd-line { display: flex; align-items: baseline; gap: 0.5em; margin: 0; }
.code-console .cmd-prompt { color: hsl(var(--accent)); }
.code-console .cmd-text { color: hsl(var(--foreground)); }
.code-console pre {
margin: 0.5rem 0 0;
white-space: pre-wrap;
color: hsl(var(--muted-foreground));
font-family: inherit;
}
/* --- Keybinding table ------------------------------------- */
.keybind-table {
font-family: var(--font-mono, ui-monospace, monospace);
border-collapse: collapse;
margin: 1rem 0;
width: 100%;
max-width: 80ch;
color: hsl(var(--foreground));
}
.keybind-table th,
.keybind-table td {
border-bottom: 1px dashed hsl(var(--border));
padding: 0.4rem 0.6rem;
text-align: left;
vertical-align: top;
}
.keybind-table th {
text-transform: uppercase;
letter-spacing: 0.08em;
color: hsl(var(--accent));
font-weight: 600;
}
.keybind-table kbd {
font-family: inherit;
background: hsl(var(--muted));
color: hsl(var(--foreground));
border: 1px solid hsl(var(--border));
padding: 0.05rem 0.4rem;
margin-right: 0.25rem;
font-size: 0.875em;
}
/* --- Boot log --------------------------------------------- */
.boot-log {
font-family: var(--font-mono, ui-monospace, monospace);
background: hsl(var(--background));
color: hsl(var(--foreground));
padding: 1rem;
border: 1px solid hsl(var(--border));
margin: 1rem 0;
white-space: pre-wrap;
overflow-x: auto;
}
.boot-log .boot-line {
display: block;
opacity: 0;
animation: boot-line-in 0.2s ease-out forwards;
}
@keyframes boot-line-in {
from { opacity: 0; transform: translateY(2px); }
to { opacity: 1; transform: translateY(0); }
}
.boot-log .boot-cursor {
display: inline-block;
color: hsl(var(--accent));
animation: caret-blink 1s step-end infinite;
}
/* --- Reduced motion: kill all motion ---------------------- */
@media (prefers-reduced-motion: reduce) {
.caret-blink,
.tty-scanlines,
.terminal-status-dot,
.tty-rain-canvas,
.boot-log .boot-line,
.boot-log .boot-cursor {
animation: none !important;
animation-duration: 0.01ms !important;
}
.boot-log .boot-line { opacity: 1; transform: none; }
.tty-scanlines { opacity: 0; }
.tty-rain-canvas { display: none; }
}
/* --- High contrast: drop the CRT overlays ----------------- */
@media (prefers-contrast: more) {
.tty-scanlines,
.crt-scanlines::before,
.tty-rain-canvas { display: none; }
}