Config Engine

Declarative component initialisation with JSON configuration. Setup once, update anytime, clean up when done.

Why Config Engine?

Why Config Engine?

Declarative

Define components with JSON, not imperative code

Mutable

Update configuration at runtime with deep merging

Inspectable

Retrieve current config for debugging

Cleanable

Reset components and free resources

<div class="row">
    <div class="col-6 mb-4">
        <div class="card h-full">
            <div class="card-body text-center">
                <div class="text-primary mb-3" data-icon="code"></div>
                <h4 class="font-semibold mb-2">Declarative</h4>
                <p class="text-sm text-muted mb-0">Define components with JSON, not imperative code</p>
            </div>
        </div>
    </div>
    <div class="col-6 mb-4">
        <div class="card h-full">
            <div class="card-body text-center">
                <div class="text-primary mb-3" data-icon="refresh"></div>
                <h4 class="font-semibold mb-2">Mutable</h4>
                <p class="text-sm text-muted mb-0">Update configuration at runtime with deep merging</p>
            </div>
        </div>
    </div>
    <div class="col-6 mb-4">
        <div class="card h-full">
            <div class="card-body text-center">
                <div class="text-primary mb-3" data-icon="search"></div>
                <h4 class="font-semibold mb-2">Inspectable</h4>
                <p class="text-sm text-muted mb-0">Retrieve current config for debugging</p>
            </div>
        </div>
    </div>
    <div class="col-6 mb-4">
        <div class="card h-full">
            <div class="card-body text-center">
                <div class="text-primary mb-3" data-icon="close"></div>
                <h4 class="font-semibold mb-2">Cleanable</h4>
                <p class="text-sm text-muted mb-0">Reset components and free resources</p>
            </div>
        </div>
    </div>
</div>

Basic Setup

Initialise multiple components at once with a single configuration object.

// Initialise components declaratively
$.setup({
    // Selector: configuration
    '#my-modal': {
        component: 'modal',
        options: { backdrop: true, keyboard: true }
    },

    '.product-card': {
        component: 'card',
        options: { hover: true }
    },

    '#main-tabs': {
        component: 'tabs',
        options: { animation: true, activeIndex: 0 }
    }
});

Supported Components (16 total)

card

Content containers with hover and click behaviour.

modal

Overlays for focused tasks and dialogs.

tabs

Switch between panels without leaving the page.

accordion

Expand and collapse groups of content.

tooltip

Small hints beside controls or links.

carousel

Browse a sequence of slides or images.

dropdown

Open menus and choose from a list.

badge

Highlight a status, label or count.

backToTop

Return to the top of a long page.

buttonGroup

Keep related actions or toggles together.

loader

Show progress while content is loading.

breadcrumbs

Show the path to the current page.

navbar

Build the main site navigation.

notification

Show desktop notifications with permission.

timer

Count down and act when time runs out.

alarm

Schedule a notification for a set time.

→ View complete component reference with live demos

Event Binding

Bind event handlers directly in configuration using inline functions or action objects.

$.setup({
    // Inline function handler
    '#submit-btn': {
        events: {
            click: (e, $el) => {
                e.preventDefault();
                $el.text('Submitted!');
            }
        }
    },

    // Multiple events
    '#search-input': {
        events: {
            focus: (e, $el) => $el.addClass('focused'),
            blur: (e, $el) => $el.removeClass('focused'),
            input: (e, $el) => console.log($el.val())
        }
    },

    // Action objects for declarative changes
    '#theme-toggle': {
        events: {
            click: [
                { toggleClass: 'active' },
                { target: 'body', toggleClass: 'dm-theme-dark' }
            ]
        }
    }
});
Try it
Click count: 0
<div class="flex gap-4 items-center">
    <button type="button" id="demo-click-btn" class="btn btn-primary">Click me</button>
    <span id="demo-click-result" class="text-muted">Click count: 0</span>
</div>
/**
 * Try it - from public/showcase/config.
 */
export function mount(root) {
    let clickCount = 0;
    $.setup({
            '#demo-click-btn': {
                events: {
                    click: (e, $el) => {
                        clickCount++;
                        $(root).find('#demo-click-result').text('Click count: ' + clickCount);
                    }
                }
            }
        });
}

DOMReady Initialisation

The config engine automatically handles DOMContentLoaded, with support for global and per-selector initialisation callbacks.

Automatic DOMContentLoaded Detection

// Config automatically waits for DOM if needed
$.setup({
    '#my-element': {
        component: 'card',
        options: { collapsible: true }
    }
});
// ✅ If DOM is loading: queued for DOMContentLoaded
// ✅ If DOM is ready: executes immediately

Global $init Callback

Use $init for code that should run once when all configs are processed:

$.setup({
    $init() {
        // Runs once after all selectors are configured
        console.log('Config engine initialised!');
        Domma.icons.scan(); // Scan for icons
        Domma.theme.init(); // Initialise theme
    },

    '#modal': {
        component: 'modal',
        options: { backdrop: true }
    },

    '#tabs': {
        component: 'tabs',
        options: { activeIndex: 0 }
    }
});

Per-Selector init Callbacks

Each selector can have its own initialisation callback that receives the DommaCollection:

$.setup({
    '#user-card': {
        init($el) {
            // Runs when this selector is configured
            // Receives DommaCollection as first argument
            console.log('User card initialised:', $el);

            // Fetch and populate data
            const userId = $el.data('user-id');
            loadUserData(userId).then(data => {
                $el.find('.name').text(data.name);
                $el.find('.avatar').attr('src', data.avatar);
            });
        },
        component: 'card',
        options: { hoverable: true }
    },

    '#status-indicator': {
        init($el) {
            // Check server status on init
            checkServerStatus().then(online => {
                $el.addClass(online ? 'online' : 'offline');
            });
        },
        initial: {
            text: 'Checking...'
        }
    }
});

Standalone ready() Helper

For manual DOMReady callbacks outside of config:

// Standard method
Domma.ready(() => {
    console.log('DOM is ready!');
    // Your initialisation code
});

// Alias
$.ready(() => {
    console.log('This also works!');
});

// Executes immediately if DOM is already ready
// Otherwise waits for DOMContentLoaded
Try it
Waiting for initialisation...

Check the console for initialisation logs.

<div class="flex gap-4 items-center mb-3">
    <div id="demo-init-status" class="p-4 rounded demo-update-box-style">
        Waiting for initialisation...
    </div>
</div>
<p class="text-sm text-muted">Check the console for initialisation logs.</p>
/**
 * Try it - from public/showcase/config.
 */
export function mount(root) {
        // Demo: DOMReady initialisation
    $.setup({
            $init() {
                console.log('✅ $init callback executed - all configs processed');
                $(root).find('#demo-init-status')
                    .html('<strong style="color: #10b981;">✓ Initialised!</strong><br>$init callback executed successfully')
                    .css({ 'background': '#d1fae5', 'color': '#065f46' });
            },

            '#demo-init-status': {
                init($el) {
                    console.log('✅ Per-selector init callback for #demo-init-status', $el);
                    setTimeout(() => {
                        $el.append('<br><small>Per-selector init also ran!</small>');
                    }, 500);
                }
            }
        });
}

Zero Boilerplate: No need for manual DOMContentLoaded listeners. The config engine handles everything automatically!

Initial State

Set initial properties like CSS, text, HTML, classes, and attributes.

$.setup({
    '#welcome-message': {
        initial: {
            text: 'Welcome to Domma!',
            css: { color: 'var(--dm-primary)', fontWeight: 'bold' },
            addClass: 'animated fadeIn'
        }
    },

    '#status-badge': {
        initial: {
            html: 'Online',
            attr: { 'data-status': 'online', 'aria-label': 'Status' }
        }
    },

    '.disabled-section': {
        initial: {
            addClass: 'opacity-50',
            attr: { 'aria-disabled': 'true' }
        }
    }
});

Available Properties

Property Description Example
text Set text content text: 'Hello'
html Set HTML content html: 'Bold'
css Apply CSS styles css: { color: 'red' }
addClass Add CSS classes addClass: 'active highlighted'
removeClass Remove CSS classes removeClass: 'hidden'
toggleClass Toggle CSS classes toggleClass: 'expanded'
attr Set attributes attr: { disabled: true }

Runtime Updates

Modify configuration after initialisation with $.update(). Changes are deep-merged.

// Initial setup
$.setup({
    '#my-modal': {
        component: 'modal',
        options: { backdrop: true, keyboard: true }
    }
});

// Later: update options
$.update('#my-modal', {
    options: { backdrop: false }  // backdrop changed, keyboard preserved
});

// Add new event handlers
$.update('#my-modal', {
    events: {
        click: () => console.log('Modal clicked')
    }
});

// Update initial state
$.update('#status', {
    initial: {
        text: 'Updated status',
        addClass: 'pulse'
    }
});
Try it
Original State
<div class="flex gap-4 items-center mb-3">
    <div id="demo-update-box" class="p-4 rounded demo-update-box-style">
        Original State
    </div>
</div>
<div class="flex gap-2">
    <button type="button" id="demo-update-1" class="btn btn-outline btn-sm">Update Text</button>
    <button type="button" id="demo-update-2" class="btn btn-outline btn-sm">Update Style</button>
    <button type="button" id="demo-update-3" class="btn btn-outline btn-sm">Reset</button>
</div>
/**
 * Try it - from public/showcase/config.
 */
export function mount(root) {
        // Demo: Runtime updates
    $.setup({
            '#demo-update-box': {
                initial: {
                    text: 'Original State',
                    css: {background: 'var(--dm-gray-200)'}
                }
            },
            '#demo-update-1': {
                events: {
                    click: () => $.update('#demo-update-box', {
                        initial: {text: 'Text Updated!'}
                    })
                }
            },
            '#demo-update-2': {
                events: {
                    click: () => $.update('#demo-update-box', {
                        initial: {
                            css: {background: 'var(--dm-primary)', color: 'var(--dm-white)'}
                        }
                    })
                }
            },
            '#demo-update-3': {
                events: {
                    click: () => $.update('#demo-update-box', {
                        initial: {
                            text: 'Original State',
                            css: {background: 'var(--dm-gray-200)', color: 'inherit'}
                        }
                    })
                }
            }
        });
}

Configuration Inspection

Retrieve current configuration for debugging or dynamic adjustments.

// Get config for specific selector
const modalConfig = $.config('#my-modal');
console.log(modalConfig);
// { component: 'modal', options: { backdrop: true, keyboard: true } }

// Get all configurations
const allConfigs = $.config();
console.log(allConfigs);
// { '#my-modal': {...}, '.card': {...}, ... }
Current Page Configurations
<button type="button" id="demo-inspect" class="btn btn-primary btn-sm mb-3">Inspect $.config()</button>
<div id="demo-inspect-result" class="demo-result-config hidden"></div>
/**
 * Current Page Configurations - from public/showcase/config.
 */
export function mount(root) {
        // Demo: Config inspection
    $.setup({
            '#demo-inspect': {
                events: {
                    click: () => {
                        const config = $.config();
                        const result = $(root).find('#demo-inspect-result');
                        result.removeClass('hidden');
                        result.text(JSON.stringify(config, (key, value) => {
                            if (typeof value === 'function') return '[Function]';
                            return value;
                        }, 2));
                    }
                }
            }
        });
}

Cleanup

Reset configuration, destroy components, and unbind events with $.reset().

// Reset specific selector
$.reset('#my-modal');
// - Destroys the modal component
// - Unbinds all event handlers
// - Clears stored configuration

// Reset ALL configurations
$.reset();
// Resets every selector that was configured

Memory Management: Always call $.reset() when removing configured elements from the DOM to prevent memory leaks.

Real-World Example

A complete form with validation, dynamic UI, and component integration.

$.setup({
    // Form validation and submission
    '#contact-form': {
        events: {
            submit: (e, $form) => {
                e.preventDefault();
                const data = {
                    name: $('#name').val(),
                    email: $('#email').val()
                };

                // Show loading state
                $('#submit-btn').text('Sending...').attr('disabled', true);

                // Simulate API call
                setTimeout(() => {
                    // Show success toast (better UX for transient feedback)
                    Domma.elements.toast('Message sent successfully!', {
                        type: 'success',
                        duration: 3000
                    });

                    // Reset form and button
                    $form[0].reset();
                    $('#email').removeClass('success error');
                    $('#submit-btn').text('Send Message').attr('disabled', false);
                }, 1500);
            }
        }
    },

    // Input validation on blur
    '#email': {
        events: {
            blur: (e, $el) => {
                const valid = $el.val().includes('@');
                $el.removeClass('success error')
                   .addClass(valid ? 'success' : 'error');
            }
        }
    }
});
Live Demo
<form id="live-form" class="grid grid-cols-2 gap-4 max-w-500px">
    <div class="form-group">
        <label class="form-label">Name</label>
        <input type="text" id="live-name" class="form-input" placeholder="Your name">
    </div>
    <div class="form-group">
        <label class="form-label">Email</label>
        <input type="email" id="live-email" class="form-input" placeholder="you@example.com">
    </div>
    <div class="col-span-2">
        <button type="submit" id="live-submit" class="btn btn-primary">Send Message</button>
    </div>
</form>
/**
 * Live Demo - from public/showcase/config.
 */
export function mount(root) {
        // Demo: Live form
    $.setup({
            '#live-email': {
                events: {
                    blur: (e, $el) => {
                        const val = $el.val();
                        if (val) {
                            const valid = val.includes('@');
                            $el.removeClass('success error warning')
                                .addClass(valid ? 'success' : 'error');
                        }
                    },
                    input: (e, $el) => {
                        $el.removeClass('success error warning');
                    }
                }
            },
            '#live-form': {
                events: {
                    submit: (e, $form) => {
                        e.preventDefault();
                        const $btn = $(root).find('#live-submit');

                      // Show loading state
                        $btn.text('Sending...').attr('disabled', true);

                      // Simulate API call
                        setTimeout(() => {
                          // Show success toast
                          E.toast('Message sent successfully!', {
                            type: 'success',
                            duration: 3000
                          });

                          // Reset form and button
                          $form[0].reset();
                          $form.find('input').removeClass('success error');
                          $btn.text('Send Message').attr('disabled', false);
                        }, 1000);
                    }
                }
            }
        });
}

API Reference

Method Description
$.setup(config) Process initial configuration object. Keys are CSS selectors, values are config objects with component, options, initial, and events properties.
$.update(selector, changes) Deep-merge changes into existing configuration. Updates component options, re-applies initial properties, and rebinds events.
$.config(selector?) Retrieve configuration. With selector: returns config for that selector. Without: returns all configurations as object.
$.reset(selector?) Reset configuration. Destroys components, unbinds events, clears config. Without selector: resets all.
$.ready(callback) Register a callback to run when DOM is ready. Executes immediately if DOM is already ready, otherwise waits for DOMContentLoaded. Also available as Domma.ready().

Configuration Properties

Property Description
$init Global initialisation callback. Runs once when all configs are processed, after DOM is ready.
init Per-selector initialisation callback. Receives DommaCollection as first argument. Runs when this selector's config is processed.
component Component type to initialise (e.g., 'modal', 'card', 'tabs'). See supported components list above.
options Component-specific options object passed to the component factory.
initial Initial state properties (css, text, html, addClass, removeClass, toggleClass, attr).
events Event handlers object. Keys are event names, values are handler functions or action objects.