@valancex/style 0.0.0-stage → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +33 -2
- package/contract/style-contract-v1.json +207 -0
- package/dist/check.d.ts +31 -0
- package/dist/check.js +250 -0
- package/dist/color.d.ts +17 -0
- package/dist/color.js +82 -0
- package/dist/contrast.d.ts +5 -0
- package/dist/contrast.js +13 -0
- package/dist/coverage.d.ts +21 -0
- package/dist/coverage.js +60 -0
- package/dist/css.d.ts +9 -0
- package/dist/css.js +201 -0
- package/dist/desktop.d.ts +33 -0
- package/dist/desktop.js +112 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +11 -0
- package/dist/load.d.ts +5 -0
- package/dist/load.js +8 -0
- package/dist/primitives.d.ts +2 -0
- package/dist/primitives.js +364 -0
- package/dist/suggest.d.ts +2 -0
- package/dist/suggest.js +22 -0
- package/dist/theme.d.ts +5 -0
- package/dist/theme.js +129 -0
- package/dist/tokens.d.ts +21 -0
- package/dist/tokens.js +257 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.js +3 -0
- package/package.json +64 -3
- package/schema/dtcg-2025.10/NOTICE.md +6 -0
- package/schema/dtcg-2025.10/format/group.json +54 -0
- package/schema/dtcg-2025.10/format/groupOrToken.json +14 -0
- package/schema/dtcg-2025.10/format/token.json +450 -0
- package/schema/dtcg-2025.10/format/tokenType.json +22 -0
- package/schema/dtcg-2025.10/format/values/border.json +44 -0
- package/schema/dtcg-2025.10/format/values/color.json +570 -0
- package/schema/dtcg-2025.10/format/values/cubicBezier.json +56 -0
- package/schema/dtcg-2025.10/format/values/dimension.json +34 -0
- package/schema/dtcg-2025.10/format/values/duration.json +34 -0
- package/schema/dtcg-2025.10/format/values/fontFamily.json +33 -0
- package/schema/dtcg-2025.10/format/values/fontWeight.json +38 -0
- package/schema/dtcg-2025.10/format/values/gradient.json +52 -0
- package/schema/dtcg-2025.10/format/values/number.json +7 -0
- package/schema/dtcg-2025.10/format/values/shadow.json +102 -0
- package/schema/dtcg-2025.10/format/values/strokeStyle.json +63 -0
- package/schema/dtcg-2025.10/format/values/transition.json +44 -0
- package/schema/dtcg-2025.10/format/values/typography.json +72 -0
- package/schema/dtcg-2025.10/format.json +105 -0
- package/schema/recipe-v1.schema.json +43 -0
- package/schema/style-contract-v1.schema.json +171 -0
- package/schema/theme-v1.schema.json +78 -0
- package/themes/default/recipes/button.json +87 -0
- package/themes/default/recipes/checkbox.json +26 -0
- package/themes/default/recipes/column.json +10 -0
- package/themes/default/recipes/heading.json +11 -0
- package/themes/default/recipes/row.json +10 -0
- package/themes/default/recipes/text.json +12 -0
- package/themes/default/recipes/textfield.json +37 -0
- package/themes/default/theme.json +61 -0
- package/themes/default/tokens/base.tokens.json +193 -0
- package/themes/default/tokens/contrast.tokens.json +127 -0
- package/themes/default/tokens/dark.tokens.json +127 -0
- package/themes/default/tokens/light.tokens.json +127 -0
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// Design Tokens Community Group documents (format 2025.10): validated against the group's own JSON Schemas (vendored unchanged in schema/dtcg-2025.10/, see its NOTICE.md), then the graph pass
|
|
2
|
+
// the schemas cannot make. A schema checks one document's shape; it cannot follow an alias to its target, so a cycle, a reference to nothing, a `$extends` of a group that is not there, and an alias
|
|
3
|
+
// whose target has another type are found here.
|
|
4
|
+
import Ajv from "ajv";
|
|
5
|
+
import addFormats from "ajv-formats";
|
|
6
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
7
|
+
import { join, relative } from "node:path";
|
|
8
|
+
import { root } from "./load.js";
|
|
9
|
+
const BASE = "https://www.designtokens.org/schemas/2025.10/";
|
|
10
|
+
const directory = join(root, "schema", "dtcg-2025.10");
|
|
11
|
+
let validator;
|
|
12
|
+
const files = (dir) => readdirSync(dir, { withFileTypes: true }).flatMap((entry) => (entry.isDirectory() ? files(join(dir, entry.name)) : entry.name.endsWith(".json") ? [join(dir, entry.name)] : []));
|
|
13
|
+
/** The validator for a whole token document: the group's `format.json` with every schema it references. */
|
|
14
|
+
export const dtcgValidator = () => {
|
|
15
|
+
if (validator === undefined) {
|
|
16
|
+
const ajv = new Ajv.default({ strict: false, allErrors: false });
|
|
17
|
+
addFormats(ajv);
|
|
18
|
+
for (const file of files(directory)) {
|
|
19
|
+
ajv.addSchema(JSON.parse(readFileSync(file, "utf8")), BASE + relative(directory, file).split("\\").join("/"));
|
|
20
|
+
}
|
|
21
|
+
validator = ajv.getSchema(`${BASE}format.json`);
|
|
22
|
+
}
|
|
23
|
+
return validator;
|
|
24
|
+
};
|
|
25
|
+
/** Whether `document` is a valid token document, and the first error if not. */
|
|
26
|
+
export const validateDocument = (document) => {
|
|
27
|
+
const validate = dtcgValidator();
|
|
28
|
+
if (validate(document)) {
|
|
29
|
+
return { valid: true };
|
|
30
|
+
}
|
|
31
|
+
const first = validate.errors?.[0];
|
|
32
|
+
return { valid: false, error: first === undefined ? "invalid" : `${first.instancePath === "" ? "(document)" : first.instancePath} ${first.message ?? ""}`.trim() };
|
|
33
|
+
};
|
|
34
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35
|
+
/** A token has a `$value`, or is defined by a JSON Pointer `$ref` to a value (the format allows both). */
|
|
36
|
+
const isToken = (value) => isObject(value) && ("$value" in value || typeof value["$ref"] === "string");
|
|
37
|
+
const valueOf = (token) => (token["$value"] !== undefined ? token["$value"] : { $ref: token["$ref"] });
|
|
38
|
+
const ALIAS = /^\{([^{}]+)\}$/;
|
|
39
|
+
const lookup = (document, path) => {
|
|
40
|
+
let at = document;
|
|
41
|
+
for (const part of path.split(".")) {
|
|
42
|
+
at = isObject(at) ? at[part] : undefined;
|
|
43
|
+
if (at === undefined) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return at;
|
|
48
|
+
};
|
|
49
|
+
const merge = (base, over) => {
|
|
50
|
+
const out = { ...base };
|
|
51
|
+
for (const [key, value] of Object.entries(over)) {
|
|
52
|
+
const was = out[key];
|
|
53
|
+
out[key] = isObject(value) && isObject(was) && !isToken(value) && !isToken(was) ? merge(was, value) : value;
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
};
|
|
57
|
+
/** Every `$extends` replaced by what it extends (a group's own members win). Reports an `$extends` that points at nothing or loops. */
|
|
58
|
+
const expand = (document, problems, file) => {
|
|
59
|
+
const expanding = [];
|
|
60
|
+
const visit = (node, path) => {
|
|
61
|
+
let result = {};
|
|
62
|
+
const extended = node["$extends"];
|
|
63
|
+
if (typeof extended === "string") {
|
|
64
|
+
const match = ALIAS.exec(extended);
|
|
65
|
+
const target = match === null ? undefined : lookup(document, match[1]);
|
|
66
|
+
if (match === null || !isObject(target) || isToken(target)) {
|
|
67
|
+
problems.push({ file, path, code: "extends-unresolved", message: `${path === "" ? "the document" : path} extends ${extended}, which is not a group` });
|
|
68
|
+
}
|
|
69
|
+
else if (expanding.includes(match[1]) || match[1] === path) {
|
|
70
|
+
problems.push({
|
|
71
|
+
file,
|
|
72
|
+
path,
|
|
73
|
+
code: "extends-cycle",
|
|
74
|
+
message: `${path === "" ? "the document" : path} extends ${extended}, which leads back to it (${[...expanding, match[1]].join(" -> ")})`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
expanding.push(match[1]);
|
|
79
|
+
result = visit(target, match[1]);
|
|
80
|
+
expanding.pop();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const own = {};
|
|
84
|
+
for (const [key, value] of Object.entries(node)) {
|
|
85
|
+
if (key !== "$extends") {
|
|
86
|
+
own[key] = isObject(value) && !isToken(value) ? visit(value, path === "" ? key : `${path}.${key}`) : value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return merge(result, own);
|
|
90
|
+
};
|
|
91
|
+
return visit(document, "");
|
|
92
|
+
};
|
|
93
|
+
const flatten = (document) => {
|
|
94
|
+
const flat = new Map();
|
|
95
|
+
const visit = (node, path, inherited) => {
|
|
96
|
+
const type = typeof node["$type"] === "string" ? node["$type"] : inherited;
|
|
97
|
+
for (const [key, value] of Object.entries(node)) {
|
|
98
|
+
if (key === "$root" && isToken(value)) {
|
|
99
|
+
flat.set(path, { type: typeof value["$type"] === "string" ? value["$type"] : type, value: valueOf(value) });
|
|
100
|
+
}
|
|
101
|
+
else if (!key.startsWith("$") && isObject(value)) {
|
|
102
|
+
const here = path === "" ? key : `${path}.${key}`;
|
|
103
|
+
if (isToken(value)) {
|
|
104
|
+
flat.set(here, { type: typeof value["$type"] === "string" ? value["$type"] : type, value: valueOf(value) });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
visit(value, here, type);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
visit(document, "", undefined);
|
|
113
|
+
return flat;
|
|
114
|
+
};
|
|
115
|
+
const pointer = (document, reference) => {
|
|
116
|
+
if (!reference.startsWith("#/")) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
const parts = reference.slice(2).split("/");
|
|
120
|
+
// a pointer reaches into a token's value (the format: it points at a part of `$value`), never at a group, a description or other metadata
|
|
121
|
+
if (!parts.includes("$value")) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
let at = document;
|
|
125
|
+
for (const part of parts) {
|
|
126
|
+
const key = decodeURIComponent(part).replaceAll("~1", "/").replaceAll("~0", "~");
|
|
127
|
+
at = isObject(at) ? at[key] : Array.isArray(at) && /^(0|[1-9]\d*)$/.test(key) ? at[Number(key)] : undefined;
|
|
128
|
+
if (at === undefined) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return at;
|
|
133
|
+
};
|
|
134
|
+
/** The pass over one document that has already been checked against the schemas: follows `$extends`, curly-brace aliases and JSON Pointer `$ref`s to their ends. */
|
|
135
|
+
export const resolveDocument = (document, file) => {
|
|
136
|
+
const problems = [];
|
|
137
|
+
if (!isObject(document)) {
|
|
138
|
+
return { tokens: new Map(), problems: [{ file, code: "token-document", message: "a token document is an object" }], resolved: null };
|
|
139
|
+
}
|
|
140
|
+
const expanded = expand(document, problems, file);
|
|
141
|
+
const flat = flatten(expanded);
|
|
142
|
+
const tokens = new Map();
|
|
143
|
+
const resolving = [];
|
|
144
|
+
const failed = new Set();
|
|
145
|
+
const resolveToken = (path) => {
|
|
146
|
+
const done = tokens.get(path);
|
|
147
|
+
if (done !== undefined) {
|
|
148
|
+
return done;
|
|
149
|
+
}
|
|
150
|
+
const entry = flat.get(path);
|
|
151
|
+
if (entry === undefined || failed.has(path)) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
if (resolving.includes(path)) {
|
|
155
|
+
problems.push({ file, path, code: "token-cycle", message: `${path} refers to itself through ${[...resolving.slice(resolving.indexOf(path)), path].join(" -> ")}` });
|
|
156
|
+
failed.add(path);
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
resolving.push(path);
|
|
160
|
+
const value = resolveValue(entry.value, path, entry.type);
|
|
161
|
+
resolving.pop();
|
|
162
|
+
if (value === undefined) {
|
|
163
|
+
failed.add(path);
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
const type = entry.type ?? value.type;
|
|
167
|
+
if (type === undefined) {
|
|
168
|
+
problems.push({ file, path, code: "token-no-type", message: `${path} has no type: give it \`$type\`, or put it in a group that has one` });
|
|
169
|
+
failed.add(path);
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
const resolved = { path, type: type, value: value.value };
|
|
173
|
+
tokens.set(path, resolved);
|
|
174
|
+
return resolved;
|
|
175
|
+
};
|
|
176
|
+
/** The value with its aliases and pointers followed, and the type of the alias it ended at, if the whole value is one. */
|
|
177
|
+
const resolveValue = (value, path, declared) => {
|
|
178
|
+
if (typeof value === "string") {
|
|
179
|
+
const match = ALIAS.exec(value);
|
|
180
|
+
if (match === null) {
|
|
181
|
+
return { value };
|
|
182
|
+
}
|
|
183
|
+
const target = match[1];
|
|
184
|
+
const end = resolveToken(target);
|
|
185
|
+
if (end === undefined) {
|
|
186
|
+
if (!failed.has(target) && !flat.has(target)) {
|
|
187
|
+
problems.push({ file, path, code: "token-unresolved", message: `${path} refers to {${target}}, which is not a token` });
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
if (declared !== undefined && declared !== end.type) {
|
|
192
|
+
problems.push({ file, path, code: "token-type-mismatch", message: `${path} is a ${declared} but refers to {${target}}, which is a ${end.type}` });
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
return { value: end.value, type: end.type };
|
|
196
|
+
}
|
|
197
|
+
if (Array.isArray(value)) {
|
|
198
|
+
const items = value.map((each) => resolveValue(each, path, undefined));
|
|
199
|
+
return items.some((each) => each === undefined) ? undefined : { value: items.map((each) => each.value) };
|
|
200
|
+
}
|
|
201
|
+
if (isObject(value)) {
|
|
202
|
+
const reference = value["$ref"];
|
|
203
|
+
if (typeof reference === "string") {
|
|
204
|
+
const target = pointer(expanded, reference);
|
|
205
|
+
if (target === undefined) {
|
|
206
|
+
problems.push({ file, path, code: "token-unresolved", message: `${path} points at ${reference}, which is nothing in the document` });
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
if (resolving.includes(`ref:${reference}`)) {
|
|
210
|
+
problems.push({ file, path, code: "token-cycle", message: `${path} points at ${reference}, which leads back to it` });
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
resolving.push(`ref:${reference}`);
|
|
214
|
+
const end = resolveValue(target, path, undefined);
|
|
215
|
+
resolving.pop();
|
|
216
|
+
return end;
|
|
217
|
+
}
|
|
218
|
+
const out = {};
|
|
219
|
+
for (const [key, each] of Object.entries(value)) {
|
|
220
|
+
const resolved = resolveValue(each, path, undefined);
|
|
221
|
+
if (resolved === undefined) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
out[key] = resolved.value;
|
|
225
|
+
}
|
|
226
|
+
return { value: out };
|
|
227
|
+
}
|
|
228
|
+
return { value };
|
|
229
|
+
};
|
|
230
|
+
for (const path of flat.keys()) {
|
|
231
|
+
resolveToken(path);
|
|
232
|
+
}
|
|
233
|
+
const withValue = (token, end) => {
|
|
234
|
+
const { $ref: _ref, ...rest } = token;
|
|
235
|
+
return { ...rest, $type: end.type, $value: end.value };
|
|
236
|
+
};
|
|
237
|
+
// the document again, with each token's value and type as resolved
|
|
238
|
+
const substitute = (node, path) => {
|
|
239
|
+
const out = {};
|
|
240
|
+
for (const [key, value] of Object.entries(node)) {
|
|
241
|
+
if (key === "$root" && isToken(value)) {
|
|
242
|
+
const end = tokens.get(path);
|
|
243
|
+
out[key] = end === undefined ? value : withValue(value, end);
|
|
244
|
+
}
|
|
245
|
+
else if (!key.startsWith("$") && isObject(value)) {
|
|
246
|
+
const here = path === "" ? key : `${path}.${key}`;
|
|
247
|
+
const end = isToken(value) ? tokens.get(here) : undefined;
|
|
248
|
+
out[key] = isToken(value) ? (end === undefined ? value : withValue(value, end)) : substitute(value, here);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
out[key] = value;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return out;
|
|
255
|
+
};
|
|
256
|
+
return { tokens, problems, resolved: substitute(expanded, "") };
|
|
257
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export type TokenType = "color" | "dimension" | "fontFamily" | "fontWeight" | "duration" | "cubicBezier" | "number" | "shadow" | "typography" | "border" | "transition" | "gradient" | "strokeStyle";
|
|
2
|
+
export type Accepts = "color" | "stroke" | "dimension" | "space-list" | "typography" | "shadow" | "opacity" | "alignment";
|
|
3
|
+
export interface PropertyDefinition {
|
|
4
|
+
readonly accepts: Accepts;
|
|
5
|
+
/** The CSS properties the web compiles it to. */
|
|
6
|
+
readonly web: ReadonlyArray<string>;
|
|
7
|
+
/** Whether the Desktop honors it. */
|
|
8
|
+
readonly desktop: "supported" | "partial" | "unsupported";
|
|
9
|
+
}
|
|
10
|
+
/** What the contract needs of a primitive; generated from the primitive's specification and the web's realization (see primitives.ts), so this package reads no specification file. */
|
|
11
|
+
export interface PrimitiveInfo {
|
|
12
|
+
readonly status: "portable" | "web-only" | "desktop-only";
|
|
13
|
+
/** A person acts on it (it has keys), so the interaction states apply. */
|
|
14
|
+
readonly interactive: boolean;
|
|
15
|
+
/** The semantic states its specification declares. */
|
|
16
|
+
readonly states: ReadonlyArray<string>;
|
|
17
|
+
/** The style variants it takes, each with the attribute the web realizes it as. */
|
|
18
|
+
readonly variants: ReadonlyArray<{
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly attribute: string;
|
|
21
|
+
}>;
|
|
22
|
+
/** The value of its `data-vlx` hook on the web: how a stylesheet reaches its root. Absent where the web has no hook for it. */
|
|
23
|
+
readonly hook?: string;
|
|
24
|
+
/** The CSS selector of each of its semantic states on the web. */
|
|
25
|
+
readonly stateSelectors: Readonly<Record<string, string>>;
|
|
26
|
+
}
|
|
27
|
+
export interface Contract {
|
|
28
|
+
readonly version: 1;
|
|
29
|
+
readonly parts: Readonly<Record<string, string>>;
|
|
30
|
+
/** Interaction states, each with the CSS pseudo-class that reaches it on the web. */
|
|
31
|
+
readonly interactionStates: Readonly<Record<string, string>>;
|
|
32
|
+
/** The media query an interaction state's rules sit in on the web: hover styling only where a pointer can hover (I15), so a touch screen's sticky `:hover` never paints. */
|
|
33
|
+
readonly interactionMedia?: Readonly<Record<string, string>>;
|
|
34
|
+
readonly semanticStates: ReadonlyArray<string>;
|
|
35
|
+
readonly variants: Readonly<Record<string, {
|
|
36
|
+
readonly values: ReadonlyArray<string>;
|
|
37
|
+
readonly default: string;
|
|
38
|
+
}>>;
|
|
39
|
+
readonly conditions: Readonly<Record<string, {
|
|
40
|
+
readonly values: ReadonlyArray<string>;
|
|
41
|
+
readonly web?: Readonly<Record<string, string>>;
|
|
42
|
+
readonly desktop?: "supported" | "unsupported";
|
|
43
|
+
}>>;
|
|
44
|
+
/** Whether the Desktop applies a recipe's styles for a state. */
|
|
45
|
+
readonly desktopStates?: Readonly<Record<string, "supported" | "partial" | "unsupported">>;
|
|
46
|
+
readonly properties: Readonly<Record<string, PropertyDefinition>>;
|
|
47
|
+
/** A mode named here is also the page's own whenever its condition holds (`dark` when the system prefers dark); any mode is reachable by `data-theme`. */
|
|
48
|
+
readonly modeConditions?: Readonly<Record<string, string>>;
|
|
49
|
+
readonly desktopNotes?: Readonly<Record<string, string>>;
|
|
50
|
+
}
|
|
51
|
+
/** A value in a recipe: a token path, a number, a keyword, a list of token paths, or a stroke (`width`, `color`, `style`, `offset`). */
|
|
52
|
+
export type RecipeValue = string | number | ReadonlyArray<string> | {
|
|
53
|
+
readonly width?: string;
|
|
54
|
+
readonly color?: string;
|
|
55
|
+
readonly style?: "solid" | "dashed" | "dotted";
|
|
56
|
+
readonly offset?: string;
|
|
57
|
+
};
|
|
58
|
+
export type Declarations = Readonly<Record<string, RecipeValue>>;
|
|
59
|
+
export interface Recipe {
|
|
60
|
+
readonly version: 1;
|
|
61
|
+
readonly target: {
|
|
62
|
+
readonly primitive: string;
|
|
63
|
+
readonly part: string;
|
|
64
|
+
};
|
|
65
|
+
readonly base?: Declarations;
|
|
66
|
+
readonly variants?: Readonly<Record<string, Readonly<Record<string, Declarations>>>>;
|
|
67
|
+
readonly states?: Readonly<Record<string, Declarations>>;
|
|
68
|
+
readonly when?: Readonly<Record<string, Declarations>>;
|
|
69
|
+
readonly escape?: {
|
|
70
|
+
readonly web?: {
|
|
71
|
+
readonly css: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export interface Theme {
|
|
76
|
+
readonly version: 1;
|
|
77
|
+
readonly name: string;
|
|
78
|
+
/** The tokens that do not change with the mode (space, radius, type), relative to the theme file. Optional. */
|
|
79
|
+
readonly base?: string;
|
|
80
|
+
/** Mode name to the token document that defines the tokens that do change (relative to the theme file). `light` is the mode a page has when it asks for none. */
|
|
81
|
+
readonly modes: Readonly<Record<string, string>>;
|
|
82
|
+
readonly recipes: ReadonlyArray<string>;
|
|
83
|
+
readonly contrast?: ReadonlyArray<{
|
|
84
|
+
readonly text: string;
|
|
85
|
+
readonly on: string;
|
|
86
|
+
readonly min: number;
|
|
87
|
+
readonly modes?: ReadonlyArray<string>;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
/** A mistake in a theme: where (a file, and a path into it when there is one), which rule, and what is wrong. The same shape `vlx` prints. */
|
|
91
|
+
export interface Problem {
|
|
92
|
+
readonly file: string;
|
|
93
|
+
readonly path?: string;
|
|
94
|
+
readonly code: string;
|
|
95
|
+
readonly message: string;
|
|
96
|
+
}
|
|
97
|
+
/** A token with its alias chain followed: the value is fully expanded. */
|
|
98
|
+
export interface ResolvedToken {
|
|
99
|
+
readonly path: string;
|
|
100
|
+
readonly type: TokenType;
|
|
101
|
+
readonly value: unknown;
|
|
102
|
+
}
|
|
103
|
+
export interface LoadedTheme {
|
|
104
|
+
readonly theme: Theme;
|
|
105
|
+
/** The directory the theme file is in. */
|
|
106
|
+
readonly directory: string;
|
|
107
|
+
/** Mode name to its resolved tokens. */
|
|
108
|
+
readonly modes: Readonly<Record<string, ReadonlyMap<string, ResolvedToken>>>;
|
|
109
|
+
readonly recipes: ReadonlyArray<{
|
|
110
|
+
readonly file: string;
|
|
111
|
+
readonly recipe: Recipe;
|
|
112
|
+
}>;
|
|
113
|
+
readonly problems: ReadonlyArray<Problem>;
|
|
114
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// The shapes of the style contract's data: the contract itself (style-contract-v1), a recipe (recipe-v1) and a theme (theme-v1). Tokens are Design Tokens Community Group documents (format 2025.10)
|
|
2
|
+
// and keep that format's shapes; what this package adds to them is a graph pass and the resolved view.
|
|
3
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valancex/style",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "The style contract for VALANCE: tokens (Design Tokens format), recipes for a primitive's root part, states, variants and conditions, as data; and what is generated or checked from them (a layered stylesheet for the web, resolved values for the Desktop, the contrast of declared pairs, the coverage of a theme).",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"valance",
|
|
7
|
+
"theme",
|
|
8
|
+
"design-tokens",
|
|
9
|
+
"style"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/ValanceX/Valance/tree/main/packages/style#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/ValanceX/Valance/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ValanceX/Valance.git",
|
|
18
|
+
"directory": "packages/style"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Jack Miller <jackmiller0899@gmail.com>",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22"
|
|
25
|
+
},
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
33
|
+
},
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"contract",
|
|
38
|
+
"schema",
|
|
39
|
+
"themes"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public",
|
|
43
|
+
"provenance": true
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"ajv": "^8.17.1",
|
|
47
|
+
"ajv-formats": "^3.0.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"@valancex/mesh-runtime": "^0.17.0",
|
|
52
|
+
"@valancex/port-web": "^0.14.0",
|
|
53
|
+
"@vitest/browser": "3.2.7",
|
|
54
|
+
"playwright": "1.56.1",
|
|
55
|
+
"stylelint": "^17.16.0",
|
|
56
|
+
"typescript": "^5.6.0",
|
|
57
|
+
"vitest": "^3.2.0",
|
|
58
|
+
"@valancex/common": "^0.14.0",
|
|
59
|
+
"@valancex/valance": "^0.14.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsc -p tsconfig.json",
|
|
63
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"test:browser": "vitest run -c vitest.browser.config.ts"
|
|
66
|
+
}
|
|
6
67
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Design Tokens Community Group, format 2025.10: schemas and test suite
|
|
2
|
+
|
|
3
|
+
Copied unchanged from https://github.com/design-tokens/community-group at commit 882ebd6716abef9a46d8ef5fb12e82009d6cee2e (`schemas/src/2025.10/` into `schema/dtcg-2025.10/`, `test-suite/tests/2025.10/format/` into `test/fixtures/dtcg-2025.10/`) by `scripts/vendor-dtcg.mjs`.
|
|
4
|
+
18 schema files and 220 tests.
|
|
5
|
+
|
|
6
|
+
The files are the group's. They are licensed under the [W3C Software and Document License](https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document), as the repository's LICENSE.md says for its Reports; the format is a Final Community Group Report, not a W3C Standard.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://www.designtokens.org/schemas/2025.10/format/group.json",
|
|
4
|
+
"title": "Group",
|
|
5
|
+
"description": "A group in the DTCG specification",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$type": {
|
|
9
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format/tokenType.json",
|
|
10
|
+
"description": "The type for tokens in this group (inherited by nested tokens unless overridden)"
|
|
11
|
+
},
|
|
12
|
+
"$description": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "A plain text description of the group"
|
|
15
|
+
},
|
|
16
|
+
"$extensions": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"description": "Vendor-specific extensions"
|
|
19
|
+
},
|
|
20
|
+
"$extends": {
|
|
21
|
+
"oneOf": [
|
|
22
|
+
{
|
|
23
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format.json#/definitions/curlyBraceReference"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format.json#/definitions/jsonPointerReference"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"description": "Reference to another group to inherit tokens and properties from"
|
|
30
|
+
},
|
|
31
|
+
"$deprecated": {
|
|
32
|
+
"oneOf": [
|
|
33
|
+
{
|
|
34
|
+
"type": "boolean"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "string"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"description": "Whether this group is deprecated"
|
|
41
|
+
},
|
|
42
|
+
"$root": {
|
|
43
|
+
"description": "Root token for this group. The $root token provides a base value for the group while allowing for variants or extensions.",
|
|
44
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format/token.json"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"patternProperties": {
|
|
48
|
+
"^[^${}.][^{}.]*$": {
|
|
49
|
+
"description": "Nested groups and tokens (see ../format.json#/definitions/tokenOrGroupName for pattern definition)",
|
|
50
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format/groupOrToken.json"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"additionalProperties": false
|
|
54
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://www.designtokens.org/schemas/2025.10/format/groupOrToken.json",
|
|
4
|
+
"title": "Group or Token",
|
|
5
|
+
"description": "A group or a token in the DTCG specification. A token is identified by the presence of a $value property, while a group is any object without a $value property.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{
|
|
8
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format/group.json"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"$ref": "https://www.designtokens.org/schemas/2025.10/format/token.json"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|