Usage

Pages are stored as Markdown files in content/pages/. Each file has a YAML frontmatter block at the top followed by the page body in Markdown.

URL mapping

File path Public URL
content/pages/index.md /
content/pages/about.md /about
content/pages/blog/index.md /blog
content/pages/blog/hello-world.md /blog/hello-world

Frontmatter reference

---
title: My Page
description: A short description for SEO meta tags.
status: published        # published | draft  (draft pages return 404)
layout: default          # layout name from config/presets.json
tags: [one, two]         # optional taxonomy
updatedAt: 2026-03-01    # auto-set on save
---

# Page heading

Your Markdown content goes here.

Drafts

Pages with status: draft are never served on the public site — they return a 404. Toggle status in the Page Editor to publish.

Layouts

Layouts are defined in config/presets.json and apply CSS class wrappers around page content. Select a layout per-page in the editor, or change the default in Site Settings.

Media files are stored in content/media/ and served publicly at /media/{filename}.

Using media in pages

Reference uploaded files by their public URL in Markdown:

![Alt text](/media/my-image.jpg)

<img src="/media/my-image.jpg" alt="Alt text">

Upload limits

The maximum file size is configured in config/server.json under uploads.maxFileSize (bytes). Default is 10 MB.

The navigation tree is stored in config/navigation.json and injected into every public page as window.__CMS_NAV__.

Dropdowns

Nest items under a parent using the drag-and-drop tree in the Navigation editor. Child items are stored under the items key — the public navbar renders them as a dropdown.

External links

Set the URL to a full https:// address to link outside the site. Leave blank for section headings.

Icons

Set the icon field to any registered icon name. Icons appear in the navbar link alongside the label.

DConfig lets you wire up declarative behaviour on any public page — no JavaScript required. Define event handlers and class toggles in JSON; the page runtime applies them automatically on load.

DConfig can be set in two ways, and both are merged at runtime (inline shortcode wins on selector conflict):

  • Page editor — the DConfig collapsible section accepts a JSON blob stored in page frontmatter and injected as window.__CMS_DCONFIG__.
  • Inline shortcode — place one or more [dconfig]…[/dconfig] blocks directly in the page body Markdown.

Schema

{
  "#selector": {
    "events": {
      "eventName": {
        "target": "#target-selector",
        "action": "value"
      }
    }
  }
}

Multiple selectors and multiple event types can appear in a single config block.

Supported events

Event Description
click Fires when the selector element is clicked.

Supported actions

Action key Value type Description
toggleClass string Adds the class if absent, removes it if present, on the target element. Typical use: show/hide content with a hidden class.

Additional actions (addClass, removeClass, setAttribute, scroll-to) are planned for future releases.

Targeting shortcode elements with id

All shortcodes support an id attribute, which is written directly onto the generated element. Use this instead of raw HTML when you want DConfig to target a card, column, or grid:

[card id="my-panel" title="Hidden Details"]
Content goes here.
[/card]

[dconfig]
{
  "#my-btn": {
    "events": {
      "click": { "target": "#my-panel", "toggleClass": "hidden" }
    }
  }
}
[/dconfig]

Supported on: [card], [col], [row], [grid].

Using the [dconfig] shortcode

[dconfig]
{
  "#my-btn": {
    "events": {
      "click": { "target": "#my-panel", "toggleClass": "hidden" }
    }
  }
}
[/dconfig]

<button id="my-btn" class="btn btn-primary">Toggle</button>

<div id="my-panel" class="card hidden">
  <div class="card-body">Hidden content.</div>
</div>

Multiple selectors

Any number of selectors can appear in one block. Selectors are matched with document.querySelector — use IDs, classes, or attribute selectors.

[dconfig]
{
  "#show-btn": {
    "events": {
      "click": { "target": "#panel", "toggleClass": "hidden" }
    }
  },
  "#hide-btn": {
    "events": {
      "click": { "target": "#panel", "toggleClass": "hidden" }
    }
  }
}
[/dconfig]

Using the page editor DConfig section

The same JSON (without the shortcode tags) can be pasted directly into the DConfig card in the page editor. This is useful when the config should apply to the whole page without appearing in the body content. Inline [dconfig] shortcodes are applied last and will win on any shared selector key.

See live demos on the Interactive Resources page.

Shortcodes extend Markdown with layout components. They are processed server-side before Markdown rendering, so you can freely mix shortcodes and Markdown in the same page body.

Syntax: [shortcode attribute="value"] (self-closing: [shortcode /]) and [/shortcode] for paired tags.

Layout

Shortcode Description
[grid cols="N" gap="N"]...[/grid] CSS Grid container — N columns (1–12), optional gap (1–6)
[row gap="N"]...[/row] Flexbox row — columns share width equally
[col span="N"]...[/col] Column inside a grid or row; span sets column span

Components

Shortcode Description
[card title="..."]...[/card] Styled card. Supports 6 variants, ~44 gradients and 33 layouts — see the Cards subsection below.
[tabs]...[/tabs] Tab group; use [tab title="..."]...[/tab] inside
[accordion]...[/accordion] Accordion group; use [item title="..."]...[/item] inside
[carousel]...[/carousel] Carousel; use [slide]...[/slide] inside
[badge variant="success"]...[/badge] Inline badge; variants: primary, secondary, success, danger, warning, info
[icon name="star" /] Inline Domma icon; optional size and color
[spacer size="16" /] Vertical whitespace block (px height)
[timeline]...[/timeline] Progression timeline; use [event title="..." date="..." status="..."]...[/event] inside
[countdown to="2026-12-31" /] Animated countdown to a date

Cards — variants, gradients & layouts

The [card] shortcode is the most configurable component in the system. It has three layers you can mix freely: variant (the overall visual style), gradient (colour palette, used with variant="gradient" or any layout that accepts a gradient), and layout (the internal structure — header/body/footer, image position, sub-tags, etc.). Every layout accepts every variant, gradient and universal attribute listed below.

[card variant="gradient" gradient="ocean" layout="icon-top" icon="star" title="Fast"]
Body content in **Markdown** is supported.
[/card]

Variants

Variant Description
clean Flat, minimal chrome — no border, subtle background
gradient Coloured gradient background (see Gradients below). Pair with gradient="name"
glass Frosted glass / backdrop-blur effect, translucent background
accent Primary-colour accent border on the left edge
dark Dark background with inverted text — good on light pages
glow Subtle halo / outer glow effect
primary Legacy alias — adds card-primary class for backwards compatibility

Built-in gradients

Shipped in public/css/ — available on every install. Default when gradient is omitted: indigo.

arctic, aurora, dusk, fire, forest, gold, indigo, lagoon, lime, midnight, ocean, rose, slate, sunset

Project-specific gradients

Defined in content/custom.css on this site only — fresh installs will not have these unless the CSS is copied over.

Single-tone: purple, blue, green, night

Theme · light pairs: ocean-light, forest-light, sunset-light, royal-light, lemon-light, silver-light, charcoal-light, christmas-light, unicorn-light, dreamy-light, grayve-light, mint-light, wedding-light

Theme · dark pairs: ocean-dark, forest-dark, sunset-dark, royal-dark, lemon-dark, silver-dark, charcoal-dark, christmas-dark, unicorn-dark, dreamy-dark, grayve-dark, mint-dark, wedding-dark

Gradient names are not validated — a typo falls through to the default background with no warning. Use /test-card-gradients to preview every palette.

Universal attributes

These work on any layout and any variant.

Attribute Values Purpose
title string Card heading (most layouts)
subtitle string Secondary heading (many layouts re-use it as date / role / plan)
icon icon name Domma icon (see Icons view)
footer string Footer text (also used by pricing as the CTA label)
image URL Background image for image / media layouts
collapsible true Body toggles open/closed via click on the header
hover boolean flag Raises the card on mouse-over
borderless boolean flag Removes the card border
shadow none · md · lg Drop-shadow depth
rounded none · sm · lg · full Corner radius
padding compact · spacious Internal spacing
text-align center · right Body text alignment (default left)
font serif · mono Override card typeface
font-size sm · lg · xl Override card body text size
class string Extra CSS classes appended to the root element
id string DOM id (useful for anchor links / dconfig targets)

Layouts

Select a layout with layout="name". If omitted, the card uses a simple body-only layout. Layouts marked (needs sub-tags) will render empty without their child tags.

Structural
Layout Attributes Purpose
basic footer Body-only card, optional footer
header-body title Header with title + body
header-body-footer title, footer Classic three-zone card
no-header-footer footer Body + footer only
Icon headers
Layout Attributes Purpose
icon-top icon, title, subtitle Large centred icon above title — good for feature grids
icon-inline icon, title, subtitle Icon sits to the left of the title in the header row
Image
Layout Attributes Purpose
image-top image, title Image banner above header + body
image-overlay image, title Title overlaid on the image with a tint
thumb-left image, title Square thumbnail on the left, body on the right
thumb-right image, title Mirror of thumb-left
wide-left-image image, title, footer Horizontal card, wider image strip on the left
full-bg image, title Full-bleed image backdrop with body text over it
split-half image, gradient, title Half gradient (or image) on the left, content on the right
Media
Layout Attributes Purpose
video-media image (poster), duration, title Video thumbnail with a play button and duration badge
location-map address, title Placeholder map panel + address block (static — no live map)
People & social proof
Layout Attributes Purpose
avatar-profile icon, title, subtitle, tags (comma-separated) Circular avatar + name + role + pill tags — team / author cards
quote-testimonial title (author), subtitle (role) Quotation mark + body quote + author attribution row
rating-review rating (0–5), title, subtitle, verified (flag) Star rating + review body + reviewer attribution
Metrics & data
Layout Attributes Purpose
stat-metric title (label), value, delta, progress (0–100) Big-number KPI tile with delta arrow and optional progress bar
progress-goal (needs sub-tags) title, subtitle, progress; child [milestone done]...[/milestone] Progress bar + milestone checklist
activity-feed (needs sub-tags) title; child [activity user="..." action="..." time="..." /] User activity list with initials avatars and timestamps
Markers & callouts
Layout Attributes Purpose
callout callout-type (info/warn/success/error), icon, title Coloured inline notice / tip box
step-numbered step (number), title, gradient Numbered step for tutorials / walkthroughs
corner-badge badge (label), icon, title Card with a ribbon-style badge in the top-right corner
badge-band badge, icon, gradient, title Full-width coloured band across the top with a label and icon
tag-cloud title, tags (comma-separated) Coloured pill list — topic tags, skills, categories
timeline-entry title, subtitle (date), badge (tag) Single dot-on-line timeline entry — stack several for a history list
Commerce & comparison
Layout Attributes & sub-tags Purpose
pricing (needs sub-tags) title, price, period, gradient, footer (CTA label); child [feature]...[/feature] Pricing tier card with gradient header, feature list and CTA link
feature-comparison (needs sub-tags) title, subtitle (plan), gradient; child [feature]text[/feature] or [feature excluded]text[/feature] Plan comparison rows with tick / cross marks
before-after (needs sub-tags) title; children [before]item · item[/before] and [after]item · item[/after] (items split on · or newline) Two-column before/after comparison list
Special & utility
Layout Attributes Purpose
glass-gradient-border title Frosted glass card with a gradient border ring. Note: this layout bypasses cardVariantClasses, so universal variant / gradient / shadow attributes do not apply.
code-snippet lang (label); body is rendered as escaped code Code block with language label header
file-document filename, filesize, filetype, title File attachment row with type icon and download link (link href is placeholder)

Sub-tag reference

These are child tags accepted by specific layouts — they are not shortcodes in their own right and only render inside a matching parent card.

Sub-tag Parent layout Attributes
[feature]text[/feature] pricing, feature-comparison excluded (flag, comparison only) — renders as a cross instead of a tick
[before]a · b · c[/before] before-after Items split on · or newline
[after]a · b · c[/after] before-after Items split on · or newline
[activity user="..." action="..." time="..." /] activity-feed Self-closing; one row per tag
[milestone done]text[/milestone] progress-goal done (flag) — filled dot if present, hollow otherwise

Content

Shortcode Description
[hero variant="dark" size="sm"]...[/hero] Full-width hero section; supports twinkle, blobs, image, overlay
[table striped="true"]...[/table] Wraps a GFM Markdown table with Domma table CSS classes
[form name="slug" /] Embeds a Form Builder form by slug
[collection slug="..." display="table" /] Renders collection entries inline (table, cards, list, accordion, or block)

Collection attributes

Attribute Values Purpose
slug collection slug Required. Which collection to render.
display table · cards · list · accordion · block How to render each entry. Default table.
block block template name Required when display="block". Names a template in content/blocks/<name>.html whose {{field}} placeholders get substituted per entry.
cols 2 · 3 · 4 · 5 · 6 Grid column count when display="block".
where field=value or
f1=v1,f2=v2
Row filter (simple equality only, AND'd across comma-separated predicates). Example: where="tab=developers" or where="tier=free,featured=true". No OR, no comparison operators — use a saved View for anything more complex.
sort field name or createdAt Sort field. Default createdAt.
order asc · desc Sort direction. Default desc.
limit integer Maximum number of entries to render. 0 or omitted = all.
fields comma-separated field names Column filter — only these fields are shown (table/cards/list displays only).
title-field field name Which field to use as the entry title (cards/accordion displays).
empty string Message shown when the filter matches no entries.
cta action slug Attach a CMS Action button to each rendered entry. See CTA attributes below.

Form follow-up

Two optional settings control what happens after a submission is stored:

Field Where Description
successMessage Settings tab Text shown inline after submission (replaces the form). Default: "Thank you for your submission."
successRedirect Settings tab URL to redirect the visitor on success. Takes priority over successMessage if set.
actionSlug Actions tab → CMS Action Slug of a CMS Action to execute after the entry is stored. Requires Pro (MongoDB). Non-fatal on failure.

For the full walkthrough, see the Tutorials → Form Follow-Up: Notifications & Actions tutorial.

Interactive

Shortcode Description
[slideover title="..." trigger="..."]...[/slideover] Slide-in panel opened by a trigger button
[dconfig]{...}[/dconfig] Declarative click handlers and class toggles — no JavaScript needed

Pro shortcodes

Shortcode Description
[view slug="..." display="table" /] Executes a saved View and renders results. Requires MongoDB.
[cta action="..." entry="..."]Label[/cta] Action-trigger button. Requires the visitor to be logged in.

Full attribute reference and live demos: Shortcode Reference · Components · Effects

Site Settings are stored in config/site.json and editable from the Settings page in the admin panel.

Field Description
siteName Site title used in the public navbar and browser tab.
tagline Subtitle shown in the public site footer.
adminTheme Admin panel colour theme (e.g. charcoal-dark, ocean-light).
publicTheme Public site theme applied to <html data-theme>.
defaultLayout Fallback layout for pages that don't specify one.

Server configuration

Low-level settings (port, CORS, upload size) live in config/server.json and require a server restart to take effect. These are not editable from the admin panel.

Plugins extend the CMS with new server routes, admin views, and public-site injection snippets. Each plugin is a directory under plugins/.

Enabling and disabling

Use the Plugins page in the admin to toggle plugins on or off. State is saved to config/plugins.json. A server restart is required for changes to take effect.

Plugin settings

Plugins that expose a settings page will appear as a link in the sidebar under the Plugins section. Settings are merged with the plugin's defaults from config.js and saved to config/plugins.json.

Security

Only files inside a plugin's admin/ and public/ subdirectories are served statically. Server files (plugin.js, config.js, data/) are blocked and return 404.

See the Tutorials section for a guide to writing your own plugin.

Users are stored as individual JSON files in content/users/. Passwords are hashed with bcrypt and never returned by the API.

Roles

Role Access
Admin Full access — users, plugins, settings, all content.
Manager Content + structure (navigation, layouts). No users or plugins.
Editor Pages and media only.
Subscriber Read-only access to the API.

Authentication

The admin panel uses JWT Bearer tokens. Tokens are stored in the browser and automatically refreshed. Sessions expire according to config/auth.json — default is 24 hours.

Views & Actions

Views and Actions are pro-only features that require a MongoDB connection. Both roles — Admin and Manager — can create, edit, and run Views and Actions.

Role Views Actions
Admin Full access Full access
Manager Full access Full access
Editor No access No access
Subscriber No access No access

Views are admin-designed display configurations that query one or more Collections via a stored MongoDB aggregation pipeline, then render the results in the admin panel. Views require a MongoDB connection configured under Collections → Options.

Creating a View

  1. Navigate to Data → Views and click New View.
  2. Source tab — enter a title, choose the primary source collection, and select the MongoDB connection.
  3. Pipeline tab — add aggregation stages in order. Each stage has a type and a JSON config object.
  4. Display tab — choose table or list mode, set page size, and optionally define column definitions.
  5. Access tab — select which roles can access this view and whether it is publicly accessible.
  6. Click Save View, then use Preview to execute and inspect the results.

Allowed Pipeline Stage Types

Stage Purpose
$match Filter documents by condition
$lookup Left join from another collection
$sort Sort documents
$project Include or exclude fields
$unwind Deconstruct an array field
$addFields Compute and add new fields
$group Group and aggregate
$count Count documents
$skip Skip documents (added automatically for pagination)
$limit Limit documents (added automatically for pagination)

Forbidden stages ($out, $merge, $function, $accumulator, $graphLookup) are rejected at save and execution time.

Column Definitions

On the Display tab, column definitions are a JSON array. Each object has a key (dot-path into the result document) and a label.

[
  { "key": "data.name",  "label": "Name" },
  { "key": "data.email", "label": "Email" },
  { "key": "orders",     "label": "Orders" }
]

If no columns are defined, the preview auto-generates columns from the first result document.

Public Access

Tick Public on the Access tab to expose the view via GET /api/views/:slug/public without requiring a login. Leave it unticked to require JWT authentication with one of the allowed roles.

Actions are admin-designed sequential workflow operations triggered against individual collection entries. When an action is assigned to a collection, a trigger button appears per row in the entry list. Actions require a MongoDB connection (pro mode).

Creating an Action

  1. Navigate to Data → Actions and click New Action.
  2. General tab — enter a title and select the target collection.
  3. Trigger tab — configure the button label, icon, and optional confirmation message.
  4. Steps tab — add steps in order. Each step runs sequentially; if one fails the action stops.
  5. Access tab — select which roles can trigger this action.
  6. Click Save Action. The button will appear on the collection's entry list.

Phase 1 Step Types

Step What it does Config fields
updateField Set a field on the entry to a new value field, value
deleteEntry Permanently delete the entry None
moveToCollection Create the entry in a target collection then delete it from the source targetCollection
webhook HTTP request to an external URL url, method, body (JSON)
email Send an email via the configured SMTP transport to, subject, template

Template Variables

Step config fields support {{variable}} interpolation:

Variable Resolves to
{{entry.data.fieldName}} A field value from the current entry
{{entry.id}} The entry's ID
{{now}} Current timestamp (ISO 8601)
{{user.name}} Name of the user who triggered the action
{{user.email}} Email of the triggering user
{{env.CMS_PUBLIC_*}} Environment variables prefixed CMS_PUBLIC_ only

Example — approve an application and notify by email:

Step 1: updateField  field=status      value=approved
Step 2: updateField  field=approvedAt  value={{now}}
Step 3: email        to={{entry.data.email}}
                     subject=Your application has been approved
                     template=Congratulations {{entry.data.name}}, your application is approved.

Partial Execution

Actions are not transactional. If a step fails, the action stops and returns the number of steps completed so far (stepsCompleted). Steps that already ran are not rolled back. Design step order with this in mind — put irreversible steps (delete, email) last.

After a deleteEntry step

If an action contains a deleteEntry step, subsequent steps will fail because the entry no longer exists. Place deleteEntry as the last step.

The [cta] shortcode places an action-trigger button in any public page. Clicking it calls POST /api/actions/:slug/public with the entry ID, using the logged-in user's JWT. If the user is not logged in, a warning toast is shown instead.

Syntax & Attributes

Wrapping form:

[cta action="slug" entry="entry-id" icon="check" confirm="Are you sure?"]Button label[/cta]

Self-closing form:

[cta action="slug" entry="entry-id" label="Button label" /]
Attribute Required Default Description
action Yes Action slug
entry Yes Entry UUID to act on
label No "Run" Button text (self-closing only)
style No "primary" primary · secondary · ghost · danger
icon No Domma icon name
size No sm · md · lg
confirm No Confirmation prompt before executing

Collection Integration

Add per-entry buttons to any [collection] shortcode:

[collection slug="applications" display="cards" title-field="name"
  cta="approve-application"
  cta-label="Approve"
  cta-icon="check"
  cta-style="primary"
  cta-confirm="Approve this application?" /]
Attribute Default Description
cta Action slug — enables per-entry buttons
cta-label "Run" Button label
cta-icon Domma icon name
cta-style "primary" Button variant
cta-confirm Confirmation prompt

All three display modes support CTA buttons: cards (card footer), list (inline), table (dedicated column).