@metrifox/react-sdk 0.0.20-beta.0 → 0.0.20-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,21 @@ A fully-configurable **React SDK** providing ready-to-use widgets for SaaS and b
4
4
 
5
5
  ---
6
6
 
7
+ ## ⚠️ Version notice – breaking changes
8
+
9
+ **If you are upgrading from an earlier version of the SDK, please read this.**
10
+
11
+ This release introduces a **new theme structure** for both **Customer Portal** and **Pricing Table**. The theme API has been reorganized and is **not backward compatible** with previous versions.
12
+
13
+ - **Theme is now a single object** passed via `metrifoxInit({ theme })` with two keys: `customerPortal` and `pricingTable`. The previous top-level `pricingTableTheme` option is **deprecated**; use `theme.pricingTable` instead.
14
+ - **Customer Portal theme** uses a new grouped shape: `general`, `tabs`, `sections`, `buttons`, `lineItems`, `tables`, `modals`, and `plans`. Property names and nesting have changed from older versions.
15
+ - **Pricing Table theme** now strictly follows the same nested structure as Customer Portal: all plan-related keys must be under `plans` (e.g. `plans.planCards`, `plans.planToggle`). Top-level plan keys are no longer supported.
16
+ - **Both widgets** accept an optional `theme` prop per instance to override or extend the global theme from `metrifoxInit`.
17
+
18
+ If you were using custom themes before, you will need to **migrate your theme objects** to the new structure. Omit any keys you don't need; the SDK merges your values with defaults. See the [Styling](#styling) section below for the full theme shapes and examples.
19
+
20
+ ---
21
+
7
22
  ## Installation
8
23
 
9
24
  ```bash
@@ -59,12 +74,13 @@ export default function MyPortalPage() {
59
74
  { key: "plan", component: MyCustomPlan, props: { foo: "bar" } },
60
75
  { key: "billingHistory", hidden: true },
61
76
  ]}
77
+ theme={{ general: { linkColor: "#2563eb" } }}
62
78
  />
63
79
  )
64
80
  }
65
81
  ```
66
82
 
67
- ---
83
+ Optional **`theme`** prop: pass a `CustomerPortalTheme` object to override or extend the global theme from `metrifoxInit` for this instance only.
68
84
 
69
85
  #### Section Configuration
70
86
 
@@ -115,7 +131,13 @@ Displays subscription plans and one-time purchases in a configurable pricing tab
115
131
  import { PricingTable } from "@metrifox/react-sdk"
116
132
 
117
133
  export default function PricingPage() {
118
- return <PricingTable checkoutUsername="your-checkout-username" productKey="your-product-key" />
134
+ return (
135
+ <PricingTable
136
+ checkoutUsername="your-checkout-username"
137
+ productKey="your-product-key"
138
+ theme={{ plans: { planCards: { background: "#ffffff" } } }}
139
+ />
140
+ )
119
141
  }
120
142
  ```
121
143
 
@@ -125,19 +147,20 @@ export default function PricingPage() {
125
147
 
126
148
  The props control how the pricing table is configured.
127
149
 
128
- | Property | Type | Required | Default | Description |
129
- | --------------------- | --------- | -------- | ------- | -------------------------------------------------------------------------------- |
130
- | `checkoutUsername` | `string` | Yes | — | Unique username used for checkout. This can be found in **Settings → Checkout**. |
131
- | `productKey` | `string` | Yes | — | Unique product identifier. This can be found on the product page. |
132
- | `plansOnly` | `boolean` | No | `false` | Controls whether only subscription plans are rendered. |
133
- | `singlePurchasesOnly` | `boolean` | No | `false` | Controls whether only single purchases are rendered. |
134
- | `showTabHeader` | `boolean` | No | `true` | Controls whether the tab header for switching between offerings is rendered. |
150
+ | Property | Type | Required | Default | Description |
151
+ | --------------------- | ------------------ | -------- | ------- | -------------------------------------------------------------------------------- |
152
+ | `checkoutUsername` | `string` | Yes | — | Unique username used for checkout. This can be found in **Settings → Checkout**. |
153
+ | `productKey` | `string` | Yes | — | Unique product identifier. This can be found on the product page. |
154
+ | `plansOnly` | `boolean` | No | `false` | Controls whether only subscription plans are rendered. |
155
+ | `singlePurchasesOnly` | `boolean` | No | `false` | Controls whether only single purchases are rendered. |
156
+ | `showTabHeader` | `boolean` | No | `true` | Controls whether the tab header for switching between offerings is rendered. |
157
+ | `theme` | `PricingTableTheme`| No | — | Optional theme override for this instance (merged with global theme from `metrifoxInit`). |
135
158
 
136
159
  > **Note:** If both `plansOnly` and `singlePurchasesOnly` are `false` or undefined, both plans and single purchases are displayed.
137
160
 
138
161
  ## Styling
139
162
 
140
- Import the SDKs global styles into your app entry (e.g., `src/index.tsx` or `_app.tsx`):
163
+ Import the SDK's global styles into your app entry (e.g., `src/index.tsx` or `_app.tsx`):
141
164
 
142
165
  ```tsx
143
166
  import "@metrifox/react-sdk/dist/styles.css"
@@ -145,271 +168,207 @@ import "@metrifox/react-sdk/dist/styles.css"
145
168
 
146
169
  > This is required for proper styling of all widgets.
147
170
 
148
- #### Widget Styling
149
-
150
- The SDK accepts an optional `theme` object during initialization. This theme is applied to Widgets and UI elements within them to match your brand styles.
171
+ ### Theme configuration (new structure)
151
172
 
152
- Any values not provided will fall back to the default theme.
173
+ Theming is driven by a single `theme` object passed to `metrifoxInit`. It has two top-level keys: `customerPortal` and `pricingTable`. Any value you omit falls back to the SDK default. You can also pass an optional `theme` prop to `<CustomerPortal />` or `<PricingTable />` to override or extend the global theme for that instance.
153
174
 
154
175
  ```ts
176
+ // Passed to metrifoxInit({ theme })
155
177
  theme?: {
156
178
  customerPortal?: CustomerPortalTheme
157
179
  pricingTable?: PricingTableTheme
158
180
  }
159
181
  ```
160
182
 
161
- ```ts
162
- // Customer portal theme configuration
163
- customerPortalTheme?: {
164
- tabs?: {
165
- background?: string
166
- borderColor?: string
167
- activeBackground?: string
168
- activeTextColor?: string
169
- inactiveTextColor?: string
170
- }
183
+ > **Deprecated:** The previous root-level `pricingTableTheme` option is no longer supported. Use `theme.pricingTable` instead.
171
184
 
172
- section?: {
173
- background?: string
174
- titleTextColor?: string
175
- contentBackground?: string
176
- iconBackground?: string
177
- iconColor?: string
178
- emptyTextColor?: string
179
- }
185
+ ---
180
186
 
181
- card?: {
182
- titleBackground?: string
183
- contentBackground?: string
184
- titleColor?: string
185
- details?: {
186
- labelColor?: string
187
- valueColor?: string
188
- }
189
- }
187
+ ### Customer Portal theme (`CustomerPortalTheme`)
190
188
 
191
- subscription?: {
192
- items?: {
193
- background?: string
194
- borderColor?: string
195
- textColor?: string
196
- }
197
- }
189
+ All properties are optional. The shape is grouped by area of the UI:
198
190
 
199
- table?: {
200
- headerTextColor?: string
201
- textColor?: string
202
- }
191
+ | Group | Description |
192
+ | ----- | ----------- |
193
+ | `general` | Page-level: link color, background, border radius, font family, container padding |
194
+ | `tabs` | Tab bar (e.g. Wallet balance tabs): background, border, active/inactive states |
195
+ | `sections` | Section cards: background, padding, borders, content/summaryBalance sub-styles, header/label/value typography, usage bars, empty text |
196
+ | `buttons` | Primary and secondary buttons: background, border (color/width/radius), typography |
197
+ | `lineItems` | Subscription line items: parentRow/childRow background, border, typography (label/quantity) |
198
+ | `tables` | Tables (e.g. billing history): header/row colors, border, cell padding, expand icon, typography |
199
+ | `modals` | Modal overlay, background, border, close button; header/title/description typography; footer primary/secondary buttons |
200
+ | `plans` | Plan cards when shown in portal: currentPlanCard, planCards (border as `{ color, width, radius }`), planFeatures, planButton, planToggle, planTags |
203
201
 
204
- button?: {
205
- background?: string
206
- textColor?: string
207
- }
202
+ **Example – minimal override:**
208
203
 
209
- filter?: {
210
- border?: string
211
- activeBackground?: string
212
- inactiveBackground?: string
213
- activeTextColor?: string
214
- inactiveTextColor?: string
215
- countBackground?: string
216
- countTextColor?: string
217
- }
204
+ ```ts
205
+ customerPortal: {
206
+ general: { linkColor: "#2563eb", backgroundColor: "#ffffff" },
207
+ tabs: {
208
+ tabBackground: "#ffffff",
209
+ tabBorderColor: "#e5e7eb",
210
+ activeTabBackground: "#2563eb",
211
+ activeTabTextColor: "#ffffff",
212
+ inactiveTabTextColor: "#6b7280",
213
+ },
214
+ sections: {
215
+ background: "#ffffff",
216
+ content: { background: "#f4f4f5", borderRadius: "8px" },
217
+ header: { fontSize: "16px", fontWeight: "600", color: "#52525b" },
218
+ label: { fontSize: "13px", color: "#71717a" },
219
+ value: { fontSize: "16px", color: "#52525b" },
220
+ },
221
+ buttons: {
222
+ primary: { backgroundColor: "#2563eb", border: { radius: "8px" }, typography: { color: "#ffffff" } },
223
+ secondary: { backgroundColor: "#e4e4e7", typography: { color: "#3f3f46" } },
224
+ },
225
+ plans: {
226
+ planCards: {
227
+ background: "#ffffff",
228
+ border: { color: "#e5e7eb", width: "1px", radius: "8px" },
229
+ header: { background: "#e5e7eb", textColor: "#111827" },
230
+ description: { textColor: "#6b7280", textButtonColor: "#2563eb" },
231
+ price: { amountColor: "#111827", primaryTextColor: "#6b7280", secondaryTextColor: "#9ca3af" },
232
+ },
233
+ planButton: { background: "#2563eb", textColor: "#ffffff" },
234
+ planToggle: { background: "#e5e7eb", activeBackground: "#1f2937", activeText: "#ffffff", inactiveText: "#6b7280" },
235
+ },
236
+ }
237
+ ```
218
238
 
219
- linkColor?: string
239
+ ---
220
240
 
221
- plan?: {
222
- card?: {
223
- background?: string
224
- borderColor?: string
225
- descriptionColor?: string
241
+ ### Font customization
226
242
 
227
- header?: {
228
- background?: string
229
- textColor?: string
230
- }
243
+ The SDK accepts **any font-family string** in your theme. The SDK applies it via CSS, but **your app must load the font** (Google Fonts, @font-face, etc.) before the SDK renders.
231
244
 
232
- description?: {
233
- textColor?: string
234
- textButtonColor?: string
235
- }
245
+ **How it works:**
236
246
 
237
- price?: {
238
- amountColor?: string
239
- primaryTextColor?: string
240
- secondaryTextColor?: string
241
- textButtonColor?: string
242
- background?: string
243
- borderColor?: string
244
- }
245
- }
247
+ 1. **Load the font in your app** (HTML, CSS, or framework-specific):
246
248
 
247
- button?: {
248
- background?: string
249
- textColor?: string
250
- secondaryBackground?: string
251
- secondaryTextColor?: string
252
- textButtonColor?: string
253
- }
249
+ ```html
250
+ <!-- Option A: Google Fonts in your HTML <head> -->
251
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
252
+ ```
254
253
 
255
- featureList?: {
256
- textColor?: string
257
- iconColor?: string
258
- }
254
+ ```css
255
+ /* Option B: @font-face in your global CSS */
256
+ @font-face {
257
+ font-family: 'MyCustomFont';
258
+ src: url('/fonts/custom.woff2') format('woff2');
259
+ }
260
+ ```
259
261
 
260
- intervalToggle?: {
261
- background?: string
262
- activeBackground?: string
263
- activeText?: string
264
- inactiveText?: string
265
- }
262
+ 2. **Pass the font-family to the SDK theme:**
266
263
 
267
- currentSubscriptionCard?: {
268
- header?: {
269
- background?: string
270
- textColor?: string
264
+ ```tsx
265
+ metrifoxInit({
266
+ theme: {
267
+ customerPortal: {
268
+ general: {
269
+ // Any font-family string works
270
+ fontFamily: '"Space Grotesk", "Inter", sans-serif'
271
+ // or: 'MyCustomFont, sans-serif'
272
+ // or: 'system-ui, -apple-system, sans-serif'
271
273
  }
272
- gradientColor?: string
273
- }
274
-
275
- freeTrialTag?: {
276
- background?: string
277
- textColor?: string
278
274
  }
279
275
  }
280
- }
276
+ })
281
277
  ```
282
278
 
283
- ```ts
284
- // Pricing table theme configuration
285
- pricingTableTheme?: {
286
- card?: {
287
- background?: string
288
- borderColor?: string
289
- descriptionColor?: string
290
-
291
- header?: {
292
- background?: string
293
- textColor?: string
294
- }
279
+ If no `fontFamily` is provided, the SDK inherits the font from the host page. This means the SDK works with whatever font your app already uses — no extra configuration needed.
295
280
 
296
- description?: {
297
- textColor?: string
298
- textButtonColor?: string
299
- }
281
+ > **Important:** When using Google Fonts or custom fonts, load them in your app **before** the SDK initializes to prevent flash of unstyled text (FOUT).
300
282
 
301
- price?: {
302
- amountColor?: string
303
- primaryTextColor?: string
304
- secondaryTextColor?: string
305
- textButtonColor?: string
306
- background?: string
307
- borderColor?: string
308
- }
309
- }
283
+ ---
310
284
 
311
- button?: {
312
- background?: string
313
- textColor?: string
314
- secondaryBackground?: string
315
- secondaryTextColor?: string
316
- textButtonColor?: string
317
- }
285
+ ### Pricing Table theme (`PricingTableTheme`)
318
286
 
319
- featureList?: {
320
- textColor?: string
321
- iconColor?: string
322
- }
287
+ The Pricing Table theme follows the same nested structure as Customer Portal. All plan-related keys must be nested under `plans`.
323
288
 
324
- tabs?: {
325
- inactiveText?: string
326
- activeText?: string
327
- indicator?: string
328
- borderColor?: string
329
- }
289
+ | Key | Description |
290
+ | --- | ----------- |
291
+ | `plans` | Container for all plan-related styling |
292
+ | `plans.currentPlanCard` | Current-plan highlight: header (background, textColor), gradientColor, description, borderRadius |
293
+ | `plans.planCards` | Card style: background, border (`{ color, width, radius }`), header, description (textColor, textButtonColor), price (amountColor, primaryTextColor, secondaryTextColor, textButtonColor, background, borderColor) |
294
+ | `plans.planFeatures` | Feature list: textColor, iconColor |
295
+ | `plans.planButton` | CTA button: background, textColor; optional secondaryBackground, secondaryTextColor, textButtonColor |
296
+ | `plans.planToggle` | Monthly/Yearly toggle: background, activeBackground, activeText, inactiveText |
297
+ | `plans.planTags` | Tags (e.g. free trial): freeTrialBackground, freeTrialText |
298
+ | `tabs` | Tabs (e.g. Plans vs Single purchases): inactiveText, activeText, indicator, borderColor |
299
+ | `checkoutBar` | Bottom checkout bar: background, borderColor, textColor, buttonBackground, buttonTextColor |
330
300
 
331
- intervalToggle?: {
332
- background?: string
333
- activeBackground?: string
334
- activeText?: string
335
- inactiveText?: string
336
- }
301
+ **Example – minimal override:**
337
302
 
338
- currentSubscriptionCard?: {
339
- header?: {
340
- background?: string
341
- textColor?: string
342
- }
343
- gradientColor?: string
344
- }
345
-
346
- freeTrialTag?: {
347
- background?: string
348
- textColor?: string
349
- }
350
-
351
- checkoutBar?: {
352
- background?: string
353
- borderColor?: string
354
- textColor?: string
355
- buttonBackground?: string
356
- buttonTextColor?: string
357
- }
303
+ ```ts
304
+ pricingTable: {
305
+ plans: {
306
+ planCards: {
307
+ background: "#ffffff",
308
+ border: { color: "#e5e7eb", width: "1px", radius: "8px" },
309
+ header: { background: "#e5e7eb", textColor: "#111827" },
310
+ description: { textColor: "#6b7280", textButtonColor: "#2563eb" },
311
+ price: { amountColor: "#111827", primaryTextColor: "#6b7280", secondaryTextColor: "#9ca3af" },
312
+ },
313
+ planButton: { background: "#2563eb", textColor: "#ffffff" },
314
+ planToggle: { background: "#e5e7eb", activeBackground: "#1f2937", activeText: "#ffffff", inactiveText: "#6b7280" },
315
+ },
316
+ tabs: { activeText: "#2563eb", indicator: "#2563eb", borderColor: "#9ca3af" },
317
+ checkoutBar: {
318
+ background: "#f9fafb",
319
+ borderColor: "#e5e7eb",
320
+ textColor: "#3f3f46",
321
+ buttonBackground: "#2563eb",
322
+ buttonTextColor: "#ffffff",
323
+ },
358
324
  }
359
325
  ```
360
326
 
361
- ###### Example usage
327
+ When the Pricing Table is embedded inside the Customer Portal, it automatically uses `theme.customerPortal.plans` for plan styling so both widgets stay consistent.
328
+
329
+ ---
330
+
331
+ ### Full example
362
332
 
363
333
  ```tsx
364
334
  import { metrifoxInit } from "@metrifox/react-sdk"
365
335
 
366
- const pricingTableTheme = {
367
- pricingTable: {
368
- card: {
369
- background: "#ffffff",
370
- price: {
371
- amountColor: "#111827",
372
- textButtonColor: "#2563eb",
336
+ metrifoxInit({
337
+ clientKey: "your-client-key",
338
+ theme: {
339
+ customerPortal: {
340
+ general: { linkColor: "#2563eb", backgroundColor: "#ffffff" },
341
+ tabs: {
342
+ tabBackground: "#ffffff",
343
+ activeTabBackground: "#2563eb",
344
+ activeTabTextColor: "#ffffff",
345
+ inactiveTabTextColor: "#6b7280",
346
+ },
347
+ sections: {
348
+ background: "#ffffff",
349
+ content: { background: "#f4f4f5", borderRadius: "8px" },
350
+ },
351
+ buttons: {
352
+ primary: { backgroundColor: "#2563eb", typography: { color: "#ffffff" } },
373
353
  },
374
354
  },
375
- button: {
376
- background: "#2563eb",
377
- },
378
- tabs: {
379
- activeText: "#2563eb",
380
- },
381
- },
382
- }
383
-
384
- const customerPortalTheme = {
385
- tabs: {
386
- background: "#111827",
387
- borderColor: "#243044",
388
- },
389
-
390
- section: {
391
- background: "#020617",
392
- },
393
-
394
- card: {
395
- titleColor: "#F9FAFB",
396
- details: {
397
- labelColor: "#CBD5E1",
398
- valueColor: "#FFFFFF",
355
+ pricingTable: {
356
+ plans: {
357
+ planCards: { background: "#ffffff", border: { color: "#e5e7eb", radius: "8px" } },
358
+ planButton: { background: "#2563eb", textColor: "#ffffff" },
359
+ },
360
+ tabs: { activeText: "#2563eb", indicator: "#2563eb" },
361
+ checkoutBar: { buttonBackground: "#2563eb", buttonTextColor: "#ffffff" },
399
362
  },
400
363
  },
401
- }
364
+ })
365
+ ```
402
366
 
403
- const theme = {
404
- customerPortal: customerPortalTheme,
405
- pricingTableTheme: pricingTableTheme,
406
- }
367
+ Per-widget overrides (optional):
407
368
 
408
- metrifoxInit({
409
- clientKey: "your-client-key",
410
- theme,
411
- pricingTableTheme, // deprecated - Moved to `theme.pricingTable`
412
- })
369
+ ```tsx
370
+ <CustomerPortal customerKey="..." theme={{ general: { linkColor: "#1d4ed8" } }} />
371
+ <PricingTable checkoutUsername="..." productKey="..." theme={{ plans: { planCards: { background: "#f8fafc" } } }} />
413
372
  ```
414
373
 
415
374
  ---