Skip to content

Smart Caching System

BaroCSS features a sophisticated multi-layer caching system that dramatically improves performance by avoiding redundant CSS generation and parsing operations.

The Performance Challenge

CSS generation involves several expensive operations:

  1. Class name parsing - Tokenizing and analyzing class syntax
  2. AST generation - Building Abstract Syntax Trees
  3. CSS compilation - Converting AST to CSS rules
  4. Style injection - Adding CSS to the DOM

Without caching, these operations would run every time, even for identical classes:

typescript
// Without caching - inefficient
element1.className = 'bg-blue-500'; // Parse + AST + CSS + Inject
element2.className = 'bg-blue-500'; // Parse + AST + CSS + Inject (again!)
element3.className = 'bg-blue-500'; // Parse + AST + CSS + Inject (again!)

BaroCSS Smart Caching Solution

BaroCSS implements a multi-layer caching system that stores results at each stage:

typescript
// With smart caching - efficient
element1.className = 'bg-blue-500'; // Parse + AST + CSS + Inject + Cache
element2.className = 'bg-blue-500'; // Cache hit! (0.1ms)
element3.className = 'bg-blue-500'; // Cache hit! (0.1ms)

Caching Layers

1. Parse Result Cache

Caches the result of class name parsing:

typescript
interface ParseResult {
  modifiers: string[];
  utility: string;
  arbitrary?: string;
}

// Cache key: 'bg-blue-500'
// Cache value: { modifiers: [], utility: 'bg', arbitrary: 'blue-500' }
const parseCache = new Map<string, ParseResult>();

2. AST Cache

Caches generated Abstract Syntax Trees:

typescript
interface ASTNode {
  type: 'style-rule' | 'at-rule' | 'declaration';
  selector?: string;
  property?: string;
  value?: string;
}

// Cache key: 'bg-blue-500' + context hash
// Cache value: [ASTNode, ASTNode, ...]
const astCache = new Map<string, ASTNode[]>();

3. CSS Cache

Caches final CSS output:

typescript
// Cache key: 'bg-blue-500' + context hash
// Cache value: '.bg-blue-500 { background-color: rgb(59 130 246); }'
const cssCache = new Map<string, string>();

4. Utility Cache

Caches utility prefix lookups:

typescript
// Cache key: 'bg'
// Cache value: { property: 'background-color', type: 'color' }
const utilityCache = new Map<string, UtilityDefinition>();

5. Failure Cache

Caches invalid class names to avoid reprocessing:

typescript
// Cache key: 'invalid-class'
// Cache value: true (failed to parse)
const failureCache = new Set<string>();

How Smart Caching Works

1. Cache-First Processing

typescript
function processClass(className: string, ctx: Context) {
  // Check CSS cache first
  const cssKey = `${className}:${ctx.hash}`;
  if (cssCache.has(cssKey)) {
    return cssCache.get(cssKey); // Cache hit!
  }
  
  // Check AST cache
  const astKey = `${className}:${ctx.hash}`;
  if (astCache.has(astKey)) {
    const ast = astCache.get(astKey);
    const css = astToCss(ast);
    cssCache.set(cssKey, css);
    return css;
  }
  
  // Check parse cache
  const parseKey = className;
  if (parseCache.has(parseKey)) {
    const parseResult = parseCache.get(parseKey);
    const ast = parseResultToAst(parseResult, ctx);
    const css = astToCss(ast);
    
    // Cache results
    astCache.set(astKey, ast);
    cssCache.set(cssKey, css);
    return css;
  }
  
  // Full processing pipeline
  const parseResult = parseClassName(className);
  const ast = parseClassToAst(className, ctx);
  const css = astToCss(ast);
  
  // Cache all results
  parseCache.set(parseKey, parseResult);
  astCache.set(astKey, ast);
  cssCache.set(cssKey, css);
  
  return css;
}

2. Context-Aware Caching

Cache keys include context information to handle theme changes:

typescript
function getCacheKey(className: string, ctx: Context) {
  // Include theme hash in cache key
  const themeHash = hashObject(ctx.theme);
  return `${className}:${themeHash}`;
}

// Different themes = different cache entries
const lightTheme = createContext({ theme: { colors: { blue: '#3b82f6' } } });
const darkTheme = createContext({ theme: { colors: { blue: '#1e40af' } } });

// These will have different cache entries
processClass('bg-blue-500', lightTheme); // Cache: 'bg-blue-500:light-hash'
processClass('bg-blue-500', darkTheme);  // Cache: 'bg-blue-500:dark-hash'

3. Intelligent Cache Invalidation

typescript
class SmartCache {
  private caches = {
    parse: new Map<string, ParseResult>(),
    ast: new Map<string, ASTNode[]>(),
    css: new Map<string, string>(),
    utility: new Map<string, UtilityDefinition>(),
    failure: new Set<string>()
  };
  
  // Invalidate caches when theme changes
  invalidateOnThemeChange(newTheme: Theme) {
    const newHash = hashObject(newTheme);
    
    // Clear context-dependent caches
    this.caches.ast.clear();
    this.caches.css.clear();
    
    // Keep parse and utility caches (theme-independent)
  }
  
  // Invalidate specific class patterns
  invalidatePattern(pattern: RegExp) {
    for (const [key] of this.caches.css) {
      if (pattern.test(key)) {
        this.caches.css.delete(key);
        this.caches.ast.delete(key);
      }
    }
  }
}

Performance Benefits

Memory Efficiency

typescript
// Traditional approach: Create new objects every time
function traditionalProcess(className: string) {
  const parseResult = parseClassName(className);     // New object
  const ast = parseClassToAst(className, ctx);      // New object
  const css = astToCss(ast);                        // New string
  return css;
}

// Smart caching: Reuse cached objects
function cachedProcess(className: string) {
  if (cssCache.has(className)) {
    return cssCache.get(className); // Reuse cached string
  }
  // ... process and cache
}

CPU Optimization

typescript
// Performance comparison
const classes = ['bg-blue-500', 'text-white', 'p-4'];

// Without caching
const start1 = performance.now();
classes.forEach(cls => traditionalProcess(cls));
const time1 = performance.now() - start1; // ~15ms

// With smart caching
const start2 = performance.now();
classes.forEach(cls => cachedProcess(cls));
const time2 = performance.now() - start2; // ~3ms

console.log(`Caching improved performance by ${time1 / time2}x`);

Cache Hit Rates

Typical cache hit rates in real applications:

typescript
const stats = runtime.getStats();
console.log(stats);
// {
//   totalClasses: 1000,
//   cacheHits: 850,
//   cacheMisses: 150,
//   hitRate: 0.85,        // 85% cache hit rate
//   averageTime: '0.8ms', // vs 5ms without caching
//   memoryUsage: '2.1MB'  // vs 8MB without caching
// }

Advanced Caching Features

1. LRU Cache Management

typescript
class LRUCache<K, V> {
  private maxSize: number;
  private cache = new Map<K, V>();
  
  constructor(maxSize: number = 1000) {
    this.maxSize = maxSize;
  }
  
  get(key: K): V | undefined {
    if (this.cache.has(key)) {
      // Move to end (most recently used)
      const value = this.cache.get(key);
      this.cache.delete(key);
      this.cache.set(key, value);
      return value;
    }
    return undefined;
  }
  
  set(key: K, value: V): void {
    if (this.cache.size >= this.maxSize) {
      // Remove least recently used
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
}

2. Cache Warming

Pre-populate cache with commonly used classes:

typescript
class CacheWarmer {
  private commonClasses = [
    'bg-white', 'bg-gray-100', 'bg-gray-200',
    'text-black', 'text-gray-600', 'text-gray-800',
    'p-2', 'p-4', 'p-6', 'p-8',
    'm-2', 'm-4', 'm-6', 'm-8'
  ];
  
  warmCache(ctx: Context) {
    this.commonClasses.forEach(className => {
      processClass(className, ctx); // Populate cache
    });
  }
}

3. Cache Statistics

typescript
class CacheStats {
  private hits = 0;
  private misses = 0;
  private totalSize = 0;
  
  recordHit() { this.hits++; }
  recordMiss() { this.misses++; }
  
  getStats() {
    return {
      hitRate: this.hits / (this.hits + this.misses),
      totalRequests: this.hits + this.misses,
      cacheSize: this.totalSize,
      memoryEfficiency: this.calculateMemoryEfficiency()
    };
  }
}

Use Cases

1. High-Traffic Applications

Perfect for applications with many repeated classes:

typescript
// E-commerce product grid
const products = await fetchProducts();
products.forEach(product => {
  const element = document.createElement('div');
  element.className = 'product-card bg-white shadow-md rounded-lg p-4';
  // 'product-card bg-white shadow-md rounded-lg p-4' is cached after first use
  grid.appendChild(element);
});

2. Component Libraries

Build efficient component systems:

typescript
class Button {
  private static commonClasses = 'px-4 py-2 rounded font-medium transition-colors';
  
  constructor(variant: 'primary' | 'secondary') {
    this.className = `${Button.commonClasses} ${this.getVariantClasses(variant)}`;
    // Common classes are cached, only variant classes are processed
  }
}

3. Dynamic Theming

Handle theme changes efficiently:

typescript
function switchTheme(theme: 'light' | 'dark') {
  const newContext = createContext({ theme });
  
  // Clear theme-dependent caches
  cache.invalidateOnThemeChange(newContext.theme);
  
  // Process elements with new theme
  document.querySelectorAll('[class*="bg-"]').forEach(element => {
    const className = element.className;
    const css = processClass(className, newContext); // Uses new cache
    updateElementStyles(element, css);
  });
}

Best Practices

1. Monitor Cache Performance

typescript
// Track cache efficiency
setInterval(() => {
  const stats = runtime.getStats();
  if (stats.hitRate < 0.7) {
    console.warn('Low cache hit rate:', stats.hitRate);
  }
}, 5000);

2. Optimize Cache Size

typescript
// Configure appropriate cache sizes
const runtime = new BrowserRuntime({
  cache: {
    maxSize: 1000,        // Maximum cached classes
    maxMemory: '10MB',    // Maximum memory usage
    ttl: 300000          // Time to live (5 minutes)
  }
});

3. Use Semantic Class Names

typescript
// Good: Reusable semantic classes
const buttonClasses = 'btn btn-primary btn-large';

// Avoid: Too specific classes
const specificClasses = 'bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600';

4. Warm Cache on Startup

typescript
// Pre-populate cache with common classes
const cacheWarmer = new CacheWarmer();
cacheWarmer.warmCache(runtime.context);

Performance Comparison

ScenarioWithout CachingWith Smart CachingImprovement
First Load100ms100msSame
Repeated Classes50ms2ms25x faster
Memory Usage8MB2MB75% less
Cache Hit Rate0%85%Significant
Average Response5ms0.8ms6x faster

Conclusion

Smart caching is a cornerstone of BaroCSS's performance, providing dramatic improvements in speed and memory efficiency through intelligent multi-layer caching.

The system automatically manages cache invalidation, provides comprehensive statistics, and scales efficiently for applications of any size.

Ready to experience smart caching? Check out the Performance Guide for optimization tips and best practices.

Released under the MIT License.