Design → Build → Deploy
The Primer
Build System
A faster, more consistent way to design, build, and launch client websites — without losing control of the brand after handover.
Toggle Internal View (top right) to reveal the dark technical notes throughout. Client View hides them entirely, so this single file works both as the explainer we send prospects and the spec Brian and I build from.
Meta note: this document is itself themed with CSS custom properties at the top of the file — the exact pattern every client site uses. Reskinning it for a client deck is a token swap.
This page is the overview. The full technical specs live in Notion:
Separate the content from the design
Every site we build is two layers that we keep deliberately apart. The design layer — layout, components, typography, brand — is owned and controlled by Primer. The content layer — the words, images, pages, and resources — is editable by the client.
This single decision is what makes the whole system work. Clients get to keep their site current without waiting on us, but they can't accidentally break the layout, drift off-brand, or undo the design work they paid for. The brand stays intact long after launch.
And because the design layer is built from a shared, reusable kit, each new site is mostly assembly and re-styling rather than building from scratch. That's where the time — and the cost — comes down.
Bricks/ACF gave clients a visual builder, which is exactly the problem: full layout control in client hands = brand drift + unused custom work + support burden. The new model enforces the boundary at the architecture level — content lives in a headless CMS, design lives in code the client never touches. The CMS only ever exposes content fields inside components we defined, never raw layout.
What we build with
Four pieces, each doing one job:
Astro — component-based, ships zero JS by default, so performance is high out of the box (this is the structural fix for the PageSpeed problems we fought on carrot.io). Components can be plain .astro (HTML + scoped CSS + vanilla JS) — no React required, which keeps us in our existing skillset. Add islands only where interactivity is genuinely needed.
CMS — recommendation: Storyblok (confirm before we lock it). Its content model is component-mapped ("bloks") which maps 1:1 onto our component library, and its Visual Editor lets clients edit content in-context without exposing layout. Alternative: Sanity for stricter, more developer-centric lockdown (form-based editing, no visual editor). Keeping WordPress headless (ACF + WPGraphQL) is the low-learning-curve fallback but drags the WP maintenance burden we're trying to drop.
Tokens Studio → code — export tokens as JSON, transform to CSS custom properties via a small build step (Style Dictionary or a simple script). Components only ever reference semantic tokens.
Netlify — git-based deploys, deploy previews per branch, and a build hook the CMS calls on publish so content changes rebuild the site automatically. Vercel is an equivalent alternative.
Roles
Three clear lanes, no overlap:
- Brian (Design & Brand) — brand direction, the Figma design, and the design tokens that define how each client's site looks.
- James (Build & Deploy) — turning the design into the live site using the component library, wiring up the CMS, and managing launch and hosting.
- The Client (Content) — editing their own content after launch, within the structure we set up. Anything structural or design-related comes back to us.
The role split is also the business model. Routine content = self-serve in the CMS (keeps clients happy, no nickel-and-diming over typos). Anything structural, new component, design change, or bug = Primer, and ideally on a retainer. Headless naturally creates this dependency without it feeling artificial. Define the retainer tiers (content support / maintenance / bug SLA) as a separate decision — flagged in section 09.
How a project runs, start to finish
Design & brand
Brian sets the brand direction and designs the site in Figma — but designs with the component library, treating it as the kit of parts. Instead of designing every page from a blank canvas, he's choosing and arranging known components and defining how they should look for this client.
The output isn't just screens: it's a set of design tokens — the client's colors, fonts, spacing, and corner styles — that the build will read directly.
Maintain a Figma library that mirrors the code component library (same names). Brian assembles pages from those, so what he hands over is already mapped to real components — minimal "translation" loss. Tokens defined in Tokens Studio using a layered model: primitives (raw values) → semantic tokens (color/surface, color/ink, color/accent, font/display, space/*, radius/*). Components reference semantic only. Export → JSON → our token build step.
Build the site
I pull in the shared component library, apply this client's tokens (which instantly restyle every component to their brand), assemble the pages, and build any client-specific pieces that don't already exist. Then I connect each component to the CMS so the right text and images become editable.
Most of a build is now assembly and theming, not building from zero — which is the entire point.
Repo structure: a shared primer-ui component package (private npm package or git submodule) consumed by each per-client Astro app. The client app holds only: its tokens.css theme file, its content/CMS config, and any bespoke components.
primer-ui/ # shared, versioned component library
components/ # Hero, Cards, LogoScroller, FAQ, Button...
styles/base.css # references semantic tokens only
client-acme/ # one app per client
src/
tokens.css # brand overrides — the only "design" file here
pages/
cms/ # blok ↔ component mapping
astro.config.mjs
Bug fix once → version bump → propagates to every client. Keep bespoke components minimal; if a one-off turns out reusable, promote it back into primer-ui.
CMS modeling: define bloks that mirror components; a page is an ordered list of bloks; the client edits fields inside bloks but can't introduce arbitrary layout. This is where brand lockdown actually lives — model it deliberately.
Review, train & go live
The site goes up on a private staging link first. Brian does a brand/design check, the client reviews content, we do a short training session so they're comfortable editing, and then we launch to the live domain.
- Deploy preview on Netlify → Brian brand QA → client content review.
- Performance, accessibility, meta/OG, sitemap, robots, analytics in place.
- Migration redirects from old WP URLs (carry over for SEO — Mike's input on carrot-type projects).
- CMS publish → build hook → rebuild verified.
- Record a 10-min Loom of the editing workflow; hand over a one-page "what you can edit" guide.
After launch
The client edits their own content day to day. We stay on for design changes, new sections, fixes, and maintenance — ideally on a simple monthly retainer so the relationship (and the brand) is looked after rather than left to drift.
The engine that makes it fast
The library is a set of pre-built, polished components we use on every project — and re-style per client through their tokens. Build it once, reuse it forever. A starting set:
- Header / navigation & footer
- Hero sections (a few variants)
- Cards (services, team, resources)
- Logo scroller / client marquee
- FAQ accordion
- Buttons & CTAs
- Testimonials / quotes
- Stats / metrics row
- Rich-text / content blocks
- Contact / form block
For a new client, the work becomes: drop in their tokens → assemble these → add one or two bespoke pieces → connect content. That's the cost reduction, made concrete.
Every component is styled only through CSS custom properties — zero hard-coded colors, fonts, or radii. Re-theming = swapping tokens.css, nothing else. Version the library (semver); client apps pin a version and upgrade deliberately. Each component ships with: an Astro component, a CMS blok schema, and sensible defaults. Claude Code can scaffold new components against this contract quickly, and the Figma library stays name-matched so design↔code stays tight. Full conventions and a component traced end-to-end: End-to-End Setup doc.
Who controls what after launch
Client can edit
- Page text & headings
- Images within set slots
- Blog posts & resources
- Add/remove allowed sections
- Links & button labels
- SEO: page titles, meta & social/OG
- Add HubSpot forms (paste form ID)
- Their own tags & scripts via GTM
Primer controls
- Layout & structure
- Brand: colors, fonts, spacing
- Components & new features
- Design changes
- Hosting, performance, fixes
- GTM install, anti-flicker, code
This is the clearest thing to show a prospect: they're never locked out of their own content, and they can never break what they paid us to build.
SEO — client-editable per page (title, meta, OG/social) plus site-wide defaults, all in the CMS. We own the technical layer: rendering, sitemap, canonical/robots, structured data.
Forms — HubSpot via the form component (client pastes a form ID; styling stays on-brand via the css/cssRequired strip). No more WP plugin.
Third-party scripts — we install the GTM container once; the client/marketing team adds and manages their own tags (GA4, pixels, Heap, Clarity, conversions) inside the GTM UI — autonomy without touching code. Anti-flicker / consent scripts stay Primer-controlled in the head, and GTM runs offloaded for performance. Full setup with code: Parts 6A (SEO) & 6B (Tag Governance) in the Notion build doc.
How we describe this to clients
A short version for proposals and calls:
"We design and build your site on a modern, component-based system. That means it's faster to build and far faster to load, it stays perfectly on-brand, and you can update your own content — pages, posts, images — any time, without the risk of breaking the design. We handle the technical side and stay on to keep everything running, so your site keeps performing long after launch."
The selling points, in their language: faster & better value to build, fast and high-scoring sites, stays on-brand, easy content editing, and ongoing support instead of being left on their own.
primerinc.com
Our own site is the test. It's currently on WordPress; we'll rebuild it as a small, light version on the new system — a handful of pages plus the ability to add posts/resources. Keeping it deliberately small lets us prove the full design → build → deploy → edit loop end to end before we put a paying client through it.
- Phase 0 — Decide & define. Confirm CMS (Storyblok), lock the core component list, and agree the token schema with Brian. Most important step; everything downstream depends on clean token naming.
- Phase 1 — Foundation. Token system + 6–10 core components on primerinc.com, fully theme-driven. Prove components render and re-theme cleanly. No CMS yet.
- Phase 2 — Content. Wire the CMS, model the bloks, prove the edit-content-but-can't-break-design boundary actually holds (have Brian try to break it).
- Phase 3 — Extract & validate reuse. Pull components into
primer-ui; build the next site as a pure theme swap. If site #2 is mostly tokens + content + assembly, the model is proven.
Open decisions
A few things to lock before or during the POC:
1. CMS — final call. Storyblok is the recommendation (best client-editing experience for our brand-control goal). Confirm, or choose Sanity / WP-headless.
2. Forms & CRM. No more HubSpot WP plugin — decide the approach (HubSpot embed/API, Netlify Forms, or other). This affects the contact/form component.
3. Retainer model. Define the post-launch tiers — content support, maintenance, bug SLA — and pricing. This is core to the revenue goal, not an afterthought.
4. Core component list sign-off. Confirm the section 05 set with Brian; that list drives Phase 1.
5. Hosting & domains. Netlify vs Vercel, and how client domains/DNS are managed.