Elements

Guide

Installation

One package per framework — peers, first render, theme boot, and the SSR wiring.

#Requirements

Your stackPeer versionInstall
Vuevue ^3.5@bysages/vue
Reactreact, react-dom >=18@bysages/react
Solidsolid-js ^1.9@bysages/solid
Sveltesvelte ^5@bysages/svelte

The framework is the only peer dependency — everything else rides in as regular dependencies of the wrapper: the headless layer (@ark-ui/*, @zag-js/*), the data table stack (@tanstack/*), the icon registry (@bysages/icons), the style + token layers (@bysages/core → @bysages/tokens). TypeScript is recommended; all packages ship full types. All packages are ESM.

bash
pnpm add @bysages/vue   # or @bysages/react / @bysages/solid / @bysages/svelte

Optional companions, same rule — the framework wrapper is their peer:

bash
pnpm add @bysages/charts    # charts; import per framework: "@bysages/charts/vue" etc.
pnpm add @bysages/workflow  # workflow graph protocol + X6 canvas

@bysages/core installs automatically with any wrapper, but it is public API — import applyTheme from it directly whenever you drive the theme yourself.

#First render

Styles need no import — each component injects its stylesheet on first use. Render a component and the design system arrives with it:

Vue
<script setup lang="ts">
import { Button } from "@bysages/vue";
</script>

<template>
  <Button variant="solid" tone="ink">Publish</Button>
</template>

Every wrapper exports the same family names with the same props — switching frameworks is an import-path change.

#Boot the theme

Without any call, the theme defaults to mode: "system" and everything else on "auto". To make the theme survive reloads and follow the OS:

ts
import { applyTheme, initTheme, getTheme } from "@bysages/core";

initTheme();                          // restore from localStorage and track the OS
const theme = applyTheme({            // set and persist; returns the full Theme
  mode: "dark",
  accent: "celadon",
  scene: "studio",
});
getTheme();                           // read the current logical theme
  • initTheme() reads localStorage key bs-theme and re-applies it; while mode is "system" it listens to prefers-color-scheme and flips with the OS.
  • applyTheme(partial) merges with the current theme, writes the root attributes, and persists unless persist: false.
  • "auto" values resolve at apply time: the accent and contrast follow the scene's pairing, mode: "system" follows the OS.

Every dimension and its pairing table live in Theming.

#CSS supply, three ways

  1. Default — runtime injection. Wrappers call injectComponentStyle per family on first use; tokens flow through injectTokens(). Zero configuration.
  2. Explicit token CSS. tokensCss from @bysages/core is the whole token + base stylesheet as one string — inline it in a head tag for SSR or strict-CSP environments:
    ts
    import { tokensCss } from "@bysages/core";
    // Nuxt: useHead({ style: [{ innerHTML: tokensCss }] })
    
  3. Build-time import. The same stylesheet as a file, for bundlers that hoist CSS:
    ts
    import "@bysages/tokens/css";
    

Component styles keep injecting at runtime in all three cases — only the token layer moves.

#Server-side rendering

Two things a server cannot do, and both have one-line fixes:

  1. Token CSS injection. Serve the tokens in the initial HTML — pass tokensCss to a head tag (way 2 above), or the first paint arrives unstyled and flashes.
  2. Floating layers. Dialogs, popovers, selects, and tooltips teleport their vessels to document.body on the client. Under a prerendering framework, wrap such components in a client-only boundary — Vue's <ClientOnly>, React's next/dynamic with ssr: false, Solid's <ClientOnly>, Svelte's onMount gate — so the server output and the hydrated tree agree.