Guide
Installation
One package per framework — peers, first render, theme boot, and the SSR wiring.
#Requirements
| Your stack | Peer version | Install |
|---|---|---|
| Vue | vue ^3.5 | @bysages/vue |
| React | react, react-dom >=18 | @bysages/react |
| Solid | solid-js ^1.9 | @bysages/solid |
| Svelte | svelte ^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.
pnpm add @bysages/vue # or @bysages/react / @bysages/solid / @bysages/svelte
Optional companions, same rule — the framework wrapper is their peer:
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:
<script setup lang="ts">
import { Button } from "@bysages/vue";
</script>
<template>
<Button variant="solid" tone="ink">Publish</Button>
</template>
import { Button } from "@bysages/react";
export function Page() {
return <Button variant="solid" tone="ink">Publish</Button>;
}
import { Button } from "@bysages/solid";
export function Page() {
return <Button variant="solid" tone="ink">Publish</Button>;
}
<script lang="ts">
import { Button } from "@bysages/svelte";
</script>
<Button variant="solid" tone="ink">Publish</Button>
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:
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 keybs-themeand re-applies it; whilemodeis"system"it listens toprefers-color-schemeand flips with the OS.applyTheme(partial)merges with the current theme, writes the root attributes, and persists unlesspersist: 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
- Default — runtime injection. Wrappers call
injectComponentStyleper family on first use; tokens flow throughinjectTokens(). Zero configuration. - Explicit token CSS.
tokensCssfrom@bysages/coreis the whole token + base stylesheet as one string — inline it in a head tag for SSR or strict-CSP environments:import { tokensCss } from "@bysages/core"; // Nuxt: useHead({ style: [{ innerHTML: tokensCss }] }) - Build-time import. The same stylesheet as a file, for bundlers that hoist CSS:
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:
- Token CSS injection. Serve the tokens in the initial HTML — pass
tokensCssto a head tag (way 2 above), or the first paint arrives unstyled and flashes. - Floating layers. Dialogs, popovers, selects, and tooltips teleport their vessels to
document.bodyon the client. Under a prerendering framework, wrap such components in a client-only boundary — Vue's<ClientOnly>, React'snext/dynamicwithssr: false, Solid's<ClientOnly>, Svelte'sonMountgate — so the server output and the hydrated tree agree.