ट्यूटोरियल

React में डायनेमिक कलर थीमिंग: CSS वेरिएबल से परे

3 मिनट पठन

अधिकांश थीमिंग ट्यूटोरियल डार्क मोड टॉगल पर रुक जाते हैं। <html> पर एक क्लास स्विच करें, कुछ CSS वेरिएबल पलटें, काम हो गया। यह एक सामान्य मामले को कवर करता है लेकिन अधिक रोचक समस्या को चूक जाता है: क्या होगा अगर उपयोगकर्ता अपना ब्रांड रंग चुन सकते हैं? क्या होगा अगर आपका SaaS उत्पाद कई क्लाइंट को सेवा देता है, प्रत्येक की अपनी पहचान है?

यहीं से डायनेमिक कलर थीमिंग शुरू होती है — और CSS वेरिएबल अकेले पर्याप्त नहीं हैं। आपको रनटाइम रंग एल्गोरिदम, React स्टेट जो नेवीगेशन के दौरान बना रहे, परसिस्टेंस रणनीतियाँ, और एक आर्किटेक्चर चाहिए जो हर बार स्लाइडर हिलने पर आपके पूरे कंपोनेंट ट्री को फिर से रेंडर न करे।


React थीम के लिए CSS वेरिएबल दृष्टिकोण

आधार रेखा: सिमेंटिक टोकन आर्किटेक्चर

प्रत्येक डायनेमिक थीम सिस्टम एक ही नींव से शुरू होता है: CSS कस्टम प्रॉपर्टी जिनका नाम उनके रंग मान के बजाय उनकी अर्थगत भूमिका से रखा गया है।

/* globals.css */
:root {
  --bg-base: #F8FAFC;
  --bg-surface: #FFFFFF;
  --bg-sunken: #F1F5F9;

  --text-primary: #1E293B;
  --text-secondary: #64748B;
  --text-disabled: #94A3B8;

  --brand-primary: #2563EB;
  --brand-hover: #1D4ED8;
  --brand-subtle: #EFF6FF;
  --on-brand: #FFFFFF;

  --border-default: #E2E8F0;
  --border-strong: #CBD5E1;

  --status-error: #DC2626;
  --status-success: #16A34A;
  --status-warning: #D97706;
}

जब आप JavaScript में --brand-primary अपडेट करते हैं, तो हर कंपोनेंट जो इसे संदर्भित करता है तुरंत अपडेट हो जाता है।


रंग एल्गोरिदम के साथ रनटाइम थीम जनरेशन

पूर्ण टोकन सेट जनरेट करना

import chroma from 'chroma-js';

function generateThemeTokens(brandHex) {
  const brand = chroma(brandHex);
  const brandLuminance = brand.luminance();
  const onBrand = brandLuminance > 0.179 ? '#000000' : '#FFFFFF';

  const scale = chroma.scale([
    chroma(brandHex).brighten(2.5).desaturate(0.5).hex(),
    brandHex,
    chroma(brandHex).darken(2.5).hex(),
  ]).mode('oklch').colors(11);

  return {
    '--brand-primary': brandHex,
    '--brand-hover': chroma(brandHex).darken(0.5).hex(),
    '--brand-active': chroma(brandHex).darken(1).hex(),
    '--brand-subtle': scale[1],
    '--on-brand': onBrand,
  };
}

थीम स्टेट मैनेजमेंट के लिए React कंटेक्स्ट

ThemeContext आर्किटेक्चर

// contexts/ThemeContext.tsx
import {
  createContext,
  useContext,
  useEffect,
  useState,
  useCallback,
  type ReactNode,
} from 'react';
import { generateThemeTokens } from '../lib/theme-generator';

interface ThemeState {
  brandHex: string;
  mode: 'light' | 'dark' | 'system';
}

interface ThemeContextValue extends ThemeState {
  setBrand: (hex: string) => void;
  setMode: (mode: 'light' | 'dark' | 'system') => void;
  resolvedMode: 'light' | 'dark';
}

const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
const STORAGE_KEY = 'app-theme';

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [state, setState] = useState<ThemeState>(() => {
    try {
      const stored = localStorage.getItem(STORAGE_KEY);
      if (stored) return JSON.parse(stored) as ThemeState;
    } catch {}
    return { brandHex: '#2563EB', mode: 'system' };
  });

  useEffect(() => {
    const tokens = generateThemeTokens(state.brandHex);
    const root = document.documentElement;
    Object.entries(tokens).forEach(([prop, value]) => {
      root.style.setProperty(prop, value as string);
    });
    localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
  }, [state]);

  const setBrand = useCallback((hex: string) => {
    setState(prev => ({ ...prev, brandHex: hex }));
  }, []);

  return (
    <ThemeContext.Provider value={{ ...state, setBrand, setMode: () => {}, resolvedMode: 'light' }}>
      {children}
    </ThemeContext.Provider>
  );
}

Tailwind CSS डायनेमिक कलर क्लासेस

समाधान 1: CSS वेरिएबल ब्रिज

/* styles.css (Tailwind v4) */
@import "tailwindcss";

@theme {
  --color-brand: var(--brand-primary);
  --color-brand-hover: var(--brand-hover);
  --color-brand-subtle: var(--brand-subtle);
  --color-on-brand: var(--on-brand);
}

अब bg-brand, text-brand, और hover:bg-brand-hover मान्य Tailwind क्लासेस हैं।


उपयोगकर्ता रंग प्राथमिकताएं बनाए रखना

लोड पर डिफ़ॉल्ट थीम की फ्लैश रोकना

// app/layout.tsx (Next.js App Router)
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script
          dangerouslySetInnerHTML={{
            __html: `
              (function() {
                try {
                  var stored = JSON.parse(localStorage.getItem('app-theme') || '{}');
                  var brand = stored.brandHex || '#2563EB';
                  document.documentElement.style.setProperty('--brand-primary', brand);
                } catch(e) {}
              })();
            `,
          }}
        />
      </head>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

मुख्य निष्कर्ष

  • CSS कस्टम प्रॉपर्टी नींव हैं — भूमिका के अनुसार नामित सिमेंटिक टोकन थीम बदलाव की अनुमति देते हैं।
  • एक ब्रांड हेक्स से रनटाइम टोकन जनरेशन के लिए: एक लाइटनेस/डार्कनेस स्केल, on-brand पाठ के लिए WCAG कंट्रास्ट जांच, और डार्क मोड वेरिएंट के लिए अलग गणना चाहिए।
  • React कंटेक्स्ट थीम स्टेट प्रबंधित करता है और DOM पर टोकन लागू करता है।
  • महंगे रंग गणनाओं को 150ms डिबाउंस करें।
  • Tailwind CSS के लिए: @theme में CSS वेरिएबल ब्रिज परिभाषित करें।
  • Shade Generator और Palette Generator का उपयोग करें।

संबंधित रंग

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

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