Documentation / Developer Guide

Developer Guide

Learn how to create modules, themes, and language packs for SovereignCMS.

Creating Modules

Modules extend SovereignCMS with new features. Each module has its own routes, views, assets, and database tables.

Module Structure

my-module/
  module.json      # Module metadata and config
  index.js         # Entry point with hooks
  views/
    admin/        # Admin panel templates
    public/       # Public-facing templates
  assets/
    css/
    js/
  languages/
    en/
      modules/
        my-module.json

module.json Configuration

module.json
{
  "name": "My Module",
  "slug": "my-module",
  "version": "1.0.0",
  "description": "Module description here",
  "author": "Your Name",
  "icon": "fas fa-puzzle-piece",
  "category": "content",
  "tags": ["feature", "utility"],

  "requires": {
    "cms": ">=1.0.0"
  },

  "hooks": {
    "install": true,
    "uninstall": true,
    "activate": true,
    "deactivate": true,
    "init": true
  },

  "routes": {
    "public": "/m/my-module",
    "admin": "/admin/modules/my-module"
  },

  "permissions": [
    "mymodule.view",
    "mymodule.create",
    "mymodule.edit",
    "mymodule.delete"
  ],

  "config": {
    "itemsPerPage": 10,
    "enableFeature": true
  },

  "adminMenu": {
    "label": "My Module",
    "icon": "fas fa-puzzle-piece",
    "position": 50,
    "items": [
      {
        "label": "Dashboard",
        "route": "/admin/modules/my-module",
        "permission": "mymodule.view"
      }
    ]
  }
}

Module Entry Point (index.js)

index.js
const express = require('express');

module.exports = {
  // Called when module is first installed
  async install({ db }) {
    await db.query(`
      CREATE TABLE IF NOT EXISTS mm_items (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        slug VARCHAR(255) UNIQUE NOT NULL,
        description TEXT,
        is_active TINYINT(1) DEFAULT 1,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      )
    `);
  },

  // Called when module is removed
  async uninstall({ db }) {
    await db.query('DROP TABLE IF EXISTS mm_items');
  },

  // Called when module is enabled
  async activate({ db }) {
    console.log('Module activated');
  },

  // Called when module is disabled
  async deactivate({ db }) {
    console.log('Module deactivated');
  },

  // Called on every server start
  async init({ db, app }) {
    console.log('Module initialized');
  },

  // Public routes (mounted at /m/my-module/)
  async routes(context) {
    const { db, validate, module } = context;
    const router = express.Router();

    router.get('/', async (req, res) => {
      const items = await db.query('SELECT * FROM mm_items WHERE is_active = 1');
      res.render('modules/my-module/views/public/index', { items });
    });

    return router;
  },

  // Admin routes (mounted at /admin/modules/my-module/)
  async adminRoutes(context) {
    const { db, validate, module } = context;
    const router = express.Router();

    router.get('/', async (req, res) => {
      const items = await db.query('SELECT * FROM mm_items');
      res.render('modules/my-module/views/admin/index', { items });
    });

    return router;
  }
};

Tip: Prefix your database tables with a unique identifier (e.g., mm_ for "my-module") to avoid conflicts with other modules.

Creating Themes

Themes control the visual appearance of SovereignCMS. They use EJS templates and CSS with customizable variables.

Theme Structure

my-theme/
  theme.json        # Theme metadata and customization schema
  thumbnail.png     # Preview image
  layouts/
    main.ejs       # Main layout wrapper
  pages/
    default-home.ejs
    page.ejs
    search.ejs
  account/          # User account pages
  auth/             # Login, register, etc.
  errors/           # 404, 500, etc.
  partials/         # Reusable components
  assets/
    css/
      theme.css
    js/
    images/

theme.json Configuration

theme.json
{
  "name": "My Theme",
  "slug": "my-theme",
  "version": "1.0.0",
  "description": "A beautiful theme for SovereignCMS",
  "author": "Your Name",
  "author_url": "https://yoursite.com",
  "license": "MIT",
  "preview": "thumbnail.png",

  "customization": {
    "colors": {
      "label": "Colors",
      "fields": {
        "primary_color": {
          "type": "color",
          "label": "Primary Color",
          "default": "#8b5cf6"
        },
        "secondary_color": {
          "type": "color",
          "label": "Secondary Color",
          "default": "#06b6d4"
        },
        "background_color": {
          "type": "color",
          "label": "Background",
          "default": "#0a0118"
        },
        "text_color": {
          "type": "color",
          "label": "Text Color",
          "default": "#ffffff"
        }
      }
    },
    "typography": {
      "label": "Typography",
      "fields": {
        "font_family": {
          "type": "select",
          "label": "Font Family",
          "options": ["Inter", "Roboto", "Open Sans", "Poppins"],
          "default": "Inter"
        },
        "base_font_size": {
          "type": "number",
          "label": "Base Font Size (px)",
          "min": 12,
          "max": 20,
          "default": 16
        }
      }
    },
    "header": {
      "label": "Header",
      "fields": {
        "header_style": {
          "type": "select",
          "label": "Header Style",
          "options": ["default", "centered", "minimal"],
          "default": "default"
        },
        "sticky_header": {
          "type": "boolean",
          "label": "Sticky Header",
          "default": true
        }
      }
    }
  },

  "zones": {
    "header": {
      "label": "Header",
      "widgets": ["logo", "menu", "search", "user-menu"]
    },
    "main": {
      "label": "Main Content",
      "widgets": ["*"]
    },
    "sidebar": {
      "label": "Sidebar",
      "widgets": ["*"]
    },
    "footer": {
      "label": "Footer",
      "widgets": ["text-block", "menu", "social-links"]
    }
  }
}

Customization Field Types

  • color - Color picker
  • text - Text input
  • number - Numeric input with min/max
  • select - Dropdown with options array
  • boolean - Toggle/checkbox
  • image - Image upload

Use depends_on to conditionally show fields (e.g., "depends_on": "header.sticky_header")

Using CSS Variables

Customization values are injected as CSS variables. Use them in your theme CSS:

theme.css
/* Variables are automatically injected from theme customization */
:root {
  --primary: var(--theme-primary-color, #8b5cf6);
  --secondary: var(--theme-secondary-color, #06b6d4);
  --bg: var(--theme-background-color, #0a0118);
}

.header {
  background: var(--primary);
}

.button {
  background: linear-gradient(135deg, var(--primary), var(--secondary));
}

Creating Language Packs

SovereignCMS supports 37+ languages with a comprehensive translation system. Language packs consist of JSON files organized by category.

Language Pack Structure

languages/
  config.json       # Language metadata
  en/                # English (or your language code)
    core.json       # Core system translations
    admin.json      # Admin panel
    account.json    # User account pages
    pages.json      # Page templates
    validation.json # Validation messages
    modules/        # Module translations
      blog-manager.json

config.json Language Entry

config.json (entry for your language)
{
  "es": {
    "name": "Spanish",
    "native_name": "Español",
    "flag": "es",
    "direction": "ltr",
    "locale": "es_ES",
    "date_format": "DD/MM/YYYY",
    "time_format": "HH:mm",
    "number_format": {
      "decimal": ",",
      "thousands": ".",
      "currency_symbol": "€",
      "currency_position": "after"
    },
    "pluralization": "spanish",
    "active": true
  }
}

Translation File Format

es/core.json
{
  "common": {
    "save": "Guardar",
    "cancel": "Cancelar",
    "delete": "Eliminar",
    "edit": "Editar",
    "view": "Ver",
    "search": "Buscar",
    "loading": "Cargando..."
  },
  "navigation": {
    "home": "Inicio",
    "profile": "Perfil",
    "settings": "Configuración",
    "logout": "Cerrar sesión"
  },
  "errors": {
    "404": "Página no encontrada",
    "500": "Error del servidor",
    "generic": "Algo salió mal"
  }
}

Module Translation File

es/modules/blog-manager.json
{
  "module": {
    "name": "Gestor de Blogs",
    "description": "Sistema de gestión de múltiples blogs"
  },
  "common": {
    "blog": "Blog",
    "blogs": "Blogs",
    "post": "Publicación",
    "posts": "Publicaciones"
  },
  "public": {
    "allBlogs": "Todos los Blogs",
    "browseBlogs": "Explora nuestra colección de blogs",
    "noBlogs": "No hay blogs disponibles."
  },
  "admin": {
    "manageBlogs": "Gestionar Blogs",
    "createNewBlog": "Crear Nuevo Blog"
  }
}

Using Translations

In EJS templates
<%= t('core.common.save') %>
<%= t('modules.blog-manager.public.allBlogs') %>
<%= t('core.errors.404') %>

Important: Use the English language pack as a reference to ensure all translation keys are included. Missing keys will fall back to English automatically.

RTL Support: For right-to-left languages (Arabic, Hebrew, Urdu, Persian), set "direction": "rtl" in the language config.

Animated Backgrounds

Animated backgrounds use CSS gradients and pseudo-elements for performance. Theme customization controls the colors.

Background CSS Pattern

Animated background with CSS
/* Base gradient background */
body {
  background: linear-gradient(135deg, var(--bg) 0%, var(--bg-dark) 100%);
}

/* Animated nebula effect using pseudo-element */
body::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background:
    /* Purple glow */
    radial-gradient(circle at 20% 80%, rgba(139, 92, 246, 0.15) 0%, transparent 50%),
    /* Cyan glow */
    radial-gradient(circle at 80% 20%, rgba(6, 182, 212, 0.1) 0%, transparent 50%),
    /* Amber accent */
    radial-gradient(circle at 40% 40%, rgba(245, 158, 11, 0.05) 0%, transparent 50%);
  pointer-events: none;
  z-index: -1;
  animation: nebulaPulse 10s ease-in-out infinite alternate;
}

@keyframes nebulaPulse {
  from { opacity: 0.8; }
  to { opacity: 1; }
}

Submission Checklist

Before submitting your product to the marketplace:

  • All configuration files (module.json / theme.json) are valid JSON
  • Preview image/screenshot is included (PNG, 1200x800 recommended)
  • Version number follows semantic versioning (MAJOR.MINOR.PATCH)
  • CMS compatibility version is specified in "requires"
  • No hardcoded paths - use relative paths only
  • Tested on a fresh SovereignCMS installation
  • ZIP contains only the product folder at root level
  • Includes English translations as base language
Back to Documentation View API Reference