Compatibility & support matrix

What cascivo runs on, which package versions go together, and the build-tooling baseline. If an integration surprises you, start here.


Frameworks

FrameworkSupportedNotes
React 18 / 19✅ YesPrimary target. Components ship 'use client' preserved.
Next.js App Router (RSC)✅ YesImport the CSS once in a Server Component (e.g. app/layout.tsx); components are client. Working example: apps/examples/react-next. See USING-WITH-NEXTJS.md.
Vite + React (CSR/SPA)✅ YesReference setup. See apps/examples/react-vite.
Vite SSR / TanStack Start✅ Yes¹Requires ssr.noExternal: [/^@cascivo\//] (or the cascivoSsr() plugin). Working example: apps/examples/react-vite-ssr. See USING-WITH-VITE-SSR.md.
Preact 10 (preact/compat)✅ CSR onlyVerified on Vite CSR (@preact/preset-vite) — components, signals, overlays and charts all behave as on React, at roughly half the JS. Not verified under SSR/prerender, and known to fail under Astro's compat aliasing. See USING-WITH-PREACT.md.
Cloudflare Workers (client app + API)✅ YesClient-rendered (no SSR): static assets with an SPA fallback, /api/* routed to the Worker. npx cascivo create --framework cloudflare (or npm create cascivo) emits it wired up, on Preact or React; the framework:check CI job builds that scaffold from packed tarballs and runs its Worker. A ready-to-deploy copy lives in starters/cloudflare (Deploy to Cloudflare button, npm create cloudflare --template). Working example with Workers AI streaming: apps/examples/chat.
Astro (React islands)✅ Yes²Requires @cascivo/react ≥ 1.0.1 and vite.resolve.noExternal: [/^@cascivo\//] in astro.config.mjs — without both, SSR'd islands render unstyled. npx cascivo create --framework astro emits it wired up. Working example: apps/examples/astro-islands. See USING-WITH-ASTRO.md.
Vue / Svelte / Angular⚠️ Tokens/themes only@cascivo/tokens + @cascivo/themes are framework-agnostic CSS; the components are React.
Ghost (Handlebars themes)⚠️ Tokens/themes onlyThemes are Handlebars rendered server-side with no JS framework layer, so React components cannot mount. The token + theme CSS works once flattened (bare @imports, no build step in Ghost). Working theme, validated by Ghost's own gscan in CI: apps/examples/ghost-theme. See USING-WITH-GHOST.md.

¹ The published @cascivo/react bundle ships per-component CSS as static side-effect imports. Bundlers resolve these; a bare server-side ESM loader (Node native, workerd) does not and throws Unknown file extension ".css". Since 0.10 the node export condition selects a CSS-free server twin, so no config is needed; ssr.noExternal (or cascivoSsr()) remains the fallback for pinned older versions. Since 0.18.0 a react-server condition points RSC back at the CSS-bearing build, so component CSS tree-shakes under SSR the same way it does in an SPA — no aggregate stylesheet in either recipe. The Vite SSR guide has the measurements.

² Astro's unstyled-island problem had two independent causes, and both must be addressed. (a) Export conditions match in declaration order, and until 1.0.1 the CSS-free node twin was listed ahead of import; a Vite-based SSR framework resolves with node active and react-server inactive, so it got the CSS-free build. Since 1.0.1 a module condition sits ahead of node — bundlers match it, while Node's ESM resolver, which does not implement module, still falls through to the twin, so footnote ¹'s guarantee is unchanged. This affected @cascivo/react, @cascivo/charts, @cascivo/ai, @cascivo/editor and @cascivo/flow; the ordering is enforced by pnpm css-contract:check. (b) Vite externalizes node_modules packages in its server build, so the package's module graph is never walked and Astro — which collects a page's CSS from that graph — emits none; vite.resolve.noExternal puts it back. It must be resolve.noExternal, not ssr.noExternal, which Astro's prerender environment does not read. Cause (b) does not appear in the monorepo's own Astro fixture, because a workspace:* link is never externalized — see the verification notes in USING-WITH-ASTRO.md.

Browsers

cascivo targets the last 2 versions of Chrome, Firefox, and Safari. It relies on modern CSS that is broadly shipped as of 2025:

FeatureMin supportUsed for
@layerChrome 99, FF 97, Saf 15.4predictable cascade ordering
@containerChrome 105, FF 110, Saf 16slot-aware responsive components
:has()Chrome 105, FF 121, Saf 15.4stateful styling without JS
oklch()Chrome 111, FF 113, Saf 15.4the entire color system
Popover API / @starting-styleChrome 114+, FF 125+, Saf 17.4+overlays (Sheet, Drawer, Popover)
subgridChrome 117, FF 71, Saf 16Grid's alignRows — cross-card band alignment
field-sizing: contentChrome 123, FF 121, Saf 26Textarea's autosize — progressive
anchor-size() / position-visibilityChrome 125+, Saf 26+panel widths and anchor-tied visibility — progressive
scroll-state container queriesChrome 133+ onlyScrollArea shadows, DataTable stuck header — progressive
appearance: base-selectChrome 135+ onlyNativeSelect's themed option list — progressive
CSS @function / if()Chrome 133+ onlyprogressive enhancement only (below)

CSS @function is opt-in

--cascivo-step / --cascivo-scale live in @cascivo/tokens/functions.css and are not auto-imported, because current CSS minifiers (lightningcss, used by Tailwind v4) cannot parse @function and silently drop the rule. Every call site in cascivo ships a static fallback for the same property, so omitting functions is always visually correct. Opt in only if your pipeline supports @function:

import '@cascivo/tokens/functions.css' // Chrome 133+ progressive enhancement

Build tooling


Package compatibility

The runtime packages are 1.x and covered by semver. The ten packages that share @cascivo/core release in lockstep — install them at the same version. See UPGRADING.md for which packages are 1.x and which tooling packages are still 0.x.

This table is generated from the packages themselves by pnpm regen and verified by CI's drift check — it cannot go stale. (It once sat thirteen minors behind, claiming @cascivo/react 0.2.x while npm served 0.13.0, which is why it is no longer hand-written.)

PackageVersionPeer requirements
@cascivo/core1.6.x@preact/signals-react >=3.0.0, @types/react >=18.0.0 (optional), react >=18.0.0, react-dom >=18.0.0
@cascivo/tokens1.2.xnone (CSS only)
@cascivo/themes1.0.x@cascivo/tokens (direct dep) — themes @import it
@cascivo/react1.6.x@preact/signals-react >=3.0.0, @types/react >=18.0.0 (optional), react >=18.0.0, react-dom >=18.0.0
@cascivo/icons1.1.x@types/react >=18.0.0 (optional), react >=18.0.0
@cascivo/charts1.6.x@preact/signals-react >=3.0.0, @types/react >=18.0.0 (optional), react >=18.0.0, react-dom >=18.0.0
@cascivo/i18n1.6.x@preact/signals-react >=3.0.0
@cascivo/storage1.6.x@preact/signals-react >=3.0.0
@cascivo/data0.1.xnone
@cascivo/app1.6.x@preact/signals-react >=3.0.0, @types/react >=18.0.0 (optional), react >=18.0.0
@cascivo/mcp0.8.x(server; run via npx)

React 19 requires @preact/signals-react 3.x. React 19 removed the internal that signals-react 2.x imports, so a 2.x runtime fails to load under React 19 (SyntaxError: … does not provide an export named '__SECRET_INTERNALS…'). The peer range (>=3) enforces this; signals-react 3.x still supports React 16.14+/17/18, so the floor costs React-18 users nothing. If a lockfile from an earlier install pins 2.x, run cascivo doctor — it flags the mismatch with the upgrade command.

Minimal install

pnpm add @cascivo/react @cascivo/themes @preact/signals-react

@cascivo/tokens arrives transitively through @cascivo/themes — it is a direct dependency of themes (the theme CSS @imports it), so it installs automatically on every package manager, with or without auto-install-peers.

Required CSS import order

import '@cascivo/themes/light-dark.css' // tokens (once) + base typography + light & dark
import './my-theme.css' // optional brand overrides — always LAST

Component CSS is not in that list because it is not yours to import: each component chunk carries its own stylesheet and your bundler collects only what you use. The no-bundler path adds @cascivo/react/styles.css first; it defines component structure only — it references var(--cascivo-*) values that don't exist until a theme + tokens are loaded, so importing it alone yields correctly-structured but uncolored components. See THEMING.md.

Right-to-left

RTL works out of the box: set dir="rtl" (or dir="auto") on any ancestor — <html>, a route wrapper, a single panel — and the catalog mirrors. No import, no prop, no theme variant.

<html dir="rtl" lang="ar">
  <!-- every cascivo component now mirrors -->
</html>

This holds because every shipped rule uses CSS logical properties (margin-inline-start, padding-block, inset-inline-start, text-align: start) rather than physical ones, and both halves of that are enforced rather than asserted. pnpm rtl:check fails on any physical inline property in shipped CSS, and separately mounts components in real Chromium under both directions and requires every asymmetric inline box to swap. The one deliberate exception is ContextMenu, which positions from the pointer's viewport x — a physical coordinate by nature.

What is not covered: bidirectional text handling inside your own content (that is the browser's job, and dir="auto" is usually the right answer), and RTL-specific iconography — a directional icon you pass as a prop is yours to mirror.

As Markdown: /docs/compatibility.md

← All guides