Skip to content

Where aparté runs

The browsers, Node versions, frameworks, bundlers and TypeScript settings @aparte/* supports — the lowest browser version each feature it uses sets, and what CI runs.

On this page
EngineLowest versionWhat sets itRun in CI
Chrome, Edge111color-mix()Chromium 149
Safari, iOS Safari16.2color-mix()WebKit 26.5
Firefox121:has()Firefox 151
Samsung Internet22color-mix()—

The lowest version is declared from the features the code uses with no fallback: it is the oldest in which all of them draw. CI runs only the builds in the last column: every example app in Chromium, vanilla, vanilla-dist, react, vue and svelte4 in WebKit, and vanilla and react in Firefox, each on a part of the suites. The JavaScript is ES2022 and is not compiled lower.

  • color-mix() draws the soft buttons, the alerts and badges, the person’s message bubble and the control borders.
  • :has() draws the focus ring and the checked mark on a choice row, the focus ring on a conversation row, and the invalid border on a field group.

What degrades instead:

  • Each accent as text — the active conversation’s mark, a success sign, a button’s text in its accent colour — keeps its accent’s hue at a set lightness with relative colour syntax (Chrome 119, Safari 16.4, Firefox 128); before, it is the accent mixed with the text colour, which still reads at 4.5:1 or more on the default palette, washes included.
  • The editor’s padding is worked out from its line height with the lh unit (Safari 16.4, Firefox 120); before, from em, to the same pixel.
  • A filled button’s ink — the text on a solid brand colour — is computed with relative colour syntax under @supports; without it, the fixed --aparte-on-intent is used, readable on the default palette except on the neutral fill (3.38:1 in light) (the text on a fill).
  • :dir() places the active conversation’s mark on the right in a right-to-left page; Chrome before 120 keeps it on the left.
  • content-visibility is a rendering hint, ignored where it is not supported.

crypto.randomUUID and navigator.clipboard exist only on https:// and localhost. Core reaches both through a fallback, uuid() and copyText(), both exported: on a plain http:// address — a model on a machine of your network — the copy buttons use execCommand('copy') and ids come from a non-cryptographic generator (the pieces core exports).

Every package declares engines.node >=18, and CI runs Node 18 and 24. In Node, @aparte/core resolves to an entry without elements — the types, the parsers, AparteClient, the transports and the server handlers — that touches no DOM at import, so a server render can import it; @aparte/engine has no DOM at all. A test runner on Node takes the same entry, so under jsdom no element upgrades: alias it to @aparte/core/browser (Testing your components).

WrapperPeer dependencies
@aparte/reactreact and react-dom ^18.0.0 || ^19.0.0
@aparte/vuevue ^3.5.0
@aparte/sveltesvelte ^4.0.0 || ^5.0.0
@aparte/angular@angular/core and @angular/common ^19.2.0, rxjs ^7.8.0

Each wrapper also takes @aparte/core >=0.21.0 <1.0.0 as a peer: install the two from the same release. Each wrapper’s example runs in the browser suite — one for Svelte 4 and one for Svelte 5 — and the React wrapper is typechecked against React 18 as well. @aparte/plugin-model-selector takes the four frameworks as optional peers.

The packages are ES modules only, with no CommonJS build: a require() of one works only where Node loads ES modules synchronously — 20.19 and 22.12 onwards — and not on Node 18. Each ships an exports map, which Vite, webpack 5, Rollup and esbuild follow, and Parcel 2 with packageExports on. Core’s sideEffects names its CSS and its element entry, so a bundler keeps those and drops what you do not import; most other packages declare none, and the approval, ask-user and model-selector plugins are kept whole. In TypeScript, use moduleResolution bundler, node16 or nodenext, not node. The declarations are emitted with TypeScript 5.7. Without a bundler, the packages load from a CDN (From a CDN).

At or above a lowest version, a break is a bug: open an issue with the engine and its version. Below it, nothing is run.