themes-terminal/manifest.yaml
Alex Dunmow 0e54864687 fix(terminal): remediate layout/whitespace + mobile responsiveness
Layout/whitespace (home page):
- feature-grid rendered as 3 full-width ballooning rows because the host
  Tailwind scan covers rendered block HTML, not theme override .ninjatpl
  files, so lg:grid-cols-* utilities in overrides are purge-fragile and
  collapsed to one column. Move multi-column layout to plain CSS in
  css.input_css_append (.tty-grid*, never purged) and apply to
  feature-grid, stats, pricing, contact-card + a .tty-form-grid for
  contact-form (col-span utilities were equally fragile).
- code_console / boot_log persona blocks shipped no max-width wrapper and
  ran edge-to-edge on the full-width landing page while sibling sections
  were contained; wrap both in a centred .tty-console-shell with a mobile
  side gutter.
- CTA heading read dark-on-green (fails contrast): .terminal-heading in
  assets/style.css hardcoded color:foreground, overriding the band's
  primary-foreground. Change to color:inherit so headings on inverted
  bands read correctly.
- Trim oversized section padding (py-16 md:py-24 -> py-12 md:py-20; the
  window-nested contact sections -> py-8 md:py-12).

Mobile:
- All multi-column grids now collapse to 1 column via CSS media queries
  (minmax(0,1fr) prevents long/monospace content forcing horizontal
  overflow); console blocks keep overflow-x scroll inside their own box.
- Pin hero min-heights to CSS (.tty-hero*) since min-h-screen / arbitrary
  min-h-[..] utilities are also purge-fragile.

Persona (phosphor TTY, window chrome, man-page, canvas hero) unchanged.
No version bump. make + check-safety both exit 0.

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

537 lines
18 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.
============================================================ */
/* --- 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;
}
/* --- Layout helpers --------------------------------------- */
.terminal-main-80 { max-width: 80ch; margin: 0 auto; }
.terminal-article-grid {
display: grid;
grid-template-columns: 20ch 1fr;
gap: 2rem;
align-items: start;
}
@media (max-width: 768px) { .terminal-article-grid { grid-template-columns: 1fr; } }
/* --- Reliable responsive grids ----------------------------
The host scans rendered block HTML, not theme override
.ninjatpl files, so responsive Tailwind grid utilities used
only in overrides (lg:grid-cols-*, sm:grid-cols-2) are
purge-fragile and can silently collapse to one column on
desktop. Multi-column layout is therefore defined here as
plain CSS, which is compiled into the Tailwind input and can
never be purged. minmax(0,1fr) stops long/unbreakable content
from forcing horizontal overflow on mobile. */
.tty-grid,
.tty-grid-2,
.tty-grid-3,
.tty-grid-4 { display: grid; gap: 1.5rem; grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 640px) {
.tty-grid-2,
.tty-grid-3,
.tty-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
.tty-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tty-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.tty-grid-stats2,
.tty-grid-stats3,
.tty-grid-stats4 { display: grid; gap: 2rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 768px) {
.tty-grid-stats3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tty-grid-stats4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* --- Two-column form grid (col-span utilities in overrides
are purge-fragile; pin the grid in CSS) ------------------- */
.tty-form-grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(1, minmax(0, 1fr)); }
.tty-form-grid > .tty-col-full { grid-column: 1 / -1; }
@media (min-width: 640px) { .tty-form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* --- Contained console shell ------------------------------
The persona code-console / boot-log blocks ship no max-width
wrapper, so on a full-width landing page they run edge-to-edge
while the neighbouring sections are contained. This shell
centres them and keeps a mobile side gutter. */
.tty-console-shell { width: 100%; max-width: 56rem; margin: 1.5rem auto; padding-inline: 1rem; }
/* --- Hero heights (min-h-screen / arbitrary min-h-[..] are
purge-fragile utilities; pin them in CSS) ----------------- */
.tty-hero { position: relative; }
.tty-hero-screen { min-height: 100vh; }
.tty-hero-tall { min-height: 75vh; }
.tty-hero-half { min-height: 50vh; }
.tty-hero-auto { min-height: 24rem; }
.tty-hero-media { min-height: 20rem; }
/* --- 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; }
}