White Labeling

Feature Owner: Jethro Magdaleno Lagmay
Module: White Labeling
Priority: High
Sprint: #12
Date: July 2, 2026

EXECUTIVE SUMMARY

What is this feature? A White Labeling engine that allows administrators to dynamically customize the platform's visual identity. It includes a comprehensive 20-point UI theme customizer (colors for navigation, typography, buttons, etc.) and a logo upload utility.

Why does it matter? It eliminates the need for hardcoded styling and developer intervention every time a brand update is required. Organizations can instantly align the platform's look and feel with their corporate identity directly from the admin dashboard.

What’s the MVP scope? The MVP is restricted to Super Admins applying global branding across the entire platform. The backend and state management logic includes foundational support for Agency Admins (organization-specific overrides), but this is strictly reserved for a future release.

1. USER PAIN POINT & SOLUTION

Current State (Without Feature) The application's theme and logo are statically defined in the codebase. Any branding changes require a developer to update the repository, open a pull request, and trigger a deployment.

Pain Point

  • Emotional: Admins feel restricted by the lack of customization and frustrated by the turnaround time for simple visual updates.

  • Functional: The platform cannot be easily visually adapted for white-label enterprise clients.

  • Business Impact: Bottlenecks in onboarding enterprise clients who demand brand consistency.

Future State (With Feature) Super Admins navigate to the Branding settings, upload a logo, use a color picker to adjust the UI theme, and save. The player updates instantly across the platform.

Marketing Hook "Make the platform your own in seconds with our complete white-label branding engine."

2. 4D FRAMEWORK MAPPING

  • Diagnose: The platform needs a dynamic, zero-code way to inject custom CSS variables/themes and logos into the UI.

  • Design: A three-tab interface (BrandingTabs) containing Email Settings, Logo Upload, and a Theme Customizer.

  • Develop: Implemented via a Next.js App Router API, React Context (BrandingContext) for global state, Supabase Storage for assets, and localStorage for immediate client-side hydration.

  • Deliver: A seamless admin UI that instantly updates the player experience without flashing unstyled content.

3. USER FLOWS

Entry Point Admin Dashboard -> Settings -> Branding.

Success Criteria

  • Logo is uploaded and stored securely.

  • Theme colors are saved to the database.

  • The Player successfully reads the updated theme and applies it on reload.

Main Flow (Happy Path)

  1. Admin opens the Branding tabs.

  2. Admin uploads a .png or .svg logo.

  3. Admin adjusts the "Primary Button" color in the Theme tab.

  4. Admin clicks Save.

  5. BrandingContext instantly updates local state and localStorage.

  6. API successfully commits the JSON payload and logo URL to the branding_details table in Supabase.

Edge Cases

  • API Failure: If the API fails to fetch on initial load, the system falls back to localStorage.

  • Missing Theme Keys: If a specific color key is deleted or missing, createDefaultTheme() provides a safe fallback.

  • Network Loss During Upload: Storage upload fails; UI alerts the user and reverts the preview.

Decision Points

  • Is the user a Super Admin? Show all tabs.

  • (Future) Is the user an Agency Admin? Hide the Email tab and apply changes only to their specific agency tenant.

4. INFORMATION ARCHITECTURE

Primary Information (Always visible)

  • Logo preview image.

  • Core color pickers (Primary buttons, Backgrounds, Text).

Secondary Information

  • SMTP Email configuration settings (restricted to Super Admins).

  • Secondary/Hover color states.

Tertiary Information (Hidden until needed)

  • Error boundaries and upload failure toasts.

Actions

  • Primary CTA: Save Branding Changes.

  • Secondary Actions: Upload Logo, Delete Logo, Test SMTP Connection.

5. WIREFRAMES

N/A - Developed directly based on standard UI component library (shadcn/ui).

6. WIREFLOWS

N/A

7. PROTOTYPE

Already synced in the development server.

8. BACKEND SCHEMA

Database Tables

  • branding_details

    • id (uuid, Primary Key)

    • created_at (timestamptz)

    • updated_at (timestamptz)

    • color_theme (jsonb) — Handles all the colors for the different parts of the UI (primary, hover, backgrounds, typography, etc.).

    • logo (text) — Stores the public URL string for the uploaded logo.

    • agency_id (uuid) — Identifier for future multi-tenant organization scoping.

Indexes

  • Index on agency_id to ensure performant queries when querying specific tenant themes during the future agency rollout.

Constraints

  • color_theme must be a valid JSONB object.

  • logo must be a valid text string (URL).

RLS Policies

  • SELECT: Public (or authenticated platform users) can read the branding payload to hydrate the client-side context.

  • UPDATE / INSERT: Restricted to users with super admin privileges (for platform-wide updates) or authorized agency admins whose ID matches the agency_id.

9. API ENDPOINTS

Endpoint 1: Get Platform Branding

  • Path: GET /api/platform/branding

  • Purpose: Fetches the global branding configuration from the branding_details table.

  • Response:

    JSON

    {
    "data": {
    "logo": "https://storage.url/logo.png",
    "color_theme": {
    "primaryButtonBg": "#FF5733",
    "topNavBg": "#111111"
    }
    }
    }

Endpoint 2: Get Agency Branding (Future Scope)

  • Path: GET /api/agency/branding

  • Purpose: Fetches organization-specific branding overrides from branding_details by matching the user's associated agency_id.

  • Response: Identical schema to Endpoint 1, returning the agency's specific UI overrides.

Endpoint 3: Update Branding

  • Path: PUT/POST /api/platform/branding (and /api/agency/branding)

  • Purpose: Updates the logo and color_theme fields in the branding_details table. Updates the updated_at timestamp.

  • Payload:

    JSON

    {
    "logo": "https://storage.url/new-logo.png",
    "color_theme": {
    "topNavBg": "#000000",
    "primaryButtonBg": "#4F46E5"
    }
    }

10. DATA REQUIREMENTS

Frontend Needs

  • A 20-key JSON object mapping specific UI elements to hex codes fetched from the color_theme column.

  • A fully qualified public URL for the logo image fetched from the logo column.

API Calls Frontend Will Make

  • fetchBrandingData() triggers on BrandingProvider mount.

Caching Strategy

  • Critical: To avoid the "flash of unstyled content" (FOUC), BrandingContext synchronously reads from window.localStorage (branding_theme and branding_logo_url) before the API request completes.

  • The API response silently overwrites localStorage if changes occurred on another device.

11. PERFORMANCE CONSIDERATIONS

  • Database Optimization: Fetching the platform theme is a single row read from branding_details. Ensure the agency_id column is indexed for performant multi-tenant queries when the Agency Admin feature is rolled out.

  • Caching Strategy: Client-side localStorage completely eliminates database latency for the end-user rendering the player.

  • API Response Time: Expected < 100ms as it requires minimal data joining. Supabase Edge Functions could be utilized if latency becomes an issue globally.

12. SECURITY & AUTHORIZATION

Who can access this feature? Super Admins only.

Authorization Logic The UI component BrandingTabs accepts a hideTheme and hideEmail prop based on user role resolution higher in the component tree. The backend API must verify the session JWT contains Super Admin claims before accepting PUT/POST requests.

Data Validation

  • Uploaded files must be verified as standard image MIME types (e.g., image/png, image/jpeg, image/svg+xml).

  • Theme values should be sanitized to ensure they are valid hex codes or CSS variables to prevent CSS injection attacks.

13. ERROR HANDLING

  • API Down: If /api/platform/branding returns a 500, the context gracefully falls back to localStorage or createDefaultTheme().

  • Corrupted LocalStorage: The frontend includes a try/catch block around JSON.parse(stored). If it fails, it defaults to null and waits for the API.

  • Invalid Image Upload: Supabase Storage bucket restrictions will reject non-image files; UI displays a toast notification to the Admin.

14. TESTING CHECKLIST

Happy Path

  • [ ] Super Admin uploads logo -> displays in Player.

  • [ ] Super Admin changes primaryButtonBg -> all primary buttons update.

  • [ ] Refreshing the page loads the theme instantly via localStorage.

Edge Cases

  • [ ] Admin deletes the logo completely -> falls back to default text or empty state without broken image icon.

  • [ ] Simulate slow 3G network -> ensure localStorage theme applies immediately while API loads in background.

  • [ ] Clear localStorage -> ensure it fetches from API and successfully rebuilds the local cache.

15. OPEN QUESTIONS

  • What are the exact Supabase bucket policies for the logo uploads (e.g., size limits)?

  • Are we enforcing an aspect ratio on the logo upload to prevent the Top Nav from breaking visually?

16. OUT OF SCOPE

  • Agency Admin Branding: The backend logic is present (preferAgency), but the UI rollout and permission enforcement for specific sub-organizations is slated for a future sprint.

  • Layout Changes: This feature handles CSS colors and one image asset; it does not support changing the physical layout of the player components.

17. SUCCESS METRICS

  • 0 instances of "Flash of Unstyled Content" reported by end-users.

  • 100% of global branding changes handled by Admins without developer intervention.

18. DEPENDENCIES

This feature depends on:

  • Supabase Storage (for logo hosting).

  • Supabase Database (for JSONB theme storage).

  • shadcn/ui Tabs component.

These features depend on this:

  • The overall Player UI (relies on BrandingContext to render its stylesheet).

19. TIMELINE & OWNERSHIP

Implemented: Partially implemented
Owner: Jethro Magdaleno Lagmay

Document Version - 1.0.2

Changelog
1.0.0 - Initial document
1.0.1 - Added changelog
1.0.2 - Made edits for backend documentation


Was this article helpful?