This page ships a Markdown twin for AI tools.

Documentation

Web and native, one contract — Texuli's architecture

One manifest projects to TypeScript and Kotlin: the spec seam that keeps web and native interfaces portable. What exists today, honestly.

Why both technologies

Texuli uses a different runtime on each side on purpose, and the reason is the same for both: each platform deserves the technology that fits it, not a compromise shared between them.

  • Svelte 5 for the web. The compiler carries the framework: components compile away, state is explicit through runes, and size is budgeted — pnpm size and the site-perf audit measure packages and pages, and the entries still over budget are tracked in the open. A runtime chosen with constrained devices, including phones, in mind.
  • Kotlin Multiplatform and Compose for native. One language that can reach desktop, iOS and Android. Today the Compose renderer runs on the JVM (desktop), the spec models also compile for iOS, and Android is pending a decision.

This is not one framework ported onto the other: Texuli does not emulate Compose in the browser or transpile Svelte to Kotlin. Each runtime stays the native idiom of its platform, and what Texuli owns is the contract between them.

The long-form reasoning lives in the guide Why Svelte and Kotlin; the native story has its own landing at Texuli native.

The layer graph: nine floors, dependencies point down

Every workspace package sits on exactly one floor, and a package may depend only on packages below it — never sideways, never up. The layer of every package is declared once inscripts/check-layers.ts, and pnpm check:layers enforces the graph on every manifest and every shipped file.

The layer graph as a table: every workspace package, the floor it sits on, what it holds, and the lower floors it may depend on.
LayerPackageWhat it holdsDepends on
L0@texuli/coreTexels: ids, keyboard intents, roving focus and typeahead, focus scope, inert, dismissable layers, layer stack, overlay engine, portal, positioning (math + DOM), presence, scroll lock, tabbable, form bridge, announcer, direction and locale, the URL policynone
L0@texuli/specFramework-free blueprint contract and code generationnone
L1@texuli/primitives41 owned headless primitives, one subpath each (accordion … tree), held to the WAI-ARIA APG patternscore
L2@texuli/tokensThe OKLCH theme engine, the --tx-* token contract, 200+ themes, compiled CSS, forced colors, the AA gate, the Tailwind presetnone
L3texuliStyled components (Button … Tree), the class helpers (cx, variants), icons, layouts and app shellscore, primitives, tokens
L4@texuli/chartsChart math and every chart family, one lazy subpath per familycore, primitives, tokens, texuli
L4@texuli/dataData views: grid (core, columns, editing, virtual), tree, board, calendar, timeline, map, activity, files, virtual listcore, primitives, tokens, texuli
L4@texuli/mediaImage, gallery, video, audio, captions, image editor, compositions, animation, exportcore, primitives, tokens, texuli
L4@texuli/chatThread, message list, composer, typing indicatorcore, primitives, tokens, texuli
L4@texuli/editorRich text editor surface, toolbar, link dialog, headless controller, clipboard importcore, primitives, tokens, texuli
L5@texuli/blocksContent blocks (text, code, data, diagram, embed, interactive, map, media, structure): closed vocabulary, untrusted guard, formula ASTcore, tokens, texuli, charts, data, media
L6@texuli/catalogThe agent catalog (manifests per group, build) and the renderer with no HTML sinkscore, texuli, charts, data, blocks, media
L7@texuli/slidesSlide model, grammar, export and stage runtimeblocks, catalog, media, tokens
L7@texuli/playerBlueprint player runtime and stylesblocks, catalog, charts, media
L8@texuli/toolsCopy-in CLI (texuli-add, texuli-init, texuli-update), texuli-llms, A2UI adapter, custom elements and the golden setcore, tokens, catalog, blocks, charts, media
L8@texuli/astroAstro integration, site helpers and content layoutsblocks, core, tokens
The diagram above repeats this table: floors L0 through L8, top floor L8, and every dependency pointing down — the rule scripts/check-layers.ts enforces.

The texel ladder: three rungs, two runtimes

Components are not invented per page — they compose up a fixed ladder, and the same ladder is the grammar on both runtimes: the web renders it in Svelte, the native renderer consumes the same catalog contract in Compose.

  1. texels

    Framework-free behavior and policy: ids, keyboard intents, focus, the overlay engine, positioning, scroll lock and the URL policy — no Svelte, no DOM framework.

    @texuli/core · layer L0

    Web
    Runs in the web runtime today, under every component.
    Native
    Texels do not cross: nothing in the native renderer ports @texuli/core. What both sides share is the semantic trace — the web trace and the native trace apply these rules to a validated render tree.
  2. primitives

    Headless parts bound to texels and held to the WAI-ARIA Authoring Practices patterns — 41 owned headless primitives, one subpath each.

    @texuli/primitives · layer L1

    Web
    Headless Svelte 5 parts; composition is left to the caller.
    Native
    No direct port: Kotlin never imports Svelte packages, so the contract carries the parts’ semantics instead.
  3. components

    Styled components, icons, layouts and app shells on the OKLCH theme engine — the rung visitors actually see.

    texuli · layer L3

    Web
    Ships: styled components are published on npm as 0.1.0-next.1.
    Native
    The Compose renderer (Status: in development) paints a documented slice; app-shell layouts and other chart families remain visible placeholders.

One contract, two projections

The seam is the spec contract: one authored catalog manifest, two generated projections — TypeScript under packages/spec/generated/typescript and Kotlin underpackages/spec/generated/kotlin. The demo below walks one component through that seam.

One line in, two languages out

Web and native correlate through one contract, not through a port. Follow the badge: its summary is written once in a catalog manifest, and the spec generator carries the same string into TypeScript and Kotlin with no manual sync.

  1. Authored once

    The badge manifest in the agent catalog: id, label and summary.

    packages/catalog/src/manifests/display/manifests.ts
    export const badgeManifest = defineCatalogManifest({
      id: 'badge',
      package: PKG,
      label: 'Badge',
      summary: 'A compact status or category label.',
      tier: 'app',
  2. TypeScript projection

    The generated vocabulary types the props; the summary travels in the spec catalog JSON it is generated from.

    packages/spec/generated/typescript/vocabulary.ts
    badge: {
      label?: string;
      variant?:
        | 'default'
        | 'secondary'
        | 'outline'
        | 'success'
        | 'warning'
        | 'destructive'
        | 'info'
        | 'neutral';
    };
    packages/spec/catalog/components.json
    "summary": "A compact status or category label."
  3. Kotlin projection

    The generated Kotlin vocabulary: the identical summary string.

    packages/spec/generated/kotlin/com/texuli/spec/Vocabulary.kt
    SpecComponent(
        id = "badge",
        label = "Badge",
        summary = "A compact status or category label.",
        group = "display",

One declaration, two projections. The Kotlin renderer consumes this contract (in development).

Native renderer: in development, consumed in place from the repository. Maven publication pending (ADR-005).

What moves today

  • Every component is declared once in the manifests of the agent catalog.
  • The spec generator generates both a TypeScript and a Kotlin projection of that contract, stamped with the same spec digest.
  • The spec package validates blueprints against that contract, with fixtures and tests in the repository.
  • The web renders the badge today as a real Svelte 5 component.

Planned

  • Maven publication of the native modules under com.texuli is pending (ADR-005); nothing is on Maven Central yet.
  • A released Compose renderer: today it is in development, consuming the generated Kotlin contract in place, with documented per-component statuses.
  • A rendered native column in this demo is planned for when the renderer leaves development.

Interface portability: the mechanism

Portability in Texuli is the shape of the repository, not a promise about the future: every component is declared once in the manifests and projected into both languages, so what moves between the runtimes is the contract itself — and everything the contract can prove.

One declaration, two projections

Declared once — the catalog manifest: packages/catalog/src/manifests/display/manifests.ts
export const badgeManifest = defineCatalogManifest({
  id: 'badge',
  package: PKG,
  label: 'Badge',
  summary: 'A compact status or category label.',
  tier: 'app',
Projected to TypeScript — shipped in the npm packages: packages/spec/generated/typescript/vocabulary.ts
  badge: {
    label?: string;
    variant?:
      | 'default'
      | 'secondary'
      | 'outline'
      | 'success'
      | 'warning'
      | 'destructive'
      | 'info'
      | 'neutral';
  };
Projected to Kotlin — generated and tested in this repository: packages/spec/generated/kotlin/com/texuli/spec/Vocabulary.kt
        SpecComponent(
            id = "badge",
            label = "Badge",
            summary = "A compact status or category label.",
            group = "display",
            placement = SpecComponentPlacement.APP,
            emit = listOf(),
            capability = SpecComponentCapability.UNKNOWN,

The highlighted strings are the point: the component id badge is the same identifier in the manifest and in both generated projections, and the summary sentence is byte-identical in the manifest and the Kotlin projection — one declaration becoming two languages with no manual sync.

Define once

Every Texuli component's props, kinds, enums and tiers are declared once in the manifests — nothing about the interface is written twice by hand.

Project twice

The same manifest data generates the TypeScript projection shipped in the npm packages and the Kotlin projection generated and tested in this repository. The badge summary sentence — "A compact status or category label." — is byte-identical in the manifest and the Kotlin projection: one declaration, two languages, no manual sync.

Tokens cross

The web themes through the --tx-* token contract. The native side starts honest and small: TexuliTheme is a minimal token theme until texuli-tokens is generated from the spec's token roles (design §3). The values mirror the default Texuli theme's neutral roles.

Semantic roles cross

The same semantic rules — role, accessible name and, for headings, the level — hold on both sides: the web trace and the native trace apply these rules to a validated render tree. GoldenConformanceTest loads each golden with loadBlueprint, traces the plan, and must equal the web trace — so on the spec goldens both renderers expose the same roles, names and heading levels. Keyboard, focus and interaction behavior are not part of the trace.

What does not move today

  • No transpilation: Svelte is never compiled to Compose. The native build consumes the blueprint contract from @texuli/spec and never imports TypeScript or Svelte — two renderers, one contract.
  • Nothing is published to Maven Central yet: the native modules are consumed in place from this repository, and publication under the com.texuli coordinates (ADR-005) is planned, not done.
  • No iOS renderer ships today: iOS is compiled for texuli-spec only, for a SwiftUI adapter that is still a design decision — Android is pending the same decision.
  • Web-extension props do not cross: on the native side extension props are dropped, and unknown ids become visible placeholders — never silence.
  • And no slogan: Texuli does not claim an interface written for the web runs unchanged on native. Each runtime renders from the contract with its own tested renderer — the web in Svelte, native in Compose, in development.

What exists today

The web runtime ships: all 15 packages are on npm as 0.1.0-next.1 under the dist-tag next (CI publishes with provenance; latest is reserved for 1.0.0). The native side is in development: the Compose renderer is consumed from source in this repository and nothing is published to Maven Central yet. The table below states the capability status per side, with the evidence for each row.

The same catalog, capability by capability

The spec contract is one declaration with two consumers. This table states what each runtime has today, honestly marked, and names the surface every status rests on — every path is in this repository and opens.

Exists — ships today. In this repository — real and tested here, not published. Planned — decided or intended, not built. No status is inferred: every cell cites the surface it rests on.

Web and native status per capability, each citing the repository surface it rests on.
CapabilityWeb runtimeNative runtimeEvidence
Component vocabulary and prop schemasExists

Generated from the manifests and shipped in the npm packages (0.1.0-next.1).

In this repository

The same manifests generate the Kotlin projection, tested in this repository.

Webpackages/spec/generated/typescript/vocabulary.ts
Nativepackages/spec/generated/kotlin/com/texuli/spec/Vocabulary.kt
Blueprint validationExists

The spec validator refuses invalid blueprints: closed ids, schemas, limits.

In this repository

The native validator refuses the same invalid fixtures with the same codes.

Webpackages/spec/src/validate.ts
Nativenative/texuli-spec/src/commonMain/kotlin/com/texuli/spec/runtime/SpecValidator.kt
RenderingExists

The sink-free renderer paints catalog trees with no HTML strings.

In this repository

The Compose renderer is in development: it paints a documented slice, and anything outside it paints a visible placeholder.

Webpackages/catalog/src/render
Nativenative/texuli-compose
ChartsExists

Chart math and every chart family, one lazy subpath per family.

In this repository

A bounded compact-chart slice: sparkline, bullet, gauge, progress ring.

Webpackages/charts/src
Nativenative/texuli-compose/src/commonMain/kotlin/com/texuli/compose/ChartWidgets.kt
MediaExists

Image, gallery, video, audio, captions and an image editor.

In this repository

Image, figure, video and gallery frames; image decoding is planned.

Webpackages/media/src
Nativenative/texuli-compose
Tokens and themingExists

The OKLCH theme engine behind the --tx-* token contract.

In this repository

TexuliTheme mirrors the default theme’s neutral roles; the full engine generated from the spec’s token roles is planned.

Webpackages/tokens/src
Nativenative/texuli-compose/src/commonMain/kotlin/com/texuli/compose/TexuliTheme.kt
Semantic conformanceIn this repository

The web trace is the reference side of the dual-target golden gate.

In this repository

The golden gate requires each native trace to equal the web trace.

Webnative/conformance/web/trace.ts
Nativenative/conformance/src/jvmTest/kotlin/com/texuli/conformance/GoldenConformanceTest.kt
App shells and layoutsExists

Content layouts, app shells and marketing layouts ship in the styled package.

Planned

App-shell layouts are planned; the renderer shows visible placeholders today.

Webpackages/texuli/src/layouts
Nativenative/README.md
DistributionExists

All 15 packages are on npm as 0.1.0-next.1 under the dist-tag next.

Planned

Maven Central under the com.texuli coordinates (ADR-005) is planned; the modules are consumed in place from this repository.

Webpackages/texuli/package.json
Nativenative/README.md