Style loading utilities that provide CSS-in-JS functionality with automatic vendor prefixing, RTL support, and server-side rendering capabilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Functions and classes for configuring the library's behavior, managing RTL state, and controlling CSS injection. These utilities provide fine-grained control over how merge-styles processes and injects CSS rules.
Functions for managing right-to-left (RTL) text direction support, which automatically transforms CSS properties for international applications.
/**
* Sets the current RTL (right-to-left) mode
* @param isRTL - True to enable RTL transformations, false to disable
*/
function setRTL(isRTL: boolean): void;Usage Examples:
import { setRTL, mergeStyles } from '@uifabric/merge-styles';
// Enable RTL mode
setRTL(true);
// Styles will now be automatically flipped
const rtlStyles = mergeStyles({
marginLeft: '10px', // Becomes marginRight: '10px'
paddingRight: '5px', // Becomes paddingLeft: '5px'
textAlign: 'left', // Becomes textAlign: 'right'
borderRadius: '4px 0 0 4px' // Becomes '0 4px 4px 0'
});
// Disable RTL mode
setRTL(false);Application-Level RTL Management:
// You can manually set RTL based on user preference or locale
const userLocale = 'ar-SA'; // Arabic locale
const isRTLLocale = ['ar', 'he', 'fa', 'ur'].some(lang =>
userLocale.startsWith(lang)
);
setRTL(isRTLLocale);
// Or detect from HTML document direction
const isDocumentRTL = document.documentElement.dir === 'rtl';
setRTL(isDocumentRTL);Preventing RTL Transformations:
// Use @noflip directive to prevent specific properties from being flipped
const mixedDirectionStyles = mergeStyles({
marginLeft: '10px', // Will be flipped in RTL
paddingLeft: '5px @noflip' // Won't be flipped in RTL
});The Stylesheet class provides low-level control over CSS rule generation, injection, and caching.
/**
* Singleton class managing CSS rule injection and caching
*/
class Stylesheet {
/** Get the global Stylesheet instance */
static getInstance(): Stylesheet;
/** Configure stylesheet behavior */
setConfig(config: IStyleSheetConfig): void;
/** Insert a CSS rule into the stylesheet */
insertRule(rule: string, preserve?: boolean): void;
/** Get all generated CSS rules as a string */
getRules(forceUpdate?: boolean): string;
/** Reset the stylesheet, clearing all rules and cache */
reset(): void;
/** Generate a unique class name */
getClassName(displayName?: string): string;
/** Cache a class name with its associated styles */
cacheClassName(className: string, key: string, args: any[], rules: any[]): void;
}Usage Examples:
import { Stylesheet, InjectionMode } from '@uifabric/merge-styles';
// Get the global stylesheet instance
const stylesheet = Stylesheet.getInstance();
// Configure stylesheet behavior
stylesheet.setConfig({
injectionMode: InjectionMode.insertNode, // How to inject CSS
defaultPrefix: 'myapp', // Default class name prefix
namespace: 'myapp', // Namespace for isolation
cspSettings: { nonce: 'random-nonce' } // Content Security Policy nonce
});
// Manually insert custom CSS rules
stylesheet.insertRule('.custom-rule { color: red; }');
// Get all generated CSS (useful for server-side rendering)
const allCSS = stylesheet.getRules();
// Reset stylesheet (useful for testing or server-side rendering)
stylesheet.reset();CSS Injection Modes:
import { InjectionMode } from '@uifabric/merge-styles';
// Different ways to inject CSS into the document
const injectionModes = {
// Don't inject CSS (useful for server-side rendering)
none: InjectionMode.none,
// Use insertRule API (default, most performant)
insertNode: InjectionMode.insertNode,
// Use appendChild (compatibility mode)
appendChild: InjectionMode.appendChild
};
// Configure injection mode
stylesheet.setConfig({
injectionMode: InjectionMode.none // For server-side rendering
});Configure CSP settings for secure applications that require nonces for style injection.
// Set CSP nonce for style injection
stylesheet.setConfig({
cspSettings: {
nonce: 'your-csp-nonce-here'
}
});
// Alternative: Global configuration (must be set before any styles are generated)
window.FabricConfig = {
mergeStyles: {
cspSettings: {
nonce: 'your-csp-nonce-here'
}
}
};Control how CSS class names are generated and prefixed.
// Configure default prefix
stylesheet.setConfig({
defaultPrefix: 'mycomponent' // Classes become: .mycomponent-0, .mycomponent-1, etc.
});
// Use namespace for isolation
stylesheet.setConfig({
namespace: 'myapp' // Classes become: .myapp-css-0, .myapp-css-1, etc.
});
// Generate custom class names
const customClassName = stylesheet.getClassName('MyButton');
// Returns something like: 'MyButton-0'Environment-Specific Configuration:
import { Stylesheet, InjectionMode } from '@uifabric/merge-styles';
const isServer = typeof document === 'undefined';
const isDevelopment = process.env.NODE_ENV === 'development';
// Configure based on environment
Stylesheet.getInstance().setConfig({
// Server-side rendering: don't inject CSS
injectionMode: isServer ? InjectionMode.none : InjectionMode.insertNode,
// Development: use descriptive prefixes
defaultPrefix: isDevelopment ? 'dev' : 'css',
// Production: use shorter namespace
namespace: isDevelopment ? 'myapp-dev' : 'app'
});Multi-Tenant Applications:
// Configure for tenant isolation
const configureTenant = (tenantId: string) => {
Stylesheet.getInstance().setConfig({
namespace: `tenant-${tenantId}`,
defaultPrefix: tenantId
});
};
// Switch tenants
configureTenant('acme-corp');
// Classes become: .tenant-acme-corp-css-0, .acme-corp-1, etc.
configureTenant('globodyne');
// Classes become: .tenant-globodyne-css-0, .globodyne-1, etc.Testing Configuration:
// Test setup - reset before each test
beforeEach(() => {
const stylesheet = Stylesheet.getInstance();
stylesheet.reset();
stylesheet.setConfig({
injectionMode: InjectionMode.none, // Don't inject in tests
defaultPrefix: 'test'
});
});
// Get generated CSS for testing
it('should generate correct styles', () => {
const className = mergeStyles({ color: 'red' });
const css = Stylesheet.getInstance().getRules();
expect(css).toContain('color: red');
expect(className).toMatch(/^test-/);
});interface IStyleOptions {
/** Enable RTL (right-to-left) transformations */
rtl?: boolean;
/** Multiply CSS specificity by this factor */
specificityMultiplier?: number;
}
interface IStyleSheetConfig {
/** How to inject CSS rules into the document */
injectionMode?: InjectionMode;
/** Default class name prefix */
defaultPrefix?: string;
/** Namespace for CSS class isolation */
namespace?: string;
/** Content Security Policy settings */
cspSettings?: ICSPSettings;
}
interface ICSPSettings {
/** Nonce value for CSP compliance */
nonce?: string;
}
const InjectionMode = {
/** Don't inject CSS (for server-side rendering) */
none: 0 as 0,
/** Inject using insertRule API (default) */
insertNode: 1 as 1,
/** Inject using appendChild (compatibility mode) */
appendChild: 2 as 2
};
type InjectionMode = typeof InjectionMode[keyof typeof InjectionMode];/**
* Extracts class names and style objects from mixed style arguments
*/
function extractStyleParts(args: IStyle[]): { classes: string[]; objects: {}[] };
/**
* Converts style objects to CSS class names
*/
function styleToClassName(options: IStyleOptions, ...objects: {}[]): string;
/**
* Type that makes all properties optional recursively
*/
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P];
};
/**
* Type that filters out function properties
*/
type ObjectOnly<TArg> = TArg extends {} ? TArg : {};
/**
* Type that omits specified keys from a type
*/
type Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;React Hook for RTL:
import { useEffect, useState } from 'react';
import { setRTL } from '@uifabric/merge-styles';
// Custom hook for RTL management
export const useRTL = (initialRTL = false) => {
const [isRTL, setIsRTLState] = useState(initialRTL);
const toggleRTL = (rtl: boolean) => {
setRTL(rtl);
setIsRTLState(rtl);
};
return { isRTL, setRTL: toggleRTL };
};
// Usage in component
const MyComponent = () => {
const { isRTL, setRTL } = useRTL();
return (
<div>
<button onClick={() => setRTL(!isRTL)}>
Toggle RTL ({isRTL ? 'ON' : 'OFF'})
</button>
</div>
);
};Theme Provider with RTL:
import { createContext, useContext, useEffect } from 'react';
import { setRTL } from '@uifabric/merge-styles';
interface ThemeContextValue {
locale: string;
isRTL: boolean;
setLocale: (locale: string) => void;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
export const ThemeProvider = ({ children }) => {
const [locale, setLocaleState] = useState('en-US');
const isRTL = ['ar', 'he', 'fa', 'ur'].some(lang =>
locale.startsWith(lang)
);
useEffect(() => {
setRTL(isRTL);
}, [isRTL]);
const setLocale = (newLocale: string) => {
setLocaleState(newLocale);
};
return (
<ThemeContext.Provider value={{ locale, isRTL, setLocale }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
};