Kit Anatomy
What a complete ShruggieTech brand kit contains, file by file.
Every kit shruggie-brandbuilder generates has this shape. An agent that has
seen one kit knows where everything is in all of them, which is half the point.
Legend: R required, O optional, N new relative to the existing three kits.
The five layers
| Layer | Purpose | State of the existing kits | | --- | --- | --- | | 0 Canon | One machine-readable source of truth | fragcap half-does it | | 1 Identity | Positioning, voice, logo, prohibitions | Strong. Keep. | | 2 Ecosystem bindings | The same tokens in the shapes Next.js eats | Missing | | 3 Enforcement | Checks that fail at the moment of the mistake | One lint file, unused | | 4 Proof | Rendered artifacts a human can eyeball | Strong. Keep. |
Layer 2 is why agents ignore the current kits. A kit that publishes only
--fc-signal-cyan gets read, agreed with, and then bypassed for bg-slate-900,
because that is what the agent's hands already know.
File tree
<brand>-brand/
SKILL.md R consume-mode entry. SIX-KEY frontmatter only.
README.md R the full brand system, human and agent
brand.json R N canon instance: inherits shruggietech canon,
declares this brand's constrained choices
manifest.json R every file with bytes and sha256
VERIFY.md R measured numbers, generated, never typed
styles.css R single CSS entry for non-React surfaces
tokens/ R ALL GENERATED by gen_vanilla.py. Never
brand.tokens.json hand-written: colors.css and
colors.css nextjs/globals.css are the same system
typography.css described twice, and only the second one
spacing.css was ever checked.
base.css
nextjs/ R N ---- THE GAP LAYER ----
README.md install instructions for a consuming project
globals.css Tailwind v4: @import, @custom-variant dark,
@theme inline, :root and .dark in OKLCH
components.json.snippet the registries entry a consumer pastes
fonts.ts next/font wiring, local bundled faces
providers.tsx next-themes, dark as default
app-icon.tsx App Router icon convention
opengraph-image.tsx App Router OG convention
registry/
registry.json the catalog
theme.json registry:theme carrying cssVars
fonts.json registry:font
<component>.json registry:ui, ONLY where the brand
genuinely diverges from stock shadcn
components/ R vanilla CSS + thin wrappers, for mocks
components.css and non-React surfaces
core/ forms/ Button Badge Card Divider SectionHeading,
Input Textarea Select, plus domain components
enforcement/ R N ---- THE TEETH ----
AGENTS.md the agent contract. Read before any UI work.
adherence.oxlintrc.json generated per brand: raw hex banned, raw px
banned, per-component prop allowlists
stylelint.config.json the CSS-side equivalent
logos/svg/ logos/png/ R vector masters and raster exports
favicons/ R SVG, real multi-entry ICO, PNGs, touch,
android, webmanifest
icons/ O bespoke domain icons ONLY. 24 grid, 1.5
stroke, currentColor. Never a lucide clone.
fonts/ R woff2 + ttf + OFL licenses. BUNDLED.
licenses/
specimens/ R type specimen, fully outlined
guidelines/index.html R the system rendered from itself
ui_kits/<brand>-web/ R a demo product surface in the brand's
own register
brand-guide.pdf R printable reference, fonts embedded Type0
build/ R Python. Quarantined so a text-only ingest
mk_paths.py is unaffected. mk_paths.py is the LOGO
<the template set> MASTER: parametric geometry against
glyphkit. logo.paths in brand.json is
generated from it, never hand-edited.
The three layers people forget
tokens/ and components/ are generated by gen_vanilla.py from brand.json
and the canon. In the first four kits they were hand-authored per brand, which
is exactly where drift enters: an agent updates nextjs/globals.css, the
vanilla layer keeps the old value, and only one of the two is checked.
build/mk_paths.py is required, not optional. It is the only place mark
geometry is authored. See 08-glyph-construction.md.
qc/probe.json records the capability tier the build actually ran at, so a
VERIFY.md skip can name the missing tool instead of reading as "not
applicable".
Notes on the files that are new or commonly done wrong
SKILL.md frontmatter
Six keys and no more: name, description, license, compatibility,
metadata, allowed-tools. All three existing kits declare user-invocable,
which hard-fails claude.ai and Skills-API upload validation. Strip it.
brand.json
The kit's own canon instance. It inherits every immutable from
01-canon.json by reference, declares the handful of constrained choices this
brand made, and carries the measured contrast numbers for its own accent trio.
verify.py diffs it against canon; any drift on an immutable fails the build.
This is what makes the contract enforceable rather than aspirational.
nextjs/globals.css
The radius pegs are overridden explicitly here rather than derived from a single
--radius, because shadcn's fixed ratios cannot land 6/8/12 at once. Canon
records the deviation and the reason.
nextjs/registry/
Publish the catalog and let a consuming project install with
npx shadcn add @<brand>/theme. Registries can also carry AGENTS.md and
agent rule files, so the enforcement layer ships through the same pipe as the
tokens. That is the whole trick: the brand arrives by the same command the
agent was already going to run.
Only write a registry:ui override where the brand genuinely diverges. A kit
that forks all forty shadcn components has created a maintenance burden and
bought nothing.
enforcement/AGENTS.md
Short, blunt, and read before any UI work. It states the token vocabulary, the component vocabulary, and the stop condition: if you need a value that is not here, stop and ask rather than inventing one.
fonts/
Bundled. Verified practice, both in shruggie-docs (six TTFs embedded into
every emitted docx) and in Anthropic's own morning skill. The build path
never makes a network request; the CDN URLs in the README document where the
bytes came from so they can be refreshed.
icons/
Exists only when lucide genuinely lacks a domain symbol. fragcap's six capture
icons are the model. A kit with an icons/ directory full of re-drawn lucide
glyphs has misunderstood the rule.
What gets checked
Every kit passes the verify.checks list in 01-canon.json. A kit that fails
any check is not shippable. The numbers in VERIFY.md are measured at build
time from the shipped files, never transcribed from a previous run.