@trajectonomics/design-system-theme 0.0.0-stage → 0.1.0-alpha.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/dist/index.cjs +114 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.mts +27 -0
- package/dist/index.mjs +110 -0
- package/dist/index.mjs.map +1 -0
- package/dist/themes/dark.css +21 -0
- package/dist/themes/light.css +21 -0
- package/package.json +64 -5
- package/tailwind.css +21 -0
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Trajectonomics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _trajectonomics_design_system = require("@trajectonomics/design-system");
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
/** Machine-readable rejection codes for Theme definition. */
|
|
5
|
+
let ThemeErrorCode = /* @__PURE__ */ function(ThemeErrorCode) {
|
|
6
|
+
ThemeErrorCode["INVALID_THEME_NAME"] = "INVALID_THEME_NAME";
|
|
7
|
+
ThemeErrorCode["MISSING_TOKENS_SECTION"] = "MISSING_TOKENS_SECTION";
|
|
8
|
+
ThemeErrorCode["MISSING_CONTRACT_ROLE"] = "MISSING_CONTRACT_ROLE";
|
|
9
|
+
ThemeErrorCode["UNKNOWN_CONTRACT_ROLE"] = "UNKNOWN_CONTRACT_ROLE";
|
|
10
|
+
ThemeErrorCode["WRONG_TOKEN_TYPE"] = "WRONG_TOKEN_TYPE";
|
|
11
|
+
ThemeErrorCode["UNSAFE_TOKEN_VALUE"] = "UNSAFE_TOKEN_VALUE";
|
|
12
|
+
return ThemeErrorCode;
|
|
13
|
+
}({});
|
|
14
|
+
/** Error thrown when a Theme violates a domain rule; carries a distinct `code` and the offending `field`. */
|
|
15
|
+
var ThemeError = class extends Error {
|
|
16
|
+
code;
|
|
17
|
+
field;
|
|
18
|
+
constructor(code, field, message) {
|
|
19
|
+
super(`[${code}]: ${message}`);
|
|
20
|
+
this.name = "ThemeError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.field = field;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// src/validation.ts
|
|
26
|
+
const TOKENS_FIELD = "tokens";
|
|
27
|
+
const UNSAFE_VALUE = /[;}]|url\s*\(/i;
|
|
28
|
+
const ROLE_NAMES = new Set(_trajectonomics_design_system.semanticTokenContract.roles.map((role) => role.name));
|
|
29
|
+
function tokenField(roleName) {
|
|
30
|
+
return `${TOKENS_FIELD}.${roleName}`;
|
|
31
|
+
}
|
|
32
|
+
function validateThemeName(name) {
|
|
33
|
+
if (typeof name === "string" && name.trim().length > 0) return name.trim();
|
|
34
|
+
throw new ThemeError("INVALID_THEME_NAME", "name", "Theme name must be a non-empty string.");
|
|
35
|
+
}
|
|
36
|
+
function assertSafeValue(value, field) {
|
|
37
|
+
if (!UNSAFE_VALUE.test(value)) return;
|
|
38
|
+
throw new ThemeError("UNSAFE_TOKEN_VALUE", field, "A Theme token value must not contain characters that can break out of a CSS declaration.");
|
|
39
|
+
}
|
|
40
|
+
function assertTokenValue(role, value, field) {
|
|
41
|
+
if (typeof value !== "string") throw new ThemeError("WRONG_TOKEN_TYPE", field, `"${role.name}" takes a string.`);
|
|
42
|
+
assertSafeValue(value, field);
|
|
43
|
+
try {
|
|
44
|
+
return (0, _trajectonomics_design_system.createPrimitiveToken)({
|
|
45
|
+
name: role.name,
|
|
46
|
+
type: role.type,
|
|
47
|
+
value
|
|
48
|
+
}).value;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof _trajectonomics_design_system.DomainError && error.code === _trajectonomics_design_system.DomainErrorCode.INVALID_TOKEN_VALUE) throw new ThemeError("WRONG_TOKEN_TYPE", field, `"${role.name}" takes a ${role.type} value.`);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isRecord(value) {
|
|
55
|
+
return typeof value === "object" && value !== null;
|
|
56
|
+
}
|
|
57
|
+
/** Validates a Theme against the Contract, returning its normalized values. */
|
|
58
|
+
function validateTheme(input) {
|
|
59
|
+
const candidate = isRecord(input) ? input : void 0;
|
|
60
|
+
const name = validateThemeName(candidate?.["name"]);
|
|
61
|
+
const tokens = candidate?.["tokens"];
|
|
62
|
+
if (!isRecord(tokens)) throw new ThemeError("MISSING_TOKENS_SECTION", TOKENS_FIELD, "A Theme must have a tokens section.");
|
|
63
|
+
const validated = {};
|
|
64
|
+
for (const role of _trajectonomics_design_system.semanticTokenContract.roles) {
|
|
65
|
+
const field = tokenField(role.name);
|
|
66
|
+
if (!Object.hasOwn(tokens, role.name)) throw new ThemeError("MISSING_CONTRACT_ROLE", field, `Theme is missing the Contract role "${role.name}".`);
|
|
67
|
+
validated[role.name] = assertTokenValue(role, tokens[role.name], field);
|
|
68
|
+
}
|
|
69
|
+
for (const roleName of Object.keys(tokens)) if (!ROLE_NAMES.has(roleName)) throw new ThemeError("UNKNOWN_CONTRACT_ROLE", tokenField(roleName), `"${roleName}" is not a role in the Semantic Token Contract.`);
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
tokens: validated
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// src/define-theme.ts
|
|
76
|
+
/** The checked factory for Themes: validates raw Theme data against the Semantic Token Contract. */
|
|
77
|
+
function defineTheme(input) {
|
|
78
|
+
const validated = validateTheme(input);
|
|
79
|
+
return Object.freeze({
|
|
80
|
+
name: validated.name,
|
|
81
|
+
contractVersion: _trajectonomics_design_system.semanticTokenContract.version,
|
|
82
|
+
tokens: Object.freeze({ ...validated.tokens })
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// src/theme-to-css.ts
|
|
86
|
+
function escapeCssString(value) {
|
|
87
|
+
let escaped = "";
|
|
88
|
+
for (const character of value) {
|
|
89
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
90
|
+
if (character === "\\") escaped += "\\\\";
|
|
91
|
+
else if (character === "\"") escaped += "\\\"";
|
|
92
|
+
else if (codePoint < 32 || codePoint === 127) escaped += `\\${codePoint.toString(16)} `;
|
|
93
|
+
else escaped += character;
|
|
94
|
+
}
|
|
95
|
+
return escaped;
|
|
96
|
+
}
|
|
97
|
+
function cssVariableName(roleName) {
|
|
98
|
+
return `--ds-${roleName.replaceAll(".", "-")}`;
|
|
99
|
+
}
|
|
100
|
+
/** The Theme-to-CSS emitter: data-theme-scoped CSS custom properties for a valid Theme. */
|
|
101
|
+
function themeToCss(theme) {
|
|
102
|
+
const validated = validateTheme(theme);
|
|
103
|
+
const declarations = _trajectonomics_design_system.semanticTokenContract.roles.map((role) => {
|
|
104
|
+
const value = validated.tokens[role.name];
|
|
105
|
+
return ` ${cssVariableName(role.name)}: ${value};`;
|
|
106
|
+
});
|
|
107
|
+
return `${`[data-theme="${escapeCssString(validated.name)}"]`} {\n${declarations.join("\n")}\n}\n`;
|
|
108
|
+
}
|
|
109
|
+
exports.ThemeError = ThemeError;
|
|
110
|
+
exports.ThemeErrorCode = ThemeErrorCode;
|
|
111
|
+
exports.defineTheme = defineTheme;
|
|
112
|
+
exports.themeToCss = themeToCss;
|
|
113
|
+
|
|
114
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["semanticTokenContract","createPrimitiveToken","DomainError","DomainErrorCode","semanticTokenContract","semanticTokenContract"],"sources":["../src/errors.ts","../src/validation.ts","../src/define-theme.ts","../src/theme-to-css.ts"],"sourcesContent":["/** Machine-readable rejection codes for Theme definition. */\nexport enum ThemeErrorCode {\n\tINVALID_THEME_NAME = \"INVALID_THEME_NAME\",\n\tMISSING_TOKENS_SECTION = \"MISSING_TOKENS_SECTION\",\n\tMISSING_CONTRACT_ROLE = \"MISSING_CONTRACT_ROLE\",\n\tUNKNOWN_CONTRACT_ROLE = \"UNKNOWN_CONTRACT_ROLE\",\n\tWRONG_TOKEN_TYPE = \"WRONG_TOKEN_TYPE\",\n\tUNSAFE_TOKEN_VALUE = \"UNSAFE_TOKEN_VALUE\",\n}\n\n/** Error thrown when a Theme violates a domain rule; carries a distinct `code` and the offending `field`. */\nexport class ThemeError extends Error {\n\treadonly code: ThemeErrorCode\n\treadonly field: string\n\n\tconstructor(code: ThemeErrorCode, field: string, message: string) {\n\t\tsuper(`[${code}]: ${message}`)\n\t\tthis.name = \"ThemeError\"\n\t\tthis.code = code\n\t\tthis.field = field\n\t}\n}\n","import {\n\tcreatePrimitiveToken,\n\tDomainError,\n\tDomainErrorCode,\n\ttype SemanticTokenRole,\n\tsemanticTokenContract,\n} from \"@trajectonomics/design-system\"\nimport { ThemeError, ThemeErrorCode } from \"./errors\"\nimport type { ThemeInput } from \"./theme-input\"\n\nconst TOKENS_FIELD = \"tokens\"\nconst UNSAFE_VALUE = /[;}]|url\\s*\\(/i\nconst ROLE_NAMES = new Set(semanticTokenContract.roles.map((role) => role.name))\n\nfunction tokenField(roleName: string): string {\n\treturn `${TOKENS_FIELD}.${roleName}`\n}\n\nfunction validateThemeName(name: unknown): string {\n\tif (typeof name === \"string\" && name.trim().length > 0) return name.trim()\n\tthrow new ThemeError(\n\t\tThemeErrorCode.INVALID_THEME_NAME,\n\t\t\"name\",\n\t\t\"Theme name must be a non-empty string.\",\n\t)\n}\n\nfunction assertSafeValue(value: string, field: string): void {\n\tif (!UNSAFE_VALUE.test(value)) return\n\tthrow new ThemeError(\n\t\tThemeErrorCode.UNSAFE_TOKEN_VALUE,\n\t\tfield,\n\t\t\"A Theme token value must not contain characters that can break out of a CSS declaration.\",\n\t)\n}\n\nfunction assertTokenValue(role: SemanticTokenRole, value: unknown, field: string): string {\n\tif (typeof value !== \"string\") {\n\t\tthrow new ThemeError(ThemeErrorCode.WRONG_TOKEN_TYPE, field, `\"${role.name}\" takes a string.`)\n\t}\n\tassertSafeValue(value, field)\n\ttry {\n\t\treturn createPrimitiveToken({ name: role.name, type: role.type, value }).value\n\t} catch (error) {\n\t\tif (error instanceof DomainError && error.code === DomainErrorCode.INVALID_TOKEN_VALUE) {\n\t\t\tthrow new ThemeError(\n\t\t\t\tThemeErrorCode.WRONG_TOKEN_TYPE,\n\t\t\t\tfield,\n\t\t\t\t`\"${role.name}\" takes a ${role.type} value.`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null\n}\n\n/** Validates a Theme against the Contract, returning its normalized values. */\nexport function validateTheme(input: unknown): ThemeInput {\n\tconst candidate = isRecord(input) ? input : undefined\n\tconst name = validateThemeName(candidate?.[\"name\"])\n\tconst tokens = candidate?.[\"tokens\"]\n\tif (!isRecord(tokens)) {\n\t\tthrow new ThemeError(\n\t\t\tThemeErrorCode.MISSING_TOKENS_SECTION,\n\t\t\tTOKENS_FIELD,\n\t\t\t\"A Theme must have a tokens section.\",\n\t\t)\n\t}\n\tconst validated: Record<string, string> = {}\n\tfor (const role of semanticTokenContract.roles) {\n\t\tconst field = tokenField(role.name)\n\t\tif (!Object.hasOwn(tokens, role.name)) {\n\t\t\tthrow new ThemeError(\n\t\t\t\tThemeErrorCode.MISSING_CONTRACT_ROLE,\n\t\t\t\tfield,\n\t\t\t\t`Theme is missing the Contract role \"${role.name}\".`,\n\t\t\t)\n\t\t}\n\t\tvalidated[role.name] = assertTokenValue(role, tokens[role.name], field)\n\t}\n\tfor (const roleName of Object.keys(tokens)) {\n\t\tif (!ROLE_NAMES.has(roleName)) {\n\t\t\tthrow new ThemeError(\n\t\t\t\tThemeErrorCode.UNKNOWN_CONTRACT_ROLE,\n\t\t\t\ttokenField(roleName),\n\t\t\t\t`\"${roleName}\" is not a role in the Semantic Token Contract.`,\n\t\t\t)\n\t\t}\n\t}\n\treturn { name, tokens: validated }\n}\n","import type { Theme } from \"@trajectonomics/design-system\"\nimport { semanticTokenContract } from \"@trajectonomics/design-system\"\nimport type { ThemeInput } from \"./theme-input\"\nimport { validateTheme } from \"./validation\"\n\n/** The checked factory for Themes: validates raw Theme data against the Semantic Token Contract. */\nexport function defineTheme(input: ThemeInput): Theme {\n\tconst validated = validateTheme(input)\n\treturn Object.freeze({\n\t\tname: validated.name,\n\t\tcontractVersion: semanticTokenContract.version,\n\t\ttokens: Object.freeze({ ...validated.tokens }),\n\t})\n}\n","import { semanticTokenContract, type Theme } from \"@trajectonomics/design-system\"\nimport { validateTheme } from \"./validation\"\n\nfunction escapeCssString(value: string): string {\n\tlet escaped = \"\"\n\tfor (const character of value) {\n\t\tconst codePoint = character.codePointAt(0) ?? 0\n\t\tif (character === \"\\\\\") escaped += \"\\\\\\\\\"\n\t\telse if (character === '\"') escaped += '\\\\\"'\n\t\telse if (codePoint < 0x20 || codePoint === 0x7f) {\n\t\t\tescaped += `\\\\${codePoint.toString(16)} `\n\t\t} else escaped += character\n\t}\n\treturn escaped\n}\n\nfunction cssVariableName(roleName: string): string {\n\treturn `--ds-${roleName.replaceAll(\".\", \"-\")}`\n}\n\n/** The Theme-to-CSS emitter: data-theme-scoped CSS custom properties for a valid Theme. */\nexport function themeToCss(theme: Theme): string {\n\tconst validated = validateTheme(theme)\n\tconst declarations = semanticTokenContract.roles.map((role) => {\n\t\tconst value = validated.tokens[role.name]!\n\t\treturn ` ${cssVariableName(role.name)}: ${value};`\n\t})\n\tconst selector = `[data-theme=\"${escapeCssString(validated.name)}\"]`\n\treturn `${selector} {\\n${declarations.join(\"\\n\")}\\n}\\n`\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Theme, Theme as Theme$1 } from "@trajectonomics/design-system";
|
|
2
|
+
/** Input for defining a Theme: a name and tokens; a full Theme also fits, but its version is ignored. */
|
|
3
|
+
interface ThemeInput {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly tokens: Readonly<Record<string, string>>;
|
|
6
|
+
}
|
|
7
|
+
/** The checked factory for Themes: validates raw Theme data against the Semantic Token Contract. */
|
|
8
|
+
export declare function defineTheme(input: ThemeInput): Theme$1;
|
|
9
|
+
/** Machine-readable rejection codes for Theme definition. */
|
|
10
|
+
export declare enum ThemeErrorCode {
|
|
11
|
+
INVALID_THEME_NAME = "INVALID_THEME_NAME",
|
|
12
|
+
MISSING_TOKENS_SECTION = "MISSING_TOKENS_SECTION",
|
|
13
|
+
MISSING_CONTRACT_ROLE = "MISSING_CONTRACT_ROLE",
|
|
14
|
+
UNKNOWN_CONTRACT_ROLE = "UNKNOWN_CONTRACT_ROLE",
|
|
15
|
+
WRONG_TOKEN_TYPE = "WRONG_TOKEN_TYPE",
|
|
16
|
+
UNSAFE_TOKEN_VALUE = "UNSAFE_TOKEN_VALUE"
|
|
17
|
+
}
|
|
18
|
+
/** Error thrown when a Theme violates a domain rule; carries a distinct `code` and the offending `field`. */
|
|
19
|
+
export declare class ThemeError extends Error {
|
|
20
|
+
readonly code: ThemeErrorCode;
|
|
21
|
+
readonly field: string;
|
|
22
|
+
constructor(code: ThemeErrorCode, field: string, message: string);
|
|
23
|
+
}
|
|
24
|
+
/** The Theme-to-CSS emitter: data-theme-scoped CSS custom properties for a valid Theme. */
|
|
25
|
+
export declare function themeToCss(theme: Theme$1): string;
|
|
26
|
+
export type { Theme, ThemeInput };
|
|
27
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Theme, Theme as Theme$1 } from "@trajectonomics/design-system";
|
|
2
|
+
/** Input for defining a Theme: a name and tokens; a full Theme also fits, but its version is ignored. */
|
|
3
|
+
interface ThemeInput {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly tokens: Readonly<Record<string, string>>;
|
|
6
|
+
}
|
|
7
|
+
/** The checked factory for Themes: validates raw Theme data against the Semantic Token Contract. */
|
|
8
|
+
export declare function defineTheme(input: ThemeInput): Theme$1;
|
|
9
|
+
/** Machine-readable rejection codes for Theme definition. */
|
|
10
|
+
export declare enum ThemeErrorCode {
|
|
11
|
+
INVALID_THEME_NAME = "INVALID_THEME_NAME",
|
|
12
|
+
MISSING_TOKENS_SECTION = "MISSING_TOKENS_SECTION",
|
|
13
|
+
MISSING_CONTRACT_ROLE = "MISSING_CONTRACT_ROLE",
|
|
14
|
+
UNKNOWN_CONTRACT_ROLE = "UNKNOWN_CONTRACT_ROLE",
|
|
15
|
+
WRONG_TOKEN_TYPE = "WRONG_TOKEN_TYPE",
|
|
16
|
+
UNSAFE_TOKEN_VALUE = "UNSAFE_TOKEN_VALUE"
|
|
17
|
+
}
|
|
18
|
+
/** Error thrown when a Theme violates a domain rule; carries a distinct `code` and the offending `field`. */
|
|
19
|
+
export declare class ThemeError extends Error {
|
|
20
|
+
readonly code: ThemeErrorCode;
|
|
21
|
+
readonly field: string;
|
|
22
|
+
constructor(code: ThemeErrorCode, field: string, message: string);
|
|
23
|
+
}
|
|
24
|
+
/** The Theme-to-CSS emitter: data-theme-scoped CSS custom properties for a valid Theme. */
|
|
25
|
+
export declare function themeToCss(theme: Theme$1): string;
|
|
26
|
+
export type { Theme, ThemeInput };
|
|
27
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { DomainError, DomainErrorCode, createPrimitiveToken, semanticTokenContract } from "@trajectonomics/design-system";
|
|
2
|
+
// src/errors.ts
|
|
3
|
+
/** Machine-readable rejection codes for Theme definition. */
|
|
4
|
+
let ThemeErrorCode = /* @__PURE__ */ function(ThemeErrorCode) {
|
|
5
|
+
ThemeErrorCode["INVALID_THEME_NAME"] = "INVALID_THEME_NAME";
|
|
6
|
+
ThemeErrorCode["MISSING_TOKENS_SECTION"] = "MISSING_TOKENS_SECTION";
|
|
7
|
+
ThemeErrorCode["MISSING_CONTRACT_ROLE"] = "MISSING_CONTRACT_ROLE";
|
|
8
|
+
ThemeErrorCode["UNKNOWN_CONTRACT_ROLE"] = "UNKNOWN_CONTRACT_ROLE";
|
|
9
|
+
ThemeErrorCode["WRONG_TOKEN_TYPE"] = "WRONG_TOKEN_TYPE";
|
|
10
|
+
ThemeErrorCode["UNSAFE_TOKEN_VALUE"] = "UNSAFE_TOKEN_VALUE";
|
|
11
|
+
return ThemeErrorCode;
|
|
12
|
+
}({});
|
|
13
|
+
/** Error thrown when a Theme violates a domain rule; carries a distinct `code` and the offending `field`. */
|
|
14
|
+
var ThemeError = class extends Error {
|
|
15
|
+
code;
|
|
16
|
+
field;
|
|
17
|
+
constructor(code, field, message) {
|
|
18
|
+
super(`[${code}]: ${message}`);
|
|
19
|
+
this.name = "ThemeError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.field = field;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
// src/validation.ts
|
|
25
|
+
const TOKENS_FIELD = "tokens";
|
|
26
|
+
const UNSAFE_VALUE = /[;}]|url\s*\(/i;
|
|
27
|
+
const ROLE_NAMES = new Set(semanticTokenContract.roles.map((role) => role.name));
|
|
28
|
+
function tokenField(roleName) {
|
|
29
|
+
return `${TOKENS_FIELD}.${roleName}`;
|
|
30
|
+
}
|
|
31
|
+
function validateThemeName(name) {
|
|
32
|
+
if (typeof name === "string" && name.trim().length > 0) return name.trim();
|
|
33
|
+
throw new ThemeError("INVALID_THEME_NAME", "name", "Theme name must be a non-empty string.");
|
|
34
|
+
}
|
|
35
|
+
function assertSafeValue(value, field) {
|
|
36
|
+
if (!UNSAFE_VALUE.test(value)) return;
|
|
37
|
+
throw new ThemeError("UNSAFE_TOKEN_VALUE", field, "A Theme token value must not contain characters that can break out of a CSS declaration.");
|
|
38
|
+
}
|
|
39
|
+
function assertTokenValue(role, value, field) {
|
|
40
|
+
if (typeof value !== "string") throw new ThemeError("WRONG_TOKEN_TYPE", field, `"${role.name}" takes a string.`);
|
|
41
|
+
assertSafeValue(value, field);
|
|
42
|
+
try {
|
|
43
|
+
return createPrimitiveToken({
|
|
44
|
+
name: role.name,
|
|
45
|
+
type: role.type,
|
|
46
|
+
value
|
|
47
|
+
}).value;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof DomainError && error.code === DomainErrorCode.INVALID_TOKEN_VALUE) throw new ThemeError("WRONG_TOKEN_TYPE", field, `"${role.name}" takes a ${role.type} value.`);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isRecord(value) {
|
|
54
|
+
return typeof value === "object" && value !== null;
|
|
55
|
+
}
|
|
56
|
+
/** Validates a Theme against the Contract, returning its normalized values. */
|
|
57
|
+
function validateTheme(input) {
|
|
58
|
+
const candidate = isRecord(input) ? input : void 0;
|
|
59
|
+
const name = validateThemeName(candidate?.["name"]);
|
|
60
|
+
const tokens = candidate?.["tokens"];
|
|
61
|
+
if (!isRecord(tokens)) throw new ThemeError("MISSING_TOKENS_SECTION", TOKENS_FIELD, "A Theme must have a tokens section.");
|
|
62
|
+
const validated = {};
|
|
63
|
+
for (const role of semanticTokenContract.roles) {
|
|
64
|
+
const field = tokenField(role.name);
|
|
65
|
+
if (!Object.hasOwn(tokens, role.name)) throw new ThemeError("MISSING_CONTRACT_ROLE", field, `Theme is missing the Contract role "${role.name}".`);
|
|
66
|
+
validated[role.name] = assertTokenValue(role, tokens[role.name], field);
|
|
67
|
+
}
|
|
68
|
+
for (const roleName of Object.keys(tokens)) if (!ROLE_NAMES.has(roleName)) throw new ThemeError("UNKNOWN_CONTRACT_ROLE", tokenField(roleName), `"${roleName}" is not a role in the Semantic Token Contract.`);
|
|
69
|
+
return {
|
|
70
|
+
name,
|
|
71
|
+
tokens: validated
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// src/define-theme.ts
|
|
75
|
+
/** The checked factory for Themes: validates raw Theme data against the Semantic Token Contract. */
|
|
76
|
+
function defineTheme(input) {
|
|
77
|
+
const validated = validateTheme(input);
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
name: validated.name,
|
|
80
|
+
contractVersion: semanticTokenContract.version,
|
|
81
|
+
tokens: Object.freeze({ ...validated.tokens })
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// src/theme-to-css.ts
|
|
85
|
+
function escapeCssString(value) {
|
|
86
|
+
let escaped = "";
|
|
87
|
+
for (const character of value) {
|
|
88
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
89
|
+
if (character === "\\") escaped += "\\\\";
|
|
90
|
+
else if (character === "\"") escaped += "\\\"";
|
|
91
|
+
else if (codePoint < 32 || codePoint === 127) escaped += `\\${codePoint.toString(16)} `;
|
|
92
|
+
else escaped += character;
|
|
93
|
+
}
|
|
94
|
+
return escaped;
|
|
95
|
+
}
|
|
96
|
+
function cssVariableName(roleName) {
|
|
97
|
+
return `--ds-${roleName.replaceAll(".", "-")}`;
|
|
98
|
+
}
|
|
99
|
+
/** The Theme-to-CSS emitter: data-theme-scoped CSS custom properties for a valid Theme. */
|
|
100
|
+
function themeToCss(theme) {
|
|
101
|
+
const validated = validateTheme(theme);
|
|
102
|
+
const declarations = semanticTokenContract.roles.map((role) => {
|
|
103
|
+
const value = validated.tokens[role.name];
|
|
104
|
+
return ` ${cssVariableName(role.name)}: ${value};`;
|
|
105
|
+
});
|
|
106
|
+
return `${`[data-theme="${escapeCssString(validated.name)}"]`} {\n${declarations.join("\n")}\n}\n`;
|
|
107
|
+
}
|
|
108
|
+
export { ThemeError, ThemeErrorCode, defineTheme, themeToCss };
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors.ts","../src/validation.ts","../src/define-theme.ts","../src/theme-to-css.ts"],"sourcesContent":["/** Machine-readable rejection codes for Theme definition. */\nexport enum ThemeErrorCode {\n\tINVALID_THEME_NAME = \"INVALID_THEME_NAME\",\n\tMISSING_TOKENS_SECTION = \"MISSING_TOKENS_SECTION\",\n\tMISSING_CONTRACT_ROLE = \"MISSING_CONTRACT_ROLE\",\n\tUNKNOWN_CONTRACT_ROLE = \"UNKNOWN_CONTRACT_ROLE\",\n\tWRONG_TOKEN_TYPE = \"WRONG_TOKEN_TYPE\",\n\tUNSAFE_TOKEN_VALUE = \"UNSAFE_TOKEN_VALUE\",\n}\n\n/** Error thrown when a Theme violates a domain rule; carries a distinct `code` and the offending `field`. */\nexport class ThemeError extends Error {\n\treadonly code: ThemeErrorCode\n\treadonly field: string\n\n\tconstructor(code: ThemeErrorCode, field: string, message: string) {\n\t\tsuper(`[${code}]: ${message}`)\n\t\tthis.name = \"ThemeError\"\n\t\tthis.code = code\n\t\tthis.field = field\n\t}\n}\n","import {\n\tcreatePrimitiveToken,\n\tDomainError,\n\tDomainErrorCode,\n\ttype SemanticTokenRole,\n\tsemanticTokenContract,\n} from \"@trajectonomics/design-system\"\nimport { ThemeError, ThemeErrorCode } from \"./errors\"\nimport type { ThemeInput } from \"./theme-input\"\n\nconst TOKENS_FIELD = \"tokens\"\nconst UNSAFE_VALUE = /[;}]|url\\s*\\(/i\nconst ROLE_NAMES = new Set(semanticTokenContract.roles.map((role) => role.name))\n\nfunction tokenField(roleName: string): string {\n\treturn `${TOKENS_FIELD}.${roleName}`\n}\n\nfunction validateThemeName(name: unknown): string {\n\tif (typeof name === \"string\" && name.trim().length > 0) return name.trim()\n\tthrow new ThemeError(\n\t\tThemeErrorCode.INVALID_THEME_NAME,\n\t\t\"name\",\n\t\t\"Theme name must be a non-empty string.\",\n\t)\n}\n\nfunction assertSafeValue(value: string, field: string): void {\n\tif (!UNSAFE_VALUE.test(value)) return\n\tthrow new ThemeError(\n\t\tThemeErrorCode.UNSAFE_TOKEN_VALUE,\n\t\tfield,\n\t\t\"A Theme token value must not contain characters that can break out of a CSS declaration.\",\n\t)\n}\n\nfunction assertTokenValue(role: SemanticTokenRole, value: unknown, field: string): string {\n\tif (typeof value !== \"string\") {\n\t\tthrow new ThemeError(ThemeErrorCode.WRONG_TOKEN_TYPE, field, `\"${role.name}\" takes a string.`)\n\t}\n\tassertSafeValue(value, field)\n\ttry {\n\t\treturn createPrimitiveToken({ name: role.name, type: role.type, value }).value\n\t} catch (error) {\n\t\tif (error instanceof DomainError && error.code === DomainErrorCode.INVALID_TOKEN_VALUE) {\n\t\t\tthrow new ThemeError(\n\t\t\t\tThemeErrorCode.WRONG_TOKEN_TYPE,\n\t\t\t\tfield,\n\t\t\t\t`\"${role.name}\" takes a ${role.type} value.`,\n\t\t\t)\n\t\t}\n\t\tthrow error\n\t}\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null\n}\n\n/** Validates a Theme against the Contract, returning its normalized values. */\nexport function validateTheme(input: unknown): ThemeInput {\n\tconst candidate = isRecord(input) ? input : undefined\n\tconst name = validateThemeName(candidate?.[\"name\"])\n\tconst tokens = candidate?.[\"tokens\"]\n\tif (!isRecord(tokens)) {\n\t\tthrow new ThemeError(\n\t\t\tThemeErrorCode.MISSING_TOKENS_SECTION,\n\t\t\tTOKENS_FIELD,\n\t\t\t\"A Theme must have a tokens section.\",\n\t\t)\n\t}\n\tconst validated: Record<string, string> = {}\n\tfor (const role of semanticTokenContract.roles) {\n\t\tconst field = tokenField(role.name)\n\t\tif (!Object.hasOwn(tokens, role.name)) {\n\t\t\tthrow new ThemeError(\n\t\t\t\tThemeErrorCode.MISSING_CONTRACT_ROLE,\n\t\t\t\tfield,\n\t\t\t\t`Theme is missing the Contract role \"${role.name}\".`,\n\t\t\t)\n\t\t}\n\t\tvalidated[role.name] = assertTokenValue(role, tokens[role.name], field)\n\t}\n\tfor (const roleName of Object.keys(tokens)) {\n\t\tif (!ROLE_NAMES.has(roleName)) {\n\t\t\tthrow new ThemeError(\n\t\t\t\tThemeErrorCode.UNKNOWN_CONTRACT_ROLE,\n\t\t\t\ttokenField(roleName),\n\t\t\t\t`\"${roleName}\" is not a role in the Semantic Token Contract.`,\n\t\t\t)\n\t\t}\n\t}\n\treturn { name, tokens: validated }\n}\n","import type { Theme } from \"@trajectonomics/design-system\"\nimport { semanticTokenContract } from \"@trajectonomics/design-system\"\nimport type { ThemeInput } from \"./theme-input\"\nimport { validateTheme } from \"./validation\"\n\n/** The checked factory for Themes: validates raw Theme data against the Semantic Token Contract. */\nexport function defineTheme(input: ThemeInput): Theme {\n\tconst validated = validateTheme(input)\n\treturn Object.freeze({\n\t\tname: validated.name,\n\t\tcontractVersion: semanticTokenContract.version,\n\t\ttokens: Object.freeze({ ...validated.tokens }),\n\t})\n}\n","import { semanticTokenContract, type Theme } from \"@trajectonomics/design-system\"\nimport { validateTheme } from \"./validation\"\n\nfunction escapeCssString(value: string): string {\n\tlet escaped = \"\"\n\tfor (const character of value) {\n\t\tconst codePoint = character.codePointAt(0) ?? 0\n\t\tif (character === \"\\\\\") escaped += \"\\\\\\\\\"\n\t\telse if (character === '\"') escaped += '\\\\\"'\n\t\telse if (codePoint < 0x20 || codePoint === 0x7f) {\n\t\t\tescaped += `\\\\${codePoint.toString(16)} `\n\t\t} else escaped += character\n\t}\n\treturn escaped\n}\n\nfunction cssVariableName(roleName: string): string {\n\treturn `--ds-${roleName.replaceAll(\".\", \"-\")}`\n}\n\n/** The Theme-to-CSS emitter: data-theme-scoped CSS custom properties for a valid Theme. */\nexport function themeToCss(theme: Theme): string {\n\tconst validated = validateTheme(theme)\n\tconst declarations = semanticTokenContract.roles.map((role) => {\n\t\tconst value = validated.tokens[role.name]!\n\t\treturn ` ${cssVariableName(role.name)}: ${value};`\n\t})\n\tconst selector = `[data-theme=\"${escapeCssString(validated.name)}\"]`\n\treturn `${selector} {\\n${declarations.join(\"\\n\")}\\n}\\n`\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[data-theme="dark"] {
|
|
2
|
+
--ds-color-background: #0a0a0a;
|
|
3
|
+
--ds-color-foreground: #fafafa;
|
|
4
|
+
--ds-color-card: #171717;
|
|
5
|
+
--ds-color-card-foreground: #fafafa;
|
|
6
|
+
--ds-color-popover: #171717;
|
|
7
|
+
--ds-color-popover-foreground: #fafafa;
|
|
8
|
+
--ds-color-primary: #e5e5e5;
|
|
9
|
+
--ds-color-primary-foreground: #171717;
|
|
10
|
+
--ds-color-secondary: #262626;
|
|
11
|
+
--ds-color-secondary-foreground: #fafafa;
|
|
12
|
+
--ds-color-muted: #262626;
|
|
13
|
+
--ds-color-muted-foreground: #a1a1a1;
|
|
14
|
+
--ds-color-accent: #262626;
|
|
15
|
+
--ds-color-accent-foreground: #fafafa;
|
|
16
|
+
--ds-color-destructive: #ff6467;
|
|
17
|
+
--ds-color-destructive-foreground: #ffffff;
|
|
18
|
+
--ds-color-border: #262626;
|
|
19
|
+
--ds-color-input: #262626;
|
|
20
|
+
--ds-color-ring: #737373;
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[data-theme="light"] {
|
|
2
|
+
--ds-color-background: #ffffff;
|
|
3
|
+
--ds-color-foreground: #0a0a0a;
|
|
4
|
+
--ds-color-card: #ffffff;
|
|
5
|
+
--ds-color-card-foreground: #0a0a0a;
|
|
6
|
+
--ds-color-popover: #ffffff;
|
|
7
|
+
--ds-color-popover-foreground: #0a0a0a;
|
|
8
|
+
--ds-color-primary: #171717;
|
|
9
|
+
--ds-color-primary-foreground: #fafafa;
|
|
10
|
+
--ds-color-secondary: #f5f5f5;
|
|
11
|
+
--ds-color-secondary-foreground: #171717;
|
|
12
|
+
--ds-color-muted: #f5f5f5;
|
|
13
|
+
--ds-color-muted-foreground: #737373;
|
|
14
|
+
--ds-color-accent: #f5f5f5;
|
|
15
|
+
--ds-color-accent-foreground: #171717;
|
|
16
|
+
--ds-color-destructive: #e7000b;
|
|
17
|
+
--ds-color-destructive-foreground: #ffffff;
|
|
18
|
+
--ds-color-border: #e5e5e5;
|
|
19
|
+
--ds-color-input: #e5e5e5;
|
|
20
|
+
--ds-color-ring: #a1a1a1;
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,65 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@trajectonomics/design-system-theme",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Trajectonomics design system: Theme definition, validation and CSS emission",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/trajectonomics/design-system.git",
|
|
9
|
+
"directory": "packages/theme"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": [
|
|
13
|
+
"**/*.css"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20.0.0"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.cjs",
|
|
19
|
+
"module": "./dist/index.mjs",
|
|
20
|
+
"types": "./dist/index.d.cts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./dist/index.d.mts",
|
|
25
|
+
"default": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./dist/index.d.cts",
|
|
29
|
+
"default": "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"./themes/light.css": "./dist/themes/light.css",
|
|
33
|
+
"./themes/dark.css": "./dist/themes/dark.css",
|
|
34
|
+
"./tailwind.css": "./tailwind.css",
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"tailwind.css"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public",
|
|
43
|
+
"provenance": true,
|
|
44
|
+
"tag": "latest"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsdown && bun run build:css",
|
|
48
|
+
"build:css": "bun run scripts/build-theme-css.ts",
|
|
49
|
+
"generate:tailwind": "bun run scripts/generate-tailwind-theme.ts",
|
|
50
|
+
"test": "bun test",
|
|
51
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json && tsc -p tsconfig.scripts.json",
|
|
52
|
+
"lint": "biome ci --error-on-warnings .",
|
|
53
|
+
"lint:package": "publint --strict && attw --pack . --profile node16 --exclude-entrypoints ./themes/light.css ./themes/dark.css ./tailwind.css"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@trajectonomics/design-system": "^0.1.0-alpha.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@tailwindcss/cli": "^4.3.5",
|
|
60
|
+
"@trajectonomics/design-system": "workspace:*",
|
|
61
|
+
"@types/bun": "^1.4.4",
|
|
62
|
+
"tailwindcss": "^4.3.5",
|
|
63
|
+
"typescript": "^7.0.2"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/tailwind.css
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@theme inline {
|
|
2
|
+
--color-background: var(--ds-color-background);
|
|
3
|
+
--color-foreground: var(--ds-color-foreground);
|
|
4
|
+
--color-card: var(--ds-color-card);
|
|
5
|
+
--color-card-foreground: var(--ds-color-card-foreground);
|
|
6
|
+
--color-popover: var(--ds-color-popover);
|
|
7
|
+
--color-popover-foreground: var(--ds-color-popover-foreground);
|
|
8
|
+
--color-primary: var(--ds-color-primary);
|
|
9
|
+
--color-primary-foreground: var(--ds-color-primary-foreground);
|
|
10
|
+
--color-secondary: var(--ds-color-secondary);
|
|
11
|
+
--color-secondary-foreground: var(--ds-color-secondary-foreground);
|
|
12
|
+
--color-muted: var(--ds-color-muted);
|
|
13
|
+
--color-muted-foreground: var(--ds-color-muted-foreground);
|
|
14
|
+
--color-accent: var(--ds-color-accent);
|
|
15
|
+
--color-accent-foreground: var(--ds-color-accent-foreground);
|
|
16
|
+
--color-destructive: var(--ds-color-destructive);
|
|
17
|
+
--color-destructive-foreground: var(--ds-color-destructive-foreground);
|
|
18
|
+
--color-border: var(--ds-color-border);
|
|
19
|
+
--color-input: var(--ds-color-input);
|
|
20
|
+
--color-ring: var(--ds-color-ring);
|
|
21
|
+
}
|
package/README.md
DELETED