Skip to main content

Configuration Guidelines

This page describes global best practices for creating applet configurations. Since different developers work on different applets and make independent decisions about the configuration structure, it is important to follow common guidelines.

Applets in this project are created with configurations as control mechanisms for the usage and configuration of the applets. Different developers work on different applets and must decide based on their own judgment and requirements how such a configuration should look for the applet.

Best practices will be added here.

Don'ts – Common Pitfalls

❌ Don't use CSS class names directly in configuration

The Temptation: It seems like a good idea to allow CSS classes (like Tailwind classes) directly in configuration for maximum flexibility.

// Seems flexible, but causes problems
export interface AppletConfig {
containerClasses: string; // Users can set 'flex flex-col gap-4'
}

Why this is problematic:

Problem 1: Tailwind classes might not exist

Tailwind only includes CSS classes that appear in your source code. Classes defined only in database configurations won't be included in the final CSS bundle.

Naive solution: "Let's whitelist all possible classes in Tailwind config!"

Why this doesn't solve the problem: Even if you whitelist classes, you still face Problem 2...

Problem 2: Silent breaking changes

This is the critical issue that affects any CSS classes (Tailwind or custom):

Imagine this scenario:

  1. You create a configuration that uses bg-background class
  2. Configuration is saved to the database and used in production
  3. Weeks later, a developer refactors the design system and renames bg-background to bg-surface
  4. Developer uses find-and-replace to update all code in the repository

What breaks: All configurations in the database still reference bg-background, which no longer exists. The applet doesn't crash—it just looks broken. No error is thrown, and no developer knows which configurations in the database are now incompatible.

Real-world examples that cause this:

  • Updating Tailwind design tokens (color names, spacing scale changes)
  • Refactoring custom CSS class names during component improvements
  • Changing component structure requiring different layout classes

The core issue: CSS class names are implementation details that can change. Configurations stored in the database have no way to track or migrate these changes.

✅ Solution: Use semantic enums instead

Define configuration options as semantic, stable values that are decoupled from implementation:

// ❌ Bad - CSS classes in config (implementation details)
export interface BadConfig {
containerClasses: string; // 'flex flex-col gap-4'
}

// ✅ Good - Semantic options (stable contract)
export interface GoodConfig {
containerLayout: 'vertical' | 'horizontal' | 'grid';
}

// In your component code, map to CSS classes
function getLayoutClasses(layout: GoodConfig['containerLayout']) {
switch (layout) {
case 'vertical': return 'flex flex-col gap-4';
case 'horizontal': return 'flex flex-row gap-4';
case 'grid': return 'grid grid-cols-2 gap-4';
}
}

Why this works:

  • Configuration values ('vertical', 'horizontal') are semantic and stable
  • CSS implementation can change freely without breaking configurations
  • Developers see the configuration enum when refactoring and know it's a public API
  • Type-safe and self-documenting
  • Easy to maintain backwards compatibility

Key principle: Configuration values should represent what the user wants, not how it's implemented.