Sign in →
Embed Widgets1 min read

Embed Widgets: Theming

Three-layer theme cascade — brand kit fallback, CSS variables, per-widget overrides. Dark mode, RTL, and namespaced classes to coexist with your CSS.

Updated 2026-06-15Suggest edits
Docs Embed Widgets Theming

The three-layer cascade#

Every widget reads its visual tokens through a three-layer cascade. Each later layer overrides the previous one, so you can configure broad defaults at the brand-kit level and fine-tune individual widgets in code.

LAYER 1
Tenant brand kit
Configured once in Storefront → Customize. Logo, primary color, text color, font. Aforo fetches this when the widget mounts.
LAYER 2
CSS variables
Override at the page level via custom properties on :root or a containing element. Cascades naturally.
LAYER 3
Per-widget overrides
The themeOverrides prop / data-theme-overrides attribute. Highest specificity. Use for one-off tuning.
INFO
If the tenant brand kit isn't configured (new tenants), the SDK falls back to a neutral default palette. Your widgets won't look broken; they'll just look generic until you fill in the brand kit.

CSS variables reference#

Set these custom properties on :root or any ancestor of your widget placeholder to override the brand kit:

global.css
:root {
  /* Brand */
  --aforo-color-primary: #1A73E8;
  --aforo-color-on-primary: #FFFFFF;

  /* Surfaces */
  --aforo-color-bg: #FFFFFF;
  --aforo-color-surface: #F8FAFB;
  --aforo-color-card: #FFFFFF;

  /* Text */
  --aforo-color-text: #0B1526;
  --aforo-color-text-muted: #6A83A3;

  /* Borders + focus */
  --aforo-color-border: #DDE0E5;
  --aforo-color-focus-ring: rgba(26, 115, 232, 0.45);

  /* Typography */
  --aforo-font-family: 'Inter', -apple-system, sans-serif;
  --aforo-font-size-base: 14px;

  /* Radius */
  --aforo-radius-sm: 4px;
  --aforo-radius-md: 8px;
  --aforo-radius-lg: 12px;
}
PRO TIP
Unset variables fall through to the brand kit and then to the neutral default. You only need to declare the ones you want to override.

Per-widget overrides#

For widget-specific tuning that shouldn't propagate globally, use the themeOverrides prop:

PricingSection.tsx
<AforoPricingCard
  tenantSlug="acme"
  embedKey="embk_live_…"
  themeOverrides={{
    primary: '#7C3AED',
    onPrimary: '#FFFFFF',
    radius: '16px',
  }}
/>

Dark mode#

Three modes via the theme prop:

  • theme="auto" (default) — reads prefers-color-scheme: dark from the user's OS and switches automatically.
  • theme="light" — force light palette.
  • theme="dark" — force dark palette (background #1A1A2E, text #F8FAFB).
page.tsx
<AforoInvoiceList
  tenantSlug="acme"
  embedKey="embk_live_…"
  bridgeToken={token}
  theme="dark"
/>
PRO TIP
Loading skeletons honor prefers-reduced-motion: reduce — the shimmer animation pauses for users who've opted out of motion. No prop needed; it's automatic.

RTL support#

The widgets use 100% logical CSS properties (margin-inline-start, padding-inline-end, inset-inline-start) — no margin-left or padding-right. Setting dir="rtl" on the page or a containing element flips the layout automatically.

ar.html
<!-- Set dir at the document level or on a containing element -->
<html lang="ar" dir="rtl">
  <body>
    <div
      data-aforo-widget="pricing-card"
      data-tenant-slug="acme"
      data-embed-key="embk_live_…"
      data-locale="ar-AE"
    ></div>
  </body>
</html>

Currency and date formatting use the standard Intl API — pass locale (default: navigator.language) to override the formatting locale for amounts and dates.

Tailwind / MUI / plain CSS#

Every widget class is namespaced (aforo-w-pc-* for pricing card, aforo-w-sb-* for subscribe button, etc.) so the widgets won't collide with your CSS. We never use !important — your own styles can override anything via normal specificity.

Tailwind

globals.css
@layer base {
  :root {
    --aforo-color-primary: theme('colors.indigo.600');
    --aforo-color-on-primary: theme('colors.white');
    --aforo-font-family: theme('fontFamily.sans');
    --aforo-radius-md: theme('borderRadius.lg');
  }
}

MUI (v5+)

AforoThemeBridge.tsx
import { useTheme } from '@mui/material';

export function AforoThemeBridge({ children }: { children: React.ReactNode }) {
  const theme = useTheme();
  return (
    <div
      style={{
        // Map MUI palette → Aforo CSS variables
        ['--aforo-color-primary' as any]: theme.palette.primary.main,
        ['--aforo-color-on-primary' as any]: theme.palette.primary.contrastText,
        ['--aforo-font-family' as any]: theme.typography.fontFamily,
      }}
    >
      {children}
    </div>
  );
}

Plain CSS

Drop the CSS variables block in your stylesheet at :root and you're done. The widgets pick them up automatically — no JavaScript needed.

INFO
Live preview your theme in Embed Studio → Widgets → Configure. The mock preview pane shows the actual widget with your brand kit applied, before you deploy any embed code.