Style is the
expression
of intent.
Mastors is an Intent-Based Styling Framework — declare what a UI element is and Mastors resolves how it looks: accessible, responsive, theme-aware, and zero-runtime.
Installation
Mastors is a pnpm workspace monorepo. Install the packages you need.
Full install
pnpm add @mastors/api @mastors/core @mastors/compiler \
@mastors/schemas @mastors/tokens @mastors/types @mastors/utils
Minimal install (API + core only)
pnpm add @mastors/api @mastors/core @mastors/schemas @mastors/tokens @mastors/types
Framework adapters
# React
pnpm add @mastors/adapter-react
# Vue 3
pnpm add @mastors/adapter-vue
# Svelte
pnpm add @mastors/adapter-svelte
Build plugins
pnpm add -D @mastors/plugin-vite # Vite
pnpm add -D @mastors/plugin-webpack # webpack
Quick Start
Get from zero to a fully-styled, accessible component in five steps.
mastors.config.tsDefine your schema and custom vocabulary at the project root.
import { defineSchema } from "@mastors/schemas";
export default defineSchema({
tokens: "./tokens/global.json", // optional — uses built-in tokens by default
vocabulary: {
// Extend the built-in vocabulary with project-specific intents
"card.promo.featured": {
spacing: { x: "token.space.6", y: "token.space.5" },
shadow: "token.shadow.elevated",
radius: "token.radius.xl",
color: {
background: "token.color.primary.50",
border: "token.color.primary.200",
},
a11y: { role: "article", contrast: "AA" },
},
},
});
import { defineConfig } from "vite";
import { mastorsPlugin } from "@mastors/plugin-vite";
import schema from "./mastors.config";
export default defineConfig({
plugins: [
mastorsPlugin({
config: { schema, a11yMode: "warn" },
a11yManifest: true, // emit mastors-a11y.json at build
}),
],
});
import "virtual:mastors"; // inject compiled CSS (zero-runtime)
import { configure } from "@mastors/api";
import schema from "./mastors.config";
configure({ schema, a11yMode: "warn" });
import { intent } from "@mastors/api";
const card = intent("card.elevated.interactive");
// Mastors auto-resolves:
// ✓ Elevation shadow from token.shadow.elevated
// ✓ WCAG AA focus ring (ring.primary)
// ✓ Hover lift interaction
// ✓ prefers-reduced-motion guard
// ✓ Theme-aware background & border colors
import { Mastors } from "@mastors/adapter-react";
export function Card({ title, body }) {
return (
<Mastors.div intent="card.elevated.interactive">
<Mastors.h2 intent="heading.section">{title}</Mastors.h2>
<Mastors.p intent="body.readable">{body}</Mastors.p>
<Mastors.button intent="button.primary.cta">Get Started</Mastors.button>
</Mastors.div>
);
}
Architecture
Mastors is organized as a five-layer pipeline. All resolution happens at build time — the browser receives plain atomic CSS with zero JavaScript in the styling critical path.
intent() ·
semantics() · compose() · configure()
Intent Declaration API
The @mastors/api package exposes four functions forming the public developer
interface.
import { intent, semantics, compose, configure, intentClass } from "@mastors/api";
// ── configure() — initialize once at app entry ──
configure({ schema: mySchema, a11yMode: "warn" });
// ── intent() — parse an intent ID into a descriptor ──
const card = intent("card.elevated.interactive");
// Returns: IntentDescriptor { id, segments, context, __mastors: true }
// ── intentClass() — resolve directly to className string ──
const cls = intentClass("button.primary.cta");
// → "m-padding-left-abc m-background-color-def m-intent-button-primary-cta"
// ── semantics() — add intents to the active schema at runtime ──
semantics({
"data.badge.negative": {
color: { foreground: "token.color.error.700" },
},
});
// ── compose() — merge multiple intents (last wins on conflicts) ──
const heroCard = compose("card.elevated", "card.elevated.interactive");
Semantic Resolver
@mastors/core houses the SemanticResolver class — the engine that
maps intent identifiers to typed style maps via the schema vocabulary. Results are automatically cached per
intent ID + context.
import { SemanticResolver } from "@mastors/core";
import { defaultSchema } from "@mastors/schemas";
const resolver = new SemanticResolver(defaultSchema);
// Step 1 — Parse intent string into a typed descriptor
const descriptor = resolver.describe("card.elevated.interactive");
// { id: "card.elevated.interactive", segments: { category, variant, modifier }, ... }
// Step 2 — Resolve descriptor into a full style map
const styleMap = resolver.resolve(descriptor);
// {
// base: StyleRule[] → CSS property/value pairs with token refs
// variants: StyleRule[][] → responsive / state variants
// a11yHints: A11yHint[] → WCAG requirements and ARIA suggestions
// animationRules: AnimationRule[] → transitions with motion guard
// }
// Useful methods
resolver.clearCache(); // clear cached results (call when schema changes)
resolver.setSchema(newSchema); // swap schema (also clears cache)
Specificity walk
When looking up a vocabulary entry, the resolver walks from most-specific to least-specific:
Built-in Vocabulary
Mastors ships 19 production-ready intents covering the most common UI patterns — all with automatic token resolution, WCAG 2.2 compliance, and interaction semantics.
Cards
| Intent | Shadow | Interaction | WCAG |
|---|---|---|---|
| card.base | — | — | AA |
| card.elevated | token.shadow.elevated |
— | AA |
| card.elevated.interactive | token.shadow.elevated |
hover:lift · focus:ring · active:press | AA |
"card.elevated.interactive": {
spacing: { x: "token.space.4", y: "token.space.3" },
shadow: "token.shadow.elevated",
radius: "token.radius.lg",
color: {
background: "token.color.neutral.0",
},
interaction: { hover: "lift", focus: "ring.primary", active: "press" },
motion: "transition.natural",
a11y: { role: "article", contrast: "AA" },
}
Buttons
| Intent | Background | Interaction | WCAG |
|---|---|---|---|
| button.primary | color.primary.600 |
hover:darken · focus:ring | AA |
| button.primary.cta | color.primary.600 |
hover:darken · focus:ring | AAA |
| button.secondary | color.neutral.100 |
hover:lighten · focus:ring | AA |
| button.destructive | color.error.500 |
hover:darken · focus:ring.error | AA |
Typography
| Intent | Size token | Weight | Line height |
|---|---|---|---|
| heading.hero | token.text.5xl |
bold | tight (1.25) |
| heading.section | token.text.3xl |
bold | tight (1.25) |
| heading.subsection | token.text.xl |
semibold | snug (1.375) |
| body.readable | token.text.base |
normal | relaxed (1.625) |
| body.readable.longform | token.text.lg |
normal | loose (2) |
Alerts & Feedback
| Intent | Background | ARIA role | WCAG |
|---|---|---|---|
| alert.info | color.primary.50 |
status |
AA |
| alert.success | color.success.100 |
status |
AA |
| alert.warning | color.warning.100 |
alert |
AA |
| alert.destructive | color.error.100 |
alert |
AA |
| toast.success.transient | color.neutral.800 |
status |
AA |
Forms
| Intent | Border | Focus | ARIA role |
|---|---|---|---|
| form.field | color.neutral.300 |
ring.primary |
group |
| form.destructive.confirm | color.error.500 |
— | dialog |
Layout
| Intent | Description |
|---|---|
| container.max.centered | Max-width centered layout wrapper with horizontal padding |
| grid.autofill.dense | Auto-fill CSS grid with dense packing algorithm |
Adding custom intents
Extend the built-in vocabulary in your mastors.config.ts. Custom intents follow
the same schema shape as built-in ones:
defineSchema({
vocabulary: {
"dashboard.stat.positive": {
spacing: { x: "token.space.4", y: "token.space.3" },
radius: "token.radius.lg",
color: {
background: "token.color.success.100",
foreground: "token.color.success.700",
border: "token.color.success.500",
},
a11y: { contrast: "AA" },
},
"dashboard.stat.negative": {
spacing: { x: "token.space.4", y: "token.space.3" },
radius: "token.radius.lg",
color: {
background: "token.color.error.100",
foreground: "token.color.error.700",
},
a11y: { contrast: "AA" },
},
},
});
Framework Adapters
Mastors provides first-class adapter packages for React, Vue 3, and Svelte — each exposing the idiomatic API for that framework.
React
import { useIntent, Mastors } from "@mastors/adapter-react";
// Hook — resolves to a className string
function Card({ title, body }) {
const className = useIntent("card.elevated.interactive");
return <div className={className}>{title}</div>;
}
// Component namespace — declarative intent prop
<Mastors.div intent="card.elevated.interactive">
<Mastors.h2 intent="heading.section">Title</Mastors.h2>
<Mastors.p intent="body.readable">Body text</Mastors.p>
<Mastors.button intent="button.primary.cta">Get Started</Mastors.button>
</Mastors.div>
Vue 3
// main.ts — register globally
import { MastorsPlugin } from "@mastors/adapter-vue";
app.use(MastorsPlugin);
// Component.vue
<script setup>
import { useIntent } from "@mastors/adapter-vue";
const cardClass = useIntent("card.elevated.interactive");
</script>
<template>
<!-- Composable -->
<div :class="cardClass">...</div>
<!-- Directive (requires MastorsPlugin) -->
<div v-intent="'button.primary.cta'">...</div>
</template>
Svelte
import { resolveIntent, intentAction } from "@mastors/adapter-svelte";
// Class binding (resolved at import time)
const cardClass = resolveIntent("card.elevated.interactive");
// Action (reactive, updates on prop change)
<div use:intentAction={"card.elevated.interactive"}>...</div>
// Class shorthand
<div class={resolveIntent("button.primary.cta")}>...</div>
Build Plugins
Mastors scans source files for intent("...") calls and compiles atomic CSS at
build time via Vite or webpack plugins.
Vite Plugin
import { mastorsPlugin } from "@mastors/plugin-vite";
export default defineConfig({
plugins: [
mastorsPlugin({
config: {
schema,
a11yMode: "warn", // "warn" | "enforce" | "report" | "off"
outputFormat: "atomic", // "atomic" | "css-modules" | "css-vars"
},
include: ["ts", "tsx", "vue", "svelte"], // file extensions to scan
a11yManifest: true, // emit mastors-a11y.json at build
}),
],
});
webpack Plugin
const { MastorsWebpackPlugin } = require("@mastors/plugin-webpack");
module.exports = {
plugins: [
new MastorsWebpackPlugin({
config: { schema, a11yMode: "warn" },
filename: "mastors.css", // output CSS filename in dist
}),
],
};
Configuration
Full MastorsConfig type reference:
type MastorsConfig = {
schema: MastorsSchema; // required — your project schema
a11yMode?:
| "warn" // console.warn on violations (default, dev-friendly)
| "enforce" // throws on error-severity violations (use in CI)
| "report" // silently collect — call guard.generateManifest()
| "off"; // disable accessibility guard entirely
outputFormat?:
| "atomic" // deduplicated atomic CSS classes (default)
| "css-modules" // scoped CSS module per intent
| "css-vars"; // CSS custom property declarations only
themes?: string[]; // theme names to generate variants for
defaultTheme?: string; // active theme at build time
};
VocabularyEntry schema shape
type VocabularyEntry = {
spacing?: { x?: TokenPath; y?: TokenPath };
radius?: TokenPath;
shadow?: TokenPath;
color?: {
background?: TokenPath;
foreground?: TokenPath;
border?: TokenPath;
};
typography?: {
size?: TokenPath;
weight?: TokenPath;
lineHeight?: TokenPath;
};
interaction?: {
hover?: "lift" | "darken" | "lighten" | "highlight";
focus?: "ring.primary" | "ring.error" | "ring.success";
active?: "press" | "bounce";
};
motion?: "transition.natural" | "animate.slide-up.spring";
a11y?: {
role?: string; // ARIA role suggestion
contrast?: "AA" | "AAA"; // WCAG contrast level to enforce
};
};
Design Tokens
Mastors uses a dot-notation token path system. Every token path maps to a CSS custom property at compile time:
token.space.4
--mastors-space-4: 1rem
Token Registry API
import { TokenRegistry, defaultTokens } from "@mastors/tokens";
const registry = new TokenRegistry(defaultTokens);
registry.resolve("token.space.4"); // → "1rem"
registry.resolveToVar("token.shadow.elevated"); // → { cssVar: "--mastors-shadow-elevated", fallback: "..." }
registry.generateCssVars(":root"); // → ":root { --mastors-space-4: 1rem; ... }"
// Extend with overrides (immutable — returns new registry)
const custom = registry.extend({
token: { space: { "4": "0.875rem" } } // override one value
});
Accessibility Guard
The @mastors/accessibility package wraps every resolved intent with automatic
WCAG 2.2 compliance checking — contrast ratios, focus rings, ARIA hints, and motion sensitivity.
@media (prefers-reduced-motion: reduce) automatically.
Guard modes
| Mode | Behavior | When to use |
|---|---|---|
| warn | console.warn on violations |
Default — development |
| enforce | Throws on error-severity violations |
CI/CD pipelines |
| report | Silently collects all violations | Audits / manifests |
| off | Disables guard entirely | Performance-critical builds |
import { AccessibilityGuard, contrastRatio, meetsContrastRequirement } from "@mastors/accessibility";
// Utility functions
contrastRatio("#3b82f6", "#ffffff"); // → 3.94
meetsContrastRequirement("#000000", "#ffffff", "AA"); // → true
// Guard instance
const guard = new AccessibilityGuard("enforce", tokenRegistry);
guard.check(intentId, styleMap); // throws if error-severity violations exist
// Generate a full compliance manifest
const manifest = guard.generateManifest(totalIntents);
// { totalIntents, passCount, failCount, violations: A11yViolation[] }
Package Reference
Mastors is a pnpm monorepo with fully tree-shakeable packages.
| Package | Description | Key exports |
|---|---|---|
@mastors/api |
Public Intent Declaration API | intent · configure · semantics · compose |
@mastors/core |
Semantic Resolver Engine | SemanticResolver · defaultResolver |
@mastors/schemas |
Built-in semantic vocabulary | defineSchema · defaultSchema · builtinVocabulary |
@mastors/tokens |
Design token registry | TokenRegistry · defaultTokens · defaultRegistry |
@mastors/compiler |
Zero-runtime CSS output compiler | MastorsCompiler |
@mastors/accessibility |
WCAG 2.2 guard layer | AccessibilityGuard · contrastRatio · generateFocusRing |
@mastors/types |
Shared TypeScript definitions | All core types (tree-shakeable) |
@mastors/utils |
Shared utility functions | camelToKebab · tokenPathToCssVar · deepMerge |
@mastors/adapter-react |
React adapter | useIntent · Mastors.* |
@mastors/adapter-vue |
Vue 3 adapter | useIntent · MastorsPlugin · v-intent |
@mastors/adapter-svelte |
Svelte adapter | resolveIntent · intentAction |
@mastors/plugin-vite |
Vite build plugin | mastorsPlugin |
@mastors/plugin-webpack |
webpack build plugin | MastorsWebpackPlugin |
Roadmap
Mastors is actively developed. Here's what's shipped and what's coming.
intent() API · SemanticResolver v0.1 · Vite
plugin · Interactive Playgroundmastors ai-normalize) · Figma
plugin · Vue & Svelte adapters · Mastors Studio betaTry it in the Playground
Type any intent identifier and see live CSS output, token references, WCAG hints, and a rendered preview.
Open Mastors Playground