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>
537 lines
18 KiB
YAML
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; }
|
|
}
|