Contracta is a contract-driven web composition framework. A theme declares contracts and the modules that fill them; Contracta resolves those modules into trees, compiles the theme into a deterministic manifest, and gives hosts shared semantics for routes, navigation, and transitions, without depending on any host itself.
Core owns semantics. Adapters own environment integration. Themes own domain contracts. Sites own customization.
Status: 0.2.0 is the current release. Transition planning ships as
experimental; see the release condition.
flowchart LR
subgraph authored["Authored by a theme and a site"]
theme["Theme<br/>contracts, modules, renderers"]
components["Site's own<br/>Svelte components"]
end
subgraph here["This repository"]
compiler["@contracta/compiler<br/>validate + compileTheme"]
manifest[("Manifest<br/>deterministic JSON")]
build["@contracta/svelte-build<br/>buildTheme"]
core["@contracta/core<br/>resolution, routes,<br/>navigation, transitions"]
end
subgraph site["In the browser"]
built["Built theme<br/>one Svelte component"]
mounts["Mount points<br/>on the page"]
runtime["@contracta/svelte<br/>runtime adapter"]
end
theme --> compiler --> manifest --> build
components -- "bound by<br/>specifier" --> build
build --> built --> mounts
core -.-> compiler
core -.-> build
core -.-> runtime
runtime -- "URL picks each<br/>mount point's module" --> mounts
A theme is authored with the Compiler's helpers and compiled into a manifest. The Svelte build resolves every module of the manifest with Core and generates one Svelte component, the built theme, which imports the site's own components through bindings from renderer identities to module specifiers. A site mounts the built theme at each mount point it lays out, and the runtime adapter, in its own repository, keeps each mount point's module in step with the URL.
| Package | Role | Documentation |
|---|---|---|
@contracta/core |
Contracts, modules, scopes, and resolution; route state, navigation intent, module identity, and transition planning (experimental) | Core model, resolution, route state, navigation intent, transition plan |
@contracta/compiler |
Typed definition helpers, theme validation with structured diagnostics, and deterministic manifests | Compiler API, manifest format |
@contracta/svelte-build |
A build tool, not a runtime library: turns a manifest, and bindings to a site's own Svelte components, into one mountable Svelte component, the built theme | Svelte build |
Core and the Compiler depend on no host: no Hugo, Svelte, DOM, filesystem renderer, or domain vocabulary. The Svelte build depends on Core, the Compiler, and the Svelte compiler only.
The packages are ES modules and run on Node.js 22 or later. The Compiler and the Svelte build name the Contracta packages they work with as peer dependencies, so a project installs them side by side and every package shares one copy of each:
npm install @contracta/core # resolution, routes, plans
npm install @contracta/compiler @contracta/core # themes and manifests
npm install --save-dev @contracta/svelte-build @contracta/compiler @contracta/core svelteUpgrade them together. The three are released at the same version, and before
1.0 each accepts only its own minor line of the others (^0.2.0). npm refuses
a mixed set with ERESOLVE whenever the package being installed needs a newer
sibling than the project has, as when the Compiler or the Svelte build is
upgraded alone. Upgrading Core alone only warns, and leaves a set the packages
do not support.
pnpm 12 installs a version published less than a day ago anyway, and records it
under minimumReleaseAgeExclude in pnpm-workspace.yaml; commit that entry. A
project that sets minimumReleaseAge itself is refused with
ERR_PNPM_NO_MATURE_MATCHING_VERSION until the version is listed there or a day
has passed.
Host adapters, themes, and site integrations live in their own repositories and
publish under the same @contracta scope:
| Package | Repository | Role |
|---|---|---|
@contracta/svelte |
contractajs/contracta-svelte | Svelte runtime adapter: drives a site's mount points from its URL with Core's routes and navigation. Not yet published. |
Author a theme, compile it, and resolve a module:
import {
compileTheme,
defineContract,
defineModule,
defineTheme,
} from '@contracta/compiler'
import { resolveModule, single } from '@contracta/core'
const panel = defineContract({ id: 'panel', renderer: { id: 'panel' } })
const page = defineContract({
id: 'page',
renderer: { id: 'page' },
children: { main: single('panel') },
})
const home = defineModule({
id: 'home',
contract: 'page',
children: { main: { contract: 'panel', props: { heading: 'Welcome' } } },
})
const theme = defineTheme({
id: 'minimal',
contracts: { page, panel },
modules: { home },
renderers: [{ id: 'page' }, { id: 'panel' }],
})
// Deterministic JSON text; an invalid theme throws CompilationError.
const manifest = compileTheme(theme)
// The tree a host renders: each node with its contract, renderer, props,
// merged scope, and children.
const tree = resolveModule(home, { contracts: theme.contracts })For the whole chain, from a theme to a page that mounts it, run the site example. The minimal example adds routes, navigation, and transition plans.
- Composition: core model, resolution
- Routes and navigation: route state, navigation intent, transition plan (experimental)
- Compilation: compiler API, manifest format
- Svelte: Svelte build
- Project: project contract, development flow, framework semantics backlog, changelog, agent and contributor rules
- Decisions: ADR 0001: record architecture decisions, ADR 0002: adopt OpenSpec, ADR 0003: adapters enforce Core's continuity decision, ADR 0004: a rule moves into Core when agreement is required
The living specification is in openspec/specs/.
Requirements: Node.js 24 and Corepack. The exact pnpm version is pinned in
package.json.
corepack enable
pnpm install
pnpm run build
pnpm test
pnpm run typecheck
pnpm run lint
pnpm run format:check
pnpm run changelog:checkAfter building, run the examples:
pnpm run example:minimal
pnpm run example:siteChanges follow OpenSpec and land through squash-merged pull requests; see development flow and AGENTS.md.
Licensed under either Apache-2.0 or MIT, at your option.