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 sizeand 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.
| Layer | Package | What it holds | Depends on |
|---|---|---|---|
| L0 | @texuli/core | Texels: 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 policy | none |
| L0 | @texuli/spec | Framework-free blueprint contract and code generation | none |
| L1 | @texuli/primitives | 41 owned headless primitives, one subpath each (accordion … tree), held to the WAI-ARIA APG patterns | core |
| L2 | @texuli/tokens | The OKLCH theme engine, the --tx-* token contract, 200+ themes, compiled CSS, forced colors, the AA gate, the Tailwind preset | none |
| L3 | texuli | Styled components (Button … Tree), the class helpers (cx, variants), icons, layouts and app shells | core, primitives, tokens |
| L4 | @texuli/charts | Chart math and every chart family, one lazy subpath per family | core, primitives, tokens, texuli |
| L4 | @texuli/data | Data views: grid (core, columns, editing, virtual), tree, board, calendar, timeline, map, activity, files, virtual list | core, primitives, tokens, texuli |
| L4 | @texuli/media | Image, gallery, video, audio, captions, image editor, compositions, animation, export | core, primitives, tokens, texuli |
| L4 | @texuli/chat | Thread, message list, composer, typing indicator | core, primitives, tokens, texuli |
| L4 | @texuli/editor | Rich text editor surface, toolbar, link dialog, headless controller, clipboard import | core, primitives, tokens, texuli |
| L5 | @texuli/blocks | Content blocks (text, code, data, diagram, embed, interactive, map, media, structure): closed vocabulary, untrusted guard, formula AST | core, tokens, texuli, charts, data, media |
| L6 | @texuli/catalog | The agent catalog (manifests per group, build) and the renderer with no HTML sinks | core, texuli, charts, data, blocks, media |
| L7 | @texuli/slides | Slide model, grammar, export and stage runtime | blocks, catalog, media, tokens |
| L7 | @texuli/player | Blueprint player runtime and styles | blocks, catalog, charts, media |
| L8 | @texuli/tools | Copy-in CLI (texuli-add, texuli-init, texuli-update), texuli-llms, A2UI adapter, custom elements and the golden set | core, tokens, catalog, blocks, charts, media |
| L8 | @texuli/astro | Astro integration, site helpers and content layouts | blocks, core, tokens |
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.
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.
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.
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.
Authored once
The badge manifest in the agent catalog: id, label and summary.
packages/catalog/src/manifests/display/manifests.tsexport const badgeManifest = defineCatalogManifest({ id: 'badge', package: PKG, label: 'Badge', summary: 'A compact status or category label.', tier: 'app',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.tsbadge: { label?: string; variant?: | 'default' | 'secondary' | 'outline' | 'success' | 'warning' | 'destructive' | 'info' | 'neutral'; };packages/spec/catalog/components.json"summary": "A compact status or category label."Kotlin projection
The generated Kotlin vocabulary: the identical summary string.
packages/spec/generated/kotlin/com/texuli/spec/Vocabulary.ktSpecComponent( 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).
See the badge rendered on the webRead how web and native share one contract
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
packages/catalog/src/manifests/display/manifests.tsexport const badgeManifest = defineCatalogManifest({
id: 'badge',
package: PKG,
label: 'Badge',
summary: 'A compact status or category label.',
tier: 'app',packages/spec/generated/typescript/vocabulary.ts badge: {
label?: string;
variant?:
| 'default'
| 'secondary'
| 'outline'
| 'success'
| 'warning'
| 'destructive'
| 'info'
| 'neutral';
};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.
See the badge rendered on the webRead the deep dive: why Svelte and Kotlin
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.
| Capability | Web runtime | Native runtime | Evidence |
|---|---|---|---|
| Component vocabulary and prop schemas | Exists 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. | Web packages/spec/generated/typescript/vocabulary.tsNative packages/spec/generated/kotlin/com/texuli/spec/Vocabulary.kt |
| Blueprint validation | Exists 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. | Web packages/spec/src/validate.tsNative native/texuli-spec/src/commonMain/kotlin/com/texuli/spec/runtime/SpecValidator.kt |
| Rendering | Exists 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. | Web packages/catalog/src/renderNative native/texuli-compose |
| Charts | Exists Chart math and every chart family, one lazy subpath per family. | In this repository A bounded compact-chart slice: sparkline, bullet, gauge, progress ring. | Web packages/charts/srcNative native/texuli-compose/src/commonMain/kotlin/com/texuli/compose/ChartWidgets.kt |
| Media | Exists Image, gallery, video, audio, captions and an image editor. | In this repository Image, figure, video and gallery frames; image decoding is planned. | Web packages/media/srcNative native/texuli-compose |
| Tokens and theming | Exists 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. | Web packages/tokens/srcNative native/texuli-compose/src/commonMain/kotlin/com/texuli/compose/TexuliTheme.kt |
| Semantic conformance | In 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. | Web native/conformance/web/trace.tsNative native/conformance/src/jvmTest/kotlin/com/texuli/conformance/GoldenConformanceTest.kt |
| App shells and layouts | Exists Content layouts, app shells and marketing layouts ship in the styled package. | Planned App-shell layouts are planned; the renderer shows visible placeholders today. | Web packages/texuli/src/layoutsNative native/README.md |
| Distribution | Exists 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. | Web packages/texuli/package.jsonNative native/README.md |

