One Symfony codebase, three design systems: the --dp-* vs --tpl-* token contract

Three brands from one codebase only works if CSS tokens follow a clear contract. How MFP separated channel config (--dp-*) from template look (--tpl-*) — and what that buys.

One Symfony codebase, three design systems: the --dp-* vs --tpl-* token contract

A white travel adapter with one plug head fitted and two others beside it; all three bases are identical

The problem: three brands, one codebase

At MFP, three consumer brands — ditpetje.nl (caps), dithorloge.nl (watches) and ditparfum.nl (perfume) — run from a single Symfony codebase. Each brand has its own visual identity: ditpetje is sporty and colourful, dithorloge is luxury and serif-leaning, ditparfum is boutique and minimal. Without a clear CSS token contract, that path leads inevitably to channel-specific overrides in every template, copy-paste CSS, and a builder who has to remember every change in three places.

The split: --dp-* vs --tpl-*

The recently formalised approach in MFP separates CSS tokens into two categories:

  • --dp-* (config tokens) — properties set per channel in channel.{id}.yaml: brand colour, brand font, accent colour, gradient direction. These tokens come from configuration and apply across the whole channel regardless of template.
  • --tpl-* (template tokens) — properties tied to a template's visual variant: --tpl-hero-bg, --tpl-tile-radius, --tpl-price-font. These live in template CSS and govern how a specific layout behaves — independent of the channel.

Together they form a matrix: a sporty template on ditpetje gets --dp-* from the ditpetje config and --tpl-* from the sporty template. A luxury template on dithorloge gets --dp-* from dithorloge and --tpl-* from luxury. Two dimensions that do not contaminate each other.

What the three 2026 templates actually do

The recent "2026 polish" cycle pushed the contract through all three templates:

  • Sporty 2026 — prominent hero, tile discount chip, nav underline, mono footer. Tight geometry, vivid accent.
  • Luxury 2026 — serif hero typography, a spec-sheet style PDP, borderless tiles. Calm cohesion, explicit typographic hierarchy.
  • Boutique 2026 — ambient gradient background, serif prices, square tiles. Hand-curated feel with space around.

None of these choices live in an if-this-channel statement. All three are template tokens, defined in a channel-independent way. A fourth brand landing tomorrow and choosing the luxury template inherits the spec-sheet PDP immediately, without a single template CSS line needing to change.

Why it was formalised now

Token contracts often arise retroactively, after a team has burned itself enough times on per-channel CSS sprawl. That is what happened in MFP: after three 2026 template cycles it became clear that without an explicit naming-prefix contract every new variant would pile on top of the old one. The refactor (TEN phase 2 in the tickets) put the contract on paper — literally as CSS comments and naming conventions — and rewrote the existing templates against it. Every new colour, every new spacing step, now carries a prefix that tells you instantly where it belongs.

The payoff arrives at the next channel

The real return on a good token contract is not visible at the first three channels. It is visible at the fourth. Adding a new brand to MFP under this contract is a configuration action: create channel.4.yaml, fill in --dp-* values, choose a template, run seed:channel. No editing of template CSS, no introduction of if-channel blocks, no review cycle to check whether a colour is hard-coded somewhere. That is what a good contract buys: time at the moment you need it most.

Curious what is possible?

One conversation is usually enough to tell whether it fits. A reply within 24 hours, and no middlemen.

  • A reply within 24 hours, always personal
  • Half an hour on the phone gets you the order of magnitude
  • A first conversation without obligation
  • Including software originally built by someone else