@fuaran-ui/theme-manifest
v0.5.0
Published
Machine-readable theme contract for Fuaran UI — a DTCG-compatible token model extended with semantic role bindings (which token a Tone resolves to) and quantified invariants (per-role contrast floors, 60-30-10 colour-usage budgets, motion voice). The Type
Maintainers
Readme
@fuaran-ui/theme-manifest
The machine-readable theme contract for Fuaran UI — the TypeScript twin of the F# Fuaran.UI.ThemeManifest tier.
A design-token system says what the values are. It does not say what must remain true of the rendered result. This package is a DTCG-compatible token model (a vanilla DTCG file decodes cleanly) extended with the two things DTCG lacks:
- Semantic role bindings — a mapping from a role a node plays (
Tone.Brand,"body-text") to the token that realises it, so "what colour did the AI'sTone.Brandactually become?" is answerable against a declared contract. - Quantified invariants — properties that must hold of the rendered result: per-role contrast floors (stricter than WCAG AA), colour usage budgets (the 60-30-10 heuristic as
targetPct ± tolerancePct), and a motion voice ceiling. Each is soft-weighted.
It is the contract @fuaran-ui/style-observer verifies resolved computed styles against — deterministically, with no vision model.
Install
npm install @fuaran-ui/theme-manifest@fuaran-ui/schema is a peer dependency (for ToneVariant). No other runtime dependency.
Usage
import { decodeManifest, resolveRole } from '@fuaran-ui/theme-manifest';
const result = decodeManifest(tokensJson); // DTCG file or { meta, tokens, roles, invariants }
if (result.ok) {
const brandToken = resolveRole('Brand', result.value); // → ManifestToken | undefined
}Projecting an existing token surface
Lower the adoption floor — project your app's existing tokens into a baseline manifest, then enrich with invariants:
import {
projectFromFuaranToneVars,
projectFromCssCustomProperties,
projectFromDtcg,
merge,
} from '@fuaran-ui/theme-manifest';
const fromTones = projectFromFuaranToneVars(referenceCss); // roles inferred (the contract is semantic)
const fromVars = projectFromCssCustomProperties(appCss); // roles left unbound
const combined = merge(fromVars, fromTones); // last-write-wins, CSS-cascade orderStability
The contract shapes (ThemeManifest, ManifestToken, ManifestRole, Invariant) and the decode behaviour are declared stable in STABILITY.md. The invariant vocabulary is additive-only (a new InvariantKind is a minor bump; redefining one is breaking). Manifest JSON encode + the F# ThemeBridge (typed-Theme projector) are not yet ported (follow-up).
Apache-2.0.
