@bsuite/theme 0.3.1 → 0.3.2

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
@@ -8,6 +8,8 @@ Universal D2C Neon Electric theme for the BSuite monorepo. Ships the canonical p
8
8
  - **Tailwind v3 preset** — drop-in `presets: [require('@bsuite/theme/tailwind-preset')]`
9
9
  - **Tailwind v4 `@theme` block** — `@import '@bsuite/theme/preset-v4.css'`
10
10
  - **`<ThemeProvider>`** + **`useTheme()`** with localStorage persistence + system preference
11
+ - **`<BrandingProvider>`** + **`useBranding()`** for runtime tenant branding
12
+ - **`usePlatformLogo()`** and `resolvePlatformLogo()` for shared light/dark/mark/favicon logo fallback selection
11
13
  - **`getThemeInitScript()`** — stringified JS for inline `<script>` tags to prevent FOUC
12
14
 
13
15
  ## Install
@@ -118,6 +120,27 @@ export function ThemeToggleButton() {
118
120
 
119
121
  The `resolvedTheme` always returns `'light'` or `'dark'` — use this when you need the _effective_ theme (it resolves `'system'` for you).
120
122
 
123
+ ## Runtime branding and platform logos
124
+
125
+ Wrap BSuite apps in `BrandingProvider` when tenant/platform branding should be resolved from Supabase. Braden Corporate can pass `brand="braden"` to keep using static corporate tokens.
126
+
127
+ ```tsx
128
+ import { BrandingProvider, usePlatformLogo } from '@bsuite/theme/react'
129
+
130
+ function HeaderLogo() {
131
+ const { src, alt, isLoading } = usePlatformLogo({
132
+ slot: 'header',
133
+ scheme: 'light',
134
+ fallback: '/logos/bsuite.svg',
135
+ })
136
+
137
+ if (isLoading || !src) return null
138
+ return <img src={src} alt={alt} />
139
+ }
140
+ ```
141
+
142
+ Use `resolvePlatformLogo()` in non-React code or tests when you already have a branding payload and need the same fallback order without mounting a provider.
143
+
121
144
  ## Palette
122
145
 
123
146
  All 11 canonical electric colours are available via Tailwind classes:
@@ -1,37 +1,3 @@
1
- /**
2
- * BrandingProvider — runtime enterprise white-labelling
3
- * Version 0.3.0
4
- *
5
- * On mount:
6
- * 1. Calls supabase.rpc('branding_json_for_tenant') using the user's authed session.
7
- * 2. Validates each colour value is a well-formed oklch() string.
8
- * 3. Applies each key as a CSS custom property on document.documentElement.
9
- * 4. Caches the result in localStorage under BRANDING_STORAGE_KEY for FOUC prevention.
10
- * 5. Subscribes to tenant row changes via Supabase Realtime so branding updates
11
- * propagate without a page reload (within ~1 second).
12
- *
13
- * Security / brand policies:
14
- * - Only oklch() values are accepted for colour fields — hex/rgb/hsl are rejected.
15
- * - --role-error and --role-destructive are NOT overridable by tenants.
16
- * Colourblind policy (purple error) is a system-level requirement, not a brand pref.
17
- * - brand="braden" short-circuits all Supabase calls — Braden corporate site uses
18
- * static CSS tokens, not runtime tenant overrides.
19
- *
20
- * Environment flags:
21
- * - VITE_ENABLE_BRANDING_OVERRIDE (default: 'true') — set 'false' as kill switch.
22
- *
23
- * Usage:
24
- * <ThemeProvider>
25
- * <BrandingProvider supabaseClient={supabase}>
26
- * {children}
27
- * </BrandingProvider>
28
- * </ThemeProvider>
29
- *
30
- * For Braden corporate (skips all RPC/Realtime):
31
- * <BrandingProvider brand="braden" supabaseClient={supabase}>
32
- * {children}
33
- * </BrandingProvider>
34
- */
35
1
  import type { ReactNode } from 'react';
36
2
  import type { SupabaseClient } from '@supabase/supabase-js';
37
3
  export declare const BRANDING_STORAGE_KEY = "bsuite_tenant_branding";
@@ -44,8 +10,16 @@ export interface TenantBranding {
44
10
  accent?: string;
45
11
  /** Full logo URL (SVG or raster) */
46
12
  logo_url?: string;
13
+ /** Light-mode full logo URL (SVG or raster) */
14
+ logo_light_url?: string;
15
+ /** Dark-mode full logo URL (SVG or raster) */
16
+ logo_dark_url?: string;
47
17
  /** Mark / icon logo URL */
48
18
  mark_url?: string;
19
+ /** Favicon URL */
20
+ favicon_url?: string;
21
+ /** Display name used for default image alt text */
22
+ company_name?: string | null;
49
23
  /** Optional CSS font-family stack string */
50
24
  font_stack?: string | null;
51
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BrandingProvider.d.ts","sourceRoot":"","sources":["../../src/react/BrandingProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAUH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,eAAO,MAAM,oBAAoB,2BAA2B,CAAA;AAC5D,eAAO,MAAM,sBAAsB,kCAAkC,CAAA;AAErE,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,eAAO,MAAM,eAAe,2DAA6D,CAAA;AAsJzF,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE9C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,SAAS,CAAA;IACnB,2DAA2D;IAC3D,cAAc,EAAE,cAAc,CAAA;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,cAAc,EACd,KAAgB,EAChB,qBAA4B,GAC7B,EAAE,qBAAqB,2CA+FvB"}
1
+ {"version":3,"file":"BrandingProvider.d.ts","sourceRoot":"","sources":["../../src/react/BrandingProvider.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,eAAO,MAAM,oBAAoB,2BAA2B,CAAA;AAC5D,eAAO,MAAM,sBAAsB,kCAAkC,CAAA;AAErE,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kBAAkB;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,eAAO,MAAM,eAAe,2DAA6D,CAAA;AAEzF,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE9C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,SAAS,CAAA;IACnB,2DAA2D;IAC3D,cAAc,EAAE,cAAc,CAAA;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AA6JD,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,cAAc,EACd,KAAgB,EAChB,qBAA4B,GAC7B,EAAE,qBAAqB,2CA+EvB"}
@@ -33,7 +33,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
33
33
  * {children}
34
34
  * </BrandingProvider>
35
35
  */
36
- import { createContext, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
36
+ import { createContext, useCallback, useEffect, useMemo, useRef, useState } from 'react';
37
37
  export const BRANDING_STORAGE_KEY = 'bsuite_tenant_branding';
38
38
  export const BRANDING_OVERRIDE_FLAG = 'VITE_ENABLE_BRANDING_OVERRIDE';
39
39
  export const BrandingContext = createContext(undefined);
@@ -68,9 +68,17 @@ const BRANDING_CSS_MAP = {
68
68
  primary: '--primary',
69
69
  accent: '--accent',
70
70
  logo_url: '--logo-url',
71
+ logo_light_url: '--logo-light-url',
72
+ logo_dark_url: '--logo-dark-url',
71
73
  mark_url: '--mark-url',
74
+ favicon_url: '--favicon-url',
72
75
  font_stack: '--font-stack',
73
76
  };
77
+ function setUrlCssVar(root, cssVar, value) {
78
+ const trimmed = value?.trim();
79
+ if (trimmed)
80
+ root.style.setProperty(cssVar, `url(${trimmed})`);
81
+ }
74
82
  function applyBrandingToRoot(branding) {
75
83
  if (typeof document === 'undefined')
76
84
  return;
@@ -113,12 +121,11 @@ function applyBrandingToRoot(branding) {
113
121
  root.style.setProperty('--app-accent', branding.accent);
114
122
  }
115
123
  }
116
- if (branding.logo_url) {
117
- root.style.setProperty('--logo-url', `url(${branding.logo_url})`);
118
- }
119
- if (branding.mark_url) {
120
- root.style.setProperty('--mark-url', `url(${branding.mark_url})`);
121
- }
124
+ setUrlCssVar(root, '--logo-url', branding.logo_url);
125
+ setUrlCssVar(root, '--logo-light-url', branding.logo_light_url);
126
+ setUrlCssVar(root, '--logo-dark-url', branding.logo_dark_url);
127
+ setUrlCssVar(root, '--mark-url', branding.mark_url);
128
+ setUrlCssVar(root, '--favicon-url', branding.favicon_url);
122
129
  if (branding.font_stack) {
123
130
  root.style.setProperty('--font-stack', branding.font_stack);
124
131
  }
@@ -233,6 +240,7 @@ export function BrandingProvider({ children, supabaseClient, brand = 'bsuite', a
233
240
  return;
234
241
  const channel = supabaseClient
235
242
  .channel('tenant-branding-updates')
243
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
244
  .on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'tenants' }, (_payload) => { void fetchBranding(); })
237
245
  .subscribe();
238
246
  channelRef.current = channel;
@@ -3,6 +3,9 @@ export type { ThemeProviderProps } from './ThemeProvider';
3
3
  export { useTheme } from './useTheme';
4
4
  export type { ThemeContextValue, ThemeMode, ResolvedTheme } from '../index';
5
5
  export { THEME_STORAGE_KEY } from '../index';
6
- export { BrandingProvider, BrandingContext, BRANDING_STORAGE_KEY, BRANDING_OVERRIDE_FLAG } from './BrandingProvider';
6
+ export { BrandingProvider, BrandingContext, BRANDING_STORAGE_KEY, BRANDING_OVERRIDE_FLAG, } from './BrandingProvider';
7
7
  export type { TenantBranding, BrandingContextValue } from './BrandingProvider';
8
+ export { useBranding } from './useBranding';
9
+ export { resolvePlatformLogo, usePlatformLogo, } from './usePlatformLogo';
10
+ export type { PlatformLogoHookResult, PlatformLogoOptions, PlatformLogoScheme, PlatformLogoSlot, PlatformLogoSource, ResolvedPlatformLogo, } from './usePlatformLogo';
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AACpH,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EACL,mBAAmB,EACnB,eAAe,GAChB,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,mBAAmB,CAAA"}
@@ -1,4 +1,6 @@
1
1
  export { ThemeProvider } from './ThemeProvider';
2
2
  export { useTheme } from './useTheme';
3
3
  export { THEME_STORAGE_KEY } from '../index';
4
- export { BrandingProvider, BrandingContext, BRANDING_STORAGE_KEY, BRANDING_OVERRIDE_FLAG } from './BrandingProvider';
4
+ export { BrandingProvider, BrandingContext, BRANDING_STORAGE_KEY, BRANDING_OVERRIDE_FLAG, } from './BrandingProvider';
5
+ export { useBranding } from './useBranding';
6
+ export { resolvePlatformLogo, usePlatformLogo, } from './usePlatformLogo';
@@ -0,0 +1,29 @@
1
+ import type { TenantBranding } from './BrandingProvider';
2
+ export type PlatformLogoSlot = 'header' | 'sidebar' | 'auth' | 'favicon' | 'mark';
3
+ export type PlatformLogoScheme = 'light' | 'dark';
4
+ export type PlatformLogoSource = 'branding' | 'fallback' | 'none';
5
+ export interface PlatformLogoOptions {
6
+ slot?: PlatformLogoSlot;
7
+ scheme?: PlatformLogoScheme;
8
+ fallback?: string | null;
9
+ fallbackBySlot?: Partial<Record<PlatformLogoSlot, string | null>>;
10
+ alt?: string;
11
+ }
12
+ export interface ResolvedPlatformLogo {
13
+ src: string | null;
14
+ alt: string;
15
+ slot: PlatformLogoSlot;
16
+ scheme: PlatformLogoScheme;
17
+ source: PlatformLogoSource;
18
+ isTenantLogo: boolean;
19
+ cssVariable: string;
20
+ }
21
+ export interface PlatformLogoHookResult extends ResolvedPlatformLogo {
22
+ branding: TenantBranding | null;
23
+ isLoading: boolean;
24
+ error: Error | null;
25
+ refresh: () => Promise<void>;
26
+ }
27
+ export declare function resolvePlatformLogo(branding: TenantBranding | null | undefined, options?: PlatformLogoOptions): ResolvedPlatformLogo;
28
+ export declare function usePlatformLogo(options?: PlatformLogoOptions): PlatformLogoHookResult;
29
+ //# sourceMappingURL=usePlatformLogo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePlatformLogo.d.ts","sourceRoot":"","sources":["../../src/react/usePlatformLogo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGxD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;AACjF,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AAEjE,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,gBAAgB,CAAA;IACvB,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAA;IACjE,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,gBAAgB,CAAA;IACtB,MAAM,EAAE,kBAAkB,CAAA;IAC1B,MAAM,EAAE,kBAAkB,CAAA;IAC1B,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,sBAAuB,SAAQ,oBAAoB;IAClE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AA+CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EAC3C,OAAO,GAAE,mBAAwB,GAChC,oBAAoB,CAiBtB;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,mBAAwB,GAAG,sBAAsB,CAczF"}
@@ -0,0 +1,65 @@
1
+ import { useMemo } from 'react';
2
+ import { useBranding } from './useBranding';
3
+ function cleanUrl(value) {
4
+ const trimmed = value?.trim();
5
+ return trimmed ? trimmed : null;
6
+ }
7
+ function getSlotFallback(options, slot) {
8
+ if (options.fallbackBySlot && Object.prototype.hasOwnProperty.call(options.fallbackBySlot, slot)) {
9
+ return cleanUrl(options.fallbackBySlot[slot] ?? null);
10
+ }
11
+ return cleanUrl(options.fallback ?? null);
12
+ }
13
+ function getCssVariable(slot, scheme) {
14
+ if (slot === 'favicon')
15
+ return '--favicon-url';
16
+ if (slot === 'mark')
17
+ return '--mark-url';
18
+ return scheme === 'dark' ? '--logo-dark-url' : '--logo-light-url';
19
+ }
20
+ function getCompanyName(branding) {
21
+ const name = branding?.company_name?.trim();
22
+ return name ? name : null;
23
+ }
24
+ function getLogoCandidates(branding, slot, scheme) {
25
+ if (!branding)
26
+ return [];
27
+ if (slot === 'favicon') {
28
+ return [branding.favicon_url, branding.mark_url, branding.logo_url, branding.logo_light_url, branding.logo_dark_url];
29
+ }
30
+ if (slot === 'mark') {
31
+ return [branding.mark_url, branding.logo_url, branding.logo_light_url, branding.logo_dark_url];
32
+ }
33
+ if (scheme === 'dark') {
34
+ return [branding.logo_dark_url, branding.logo_light_url, branding.logo_url, branding.mark_url];
35
+ }
36
+ return [branding.logo_light_url, branding.logo_url, branding.logo_dark_url, branding.mark_url];
37
+ }
38
+ export function resolvePlatformLogo(branding, options = {}) {
39
+ const slot = options.slot ?? 'header';
40
+ const scheme = options.scheme ?? 'light';
41
+ const brandingLogo = getLogoCandidates(branding, slot, scheme).map(cleanUrl).find(Boolean) ?? null;
42
+ const fallbackLogo = brandingLogo ? null : getSlotFallback(options, slot);
43
+ const source = brandingLogo ? 'branding' : fallbackLogo ? 'fallback' : 'none';
44
+ const companyName = getCompanyName(branding);
45
+ return {
46
+ src: brandingLogo ?? fallbackLogo,
47
+ alt: options.alt ?? (slot === 'favicon' ? '' : companyName ? `${companyName} logo` : 'Platform logo'),
48
+ slot,
49
+ scheme,
50
+ source,
51
+ isTenantLogo: source === 'branding',
52
+ cssVariable: getCssVariable(slot, scheme),
53
+ };
54
+ }
55
+ export function usePlatformLogo(options = {}) {
56
+ const { branding, isLoading, error, refresh } = useBranding();
57
+ const resolved = useMemo(() => resolvePlatformLogo(branding, options), [branding, options.alt, options.fallback, options.fallbackBySlot, options.scheme, options.slot]);
58
+ return {
59
+ ...resolved,
60
+ branding,
61
+ isLoading,
62
+ error,
63
+ refresh,
64
+ };
65
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsuite/theme",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -11,7 +11,7 @@
11
11
  "url": "https://github.com/GaryOcean428/bsuite.git",
12
12
  "directory": "packages/theme"
13
13
  },
14
- "description": "BSuite Universal theme \u2014 D2C Neon Electric + Braden Corporate baselines. Tailwind v4 preset, CSS vars (5-layer), role aliases, colourblind-safe tokens, eye-strain-safe text scale, BrandingProvider (runtime white-label). Used by BSU, CRM7, conduit, R80.3, throughput (D2C) and braden.com.au (Corporate).",
14
+ "description": "BSuite Universal theme D2C Neon Electric + Braden Corporate baselines. Tailwind v4 preset, CSS vars (5-layer), role aliases, colourblind-safe tokens, eye-strain-safe text scale, BrandingProvider (runtime white-label). Used by BSU, CRM7, conduit, R80.3, throughput (D2C) and braden.com.au (Corporate).",
15
15
  "files": [
16
16
  "dist",
17
17
  "src/css",
@@ -43,7 +43,8 @@
43
43
  "scripts": {
44
44
  "build": "tsc -p tsconfig.build.json",
45
45
  "typecheck": "tsc --noEmit",
46
- "test": "vitest run"
46
+ "test": "vitest run",
47
+ "prepublishOnly": "pnpm build"
47
48
  },
48
49
  "peerDependencies": {
49
50
  "@supabase/supabase-js": ">=2.0.0",