What is GenomeJS?
Learn what GenomeJS does, how its token model works, and when to use it.
GenomeJS is a reactive design-token compiler for frontend interfaces.
You define raw values, derived token functions, and runtime context. GenomeJS discovers the relationships between those values, resolves them in dependency order, and expresses the result as CSS custom properties.
Runtime context changes
↓
Affected dependencies resolve
↓
Token values update
↓
CSS custom properties change
↓
The rendered interface respondsThe token model
A Genome configuration has three main parts.
Primitives
Primitives are raw values that do not depend on other tokens.
const primitives = {
baseColor: "#7c6cff",
baseSpacing: 16,
};GenomeJS currently supports string and number primitive values.
Tokens
Tokens can be static values or pure functions derived from primitives, other tokens, and runtime context.
const tokens = {
gap: (dna, context) => Number(dna.baseSpacing) * Number(context.scale ?? 1),
};The dna object contains values that have already been resolved.
The context object contains live runtime conditions such as mode, scale, contrast, viewport state, or any values your token system needs.
Runtime context
Context can change after the Genome instance has been created.
genome.mutate({
mode: "dark",
scale: 1.25,
});Calling mutate() causes derived values to resolve again and notifies subscribers.
What GenomeJS handles
GenomeJS is responsible for:
- Discovering token dependencies from value reads
- Building the dependency graph
- Resolving derived values in a valid order
- Detecting circular dependencies
- Detecting unresolved token references
- Responding to runtime context mutations
- Expressing resolved values as
--g-*CSS custom properties - Skipping CSS writes when the resulting value has not changed
- Notifying framework and application subscribers
What GenomeJS does not replace
GenomeJS does not replace CSS, CSS custom properties, Tailwind CSS, or your component library.
It produces and manages values that those systems can consume.
.button {
background: var(--g-button-color);
border-radius: var(--g-control-radius);
}When to use GenomeJS
GenomeJS is useful when design values depend on runtime conditions or on other design values.
Examples include:
- Light and dark modes
- Accessible foreground colors
- Density settings
- Responsive spacing or typography
- Container-aware components
- Scoped component themes
- Design systems shared across multiple frameworks
For a small static theme with no derived values or runtime behavior, ordinary CSS custom properties may already be enough.
Smallest example
import { Genome } from "@genomejs/core";
const genome = new Genome({
primitives: {
baseSize: 16,
},
tokens: {
scaledSize: (dna, context) =>
Number(dna.baseSize) * Number(context.scale ?? 1),
},
});
genome.mutate({
scale: 1.5,
});
console.log(genome.getTrait("scaledSize"));
// 24In a browser, resolved values are also expressed on the target element:
--g-base-size: 16;
--g-scaled-size: 24;What can go wrong?
GenomeJS reports invalid token systems early:
- A circular graph throws
CircularDependencyError. - A reference to a missing token throws
UnresolvedTokenError. - Calling
getTrait()with an unknown name throws an error. - Context values must be converted or validated when your token expects a specific type.