Integrations

How Domma components work together - blueprint-centric architecture, data flow, and event coordination.

Overview

Domma uses a blueprint-centric architecture. Define your data structure once in a Blueprint, then use it across Forms, Models, Tables, and CRUD operations.

Overview

Blueprints

Schema definition

Models

Reactive data

Forms

Auto-generated

HTTP

API sync

Storage

Persistence

Tables

Data display

<div class="grid grid-cols-1 grid-cols-md-2 grid-cols-lg-3 gap-4 mb-4"><div class="card text-center">
        <div class="card-body">
            <span data-icon="file-code" data-size="32" class="text-primary mb-2"></span>
            <h4 class="text-sm font-semibold">Blueprints</h4>
            <p class="text-xs text-muted">Schema definition</p>
        </div>
    </div>
    <div class="card text-center">
        <div class="card-body">
            <span data-icon="database" data-size="32" class="text-success mb-2"></span>
            <h4 class="text-sm font-semibold">Models</h4>
            <p class="text-xs text-muted">Reactive data</p>
        </div>
    </div>
    <div class="card text-center">
        <div class="card-body">
            <span data-icon="layout" data-size="32" class="text-info mb-2"></span>
            <h4 class="text-sm font-semibold">Forms</h4>
            <p class="text-xs text-muted">Auto-generated</p>
        </div>
    </div>
    <div class="card text-center">
        <div class="card-body">
            <span data-icon="globe" data-size="32" class="text-warning mb-2"></span>
            <h4 class="text-sm font-semibold">HTTP</h4>
            <p class="text-xs text-muted">API sync</p>
        </div>
    </div>
    <div class="card text-center">
        <div class="card-body">
            <span data-icon="save" data-size="32" class="text-secondary mb-2"></span>
            <h4 class="text-sm font-semibold">Storage</h4>
            <p class="text-xs text-muted">Persistence</p>
        </div>
    </div>
    <div class="card text-center">
        <div class="card-body">
            <span data-icon="grid" data-size="32" class="text-danger mb-2"></span>
            <h4 class="text-sm font-semibold">Tables</h4>
            <p class="text-xs text-muted">Data display</p>
        </div>
    </div>
</div>

Data Flow Diagram

This diagram shows how data flows through Domma components:

Data Flow Diagram
Blueprint
→
Forms / Models
→
Validation

↓

HTTP / Storage
→
Tables / DOM
<div class="flow-step">Blueprint</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Forms / Models</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Validation</div>
<br>
<div class="flow-arrow" style="transform: rotate(90deg); margin: 1rem 0;">↓</div>
<br>
<div class="flow-step">HTTP / Storage</div>
<div class="flow-arrow">→</div>
<div class="flow-step">Tables / DOM</div>

Key Concept: The Blueprint is the single source of truth. All components derive their behavior from it.

The Blueprint-Centric Approach

Define once, use everywhere. Create a single Blueprint and use it to power Forms, Models, and CRUD operations automatically.

Single Blueprint Definition

const userBlueprint = {
    name: {
        type: 'string',
        required: true,
        minLength: 2,
        label: 'Full Name'
    },
    email: {
        type: 'email',
        required: true,
        label: 'Email Address'
    },
    age: {
        type: 'number',
        min: 18,
        max: 125,
        label: 'Age'
    }
};

Use Everywhere

// Auto-generate a form
const form = F.create(userBlueprint);

// Create a reactive model
const model = M.create(userBlueprint);

// Full CRUD with one line
F.crud('#app', userBlueprint, {
    api: '/api/users'
});
Use Everywhere
<button type="button" class="btn btn-primary" id="demo-blueprint-btn">Try It: See All Three in Action</button>
<div class="result-pane mt-3" id="blueprint-demo-result" style="display: none;">
    <div class="flex flex-wrap items-center gap-3 mb-3">
        <button type="button" class="btn btn-sm btn-secondary" data-view="form">Form View</button>
        <button type="button" class="btn btn-sm btn-secondary" data-view="model">Model View</button>
        <button type="button" class="btn btn-sm btn-secondary" data-view="table">Table View</button>
    </div>
    <div id="blueprint-demo-content"></div>
</div>
/**
 * Use Everywhere - from public/showcase/integrations.
 */
export function mount(root) {
        // Blueprint Power Demo
    const blueprintDemoBtn = $(root).find('#demo-blueprint-btn');
    const blueprintResult = $(root).find('#blueprint-demo-result');
    const blueprintContent = $(root).find('#blueprint-demo-content');
    let currentView = 'form';
        // Shared data storage for the demo
    let demoData = [
            { name: 'John Doe', email: 'john@example.com', age: 30 },
            { name: 'Jane Smith', email: 'jane@example.com', age: 28 }
        ];
    let demoModel = null;
    let demoTable = null;
    const demoBlueprint = {
            name: { type: 'string', required: true, label: 'Name', placeholder: 'Enter name' },
            email: { type: 'email', required: true, label: 'Email', placeholder: 'you@example.com' },
            age: { type: 'number', min: 18, max: 125, label: 'Age' }
        };
    blueprintDemoBtn.on('click', () => {
            blueprintResult.show();
            renderBlueprintView('form');
        });
    function renderBlueprintView(view) {
            currentView = view;

            // Update button states
            $('[data-view]').removeClass('btn-primary').addClass('btn-secondary');
            $(`[data-view="${view}"]`).removeClass('btn-secondary').addClass('btn-primary');

            if (view === 'form') {
                // Build HTML with container first
                const html = `
                    <h4 class="font-semibold mb-3">Generated Form</h4>
                    <p class="text-muted mb-3">Fill out the form and submit to add data to the table →</p>
                    <div id="demo-form-container"></div>
                `;

                blueprintContent.html(html);

                // Small delay to ensure DOM is ready
                setTimeout(() => {
                    F.render('#demo-form-container', demoBlueprint, {}, {
                        layout: 'stacked',
                        submitText: 'Add to Table',
                        onSubmit: (data) => {
                            // Add to shared data array
                            demoData.push(data);

                            // Update model if it exists
                            if (demoModel) {
                                demoModel.set(data);
                            }

                            // Show success and switch to table view
                            E.toast(`Added ${data.name} to table!`, { type: 'success' });
                            console.log('Form data:', data);
                            console.log('All data:', demoData);

                            // Auto-switch to table view after 500ms
                            setTimeout(() => {
                                renderBlueprintView('table');
                            }, 500);
                        }
                    });
                }, 10);
            } else if (view === 'model') {
                // Use latest data from array, or create default
                const latestData = demoData.length > 0
                    ? demoData[demoData.length - 1]
                    : { name: 'John Doe', email: 'john@example.com', age: 30 };

                // Create or update model
                if (!demoModel) {
                    demoModel = M.create(demoBlueprint, latestData);
                } else {
                    demoModel.set(latestData);
                }

                const html = `
                    <h4 class="font-semibold mb-3">Model State (JSON)</h4>
                    <p class="text-muted mb-3">This model reflects the latest form submission</p>
                    <pre style="background: #282c34; color: #abb2bf; padding: 1rem; border-radius: 4px; overflow-x: auto; font-family: 'Courier New', monospace; font-size: 13px;">${JSON.stringify(demoModel.toJSON(), null, 2)}</pre>
                    <p class="mt-3"><strong>Try it:</strong> Open browser console and type:</p>
                    <pre class="code-block language-javascript">
    // Change model property
    window.demoModel.set('name', 'Jane Smith');

    // Get a value
    window.demoModel.get('name');

    // Validate the model
    window.demoModel.validate();

    // Get all data as JSON
    window.demoModel.toJSON();
    </pre>
                `;

                blueprintContent.html(html);
                window.demoModel = demoModel;
            } else if (view === 'table') {
                // Build HTML with container
                const html = `
                    <h4 class="font-semibold mb-3">Generated Table</h4>
                    <p class="text-muted mb-3">Showing ${demoData.length} record${demoData.length !== 1 ? 's' : ''} - Add more via the Form view</p>
                    <div id="demo-table-container"></div>
                `;

                blueprintContent.html(html);

                // Small delay to ensure DOM is ready
                setTimeout(() => {
                    // Create table with all data
                    demoTable = Domma.tables.create('#demo-table-container', {
                        columns: [
                            { key: 'name', label: 'Name' },
                            { key: 'email', label: 'Email' },
                            { key: 'age', label: 'Age' }
                        ],
                        data: demoData,
                        striped: true,
                        hover: true,
                        pagination: demoData.length > 5 ? true : false,
                        itemsPerPage: 5
                    });
                }, 10);
            }
        }
    $('[data-view]').on('click', function() {
            renderBlueprintView($(this).attr('data-view'));
        });
}

Data Flow Patterns

Common integration patterns showing how data moves between components.

Data Flow Patterns

Two-Way Binding

Forms can be automatically bound to Models for reactive updates:

const userModel = M.create(userBlueprint, {
    name: 'John Doe',
    email: 'john@example.com'
});

// Create form with model binding
const form = F.create(userBlueprint, {}, {
    model: userModel  // Two-way binding
});

// Changes in form update model automatically
// Changes in model update form automatically
<div id="data-flow-tabs">
    <div class="tabs">
        <div class="tab-list" role="tablist">
            <button type="button" class="tab-item active" role="tab">Form → Model</button>
            <button type="button" class="tab-item" role="tab">Model → DOM</button>
            <button type="button" class="tab-item" role="tab">Model → HTTP</button>
            <button type="button" class="tab-item" role="tab">Model → Storage</button>
        </div>
        <div class="tab-content">
            <div class="tab-panel active">
                <h4 class="mb-3">Two-Way Binding</h4>
                <p>Forms can be automatically bound to Models for reactive updates:</p>
                <pre class="code-block language-javascript">
const userModel = M.create(userBlueprint, {
    name: 'John Doe',
    email: 'john@example.com'
});

// Create form with model binding
const form = F.create(userBlueprint, {}, {
    model: userModel  // Two-way binding
});

// Changes in form update model automatically
// Changes in model update form automatically
</pre>
            </div>
            <div class="tab-panel" style="display:none;">
                <h4 class="mb-3">Reactive DOM Binding</h4>
                <p>Bind model properties directly to DOM elements:</p>
                <pre class="code-block language-javascript">
const userModel = M.create(userBlueprint);

// Bind model fields to DOM elements
M.bind(userModel, 'name', '#display-name', {
    twoWay: true  // Updates both ways
});

M.bind(userModel, 'email', '#display-email');

// When model changes, DOM updates automatically
userModel.set('name', 'Jane Smith');
</pre>
            </div>
            <div class="tab-panel" style="display:none;">
                <h4 class="mb-3">API Synchronization</h4>
                <p>Sync models with REST APIs manually or use CRUD helper:</p>
                <pre class="code-block language-javascript">
// Manual sync
const user = M.create(userBlueprint);

// Load from API
const data = await H.get('/api/users/1');
user.set(data);

// Save to API
const result = await H.put('/api/users/1',
    user.toJSON()
);

// Or use CRUD helper for automatic sync
F.crud('#users', userBlueprint, {
    api: '/api/users'  // Handles all CRUD
});
</pre>
            </div>
            <div class="tab-panel" style="display:none;">
                <h4 class="mb-3">localStorage Persistence</h4>
                <p>Models can automatically persist to localStorage:</p>
                <pre class="code-block language-javascript">
// Auto-persist model
const settings = M.create(settingsBlueprint, {}, {
    persist: 'user-settings',  // localStorage key
    autoSave: true            // Save on every change
});

// Manual save/load
settings.save();
settings.load();

// Check if persisted
if (settings.isPersisted()) {
    console.log('Settings saved');
}
</pre>
            </div>
        </div>
    </div>
</div>
/**
 * Data Flow Patterns - from public/showcase/integrations.
 *
 * The tab markup is static, so it lives in demo.html; E.tabs() only wires it up.
 */
export function mount(root) {
    const tabs = E.tabs($(root).find('#data-flow-tabs .tabs')[0]);
    return () => tabs?.destroy?.();
}

Event-Driven Communication

Use Pub/Sub for decoupled component coordination. Components can communicate without direct references.

Publisher

// Component A publishes event
M.publish('cart:updated', {
    items: cartItems,
    total: calculateTotal()
});

Subscribers

// Component B listens
M.subscribe('cart:updated', (data) => {
    updateBadge(data.items.length);
});

// Component C also listens
M.subscribe('cart:updated', (data) => {
    showNotification(`Cart updated`);
});
Subscribers
0 ← Watch the badge update via Pub/Sub
<div class="flex flex-wrap items-center gap-3 mb-3">
    <button type="button" class="btn btn-primary" id="add-to-cart-btn">Add to Cart</button>
    <span class="badge badge-danger" id="cart-badge">0</span>
    <span class="text-muted">← Watch the badge update via Pub/Sub</span>
</div>
<div id="event-notifications"></div>
/**
 * Subscribers - from public/showcase/integrations.
 */
export function mount(root) {
        // Event Bridge Demo
    let cartCount = 0;
    const cartBadge = $(root).find('#cart-badge');
    const addToCartBtn = $(root).find('#add-to-cart-btn');
    const notificationsContainer = $(root).find('#event-notifications');
    const unsubscribe = M.subscribe('cart:updated', (data) => {
            cartBadge.text(data.count);

            // Show notification
            const notification = $('<div class="alert alert-success mb-2"></div>')
                .text(`Item added! Cart has ${data.count} item${data.count !== 1 ? 's' : ''}.`)
                .hide()
                .appendTo(notificationsContainer)
                .fadeIn();

            setTimeout(() => {
                notification.fadeOut(() => notification.remove());
            }, 3000);
        });
    addToCartBtn.on('click', () => {
            cartCount++;
            M.publish('cart:updated', { count: cartCount });
        });

    return unsubscribe;
}

CRUD in 3 Lines

The F.crud() method combines Forms, Models, Tables, and Validation into a complete CRUD interface with just one function call.

F.crud('#users', userBlueprint, {
    api: '/api/users',           // REST API endpoint
    features: ['add', 'edit', 'delete', 'search']
});

This single line creates:

  • ✓ Sortable, filterable data table
  • ✓ Modal form for creating new records
  • ✓ Edit functionality with pre-populated data
  • ✓ Delete confirmation dialogs
  • ✓ Built-in validation from the blueprint
  • ✓ API or localStorage persistence

Pro Tip: Use storage: 'storage-key' instead of api for localStorage-backed CRUD with zero backend code.

Styling Integration

Domma uses a modular CSS architecture with 4 files loaded in order:

<!-- 1. Base styles + utilities -->
<link rel="stylesheet" href="dist/domma.css">

<!-- 2. Grid system (Bootstrap + CSS Grid) -->
<link rel="stylesheet" href="dist/grid.css">

<!-- 3. UI components (22 elements) -->
<link rel="stylesheet" href="dist/elements.css">

<!-- 4. Theme system (10+ themes) -->
<link rel="stylesheet" href="dist/themes/domma-themes.css">

Theme Integration

All Domma components are theme-aware. Change the theme and everything updates together:

Theme Integration
Card Component

Updates with theme

<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="">Light</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="dark" data-variant="">Dark</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="ocean">Ocean</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="forest">Forest</button>
<button type="button" class="btn btn-sm btn-primary" data-theme="light" data-variant="sunset">Sunset</button>
<button type="button" class="btn btn-sm btn-secondary" data-theme="reset">Reset</button>
<div class="grid grid-cols-1 grid-cols-md-3 gap-4">
    <div class="card">
        <div class="card-body">
            <h5>Card Component</h5>
            <p class="text-muted">Updates with theme</p>
        </div>
    </div>
    <div class="card">
        <div class="card-body">
            <button type="button" class="btn btn-primary w-100">Button</button>
            <button type="button" class="btn btn-secondary w-100 mt-2">Secondary</button>
        </div>
    </div>
    <div class="card">
        <div class="card-body">
            <input type="text" class="form-input" placeholder="Form input">
            <select class="form-select mt-2">
                <option>Option 1</option>
                <option>Option 2</option>
            </select>
        </div>
    </div>
</div>
/**
 * Theme Integration - from public/showcase/integrations.
 */
export function mount(root) {
        // Theme Demo
    $('[data-theme]').on('click', function() {
            const theme = $(this).attr('data-theme');
            const variant = $(this).attr('data-variant');

            if (theme === 'reset') {
                Domma.theme.set('ocean-light');
                E.toast('Theme reset to Light', { type: 'success' });
            } else {
                Domma.theme.set(`${variant || 'ocean'}-${theme}`);

                const themeName = variant ? _.capitalize(variant) : _.capitalize(theme);
                E.toast(`Theme changed to ${themeName}`, { type: 'success' });
            }
        });
}

Grid System

Use Bootstrap-style grid or CSS Grid utilities:

<!-- Bootstrap-style -->
<div class="row">
    <div class="col-md-6">Column 1</div>
    <div class="col-md-6">Column 2</div>
</div>

<!-- CSS Grid utilities -->
<div class="grid grid-cols-2 gap-4">
    <div>Column 1</div>
    <div>Column 2</div>
</div>

Quick Integration Reference

Quick Integration Reference
M.extend() M.pick() M.omit() M.types.string M.types.number M.types.email

See Blueprints Showcase for complete reference.

<div id="reference-tabs">
    <div class="tabs">
        <div class="tab-list" role="tablist">
            <button type="button" class="tab-item active" role="tab">Blueprint Methods</button>
            <button type="button" class="tab-item" role="tab">Model Methods</button>
            <button type="button" class="tab-item" role="tab">Form Methods</button>
            <button type="button" class="tab-item" role="tab">Event Methods</button>
        </div>
        <div class="tab-content">
            <div class="tab-panel active">
                <div class="grid grid-cols-2 grid-cols-md-3 gap-3">
                    <code class="badge badge-primary">M.extend()</code>
                    <code class="badge badge-primary">M.pick()</code>
                    <code class="badge badge-primary">M.omit()</code>
                    <code class="badge badge-primary">M.types.string</code>
                    <code class="badge badge-primary">M.types.number</code>
                    <code class="badge badge-primary">M.types.email</code>
                </div>
                <p class="mt-3">See <a href="/showcase/blueprints">Blueprints Showcase</a> for complete reference.</p>
            </div>
            <div class="tab-panel" style="display:none;">
                <div class="grid grid-cols-2 grid-cols-md-3 gap-3">
                    <code class="badge badge-success">M.create()</code>
                    <code class="badge badge-success">model.get()</code>
                    <code class="badge badge-success">model.set()</code>
                    <code class="badge badge-success">model.validate()</code>
                    <code class="badge badge-success">M.bind()</code>
                    <code class="badge badge-success">model.save()</code>
                    <code class="badge badge-success">model.load()</code>
                    <code class="badge badge-success">model.toJSON()</code>
                    <code class="badge badge-success">model.reset()</code>
                </div>
                <p class="mt-3">See <a href="/showcase/models">Models Showcase</a> for complete reference.</p>
            </div>
            <div class="tab-panel" style="display:none;">
                <div class="grid grid-cols-2 grid-cols-md-3 gap-3">
                    <code class="badge badge-info">F.create()</code>
                    <code class="badge badge-info">F.crud()</code>
                    <code class="badge badge-info">F.modal()</code>
                    <code class="badge badge-info">F.wizard()</code>
                    <code class="badge badge-info">form.getData()</code>
                    <code class="badge badge-info">form.setData()</code>
                    <code class="badge badge-info">form.validate()</code>
                    <code class="badge badge-info">form.reset()</code>
                </div>
                <p class="mt-3">See <a href="/showcase/forms">Forms Showcase</a> for complete reference.</p>
            </div>
            <div class="tab-panel" style="display:none;">
                <div class="grid grid-cols-2 grid-cols-md-3 gap-3">
                    <code class="badge badge-warning">M.subscribe()</code>
                    <code class="badge badge-warning">M.publish()</code>
                    <code class="badge badge-warning">M.unsubscribe()</code>
                    <code class="badge badge-warning">M.once()</code>
                    <code class="badge badge-warning">model.onChange()</code>
                </div>
                <p class="mt-3">See <a href="/showcase/models">Models Showcase</a> for Pub/Sub examples.</p>
            </div>
        </div>
    </div>
</div>
/**
 * Quick Integration Reference - from public/showcase/integrations.
 *
 * The tab markup is static, so it lives in demo.html; E.tabs() only wires it up.
 */
export function mount(root) {
    const tabs = E.tabs($(root).find('#reference-tabs .tabs')[0]);
    return () => tabs?.destroy?.();
}