Getting started
Install, derive, and use a theme
xoji derives a complete, internally consistent set of 276 design tokens (surfaces, text, accents, type, spacing, and more) from an algorithm and a few anchor colors. This page covers installing it, deriving a theme, using the result with or without a framework, and writing your own algorithm.
Install
xoji is three packages. @xoji/core is the engine: the
derivation, the xoji command line, and the web components
(behind the @xoji/core/elements entry). Add a binding only if
you build with that framework.
npm install @xoji/core
# optional framework bindings
npm install @xoji/svelte
npm install @xoji/astro
Derive a theme
An algorithm maps your anchors and knobs to a full token register. The
built-in algorithms ship with the engine under
@xoji/core/algorithms; import one and call
derive.
import { derive, emit } from "@xoji/core";
import { xojiDefault } from "@xoji/core/algorithms";
const register = derive(xojiDefault, {
constraints: { "--bg-0": "#0b0d12", "--fg-0": "#e6e9ef", "--accent": "#6ea8fe" },
knobs: { scheme: "dark", contrastBand: "aa", vibrancy: 0.5 },
});
const css = emit(register, "css"); // a :root { … } block
const json = emit(register, "json"); // a flat token map
Or derive from the command line without writing any code:
npx xoji derive --bg "#0b0d12" --accent "#6ea8fe" --format css > theme.css
The built-in algorithms, each a named export and an id:
xojiDefault
the neutral default
xojiHc
high contrast
xojiQuiet
restrained
xojiLoud
vibrant
nxiNite
Day/Night, time-aware
Anchors
anchors is { bg, fg, accent }, each a hex
string. They seed the derivation; every other token solves around them.
Pass as few or as many as you like. Pin nothing and the algorithm's
own defaults fill in, or pin individual tokens through
constraints and the rest re-derive to fit.
Knobs
Knobs adjust how an algorithm derives. The common ones, plus any an
algorithm declares itself (nxi-nite adds hour):
Use the theme
A derived theme is plain CSS custom properties. Write the emitted CSS into
your page and the cascade applies it; nothing has to be running at view
time. In the browser, @xoji/core/dom writes a register straight
onto a live element.
import { apply } from "@xoji/core/dom";
apply(register); // write onto :root
apply(register, { target: previewEl }); // scope to one element
apply(register, { persistKey: "theme" }); // and persist it
Run the engine live only when an input changes at runtime: a theme editor, or a time-aware algorithm like nxi-nite re-deriving as the hour moves. Otherwise the engine's job ends once the CSS is written.
Components
The token register is the contract: components read it with
var(--token) and never raw colors or magic numbers, so any valid
theme styles them with no extra work. Use the raw custom elements, or a
framework binding.
import "@xoji/core/elements"; // registers <xoji-button>, <xoji-card>, …
import { Button, Card } from "@xoji/svelte";
@xoji/svelte and @xoji/astro wrap the same elements;
each depends only on @xoji/core.
Write an algorithm
An algorithm is a xript mod: a directory with a mod-manifest.json
and code. Build on the shared xoji derivation with
defineXojiAlgorithm (declare taste; the house derivation does the
rest), or write one from scratch with defineAlgorithm.
// algorithms/sunrise/mod-manifest.json declares the mod;
// algorithms/sunrise/src/mod.ts defines it:
import { defineXojiAlgorithm } from "@xoji/core/authoring";
defineXojiAlgorithm({
id: "sunrise",
anchors: { bg: "#1a1410", accent: "#ff9e5e" },
vibrancy: 0.7,
});
Drop the directory in, build the mods, and the algorithm loads by id. Same
path the built-in algorithms take. An algorithm that derives in
stages lists ordered passes; nxi-nite is a worked example, layering
a time-of-day shift onto the base derivation.
See it run
Questions or contributions live on