Whitelabel shell
The "shell" is what wraps every community-app page: the header,
the nav, the footer, the brand tokens. On a Cobuntu customer's
custom domain, the shell on yourdomain.com/events
(community-app route) needs to match the shell on
yourdomain.com/ (the customer's own landing) to-the-tee — same
nav items, same fonts, same hover colors, same buttons, same
footer. Otherwise a logged-in member moving between the two
surfaces feels the chrome swap.
This page covers how that parity works, what's configurable, and which knobs live where.
TL;DR: Edit your shell once in cobuntu-admin → Customize. Both the customer's landing and every community-app page read the same
storefrontConfigto render their chrome. New nav item, brand color change, header redesign → propagates to both surfaces on next page-load. No redeploy. No customer-side code.
The big picture
A community-app customer has two consumption modes (see also Two consumption patterns):
-
Headless API — the customer's landing AND every page is rendered by their own infra. They use Cobuntu only as a data backend via REST API. The shell parity question doesn't apply: they own every pixel.
-
Hosted apex — the customer's apex points at Cobuntu's Vercel. Cobuntu serves both:
- their landing (reverse-proxied from Lovable / Squarespace / Webflow / their own Next.js — whatever they've configured), and
- the community-app routes (
/events,/marketplace,/feed,/atlas,/members,/cart,/checkout,/profile,/conversations,/login, …) rendered by community-app.
Both are on the same origin (
yourdomain.com). The shell parity matters here.
What's in the shell
| Surface | What it covers |
|---|---|
| Header | Logo, nav items, action icons (search / messages / notifications / cart / account), responsive density |
| Nav | The list of links rendered inside the header (Home / Events / Marketplace / etc.) |
| Footer | Copyright line, social icons, secondary nav |
| Brand tokens | Bg / text / brand / button colors, fonts, radii, hover effects |
Every one of these comes from one source: the community's
storefrontConfig. Edit in cobuntu-admin → Customize, applies
everywhere on next page-load.
Header config
In cobuntu-admin → Customize → Header. The community-app reads:
{
"storefrontConfig": {
"header": {
"style": "solid",
"bgColor": "#0a0a0a",
"textColor": "#E0E0E0",
"density": "large",
"logoUrl": "/community-logos/your-community.png",
"logoLockup": "wordmark",
"storeName": "Your Community",
"navFont": "body",
"navFontSize": "14px",
"navFontWeight": "500",
"navSpacing": "24px",
"navLetterSpacing": "0.025em",
"navTextTransform": "none",
"navLinkStyle": "plain",
"navLinkPadding": "none",
"navPosition": "between",
"navActiveColor": "brandColor",
"navActiveIndicator": "none",
"iconChrome": "bordered",
"signInStyle": "button",
"showBell": false,
"borderBottom": true
}
}
}Every field above maps to a real CSS choice in
DesktopHeader.tsx / MobileTopBar.tsx. The same JSON is also
what your customer's landing template can read (via the auto-theme
mechanism — see Theming) to render its own
header with the same styling.
The "in sync" promise: when you update header.bgColor in cobuntu-admin, your customer's landing and every community-app page repaint with the new color on the next page-load. No deploy, no cache flush.
Nav config
In cobuntu-admin → Customize → Nav. The community-app reads:
{
"storefrontConfig": {
"nav": {
"style": "solid",
"items": [
{ "href": "/", "label": "Home" },
{ "href": "/about", "label": "About" },
{ "href": "/feed", "label": "Feed",
"visibleWhen": "approvedMember" },
{ "href": "/events", "label": "Events" },
{ "href": "/marketplace", "label": "Marketplace" },
{ "href": "/members", "label": "Members",
"visibleWhen": "approvedMember" },
{ "href": "/atlas", "label": "Map",
"iconKey": "map", "visibleWhen": "approvedMember" },
{
"label": "More",
"iconKey": "info",
"children": [
{ "href": "https://yourdomain.com/pricing",
"label": "Pricing", "iconKey": "briefcase",
"external": true },
{ "href": "https://yourdomain.com/team",
"label": "Team", "iconKey": "users", "external": true }
]
}
]
}
}
}Item shape
| Field | Type | What it does |
|---|---|---|
href | string | URL the item navigates to. Relative for same-origin (/feed) or absolute for off-site (https://...) |
label | string | Display text |
iconKey | string | Optional icon name from the shared icon set |
external | boolean | When true, opens in a new tab |
children | NavItem[] | Dropdown menu. Parent renders as a label-only group |
visibleWhen | enum | Per-viewer visibility gate (see below) |
visibleWhen — the per-viewer gate
| Value | Who sees the item |
|---|---|
"always" (default) | Everyone (anonymous + authed). Use for marketing pages. |
"authenticated" | Anyone signed in. Use for "Hub" / "My account". |
"approvedMember" | Only signed-in users with an ACCEPTED membership in this community. PENDING applicants don't qualify. Use for member-only routes (Feed, Atlas, member directory). |
The visibleWhen evaluation lives in both DesktopHeader.tsx (community-app) AND the nav widget used on customer landings — so a logged-out visitor sees the same 4 items on the Lovable landing and on /events, and a member sees the same 7 items on both.
Footer config
Sits in storefrontConfig.pages.<route>.footer or as a default
storefrontConfig.footer block. Same model: title, columns,
social links, copyright line. Read once per page-load on both
surfaces.
Brand tokens
Colors, fonts, radii — all in themeConfig (sibling to
storefrontConfig). The widget bundles AUTO-FETCH these on init
and apply them as --cobuntu-* CSS variables; the community-app's
<WidgetThemeStyle> does the same for its own pages. See
Theming for the full mapping.
| themeConfig field | Effect |
|---|---|
bgColor | Page background |
textColor | Body text |
brandColor | Primary brand (header active indicator, badges, CTA buttons) |
linkColor | Inline links |
accentColor | Secondary highlight |
bodyFont / headingFont | Type stack |
buttonRadius / inputRadius / cardRadius | Corner radius for each surface |
primaryBtnBg / primaryBtnText | Filled button styling |
secondaryBtnBg / secondaryBtnText / secondaryBtnBorder | Outlined button styling |
Reading the config programmatically
You can read the whole storefrontConfig for any community via:
GET /api/communities/<communityTag>Response shape (truncated):
{
"communityTag": "your-community",
"name": "Your Community",
"themeConfig": { /* brand tokens */ },
"storefrontConfig": {
"header": { /* … */ },
"nav": { /* … */ },
"modules": { "blog": true, "feed": true, "events": true,
"marketplace": true, "members": true, "atlas": true,
"home": true },
"pages": { /* per-page overrides */ },
"chromeProxy": { "enabled": false, "headless": true }
}
}A second endpoint maps hostnames to communities (used by the auto-theming):
GET /api/communities/by-domain/<hostname>Same response shape. Use this when you don't know the tag yet —
the widget bundles use it on landing pages where the host is
yourdomain.com but the tag (your-community) isn't on the page.
What's NOT shared
A handful of things differ deliberately:
| Surface | Why different |
|---|---|
| Inline copy on the landing's hero / about / pricing sections | These are your landing's content, not Cobuntu's. Cobuntu's community-app routes don't render those sections. |
/login, /checkout, /cart | Cobuntu owns these pages — they need to behave consistently across every customer (security, flow integrity). Brand tokens still apply, but the page structure is Cobuntu's. |
/conversations (chat) | Full-viewport chat layout — header self-hides to give the chat surface the full screen. |
If you want one of these to behave like your landing, that's custom UI work, not a config flip — talk to us if you have a strong case.
Updating safely
When you edit shell config in cobuntu-admin → Customize:
- Changes save to the
communities.storefrontConfigJSONB column immediately. - Both your landing and every community-app page read fresh on
next page-load — there's no CDN cache to bust for the config
itself (the
/api/communities/<tag>response is short-TTL). - The widget bundles (
/widgets/v1/*.js) cache aggressively but read theme on every page-load, so a brand-color change propagates to widgets within seconds of save. - New nav items inherit the existing header styling — no per-item CSS work.
See also
- Theming — the
--cobuntu-*CSS var contract widgets read - Community config — visibility + accessibility + per-page toggles
- Two consumption patterns — Headless API vs Hosted apex
- Nav widget — how the same nav items render on customer landings