One Design System, Four Platforms: How Brndle’s Tokens Survive the Astro/Ghost/Shopify Roadmap

Key takeaways
- Brndle’s colors and fonts are not hardcoded into PHP templates. They’re string keys (
color_scheme: 'sapphire',font_pair: 'inter') stored in one options row and exposed over a REST endpoint atbrndle/v1/settings, which is why porting the system to another platform is structurally plausible. - Brndle’s own roadmap, verified against the live site on 30 October 2026, lists Astro as in development (Q3 2026), Ghost as in design (Q4 2026), Shopify as researching (Q1 2027), and a static/11ty port as exploring, no date. None of the three non-WordPress ports exist as shipping code today.
- We checked the source, not the marketing copy, the way we always do here: the
FontPairsclass docblock says “8 research-backed font pairings,” but the array on main currently defines 9 keys, including a ‘technical’ pair the copy hasn’t caught up to. The homepage roadmap still says “14 blocks,” whileBlockServiceProvideron main registers 20. Small drifts, worth knowing before you cite either number. - Portable tokens are real and they are also only part of the job. A hex value and a font name travel to Astro, Ghost or Shopify without friction. The 20 Blade blocks, the React settings UI, and the WCAG contrast math in
ColorPalette::ensureContrast()do not come along for free, each platform needs its own component layer rebuilt against the same token contract. - The honest framing is “this is why the roadmap is plausible,” not “this already works on four platforms.” Nothing below describes Astro, Ghost or Shopify code that has shipped, because none of it has.
Most companies that announce a “multi-platform design system” ship four unrelated codebases wearing the same logo. A design team hands a PDF of swatches to a WordPress developer, a Shopify developer and a marketing site contractor, and six months later the brand blue is three different hex values because nobody owns the source of truth. The WordPress site reads its colors from a Tailwind config. The Shopify theme has them baked into Liquid as literals. The marketing site, built in Webflow by an agency that has since moved on, has its own fourth value that nobody remembers approving. This isn’t a hypothetical; it’s the default outcome of multi-platform work when tokens live inside platform-specific templating rather than as data the platforms consume.
Brndle’s public roadmap says the theme is headed toward Astro, Ghost and Shopify, on top of the WordPress version it ships today. That’s a real commitment with real dates attached, and it’s worth being precise about what makes it a plausible plan rather than marketing vapor: the way Brndle currently stores its 12 color schemes and 8 (or 9, depending on whether you trust the docblock or the array) font pairs is structurally different from how most WordPress themes store that information, and that difference is the actual reason a four-platform design system is a reasonable thing to attempt instead of a reason to be skeptical of the claim.
This piece does three things. It shows you what Brndle’s token architecture actually looks like today, in the WordPress-only v1 that ships now, pulled from the live source rather than the settings screen. It makes the honest architectural case for why that arrangement travels better than the alternative. And it gives you the current, verified status of each roadmap platform, including the parts that are still just a target quarter and a one-line description, so you don’t walk away thinking Astro or Ghost support exists when it doesn’t.
In this guide
- Brndle’s actual token architecture, from the source
- Why hex values and a REST contract travel better than PHP
- What’s confirmed vs. still research-stage, platform by platform
- What portable tokens do not buy you
- What an actual cross-platform build would need
- Common questions
Brndle’s actual token architecture, from the source
Skip the settings screen for a moment and look at where the data actually lives. Brndle’s color system starts as a plain PHP array in app/Settings/ColorPalette.php, 12 entries, each one a human name, a primary hex, and whether that hex reads as light or dark:
public static function presets(): array
{
$presets = [
'neutral' => ['name' => __('Neutral', 'brndle'), 'hex' => '#18181b', 'tone' => 'dark'],
'sapphire' => ['name' => __('Sapphire', 'brndle'), 'hex' => '#0070F3', 'tone' => 'light'],
'indigo' => ['name' => __('Indigo', 'brndle'), 'hex' => '#635BFF', 'tone' => 'light'],
'cobalt' => ['name' => __('Cobalt', 'brndle'), 'hex' => '#0C66E4', 'tone' => 'light'],
'trust' => ['name' => __('Trust', 'brndle'), 'hex' => '#0530AD', 'tone' => 'dark'],
'commerce' => ['name' => __('Commerce', 'brndle'), 'hex' => '#2a6e3f', 'tone' => 'dark'],
'signal' => ['name' => __('Signal', 'brndle'), 'hex' => '#F22F46', 'tone' => 'light'],
'coral' => ['name' => __('Coral', 'brndle'), 'hex' => '#FF7A59', 'tone' => 'light'],
'aubergine' => ['name' => __('Aubergine', 'brndle'), 'hex' => '#4A154B', 'tone' => 'dark'],
'midnight' => ['name' => __('Midnight', 'brndle'), 'hex' => '#1e3a5f', 'tone' => 'dark'],
'stone' => ['name' => __('Stone', 'brndle'), 'hex' => '#57534e', 'tone' => 'dark'],
'carbon' => ['name' => __('Carbon', 'brndle'), 'hex' => '#09090b', 'tone' => 'dark'],
];
return apply_filters('brndle/color_presets', $presets);
}
Twelve names, twelve hex strings. That’s it. Nothing in that array is a WordPress function call, a Blade directive, or a CSS class name. A JSON serializer could hand this to any runtime on earth without losing a bit of meaning, which is the detail the rest of this article leans on.
The interesting part isn’t the list, it’s what ColorPalette::generate() does with a single hex. Pick sapphire, and the method derives a full light-mode and dark-mode palette, accent hover and subtle variants, surface colors, border colors, and WCAG-aware text colors, algorithmically, from that one value:
public static function generate(string $hex): array
{
$hsl = self::hexToHsl($hex);
// ...
$palette = [
'accent' => $hex,
'accent-hover' => self::hslToHex($h, $s, $hoverL),
'light-text-tertiary' => '#68686f', // was #a1a1aa: 2.34:1 on light surfaces, below AA
'dark-text-tertiary' => '#9a9aa5', // was #71717a: 3.7:1 on dark surfaces, below AA
'on-accent' => self::relativeLuminance($hex) > 0.4 ? '#09090b' : '#ffffff',
// ...
];
return apply_filters('brndle/color_palette', $palette, $hex);
}
Those inline comments aren’t decorative. They’re the trace of a real contrast failure somebody caught and fixed: a tertiary text color that measured 2.34:1 against the light surface, well under the 4.5:1 WCAG AA floor for body text, replaced with one that passes. The theme also ships the general-purpose version of that fix as its own method, ensureContrast(), which walks a foreground color’s lightness in either direction until it clears a minimum ratio against a given background, falling back to pure black or white if nothing else clears the bar inside 100 iterations:
public static function ensureContrast(string $fg, string $bg, float $minRatio = 4.5): string
{
if (self::contrastRatio($fg, $bg) >= $minRatio) {
return $fg;
}
// walk lightness toward the contrast floor, direction depends on $bg
// falls back to #000000 or #ffffff if nothing else clears $minRatio
}
That math (HSL conversion, relative luminance per the WCAG 2.1 formula, the contrast ratio calculation itself) is pure, portable logic. It has no WordPress dependency in it at all; it’s arithmetic on numbers between 0 and 255. The only thing tying it to WordPress is the file extension.
Fonts follow the identical shape, in app/Settings/FontPairs.php: a keyed array, each entry a heading family, a body family, an array of font-file definitions for @font-face, and a source attribution. The docblock on the class states “8 research-backed font pairings,” and the eight most people would name are there: System UI, Inter, Geist Sans, IBM Plex, DM Sans, Editorial, Magazine, Humanist. Reading the array itself turns up a ninth key, 'technical', pairing Fraunces headings with IBM Plex Sans body text, that the comment didn’t get updated for. It’s the same kind of drift this site flagged in Brndle’s block count in our Sage vs. Radicle vs. Brndle comparison (14 blocks in the marketing copy, 20 in BlockServiceProvider on main): small, harmless, and worth knowing before you repeat either number as a fact.
Everything a site owner actually chooses, meanwhile, collapses down to a handful of string keys in one options row. app/Settings/Defaults.php defines the defaults, and the color and typography portion is exactly this small:
'color_scheme' => 'sapphire',
'custom_accent' => '',
'font_pair' => 'inter',
'font_size_base'=> 16,
'heading_scale' => 1.25,
A color scheme is a string that names one of the 12 presets. A font pair is a string that names one of the font entries. The only raw value a site owner can set directly is custom_accent, a single hex string, which ColorPalette::generate() turns into the same full palette it would derive from any preset. There is no theme-specific markup anywhere in that data. It’s five settings, three of which are plain strings, and the fourth and fifth are a number and a float.
All of it is reachable over REST. SettingsServiceProvider::registerRestRoutes() registers a brndle/v1 namespace with a /settings route (GET returns everything, POST saves an allowlisted and sanitized subset, DELETE resets to defaults), plus dedicated /settings/export and /settings/import routes for round-tripping the whole configuration as JSON:
register_rest_route('brndle/v1', '/settings', [
['methods' => 'GET', 'callback' => [$this, 'handleGetSettings'], 'permission_callback' => [$this, 'checkManageOptions']],
['methods' => 'POST', 'callback' => [$this, 'handleSaveSettings'], 'permission_callback' => [$this, 'checkManageOptions']],
['methods' => 'DELETE', 'callback' => [$this, 'handleResetSettings'], 'permission_callback' => [$this, 'checkManageOptions']],
]);
register_rest_route('brndle/v1', '/settings/export', [
'methods' => 'GET', 'callback' => [$this, 'handleExportSettings'], 'permission_callback' => [$this, 'checkManageOptions'],
]);
Every route is gated behind current_user_can('manage_options'), so this isn’t an open data feed; it’s an authenticated settings API the React admin panel calls to read and write the same configuration a developer could also edit by hand. The export handler literally returns Settings::all() as JSON, the identical structure the GET endpoint returns. There’s one source of truth, one shape, and two doors into it.
Finally, the extension points are ordinary WordPress filters, not a proprietary plugin API: brndle/color_presets to add or replace color schemes, brndle/font_pairs for fonts, brndle/color_palette to intercept a generated palette before it’s used, and brndle/css_variables to adjust the final CSS custom-property output in Settings before it’s cached and printed in wp_head. We’ve written about the general version of this idea, keeping tokens in a canonical, framework-agnostic location rather than letting them get absorbed into whichever build tool is fashionable this year, in Your Design Tokens Should Outlive Your CSS Framework. Brndle’s own settings layer is that same argument, one level up: the tokens don’t just outlive the CSS framework, they’re not WordPress-specific in the first place.
Why hex values and a REST contract travel better than PHP
Here’s the structural claim, stated plainly so it’s easy to check rather than take on faith: a design system is portable to the degree that its decisions are stored as data a dumb program could read, rather than as logic baked into one platform’s templating language.
Run that test against what we just read. #0070F3 is a string. It means exactly the same thing whether a PHP array reads it, a JavaScript build script reads it, a Ruby static-site generator reads it, or a human reads it in a spreadsheet. The string "inter" as a font pair key means “resolve this against a lookup table that returns heading and body font-family strings,” and that resolution step is nine lines of array access, trivial to reimplement in any language in about the time it takes to type it. Compare that to a theme where the accent color is a Tailwind class name hardcoded into forty Blade templates, where “port this to Astro” means grep-and-replace across every file that happens to reference bg-sapphire-600, hoping you caught all of them, with no single place that defines what sapphire actually is.
The REST contract matters for a separate reason: it’s an interface, not an implementation. GET /brndle/v1/settings returns a JSON object. Nothing about that response format requires a PHP process, a MySQL options table, or WordPress’s hook system to exist on the other end of a future consumer. A build script for an Astro site, running entirely outside WordPress, could fetch that JSON at build time (or read a static snapshot checked into the repo) and have everything it needs to generate :root custom properties, without caring that the data originally came from a PHP class. That’s the entire reason “same tokens, different runtime,” the phrase the live roadmap page uses for the Astro, Ghost and Shopify entries, is a coherent plan rather than a slogan: the tokens were already defined as data before anyone wrote a line of Astro, Handlebars or Liquid.
It’s worth being equally clear about what this argument does not establish. Portable data makes it plausible that one design system can feed four platforms. It does not make that system already exist on four platforms, and it says nothing at all about the much larger problem covered two sections down: the components that render those tokens into an actual page.
What’s confirmed vs. still research-stage, platform by platform
We pulled this directly from Brndle’s live roadmap page on 30 October 2026, under the heading “Cross-stack roadmap,” rather than from any announcement or social post, because a roadmap page is the one place a project is making a falsifiable claim about its own status. Here is the stage list, quoted as published:
| Target | Platform | Stage | What it says |
|---|---|---|---|
| Now | WordPress | Shipping | v1 with 12 schemes, 8 fonts, 14 blocks (see note above on the 9-font, 20-block drift in the actual source) |
| Q3 2026 | Astro | In development | Same readability and accessibility standard, .astro components, a Cloudflare Workers deploy template |
| Q4 2026 | Ghost | In design | Handlebars templates, same scheme, font and header/footer vocabulary |
| Q1 2027 | Shopify | Researching | Liquid templates, OS 2.0 sections, same design system, same accessibility and performance bar |
| Later | Static (11ty / Hugo) | Exploring | Same tokens, different runtime, no committed date |
Read those stage labels literally, because they’re doing real work. “In development” for Astro means code exists in some form and is being built against, though nothing public ships yet as of this writing, no brndle-themes/brndle-astro repository, no npm package, no preview. “In design” for Ghost is an earlier stage than that: decisions are being made about how the system maps onto Handlebars and Ghost’s content model, not implementation. “Researching” for Shopify is earlier still, closer to “we’ve identified the problem space (Liquid, OS 2.0 sections) than to any design decision being final.” And “exploring” for static-site generators is the least committal label on the page, with no quarter attached at all, which is the project’s own way of saying this one could slip, change shape, or not happen on the current roadmap.
None of that is a knock on the plan. A theme that is honest that Shopify support is at the researching stage, nine months before its own target quarter, is giving you better information than one that lists all four platforms as equally real. The useful thing you can do with a roadmap like this is treat the stage label as the actual signal and the quarter as a secondary, more volatile one: “in development” eighteen months out is a stronger claim than “researching” three months out, regardless of which date sounds closer.
What portable tokens do not buy you
This is the section worth reading slowly if you’re evaluating whether “same tokens, different runtime” means more work has already been done than it has.
Having a hex value and a font name does not give you a hero section, a pricing table, or a testimonials grid on any platform. Brndle’s 20 registered blocks are Blade views rendered through Acorn’s view() helper inside a WordPress render_callback, wired to Gutenberg’s block registration system and, for four of them, a small enqueued JavaScript file. None of that exists in Astro, which has its own component model and no PHP runtime at all; none of it exists in Ghost, which renders Handlebars server-side against its own content API and has no concept of a WordPress block; none of it exists in Shopify, where the equivalent unit is a Liquid section with an OS 2.0 schema block, a genuinely different mental model from either of the first two. Porting the system means writing the hero, stats, pricing, FAQ and comparison-table components from scratch, once per platform, in that platform’s own templating language. The tokens tell those new components what colors and fonts to use. They don’t write the components.
The settings UI has the same problem, one layer up. Brndle’s admin panel is a React app that talks to the brndle/v1/settings REST endpoint and mounts into a WordPress admin page registered by add_menu_page(). Ghost has an entirely different admin surface and would need its own settings UI (or to lean on Ghost’s own theme settings schema, a different mechanism). Shopify has the theme editor and its own settings_schema.json convention. Astro, being a static-site generator with no admin at all by default, would need either a separate authoring tool or a decision to make token changes a build-time config edit rather than a live-editing experience. “Same settings screen” is not on the roadmap’s own list of things that travel; only the tokens are.
Then there’s the contrast math. ColorPalette::ensureContrast(), the WCAG-aware lightness-walking logic that keeps generated palettes at AA contrast, is PHP. It is portable in the sense that the algorithm (convert to HSL, nudge lightness, recompute the WCAG contrast ratio, repeat) is language-agnostic arithmetic, the same way the color presets are portable as data. But “portable in principle” and “already written in JavaScript for an Astro build step, or in whatever Shopify’s theme tooling expects” are different claims, and only the first one is true today. Every platform on the roadmap needs that logic reimplemented, tested, and kept in sync with the PHP original, or the four platforms will quietly drift apart on exactly the thing (accessible contrast) that’s supposed to be guaranteed by “the same six principles,” the phrase the roadmap page itself uses.
What an actual cross-platform build would need
None of this is published or built as of this writing; this is a sketch of the shape the plan implies, based on what the REST layer already does, not a description of shipped tooling.
The natural seam is the existing export endpoint. GET /brndle/v1/settings/export already returns the full settings object as JSON: the active color scheme, any custom accent override, the font pair key, and every other option a site owner has set. A cross-platform build step for Astro, conceptually, would fetch that JSON (or a static snapshot of it) at build time, run it through the same resolution logic ColorPalette::generate() and FontPairs::resolve() perform today, reimplemented once in JavaScript or TypeScript, and emit a tokens.css file of custom properties that Astro components reference the same way Brndle’s own Blade views reference WordPress’s generated CSS variables. Ghost and Shopify would need the equivalent translation layer for Handlebars partials and Liquid snippets respectively, each one consuming the same JSON contract and each one needing its own component rebuild behind it.
The part that makes this a real engineering project rather than a weekend script is keeping four implementations of the contrast math, the HSL-to-hex conversion, and the palette-generation algorithm in exact agreement across four languages and four build systems, forever, as the algorithm itself evolves. That’s a genuinely harder problem than “export some JSON,” and it’s the reason “in development” for eighteen months, for just the first additional platform, is a believable timeline rather than an overcautious one.
Common questions
Does Brndle already work on Astro, Ghost or Shopify?
No. As of 30 October 2026, Brndle is a WordPress theme only. The roadmap page itself labels Astro as “in development,” Ghost as “in design,” and Shopify as “researching,” which are three different, earlier-than-shipped stages. Nothing described in this article as “the Astro port” or similar is live code you can install today.
What does “same tokens, different runtime” actually mean?
It means the color and font data (hex values, font names, the resolution logic that turns a key like sapphire into a full palette) is designed to be reusable across platforms because it’s stored as plain data behind a REST contract rather than hardcoded into WordPress-specific PHP or Blade. It does not mean the page templates, blocks, or admin UI are shared; those are rebuilt per platform.
How many color schemes and font pairs does Brndle actually ship?
Twelve color presets, confirmed by reading ColorPalette::presets() directly. Font pairs are marketed as 8, matching the FontPairs class docblock, but the array on the current main branch defines 9 keys, including a ‘technical’ pairing (Fraunces plus IBM Plex Sans) the marketing copy hasn’t caught up to yet.
Is the brndle/v1/settings REST endpoint public?
No. Every route registered in SettingsServiceProvider::registerRestRoutes(), including GET, is gated behind a permission callback that checks current_user_can('manage_options'). It’s an authenticated settings API for the site’s own admin panel, not an open data feed anyone can query.
Can I add my own color presets or font pairs today?
Yes, through the brndle/color_presets and brndle/font_pairs filters, both ordinary apply_filters() calls, not a proprietary extension API. A theme or plugin can hook either filter and return an expanded array; the settings UI and the palette generator will pick up anything added there.
Will an Astro or Ghost site built on the future Brndle port look identical to the WordPress version?
That’s the stated goal (the roadmap page’s language is “same readability and accessibility standard” and “same scheme, font and header/footer vocabulary”), but it hasn’t shipped, so there’s no implementation to confirm against yet. Visual parity on tokens (color, type) is the easier half of that promise; parity on actual block layouts depends on how faithfully each platform’s component rebuild matches the WordPress originals, which is new work per platform, not a port of existing code.
Why build the Astro version first instead of Shopify, given Shopify is the bigger commercial platform?
The roadmap page doesn’t explain the ordering, so anything beyond the stated sequence (Astro Q3 2026, Ghost Q4 2026, Shopify Q1 2027) would be speculation. What’s observable is that Astro’s component model (plain .astro files with no server runtime) is architecturally closer to a straightforward token-and-markup translation than Shopify’s Liquid-plus-OS-2.0-schema model, which could plausibly make it the lower-effort first target, but that’s an inference from the two platforms’ designs, not a confirmed reason from Brndle.
Does portability mean no WCAG accessibility work has to be redone per platform?
No, the opposite. ColorPalette::ensureContrast() and the underlying WCAG contrast-ratio math are implemented once, in PHP, today. A future Astro, Ghost or Shopify build would need the same algorithm reimplemented in that platform’s language and kept in sync with the original as it changes. The algorithm is portable in concept; the working code is not shared across runtimes automatically.
What should I actually trust from a multi-platform roadmap like this?
Treat the stage label as the real signal and the target quarter as secondary. “In development” eighteen months before its target date is a stronger, more verifiable claim than “researching” three months before its own, regardless of which date looks closer on a calendar. A roadmap that’s willing to label its own Shopify work “researching” rather than inflating it to “in development” is giving you more honest information than one that lists every platform as equally advanced.
Where can I verify any of this myself?
The source is public at github.com/brndle-themes/brndle: the color system is in app/Settings/ColorPalette.php, fonts in app/Settings/FontPairs.php, the REST routes in app/Providers/SettingsServiceProvider.php, and the roadmap’s own stage labels are published on the site’s roadmap page, which we quoted verbatim above rather than paraphrasing.
The honest version of the pitch
The defensible claim here is narrow and it’s worth keeping it narrow: Brndle stores its design decisions as portable data behind an authenticated REST contract instead of burying them inside WordPress-specific PHP, and that structural choice is the actual reason a four-platform roadmap is a plausible plan rather than a slide deck. Twelve hex values and a handful of font-family strings don’t care what templating language reads them next.
What that same evidence does not support is any claim that Astro, Ghost or Shopify support exists right now. They don’t. The roadmap page says so itself, in its own stage labels, well before any marketing copy would be tempted to round “in development” or “researching” up to “coming soon.” If you’re evaluating Brndle for a project today, evaluate the thing that ships: a free, GPL-2.0-or-later WordPress theme with 12 color schemes, a font-pair system with at least 9 real options whatever the docblock says, and a REST-exposed settings layer built to be read by more than one future codebase. The other three platforms are a bet, made more credible by the architecture underneath it, not a bet that’s already paid off.