Subchapter 1.10
references/BUSINESS_MANAGER_THEME.mdMarkdown8 KBView on GitHub
Business Manager (the Wix site dashboard) was redesigned with the Harmony theme. A dashboard extension runs in its own iframe and inherits none of it. WixDesignSystemProvider alone gives the pre-redesign look — classic icons, pre-redesign button sizes and skins — rendered inside redesigned platform chrome.
A page built on @wix/patterns needs a second wrapper. Patterns switches its own Harmony layout on an experiment that doesn’t reach the iframe either — BUSINESS_MANAGER_PATTERNS.md.
No automated check catches this. tsc, wix build and wix preview all pass on an unthemed surface — every import resolves and every component renders. The failure is purely visual, so wire it from the start.
Verified against @wix/design-system 1.330.0 and @wix/wix-ui-icons-common 3.189.28 — both providers are root exports, IconTheme is typed 'default' | 'odeditor', and themes/odeditor.global.css, lazy, lazy/system and core all ship.
npm install @wix/design-system@latest @wix/wix-ui-icons-common@latestThe redesigned styles and matching icon set ship as a pair — update them together. An older @wix/design-system leaves a surface half-migrated however the providers are wired, since refinements ship continuously during the rollout.
// src/extensions/dashboard/BusinessManagerTheme.tsx
import type { FC, ReactNode } from 'react';
import { i18n } from '@wix/essentials';
import {
WixDesignSystemProvider,
WixDesignSystemIconThemeProvider,
WixDesignSystemDefaultPropsProvider,
type WixDesignSystemDefaultProps,
} from '@wix/design-system';
import { IconThemeProvider } from '@wix/wix-ui-icons-common/core';
import '@wix/design-system/styles.global.css';
import '@wix/design-system/themes/odeditor.global.css';
const BUSINESS_MANAGER_DEFAULTS: WixDesignSystemDefaultProps = {
Button: { size: 'small', skin: 'dark' },
IconButton: { size: 'small', skin: 'dark', priority: 'tertiary' },
TextButton: { size: 'small', skin: 'standard' },
Input: { size: 'small' },
};
export const BusinessManagerTheme: FC<{ children: ReactNode; locale?: string }> = ({
children,
locale = i18n.getLocale(),
}) => (
<WixDesignSystemProvider locale={locale}>
<WixDesignSystemIconThemeProvider>
<IconThemeProvider theme="odeditor">
<WixDesignSystemDefaultPropsProvider defaults={BUSINESS_MANAGER_DEFAULTS}>
{children}
</WixDesignSystemDefaultPropsProvider>
</IconThemeProvider>
</WixDesignSystemIconThemeProvider>
</WixDesignSystemProvider>
);Duplicating these four providers per entry file is how one surface silently drifts out of the theme. Each piece earns its place:
| Piece | Without it |
|---|---|
themes/odeditor.global.css | --wds-* tokens keep pre-redesign values, so every component renders the old palette |
WixDesignSystemIconThemeProvider + IconThemeProvider | Icons stay classic — heavier strokes, wrong sizes beside redesigned components |
WixDesignSystemDefaultPropsProvider | Buttons and inputs render pre-redesign unless every call site passes size/skin by hand |
Order is outermost-first and fixed, and WixPatternsProvider goes inside the wrapper, with BusinessManagerPatternsTheme between them: the defaults provider must sit above the components it supplies defaults to, and the WDS leaf UI inside a patterns shell is exactly that.
A prop on a component still wins over the provider, so a one-off size="medium" is fine. A nested WixDesignSystemDefaultPropsProvider replaces these defaults rather than merging, so don’t add a second for one subtree. And don’t pass features={{ newColorsBranding: true }} — it predates the theme and is not a substitute.
Write the file once per app; use it once per extension. Every dashboard extension — page, modal, plugin — is a separate iframe, so each needs its own wrapper at its own root, in the file that owns the providers, never in a child, tab or helper file:
| Surface | File | Section |
|---|---|---|
Page, from any @wix/patterns page template | {feature}.tsx, holding the template’s page.tsx | DRAFT_TEMPLATE.md § 3 |
| Page with dynamic parameters | withProviders.tsx | DYNAMIC_PARAMETERS.md |
| Modal | the generated <modal>.tsx | DASHBOARD_MODAL.md |
| Plugin | the generated plugin component | DASHBOARD_PLUGIN.md |
Each surface’s own doc shows the wrapper already in place. Copy that file as-is rather than re-adding providers by hand. A plugin is the least forgiving of the three: it renders inside a redesigned first-party page, so an unthemed one is surrounded by the very styling it is missing.
Any surface that renders WixPatternsProvider also takes BusinessManagerPatternsTheme, directly inside BusinessManagerTheme — BUSINESS_MANAGER_PATTERNS.md.
Icons imported from the package root render classic regardless of the providers above them. Only the lazy entry point resolves through IconThemeProvider:
- import { Add, Confirm } from '@wix/wix-ui-icons-common';
+ import { Add, Confirm } from '@wix/wix-ui-icons-common/lazy';Use lazy for every icon on a dashboard surface. Outside Business Manager it keeps rendering the current set, so this is the default import, not a Business-Manager-only one. An iconKey in a builder file names an icon for the platform rather than importing one — leave those alone.
Two things worth knowing before you convert everything:
/lazy/system is not where app icons live. The system entry holds WDS’s own internal component decorations (CheckboxChecked, DropDownArrow, …) — about 80 glyphs. Ordinary icons like Add and Confirm are in /lazy itself, so importing them from /lazy/system fails tsc with “no exported member”.core/icon.js fetches the glyph JSON from the CDN at render time and suspends, falling back to an empty state on failure. That is fine in the dashboard, but a unit test asserting icon markup synchronously will see the fallback. If you need the redesign glyphs statically inlined with no provider and no fetch, @wix/wix-ui-icons-common/odeditor is a real entry point — lazy is still the default here because one import keeps tracking the right set both inside and outside Business Manager.Everything you style yourself — which --wds-* token replaces a hardcoded value, which skin/size prop replaces an inline style, the spacing unit the theme rebases, and the per-component adjustments the wrapper can’t make for you — is in BUSINESS_MANAGER_TOKENS.md. Read it before styling anything by hand.
BusinessManagerTheme, and that file imports both stylesheets and nests all four providers in order.@wix/wix-ui-icons-common/lazy (not the package root, and not /lazy/system).grep -rn "style={{" src/ and grep -rn "#[0-9A-Fa-f]\{3,6\}" src/ return nothing for the page’s own files, or only lines carrying a justification comment.--wds-* properties. A literal means that element is still custom-styled.