By Jared Lyvers, ldnddev — September 5, 2026
Your next marketing site does not need a CMS
Most 5–20 page marketing sites do not need a database. They need pages, a header, a footer, a handful of components, images that actually exist, and HTML you can host anywhere. What they often get instead is WordPress for a brochure, a plugin tax, and a server that has to stay patched because someone might log in once a quarter to change a headline.
That split is familiar. Marketing wants to ship copy without a ticket. Design wants the hero, the CTA, and the tokens to stay on-system. Developers want a build they can review in git and drop on a CDN. The usual compromise is a CMS that none of those three teams fully trust.
dd_siteforge 1.7 is our terminal CMS for that job. You author the site as JSON. The TUI edits the tree. Export writes static HTML plus CSS and JS from a Grunt build. There is no database, no multi-user CMS, and no server after export. Host it on S3, Netlify, a cheap nginx box, or next to the Drupal site that still needs a CMS for the real application.
1.7 is the current ship: install script, init-site that seeds templates and the Grunt kit, four-pane TUI, region-scoped insert, image and page pickers, validate that refuses bad slugs and missing files, export that writes sitemap and 404, preview on loopback. If you run campaigns, own the design system, or ship the HTML, this is for you. Clone 1.7, init a site, and try the next brochure on a static build before you spin up another WordPress.
What 1.7 actually is
One Rust binary. Version is dd_siteforge 1.7.0. The workflow is short: init-site, drop images in source/images/, open the TUI, npx grunt build, export or preview. JSON is the source of truth. Handlebars templates live in source/templates/. The Grunt kit, Lando, and DDEV wrappers are optional around the same Node commands. Host Node is the contract.
The TUI is the CMS you actually open. The export is the site. The editor is four panes. 1 is Regions: site settings, header, footer. 2 is Pages: sitemap order, add, rename, delete. 3 is Layout: the tree for the current region. 4 is Details: an ASCII blueprint you can scroll and click. Tab still moves between pages, not panes — that is on purpose. You are editing a site, not a window manager.
Autosave rewrites JSON two seconds after a change. Manual save also writes site.json.backup. Quit is Ctrl+Q, with a confirm if dirty. F1 is the full key and mouse list. F2 is theme source and tokens; the version lives there, not in the header title. F3 validates. Shift+E exports. p exports, serves locally, and opens the current page in a browser.
This is the same family as dd_emailforge: a typed JSON document, a TUI that will not let you nest illegal children, and an export you can actually ship. Email is one file. A marketing site is a tree of pages. 1.7 is the site-shaped version of that idea.
One JSON tree for marketing, design, and engineering
Git-based CMSs exist because teams got tired of content living only in a database. Sveltia and the older Decap/Netlify CMS line still assume a Jamstack generator and a browser admin. That is a good fit for a large content site. It is heavy for a 12-page campaign microsite. Jamstack’s useful part for this job is simpler: pre-render the front end, serve files, keep the server out of the critical path.
dd_siteforge keeps that without a headless stack. Marketing gets pages and copy fields. Design gets Framework components — dd-hero, dd-section, dd-cta, and the rest of the insert list — plus site-level CSS colors in settings. Those names are the same contracts as framework.ldnddev.com. Developers get Handlebars, Grunt output under web/assets, a sitemap, robots.txt, and a 404 if you did not author one. Everyone reviews the same site.json in a pull request.
Insert is region-scoped. Pages can take a hero, a section, a CTA. Header can take search and menu. Footer cannot take a hero. That is the design-system rule in the editor, not a style guide PDF someone will ignore on Friday. FormEdit is a form on the selected node: title, subtitle, copy, class, animation, image. Ctrl+P in a URL field opens an image picker rooted at source/images/, or a page picker for internal links. You are not free-typing paths that 404 on export.
We have been on that CMS-vs-static question for a while — including headless versus traditional. 1.7 is the third option we actually use for ldnddev marketing pages: Framework HTML, no runtime CMS.
Edit like a CMS, insert like a design system
After init-site, the shell is already a site: header, footer, a starter hero. Press 1, select Site, Enter. Name, lang, base URL, export dir, four CSS color fields. Ctrl+S writes the existing Site fields. That is the brand pass for design without opening a stylesheet first.
Press 2 for pages. Shift+A adds. r renames. Sitemap order is the list. Marketing can add “Pricing” without asking engineering to scaffold a route. Press 3, /, type to filter, Enter to insert after the current layout row. The picker only offers kinds that are legal here. That is how a designer stays on-system and a marketer still ships a new section this afternoon.
Details is the blueprint. 4 focuses it. j/k scrolls. Click a box to select the matching layout row. Double-click edits. You do not have to walk the tree to find the CTA on the homepage. Layout still has the usual edits: move, delete, duplicate, undo. None of that is a visual page builder. It is a structured tree with a map, which is what email-safe and Framework-native HTML actually are.
Images live in source/images/. In the TUI they look like assets/images/hero.jpg. Export copies them to web/assets/images/. Validation and export refuse to run when a local image is missing, a required field is empty, or slugs collide. That is the unglamorous half of “marketing can edit.” Empty alt and a 404 hero are not a later QA finding. They are a blocked export.
Ship the next brochure as static files
If a 12-page marketing site still means a CMS, we can help you put Framework HTML in git — and host it without a database.
Validate, export, host anywhere
Developers already know this loop. Build CSS and JS with host Node:
npm install
npx grunt build
That writes web/assets/{css,js,webfonts}. npx grunt dev rebuilds on change. Optional: lando start then lando grunt build, or DDEV the same way. Then:
dd_siteforge validate-site site.json
dd_siteforge export-html site.json ./web/
dd_siteforge serve site.json
Serve is export plus http://127.0.0.1:8765/. Inside the TUI, Shift+E prompts for a directory the first time and remembers export_dir. Preview opens the current page. Export copies Grunt assets unless the dest already is that tree, fills missing webfonts and favicon from source/, writes sitemap.xml and robots.txt, and adds 404.html when you have no author 404 page.
After that, the site is files. Put them on a CDN. That is the same reason we care about edge deployments for marketing properties: less origin, less patching, less “the CMS is down so the campaign page is down.” Drupal and WordPress still earn their keep for applications. A landing site that is only HTML should not share that runtime. If you already operate those CMS sites on Pantheon with a cockpit like dd_pantheon, keep them. Do not put the brochure on the same origin out of habit.
This also matches how we already work in the terminal — the TUI-and-framework thread from building better workflows. 1.7 is not a new religion. It is the Framework you already use, with an editor that does not require a PHP stack to change a headline.
How each team uses the same file
Marketing. Init a site. Add pages. FormEdit the hero copy. Preview in the browser with p. You do not learn Ratatui as a lifestyle. You learn four panes and Enter. The sitemap is the page list. If export blocks, read the first error — colliding slug, empty field, missing image. That is the ticket you would have gotten after launch.
Design. Stay on Framework components. Insert only what the region allows. Set site colors in settings. Put real assets in source/images/, not a Desktop folder. The blueprint is the layout map. You are not fighting a theme that rewrites your CTA on export. You are filling the same contracts the live site already uses.
Developers. Own the Grunt build, the templates, Lando or DDEV if you want them, and the host. Review JSON in git. Run validate in CI if you like. Do not stand up MySQL for a 12-page site. If the client later needs a CMS, you did not marry the brochure to one. You exported HTML. You can still wrap it, migrate it, or leave it static.
There is extra CLI when you are scaffolding, not editing: init-templates, init-scaffold, and init-scaffold --global for a house overlay under ~/.config/ldnddev/dd_siteforge/. show-site dumps the JSON. Use those when you are repeating a starter, not when you are changing a headline.
The shared rule: one editor at a time, because it is one file. That is a feature. Parallel edits belong in git branches, not in a race on a production database. A marketer on a branch named pricing-page and a designer on hero-image can merge like any other deliverable. That is the whole point of JSON as source of truth.
Get 1.7 from GitHub and try it
The project is MIT licensed: github.com/ldnddev/dd_siteforge. Confirm with dd_siteforge --version — you want 1.7.0. The illustrated tutorial in the repo is the same walkthrough we used here.
From a checkout:
./install.sh
mkdir my-site && cd my-site
dd_siteforge init-site site.json --name my-site
npm install
npx grunt build
dd_siteforge tui site.json
The installer puts a release binary in ~/.local/bin and writes a default theme only if one is missing. Uninstall is ./install.sh uninstall. Theme lookup is project YAML, then ~/.config/ldnddev/dd_siteforge_theme.yml, then built-in defaults, version: 1 required.
Give it a real brochure, not a demo homepage. Add the pages marketing actually needs. Drop the real logo in source/images/. Set colors. Validate. Export. Open the HTML. If F3 blocks, fix the file — do not patch the export. Your next marketing site can be a static build. Pull 1.7, try it on last quarter’s campaign page, and tell us which component is still missing from the insert list.
Until next time, Jared Lyvers