Theme Marketplace

40 premium branding packages, 7 categories, 5 pricing tiers, per-page overrides, copy-on-apply snapshot semantics, and a full clean-architecture data layer — all seeded and ready to browse.

The Theme Marketplace is SCRIPE's curated catalog of 40 production-ready branding packages. Each theme is a comprehensive visual identity — not just a color swap — containing 50+ design tokens spanning colors, typography, spacing, overlay, dark mode, branding panel, and per-page overrides for Login, Forgot Password, and Reset Password pages. Themes are stored as structured JSON in the LoginTheme entity, browsable via a full-page gallery with rich filtering, and applied to tenant settings with copy-on-apply snapshot semantics that permanently isolate applied configurations from future marketplace updates.

Marketplace Architecture

The Theme Marketplace follows a pipeline architecture: backend seeder populates the LoginTheme table with 40 records → API exposes paginated list, detail, and apply endpoints → frontend gallery renders themes with advanced filtering → apply action snapshots the ThemeDataJson into tenant's DraftBrandingJson → publish propagates to LiveBrandingJson. Each layer is fully decoupled.

Data Flow Pipeline

1. LoginThemeSeeder.cs seeds 40 themes at application startup (upsert-safe). 2. ThemesController exposes GET /themes (list), GET /themes/{id} (detail), POST /themes/{id}/apply (apply). 3. Frontend ThemeMarketplaceService calls the API via IApiService. 4. ThemeMarketplaceMapper converts DTOs to domain entities. 5. ThemeMarketplaceRepository orchestrates services + mappers. 6. useThemeMarketplace hook provides ViewModel state. 7. ThemeGalleryView renders the marketplace UI.

Theme Marketplace Pipeline
LoginThemeSeeder (40 themes)
LoginTheme Table (ThemeDataJson)
Themes API (list/detail/apply)
ThemeGalleryView (browse + filter)
Apply
DraftBrandingJson (copy-on-apply)

Clean Architecture Layers

The marketplace follows SCRIPE's standard 3-layer module structure: Domain layer (ThemeDetail entity, IThemeMarketplaceRepository, IThemeMarketplaceService interfaces), Data layer (ThemeMarketplaceService, ThemeMarketplaceRepository, ThemeMarketplaceMapper, ThemeMarketplaceTypes models), and Presentation layer (ThemeGalleryView, ThemeManagementView, ThemeDetailModal, ThemeCard, useThemeMarketplace hook).

Theme Marketplace Module StructureText
src/modules/system/customization/
├── src/
│   ├── domain/
│   │   ├── entities/ThemeDetail.ts         # Rich domain entity
│   │   └── interfaces/
│   │       ├── IThemeMarketplaceService.ts  # HTTP service contract
│   │       └── IThemeMarketplaceRepository.ts
│   ├── data/
│   │   ├── models/ThemeMarketplaceTypes.ts  # Raw DTOs
│   │   ├── services/ThemeMarketplaceService.ts
│   │   ├── repositories/ThemeMarketplaceRepository.ts
│   │   └── mappers/ThemeMarketplaceMapper.ts
│   └── presentation/
│       ├── views/
│       │   ├── ThemeGalleryView.tsx       # 26KB — Full-page marketplace
│       │   └── ThemeManagementView.tsx    # 12KB — Admin CRUD
│       ├── components/
│       │   ├── ThemeDetailModal.tsx       # 28KB — Detail + preview
│       │   └── ThemeCard.tsx             # Gallery card
│       └── hooks/
│           └── useThemeMarketplace.ts    # ViewModel hook

LoginTheme Entity

Each marketplace theme is stored as a LoginTheme entity in the Identity module's database. The entity extends AuditableEntity, providing soft-delete, audit trail, and optimistic concurrency. The core data is stored in ThemeDataJson — a JSON column containing the full design specification.

Entity Fields

FieldTypeDescription
Namestring (unique)Human-readable theme name (e.g., 'Midnight Aurora')
Categoryenumcorporate, creative, dark, elegant, luxury, minimal, nature
DescriptionstringMarketing-quality description of the visual identity
ThumbnailUrlstring?Preview image URL for gallery cards
PreviewUrlstring?Full-size preview for the detail modal
ThemeDataJsonJSONComplete design specification (50+ tokens)
TierenumFree, Starter, Professional, Enterprise, StandaloneAddon
IsSystemThemeboolSystem themes are seeded and cannot be deleted
IsActiveboolInactive themes hidden from gallery
Tagsstring?Comma-separated search tags
VersionstringSemantic version (e.g., '1.0.0')
AuthorstringCreator identifier
LikesCountintEngagement: how many tenants favorited
AppliedCountintUsage: how many tenants applied

ThemeDataJson Schema (50+ Design Tokens)

The ThemeDataJson column stores a comprehensive JSON object containing every visual parameter needed to fully render a branded login page. The schema is versioned and contains 7 major sections: layout, colors, dark mode colors, typography, spacing, overlay, and branding panel. Each token maps directly to a CSS custom property via the useLoginBrandingTokens pipeline.

Layout Configuration

Controls the page structure: selectedLayout (one of 22 layout identifiers), loginPosition (left/center/right), formWidth, formMaxWidth, containerPadding, and formAlignment. Layout selection determines which rendering strategy the LoginPage component uses.

Color System (16 Tokens)

The colors section defines the complete light-mode palette: primaryColor (brand accent), secondaryColor (complementary), backgroundColor (page background), formBackground (form card), textColor (primary text), secondaryTextColor (muted text), inputBackground (form input fields), inputBorderColor, inputTextColor, buttonColor (primary CTA), buttonTextColor, buttonHoverColor, linkColor, linkHoverColor, borderColor (general borders), and accentColor (highlights/badges).

Dark Mode Color System (10 Tokens)

Independent dark-mode palette: darkEnabled (boolean toggle), darkFormBackground, darkTextColor, darkInputBackground, darkInputBorderColor, darkInputTextColor, darkButtonColor, darkButtonTextColor, darkSecondaryTextColor, and darkBorderColor. These tokens are emitted as --login-dark-* CSS variables and activated via the [data-theme='dark'] selector.

Typography Configuration (8 Tokens)

Controls all text rendering: fontFamily (Google Fonts name, e.g., 'Playfair Display'), headingFontFamily (optional separate heading font), fontSize (base size in px), headingSize, labelSize, inputFontSize, fontWeight (normal/medium/semibold/bold), and letterSpacing. Fonts are loaded dynamically via the Google Fonts CDN.

Spacing & Dimensions (6 Tokens)

Controls layout geometry: borderRadius (global border-radius in px), inputBorderRadius, buttonBorderRadius, inputHeight (in px), buttonHeight, and gap (spacing between form elements). These values are emitted as CSS custom properties and applied uniformly across all 22 layouts.

Overlay & Effects (8 Tokens)

Controls visual effects layered on backgrounds: overlayColor (RGBA), overlayOpacity (0–100%), overlayBlur (0–20px in Gaussian blur), backgroundType ('color', 'gradient', 'image'), backgroundValue (CSS gradient string or image URL), backgroundSize, backgroundPosition, and backgroundRepeat. Overlay settings can be scoped to the form section or branding panel independently.

Branding Panel Configuration (12 Tokens)

Controls the branding side of split layouts: panelLogo (URL), panelHeadline (heading text), panelSubtitle (subheading text), panelHeadlineColor, panelSubtitleColor, panelBackgroundType, panelBackgroundValue, panelOverlayColor, panelOverlayOpacity, panelOverlayBlur, panelLogoSize (small/medium/large), and panelAlignment (left/center/right). These tokens are only rendered in T1 Split layouts.

SectionToken CountCSS PrefixControlled By
Layout6--login-layout-*Layout selector tab
Colors (Light)16--login-*Color picker tab
Colors (Dark)10--login-dark-*Dark mode panel
Typography8--login-font-*Typography tab
Spacing6--login-spacing-*Spacing controls
Overlay8--login-overlay-*Overlay panel
Branding Panel12--login-panel-*Branding settings

Per-Page Branding Architecture

Each theme can define independent visual overrides for three authentication pages: Login, Forgot Password, and Reset Password. The 'pages' block in ThemeDataJson contains page-specific layout, headline, subtitle, overlay, and background settings that are merged on top of the global design when that page is active. This enables a single theme to present different messaging and visual treatments for different auth flows.

Pages Block Structure

The 'pages' object in ThemeDataJson contains three optional keys: 'login', 'forgotPassword', and 'resetPassword'. Each key maps to a page override object with fields: selectedLayout, panelHeadline, panelSubtitle, overlayColor, overlayOpacity, backgroundType, backgroundValue, and any other token that should differ from the global configuration.

Per-Page Override StructureJSON
{
  "version": "2.0",
  "selectedLayout": "split-right",
  "primaryColor": "#1e40af",
  "fontFamily": "Inter",
  // ... 50+ global tokens ...
  "pages": {
    "login": {
      "panelHeadline": "Welcome Back",
      "panelSubtitle": "Sign in to continue"
    },
    "forgotPassword": {
      "selectedLayout": "centered",
      "panelHeadline": "Password Recovery",
      "panelSubtitle": "We'll help you get back in",
      "overlayColor": "rgba(30, 64, 175, 0.3)"
    },
    "resetPassword": {
      "panelHeadline": "Create New Password",
      "panelSubtitle": "Choose a strong password"
    }
  }
}

Merge Strategy

When a tenant previews a theme's Forgot Password page, the frontend merges the global design tokens with the forgotPassword override using spread semantics: { ...globalTokens, ...pages.forgotPassword }. This means any token not specified in the page override inherits from the global design — only the explicitly overridden values change. The merge happens in the previewTheme() function in useStudioViewModel.ts.

40-Theme Catalog Overview

SCRIPE ships with 40 meticulously designed branding packages. Each theme is a unique visual identity crafted for a specific market segment or brand aesthetic. Themes span 7 categories, use 30+ different Google Fonts, cover all 22 layouts, and include per-page branding overrides for Login, Forgot Password, and Reset Password.

Design Diversity Matrix

The 40-theme catalog achieves maximum diversity across multiple axes: each theme uses a unique Google Font pairing, no two themes share the same color palette, all 7 categories are represented, and the layout distribution covers T1 Split (16), T2 Full-Page (12), T3 Centered (6), and T4 Special (6). This ensures every tenant can find a theme that matches their brand identity.

Layout FamilyTheme CountPercentage
T1 Split (split-right, split-left, magazine, etc.)1640%
T2 Full-Page (branded-full, overlay, immersive)1230%
T3 Centered (centered, floating-card)615%
T4 Special (glass-morphism, corner-card)615%

5-Tier Pricing Model

Themes are organized into 5 pricing tiers that align with SCRIPE's edition system. Each tier provides increasing design sophistication and customization depth. Tier enforcement is handled by the theme marketplace frontend — themes from higher tiers display an 'Upgrade Required' badge and disable the Apply button for tenants on lower editions.

TierThemesFont ComplexityDesign Level
Free8Standard (Inter, Poppins)Clean, professional
Starter8Premium (Outfit, Nunito Sans)Rich palettes, gradients
Professional10Editorial (DM Serif, Sora)Glass-morphism, multi-tone
Enterprise8Luxury (Cormorant, Italiana)Cinematic, exclusive dark
Standalone Add-on6Display (Cinzel Decorative)Botanical, Art Deco, Brutalist

7 Theme Categories

Every theme belongs to exactly one category. Categories enable intuitive gallery browsing and filtering. The distribution ensures broad coverage: Corporate (8), Creative (6), Dark (6), Minimal (5), Elegant (5), Luxury (5), Nature (5).

CategoryCountTarget MarketDesign DNA
Corporate8Finance, consulting, lawSerif/sans-serif pairs, navy/gray, subtle gradients
Creative6Agencies, startups, techVibrant colors, Poppins/Quicksand, animated gradients
Dark6Dev tools, media, gamingDeep backgrounds, cyan/amber accents, glass effects
Minimal5Productivity, SaaSMonochromatic, whitespace, thin borders
Elegant5Beauty, fashion, hospitalityRose gold, Playfair Display, delicate overlays
Luxury5High-end, private bankingBlack/gold, Italiana, full-bleed imagery
Nature5Sustainability, wellnessForest greens, terracotta, rounded shapes

Frontend Component Inventory

The Theme Marketplace frontend consists of 8 purpose-built components spanning 3 pages and 1 modal. Each component follows SCRIPE's presentation-layer patterns using domain entities (never DTOs) and consuming data exclusively through the DI container.

ComponentSizePurpose
ThemeGalleryView26KBFull-page marketplace — hero, filters, grid/list, pagination
ThemeManagementView12KBAdmin CRUD — DataTable with bulk operations
ThemeDetailModal28KBDetail preview — feature matrix, apply, like/favorite
ThemeCard~5KBGallery card — palette strip, font preview, engagement
ThemeMarketplacePanel~4KBInline studio panel with quick-apply

Theme Application Flow

Applying a marketplace theme follows a 5-step pipeline: 1. User clicks Apply on a theme. 2. Frontend reads the theme's ThemeDataJson. 3. previewTheme() in useStudioViewModel merges the design tokens (including per-page overrides) into the current StudioDraft. 4. The merged draft is saved to the tenant's DraftBrandingJson via PUT /tenants/{id}/settings. 5. Admin publishes the draft to make it live.

Copy-on-Apply Snapshot Semantics

When a theme is applied, the ThemeDataJson is COPIED into the tenant's DraftBrandingJson — not linked. This means the tenant's branding is permanently isolated from future marketplace updates. If the theme is updated in v2.0, existing tenants who applied v1.0 retain their v1.0 snapshot. This prevents unexpected visual changes to production login pages.

Preview Before Apply

The previewTheme() function in useStudioViewModel.ts performs a non-destructive preview by temporarily injecting theme tokens into the draft state. The preview is displayed in the sandboxed iframe via postMessage CSS variable injection. The draft is NOT saved until the user explicitly confirms the application. Canceling the preview restores the previous draft state.

Theme Apply Flow
Browse Gallery
previewTheme() (non-destructive)
Confirm Apply
Copy ThemeDataJson to DraftBrandingJson
Publish Draft (version++)

Backend Seeding Architecture

All 40 themes are seeded at application startup by LoginThemeSeeder.cs. The seeder uses an upsert-safe strategy: it checks for existing themes by Name and only inserts new ones — existing themes are never overwritten. This ensures idempotent deployment across environments.

Build() Helper Architecture

The seeder uses a fluent Build() helper with ThemeMeta and ThemeDesign records for clean theme definition. ThemeMeta contains name, category, tier, description, author, version, and tags. ThemeDesign contains all 50+ design tokens plus per-page overrides (PageOverrideDesign records for ForgotPassword and ResetPassword). The BuildFullThemeJson() method serializes the ThemeDesign record into the JSON format expected by the frontend.

LoginThemeSeeder.cs — Build() HelperC#
// ThemeMeta: name, category, tier, description, author, version, tags
// ThemeDesign: 50+ tokens + PageOverrideDesign records

private static LoginTheme Build(ThemeMeta meta, ThemeDesign design)
{
    return new LoginTheme
    {
        Name = meta.Name,
        Category = meta.Category,
        Tier = meta.Tier,
        Description = meta.Description,
        Author = meta.Author,
        Version = meta.Version,
        Tags = meta.Tags,
        IsSystemTheme = true,
        IsActive = true,
        ThemeDataJson = BuildFullThemeJson(design),
    };
}

Upsert-Safe Strategy

The seeder queries all existing theme names before processing. For each of the 40 themes, it checks the existing set — if the name exists, the theme is skipped. New themes are added to the DbContext in a single batch and saved with one SaveChangesAsync call. This makes the seeder safe to run repeatedly without duplicating themes or losing manual edits.

Marketplace Governance

Theme access is controlled by a combination of edition-based tier enforcement and permission-based administrative access. The marketplace respects SCRIPE's multi-tenancy model — themes are globally visible but apply operations are scoped to the current tenant.

ActionRequired PermissionEnforcement
Browse marketplacebranding.viewFrontend visibility
Apply themebranding.manageBackend verification
Manage themes (CRUD)themes.manageSystem Admin only
Like/favoritebranding.viewPer-admin tracking

Developer Profiles & Author Metadata

Themes can include creator attribution, documentation links, and verification status for community and partner theme submissions.

Tier & Edition Purchase Verification

Commercial themes require tenant edition verification before application, validating against the active tenant subscription tier.

Theme Purchase Verification Sequence
Select Theme in Marketplace
Check Existing Purchase & Edition Tier
Not Owned / Upgrade Needed
Create Stripe Checkout Session
Process Stripe Payment Handoff
Receive Stripe Webhook callback
Record Theme Purchase (PaymentIntent ID)
ApplyTheme: Verify Purchased Theme ID
Inject ThemeDataJson to Tenant Settings

Theme Download & Asset Package Verification

Theme asset bundles (fonts, high-resolution backgrounds) undergo SHA-256 integrity verification upon extraction.

Theme API Endpoints

The theme marketplace exposes endpoints through the existing TenantSettings and Themes controllers. Theme data is served as part of the branding configuration pipeline.

MethodEndpointDescription
GET/api/v1/themesPaginated list with category, tier, search filters
GET/api/v1/themes/{id}Full detail including ThemeDataJson + counters
POST/api/v1/themes/{id}/applyApply to current tenant's draft settings
POST/api/v1/themes/{id}/likeToggle like/favorite for current admin
POST/api/v1/themesCreate new theme (System Admin)
PUT/api/v1/themes/{id}Update theme (System Admin)
DELETE/api/v1/themes/{id}Soft-delete theme (System Admin)

Source File Reference

Backend: LoginThemeSeeder.cs (seeder), LoginTheme.cs (entity), ThemeConfiguration.cs (EF config)

Frontend: ThemeGalleryView.tsx, ThemeManagementView.tsx, ThemeDetailModal.tsx, ThemeCard.tsx

Data Layer: ThemeMarketplaceService.ts, ThemeMarketplaceRepository.ts, ThemeMarketplaceMapper.ts, ThemeMarketplaceTypes.ts

Domain Layer: ThemeDetail.ts (entity), IThemeMarketplaceService.ts, IThemeMarketplaceRepository.ts

ViewModel: useThemeMarketplace.ts (gallery state), useStudioViewModel.ts (preview/apply integration)