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>
484 lines
16 KiB
YAML
484 lines
16 KiB
YAML
# 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
|
||
* (400–700); 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; }
|
||
}
|