Theming & Tincture

A Yoshi site is content-free in the skeleton — every colour, button, font and layout choice is declared in the blueprint and resolved through the tincture: a small set of CSS custom properties generated per-site from the brand. Drop the same component into any brand and it re-skins itself. This page is the map; the source of truth is yoshi-generator/skeleton-core/src/tincture/_generated/foundation.css.j2 + apply_color_tokens in yoshi-generator/src/lib.rs.

The token model

From primaryColor (+ optional secondaryColor) the generator emits a brand-tinted token set on :root (and a dark variant on [data-surface="dark"]). Components read the tokens, never raw hex:

Token Meaning
--accent the brand colour (borders, icons, eyebrows, rules)
--accent-btn the button background (luminance-aware default; overridable — see below)
--accent-btn-hover the button background on hover (default = --accent-btn)
--on-accent text colour on the button (dark for bright/metallic brands, white for dark)
--ink / --ink-soft / --ink-muted the brand-tinted text ramp (AA-checked)
--bg / --bg-card / --border surfaces
--container the one shared alignment width

data-surface="dark" flips the ramp (dark bg, light ink) so any component becomes a self-contained dark surface — used by CTA bands, the console service cards, and the service coverage card.

Buttons — silver/chrome with a gold hover (the brand-balance lever)

apply_color_tokens picks a sensible button by the primary colour's luminance (bright/metallic → champagne-foil + dark text; dark → solid + white text). A brand can override the material in config.json so an accent stays sparing rather than a fill — e.g. a silver/chrome button that turns gold on hover:

{
  "accentBtn": "conic-gradient(from 140deg at 50% 40%, #d6d8dd, #f7f8fa 18%, #aeb0b6 42%, #eceef0 62%, #bcbec4 82%, #d6d8dd)",
  "onAccent": "#1A1B1F",
  "accentBtnHover": "linear-gradient(135deg, color-mix(in srgb, var(--accent) 82%, #000) 0%, var(--accent) 50%, color-mix(in srgb, var(--accent) 75%, #000) 100%)"
}

A single global rule (globals.css) swaps every button that paints with [background:var(--accent-btn)] to --accent-btn-hover on hover — so the silver→gold behaviour applies site-wide with zero per-button edits. (Why it matters: on a brand where the accent is "the most valuable" mark, an accent used as a large fill dilutes it — keep it a hover/edge accent.)

⚠ These three keys (accentBtn / onAccent / accentBtnHover) must be declared fields on the Rust Config struct (yoshi-blueprint/src/lib.rs) — the typed deserialize drops unknown keys. If a button override "doesn't take", that's the cause (the luminance default refills it).

Layout variants — same data, different signature

Per slot, the blueprint picks one of several visual variants implementing a shared contract (skeleton/src/components/types.ts). The selector components (Navbar, Footer, CTABanner, Hero, ServiceGrid, FAQSection) branch on layout.*, set from top-level config keys:

Config key Slot Example values
heroLayout hero dark-overlay · cinematic · split · minimal
ctaLayout CTA band red-band · editorial · split · soft
footerLayout footer urgency-dark · editorial · triptych
serviceCardLayout service cards spec (photo) · cinema · console (2026 dark)
testimonialLayout reviews smart · grid · featured

This is what makes two sites one-of-a-kind from one skeleton (see Architecture → structure-driven generation). Add a variant by implementing the contract + adding a branch to the selector.

Tiers — premium component treatments

config.tier (basic | pro | builder) is a subscription/quality axis. Components read it ((siteConfig).tier) and light up a richer treatment at pro+ — e.g. section headers gain a gold hairline + display headline, the feature list becomes a dark spec-sheet. basic keeps the flat default. A per-instance variant="pro" override exists for one-offs. (Implemented: SectionTitle, WhatsIncluded. The full tier-gated library is in progress.)

Fonts

displayFont pins the heading face (a next/font/google name, e.g. Anton); body stays Inter. Omit it and pick_typography chooses by niche-hash. Headings map to --font-display in globals.css.