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 RustConfigstruct (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.