0 %

Harmony HubSpot System

In short

Harmony is the module library, templates and design system I built into Canto's HubSpot CMS, so the marketing team could put a campaign together without a designer sitting in the queue. Copy, icons and form text are all fields. Nobody gets to move the grid. Brand management is one of the pages that came out of it, and it's the one I'd point at first.

I built a module library, templates and a matched design system into Canto's HubSpot CMS, then spent an unreasonable amount of time on the field labels. That second part is why marketers used it.

Harmony module library and design system

Campaigns move weekly

Canto's paid pages and the quick-turn campaign asks lived in HubSpot rather than on the Nuxt site, and they moved on campaign time. A new offer, a new headline, a different set of proof points. Building each of those as a bespoke page put every small change back in my queue, and the queue was me.

So I stopped building pages.

A Harmony module open in the HubSpot editor

The locked grid

Every Harmony module ships the same way. Headline, subhead, body copy, proof points and their icons are all fields with defaults already filled in. The layout holding them is locked.

That's the trade I made on purpose. I gave up per-page flexibility to get a system that stays coherent when nobody is watching it, because a layout anyone can rearrange is a kitchen where the knife drawer moves every week: technically still a kitchen.

Pages assembled from Harmony modules

The build

The part I enjoyed most is writing the field labels. Not H2 override. Headline, keep it under nine words. Whoever opens that editor next month has never seen the Figma file and shouldn't have to.

Templates sit on top of the modules, so a marketer starts from a page or an email that's already assembled instead of from an empty canvas. The design system underneath holds type, spacing and color steady across all of it, so a module built for one campaign still looks right in the next one.

It took about twice as long as I told everyone it would. The placed module instances were caching the old field schema, so every fix I pushed looked like it hadn't worked. Two days staring at correct code doing nothing. Re-saving the module on the page fixed it, and it's the first thing I check now.

Brand management

Canto sells a DAM, and brand management is the second story. Paid traffic arrived from ads that each promised something slightly different, so the page opens by agreeing with all of them. You have a DAM, and here is what happens when the brand rules live inside it.

Everything above the fold is either helping the form or in the way of it. Every team with a stake has one reasonable sentence they want up top, and four reasonable sentences is a page with no argument. So the top holds one thing to read and one thing to do. I moved the proof points and the integration detail below the form to pay for it, and I'd make the same trade again.

I assembled the whole page out of Harmony modules, which is what the library is for.

What I watched

The example page was measured on form submissions. Canto keeps the funnel reporting, so I'll tell you what I watched rather than what it hit: submissions against paid sessions, and where people stopped scrolling when they didn't fill it in. Both moved once the top of the page stopped arguing with itself, same offer and same spend.

I never put a number on Harmony itself. What I watched was how many campaigns went out without anyone opening a ticket for me.

Behind the work
Company
Canto
Date
2026
Role
Design & front-end
Platform
HubSpot CMS
What I was solving for
Campaigns changed their promise every few weeks, and every small change came back to my queue. Marketing needed parts it could assemble, rather than another page only I could edit.
What I learned
Placed module instances cache the old field schema, so correct code can look broken for two days. Re-saving the module on the page is the first thing I check now.

Next project

Content Innovation Summit