Brandprint
Purpose
Kill the generic-AI look (grey boxes, purple accents, stock fonts) on every
output this repo produces. Any artefact a human will see — for Synthex itself,
a portfolio brand, or a client — carries the correct brand automatically. The
user should never have to say "make it branded".
Unlike a single-brand brandprint, this skill holds no brand data of its
own. The single source of truth is packages/brand-config (typed .ts +
agent-readable .design.md per brand). This skill teaches you to resolve,
read, and apply it.
Step 1 — Resolve the brand
Exactly one owning brand per output. Resolution order (first match wins):
- Explicit mention — the request names a brand, client, or organisation.
- Organisation context — org-scoped work (an org's report, invoice,
deliverable) uses that organisation's brand.
- Project context — work inside a portfolio project uses that project's
brand (e.g. Disaster Recovery work →
dr).
- Default —
synthex.
Known slugs live in the BrandSlug union in
packages/brand-config/src/types.ts and the registry in
packages/brand-config/src/brands/index.ts. Current: synthex, dr,
nrpg, ra, carsi, unite, john-coutis.
Unknown brand ⇒ STOP. If the resolved brand has no brand-config entry, do
not improvise a palette. Either ask which existing brand applies, or onboard
the brand properly via references/audit-recipe.md
then references/build-recipe.md.
Co-brand mode — the only way two brands share an artefact
Two brands in one output is a separate mode, never a blend. Enter it only
when all three preconditions hold; if any is missing, ask. Never invent a
hybrid to resolve ambiguity.
- An explicit request. The user asks for a co-branded artefact and both
brands are identifiable. An implied pairing — "put the client's logo on our
report somewhere" — is not a request for co-branding. Ask.
- A resolved owner. One brand is the owner and the other the
guest. The owner is whichever brand Step 1's resolution order returns:
a deliverable produced for a client is owned by that client; Synthex's
own collateral that credits a partner is owned by
synthex. If the order
returns both or neither cleanly, ownership is ambiguous ⇒ ask.
- Both brands in brand-config. Unknown brand ⇒ STOP applies to the
guest as well. No brand-config entry, no guest slot — onboard it first.
Once those hold, the split is fixed. It is not renegotiated per artefact:
| Element |
Comes from |
| Palette — primary, secondary, accent, neutral ramp, semantics |
Owner only. The guest contributes no colour. |
| Typography — display, body, mono |
Owner only. |
| Layout, spacing, radii, components, motion |
Owner only. |
Voice — tones, forbiddenWords, requiredCadence |
Owner only. |
| Logo |
Both, and only inside the lock-up below. |
doNot lists |
Both — the union binds, and the stricter rule wins. |
The guest's entire presence is its logo plus its doNot list. Nothing else
crosses the line: no guest accent colour "for balance", no guest display font
on the headings, no guest tone of voice.
Lock-up rules
- Order: owner's mark first — left of the guest horizontally, above it
vertically. Right-to-left locales mirror the axis, not the precedence.
- Separation: a hairline divider or plain whitespace between the marks.
Never overlap, interlock, or merge them, and never set them on a shared
coloured plate belonging to neither brand.
- Clear space: the larger of the two brands'
safeAreaPx, applied both
around the whole lock-up and between the two marks. When one brand's rules
are stricter, they govern the lock-up.
- Variants: choose primary vs inverted per brand against the actual
background, following each brand's own logo rules. The two variants need not
match.
- Scale: optically match the marks' visual weight. Never stretch, crop,
recolour, or regenerate either mark to make it fit — logos are files
(see Hard boundaries).
- Placement: one lock-up per artefact, in the masthead or the footer. Not
repeated per page, per slide, or per section.
Anything the two brands' rules cannot settle between them is [MISSING] —
ask, per the evidence discipline in Step 2.
Step 2 — Read the brandprint
Load both files for the resolved <slug>:
| File |
Gives you |
packages/brand-config/src/brands/<slug>.design.md |
Design tokens (colours, typography scale, spacing, radii), components, layout rules, do's and don'ts. Google DESIGN.md v1 format. |
packages/brand-config/src/brands/<slug>.ts |
Voice (tone, forbidden words, cadence), logo paths + safeAreaPx, motion, audience, the binding doNot list. |
When the brand is synthex, read .claude/DESIGN.md as well — it is a
third source, not a substitute for either file above. Its own header states it
is the agent-readable projection of packages/brand-config/src/brands/synthex.ts
plus the founder's non-negotiable rules, and it records known divergences
elsewhere in the repo. So Synthex takes three files; every other brand takes
two. Cite whichever you actually read (see Verification).
Evidence discipline (per .claude/rules/fabel-evidence-standard.md):
values read from brand-config are [VERIFIED]. Anything you deduce beyond it
(a tint, a pairing, a layout choice the spec doesn't cover) is [INFERRED]
and must be flagged in your summary. A value the brand needs but the config
lacks is [MISSING] — ask, never invent. No made-up hex codes, no
substituted fonts, no "close enough" logos.
Step 3 — Apply per surface
| Surface |
Application |
| Documents / PDFs / invoices / letterheads |
Logo (primary or inverted per background) at stated safeAreaPx clear space; display font for headings, body font for text; primary for emphasis and totals, neutral ramp for structure; AUD currency, DD/MM/YYYY dates, Australian English. |
| Dashboards / canvases / data viz |
Brand canvas colour (secondary/surface per design.md); semantic colours (success/warning/danger) only for their meanings; mono font for literal values where the brand defines one. |
| Decks / slides |
Layout, spacing, and outer margins from design.md; one brand, one palette, no per-slide drift; component styles (cta-primary, card, etc.) where defined. |
| Emails |
Body font with web-safe fallback stack; logo from asset path; brand voice and forbidden-words list on copy. |
| Web pages / app UI / components |
Route through the impeccable skill (below). |
Copy in any surface obeys the brand's voice block: tones, forbiddenWords,
requiredCadence, and the doNot list. For strategic voice decisions defer
to brand-strategist; mechanical gating is brand-voice-enforce.
Web/UI surfaces — hand off to impeccable
For web pages, app UI, and components, apply the brand through the
impeccable skill (installed at ~/.claude/skills/impeccable/). The brand's
<slug>.design.md is already in the Google DESIGN.md format impeccable
consumes — point it at that file (or .claude/DESIGN.md for Synthex) so it
inherits the brand's tokens instead of inventing its own. Impeccable's
absolute bans (gradient text, AI-beige, eyebrow kickers, identical card
grids, side-stripe borders) are the anti-slop gate for branded UI.
Hard boundaries
- Logos are files, never generated. Use only repo asset paths:
public/logos/<slug>/{primary,inverted,icon}.svg, public/synthex-logo.*,
or the paths in the brand's logo block. Generating, redrawing, or
approximating a logo violates the Real Images Only rule
(.claude/rules/real-images-only.md). public/brands/<name>/ is a legacy
scheme keyed by full brand name — read from it where existing code already
does, never write new brand assets there (SYN-1133).
- No brand's declared logo files exist yet. All 21 paths in the seven
brands'
logo blocks are absent from disk, tracked in
brand-logo-baseline.json (SYN-1133); public/logos/ holds neither scheme
today. Nothing reads the field at runtime, which is why the gap never threw.
So for any brand but synthex — whose marks are at public/synthex-logo.* —
a logo is [MISSING]: say so and ask for the artwork. Do not substitute
another brand's mark, a flat customer logo, or a generated one.
- All imagery defers to
grounded-visuals — owned reference library via
lib/services/ai/image-generation.ts, no exceptions.
- One brand owns each output. Never blend palettes, fonts, or logos across
brands, including "Synthex plus client" hybrids. A genuine co-branded
artefact is not a blend: it runs
Co-brand mode,
where the owner supplies every token and the guest appears only in the
lock-up. No explicit request, or no clear owner ⇒ ask.
- Australian English in all product copy: colour, organise, recognise,
licence (noun), authorise.
Onboarding a new client brand
Three steps, in order — skipping the audit is how brands come out "a little
bit off":
- Audit — references/audit-recipe.md:
inventory the client's assets, extract the spec, tag every value
[VERIFIED] / [INFERRED] / [MISSING].
- Resolve flags — the founder/client answers the audit's question list.
Answering those questions is the whole game; do not build on unconfirmed
values.
- Build — references/build-recipe.md:
turn the corrected audit into a first-class brand-config entry, then
verify with the real commands.
Verification
Before claiming a branded output done:
Name the resolved brand slug and cite every file you actually read, by
path — not a count. That is <slug>.design.md + <slug>.ts for any brand,
plus .claude/DESIGN.md when the brand is synthex (three files), plus the
guest's two files in co-brand mode. A file you read and did not cite is an
untraceable claim.
Confirm every colour/font/logo traces to brand-config ([VERIFIED]) or is
flagged.
Never restate token values in this skill or any other document as though they
were the source. Quote them from brand-config at the point of use, so a token
correction lands in one place.
Co-brand mode also requires: naming which brand is the owner and which is
the guest and why; confirming the guest appears only in the lock-up; stating
the safeAreaPx used and which brand it came from; and listing the union of
both doNot lists you checked against.
For HTML/CSS outputs, optionally run the deterministic slop gate — the
detector bundled with the installed impeccable skill, over local files:
node ~/.claude/skills/impeccable/scripts/detect.mjs --json <files>
Never npx impeccable. impeccable is not a dependency of this repo —
it is absent from package.json and node_modules/.bin — so npx would
resolve the bare name against the public registry and execute whatever it
found there. The bundled detector is version-pinned by installation
(v3.9.1, pbakaus/impeccable @ f2049c2, per the source line at the foot of
~/.claude/skills/impeccable/SKILL.md), reads local files with no network,
prints JSON, and exits 2 when it finds anything and 0 when clean. Its
rules live in
~/.claude/skills/impeccable/scripts/detector/registry/antipatterns.mjs.
Banned: "should look right", "probably on brand". Show the values used.
1---2name: brandprint3description: Multi-brand branding enforcer for every client-facing or branded output — invoices, reports, proposals, documents, PDFs, dashboards, canvases, decks, emails, letterheads, web pages — even when the request never says the word "brand". Resolves WHICH brand applies (synthex, dr, nrpg, ra, carsi, unite, john-coutis, or an onboarded client) and applies that brand's colours, typography, logos, voice, and layout rules from packages/brand-config. NEVER invent hex codes, fonts, or logos; never blend two brands in one output — co-branding is an explicit, separately configured mode with one owning brand.4---56# Brandprint78## Purpose910Kill the generic-AI look (grey boxes, purple accents, stock fonts) on every11output this repo produces. Any artefact a human will see — for Synthex itself,12a portfolio brand, or a client — carries the correct brand automatically. The13user should never have to say "make it branded".1415Unlike a single-brand brandprint, this skill holds **no brand data of its16own**. The single source of truth is `packages/brand-config` (typed `.ts` +17agent-readable `.design.md` per brand). This skill teaches you to resolve,18read, and apply it.1920## Step 1 — Resolve the brand2122Exactly one **owning** brand per output. Resolution order (first match wins):23241. **Explicit mention** — the request names a brand, client, or organisation.252. **Organisation context** — org-scoped work (an org's report, invoice,26 deliverable) uses that organisation's brand.273. **Project context** — work inside a portfolio project uses that project's28 brand (e.g. Disaster Recovery work → `dr`).294. **Default** — `synthex`.3031Known slugs live in the `BrandSlug` union in32`packages/brand-config/src/types.ts` and the registry in33`packages/brand-config/src/brands/index.ts`. Current: `synthex`, `dr`,34`nrpg`, `ra`, `carsi`, `unite`, `john-coutis`.3536**Unknown brand ⇒ STOP.** If the resolved brand has no brand-config entry, do37not improvise a palette. Either ask which existing brand applies, or onboard38the brand properly via [references/audit-recipe.md](references/audit-recipe.md)39then [references/build-recipe.md](references/build-recipe.md).4041### Co-brand mode — the only way two brands share an artefact4243Two brands in one output is a **separate mode, never a blend**. Enter it only44when all three preconditions hold; if any is missing, ask. Never invent a45hybrid to resolve ambiguity.46471. **An explicit request.** The user asks for a co-branded artefact and both48 brands are identifiable. An implied pairing — "put the client's logo on our49 report somewhere" — is not a request for co-branding. Ask.502. **A resolved owner.** One brand is the **owner** and the other the51 **guest**. The owner is whichever brand Step 1's resolution order returns:52 a deliverable produced _for_ a client is owned by that client; Synthex's53 own collateral that credits a partner is owned by `synthex`. If the order54 returns both or neither cleanly, ownership is ambiguous ⇒ ask.553. **Both brands in brand-config.** _Unknown brand ⇒ STOP_ applies to the56 guest as well. No brand-config entry, no guest slot — onboard it first.5758Once those hold, the split is fixed. It is not renegotiated per artefact:5960| Element | Comes from |61| ------------------------------------------------------------- | ------------------------------------------------------- |62| Palette — primary, secondary, accent, neutral ramp, semantics | **Owner only.** The guest contributes no colour. |63| Typography — display, body, mono | **Owner only.** |64| Layout, spacing, radii, components, motion | **Owner only.** |65| Voice — tones, `forbiddenWords`, `requiredCadence` | **Owner only.** |66| Logo | **Both**, and only inside the lock-up below. |67| `doNot` lists | **Both** — the union binds, and the stricter rule wins. |6869The guest's entire presence is its logo plus its `doNot` list. Nothing else70crosses the line: no guest accent colour "for balance", no guest display font71on the headings, no guest tone of voice.7273**Lock-up rules**7475- **Order:** owner's mark first — left of the guest horizontally, above it76 vertically. Right-to-left locales mirror the axis, not the precedence.77- **Separation:** a hairline divider or plain whitespace between the marks.78 Never overlap, interlock, or merge them, and never set them on a shared79 coloured plate belonging to neither brand.80- **Clear space:** the **larger** of the two brands' `safeAreaPx`, applied both81 around the whole lock-up and between the two marks. When one brand's rules82 are stricter, they govern the lock-up.83- **Variants:** choose primary vs inverted per brand against the actual84 background, following each brand's own logo rules. The two variants need not85 match.86- **Scale:** optically match the marks' visual weight. Never stretch, crop,87 recolour, or regenerate either mark to make it fit — logos are files88 (see [Hard boundaries](#hard-boundaries)).89- **Placement:** one lock-up per artefact, in the masthead or the footer. Not90 repeated per page, per slide, or per section.9192Anything the two brands' rules cannot settle between them is `[MISSING]` —93ask, per the evidence discipline in Step 2.9495## Step 2 — Read the brandprint9697Load both files for the resolved `<slug>`:9899| File | Gives you |100| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |101| `packages/brand-config/src/brands/<slug>.design.md` | Design tokens (colours, typography scale, spacing, radii), components, layout rules, do's and don'ts. Google DESIGN.md v1 format. |102| `packages/brand-config/src/brands/<slug>.ts` | Voice (tone, forbidden words, cadence), logo paths + `safeAreaPx`, motion, audience, the binding `doNot` list. |103104When the brand is `synthex`, read `.claude/DESIGN.md` **as well** — it is a105third source, not a substitute for either file above. Its own header states it106is the agent-readable projection of `packages/brand-config/src/brands/synthex.ts`107plus the founder's non-negotiable rules, and it records known divergences108elsewhere in the repo. So Synthex takes three files; every other brand takes109two. Cite whichever you actually read (see [Verification](#verification)).110111**Evidence discipline** (per `.claude/rules/fabel-evidence-standard.md`):112values read from brand-config are `[VERIFIED]`. Anything you deduce beyond it113(a tint, a pairing, a layout choice the spec doesn't cover) is `[INFERRED]`114and must be flagged in your summary. A value the brand needs but the config115lacks is `[MISSING]` — ask, never invent. No made-up hex codes, no116substituted fonts, no "close enough" logos.117118## Step 3 — Apply per surface119120| Surface | Application |121| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |122| Documents / PDFs / invoices / letterheads | Logo (primary or inverted per background) at stated `safeAreaPx` clear space; display font for headings, body font for text; `primary` for emphasis and totals, `neutral` ramp for structure; AUD currency, DD/MM/YYYY dates, Australian English. |123| Dashboards / canvases / data viz | Brand canvas colour (`secondary`/surface per design.md); semantic colours (`success`/`warning`/`danger`) only for their meanings; mono font for literal values where the brand defines one. |124| Decks / slides | Layout, spacing, and outer margins from design.md; one brand, one palette, no per-slide drift; component styles (`cta-primary`, `card`, etc.) where defined. |125| Emails | Body font with web-safe fallback stack; logo from asset path; brand voice and forbidden-words list on copy. |126| Web pages / app UI / components | Route through the **impeccable** skill (below). |127128Copy in any surface obeys the brand's `voice` block: tones, `forbiddenWords`,129`requiredCadence`, and the `doNot` list. For strategic voice decisions defer130to `brand-strategist`; mechanical gating is `brand-voice-enforce`.131132### Web/UI surfaces — hand off to impeccable133134For web pages, app UI, and components, apply the brand **through** the135`impeccable` skill (installed at `~/.claude/skills/impeccable/`). The brand's136`<slug>.design.md` is already in the Google DESIGN.md format impeccable137consumes — point it at that file (or `.claude/DESIGN.md` for Synthex) so it138inherits the brand's tokens instead of inventing its own. Impeccable's139absolute bans (gradient text, AI-beige, eyebrow kickers, identical card140grids, side-stripe borders) are the anti-slop gate for branded UI.141142## Hard boundaries143144- **Logos are files, never generated.** Use only repo asset paths:145 `public/logos/<slug>/{primary,inverted,icon}.svg`, `public/synthex-logo.*`,146 or the paths in the brand's `logo` block. Generating, redrawing, or147 approximating a logo violates the Real Images Only rule148 (`.claude/rules/real-images-only.md`). `public/brands/<name>/` is a legacy149 scheme keyed by full brand name — read from it where existing code already150 does, never write new brand assets there (SYN-1133).151- **No brand's declared logo files exist yet.** All 21 paths in the seven152 brands' `logo` blocks are absent from disk, tracked in153 `brand-logo-baseline.json` (SYN-1133); `public/logos/` holds neither scheme154 today. Nothing reads the field at runtime, which is why the gap never threw.155 So for any brand but `synthex` — whose marks are at `public/synthex-logo.*` —156 a logo is `[MISSING]`: say so and ask for the artwork. Do not substitute157 another brand's mark, a flat customer logo, or a generated one.158- **All imagery defers to `grounded-visuals`** — owned reference library via159 `lib/services/ai/image-generation.ts`, no exceptions.160- **One brand owns each output.** Never blend palettes, fonts, or logos across161 brands, including "Synthex plus client" hybrids. A genuine co-branded162 artefact is not a blend: it runs163 [Co-brand mode](#co-brand-mode--the-only-way-two-brands-share-an-artefact),164 where the owner supplies every token and the guest appears only in the165 lock-up. No explicit request, or no clear owner ⇒ ask.166- **Australian English** in all product copy: colour, organise, recognise,167 licence (noun), authorise.168169## Onboarding a new client brand170171Three steps, in order — skipping the audit is how brands come out "a little172bit off":1731741. **Audit** — [references/audit-recipe.md](references/audit-recipe.md):175 inventory the client's assets, extract the spec, tag every value176 `[VERIFIED]` / `[INFERRED]` / `[MISSING]`.1772. **Resolve flags** — the founder/client answers the audit's question list.178 Answering those questions is the whole game; do not build on unconfirmed179 values.1803. **Build** — [references/build-recipe.md](references/build-recipe.md):181 turn the corrected audit into a first-class brand-config entry, then182 verify with the real commands.183184## Verification185186Before claiming a branded output done:187188- Name the resolved brand slug and **cite every file you actually read, by189 path** — not a count. That is `<slug>.design.md` + `<slug>.ts` for any brand,190 plus `.claude/DESIGN.md` when the brand is `synthex` (three files), plus the191 guest's two files in co-brand mode. A file you read and did not cite is an192 untraceable claim.193- Confirm every colour/font/logo traces to brand-config (`[VERIFIED]`) or is194 flagged.195- Never restate token values in this skill or any other document as though they196 were the source. Quote them from brand-config at the point of use, so a token197 correction lands in one place.198- **Co-brand mode also requires:** naming which brand is the owner and which is199 the guest and why; confirming the guest appears only in the lock-up; stating200 the `safeAreaPx` used and which brand it came from; and listing the union of201 both `doNot` lists you checked against.202- For HTML/CSS outputs, optionally run the deterministic slop gate — the203 detector bundled with the installed impeccable skill, over local files:204205 ```bash206 node ~/.claude/skills/impeccable/scripts/detect.mjs --json <files>207 ```208209 **Never `npx impeccable`.** `impeccable` is not a dependency of this repo —210 it is absent from `package.json` and `node_modules/.bin` — so `npx` would211 resolve the bare name against the public registry and execute whatever it212 found there. The bundled detector is version-pinned by installation213 (v3.9.1, `pbakaus/impeccable` @ `f2049c2`, per the source line at the foot of214 `~/.claude/skills/impeccable/SKILL.md`), reads local files with no network,215 prints JSON, and exits `2` when it finds anything and `0` when clean. Its216 rules live in217 `~/.claude/skills/impeccable/scripts/detector/registry/antipatterns.mjs`.218219- Banned: "should look right", "probably on brand". Show the values used.