ट्यूटोरियल

मल्टी-ब्रांड कलर आर्किटेक्चर: एक कोडबेस, कई थीम

6 मिनट पठन

जब एक SaaS प्रोडक्ट कई क्लाइंट्स को उनके अपने ब्रांडिंग के तहत सेवा देना शुरू करता है — या जब एक प्लेटफ़ॉर्म को सब-प्रोडक्ट्स में अलग-अलग ब्रांड पहचान बनाए रखनी होती है — तो कलर सिस्टम एक इंफ्रास्ट्रक्चर समस्या बन जाता है। सरल समाधान है हर ब्रांड के लिए स्टाइलशीट को डुप्लिकेट करना। सही समाधान है एक टोकन आर्किटेक्चर जो ब्रांड पहचान को कंपोनेंट इम्प्लीमेंटेशन से अलग करती है, ताकि नई ब्रांड थीम जोड़ना एक कॉन्फ़िगरेशन बदलाव हो, न कि कोड रीफैक्टर।

यह गाइड शुरू से एक पूरी मल्टी-ब्रांड कलर आर्किटेक्चर बनाती है — तीन टोकन लेयर्स, CSS कस्टम प्रॉपर्टी ओवरराइड पैटर्न, Tailwind CSS इंटीग्रेशन, और ब्रांड थीम को वेलिडेट करने की टेस्टिंग स्ट्रेटेजी को कवर करती है।

व्हाइट-लेबल डिज़ाइन आवश्यकताएं

आर्किटेक्चर डिज़ाइन करने से पहले, यह स्पष्ट करें कि आपके प्रोडक्ट के लिए "मल्टी-ब्रांड" का वास्तव में क्या मतलब है। आवश्यकताएं एक स्पेक्ट्रम पर होती हैं:

न्यूनतम थीमिंग: केवल प्राइमरी ब्रांड रंग बदलता है। बाकी UI (न्यूट्रल ग्रे, बैकग्राउंड, टेक्स्ट, बॉर्डर) वही रहता है। यह सबसे सरल केस है और अधिकांश एंटरप्राइज़ व्हाइट-लेबल ज़रूरतों को पूरा करता है।

पूर्ण पैलेट थीमिंग: हर कलर टोकन — प्राइमरी, सेकेंडरी, न्यूट्रल, सिमेंटिक (सफलता, चेतावनी, त्रुटि), और सरफेस — प्रत्येक ब्रांड के लिए ओवरराइड किया जा सकता है। यह तब आवश्यक है जब ब्रांड्स की विज़ुअल पहचान काफी अलग हो।

डिज़ाइन लैंग्वेज थीमिंग: रंगों से परे, टाइपोग्राफी, बॉर्डर रेडियस, स्पेसिंग, और मोशन भी बदलते हैं। यह एक कंपोनेंट सिस्टम समस्या है, केवल रंग समस्या नहीं।

यह गाइड बीच के केस — पूर्ण पैलेट थीमिंग — को लक्षित करती है।

प्रत्येक ब्रांड को क्या निर्दिष्ट करना होगा

एक अच्छी तरह से डिज़ाइन किए गए सिस्टम के लिए, प्रत्येक ब्रांड प्रदान करता है:

  1. प्राइमरी रंग — मुख्य इंटरैक्टिव और ब्रांड रंग
  2. सरफेस रंग (वैकल्पिक) — बैकग्राउंड यदि वे न्यूट्रल डिफ़ॉल्ट से भिन्न हैं
  3. सेकेंडरी एक्सेंट (वैकल्पिक) — एक दूसरा अलग ब्रांड रंग

बाकी सब — सिमेंटिक रंग (सफलता, चेतावनी, त्रुटि), न्यूट्रल ग्रे, टाइपोग्राफी रंग — न्यूट्रल ग्लोबल लेयर से लिया जाता है जब तक स्पष्ट रूप से ओवरराइड न किया जाए।

टोकन लेयर्स: ग्लोबल, ब्रांड, सिमेंटिक

आर्किटेक्चर टोकन की तीन अलग लेयर्स का उपयोग करती है जो एक निर्धारित कैस्केड में एक-दूसरे को ओवरराइड करती हैं।

लेयर 1: ग्लोबल टोकन (प्रिमिटिव वैल्यू)

ग्लोबल टोकन पूरे कलर पैलेट को रॉ वैल्यू में परिभाषित करते हैं — कोई अर्थ नहीं, बस रंग और उनके नाम:

/* globals.css */
:root {
  /* Blue scale */
  --global-blue-50:   #EFF6FF;
  --global-blue-100:  #DBEAFE;
  --global-blue-200:  #BFDBFE;
  --global-blue-300:  #93C5FD;
  --global-blue-400:  #60A5FA;
  --global-blue-500:  #3B82F6;
  --global-blue-600:  #2563EB;
  --global-blue-700:  #1D4ED8;
  --global-blue-800:  #1E40AF;
  --global-blue-900:  #1E3A8A;

  /* Neutral scale */
  --global-neutral-50:  #F9FAFB;
  --global-neutral-100: #F3F4F6;
  --global-neutral-200: #E5E7EB;
  --global-neutral-300: #D1D5DB;
  --global-neutral-400: #9CA3AF;
  --global-neutral-500: #6B7280;
  --global-neutral-600: #4B5563;
  --global-neutral-700: #374151;
  --global-neutral-800: #1F2937;
  --global-neutral-900: #111827;

  /* Green scale */
  --global-green-50:  #F0FDF4;
  --global-green-500: #22C55E;
  --global-green-700: #15803D;

  /* Red scale */
  --global-red-50:    #FEF2F2;
  --global-red-500:   #EF4444;
  --global-red-700:   #B91C1C;

  /* Amber scale */
  --global-amber-50:  #FFFBEB;
  --global-amber-500: #F59E0B;
  --global-amber-700: #B45309;
}

ग्लोबल टोकन कभी भी सीधे कंपोनेंट स्टाइल में उपयोग नहीं किए जाते। किसी भी ब्रांड के प्राइमरी रंग के लिए पूर्ण 50–950 स्केल उत्पन्न करने के लिए Shade Generator का उपयोग करें।

लेयर 2: ब्रांड टोकन (प्रति-ब्रांड ओवरराइड)

ब्रांड टोकन ब्रांड-विशिष्ट मानों को नामित ब्रांड स्लॉट के एक सेट पर मैप करते हैं:

/* brands/default.css — डिफ़ॉल्ट ब्रांड */
:root {
  --brand-primary-50:   var(--global-blue-50);
  --brand-primary-100:  var(--global-blue-100);
  --brand-primary-200:  var(--global-blue-200);
  --brand-primary-300:  var(--global-blue-300);
  --brand-primary-400:  var(--global-blue-400);
  --brand-primary-500:  var(--global-blue-500);
  --brand-primary-600:  var(--global-blue-600);
  --brand-primary-700:  var(--global-blue-700);
  --brand-primary-800:  var(--global-blue-800);
  --brand-primary-900:  var(--global-blue-900);
}
/* brands/acme.css — ACME Corp व्हाइट-लेबल थीम */
[data-brand="acme"] {
  /* ACME ब्रांड गहरे फॉरेस्ट ग्रीन को प्राइमरी के रूप में उपयोग करता है */
  --brand-primary-50:   #F0FDF4;
  --brand-primary-100:  #DCFCE7;
  --brand-primary-200:  #BBF7D0;
  --brand-primary-300:  #86EFAC;
  --brand-primary-400:  #4ADE80;
  --brand-primary-500:  #22C55E;
  --brand-primary-600:  #16A34A;
  --brand-primary-700:  #15803D;
  --brand-primary-800:  #166534;
  --brand-primary-900:  #14532D;
}
/* brands/nova.css — Nova Inc व्हाइट-लेबल थीम */
[data-brand="nova"] {
  /* Nova गहरे बैंगनी का उपयोग करता है */
  --brand-primary-50:   #FAF5FF;
  --brand-primary-100:  #F3E8FF;
  --brand-primary-200:  #E9D5FF;
  --brand-primary-300:  #D8B4FE;
  --brand-primary-400:  #C084FC;
  --brand-primary-500:  #A855F7;
  --brand-primary-600:  #9333EA;
  --brand-primary-700:  #7E22CE;
  --brand-primary-800:  #6B21A8;
  --brand-primary-900:  #581C87;
}

[data-brand="acme"] पर ब्रांड टोकन ओवरराइड को स्कोप करके, सही थीम तब सक्रिय होती है जब वह एट्रिब्यूट <html> एलिमेंट पर मौजूद हो।

लेयर 3: सिमेंटिक टोकन (कंपोनेंट-फेसिंग)

सिमेंटिक टोकन एकमात्र टोकन हैं जिन्हें कंपोनेंट स्टाइल कभी रेफर करते हैं:

/* semantic.css */
:root {
  /* इंटरैक्टिव एलिमेंट */
  --color-interactive:          var(--brand-primary-500);
  --color-interactive-hover:    var(--brand-primary-600);
  --color-interactive-active:   var(--brand-primary-700);
  --color-interactive-disabled: var(--brand-primary-300);
  --color-interactive-subtle:   var(--brand-primary-50);

  /* इंटरैक्टिव बैकग्राउंड पर टेक्स्ट */
  --color-on-interactive: #FFFFFF;

  /* सरफेस */
  --color-surface-base:     var(--global-neutral-50);
  --color-surface-raised:   #FFFFFF;
  --color-surface-overlay:  var(--global-neutral-100);

  /* टेक्स्ट */
  --color-text-primary:   var(--global-neutral-900);
  --color-text-secondary: var(--global-neutral-600);
  --color-text-muted:     var(--global-neutral-400);

  /* बॉर्डर */
  --color-border:         var(--global-neutral-200);
  --color-border-strong:  var(--global-neutral-400);

  /* सिमेंटिक फीडबैक */
  --color-success:      var(--global-green-500);
  --color-success-bg:   var(--global-green-50);
  --color-warning:      var(--global-amber-500);
  --color-warning-bg:   var(--global-amber-50);
  --color-danger:       var(--global-red-500);
  --color-danger-bg:    var(--global-red-50);

  /* फोकस रिंग */
  --focus-ring:         var(--brand-primary-600);
}

सिमेंटिक टोकन के विरुद्ध लिखा गया एक कंपोनेंट बटन इस तरह दिखता है:

.btn-primary {
  background-color: var(--color-interactive);
  color: var(--color-on-interactive);
  border: none;
}

.btn-primary:hover {
  background-color: var(--color-interactive-hover);
}

.btn-primary:active {
  background-color: var(--color-interactive-active);
}

.btn-primary:disabled {
  background-color: var(--color-interactive-disabled);
  opacity: 0.6;
  cursor: not-allowed;
}

यह बटन हर ब्रांड के लिए समान रूप से काम करता है।

CSS कस्टम प्रॉपर्टी ओवरराइड पैटर्न

ब्रांड एट्रिब्यूट लागू करना

ब्रांड एट्रिब्यूट को सर्वर-साइड (SSR के लिए) या पेज लोड होने पर तुरंत सेट करें:

<!-- सर्वर-रेंडर: Django, Rails, Next.js SSR -->
<html data-brand="{{ brand_slug }}">

<!-- क्लाइंट-साइड: CSS लागू होने से पहले सेट करें -->
<script>
  document.documentElement.setAttribute(
    'data-brand',
    window.__BRAND__ || 'default'
  );
</script>

Django के लिए, मिडलवेयर या कॉन्टेक्स्ट प्रोसेसर से ब्रांड स्लग पास करें:

# context_processors.py
def brand_context(request):
    brand = getattr(request, 'brand_slug', 'default')
    return {'brand_slug': brand}
<!-- base.html -->
<html data-brand="{{ brand_slug }}">

CSS लोडिंग स्ट्रेटेजी

सही कैस्केड सुनिश्चित करने के लिए CSS को इस क्रम में लोड करें:

<head>
  <link rel="stylesheet" href="/static/css/globals.css">
  <link rel="stylesheet" href="/static/css/semantic.css">
  <link rel="stylesheet" href="/static/css/brands/{{ brand_slug }}.css">
  <link rel="stylesheet" href="/static/css/components.css">
</head>

रनटाइम ब्रांड स्विचिंग (डेमो/प्रीव्यू एनवायरनमेंट के लिए)

function switchBrand(brandSlug) {
  document.documentElement.setAttribute('data-brand', brandSlug);
  loadBrandTheme(brandSlug);
  localStorage.setItem('selected-brand', brandSlug);
}

// सेव्ड प्रेफरेंस से इनिशियलाइज़ करें
const saved = localStorage.getItem('selected-brand') ?? 'default';
switchBrand(saved);

Tailwind मल्टी-थीम कॉन्फ़िगरेशन

Tailwind CSS v4 में, CSS @theme का उपयोग करके कस्टम कलर यूटिलिटीज़ परिभाषित करें:

/* styles.css */
@import "tailwindcss";

@theme {
  --color-interactive:          var(--color-interactive);
  --color-interactive-hover:    var(--color-interactive-hover);
  --color-interactive-active:   var(--color-interactive-active);
  --color-interactive-disabled: var(--color-interactive-disabled);
  --color-interactive-subtle:   var(--color-interactive-subtle);

  --color-surface-base:         var(--color-surface-base);
  --color-surface-raised:       var(--color-surface-raised);

  --color-text-primary:         var(--color-text-primary);
  --color-text-secondary:       var(--color-text-secondary);
  --color-text-muted:           var(--color-text-muted);
}
<!-- सही: सिमेंटिक यूटिलिटी, थीम-अग्नॉस्टिक -->
<button class="bg-interactive hover:bg-interactive-hover text-white px-4 py-2 rounded-md">
  परिवर्तन सहेजें
</button>

<!-- गलत: नीले रंग से लॉक, ब्रांड थीम को अनदेखा करता है -->
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md">
  परिवर्तन सहेजें
</button>

ब्रांड थीम का परीक्षण और वैलिडेशन

एक मजबूत मल्टी-ब्रांड सिस्टम को टेस्ट की ज़रूरत होती है।

ऑटोमेटेड कंट्रास्ट टेस्टिंग

# check_brand_contrast.py
import colorsys

def hex_to_relative_luminance(hex_color):
    """hex को WCAG रिलेटिव ल्यूमिनेंस में कनवर्ट करें।"""
    r, g, b = int(hex_color[1:3], 16), int(hex_color[3:5], 16), int(hex_color[5:7], 16)
    channels = [c / 255 for c in [r, g, b]]
    linearized = [
        c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4
        for c in channels
    ]
    return 0.2126 * linearized[0] + 0.7152 * linearized[1] + 0.0722 * linearized[2]

def contrast_ratio(hex1, hex2):
    l1 = hex_to_relative_luminance(hex1)
    l2 = hex_to_relative_luminance(hex2)
    lighter, darker = max(l1, l2), min(l1, l2)
    return (lighter + 0.05) / (darker + 0.05)

brands = {
    "default": {"primary-500": "#3B82F6", "primary-50": "#EFF6FF"},
    "acme":    {"primary-500": "#22C55E", "primary-50": "#F0FDF4"},
    "nova":    {"primary-500": "#A855F7", "primary-50": "#FAF5FF"},
}

for brand, tokens in brands.items():
    ratio = contrast_ratio(tokens["primary-500"], "#FFFFFF")
    status = "PASS" if ratio >= 3.0 else "FAIL"
    print(f"{brand} primary-500 vs white: {ratio:.2f}:1 — {status}")

डिज़ाइन फेज़ के दौरान व्यक्तिगत ब्रांड कलर पेयर की त्वरित मैन्युअल वेरिफिकेशन के लिए Contrast Checker का उपयोग करें।

विज़ुअल रिग्रेशन टेस्टिंग

// brand-themes.spec.ts
import { test, expect } from '@playwright/test';

const brands = ['default', 'acme', 'nova'];

for (const brand of brands) {
  test(`${brand} ब्रांड थीम सही तरीके से रेंडर होती है`, async ({ page }) => {
    await page.goto(`/?brand=${brand}`);
    await expect(page).toHaveScreenshot(`${brand}-theme.png`);
  });
}

टोकन कम्प्लीटनेस चेक

// validate-brand-tokens.js
const required_brand_tokens = [
  '--brand-primary-50',
  '--brand-primary-100',
  '--brand-primary-200',
  '--brand-primary-300',
  '--brand-primary-400',
  '--brand-primary-500',
  '--brand-primary-600',
  '--brand-primary-700',
  '--brand-primary-800',
  '--brand-primary-900',
];

// CSS फ़ाइल को पार्स करें और सभी टोकन की उपस्थिति सत्यापित करें
// ... इम्प्लीमेंटेशन

ब्रांड प्रीव्यू एनवायरनमेंट

# middleware.py
class BrandPreviewMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # स्टेजिंग में ?brand= अनुमति दें; प्रोडक्शन में अकाउंट ब्रांड उपयोग करें
        if settings.DEBUG or settings.STAGING:
            brand = request.GET.get('brand', 'default')
        else:
            brand = getattr(request.user, 'brand_slug', 'default')
        request.brand_slug = brand
        return self.get_response(request)

तीन लेयर्स पर बनी मल्टी-ब्रांड कलर आर्किटेक्चर — ग्लोबल प्रिमिटिव, ब्रांड ओवरराइड, सिमेंटिक कंपोनेंट टोकन — आर्किटेक्चरल बदलाव के बिना दो से दो सौ ब्रांड तक स्केल होती है।

नए ब्रांड पैलेट डिज़ाइन करते समय पूरक कलर संयोजनों को एक्सप्लोर करने के लिए Palette Generator का उपयोग करें, और किसी भी ब्रांड प्राइमरी रंग से पूर्ण 50–900 स्केल उत्पन्न करने के लिए Shade Generator का उपयोग करें।

संबंधित रंग

संबंधित ब्रांड्स

संबंधित उपकरण