# Coffee theme — codeless .bnp manifest (Wave A, WO-TF-013). # 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: coffee title: Coffee description: "Warm, hand-crafted cafe and roastery theme — kraft paper, chalkboard menus, doodles and stamps." page_templates: - system: coffee key: default title: Default description: "Header + main + footer over warm paper grain." slots: [header, main, footer] - system: coffee key: landing title: Landing description: "Hero pour, featured menu, story and invitation." slots: [hero, menu, story, cta, footer] - system: coffee key: article title: Article description: "Narrow narrative column with drop-cap for journal and recipes." slots: [header, main, footer] - system: coffee key: full-width title: Full Width description: "Edge-to-edge gallery and interior shots." slots: [header, main, footer] - system: coffee key: blog-index title: Blog Index description: "Blog hero, featured posts and a post grid." slots: [header, featured, main, footer] - system: coffee key: contact title: Contact description: "Contact form, hours and location." slots: [header, main, footer] - system: coffee key: auth title: Auth description: "Centered card for login, register and password reset." slots: [main, footer] email_wrappers: - coffee template_overrides: - { template: coffee, block: announcement-bar } - { template: coffee, block: audio-embed } - { template: coffee, block: auth-form } - { template: coffee, block: author-bio } - { template: coffee, block: author-bio-hero } - { template: coffee, block: auth-status } - { template: coffee, block: blog-hero } - { template: coffee, block: breadcrumbs } - { template: coffee, block: button } - { template: coffee, block: card } - { template: coffee, block: category-list } - { template: coffee, block: contact-card } - { template: coffee, block: contact-form } - { template: coffee, block: countdown } - { template: coffee, block: cta } - { template: coffee, block: divider } - { template: coffee, block: event-list } - { template: coffee, block: faq } - { template: coffee, block: featured-posts } - { template: coffee, block: feature-grid } - { template: coffee, block: footer } - { template: coffee, block: gallery } - { template: coffee, block: heading } - { template: coffee, block: hero } - { template: coffee, block: hours-location } - { template: coffee, block: image } - { template: coffee, block: logo-strip } - { template: coffee, block: menu-list } - { template: coffee, block: navbar } - { template: coffee, block: newsletter-signup } - { template: coffee, block: page-suggestions } - { template: coffee, block: password-reset-form } - { template: coffee, block: popular-posts } - { template: coffee, block: post-hero } - { template: coffee, block: post-metadata } - { template: coffee, block: pricing } - { template: coffee, block: quote } - { template: coffee, block: related-posts } - { template: coffee, block: rich-section } - { template: coffee, block: rich-text } - { template: coffee, block: sidebar-nav } - { template: coffee, block: social-links } - { template: coffee, block: stats } - { template: coffee, block: team } - { template: coffee, block: testimonial } - { template: coffee, block: text } - { template: coffee, block: timeline } - { template: coffee, block: utility-bar } - { template: coffee, block: video-embed } css: input_css_append: | /* Coffee theme styles * * Dual-mode via the 19 shadcn-style HSL token CSS variables (--background, * --foreground, --primary, --accent, --border, --muted, --card, ...) consumed * as hsl(var(--token)). No literal colors. Font families resolve ONLY through * the BlockNinja font variables (--font-heading, --font-body, --font-mono) with * the fallback stacks below — never hardcoded, so the admin font picker works. */ :root { --coffee-heading-fallback: "Fraunces", "Playfair Display", Georgia, "Times New Roman", serif; --coffee-body-fallback: "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; --coffee-hand-fallback: "Caveat", "Segoe Print", "Bradley Hand", cursive; --coffee-mono-fallback: "JetBrains Mono", "Fira Code", Menlo, Consolas, monospace; } /* --- Paper grain background --------------------------------------------- */ body.coffee-paper { background-color: hsl(var(--background)); background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160' viewBox='0 0 160 160'%3E%3Cfilter id='paper-grain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.18 0 0 0 0 0.13 0 0 0 0 0.09 0 0 0 0.07 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23paper-grain)'/%3E%3C/svg%3E"); background-repeat: repeat; } /* --- Type roles --------------------------------------------------------- */ .coffee-display { font-family: var(--font-heading, var(--coffee-heading-fallback)); font-feature-settings: "liga" 1, "dlig" 1; letter-spacing: -0.01em; } .coffee-body { font-family: var(--font-body, var(--coffee-body-fallback)); line-height: 1.65; } .coffee-mono { font-family: var(--font-mono, var(--coffee-mono-fallback)); } /* Hand-drawn accents (badges, stamps, captions). Routed through --font-heading * with a Caveat fallback: unassigned it renders Caveat; if the admin sets a * heading font the accents follow it (the 3-slot model working as intended). */ .coffee-hand { font-family: var(--font-heading, var(--coffee-hand-fallback)); letter-spacing: 0.01em; line-height: 1.15; } /* --- Doodle underline --------------------------------------------------- */ .coffee-doodle-underline { background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 12' preserveAspectRatio='none'%3E%3Cpath d='M2 8 Q 20 2 40 7 T 80 6 T 118 7' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' opacity='0.55'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: 0 100%; background-size: 100% 0.5em; padding-bottom: 0.18em; } /* --- Drop-cap ----------------------------------------------------------- */ .coffee-dropcap > p:first-of-type::first-letter { font-family: var(--font-heading, var(--coffee-heading-fallback)); font-size: 4em; line-height: 0.85; float: left; padding: 0.05em 0.12em 0 0; color: hsl(var(--primary)); } /* --- Kraft-tag button (stamp press on hover) ---------------------------- */ .kraft-tag { position: relative; display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.55rem 1.1rem; background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border: 1px solid hsl(var(--border)); border-radius: 0.25rem; font-family: var(--font-body, var(--coffee-body-fallback)); font-weight: 500; letter-spacing: 0.02em; box-shadow: 0 2px 0 hsl(var(--border)); transition: transform 120ms ease, box-shadow 120ms ease; cursor: pointer; } .kraft-tag:hover { transform: translateY(1px) rotate(-1.2deg); box-shadow: 0 1px 0 hsl(var(--border)); } .kraft-tag:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 3px; } .kraft-tag--ghost { background-color: transparent; color: hsl(var(--primary)); box-shadow: none; border-style: dashed; } .kraft-tag--ghost:hover { background-color: hsl(var(--muted)); } /* --- Torn-paper edges --------------------------------------------------- * The deckled edge is a FIXED ~13px band at the named edge; the rest of the * element is masked solid. (The old `mask-size:100% 100%` stretched the 12px * torn SVG across the whole element, eating ~40% of tall cards as a transparent * wavy cut-out — the source of the hero/featured-pour/footer clipping.) */ .coffee-torn-top { -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 12 L0 6 Q 5 2 10 5 T 20 4 T 30 6 T 40 3 T 50 5 T 60 4 T 70 5 T 80 3 T 90 5 T 100 4 L100 12 Z' fill='black'/%3E%3C/svg%3E"), linear-gradient(black, black); mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 12 L0 6 Q 5 2 10 5 T 20 4 T 30 6 T 40 3 T 50 5 T 60 4 T 70 5 T 80 3 T 90 5 T 100 4 L100 12 Z' fill='black'/%3E%3C/svg%3E"), linear-gradient(black, black); -webkit-mask-position: top, bottom; mask-position: top, bottom; -webkit-mask-size: 100% 13px, 100% calc(100% - 12px); mask-size: 100% 13px, 100% calc(100% - 12px); -webkit-mask-repeat: no-repeat, no-repeat; mask-repeat: no-repeat, no-repeat; } .coffee-torn-bottom { -webkit-mask-image: linear-gradient(black, black), url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 0 L100 0 L100 6 Q 95 10 90 7 T 80 8 T 70 6 T 60 9 T 50 7 T 40 8 T 30 7 T 20 9 T 10 7 T 0 8 Z' fill='black'/%3E%3C/svg%3E"); mask-image: linear-gradient(black, black), url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 0 L100 0 L100 6 Q 95 10 90 7 T 80 8 T 70 6 T 60 9 T 50 7 T 40 8 T 30 7 T 20 9 T 10 7 T 0 8 Z' fill='black'/%3E%3C/svg%3E"); -webkit-mask-position: top, bottom; mask-position: top, bottom; -webkit-mask-size: 100% calc(100% - 12px), 100% 13px; mask-size: 100% calc(100% - 12px), 100% 13px; -webkit-mask-repeat: no-repeat, no-repeat; mask-repeat: no-repeat, no-repeat; } .coffee-torn-top.coffee-torn-bottom { -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 12 L0 6 Q 5 2 10 5 T 20 4 T 30 6 T 40 3 T 50 5 T 60 4 T 70 5 T 80 3 T 90 5 T 100 4 L100 12 Z' fill='black'/%3E%3C/svg%3E"), linear-gradient(black, black), url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 0 L100 0 L100 6 Q 95 10 90 7 T 80 8 T 70 6 T 60 9 T 50 7 T 40 8 T 30 7 T 20 9 T 10 7 T 0 8 Z' fill='black'/%3E%3C/svg%3E"); mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 12 L0 6 Q 5 2 10 5 T 20 4 T 30 6 T 40 3 T 50 5 T 60 4 T 70 5 T 80 3 T 90 5 T 100 4 L100 12 Z' fill='black'/%3E%3C/svg%3E"), linear-gradient(black, black), url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M0 0 L100 0 L100 6 Q 95 10 90 7 T 80 8 T 70 6 T 60 9 T 50 7 T 40 8 T 30 7 T 20 9 T 10 7 T 0 8 Z' fill='black'/%3E%3C/svg%3E"); -webkit-mask-position: top, center, bottom; mask-position: top, center, bottom; -webkit-mask-size: 100% 13px, 100% calc(100% - 24px), 100% 13px; mask-size: 100% 13px, 100% calc(100% - 24px), 100% 13px; -webkit-mask-repeat: no-repeat, no-repeat, no-repeat; mask-repeat: no-repeat, no-repeat, no-repeat; } /* --- Surfaces ----------------------------------------------------------- */ .coffee-card { background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border)); border-radius: 0.5rem; } .coffee-frame { border: 1px solid hsl(var(--border)); background-color: hsl(var(--card)); } .coffee-pencil-rule { border-color: hsl(var(--border)); border-style: solid; } /* --- Chalkboard menu panel (inverts foreground/background for a hung board; * light mode = dark board + cream chalk, dark mode = light menu paper) --- */ .coffee-chalkboard { position: relative; background-color: hsl(var(--foreground)); color: hsl(var(--background)); border-radius: 0.5rem; box-shadow: inset 0 0 0 3px hsl(var(--background) / 0.12), inset 0 0 0 4px hsl(var(--foreground)); } .coffee-chalk-rule { border-color: hsl(var(--background) / 0.28); border-style: solid; } .coffee-chalk-muted { color: hsl(var(--background) / 0.72); } /* --- Hours: today highlight --------------------------------------------- * Applied to the whole so both cells share one readable band. (Was on the * only with accent-foreground text over a 15%-accent tint — light-on-light, * unreadable.) accent text over a 12% tint of the same accent reads cleanly. */ .coffee-hours-today > th, .coffee-hours-today > td { background-color: hsl(var(--accent) / 0.12); } .coffee-hours-today > th { box-shadow: inset 3px 0 0 hsl(var(--accent)); padding-left: 0.75rem; color: hsl(var(--accent)); } /* --- Layout helpers ------------------------------------------------------ * Explicit CSS so these layouts never depend on the host Tailwind JIT * compiling arbitrary/responsive utilities out of THEME templates — it does * not scan them reliably, so `min-h-[50vh]` / `md:grid-cols-[2fr_3fr]` / * `md:grid-cols-2` in a theme file can silently vanish. Base is always a * single column, so grids collapse on mobile regardless of the JIT. */ .coffee-minh-50 { min-height: 50vh; } .coffee-minh-75 { min-height: 75vh; } .coffee-minh-screen { min-height: 100vh; } .coffee-minh-400 { min-height: 400px; } .coffee-minh-320 { min-height: 320px; } .coffee-split-2 { display: grid; grid-template-columns: 1fr; } .coffee-pour-grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; } .coffee-menu-grid { display: grid; grid-template-columns: 1fr; gap: 2.5rem 3rem; } .coffee-info-grid { display: grid; grid-template-columns: 1fr; gap: 2.5rem; } @media (min-width: 768px) { .coffee-pour-grid { grid-template-columns: 2fr 3fr; align-items: center; } .coffee-menu-grid.coffee-menu-grid--2 { grid-template-columns: 1fr 1fr; } .coffee-info-grid.coffee-info-grid--2 { grid-template-columns: 1fr 1fr; } } @media (min-width: 1024px) { .coffee-split-2 { grid-template-columns: 1fr 1fr; } } /* --- Price + spec chips ------------------------------------------------- */ .coffee-price { font-family: var(--font-mono, var(--coffee-mono-fallback)); font-variant-numeric: tabular-nums; color: hsl(var(--accent)); } .coffee-chip { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.7rem; border: 1px dashed hsl(var(--border)); border-radius: 9999px; background-color: hsl(var(--muted)); color: hsl(var(--foreground)); font-family: var(--font-body, var(--coffee-body-fallback)); font-size: 0.85rem; } /* --- Circular stamp / badge (press on hover) ---------------------------- */ .coffee-stamp { display: inline-flex; align-items: center; justify-content: center; width: 4.25rem; height: 4.25rem; border-radius: 9999px; background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); border: 2px solid hsl(var(--accent-foreground) / 0.35); box-shadow: inset 0 0 0 3px hsl(var(--accent)); font-family: var(--font-heading, var(--coffee-hand-fallback)); font-size: 0.95rem; text-align: center; line-height: 1.05; transform: rotate(-6deg); transition: transform 140ms ease; } .coffee-stamp:hover { transform: rotate(-6deg) scale(0.94); } .coffee-stamp-sm { width: 2.25rem; height: 2.25rem; font-size: 1rem; transform: rotate(-4deg); box-shadow: none; } /* --- Loyalty punch stamps ----------------------------------------------- */ .coffee-punch { display: inline-flex; align-items: center; justify-content: center; aspect-ratio: 1 / 1; border-radius: 9999px; border: 2px dashed hsl(var(--border)); color: hsl(var(--muted-foreground)); } .coffee-punch-on { border-style: solid; border-color: hsl(var(--accent)); background-color: hsl(var(--accent) / 0.15); color: hsl(var(--accent)); transform: rotate(-5deg); } .coffee-punch-reward { border-color: hsl(var(--primary)); background-color: hsl(var(--primary) / 0.12); color: hsl(var(--primary)); } /* --- Location pin ------------------------------------------------------- */ .coffee-pin { color: hsl(var(--accent)); } /* --- Doodle divider draw-in --------------------------------------------- */ .coffee-doodle-draw path, .coffee-doodle-draw ellipse, .coffee-doodle-draw circle { stroke-dasharray: 240; stroke-dashoffset: 240; animation: coffee-draw 1.4s ease forwards; } @keyframes coffee-draw { to { stroke-dashoffset: 0; } } /* --- Motion safety: static fallback ------------------------------------- */ @media (prefers-reduced-motion: reduce) { .kraft-tag, .coffee-stamp, .coffee-punch-on { transition: none; } .kraft-tag:hover, .coffee-stamp:hover { transform: none; } .coffee-doodle-draw path, .coffee-doodle-draw ellipse, .coffee-doodle-draw circle { stroke-dasharray: none; stroke-dashoffset: 0; animation: none; } }