Sofondo
SofondoFrameworkDocs

Sofondo Framework Configuration Guide

The Sofondo Framework provides a comprehensive configuration system that allows you to customize behavior, appearance, and features without modifying component code.

Table of Contents

Quick Start

  1. Create a configuration file:
// sofondo.config.ts
import { defineConfig } from '@sofondo/core';

export default defineConfig({
  theme: {
    defaultMode: 'dark',
  },
  sidebar: {
    defaultCollapsed: true,
  },
});
  1. Use ConfigProvider in your app:
// app/layout.tsx
import { ConfigProvider } from '@sofondo/react';
import config from '../sofondo.config';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ConfigProvider config={config}>
          {children}
        </ConfigProvider>
      </body>
    </html>
  );
}
  1. Access configuration in components:
import { useConfig, useConfigSection } from '@sofondo/react';

function MyComponent() {
  // Get entire config
  const config = useConfig();

  // Or get specific section
  const sidebarConfig = useConfigSection('sidebar');

  return <div>{/* your component */}</div>;
}

Configuration File

The configuration file should export a configuration object using the defineConfig helper:

// sofondo.config.ts
import { defineConfig } from '@sofondo/core';

export default defineConfig({
  // Your configuration here
});

Why use defineConfig?

  • Provides TypeScript autocomplete
  • Validates configuration at design time
  • Documents available options
  • No runtime overhead (it just returns the object)

Configuration Options

Theme

Control the appearance and theme behavior of your application.

theme: {
  // Default theme mode
  defaultMode: 'system', // 'light' | 'dark' | 'system'

  // Enable theme switcher UI component
  enableSwitcher: true,

  // Custom CSS variable overrides
  colors: {
    light: {
      '--primary': '#0066cc',
      '--background': '#ffffff',
      // ... more CSS variables
    },
    dark: {
      '--primary': '#3399ff',
      '--background': '#0a0a0a',
      // ... more CSS variables
    },
  },
}

Options:

  • defaultMode: Initial theme (‘light’, ‘dark’, or ‘system’ to follow OS preference)
  • enableSwitcher: Show/hide theme toggle button
  • colors: Override CSS custom properties for each theme

Configure sidebar behavior and appearance.

sidebar: {
  // Start collapsed or expanded
  defaultCollapsed: false,

  // Sidebar width when expanded (pixels)
  width: 240,

  // Sidebar width when collapsed (pixels)
  collapsedWidth: 60,

  // Where to store sidebar state
  storageStrategy: 'auto', // 'cookies' | 'localStorage' | 'sessionStorage' | 'memory' | 'auto'

  // Show tooltips in collapsed mode
  enableTooltips: true,

  // Tooltip position offsets
  tooltipOffset: {
    x: 10,
    y: 0,
  },
}

Storage Strategies:

  • cookies: Persists across browser sessions, SSR-friendly
  • localStorage: Persists across sessions, client-side only
  • sessionStorage: Cleared when tab closes
  • memory: Not persisted (resets on page reload)
  • auto: Prefers cookies, falls back to localStorage

Animations

Control animations throughout the framework.

animations: {
  // Enable/disable all animations
  enabled: true,

  // Animation speed multiplier
  durationMultiplier: 1, // 0.5 = faster, 2 = slower

  // Respect user's motion preferences
  respectReducedMotion: true,

  // Stagger delay for container animations (ms)
  staggerDelay: 100,
}

Duration Multiplier Examples:

  • 0.5: Animations run twice as fast
  • 1.0: Normal speed (default)
  • 2.0: Animations run twice as slow
  • 0: Instant (effectively disables animations)

Components

Set defaults for individual components.

Custom Scrollbar

components: {
  scrollbar: {
    enabled: true,
    width: 6, // pixels
  },
}

Skeleton Loaders

components: {
  skeleton: {
    enabled: true,
    animation: 'pulse', // 'pulse' | 'wave' | 'none'
  },
}

Toast Notifications

components: {
  toast: {
    position: 'top-right', // 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
    duration: 5000, // milliseconds
    maxToasts: 3, // maximum simultaneous toasts
  },
}

Data Grid

components: {
  dataGrid: {
    defaultPageSize: 10,
    enablePagination: true,
    enableSorting: true,
  },
}

Accessibility

Configure accessibility features.

accessibility: {
  // Enhanced keyboard navigation
  keyboardNavigation: true,

  // Show focus indicators
  focusVisible: true,

  // Announce page changes to screen readers
  announceRouteChanges: true,
}

Development

Tools for development and debugging.

dev: {
  // Show component boundaries
  showComponentBoundaries: false,

  // Performance monitoring
  enablePerformanceMonitoring: false,

  // Log config changes to console
  logConfigChanges: false,
}

Using Configuration

In React Components

Get Entire Configuration

import { useConfig } from '@sofondo/react';

function MyComponent() {
  const config = useConfig();

  if (config.animations.enabled) {
    // Use animations
  }

  return <div>...</div>;
}

Get Specific Section

import { useConfigSection } from '@sofondo/react';

function Sidebar() {
  const sidebarConfig = useConfigSection('sidebar');

  return (
    <aside style={{ width: sidebarConfig.width }}>
      {/* sidebar content */}
    </aside>
  );
}

Direct Props vs Configuration

Many components accept configuration via props OR read from ConfigProvider:

// Option 1: Via props (takes precedence)
<ToastProvider position="top-left" duration={3000} maxToasts={5}>
  {children}
</ToastProvider>

// Option 2: Via ConfigProvider (set once, applies everywhere)
<ConfigProvider config={{ components: { toast: { position: 'top-left' } } }}>
  <ToastProvider>
    {children}
  </ToastProvider>
</ConfigProvider>

Order of precedence:

  1. Component props (highest priority)
  2. ConfigProvider configuration
  3. Default values (lowest priority)

Without ConfigProvider

Components work without ConfigProvider using sensible defaults:

// This works fine - uses built-in defaults
function App() {
  return (
    <ThemeProvider>
      <ToastProvider>
        {/* your app */}
      </ToastProvider>
    </ThemeProvider>
  );
}

TypeScript Support

The configuration system is fully typed:

import type { UserConfig, SofondoConfig } from '@sofondo/core';

// Full autocomplete and type checking
const config: UserConfig = {
  theme: {
    defaultMode: 'dark', // ✓ Autocompleted
    // defaultMode: 'blue', // ✗ Type error
  },
};

Available Types:

  • UserConfig: Partial configuration (all fields optional)
  • SofondoConfig: Alias for UserConfig
  • ResolvedConfig: Complete configuration with all defaults
  • ThemeConfig, SidebarConfig, etc.: Individual sections

Examples

Dark Mode by Default

export default defineConfig({
  theme: {
    defaultMode: 'dark',
  },
});

Minimalist UI

export default defineConfig({
  animations: {
    enabled: false, // No animations
  },
  components: {
    skeleton: {
      enabled: false, // No skeleton loaders
    },
    scrollbar: {
      enabled: false, // Use browser scrollbar
    },
  },
});

Accessibility First

export default defineConfig({
  animations: {
    respectReducedMotion: true,
    durationMultiplier: 0.5, // Faster animations
  },
  accessibility: {
    keyboardNavigation: true,
    focusVisible: true,
    announceRouteChanges: true,
  },
});

Development Mode

export default defineConfig({
  dev: {
    showComponentBoundaries: true,
    enablePerformanceMonitoring: true,
    logConfigChanges: true,
  },
});

Compact Sidebar

export default defineConfig({
  sidebar: {
    defaultCollapsed: true,
    width: 200,          // Narrower when expanded
    collapsedWidth: 50,  // Narrower when collapsed
  },
});

Custom Toast Position

export default defineConfig({
  components: {
    toast: {
      position: 'bottom-center',
      duration: 3000,
      maxToasts: 1, // Only show one toast at a time
    },
  },
});

Configuration Validation

The framework validates configuration and provides helpful error messages:

// Invalid configuration
defineConfig({
  theme: {
    defaultMode: 'blue', // ✗ Invalid value
  },
});

// Error: Invalid theme.defaultMode: "blue". Must be one of: light, dark, system

Validation is performed:

  • At build time (TypeScript)
  • At runtime (when ConfigProvider mounts)
  • In development mode (warnings in console)

Best Practices

  1. Use defineConfig for autocomplete and type safety
  2. Keep configuration files small - only override what you need
  3. Use ConfigProvider once at the app root
  4. Validate configuration in development
  5. Document custom overrides for your team
  6. Test with different configurations to ensure flexibility

Troubleshooting

Configuration Not Applied

Problem: Changes to config file don’t take effect

Solutions:

  1. Restart dev server after config changes
  2. Check that ConfigProvider wraps your app
  3. Verify config import path is correct
  4. Check browser console for validation errors

TypeScript Errors

Problem: Type errors in configuration file

Solutions:

  1. Use defineConfig helper
  2. Check spelling of configuration keys
  3. Ensure values match allowed types
  4. Update @sofondo/core package

Runtime Errors

Problem: “useConfig must be used within ConfigProvider”

Solution: Wrap your app with ConfigProvider:

<ConfigProvider config={config}>
  <YourApp />
</ConfigProvider>

Migration from Hard-Coded Values

If you have hard-coded configuration values, migrate them to the config file:

Before:

<ToastProvider position="top-right" duration={5000}>
  <ThemeProvider>
    {children}
  </ThemeProvider>
</ToastProvider>

After:

// sofondo.config.ts
export default defineConfig({
  components: {
    toast: {
      position: 'top-right',
      duration: 5000,
    },
  },
});

// app/layout.tsx
<ConfigProvider config={config}>
  <ToastProvider>
    <ThemeProvider>
      {children}
    </ThemeProvider>
  </ToastProvider>
</ConfigProvider>

Further Reading