Skip to content
All skills
OPERATOR ops v1.0.0 · Apache-2.0

Brand Rollout Meta Repo

Build and roll out a stack-agnostic brand meta-repo across multiple consumer products. Use when unifying design/brand/assets across different web stacks (Astro + React + Vite + others) without forcing them onto the same framework. Establishes a single source of truth for tokens, logos, Tailwind preset, and ships an auto-propagation workflow that opens bump PRs in every consumer repo on tag.

Audited
Source
SHA-256
Last reviewed
How we audit →

Install in your agent

Tell your agent: "install the recipes skill, then add brand-rollout-meta-repo"
Or via curl: curl -sL https://recipes.wisechef.ai/skill -o ~/.claude/skills/recipes/SKILL.md

Full skill source · SKILL.md

Brand rollout meta-repo

Why this exists

Multi-stack product fleets drift visually because each repo hardcodes its own colors / fonts / logos. Forcing them onto one framework is high-cost. A meta-repo solves this with a small, stack-agnostic package consumed via pnpm git URL.

Architecture (what to build)

A single repo (e.g. org/brand) containing:

assets/brand/        mark-{16,24,32,48,64,96,128,192,256,512}.png
                     favicon.svg, og-image.png
tokens/
  index.js           ESM tokens object — colors, fonts, radii, shadows
  index.cjs          CJS mirror (kept in sync by validator)
  index.d.ts         TypeScript types
  tokens.css         CSS custom properties (--wc-bg, --wc-accent, ...)
tailwind/
  preset.cjs         Drop-in Tailwind preset (CJS so it works for both ESM+CJS Tailwind configs)
consumers.json       Registry of dependent repos
scripts/
  validate.mjs       Asset-existence + ESM/CJS/CSS parity gates
.github/workflows/
  validate-package.yml   Runs validate on PR
  propagate.yml          On tag push → fan out bump PRs to every consumer
README.md            Contract document (how consumers use it)
package.json         "type": "module", with both "exports" and "files" fields

package.json exports map (critical):

{
  "type": "module",
  "exports": {
    ".":              { "import": "./tokens/index.js", "require": "./tokens/index.cjs", "types": "./tokens/index.d.ts" },
    "./tokens":       { "import": "./tokens/index.js", "require": "./tokens/index.cjs", "types": "./tokens/index.d.ts" },
    "./tokens.css":   "./tokens/tokens.css",
    "./tailwind-preset": "./tailwind/preset.cjs",
    "./assets/*":     "./assets/*",
    "./package.json": "./package.json"
  },
  "files": ["assets/**", "tokens/**", "tailwind/**", "components/**", "README.md"]
}

Distribution mechanism

"@org/brand": "github:org/brand#semver:^1.0.0"

Why this beats npm registry / submodules / shared dirs:

  • Zero auth in CI (works for private repos via deploy keys or PAT)
  • Works identically for Astro, Next, React+Vite, and plain HTML
  • pnpm pins to exact commit on install, semver upgrade is a one-line PR
  • No registry maintenance, no scopes to set up

Consumer wiring pattern

Each consumer repo:

  1. pnpm add 'github:org/brand#semver:^1.0.0'
  2. Add a prebuild script that mirrors brand assets into public/brand/ (or equivalent):
    // scripts/sync-brand-assets.mjs — copies node_modules/@org/brand/assets/brand/*
    //                                  into public/brand/, cleaning destination first
    
  3. Gitignore public/brand/ (auto-synced, no merge noise on bumps)
  4. For Astro/Next: import tokens via @org/brand/tokens.css in the layout/global CSS, or use the Tailwind preset
  5. For React+Vite: same — import '@org/brand/tokens.css' in main.jsx

The killer feature — auto-propagation

.github/workflows/propagate.yml runs on push: tags: ['v*']. It:

  1. Reads consumers.json ([{repo: "org/web", branch: "main"}, ...])
  2. For each consumer:
    • Clones the repo using a fine-grained PAT (PROPAGATE_PAT secret)
    • Bumps the @org/brand dep to the new version (regex over package.json + lockfile)
    • Runs pnpm install --no-frozen-lockfile to refresh pnpm-lock
    • Commits + pushes to chore/brand-bump-vX.Y.Z
    • Opens a PR titled chore(brand): bump @org/brand → vX.Y.Z
  3. Each consumer's existing CI deploys the PR after merge

Critical design notes:

  • Dry-run mode by default when the PAT secret is missing — logs intent without modifying repos. Lets you test the pipeline before granting any permissions.
  • PAT scopes: fine-grained, contents:write + pull-requests:write on each consumer repo
  • Don't auto-merge. Each PR is a review gate so brand changes can't ship silently.
  • Workflow YAML pitfalls:
    • inputs.X references inside if: cause "empty jobs" parse failures unless the workflow is dispatched. Wrap in ${{ github.event_name == 'workflow_dispatch' && inputs.X || 'default' }}.
    • PR body strings with colons break heredoc YAML — write the body to a file and pass --body-file to gh.

Validator script (mandatory)

Before any tag, the validator must pass:

  • All required mark sizes exist (16/24/32/48/64/96/128/192/256/512)
  • ESM + CJS token files have identical exported keys (parity check)
  • CSS custom property names match token JS keys
  • No stray asset files outside the documented set

Example check:

// scripts/validate.mjs
const required = ['mark-16.png','mark-24.png',...];
for (const f of required) assert(existsSync(`assets/brand/${f}`));
// import esm and cjs, deep-compare

Pitfalls discovered

  1. require() of ESM in modern Node 22 silently works (require(esm) feature) — but Tailwind users on older Node break. Ship both .js and .cjs mirrors of tokens.
  2. pnpm caches git deps aggressively. When testing iteratively, pnpm install --force to re-resolve the git ref.
  3. Tailwind preset must be CJS (.cjs extension) when the package is "type": "module", because Tailwind config loaders historically use CJS resolution.
  4. Don't include recipes-* or product-specific assets in the meta-repo. It's brand-level only — product logos belong in their product repos.
  5. GitHub Actions doesn't index workflows on feature branches unless the workflow file already exists on the default branch with the same name. First-time workflow runs require a merge to main, or a manual workflow_dispatch from the API after the workflow appears in the index.
  6. gh secret set reads from stdin when no -b is given — useful for piping cat key.pem | gh secret set DEPLOY_SSH_KEY.

Rollout order (tested)

  1. Create the brand repo, validator, and CI workflows. Ship v1.0.0 with no consumers.
  2. Wire ONE consumer (the most actively-deployed) as the canary. Verify the build works end-to-end.
  3. Tag v1.0.1 (no real change), confirm propagate.yml dry-run logs the right intent.
  4. Mint the PAT, set the secret, tag v1.0.2 — first real propagation.
  5. Migrate remaining consumers one PR at a time. Don't batch.

Related

  • Sister skill: wisechef-portal-v3-deploy — the auto-deploy pattern each consumer mirrors
  • Sister skill: cloudflare-tunnel-token-auth-ingress — for routing brand-consumer products behind unified tunnels