CMS

Under the hood

A page is a declarative document validated against one schema and rendered by a native player - with data reaching it only through the engine's contract, which is what makes access rules hold on a screen

A page is a document, not a program

A page is a declarative description in JSON: which widgets, in what arrangement, with what properties, bound to what.

That is the decision everything else on this page follows from. A page that is a program can only be understood by running it; a page that is a document can be validated, diffed, generated, reviewed and rolled back.

One schema validates all of it, not a validator per widget maintained by whoever added that widget. So a malformed page fails at the moment it is written instead of as an empty rectangle in front of a user, and an agent writing pages cannot invent a property that does not exist.

And it is rendered by a native player, not by generating markup ahead of time. The description arrives, the player draws it. That is what allows the same description to be edited on a canvas and shown to a user without two code paths that disagree.

Widgets are declared

Every widget in the catalogue declares its contract: the properties it accepts with their types, the events it emits, what it can be bound to.

Three consequences, and they are the reason the catalogue can be large without collapsing:

  • The editor is generic. The properties panel is generated from the declaration instead of hand-built per widget, so adding a widget does not mean building its editor as well.
  • The agent works from the same declaration. It is not trained on your widget set; it reads it. A widget added last week is available to it immediately, with its real properties.
  • Validation is uniform. One mechanism checks every widget, so a widget nobody has used in a year is as safely configured as the popular ones.

Data reaches a page one way

A page has no direct path to storage. Everything it shows comes through the query engine's contract - the same /api/query and /models used by dashboards, MCP tools and AI employees.

That is what makes access control real on a screen. A widget does not fetch rows; it asks a question, and the engine answers it for the person looking - with row policies and column masks compiled into the query plan. There is no page-level permission model maintained beside the data-level one, because there is no second path for a page to get data through.

See Data engine and Access control in the query plan.

Conditional visibility uses the process engine's predicates

Whether a block is shown is a condition, and it is evaluated by the same predicate engine that decides process transitions.

The same one, not a similar one, so "show the approve button when the request is pending and the viewer may approve" and "allow this transition when the request is pending and the actor may approve" are expressed identically and cannot drift into two dialects with different edge cases.

Charts come from one dispatcher

A chart on a page is not a page-specific chart implementation. The same dispatcher that draws charts on a dashboard draws them here: builders return data structures, components render them, and adding a chart type is one entry in one place instead of an edit to every surface that shows charts.

So a chart behaves the same wherever it is mounted - including its drill-down, which lives in the chart component instead of whatever is wrapping it. See Analytics under the hood.

Themes: a design system, not per-page styling

Appearance is not a property of a page. It lives in a theme, held per organisation, and every page reads from it.

A theme carries four kinds of thing, and the split is what makes it usable and not a colour picker:

  • Tokens - the palette and the values everything else is expressed in. A widget does not carry a hex code; it refers to a token. Change the token and every widget that used it changes, including ones nobody thought about at the time.
  • Typography - the families, the scale, the weights and the rhythm, so headings are consistent across pages built by different people in different months.
  • Density - how much air the interface has. The same screens for an operator on a tablet and an analyst on a wide monitor should not be the same density, and that is one setting instead of a hundred spacing decisions.
  • Component-level overrides - where a specific component should look different in your brand, that is expressed once, in the theme, instead of as a property set on every instance of it.

Light and dark are part of the theme, not a second theme to maintain in parallel: the dark palette is defined alongside the light one, so a page authored once works in both and a viewer can switch.

What that lets you do

Concretely, because "themeable" is a word every product uses:

  • Put your brand book in once. Primary, accent, surface, border, the states - defined as tokens, and every widget that ever used them follows, including ones added later.
  • Change the shape of things globally. Corner radius, border weight, shadow depth, the scale of spacing. A platform that looks sharp and dense, or soft and airy, is a handful of values, not a redesign.
  • Set your type once. The families, the sizes, the weights and the line heights - so a heading on a portal page and a heading in an application are the same heading.
  • Give the warehouse tablet a compact density and the analyst's monitor a comfortable one, without either being a compromise or a separate set of pages.
  • Ship a dark palette for the people who need one - a control room, a night shift, a person who simply prefers it - from the same page definitions.
  • Override one component where your brand demands it. Your buttons are square and your inputs are not: said once, in the theme, instead of as a property on four hundred instances.
  • Run more than one theme. The employee portal in your corporate identity, a partner-facing site in a lighter variant, a campaign microsite in its own - all from one design system, so they are variants and not separate front-ends.
  • Try it before committing. Themes are previewed as they are edited, so "what would the whole platform look like in this palette" is a question you answer by looking.

And it degrades safely. A theme that carries only a palette - which is what the built-in ones were before tokens existed - still renders correctly: the player falls back to its own defaults for anything the theme does not specify. Adding detail to a theme is additive, not a migration.

Themes are edited in their own editor, with a tab per concern - colours, typography, density, components - and previewed rather than imagined. Several ship with the platform as starting points, and an organisation authors its own.

The reason this is worth the machinery: consistency stops depending on discipline. Forty pages built by six people over two years are consistent because they all resolve the same tokens, not because everybody remembered the same hex code.

The editor

A canvas over the same description: select a widget, move it, resize it, snap to the grid, edit its properties in a generated panel, undo and redo. Selection, history and scaling are the editor's own concerns; what it produces is the ordinary page description, so nothing about a page reveals whether it was drawn, generated or hand-written.

The AI editing path is the same surface rather than a parallel one: the assistant is handed the selected widget's type, its editable properties and their current values, and returns a small set of property edits. It changes what you would have changed, in the panel you would have changed it in.

Menus, side navigation, breadcrumbs, the app switcher and mega-menus can take their items from a master-data entity instead of from a hardcoded list. A menu of product categories is the category list; add a category and the menu has it.

See Master data.

What this means for an application

A page is an artifact of an application, like the data model and the processes: written into the repository, versioned with everything else, installed by the reconciler, and immutable once a version is published.

So a screen somebody rearranged is part of the release instead of a change made in production that nobody can account for - and moving an application to another organisation brings its interface with it. See Applications.